We have a reasonably large number of changes:
* lots more HE (802.11ax) support, particularly things relevant for the the AP side, but also mesh support * debugfs cleanups from Greg * some more work on extended key ID * start using genl parallel_ops, as preparation for weaning ourselves off RTNL and getting parallelism * various other changes all over -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEH1e1rEeCd0AIMq6MB8qZga/fl8QFAl1BtsAACgkQB8qZga/f l8QTdQ//bD3NLitiIW4rBmC/w9str2TRpUbRN8Hf9xTnRh1smLGQop8jI8xpYdAE hXFlWn4glCkTkZrqtT8IDMcERb2YPHI94L7lMR1L6dXH7e1tiBZ7Qum5+rojLUCQ /XXZJnVa9AbdzBDtkhzCjkN8dCldMnkId0m6vkGdFre/b70FLDg2GlolqIchbDCz GxeKaw/uTLwu9nxEJhspDmiXDQtQd1ZsGNZRrovQ2nUuUfvX9sU54OmaoDHhqrUM iSwFZJAhrCV7nidOLs2X1rVs8hRymbrYnGu6NdUlfTQMqaOVgWhqniM1RMnJnnVi Ec/1OQg4KIj2rjJIXW/3QXAzAO6TDwV8xNk9al3m+yAkkSXR7tXP01plNUoMU4UW YxcaPD341H919GIqa71lieeEMyi7XIKKFfaZvGFLiDzidKKi0JkDuC+1w5UOJrbj PM/dzvKqvwOMEa5XahjrUQLsiGMgxBkDo36+F4rfaelzDKPuJBOokDS23vg/lYf5 2v74WbeJVc45cAhSwp5/0RTsG0xhmFImfrE66VNB1vcJUVLvHTuATfXsBbRahOz/ SglGBAGqigxJwIeYfgec3lPNfdV+0LwYnPAEjBIjCO8qlGvst4jLroHg7ayApr2E JfUbaWtpEb9cgAz7X6tEjv/BAnsRWPzpxb8Nm6JoZttTtA2VakE= =eUnh -----END PGP SIGNATURE----- Merge tag 'mac80211-next-for-davem-2019-07-31' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next Johannes Berg says: ==================== We have a reasonably large number of changes: * lots more HE (802.11ax) support, particularly things relevant for the the AP side, but also mesh support * debugfs cleanups from Greg * some more work on extended key ID * start using genl parallel_ops, as preparation for weaning ourselves off RTNL and getting parallelism * various other changes all over ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
ac5fe22636
@ -6970,7 +6970,8 @@ exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw)
|
||||
static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct ath10k *ar = hw->priv;
|
||||
|
||||
|
@ -2392,7 +2392,8 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw)
|
||||
static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct ath_softc *sc = hw->priv;
|
||||
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
||||
|
@ -3256,28 +3256,16 @@ static void rs_add_debugfs(void *priv, void *priv_sta,
|
||||
struct dentry *dir)
|
||||
{
|
||||
struct iwl_lq_sta *lq_sta = priv_sta;
|
||||
lq_sta->rs_sta_dbgfs_scale_table_file =
|
||||
debugfs_create_file("rate_scale_table", 0600, dir,
|
||||
lq_sta, &rs_sta_dbgfs_scale_table_ops);
|
||||
lq_sta->rs_sta_dbgfs_stats_table_file =
|
||||
debugfs_create_file("rate_stats_table", 0400, dir,
|
||||
lq_sta, &rs_sta_dbgfs_stats_table_ops);
|
||||
lq_sta->rs_sta_dbgfs_rate_scale_data_file =
|
||||
debugfs_create_file("rate_scale_data", 0400, dir,
|
||||
lq_sta, &rs_sta_dbgfs_rate_scale_data_ops);
|
||||
lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file =
|
||||
debugfs_create_u8("tx_agg_tid_enable", 0600, dir,
|
||||
&lq_sta->tx_agg_tid_en);
|
||||
|
||||
}
|
||||
debugfs_create_file("rate_scale_table", 0600, dir, lq_sta,
|
||||
&rs_sta_dbgfs_scale_table_ops);
|
||||
debugfs_create_file("rate_stats_table", 0400, dir, lq_sta,
|
||||
&rs_sta_dbgfs_stats_table_ops);
|
||||
debugfs_create_file("rate_scale_data", 0400, dir, lq_sta,
|
||||
&rs_sta_dbgfs_rate_scale_data_ops);
|
||||
debugfs_create_u8("tx_agg_tid_enable", 0600, dir,
|
||||
&lq_sta->tx_agg_tid_en);
|
||||
|
||||
static void rs_remove_debugfs(void *priv, void *priv_sta)
|
||||
{
|
||||
struct iwl_lq_sta *lq_sta = priv_sta;
|
||||
debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file);
|
||||
debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file);
|
||||
debugfs_remove(lq_sta->rs_sta_dbgfs_rate_scale_data_file);
|
||||
debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -3303,7 +3291,6 @@ static const struct rate_control_ops rs_ops = {
|
||||
.free_sta = rs_free_sta,
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
.add_sta_debugfs = rs_add_debugfs,
|
||||
.remove_sta_debugfs = rs_remove_debugfs,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -356,10 +356,6 @@ struct iwl_lq_sta {
|
||||
struct iwl_traffic_load load[IWL_MAX_TID_COUNT];
|
||||
u8 tx_agg_tid_en;
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
struct dentry *rs_sta_dbgfs_scale_table_file;
|
||||
struct dentry *rs_sta_dbgfs_stats_table_file;
|
||||
struct dentry *rs_sta_dbgfs_rate_scale_data_file;
|
||||
struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
|
||||
u32 dbg_fixed_rate;
|
||||
#endif
|
||||
struct iwl_priv *drv;
|
||||
|
@ -4010,7 +4010,8 @@ out_unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw)
|
||||
static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
|
||||
|
@ -4093,10 +4093,6 @@ static void rs_drv_add_sta_debugfs(void *mvm, void *priv_sta,
|
||||
|
||||
MVM_DEBUGFS_ADD_FILE_RS(ss_force, dir, 0600);
|
||||
}
|
||||
|
||||
void rs_remove_sta_debugfs(void *mvm, void *mvm_sta)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -4124,7 +4120,6 @@ static const struct rate_control_ops rs_mvm_ops_drv = {
|
||||
.rate_update = rs_drv_rate_update,
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
.add_sta_debugfs = rs_drv_add_sta_debugfs,
|
||||
.remove_sta_debugfs = rs_remove_sta_debugfs,
|
||||
#endif
|
||||
.capa = RATE_CTRL_CAPA_VHT_EXT_NSS_BW,
|
||||
};
|
||||
|
@ -2216,7 +2216,8 @@ static int mac80211_hwsim_roc(struct ieee80211_hw *hw,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mac80211_hwsim_croc(struct ieee80211_hw *hw)
|
||||
static int mac80211_hwsim_croc(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct mac80211_hwsim_data *hwsim = hw->priv;
|
||||
|
||||
@ -2594,7 +2595,7 @@ static const struct ieee80211_sband_iftype_data he_capa_5ghz = {
|
||||
},
|
||||
};
|
||||
|
||||
static void mac80211_hswim_he_capab(struct ieee80211_supported_band *sband)
|
||||
static void mac80211_hwsim_he_capab(struct ieee80211_supported_band *sband)
|
||||
{
|
||||
if (sband->band == NL80211_BAND_2GHZ)
|
||||
sband->iftype_data =
|
||||
@ -2805,12 +2806,6 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||
ieee80211_hw_set(hw, SIGNAL_DBM);
|
||||
ieee80211_hw_set(hw, SUPPORTS_PS);
|
||||
ieee80211_hw_set(hw, TDLS_WIDER_BW);
|
||||
|
||||
/* We only have SW crypto and only implement the A-MPDU API
|
||||
* (but don't really build A-MPDUs) so can have extended key
|
||||
* support
|
||||
*/
|
||||
ieee80211_hw_set(hw, EXT_KEY_ID_NATIVE);
|
||||
if (rctbl)
|
||||
ieee80211_hw_set(hw, SUPPORTS_RC_TABLE);
|
||||
ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
|
||||
@ -2897,7 +2892,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||
sband->ht_cap.mcs.rx_mask[1] = 0xff;
|
||||
sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
|
||||
|
||||
mac80211_hswim_he_capab(sband);
|
||||
mac80211_hwsim_he_capab(sband);
|
||||
|
||||
hw->wiphy->bands[band] = sband;
|
||||
}
|
||||
@ -3233,6 +3228,7 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
|
||||
{
|
||||
struct mac80211_hwsim_data *data2;
|
||||
struct ieee80211_rx_status rx_status;
|
||||
struct ieee80211_hdr *hdr;
|
||||
const u8 *dst;
|
||||
int frame_data_len;
|
||||
void *frame_data;
|
||||
@ -3299,6 +3295,12 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
|
||||
rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
|
||||
rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
|
||||
|
||||
hdr = (void *)skb->data;
|
||||
|
||||
if (ieee80211_is_beacon(hdr->frame_control) ||
|
||||
ieee80211_is_probe_resp(hdr->frame_control))
|
||||
rx_status.boottime_ns = ktime_get_boottime_ns();
|
||||
|
||||
memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
|
||||
data2->rx_pkts++;
|
||||
data2->rx_bytes += skb->len;
|
||||
|
@ -1818,7 +1818,8 @@ out:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int rsi_mac80211_cancel_roc(struct ieee80211_hw *hw)
|
||||
static int rsi_mac80211_cancel_roc(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct rsi_hw *adapter = hw->priv;
|
||||
struct rsi_common *common = adapter->priv;
|
||||
|
@ -5749,7 +5749,8 @@ static void wlcore_roc_complete_work(struct work_struct *work)
|
||||
ieee80211_remain_on_channel_expired(wl->hw);
|
||||
}
|
||||
|
||||
static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw)
|
||||
static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct wl1271 *wl = hw->priv;
|
||||
|
||||
|
@ -881,6 +881,14 @@ struct ieee80211_tpc_report_ie {
|
||||
u8 link_margin;
|
||||
} __packed;
|
||||
|
||||
#define IEEE80211_ADDBA_EXT_FRAG_LEVEL_MASK GENMASK(2, 1)
|
||||
#define IEEE80211_ADDBA_EXT_FRAG_LEVEL_SHIFT 1
|
||||
#define IEEE80211_ADDBA_EXT_NO_FRAG BIT(0)
|
||||
|
||||
struct ieee80211_addba_ext_ie {
|
||||
u8 data;
|
||||
} __packed;
|
||||
|
||||
struct ieee80211_mgmt {
|
||||
__le16 frame_control;
|
||||
__le16 duration;
|
||||
@ -973,6 +981,8 @@ struct ieee80211_mgmt {
|
||||
__le16 capab;
|
||||
__le16 timeout;
|
||||
__le16 start_seq_num;
|
||||
/* followed by BA Extension */
|
||||
u8 variable[0];
|
||||
} __packed addba_req;
|
||||
struct{
|
||||
u8 action_code;
|
||||
@ -1625,6 +1635,18 @@ struct ieee80211_he_operation {
|
||||
u8 optional[0];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ieee80211_he_spr - HE spatial reuse element
|
||||
*
|
||||
* This structure is the "HE spatial reuse element" element as
|
||||
* described in P802.11ax_D4.0 section 9.4.2.241
|
||||
*/
|
||||
struct ieee80211_he_spr {
|
||||
u8 he_sr_control;
|
||||
/* Optional 0 to 19 bytes: depends on @he_sr_control */
|
||||
u8 optional[0];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ieee80211_he_mu_edca_param_ac_rec - MU AC Parameter Record field
|
||||
*
|
||||
@ -2033,8 +2055,8 @@ ieee80211_he_ppe_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
|
||||
* ieee80211_he_oper_size - calculate 802.11ax HE Operations IE size
|
||||
* @he_oper_ie: byte data of the He Operations IE, stating from the the byte
|
||||
* after the ext ID byte. It is assumed that he_oper_ie has at least
|
||||
* sizeof(struct ieee80211_he_operation) bytes, checked already in
|
||||
* ieee802_11_parse_elems_crc()
|
||||
* sizeof(struct ieee80211_he_operation) bytes, the caller must have
|
||||
* validated this.
|
||||
* @return the actual size of the IE data (not including header), or 0 on error
|
||||
*/
|
||||
static inline u8
|
||||
@ -2063,6 +2085,42 @@ ieee80211_he_oper_size(const u8 *he_oper_ie)
|
||||
return oper_len;
|
||||
}
|
||||
|
||||
/* HE Spatial Reuse defines */
|
||||
#define IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT 0x4
|
||||
#define IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT 0x8
|
||||
|
||||
/*
|
||||
* ieee80211_he_spr_size - calculate 802.11ax HE Spatial Reuse IE size
|
||||
* @he_spr_ie: byte data of the He Spatial Reuse IE, stating from the the byte
|
||||
* after the ext ID byte. It is assumed that he_spr_ie has at least
|
||||
* sizeof(struct ieee80211_he_spr) bytes, the caller must have validated
|
||||
* this
|
||||
* @return the actual size of the IE data (not including header), or 0 on error
|
||||
*/
|
||||
static inline u8
|
||||
ieee80211_he_spr_size(const u8 *he_spr_ie)
|
||||
{
|
||||
struct ieee80211_he_spr *he_spr = (void *)he_spr_ie;
|
||||
u8 spr_len = sizeof(struct ieee80211_he_spr);
|
||||
u32 he_spr_params;
|
||||
|
||||
/* Make sure the input is not NULL */
|
||||
if (!he_spr_ie)
|
||||
return 0;
|
||||
|
||||
/* Calc required length */
|
||||
he_spr_params = le32_to_cpu(he_spr->he_sr_control);
|
||||
if (he_spr_params & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT)
|
||||
spr_len++;
|
||||
if (he_spr_params & IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT)
|
||||
spr_len += 18;
|
||||
|
||||
/* Add the first byte (extension ID) to the total length */
|
||||
spr_len++;
|
||||
|
||||
return spr_len;
|
||||
}
|
||||
|
||||
/* Authentication algorithms */
|
||||
#define WLAN_AUTH_OPEN 0
|
||||
#define WLAN_AUTH_SHARED_KEY 1
|
||||
@ -2485,6 +2543,7 @@ enum ieee80211_eid_ext {
|
||||
WLAN_EID_EXT_HE_OPERATION = 36,
|
||||
WLAN_EID_EXT_UORA = 37,
|
||||
WLAN_EID_EXT_HE_MU_EDCA = 38,
|
||||
WLAN_EID_EXT_HE_SPR = 39,
|
||||
WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME = 52,
|
||||
WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION = 55,
|
||||
WLAN_EID_EXT_NON_INHERITANCE = 56,
|
||||
|
@ -246,6 +246,19 @@ struct ieee80211_rate {
|
||||
u16 hw_value, hw_value_short;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ieee80211_he_obss_pd - AP settings for spatial reuse
|
||||
*
|
||||
* @enable: is the feature enabled.
|
||||
* @min_offset: minimal tx power offset an associated station shall use
|
||||
* @max_offset: maximum tx power offset an associated station shall use
|
||||
*/
|
||||
struct ieee80211_he_obss_pd {
|
||||
bool enable;
|
||||
u8 min_offset;
|
||||
u8 max_offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ieee80211_sta_ht_cap - STA's HT capabilities
|
||||
*
|
||||
@ -896,6 +909,7 @@ enum cfg80211_ap_settings_flags {
|
||||
* @vht_required: stations must support VHT
|
||||
* @twt_responder: Enable Target Wait Time
|
||||
* @flags: flags, as defined in enum cfg80211_ap_settings_flags
|
||||
* @he_obss_pd: OBSS Packet Detection settings
|
||||
*/
|
||||
struct cfg80211_ap_settings {
|
||||
struct cfg80211_chan_def chandef;
|
||||
@ -923,6 +937,7 @@ struct cfg80211_ap_settings {
|
||||
bool ht_required, vht_required;
|
||||
bool twt_responder;
|
||||
u32 flags;
|
||||
struct ieee80211_he_obss_pd he_obss_pd;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -315,6 +315,7 @@ struct ieee80211_vif_chanctx_switch {
|
||||
* @BSS_CHANGED_FTM_RESPONDER: fime timing reasurement request responder
|
||||
* functionality changed for this BSS (AP mode).
|
||||
* @BSS_CHANGED_TWT: TWT status changed
|
||||
* @BSS_CHANGED_HE_OBSS_PD: OBSS Packet Detection status changed.
|
||||
*
|
||||
*/
|
||||
enum ieee80211_bss_change {
|
||||
@ -346,6 +347,7 @@ enum ieee80211_bss_change {
|
||||
BSS_CHANGED_MCAST_RATE = 1<<25,
|
||||
BSS_CHANGED_FTM_RESPONDER = 1<<26,
|
||||
BSS_CHANGED_TWT = 1<<27,
|
||||
BSS_CHANGED_HE_OBSS_PD = 1<<28,
|
||||
|
||||
/* when adding here, make sure to change ieee80211_reconfig */
|
||||
};
|
||||
@ -600,6 +602,8 @@ struct ieee80211_ftm_responder_params {
|
||||
* nontransmitted BSSIDs
|
||||
* @profile_periodicity: the least number of beacon frames need to be received
|
||||
* in order to discover all the nontransmitted BSSIDs in the set.
|
||||
* @he_operation: HE operation information of the AP we are connected to
|
||||
* @he_obss_pd: OBSS Packet Detection parameters.
|
||||
*/
|
||||
struct ieee80211_bss_conf {
|
||||
const u8 *bssid;
|
||||
@ -661,6 +665,8 @@ struct ieee80211_bss_conf {
|
||||
u8 bssid_indicator;
|
||||
bool ema_ap;
|
||||
u8 profile_periodicity;
|
||||
struct ieee80211_he_operation he_operation;
|
||||
struct ieee80211_he_obss_pd he_obss_pd;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1058,11 +1064,13 @@ struct ieee80211_tx_info {
|
||||
* @sta: Station that the packet was transmitted for
|
||||
* @info: Basic tx status information
|
||||
* @skb: Packet skb (can be NULL if not provided by the driver)
|
||||
* @rate: The TX rate that was used when sending the packet
|
||||
*/
|
||||
struct ieee80211_tx_status {
|
||||
struct ieee80211_sta *sta;
|
||||
struct ieee80211_tx_info *info;
|
||||
struct sk_buff *skb;
|
||||
struct rate_info *rate;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1702,6 +1710,9 @@ struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif);
|
||||
* a TKIP key if it only requires MIC space. Do not set together with
|
||||
* @IEEE80211_KEY_FLAG_GENERATE_MMIC on the same key.
|
||||
* @IEEE80211_KEY_FLAG_NO_AUTO_TX: Key needs explicit Tx activation.
|
||||
* @IEEE80211_KEY_FLAG_GENERATE_MMIE: This flag should be set by the driver
|
||||
* for a AES_CMAC key to indicate that it requires sequence number
|
||||
* generation only
|
||||
*/
|
||||
enum ieee80211_key_flags {
|
||||
IEEE80211_KEY_FLAG_GENERATE_IV_MGMT = BIT(0),
|
||||
@ -1714,6 +1725,7 @@ enum ieee80211_key_flags {
|
||||
IEEE80211_KEY_FLAG_RESERVE_TAILROOM = BIT(7),
|
||||
IEEE80211_KEY_FLAG_PUT_MIC_SPACE = BIT(8),
|
||||
IEEE80211_KEY_FLAG_NO_AUTO_TX = BIT(9),
|
||||
IEEE80211_KEY_FLAG_GENERATE_MMIE = BIT(10),
|
||||
};
|
||||
|
||||
/**
|
||||
@ -2268,11 +2280,9 @@ struct ieee80211_txq {
|
||||
* @IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID: Hardware supports multi BSSID
|
||||
* only for HE APs. Applies if @IEEE80211_HW_SUPPORTS_MULTI_BSSID is set.
|
||||
*
|
||||
* @IEEE80211_HW_EXT_KEY_ID_NATIVE: Driver and hardware are supporting Extended
|
||||
* Key ID and can handle two unicast keys per station for Rx and Tx.
|
||||
*
|
||||
* @IEEE80211_HW_NO_AMPDU_KEYBORDER_SUPPORT: The card/driver can't handle
|
||||
* active Tx A-MPDU sessions with Extended Key IDs during rekey.
|
||||
* @IEEE80211_HW_AMPDU_KEYBORDER_SUPPORT: The card and driver is only
|
||||
* aggregating MPDUs with the same keyid, allowing mac80211 to keep Tx
|
||||
* A-MPDU sessions active while rekeying with Extended Key ID.
|
||||
*
|
||||
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
|
||||
*/
|
||||
@ -2325,8 +2335,7 @@ enum ieee80211_hw_flags {
|
||||
IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN,
|
||||
IEEE80211_HW_SUPPORTS_MULTI_BSSID,
|
||||
IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
|
||||
IEEE80211_HW_EXT_KEY_ID_NATIVE,
|
||||
IEEE80211_HW_NO_AMPDU_KEYBORDER_SUPPORT,
|
||||
IEEE80211_HW_AMPDU_KEYBORDER_SUPPORT,
|
||||
|
||||
/* keep last, obviously */
|
||||
NUM_IEEE80211_HW_FLAGS
|
||||
@ -3914,7 +3923,8 @@ struct ieee80211_ops {
|
||||
struct ieee80211_channel *chan,
|
||||
int duration,
|
||||
enum ieee80211_roc_type type);
|
||||
int (*cancel_remain_on_channel)(struct ieee80211_hw *hw);
|
||||
int (*cancel_remain_on_channel)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif);
|
||||
int (*set_ringparam)(struct ieee80211_hw *hw, u32 tx, u32 rx);
|
||||
void (*get_ringparam)(struct ieee80211_hw *hw,
|
||||
u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max);
|
||||
@ -5945,7 +5955,6 @@ struct rate_control_ops {
|
||||
|
||||
void (*add_sta_debugfs)(void *priv, void *priv_sta,
|
||||
struct dentry *dir);
|
||||
void (*remove_sta_debugfs)(void *priv, void *priv_sta);
|
||||
|
||||
u32 (*get_expected_throughput)(void *priv_sta);
|
||||
};
|
||||
@ -6234,10 +6243,36 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid);
|
||||
* but for the duration of the frame handling.
|
||||
* However, also note that while in the wake_tx_queue() method,
|
||||
* rcu_read_lock() is already held.
|
||||
*
|
||||
* softirqs must also be disabled when this function is called.
|
||||
* In process context, use ieee80211_tx_dequeue_ni() instead.
|
||||
*/
|
||||
struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
|
||||
struct ieee80211_txq *txq);
|
||||
|
||||
/**
|
||||
* ieee80211_tx_dequeue_ni - dequeue a packet from a software tx queue
|
||||
* (in process context)
|
||||
*
|
||||
* Like ieee80211_tx_dequeue() but can be called in process context
|
||||
* (internally disables bottom halves).
|
||||
*
|
||||
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
* @txq: pointer obtained from station or virtual interface, or from
|
||||
* ieee80211_next_txq()
|
||||
*/
|
||||
static inline struct sk_buff *ieee80211_tx_dequeue_ni(struct ieee80211_hw *hw,
|
||||
struct ieee80211_txq *txq)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
local_bh_disable();
|
||||
skb = ieee80211_tx_dequeue(hw, txq);
|
||||
local_bh_enable();
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_next_txq - get next tx queue to pull packets from
|
||||
*
|
||||
|
@ -657,7 +657,9 @@
|
||||
* is used during CSA period.
|
||||
* @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
|
||||
* command may be used with the corresponding cookie to cancel the wait
|
||||
* time if it is known that it is no longer necessary.
|
||||
* time if it is known that it is no longer necessary. This command is
|
||||
* also sent as an event whenever the driver has completed the off-channel
|
||||
* wait time.
|
||||
* @NL80211_CMD_ACTION: Alias for @NL80211_CMD_FRAME for backward compatibility.
|
||||
* @NL80211_CMD_FRAME_TX_STATUS: Report TX status of a management frame
|
||||
* transmitted with %NL80211_CMD_FRAME. %NL80211_ATTR_COOKIE identifies
|
||||
@ -2356,6 +2358,9 @@ enum nl80211_commands {
|
||||
*
|
||||
* @NL80211_ATTR_TWT_RESPONDER: Enable target wait time responder support.
|
||||
*
|
||||
* @NL80211_ATTR_HE_OBSS_PD: nested attribute for OBSS Packet Detection
|
||||
* functionality.
|
||||
*
|
||||
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
|
||||
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||||
* @__NL80211_ATTR_AFTER_LAST: internal use
|
||||
@ -2813,6 +2818,8 @@ enum nl80211_attrs {
|
||||
|
||||
NL80211_ATTR_TWT_RESPONDER,
|
||||
|
||||
NL80211_ATTR_HE_OBSS_PD,
|
||||
|
||||
/* add attributes here, update the policy in nl80211.c */
|
||||
|
||||
__NL80211_ATTR_AFTER_LAST,
|
||||
@ -6488,4 +6495,26 @@ enum nl80211_peer_measurement_ftm_resp {
|
||||
NL80211_PMSR_FTM_RESP_ATTR_MAX = NUM_NL80211_PMSR_FTM_RESP_ATTR - 1
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl80211_obss_pd_attributes - OBSS packet detection attributes
|
||||
* @__NL80211_HE_OBSS_PD_ATTR_INVALID: Invalid
|
||||
*
|
||||
* @NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET: the OBSS PD minimum tx power offset.
|
||||
* @NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET: the OBSS PD maximum tx power offset.
|
||||
*
|
||||
* @__NL80211_HE_OBSS_PD_ATTR_LAST: Internal
|
||||
* @NL80211_HE_OBSS_PD_ATTR_MAX: highest OBSS PD attribute.
|
||||
*/
|
||||
enum nl80211_obss_pd_attributes {
|
||||
__NL80211_HE_OBSS_PD_ATTR_INVALID,
|
||||
|
||||
NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET,
|
||||
NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET,
|
||||
|
||||
/* keep last */
|
||||
__NL80211_HE_OBSS_PD_ATTR_LAST,
|
||||
NL80211_HE_OBSS_PD_ATTR_MAX = __NL80211_HE_OBSS_PD_ATTR_LAST - 1,
|
||||
};
|
||||
|
||||
|
||||
#endif /* __LINUX_NL80211_H */
|
||||
|
@ -178,17 +178,54 @@ static void sta_rx_agg_reorder_timer_expired(struct timer_list *t)
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *da, u16 tid,
|
||||
u8 dialog_token, u16 status, u16 policy,
|
||||
u16 buf_size, u16 timeout)
|
||||
static void ieee80211_add_addbaext(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb,
|
||||
const struct ieee80211_addba_ext_ie *req)
|
||||
{
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct ieee80211_addba_ext_ie *resp;
|
||||
const struct ieee80211_sta_he_cap *he_cap;
|
||||
u8 frag_level, cap_frag_level;
|
||||
u8 *pos;
|
||||
|
||||
sband = ieee80211_get_sband(sdata);
|
||||
if (!sband)
|
||||
return;
|
||||
he_cap = ieee80211_get_he_iftype_cap(sband, sdata->vif.type);
|
||||
if (!he_cap)
|
||||
return;
|
||||
|
||||
pos = skb_put_zero(skb, 2 + sizeof(struct ieee80211_addba_ext_ie));
|
||||
*pos++ = WLAN_EID_ADDBA_EXT;
|
||||
*pos++ = sizeof(struct ieee80211_addba_ext_ie);
|
||||
resp = (struct ieee80211_addba_ext_ie *)pos;
|
||||
resp->data = req->data & IEEE80211_ADDBA_EXT_NO_FRAG;
|
||||
|
||||
frag_level = u32_get_bits(req->data,
|
||||
IEEE80211_ADDBA_EXT_FRAG_LEVEL_MASK);
|
||||
cap_frag_level = u32_get_bits(he_cap->he_cap_elem.mac_cap_info[0],
|
||||
IEEE80211_HE_MAC_CAP0_DYNAMIC_FRAG_MASK);
|
||||
if (frag_level > cap_frag_level)
|
||||
frag_level = cap_frag_level;
|
||||
resp->data |= u8_encode_bits(frag_level,
|
||||
IEEE80211_ADDBA_EXT_FRAG_LEVEL_MASK);
|
||||
}
|
||||
|
||||
static void ieee80211_send_addba_resp(struct sta_info *sta, u8 *da, u16 tid,
|
||||
u8 dialog_token, u16 status, u16 policy,
|
||||
u16 buf_size, u16 timeout,
|
||||
const struct ieee80211_addba_ext_ie *addbaext)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
bool amsdu = ieee80211_hw_check(&local->hw, SUPPORTS_AMSDU_IN_AMPDU);
|
||||
u16 capab;
|
||||
|
||||
skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
|
||||
skb = dev_alloc_skb(sizeof(*mgmt) +
|
||||
2 + sizeof(struct ieee80211_addba_ext_ie) +
|
||||
local->hw.extra_tx_headroom);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
@ -222,13 +259,17 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
|
||||
mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout);
|
||||
mgmt->u.action.u.addba_resp.status = cpu_to_le16(status);
|
||||
|
||||
if (sta->sta.he_cap.has_he && addbaext)
|
||||
ieee80211_add_addbaext(sdata, skb, addbaext);
|
||||
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
}
|
||||
|
||||
void ___ieee80211_start_rx_ba_session(struct sta_info *sta,
|
||||
u8 dialog_token, u16 timeout,
|
||||
u16 start_seq_num, u16 ba_policy, u16 tid,
|
||||
u16 buf_size, bool tx, bool auto_seq)
|
||||
u16 buf_size, bool tx, bool auto_seq,
|
||||
const struct ieee80211_addba_ext_ie *addbaext)
|
||||
{
|
||||
struct ieee80211_local *local = sta->sdata->local;
|
||||
struct tid_ampdu_rx *tid_agg_rx;
|
||||
@ -410,21 +451,22 @@ end:
|
||||
}
|
||||
|
||||
if (tx)
|
||||
ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid,
|
||||
ieee80211_send_addba_resp(sta, sta->sta.addr, tid,
|
||||
dialog_token, status, 1, buf_size,
|
||||
timeout);
|
||||
timeout, addbaext);
|
||||
}
|
||||
|
||||
static void __ieee80211_start_rx_ba_session(struct sta_info *sta,
|
||||
u8 dialog_token, u16 timeout,
|
||||
u16 start_seq_num, u16 ba_policy,
|
||||
u16 tid, u16 buf_size, bool tx,
|
||||
bool auto_seq)
|
||||
bool auto_seq,
|
||||
const struct ieee80211_addba_ext_ie *addbaext)
|
||||
{
|
||||
mutex_lock(&sta->ampdu_mlme.mtx);
|
||||
___ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
|
||||
start_seq_num, ba_policy, tid,
|
||||
buf_size, tx, auto_seq);
|
||||
buf_size, tx, auto_seq, addbaext);
|
||||
mutex_unlock(&sta->ampdu_mlme.mtx);
|
||||
}
|
||||
|
||||
@ -434,7 +476,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
|
||||
size_t len)
|
||||
{
|
||||
u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num;
|
||||
struct ieee802_11_elems elems = { 0 };
|
||||
u8 dialog_token;
|
||||
int ies_len;
|
||||
|
||||
/* extract session parameters from addba request frame */
|
||||
dialog_token = mgmt->u.action.u.addba_req.dialog_token;
|
||||
@ -447,9 +491,19 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
|
||||
tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
|
||||
buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
|
||||
|
||||
ies_len = len - offsetof(struct ieee80211_mgmt,
|
||||
u.action.u.addba_req.variable);
|
||||
if (ies_len) {
|
||||
ieee802_11_parse_elems(mgmt->u.action.u.addba_req.variable,
|
||||
ies_len, true, &elems, mgmt->bssid, NULL);
|
||||
if (elems.parse_error)
|
||||
return;
|
||||
}
|
||||
|
||||
__ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
|
||||
start_seq_num, ba_policy, tid,
|
||||
buf_size, true, false);
|
||||
buf_size, true, false,
|
||||
elems.addba_ext_ie);
|
||||
}
|
||||
|
||||
void ieee80211_manage_rx_ba_offl(struct ieee80211_vif *vif,
|
||||
|
@ -980,7 +980,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
|
||||
BSS_CHANGED_SSID |
|
||||
BSS_CHANGED_P2P_PS |
|
||||
BSS_CHANGED_TXPOWER |
|
||||
BSS_CHANGED_TWT;
|
||||
BSS_CHANGED_TWT |
|
||||
BSS_CHANGED_HE_OBSS_PD;
|
||||
int err;
|
||||
int prev_beacon_int;
|
||||
|
||||
@ -1051,6 +1052,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));
|
||||
|
||||
sdata->vif.bss_conf.ssid_len = params->ssid_len;
|
||||
if (params->ssid_len)
|
||||
@ -1543,7 +1546,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
|
||||
if (ether_addr_equal(mac, sdata->vif.addr))
|
||||
return -EINVAL;
|
||||
|
||||
if (is_multicast_ether_addr(mac))
|
||||
if (!is_valid_ether_addr(mac))
|
||||
return -EINVAL;
|
||||
|
||||
sta = sta_info_alloc(sdata, mac, GFP_KERNEL);
|
||||
|
@ -271,8 +271,7 @@ static const char *hw_flag_names[] = {
|
||||
FLAG(TX_STATUS_NO_AMPDU_LEN),
|
||||
FLAG(SUPPORTS_MULTI_BSSID),
|
||||
FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
|
||||
FLAG(EXT_KEY_ID_NATIVE),
|
||||
FLAG(NO_AMPDU_KEYBORDER_SUPPORT),
|
||||
FLAG(AMPDU_KEYBORDER_SUPPORT),
|
||||
#undef FLAG
|
||||
};
|
||||
|
||||
|
@ -692,14 +692,16 @@ static inline int drv_remain_on_channel(struct ieee80211_local *local,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int drv_cancel_remain_on_channel(struct ieee80211_local *local)
|
||||
static inline int
|
||||
drv_cancel_remain_on_channel(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
trace_drv_cancel_remain_on_channel(local);
|
||||
ret = local->ops->cancel_remain_on_channel(&local->hw);
|
||||
trace_drv_cancel_remain_on_channel(local, sdata);
|
||||
ret = local->ops->cancel_remain_on_channel(&local->hw, &sdata->vif);
|
||||
trace_drv_return_int(local, ret);
|
||||
|
||||
return ret;
|
||||
|
@ -50,3 +50,42 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
he_cap->has_he = true;
|
||||
}
|
||||
|
||||
void
|
||||
ieee80211_he_op_ie_to_bss_conf(struct ieee80211_vif *vif,
|
||||
const struct ieee80211_he_operation *he_op_ie_elem)
|
||||
{
|
||||
struct ieee80211_he_operation *he_operation =
|
||||
&vif->bss_conf.he_operation;
|
||||
|
||||
if (!he_op_ie_elem) {
|
||||
memset(he_operation, 0, sizeof(*he_operation));
|
||||
return;
|
||||
}
|
||||
|
||||
vif->bss_conf.he_operation = *he_op_ie_elem;
|
||||
}
|
||||
|
||||
void
|
||||
ieee80211_he_spr_ie_to_bss_conf(struct ieee80211_vif *vif,
|
||||
const struct ieee80211_he_spr *he_spr_ie_elem)
|
||||
{
|
||||
struct ieee80211_he_obss_pd *he_obss_pd =
|
||||
&vif->bss_conf.he_obss_pd;
|
||||
const u8 *data = he_spr_ie_elem->optional;
|
||||
|
||||
memset(he_obss_pd, 0, sizeof(*he_obss_pd));
|
||||
|
||||
if (!he_spr_ie_elem)
|
||||
return;
|
||||
|
||||
if (he_spr_ie_elem->he_sr_control &
|
||||
IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT)
|
||||
data++;
|
||||
if (he_spr_ie_elem->he_sr_control &
|
||||
IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT) {
|
||||
he_obss_pd->max_offset = *data++;
|
||||
he_obss_pd->min_offset = *data++;
|
||||
he_obss_pd->enable = true;
|
||||
}
|
||||
}
|
||||
|
@ -359,7 +359,7 @@ void ieee80211_ba_session_work(struct work_struct *work)
|
||||
sta->ampdu_mlme.tid_rx_manage_offl))
|
||||
___ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid,
|
||||
IEEE80211_MAX_AMPDU_BUF_HT,
|
||||
false, true);
|
||||
false, true, NULL);
|
||||
|
||||
if (test_and_clear_bit(tid + IEEE80211_NUM_TIDS,
|
||||
sta->ampdu_mlme.tid_rx_manage_offl))
|
||||
|
@ -1480,6 +1480,7 @@ struct ieee802_11_elems {
|
||||
const struct ieee80211_meshconf_ie *mesh_config;
|
||||
const u8 *he_cap;
|
||||
const struct ieee80211_he_operation *he_operation;
|
||||
const struct ieee80211_he_spr *he_spr;
|
||||
const struct ieee80211_mu_edca_param_set *mu_edca_param_set;
|
||||
const u8 *uora_element;
|
||||
const u8 *mesh_id;
|
||||
@ -1506,6 +1507,7 @@ struct ieee802_11_elems {
|
||||
u8 max_bssid_indicator;
|
||||
u8 dtim_count;
|
||||
u8 dtim_period;
|
||||
const struct ieee80211_addba_ext_ie *addba_ext_ie;
|
||||
|
||||
/* length of them, respectively */
|
||||
u8 ext_capab_len;
|
||||
@ -1767,7 +1769,8 @@ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, u32 info_flags);
|
||||
void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb,
|
||||
struct ieee80211_supported_band *sband,
|
||||
int retry_count, int shift, bool send_to_cooked);
|
||||
int retry_count, int shift, bool send_to_cooked,
|
||||
struct ieee80211_tx_status *status);
|
||||
|
||||
void ieee80211_check_fast_xmit(struct sta_info *sta);
|
||||
void ieee80211_check_fast_xmit_all(struct ieee80211_local *local);
|
||||
@ -1804,7 +1807,8 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
|
||||
void ___ieee80211_start_rx_ba_session(struct sta_info *sta,
|
||||
u8 dialog_token, u16 timeout,
|
||||
u16 start_seq_num, u16 ba_policy, u16 tid,
|
||||
u16 buf_size, bool tx, bool auto_seq);
|
||||
u16 buf_size, bool tx, bool auto_seq,
|
||||
const struct ieee80211_addba_ext_ie *addbaext);
|
||||
void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
|
||||
enum ieee80211_agg_stop_reason reason);
|
||||
void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
|
||||
@ -1869,6 +1873,13 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_supported_band *sband,
|
||||
const u8 *he_cap_ie, u8 he_cap_len,
|
||||
struct sta_info *sta);
|
||||
void
|
||||
ieee80211_he_spr_ie_to_bss_conf(struct ieee80211_vif *vif,
|
||||
const struct ieee80211_he_spr *he_spr_ie_elem);
|
||||
|
||||
void
|
||||
ieee80211_he_op_ie_to_bss_conf(struct ieee80211_vif *vif,
|
||||
const struct ieee80211_he_operation *he_op_ie_elem);
|
||||
|
||||
/* Spectrum management */
|
||||
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
||||
@ -2133,9 +2144,11 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
|
||||
u32 cap);
|
||||
u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
|
||||
const struct cfg80211_chan_def *chandef);
|
||||
u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype);
|
||||
u8 *ieee80211_ie_build_he_cap(u8 *pos,
|
||||
const struct ieee80211_sta_he_cap *he_cap,
|
||||
u8 *end);
|
||||
u8 *ieee80211_ie_build_he_oper(u8 *pos);
|
||||
int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
|
||||
const struct ieee80211_supported_band *sband,
|
||||
const u8 *srates, int srates_len, u32 *rates);
|
||||
|
@ -270,7 +270,7 @@ int ieee80211_set_tx_key(struct ieee80211_key *key)
|
||||
|
||||
sta->ptk_idx = key->conf.keyidx;
|
||||
|
||||
if (ieee80211_hw_check(&local->hw, NO_AMPDU_KEYBORDER_SUPPORT))
|
||||
if (!ieee80211_hw_check(&local->hw, AMPDU_KEYBORDER_SUPPORT))
|
||||
clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
|
||||
ieee80211_check_fast_xmit(sta);
|
||||
|
||||
@ -290,15 +290,15 @@ static void ieee80211_pairwise_rekey(struct ieee80211_key *old,
|
||||
/* Extended Key ID key install, initial one or rekey */
|
||||
|
||||
if (sta->ptk_idx != INVALID_PTK_KEYIDX &&
|
||||
ieee80211_hw_check(&local->hw,
|
||||
NO_AMPDU_KEYBORDER_SUPPORT)) {
|
||||
!ieee80211_hw_check(&local->hw, AMPDU_KEYBORDER_SUPPORT)) {
|
||||
/* Aggregation Sessions with Extended Key ID must not
|
||||
* mix MPDUs with different keyIDs within one A-MPDU.
|
||||
* Tear down any running Tx aggregation and all new
|
||||
* Rx/Tx aggregation request during rekey if the driver
|
||||
* asks us to do so. (Blocking Tx only would be
|
||||
* sufficient but WLAN_STA_BLOCK_BA gets the job done
|
||||
* for the few ms we need it.)
|
||||
* Tear down running Tx aggregation sessions and block
|
||||
* new Rx/Tx aggregation requests during rekey to
|
||||
* ensure there are no A-MPDUs when the driver is not
|
||||
* supporting A-MPDU key borders. (Blocking Tx only
|
||||
* would be sufficient but WLAN_STA_BLOCK_BA gets the
|
||||
* job done for the few ms we need it.)
|
||||
*/
|
||||
set_sta_flag(sta, WLAN_STA_BLOCK_BA);
|
||||
mutex_lock(&sta->ampdu_mlme.mtx);
|
||||
|
@ -1048,21 +1048,15 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable Extended Key IDs when driver allowed it, or when it
|
||||
* supports neither HW crypto nor A-MPDUs
|
||||
/* Mac80211 and therefore all drivers using SW crypto only
|
||||
* are able to handle PTK rekeys and Extended Key ID.
|
||||
*/
|
||||
if ((!local->ops->set_key &&
|
||||
!ieee80211_hw_check(hw, AMPDU_AGGREGATION)) ||
|
||||
ieee80211_hw_check(&local->hw, EXT_KEY_ID_NATIVE))
|
||||
wiphy_ext_feature_set(local->hw.wiphy,
|
||||
NL80211_EXT_FEATURE_EXT_KEY_ID);
|
||||
|
||||
/* Mac80211 and therefore all cards only using SW crypto are able to
|
||||
* handle PTK rekeys correctly
|
||||
*/
|
||||
if (!local->ops->set_key)
|
||||
if (!local->ops->set_key) {
|
||||
wiphy_ext_feature_set(local->hw.wiphy,
|
||||
NL80211_EXT_FEATURE_CAN_REPLACE_PTK0);
|
||||
wiphy_ext_feature_set(local->hw.wiphy,
|
||||
NL80211_EXT_FEATURE_EXT_KEY_ID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate scan IE length -- we need this to alloc
|
||||
|
@ -532,6 +532,61 @@ int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mesh_add_he_cap_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, u8 ie_len)
|
||||
{
|
||||
const struct ieee80211_sta_he_cap *he_cap;
|
||||
struct ieee80211_supported_band *sband;
|
||||
u8 *pos;
|
||||
|
||||
sband = ieee80211_get_sband(sdata);
|
||||
if (!sband)
|
||||
return -EINVAL;
|
||||
|
||||
he_cap = ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT);
|
||||
|
||||
if (!he_cap ||
|
||||
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 0;
|
||||
|
||||
if (skb_tailroom(skb) < ie_len)
|
||||
return -ENOMEM;
|
||||
|
||||
pos = skb_put(skb, ie_len);
|
||||
ieee80211_ie_build_he_cap(pos, he_cap, pos + ie_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mesh_add_he_oper_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
const struct ieee80211_sta_he_cap *he_cap;
|
||||
struct ieee80211_supported_band *sband;
|
||||
u8 *pos;
|
||||
|
||||
sband = ieee80211_get_sband(sdata);
|
||||
if (!sband)
|
||||
return -EINVAL;
|
||||
|
||||
he_cap = ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_MESH_POINT);
|
||||
if (!he_cap ||
|
||||
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 0;
|
||||
|
||||
if (skb_tailroom(skb) < 2 + 1 + sizeof(struct ieee80211_he_operation))
|
||||
return -ENOMEM;
|
||||
|
||||
pos = skb_put(skb, 2 + 1 + sizeof(struct ieee80211_he_operation));
|
||||
ieee80211_ie_build_he_oper(pos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ieee80211_mesh_path_timer(struct timer_list *t)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata =
|
||||
@ -677,6 +732,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct mesh_csa_settings *csa;
|
||||
enum nl80211_band band;
|
||||
u8 ie_len_he_cap;
|
||||
u8 *pos;
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
int hdr_len = offsetofend(struct ieee80211_mgmt, u.beacon);
|
||||
@ -687,6 +743,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
|
||||
band = chanctx_conf->def.chan->band;
|
||||
rcu_read_unlock();
|
||||
|
||||
ie_len_he_cap = ieee80211_ie_len_he_cap(sdata,
|
||||
NL80211_IFTYPE_MESH_POINT);
|
||||
head_len = hdr_len +
|
||||
2 + /* NULL SSID */
|
||||
/* Channel Switch Announcement */
|
||||
@ -706,6 +764,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
|
||||
2 + sizeof(__le16) + /* awake window */
|
||||
2 + sizeof(struct ieee80211_vht_cap) +
|
||||
2 + sizeof(struct ieee80211_vht_operation) +
|
||||
ie_len_he_cap +
|
||||
2 + 1 + sizeof(struct ieee80211_he_operation) +
|
||||
ifmsh->ie_len;
|
||||
|
||||
bcn = kzalloc(sizeof(*bcn) + head_len + tail_len, GFP_KERNEL);
|
||||
@ -823,6 +883,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
|
||||
mesh_add_awake_window_ie(sdata, skb) ||
|
||||
mesh_add_vht_cap_ie(sdata, skb) ||
|
||||
mesh_add_vht_oper_ie(sdata, skb) ||
|
||||
mesh_add_he_cap_ie(sdata, skb, ie_len_he_cap) ||
|
||||
mesh_add_he_oper_ie(sdata, skb) ||
|
||||
mesh_add_vendor_ies(sdata, skb))
|
||||
goto out_free;
|
||||
|
||||
|
@ -218,6 +218,10 @@ int mesh_add_vht_cap_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb);
|
||||
int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb);
|
||||
int mesh_add_he_cap_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, u8 ie_len);
|
||||
int mesh_add_he_oper_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb);
|
||||
void mesh_rmc_free(struct ieee80211_sub_if_data *sdata);
|
||||
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211s_init(void);
|
||||
|
@ -218,9 +218,12 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
|
||||
bool include_plid = false;
|
||||
u16 peering_proto = 0;
|
||||
u8 *pos, ie_len = 4;
|
||||
u8 ie_len_he_cap;
|
||||
int hdr_len = offsetofend(struct ieee80211_mgmt, u.action.u.self_prot);
|
||||
int err = -ENOMEM;
|
||||
|
||||
ie_len_he_cap = ieee80211_ie_len_he_cap(sdata,
|
||||
NL80211_IFTYPE_MESH_POINT);
|
||||
skb = dev_alloc_skb(local->tx_headroom +
|
||||
hdr_len +
|
||||
2 + /* capability info */
|
||||
@ -233,6 +236,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
|
||||
2 + sizeof(struct ieee80211_ht_operation) +
|
||||
2 + sizeof(struct ieee80211_vht_cap) +
|
||||
2 + sizeof(struct ieee80211_vht_operation) +
|
||||
ie_len_he_cap +
|
||||
2 + 1 + sizeof(struct ieee80211_he_operation) +
|
||||
2 + 8 + /* peering IE */
|
||||
sdata->u.mesh.ie_len);
|
||||
if (!skb)
|
||||
@ -321,7 +326,9 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
|
||||
if (mesh_add_ht_cap_ie(sdata, skb) ||
|
||||
mesh_add_ht_oper_ie(sdata, skb) ||
|
||||
mesh_add_vht_cap_ie(sdata, skb) ||
|
||||
mesh_add_vht_oper_ie(sdata, skb))
|
||||
mesh_add_vht_oper_ie(sdata, skb) ||
|
||||
mesh_add_he_cap_ie(sdata, skb, ie_len_he_cap) ||
|
||||
mesh_add_he_oper_ie(sdata, skb))
|
||||
goto free;
|
||||
}
|
||||
|
||||
@ -433,6 +440,9 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
|
||||
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
|
||||
elems->vht_cap_elem, sta);
|
||||
|
||||
ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband, elems->he_cap,
|
||||
elems->he_cap_len, sta);
|
||||
|
||||
if (bw != sta->sta.bandwidth)
|
||||
changed |= IEEE80211_RC_BW_CHANGED;
|
||||
|
||||
|
@ -2512,7 +2512,10 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
|
||||
|
||||
if (ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) {
|
||||
ifmgd->nullfunc_failed = false;
|
||||
ieee80211_send_nullfunc(sdata->local, sdata, false);
|
||||
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
|
||||
ifmgd->probe_send_count--;
|
||||
else
|
||||
ieee80211_send_nullfunc(sdata->local, sdata, false);
|
||||
} else {
|
||||
int ssid_len;
|
||||
|
||||
@ -3381,6 +3384,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
||||
if (elems.uora_element)
|
||||
bss_conf->uora_ocw_range = elems.uora_element[0];
|
||||
|
||||
ieee80211_he_op_ie_to_bss_conf(&sdata->vif, elems.he_operation);
|
||||
ieee80211_he_spr_ie_to_bss_conf(&sdata->vif, elems.he_spr);
|
||||
/* TODO: OPEN: what happens if BSS color disable is set? */
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
|
||||
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
|
||||
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright (C) 2019 Intel Corporation
|
||||
*/
|
||||
#include <linux/export.h>
|
||||
#include <net/mac80211.h>
|
||||
@ -732,7 +733,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
|
||||
}
|
||||
|
||||
if (local->ops->remain_on_channel) {
|
||||
ret = drv_cancel_remain_on_channel(local);
|
||||
ret = drv_cancel_remain_on_channel(local, roc->sdata);
|
||||
if (WARN_ON_ONCE(ret)) {
|
||||
mutex_unlock(&local->mtx);
|
||||
return ret;
|
||||
@ -991,7 +992,7 @@ void ieee80211_roc_purge(struct ieee80211_local *local,
|
||||
if (roc->started) {
|
||||
if (local->ops->remain_on_channel) {
|
||||
/* can race, so ignore return value */
|
||||
drv_cancel_remain_on_channel(local);
|
||||
drv_cancel_remain_on_channel(local, sdata);
|
||||
ieee80211_roc_notify_destroy(roc);
|
||||
} else {
|
||||
roc->abort = true;
|
||||
|
@ -60,15 +60,6 @@ static inline void rate_control_add_sta_debugfs(struct sta_info *sta)
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void rate_control_remove_sta_debugfs(struct sta_info *sta)
|
||||
{
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
struct rate_control_ref *ref = sta->rate_ctrl;
|
||||
if (ref && ref->ops->remove_sta_debugfs)
|
||||
ref->ops->remove_sta_debugfs(ref->priv, sta->rate_ctrl_priv);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ieee80211_check_rate_mask(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
/* Get a reference to the rate control algorithm. If `name' is NULL, get the
|
||||
|
@ -1065,7 +1065,6 @@ static void __sta_info_destroy_part2(struct sta_info *sta)
|
||||
cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL);
|
||||
kfree(sinfo);
|
||||
|
||||
rate_control_remove_sta_debugfs(sta);
|
||||
ieee80211_sta_debugfs_remove(sta);
|
||||
|
||||
cleanup_single_sta(sta);
|
||||
|
@ -254,7 +254,8 @@ static void ieee80211_set_bar_pending(struct sta_info *sta, u8 tid, u16 ssn)
|
||||
tid_tx->bar_pending = true;
|
||||
}
|
||||
|
||||
static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info)
|
||||
static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info,
|
||||
struct ieee80211_tx_status *status)
|
||||
{
|
||||
int len = sizeof(struct ieee80211_radiotap_header);
|
||||
|
||||
@ -272,7 +273,14 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info)
|
||||
|
||||
/* IEEE80211_RADIOTAP_MCS
|
||||
* IEEE80211_RADIOTAP_VHT */
|
||||
if (info->status.rates[0].idx >= 0) {
|
||||
if (status && status->rate) {
|
||||
if (status->rate->flags & RATE_INFO_FLAGS_MCS)
|
||||
len += 3;
|
||||
else if (status->rate->flags & RATE_INFO_FLAGS_VHT_MCS)
|
||||
len = ALIGN(len, 2) + 12;
|
||||
else if (status->rate->flags & RATE_INFO_FLAGS_HE_MCS)
|
||||
len = ALIGN(len, 2) + 12;
|
||||
} else if (info->status.rates[0].idx >= 0) {
|
||||
if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS)
|
||||
len += 3;
|
||||
else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS)
|
||||
@ -286,12 +294,14 @@ static void
|
||||
ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
|
||||
struct ieee80211_supported_band *sband,
|
||||
struct sk_buff *skb, int retry_count,
|
||||
int rtap_len, int shift)
|
||||
int rtap_len, int shift,
|
||||
struct ieee80211_tx_status *status)
|
||||
{
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
struct ieee80211_radiotap_header *rthdr;
|
||||
unsigned char *pos;
|
||||
u16 legacy_rate = 0;
|
||||
u16 txflags;
|
||||
|
||||
rthdr = skb_push(skb, rtap_len);
|
||||
@ -310,14 +320,22 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
|
||||
*/
|
||||
|
||||
/* IEEE80211_RADIOTAP_RATE */
|
||||
if (info->status.rates[0].idx >= 0 &&
|
||||
!(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
|
||||
IEEE80211_TX_RC_VHT_MCS))) {
|
||||
u16 rate;
|
||||
|
||||
if (status && status->rate && !(status->rate->flags &
|
||||
(RATE_INFO_FLAGS_MCS |
|
||||
RATE_INFO_FLAGS_60G |
|
||||
RATE_INFO_FLAGS_VHT_MCS |
|
||||
RATE_INFO_FLAGS_HE_MCS)))
|
||||
legacy_rate = status->rate->legacy;
|
||||
else if (info->status.rates[0].idx >= 0 &&
|
||||
!(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
|
||||
IEEE80211_TX_RC_VHT_MCS)))
|
||||
legacy_rate =
|
||||
sband->bitrates[info->status.rates[0].idx].bitrate;
|
||||
|
||||
if (legacy_rate) {
|
||||
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
|
||||
rate = sband->bitrates[info->status.rates[0].idx].bitrate;
|
||||
*pos = DIV_ROUND_UP(rate, 5 * (1 << shift));
|
||||
*pos = DIV_ROUND_UP(legacy_rate, 5 * (1 << shift));
|
||||
/* padding for tx flags */
|
||||
pos += 2;
|
||||
}
|
||||
@ -341,7 +359,139 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
|
||||
*pos = retry_count;
|
||||
pos++;
|
||||
|
||||
if (info->status.rates[0].idx < 0)
|
||||
if (status && status->rate &&
|
||||
(status->rate->flags & RATE_INFO_FLAGS_MCS)) {
|
||||
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS);
|
||||
pos[0] = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
|
||||
IEEE80211_RADIOTAP_MCS_HAVE_GI |
|
||||
IEEE80211_RADIOTAP_MCS_HAVE_BW;
|
||||
if (status->rate->flags & RATE_INFO_FLAGS_SHORT_GI)
|
||||
pos[1] |= IEEE80211_RADIOTAP_MCS_SGI;
|
||||
if (status->rate->bw == RATE_INFO_BW_40)
|
||||
pos[1] |= IEEE80211_RADIOTAP_MCS_BW_40;
|
||||
pos[2] = status->rate->mcs;
|
||||
pos += 3;
|
||||
} else if (status && status->rate &&
|
||||
(status->rate->flags & RATE_INFO_FLAGS_VHT_MCS)) {
|
||||
u16 known = local->hw.radiotap_vht_details &
|
||||
(IEEE80211_RADIOTAP_VHT_KNOWN_GI |
|
||||
IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH);
|
||||
|
||||
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
|
||||
|
||||
/* required alignment from rthdr */
|
||||
pos = (u8 *)rthdr + ALIGN(pos - (u8 *)rthdr, 2);
|
||||
|
||||
/* u16 known - IEEE80211_RADIOTAP_VHT_KNOWN_* */
|
||||
put_unaligned_le16(known, pos);
|
||||
pos += 2;
|
||||
|
||||
/* u8 flags - IEEE80211_RADIOTAP_VHT_FLAG_* */
|
||||
if (status->rate->flags & RATE_INFO_FLAGS_SHORT_GI)
|
||||
*pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
|
||||
pos++;
|
||||
|
||||
/* u8 bandwidth */
|
||||
switch (status->rate->bw) {
|
||||
case RATE_INFO_BW_160:
|
||||
*pos = 11;
|
||||
break;
|
||||
case RATE_INFO_BW_80:
|
||||
*pos = 2;
|
||||
break;
|
||||
case RATE_INFO_BW_40:
|
||||
*pos = 1;
|
||||
break;
|
||||
default:
|
||||
*pos = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* u8 mcs_nss[4] */
|
||||
*pos = (status->rate->mcs << 4) | status->rate->nss;
|
||||
pos += 4;
|
||||
|
||||
/* u8 coding */
|
||||
pos++;
|
||||
/* u8 group_id */
|
||||
pos++;
|
||||
/* u16 partial_aid */
|
||||
pos += 2;
|
||||
} else if (status && status->rate &&
|
||||
(status->rate->flags & RATE_INFO_FLAGS_HE_MCS)) {
|
||||
struct ieee80211_radiotap_he *he;
|
||||
|
||||
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_HE);
|
||||
|
||||
/* required alignment from rthdr */
|
||||
pos = (u8 *)rthdr + ALIGN(pos - (u8 *)rthdr, 2);
|
||||
he = (struct ieee80211_radiotap_he *)pos;
|
||||
|
||||
he->data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_SU |
|
||||
IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN |
|
||||
IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN |
|
||||
IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN);
|
||||
|
||||
he->data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN);
|
||||
|
||||
#define HE_PREP(f, val) le16_encode_bits(val, IEEE80211_RADIOTAP_HE_##f)
|
||||
|
||||
he->data6 |= HE_PREP(DATA6_NSTS, status->rate->nss);
|
||||
|
||||
#define CHECK_GI(s) \
|
||||
BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA5_GI_##s != \
|
||||
(int)NL80211_RATE_INFO_HE_GI_##s)
|
||||
|
||||
CHECK_GI(0_8);
|
||||
CHECK_GI(1_6);
|
||||
CHECK_GI(3_2);
|
||||
|
||||
he->data3 |= HE_PREP(DATA3_DATA_MCS, status->rate->mcs);
|
||||
he->data3 |= HE_PREP(DATA3_DATA_DCM, status->rate->he_dcm);
|
||||
|
||||
he->data5 |= HE_PREP(DATA5_GI, status->rate->he_gi);
|
||||
|
||||
switch (status->rate->bw) {
|
||||
case RATE_INFO_BW_20:
|
||||
he->data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
|
||||
IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ);
|
||||
break;
|
||||
case RATE_INFO_BW_40:
|
||||
he->data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
|
||||
IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ);
|
||||
break;
|
||||
case RATE_INFO_BW_80:
|
||||
he->data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
|
||||
IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ);
|
||||
break;
|
||||
case RATE_INFO_BW_160:
|
||||
he->data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
|
||||
IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ);
|
||||
break;
|
||||
case RATE_INFO_BW_HE_RU:
|
||||
#define CHECK_RU_ALLOC(s) \
|
||||
BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_##s##T != \
|
||||
NL80211_RATE_INFO_HE_RU_ALLOC_##s + 4)
|
||||
|
||||
CHECK_RU_ALLOC(26);
|
||||
CHECK_RU_ALLOC(52);
|
||||
CHECK_RU_ALLOC(106);
|
||||
CHECK_RU_ALLOC(242);
|
||||
CHECK_RU_ALLOC(484);
|
||||
CHECK_RU_ALLOC(996);
|
||||
CHECK_RU_ALLOC(2x996);
|
||||
|
||||
he->data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
|
||||
status->rate->he_ru_alloc + 4);
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(1, "Invalid SU BW %d\n", status->rate->bw);
|
||||
}
|
||||
|
||||
pos += sizeof(struct ieee80211_radiotap_he);
|
||||
}
|
||||
|
||||
if ((status && status->rate) || info->status.rates[0].idx < 0)
|
||||
return;
|
||||
|
||||
/* IEEE80211_RADIOTAP_MCS
|
||||
@ -645,7 +795,8 @@ static int ieee80211_tx_get_rates(struct ieee80211_hw *hw,
|
||||
|
||||
void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb,
|
||||
struct ieee80211_supported_band *sband,
|
||||
int retry_count, int shift, bool send_to_cooked)
|
||||
int retry_count, int shift, bool send_to_cooked,
|
||||
struct ieee80211_tx_status *status)
|
||||
{
|
||||
struct sk_buff *skb2;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
@ -654,14 +805,14 @@ void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb,
|
||||
int rtap_len;
|
||||
|
||||
/* send frame to monitor interfaces now */
|
||||
rtap_len = ieee80211_tx_radiotap_len(info);
|
||||
rtap_len = ieee80211_tx_radiotap_len(info, status);
|
||||
if (WARN_ON_ONCE(skb_headroom(skb) < rtap_len)) {
|
||||
pr_err("ieee80211_tx_status: headroom too small\n");
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
ieee80211_add_tx_radiotap_header(local, sband, skb, retry_count,
|
||||
rtap_len, shift);
|
||||
rtap_len, shift, status);
|
||||
|
||||
/* XXX: is this sufficient for BPF? */
|
||||
skb_reset_mac_header(skb);
|
||||
@ -901,7 +1052,8 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw,
|
||||
}
|
||||
|
||||
/* send to monitor interfaces */
|
||||
ieee80211_tx_monitor(local, skb, sband, retry_count, shift, send_to_cooked);
|
||||
ieee80211_tx_monitor(local, skb, sband, retry_count, shift,
|
||||
send_to_cooked, status);
|
||||
}
|
||||
|
||||
void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
||||
|
@ -1242,9 +1242,10 @@ TRACE_EVENT(drv_remain_on_channel,
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(local_only_evt, drv_cancel_remain_on_channel,
|
||||
TP_PROTO(struct ieee80211_local *local),
|
||||
TP_ARGS(local)
|
||||
DEFINE_EVENT(local_sdata_evt, drv_cancel_remain_on_channel,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata),
|
||||
TP_ARGS(local, sdata)
|
||||
);
|
||||
|
||||
TRACE_EVENT(drv_set_ringparam,
|
||||
|
@ -3546,6 +3546,8 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
|
||||
ieee80211_tx_result r;
|
||||
struct ieee80211_vif *vif = txq->vif;
|
||||
|
||||
WARN_ON_ONCE(softirq_count() == 0);
|
||||
|
||||
begin:
|
||||
spin_lock_bh(&fq->lock);
|
||||
|
||||
@ -4647,7 +4649,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
|
||||
if (!sband)
|
||||
return bcn;
|
||||
|
||||
ieee80211_tx_monitor(hw_to_local(hw), copy, sband, 1, shift, false);
|
||||
ieee80211_tx_monitor(hw_to_local(hw), copy, sband, 1, shift, false,
|
||||
NULL);
|
||||
|
||||
return bcn;
|
||||
}
|
||||
|
@ -1200,6 +1200,13 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
|
||||
|
||||
elems->cisco_dtpc_elem = pos;
|
||||
break;
|
||||
case WLAN_EID_ADDBA_EXT:
|
||||
if (elen != sizeof(struct ieee80211_addba_ext_ie)) {
|
||||
elem_parse_failed = true;
|
||||
break;
|
||||
}
|
||||
elems->addba_ext_ie = (void *)pos;
|
||||
break;
|
||||
case WLAN_EID_TIMEOUT_INTERVAL:
|
||||
if (elen >= sizeof(struct ieee80211_timeout_interval_ie))
|
||||
elems->timeout_int = (void *)pos;
|
||||
@ -1233,6 +1240,10 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
|
||||
WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION &&
|
||||
elen == 3) {
|
||||
elems->mbssid_config_ie = (void *)&pos[1];
|
||||
} else if (pos[0] == WLAN_EID_EXT_HE_SPR &&
|
||||
elen >= sizeof(*elems->he_spr) &&
|
||||
elen >= ieee80211_he_spr_size(&pos[1])) {
|
||||
elems->he_spr = (void *)&pos[1];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -2702,6 +2713,27 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
|
||||
return pos;
|
||||
}
|
||||
|
||||
u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype)
|
||||
{
|
||||
const struct ieee80211_sta_he_cap *he_cap;
|
||||
struct ieee80211_supported_band *sband;
|
||||
u8 n;
|
||||
|
||||
sband = ieee80211_get_sband(sdata);
|
||||
if (!sband)
|
||||
return 0;
|
||||
|
||||
he_cap = ieee80211_get_he_iftype_cap(sband, iftype);
|
||||
if (!he_cap)
|
||||
return 0;
|
||||
|
||||
n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem);
|
||||
return 2 + 1 +
|
||||
sizeof(he_cap->he_cap_elem) + n +
|
||||
ieee80211_he_ppe_size(he_cap->ppe_thres[0],
|
||||
he_cap->he_cap_elem.phy_cap_info);
|
||||
}
|
||||
|
||||
u8 *ieee80211_ie_build_he_cap(u8 *pos,
|
||||
const struct ieee80211_sta_he_cap *he_cap,
|
||||
u8 *end)
|
||||
@ -2891,6 +2923,34 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
|
||||
return pos + sizeof(struct ieee80211_vht_operation);
|
||||
}
|
||||
|
||||
u8 *ieee80211_ie_build_he_oper(u8 *pos)
|
||||
{
|
||||
struct ieee80211_he_operation *he_oper;
|
||||
u32 he_oper_params;
|
||||
|
||||
*pos++ = WLAN_EID_EXTENSION;
|
||||
*pos++ = 1 + sizeof(struct ieee80211_he_operation);
|
||||
*pos++ = WLAN_EID_EXT_HE_OPERATION;
|
||||
|
||||
he_oper_params = 0;
|
||||
he_oper_params |= u32_encode_bits(1023, /* disabled */
|
||||
IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK);
|
||||
he_oper_params |= u32_encode_bits(1,
|
||||
IEEE80211_HE_OPERATION_ER_SU_DISABLE);
|
||||
he_oper_params |= u32_encode_bits(1,
|
||||
IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED);
|
||||
|
||||
he_oper = (struct ieee80211_he_operation *)pos;
|
||||
he_oper->he_oper_params = cpu_to_le32(he_oper_params);
|
||||
|
||||
/* don't require special HE peer rates */
|
||||
he_oper->he_mcs_nss_set = cpu_to_le16(0xffff);
|
||||
|
||||
/* TODO add VHT operational and 6GHz operational subelement? */
|
||||
|
||||
return pos + sizeof(struct ieee80211_vht_operation);
|
||||
}
|
||||
|
||||
bool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
|
@ -946,7 +946,8 @@ ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx)
|
||||
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
if (info->control.hw_key)
|
||||
if (info->control.hw_key &&
|
||||
!(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIE))
|
||||
return TX_CONTINUE;
|
||||
|
||||
if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
|
||||
@ -962,6 +963,9 @@ ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx)
|
||||
|
||||
bip_ipn_set64(mmie->sequence_number, pn64);
|
||||
|
||||
if (info->control.hw_key)
|
||||
return TX_CONTINUE;
|
||||
|
||||
bip_aad(skb, aad);
|
||||
|
||||
/*
|
||||
|
@ -217,6 +217,8 @@ config LIB80211_CRYPT_WEP
|
||||
|
||||
config LIB80211_CRYPT_CCMP
|
||||
tristate
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_CCM
|
||||
|
||||
config LIB80211_CRYPT_TKIP
|
||||
tristate
|
||||
|
@ -142,12 +142,10 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (rdev->wiphy.debugfsdir &&
|
||||
!debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
|
||||
rdev->wiphy.debugfsdir,
|
||||
rdev->wiphy.debugfsdir->d_parent,
|
||||
newname))
|
||||
pr_err("failed to rename debugfs dir to %s!\n", newname);
|
||||
if (rdev->wiphy.debugfsdir)
|
||||
debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
|
||||
rdev->wiphy.debugfsdir,
|
||||
rdev->wiphy.debugfsdir->d_parent, newname);
|
||||
|
||||
nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
|
||||
|
||||
@ -899,11 +897,8 @@ int wiphy_register(struct wiphy *wiphy)
|
||||
cfg80211_rdev_list_generation++;
|
||||
|
||||
/* add to debugfs */
|
||||
rdev->wiphy.debugfsdir =
|
||||
debugfs_create_dir(wiphy_name(&rdev->wiphy),
|
||||
ieee80211_debugfs_dir);
|
||||
if (IS_ERR(rdev->wiphy.debugfsdir))
|
||||
rdev->wiphy.debugfsdir = NULL;
|
||||
rdev->wiphy.debugfsdir = debugfs_create_dir(wiphy_name(&rdev->wiphy),
|
||||
ieee80211_debugfs_dir);
|
||||
|
||||
cfg80211_debugfs_rdev_add(rdev);
|
||||
nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
|
||||
|
@ -306,6 +306,8 @@ void ieee80211_set_bitrate_flags(struct wiphy *wiphy);
|
||||
void cfg80211_bss_expire(struct cfg80211_registered_device *rdev);
|
||||
void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
|
||||
unsigned long age_secs);
|
||||
void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
|
||||
struct ieee80211_channel *channel);
|
||||
|
||||
/* IBSS */
|
||||
int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <linux/ieee80211.h>
|
||||
|
||||
#include <linux/crypto.h>
|
||||
#include <crypto/aead.h>
|
||||
|
||||
#include <net/lib80211.h>
|
||||
|
||||
@ -48,20 +49,13 @@ struct lib80211_ccmp_data {
|
||||
|
||||
int key_idx;
|
||||
|
||||
struct crypto_cipher *tfm;
|
||||
struct crypto_aead *tfm;
|
||||
|
||||
/* scratch buffers for virt_to_page() (crypto API) */
|
||||
u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN],
|
||||
tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN];
|
||||
u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN];
|
||||
u8 tx_aad[2 * AES_BLOCK_LEN];
|
||||
u8 rx_aad[2 * AES_BLOCK_LEN];
|
||||
};
|
||||
|
||||
static inline void lib80211_ccmp_aes_encrypt(struct crypto_cipher *tfm,
|
||||
const u8 pt[16], u8 ct[16])
|
||||
{
|
||||
crypto_cipher_encrypt_one(tfm, ct, pt);
|
||||
}
|
||||
|
||||
static void *lib80211_ccmp_init(int key_idx)
|
||||
{
|
||||
struct lib80211_ccmp_data *priv;
|
||||
@ -71,7 +65,7 @@ static void *lib80211_ccmp_init(int key_idx)
|
||||
goto fail;
|
||||
priv->key_idx = key_idx;
|
||||
|
||||
priv->tfm = crypto_alloc_cipher("aes", 0, 0);
|
||||
priv->tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(priv->tfm)) {
|
||||
priv->tfm = NULL;
|
||||
goto fail;
|
||||
@ -82,7 +76,7 @@ static void *lib80211_ccmp_init(int key_idx)
|
||||
fail:
|
||||
if (priv) {
|
||||
if (priv->tfm)
|
||||
crypto_free_cipher(priv->tfm);
|
||||
crypto_free_aead(priv->tfm);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
@ -93,25 +87,16 @@ static void lib80211_ccmp_deinit(void *priv)
|
||||
{
|
||||
struct lib80211_ccmp_data *_priv = priv;
|
||||
if (_priv && _priv->tfm)
|
||||
crypto_free_cipher(_priv->tfm);
|
||||
crypto_free_aead(_priv->tfm);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
static inline void xor_block(u8 * b, u8 * a, size_t len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++)
|
||||
b[i] ^= a[i];
|
||||
}
|
||||
|
||||
static void ccmp_init_blocks(struct crypto_cipher *tfm,
|
||||
struct ieee80211_hdr *hdr,
|
||||
u8 * pn, size_t dlen, u8 * b0, u8 * auth, u8 * s0)
|
||||
static int ccmp_init_iv_and_aad(const struct ieee80211_hdr *hdr,
|
||||
const u8 *pn, u8 *iv, u8 *aad)
|
||||
{
|
||||
u8 *pos, qc = 0;
|
||||
size_t aad_len;
|
||||
int a4_included, qc_included;
|
||||
u8 aad[2 * AES_BLOCK_LEN];
|
||||
|
||||
a4_included = ieee80211_has_a4(hdr->frame_control);
|
||||
qc_included = ieee80211_is_data_qos(hdr->frame_control);
|
||||
@ -127,17 +112,19 @@ static void ccmp_init_blocks(struct crypto_cipher *tfm,
|
||||
aad_len += 2;
|
||||
}
|
||||
|
||||
/* CCM Initial Block:
|
||||
* Flag (Include authentication header, M=3 (8-octet MIC),
|
||||
* L=1 (2-octet Dlen))
|
||||
* Nonce: 0x00 | A2 | PN
|
||||
* Dlen */
|
||||
b0[0] = 0x59;
|
||||
b0[1] = qc;
|
||||
memcpy(b0 + 2, hdr->addr2, ETH_ALEN);
|
||||
memcpy(b0 + 8, pn, CCMP_PN_LEN);
|
||||
b0[14] = (dlen >> 8) & 0xff;
|
||||
b0[15] = dlen & 0xff;
|
||||
/* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
|
||||
* mode authentication are not allowed to collide, yet both are derived
|
||||
* from the same vector. We only set L := 1 here to indicate that the
|
||||
* data size can be represented in (L+1) bytes. The CCM layer will take
|
||||
* care of storing the data length in the top (L+1) bytes and setting
|
||||
* and clearing the other bits as is required to derive the two IVs.
|
||||
*/
|
||||
iv[0] = 0x1;
|
||||
|
||||
/* Nonce: QC | A2 | PN */
|
||||
iv[1] = qc;
|
||||
memcpy(iv + 2, hdr->addr2, ETH_ALEN);
|
||||
memcpy(iv + 8, pn, CCMP_PN_LEN);
|
||||
|
||||
/* AAD:
|
||||
* FC with bits 4..6 and 11..13 masked to zero; 14 is always one
|
||||
@ -147,31 +134,20 @@ static void ccmp_init_blocks(struct crypto_cipher *tfm,
|
||||
* QC (if present)
|
||||
*/
|
||||
pos = (u8 *) hdr;
|
||||
aad[0] = 0; /* aad_len >> 8 */
|
||||
aad[1] = aad_len & 0xff;
|
||||
aad[2] = pos[0] & 0x8f;
|
||||
aad[3] = pos[1] & 0xc7;
|
||||
memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN);
|
||||
aad[0] = pos[0] & 0x8f;
|
||||
aad[1] = pos[1] & 0xc7;
|
||||
memcpy(aad + 2, hdr->addr1, 3 * ETH_ALEN);
|
||||
pos = (u8 *) & hdr->seq_ctrl;
|
||||
aad[22] = pos[0] & 0x0f;
|
||||
aad[23] = 0; /* all bits masked */
|
||||
memset(aad + 24, 0, 8);
|
||||
aad[20] = pos[0] & 0x0f;
|
||||
aad[21] = 0; /* all bits masked */
|
||||
memset(aad + 22, 0, 8);
|
||||
if (a4_included)
|
||||
memcpy(aad + 24, hdr->addr4, ETH_ALEN);
|
||||
memcpy(aad + 22, hdr->addr4, ETH_ALEN);
|
||||
if (qc_included) {
|
||||
aad[a4_included ? 30 : 24] = qc;
|
||||
aad[a4_included ? 28 : 22] = qc;
|
||||
/* rest of QC masked */
|
||||
}
|
||||
|
||||
/* Start with the first block and AAD */
|
||||
lib80211_ccmp_aes_encrypt(tfm, b0, auth);
|
||||
xor_block(auth, aad, AES_BLOCK_LEN);
|
||||
lib80211_ccmp_aes_encrypt(tfm, auth, auth);
|
||||
xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
|
||||
lib80211_ccmp_aes_encrypt(tfm, auth, auth);
|
||||
b0[0] &= 0x07;
|
||||
b0[14] = b0[15] = 0;
|
||||
lib80211_ccmp_aes_encrypt(tfm, b0, s0);
|
||||
return aad_len;
|
||||
}
|
||||
|
||||
static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len,
|
||||
@ -214,13 +190,13 @@ static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len,
|
||||
static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct lib80211_ccmp_data *key = priv;
|
||||
int data_len, i, blocks, last, len;
|
||||
u8 *pos, *mic;
|
||||
struct ieee80211_hdr *hdr;
|
||||
u8 *b0 = key->tx_b0;
|
||||
u8 *b = key->tx_b;
|
||||
u8 *e = key->tx_e;
|
||||
u8 *s0 = key->tx_s0;
|
||||
struct aead_request *req;
|
||||
struct scatterlist sg[2];
|
||||
u8 *aad = key->tx_aad;
|
||||
u8 iv[AES_BLOCK_LEN];
|
||||
int len, data_len, aad_len;
|
||||
int ret;
|
||||
|
||||
if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len)
|
||||
return -1;
|
||||
@ -230,31 +206,28 @@ static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
pos = skb->data + hdr_len + CCMP_HDR_LEN;
|
||||
req = aead_request_alloc(key->tfm, GFP_ATOMIC);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = (struct ieee80211_hdr *)skb->data;
|
||||
ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0);
|
||||
aad_len = ccmp_init_iv_and_aad(hdr, key->tx_pn, iv, aad);
|
||||
|
||||
blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
|
||||
last = data_len % AES_BLOCK_LEN;
|
||||
skb_put(skb, CCMP_MIC_LEN);
|
||||
|
||||
for (i = 1; i <= blocks; i++) {
|
||||
len = (i == blocks && last) ? last : AES_BLOCK_LEN;
|
||||
/* Authentication */
|
||||
xor_block(b, pos, len);
|
||||
lib80211_ccmp_aes_encrypt(key->tfm, b, b);
|
||||
/* Encryption, with counter */
|
||||
b0[14] = (i >> 8) & 0xff;
|
||||
b0[15] = i & 0xff;
|
||||
lib80211_ccmp_aes_encrypt(key->tfm, b0, e);
|
||||
xor_block(pos, e, len);
|
||||
pos += len;
|
||||
}
|
||||
sg_init_table(sg, 2);
|
||||
sg_set_buf(&sg[0], aad, aad_len);
|
||||
sg_set_buf(&sg[1], skb->data + hdr_len + CCMP_HDR_LEN,
|
||||
data_len + CCMP_MIC_LEN);
|
||||
|
||||
mic = skb_put(skb, CCMP_MIC_LEN);
|
||||
for (i = 0; i < CCMP_MIC_LEN; i++)
|
||||
mic[i] = b[i] ^ s0[i];
|
||||
aead_request_set_callback(req, 0, NULL, NULL);
|
||||
aead_request_set_ad(req, aad_len);
|
||||
aead_request_set_crypt(req, sg, sg, data_len, iv);
|
||||
|
||||
return 0;
|
||||
ret = crypto_aead_encrypt(req);
|
||||
aead_request_free(req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -283,13 +256,13 @@ static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
struct lib80211_ccmp_data *key = priv;
|
||||
u8 keyidx, *pos;
|
||||
struct ieee80211_hdr *hdr;
|
||||
u8 *b0 = key->rx_b0;
|
||||
u8 *b = key->rx_b;
|
||||
u8 *a = key->rx_a;
|
||||
struct aead_request *req;
|
||||
struct scatterlist sg[2];
|
||||
u8 *aad = key->rx_aad;
|
||||
u8 iv[AES_BLOCK_LEN];
|
||||
u8 pn[6];
|
||||
int i, blocks, last, len;
|
||||
size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN;
|
||||
u8 *mic = skb->data + skb->len - CCMP_MIC_LEN;
|
||||
int aad_len, ret;
|
||||
size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN;
|
||||
|
||||
if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) {
|
||||
key->dot11RSNAStatsCCMPFormatErrors++;
|
||||
@ -337,28 +310,26 @@ static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
return -4;
|
||||
}
|
||||
|
||||
ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b);
|
||||
xor_block(mic, b, CCMP_MIC_LEN);
|
||||
req = aead_request_alloc(key->tfm, GFP_ATOMIC);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
|
||||
last = data_len % AES_BLOCK_LEN;
|
||||
aad_len = ccmp_init_iv_and_aad(hdr, pn, iv, aad);
|
||||
|
||||
for (i = 1; i <= blocks; i++) {
|
||||
len = (i == blocks && last) ? last : AES_BLOCK_LEN;
|
||||
/* Decrypt, with counter */
|
||||
b0[14] = (i >> 8) & 0xff;
|
||||
b0[15] = i & 0xff;
|
||||
lib80211_ccmp_aes_encrypt(key->tfm, b0, b);
|
||||
xor_block(pos, b, len);
|
||||
/* Authentication */
|
||||
xor_block(a, pos, len);
|
||||
lib80211_ccmp_aes_encrypt(key->tfm, a, a);
|
||||
pos += len;
|
||||
}
|
||||
sg_init_table(sg, 2);
|
||||
sg_set_buf(&sg[0], aad, aad_len);
|
||||
sg_set_buf(&sg[1], pos, data_len);
|
||||
|
||||
if (memcmp(mic, a, CCMP_MIC_LEN) != 0) {
|
||||
net_dbg_ratelimited("CCMP: decrypt failed: STA=%pM\n",
|
||||
hdr->addr2);
|
||||
aead_request_set_callback(req, 0, NULL, NULL);
|
||||
aead_request_set_ad(req, aad_len);
|
||||
aead_request_set_crypt(req, sg, sg, data_len, iv);
|
||||
|
||||
ret = crypto_aead_decrypt(req);
|
||||
aead_request_free(req);
|
||||
|
||||
if (ret) {
|
||||
net_dbg_ratelimited("CCMP: decrypt failed: STA=%pM (%d)\n",
|
||||
hdr->addr2, ret);
|
||||
key->dot11RSNAStatsCCMPDecryptErrors++;
|
||||
return -5;
|
||||
}
|
||||
@ -377,7 +348,7 @@ static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv)
|
||||
{
|
||||
struct lib80211_ccmp_data *data = priv;
|
||||
int keyidx;
|
||||
struct crypto_cipher *tfm = data->tfm;
|
||||
struct crypto_aead *tfm = data->tfm;
|
||||
|
||||
keyidx = data->key_idx;
|
||||
memset(data, 0, sizeof(*data));
|
||||
@ -394,7 +365,9 @@ static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv)
|
||||
data->rx_pn[4] = seq[1];
|
||||
data->rx_pn[5] = seq[0];
|
||||
}
|
||||
crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN);
|
||||
if (crypto_aead_setauthsize(data->tfm, CCMP_MIC_LEN) ||
|
||||
crypto_aead_setkey(data->tfm, data->key, CCMP_TK_LEN))
|
||||
return -1;
|
||||
} else if (len == 0)
|
||||
data->key_set = 0;
|
||||
else
|
||||
|
@ -281,7 +281,16 @@ nl80211_pmsr_attr_policy[NL80211_PMSR_ATTR_MAX + 1] = {
|
||||
NLA_POLICY_NESTED_ARRAY(nl80211_psmr_peer_attr_policy),
|
||||
};
|
||||
|
||||
static const struct nla_policy
|
||||
he_obss_pd_policy[NL80211_HE_OBSS_PD_ATTR_MAX + 1] = {
|
||||
[NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET] =
|
||||
NLA_POLICY_RANGE(NLA_U8, 1, 20),
|
||||
[NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET] =
|
||||
NLA_POLICY_RANGE(NLA_U8, 1, 20),
|
||||
};
|
||||
|
||||
const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
||||
[0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD },
|
||||
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
|
||||
.len = 20-1 },
|
||||
@ -574,6 +583,7 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
||||
[NL80211_ATTR_SAE_PASSWORD] = { .type = NLA_BINARY,
|
||||
.len = SAE_PASSWORD_MAX_LEN },
|
||||
[NL80211_ATTR_TWT_RESPONDER] = { .type = NLA_FLAG },
|
||||
[NL80211_ATTR_HE_OBSS_PD] = NLA_POLICY_NESTED(he_obss_pd_policy),
|
||||
};
|
||||
|
||||
/* policy for the key attributes */
|
||||
@ -749,17 +759,25 @@ int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
|
||||
int err;
|
||||
|
||||
if (!cb->args[0]) {
|
||||
struct nlattr **attrbuf;
|
||||
|
||||
attrbuf = kcalloc(NUM_NL80211_ATTR, sizeof(*attrbuf),
|
||||
GFP_KERNEL);
|
||||
if (!attrbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
err = nlmsg_parse_deprecated(cb->nlh,
|
||||
GENL_HDRLEN + nl80211_fam.hdrsize,
|
||||
genl_family_attrbuf(&nl80211_fam),
|
||||
nl80211_fam.maxattr,
|
||||
attrbuf, nl80211_fam.maxattr,
|
||||
nl80211_policy, NULL);
|
||||
if (err)
|
||||
if (err) {
|
||||
kfree(attrbuf);
|
||||
return err;
|
||||
}
|
||||
|
||||
*wdev = __cfg80211_wdev_from_attrs(
|
||||
sock_net(cb->skb->sk),
|
||||
genl_family_attrbuf(&nl80211_fam));
|
||||
*wdev = __cfg80211_wdev_from_attrs(sock_net(cb->skb->sk),
|
||||
attrbuf);
|
||||
kfree(attrbuf);
|
||||
if (IS_ERR(*wdev))
|
||||
return PTR_ERR(*wdev);
|
||||
*rdev = wiphy_to_rdev((*wdev)->wiphy);
|
||||
@ -2172,6 +2190,30 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
||||
rdev->wiphy.vht_capa_mod_mask))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
|
||||
rdev->wiphy.perm_addr))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (!is_zero_ether_addr(rdev->wiphy.addr_mask) &&
|
||||
nla_put(msg, NL80211_ATTR_MAC_MASK, ETH_ALEN,
|
||||
rdev->wiphy.addr_mask))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (rdev->wiphy.n_addresses > 1) {
|
||||
void *attr;
|
||||
|
||||
attr = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS);
|
||||
if (!attr)
|
||||
goto nla_put_failure;
|
||||
|
||||
for (i = 0; i < rdev->wiphy.n_addresses; i++)
|
||||
if (nla_put(msg, i + 1, ETH_ALEN,
|
||||
rdev->wiphy.addresses[i].addr))
|
||||
goto nla_put_failure;
|
||||
|
||||
nla_nest_end(msg, attr);
|
||||
}
|
||||
|
||||
state->split_start++;
|
||||
break;
|
||||
case 10:
|
||||
@ -2366,14 +2408,21 @@ static int nl80211_dump_wiphy_parse(struct sk_buff *skb,
|
||||
struct netlink_callback *cb,
|
||||
struct nl80211_dump_wiphy_state *state)
|
||||
{
|
||||
struct nlattr **tb = genl_family_attrbuf(&nl80211_fam);
|
||||
int ret = nlmsg_parse_deprecated(cb->nlh,
|
||||
GENL_HDRLEN + nl80211_fam.hdrsize,
|
||||
tb, nl80211_fam.maxattr,
|
||||
nl80211_policy, NULL);
|
||||
struct nlattr **tb = kcalloc(NUM_NL80211_ATTR, sizeof(*tb), GFP_KERNEL);
|
||||
int ret;
|
||||
|
||||
if (!tb)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = nlmsg_parse_deprecated(cb->nlh,
|
||||
GENL_HDRLEN + nl80211_fam.hdrsize,
|
||||
tb, nl80211_fam.maxattr,
|
||||
nl80211_policy, NULL);
|
||||
/* ignore parse errors for backward compatibility */
|
||||
if (ret)
|
||||
return 0;
|
||||
if (ret) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
state->split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP];
|
||||
if (tb[NL80211_ATTR_WIPHY])
|
||||
@ -2386,8 +2435,10 @@ static int nl80211_dump_wiphy_parse(struct sk_buff *skb,
|
||||
int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
|
||||
|
||||
netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
|
||||
if (!netdev)
|
||||
return -ENODEV;
|
||||
if (!netdev) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (netdev->ieee80211_ptr) {
|
||||
rdev = wiphy_to_rdev(
|
||||
netdev->ieee80211_ptr->wiphy);
|
||||
@ -2395,7 +2446,10 @@ static int nl80211_dump_wiphy_parse(struct sk_buff *skb,
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
ret = 0;
|
||||
out:
|
||||
kfree(tb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
@ -4361,6 +4415,34 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_parse_he_obss_pd(struct nlattr *attrs,
|
||||
struct ieee80211_he_obss_pd *he_obss_pd)
|
||||
{
|
||||
struct nlattr *tb[NL80211_HE_OBSS_PD_ATTR_MAX + 1];
|
||||
int err;
|
||||
|
||||
err = nla_parse_nested(tb, NL80211_HE_OBSS_PD_ATTR_MAX, attrs,
|
||||
he_obss_pd_policy, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!tb[NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET] ||
|
||||
!tb[NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET])
|
||||
return -EINVAL;
|
||||
|
||||
he_obss_pd->min_offset =
|
||||
nla_get_u32(tb[NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET]);
|
||||
he_obss_pd->max_offset =
|
||||
nla_get_u32(tb[NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET]);
|
||||
|
||||
if (he_obss_pd->min_offset >= he_obss_pd->max_offset)
|
||||
return -EINVAL;
|
||||
|
||||
he_obss_pd->enable = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nl80211_check_ap_rate_selectors(struct cfg80211_ap_settings *params,
|
||||
const u8 *rates)
|
||||
{
|
||||
@ -4645,6 +4727,14 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
|
||||
params.twt_responder =
|
||||
nla_get_flag(info->attrs[NL80211_ATTR_TWT_RESPONDER]);
|
||||
|
||||
if (info->attrs[NL80211_ATTR_HE_OBSS_PD]) {
|
||||
err = nl80211_parse_he_obss_pd(
|
||||
info->attrs[NL80211_ATTR_HE_OBSS_PD],
|
||||
¶ms.he_obss_pd);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
nl80211_calculate_ap_params(¶ms);
|
||||
|
||||
if (info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT])
|
||||
@ -8700,7 +8790,7 @@ static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
|
||||
|
||||
static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
struct nlattr **attrbuf = genl_family_attrbuf(&nl80211_fam);
|
||||
struct nlattr **attrbuf;
|
||||
struct survey_info survey;
|
||||
struct cfg80211_registered_device *rdev;
|
||||
struct wireless_dev *wdev;
|
||||
@ -8708,6 +8798,10 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
int res;
|
||||
bool radio_stats;
|
||||
|
||||
attrbuf = kcalloc(NUM_NL80211_ATTR, sizeof(*attrbuf), GFP_KERNEL);
|
||||
if (!attrbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
rtnl_lock();
|
||||
res = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
|
||||
if (res)
|
||||
@ -8752,6 +8846,7 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
cb->args[2] = survey_idx;
|
||||
res = skb->len;
|
||||
out_err:
|
||||
kfree(attrbuf);
|
||||
rtnl_unlock();
|
||||
return res;
|
||||
}
|
||||
@ -9611,6 +9706,7 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
|
||||
struct netlink_callback *cb)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev;
|
||||
struct nlattr **attrbuf = NULL;
|
||||
int err;
|
||||
long phy_idx;
|
||||
void *data = NULL;
|
||||
@ -9631,7 +9727,12 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
|
||||
goto out_err;
|
||||
}
|
||||
} else {
|
||||
struct nlattr **attrbuf = genl_family_attrbuf(&nl80211_fam);
|
||||
attrbuf = kcalloc(NUM_NL80211_ATTR, sizeof(*attrbuf),
|
||||
GFP_KERNEL);
|
||||
if (!attrbuf) {
|
||||
err = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
err = nlmsg_parse_deprecated(cb->nlh,
|
||||
GENL_HDRLEN + nl80211_fam.hdrsize,
|
||||
@ -9698,6 +9799,7 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
|
||||
/* see above */
|
||||
cb->args[0] = phy_idx + 1;
|
||||
out_err:
|
||||
kfree(attrbuf);
|
||||
rtnl_unlock();
|
||||
return err;
|
||||
}
|
||||
@ -12791,7 +12893,7 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
|
||||
struct cfg80211_registered_device **rdev,
|
||||
struct wireless_dev **wdev)
|
||||
{
|
||||
struct nlattr **attrbuf = genl_family_attrbuf(&nl80211_fam);
|
||||
struct nlattr **attrbuf;
|
||||
u32 vid, subcmd;
|
||||
unsigned int i;
|
||||
int vcmd_idx = -1;
|
||||
@ -12822,24 +12924,32 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
attrbuf = kcalloc(NUM_NL80211_ATTR, sizeof(*attrbuf), GFP_KERNEL);
|
||||
if (!attrbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
err = nlmsg_parse_deprecated(cb->nlh,
|
||||
GENL_HDRLEN + nl80211_fam.hdrsize,
|
||||
attrbuf, nl80211_fam.maxattr,
|
||||
nl80211_policy, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
goto out;
|
||||
|
||||
if (!attrbuf[NL80211_ATTR_VENDOR_ID] ||
|
||||
!attrbuf[NL80211_ATTR_VENDOR_SUBCMD])
|
||||
return -EINVAL;
|
||||
!attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), attrbuf);
|
||||
if (IS_ERR(*wdev))
|
||||
*wdev = NULL;
|
||||
|
||||
*rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), attrbuf);
|
||||
if (IS_ERR(*rdev))
|
||||
return PTR_ERR(*rdev);
|
||||
if (IS_ERR(*rdev)) {
|
||||
err = PTR_ERR(*rdev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
vid = nla_get_u32(attrbuf[NL80211_ATTR_VENDOR_ID]);
|
||||
subcmd = nla_get_u32(attrbuf[NL80211_ATTR_VENDOR_SUBCMD]);
|
||||
@ -12852,15 +12962,19 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
|
||||
if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd)
|
||||
continue;
|
||||
|
||||
if (!vcmd->dumpit)
|
||||
return -EOPNOTSUPP;
|
||||
if (!vcmd->dumpit) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
vcmd_idx = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (vcmd_idx < 0)
|
||||
return -EOPNOTSUPP;
|
||||
if (vcmd_idx < 0) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (attrbuf[NL80211_ATTR_VENDOR_DATA]) {
|
||||
data = nla_data(attrbuf[NL80211_ATTR_VENDOR_DATA]);
|
||||
@ -12871,7 +12985,7 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
|
||||
attrbuf[NL80211_ATTR_VENDOR_DATA],
|
||||
cb->extack);
|
||||
if (err)
|
||||
return err;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* 0 is the first index - add 1 to parse only once */
|
||||
@ -12883,7 +12997,10 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
|
||||
cb->args[4] = data_len;
|
||||
|
||||
/* keep rtnl locked in successful case */
|
||||
return 0;
|
||||
err = 0;
|
||||
out:
|
||||
kfree(attrbuf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nl80211_vendor_cmd_dump(struct sk_buff *skb,
|
||||
@ -14561,6 +14678,7 @@ static struct genl_family nl80211_fam __ro_after_init = {
|
||||
.n_ops = ARRAY_SIZE(nl80211_ops),
|
||||
.mcgrps = nl80211_mcgrps,
|
||||
.n_mcgrps = ARRAY_SIZE(nl80211_mcgrps),
|
||||
.parallel_ops = true,
|
||||
};
|
||||
|
||||
/* notification functions */
|
||||
@ -16092,7 +16210,9 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
|
||||
|
||||
if (wdev->iftype == NL80211_IFTYPE_STATION &&
|
||||
!WARN_ON(!wdev->current_bss))
|
||||
wdev->current_bss->pub.channel = chandef->chan;
|
||||
cfg80211_update_assoc_bss_entry(wdev, chandef->chan);
|
||||
|
||||
cfg80211_sched_dfs_chan_update(rdev);
|
||||
|
||||
nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
|
||||
NL80211_CMD_CH_SWITCH_NOTIFY, 0);
|
||||
|
@ -1091,6 +1091,93 @@ struct cfg80211_non_tx_bss {
|
||||
u8 bssid_index;
|
||||
};
|
||||
|
||||
static bool
|
||||
cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
|
||||
struct cfg80211_internal_bss *known,
|
||||
struct cfg80211_internal_bss *new,
|
||||
bool signal_valid)
|
||||
{
|
||||
lockdep_assert_held(&rdev->bss_lock);
|
||||
|
||||
/* Update IEs */
|
||||
if (rcu_access_pointer(new->pub.proberesp_ies)) {
|
||||
const struct cfg80211_bss_ies *old;
|
||||
|
||||
old = rcu_access_pointer(known->pub.proberesp_ies);
|
||||
|
||||
rcu_assign_pointer(known->pub.proberesp_ies,
|
||||
new->pub.proberesp_ies);
|
||||
/* Override possible earlier Beacon frame IEs */
|
||||
rcu_assign_pointer(known->pub.ies,
|
||||
new->pub.proberesp_ies);
|
||||
if (old)
|
||||
kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
|
||||
} else if (rcu_access_pointer(new->pub.beacon_ies)) {
|
||||
const struct cfg80211_bss_ies *old;
|
||||
struct cfg80211_internal_bss *bss;
|
||||
|
||||
if (known->pub.hidden_beacon_bss &&
|
||||
!list_empty(&known->hidden_list)) {
|
||||
const struct cfg80211_bss_ies *f;
|
||||
|
||||
/* The known BSS struct is one of the probe
|
||||
* response members of a group, but we're
|
||||
* receiving a beacon (beacon_ies in the new
|
||||
* bss is used). This can only mean that the
|
||||
* AP changed its beacon from not having an
|
||||
* SSID to showing it, which is confusing so
|
||||
* drop this information.
|
||||
*/
|
||||
|
||||
f = rcu_access_pointer(new->pub.beacon_ies);
|
||||
kfree_rcu((struct cfg80211_bss_ies *)f, rcu_head);
|
||||
return false;
|
||||
}
|
||||
|
||||
old = rcu_access_pointer(known->pub.beacon_ies);
|
||||
|
||||
rcu_assign_pointer(known->pub.beacon_ies, new->pub.beacon_ies);
|
||||
|
||||
/* Override IEs if they were from a beacon before */
|
||||
if (old == rcu_access_pointer(known->pub.ies))
|
||||
rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies);
|
||||
|
||||
/* Assign beacon IEs to all sub entries */
|
||||
list_for_each_entry(bss, &known->hidden_list, hidden_list) {
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
|
||||
ies = rcu_access_pointer(bss->pub.beacon_ies);
|
||||
WARN_ON(ies != old);
|
||||
|
||||
rcu_assign_pointer(bss->pub.beacon_ies,
|
||||
new->pub.beacon_ies);
|
||||
}
|
||||
|
||||
if (old)
|
||||
kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
|
||||
}
|
||||
|
||||
known->pub.beacon_interval = new->pub.beacon_interval;
|
||||
|
||||
/* don't update the signal if beacon was heard on
|
||||
* adjacent channel.
|
||||
*/
|
||||
if (signal_valid)
|
||||
known->pub.signal = new->pub.signal;
|
||||
known->pub.capability = new->pub.capability;
|
||||
known->ts = new->ts;
|
||||
known->ts_boottime = new->ts_boottime;
|
||||
known->parent_tsf = new->parent_tsf;
|
||||
known->pub.chains = new->pub.chains;
|
||||
memcpy(known->pub.chain_signal, new->pub.chain_signal,
|
||||
IEEE80211_MAX_CHAINS);
|
||||
ether_addr_copy(known->parent_bssid, new->parent_bssid);
|
||||
known->pub.max_bssid_indicator = new->pub.max_bssid_indicator;
|
||||
known->pub.bssid_index = new->pub.bssid_index;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Returned bss is reference counted and must be cleaned up appropriately. */
|
||||
struct cfg80211_internal_bss *
|
||||
cfg80211_bss_update(struct cfg80211_registered_device *rdev,
|
||||
@ -1114,88 +1201,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
|
||||
found = rb_find_bss(rdev, tmp, BSS_CMP_REGULAR);
|
||||
|
||||
if (found) {
|
||||
/* Update IEs */
|
||||
if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
|
||||
const struct cfg80211_bss_ies *old;
|
||||
|
||||
old = rcu_access_pointer(found->pub.proberesp_ies);
|
||||
|
||||
rcu_assign_pointer(found->pub.proberesp_ies,
|
||||
tmp->pub.proberesp_ies);
|
||||
/* Override possible earlier Beacon frame IEs */
|
||||
rcu_assign_pointer(found->pub.ies,
|
||||
tmp->pub.proberesp_ies);
|
||||
if (old)
|
||||
kfree_rcu((struct cfg80211_bss_ies *)old,
|
||||
rcu_head);
|
||||
} else if (rcu_access_pointer(tmp->pub.beacon_ies)) {
|
||||
const struct cfg80211_bss_ies *old;
|
||||
struct cfg80211_internal_bss *bss;
|
||||
|
||||
if (found->pub.hidden_beacon_bss &&
|
||||
!list_empty(&found->hidden_list)) {
|
||||
const struct cfg80211_bss_ies *f;
|
||||
|
||||
/*
|
||||
* The found BSS struct is one of the probe
|
||||
* response members of a group, but we're
|
||||
* receiving a beacon (beacon_ies in the tmp
|
||||
* bss is used). This can only mean that the
|
||||
* AP changed its beacon from not having an
|
||||
* SSID to showing it, which is confusing so
|
||||
* drop this information.
|
||||
*/
|
||||
|
||||
f = rcu_access_pointer(tmp->pub.beacon_ies);
|
||||
kfree_rcu((struct cfg80211_bss_ies *)f,
|
||||
rcu_head);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
old = rcu_access_pointer(found->pub.beacon_ies);
|
||||
|
||||
rcu_assign_pointer(found->pub.beacon_ies,
|
||||
tmp->pub.beacon_ies);
|
||||
|
||||
/* Override IEs if they were from a beacon before */
|
||||
if (old == rcu_access_pointer(found->pub.ies))
|
||||
rcu_assign_pointer(found->pub.ies,
|
||||
tmp->pub.beacon_ies);
|
||||
|
||||
/* Assign beacon IEs to all sub entries */
|
||||
list_for_each_entry(bss, &found->hidden_list,
|
||||
hidden_list) {
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
|
||||
ies = rcu_access_pointer(bss->pub.beacon_ies);
|
||||
WARN_ON(ies != old);
|
||||
|
||||
rcu_assign_pointer(bss->pub.beacon_ies,
|
||||
tmp->pub.beacon_ies);
|
||||
}
|
||||
|
||||
if (old)
|
||||
kfree_rcu((struct cfg80211_bss_ies *)old,
|
||||
rcu_head);
|
||||
}
|
||||
|
||||
found->pub.beacon_interval = tmp->pub.beacon_interval;
|
||||
/*
|
||||
* don't update the signal if beacon was heard on
|
||||
* adjacent channel.
|
||||
*/
|
||||
if (signal_valid)
|
||||
found->pub.signal = tmp->pub.signal;
|
||||
found->pub.capability = tmp->pub.capability;
|
||||
found->ts = tmp->ts;
|
||||
found->ts_boottime = tmp->ts_boottime;
|
||||
found->parent_tsf = tmp->parent_tsf;
|
||||
found->pub.chains = tmp->pub.chains;
|
||||
memcpy(found->pub.chain_signal, tmp->pub.chain_signal,
|
||||
IEEE80211_MAX_CHAINS);
|
||||
ether_addr_copy(found->parent_bssid, tmp->parent_bssid);
|
||||
found->pub.max_bssid_indicator = tmp->pub.max_bssid_indicator;
|
||||
found->pub.bssid_index = tmp->pub.bssid_index;
|
||||
if (!cfg80211_update_known_bss(rdev, found, tmp, signal_valid))
|
||||
goto drop;
|
||||
} else {
|
||||
struct cfg80211_internal_bss *new;
|
||||
struct cfg80211_internal_bss *hidden;
|
||||
@ -1368,6 +1375,7 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
|
||||
struct cfg80211_internal_bss tmp = {}, *res;
|
||||
int bss_type;
|
||||
bool signal_valid;
|
||||
unsigned long ts;
|
||||
|
||||
if (WARN_ON(!wiphy))
|
||||
return NULL;
|
||||
@ -1390,8 +1398,11 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
|
||||
tmp.ts_boottime = data->boottime_ns;
|
||||
if (non_tx_data) {
|
||||
tmp.pub.transmitted_bss = non_tx_data->tx_bss;
|
||||
ts = bss_from_pub(non_tx_data->tx_bss)->ts;
|
||||
tmp.pub.bssid_index = non_tx_data->bssid_index;
|
||||
tmp.pub.max_bssid_indicator = non_tx_data->max_bssid_indicator;
|
||||
} else {
|
||||
ts = jiffies;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1425,8 +1436,7 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
|
||||
|
||||
signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
|
||||
wiphy->max_adj_channel_rssi_comp;
|
||||
res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid,
|
||||
jiffies);
|
||||
res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid, ts);
|
||||
if (!res)
|
||||
return NULL;
|
||||
|
||||
@ -1440,7 +1450,7 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
|
||||
regulatory_hint_found_beacon(wiphy, channel, gfp);
|
||||
}
|
||||
|
||||
if (non_tx_data && non_tx_data->tx_bss) {
|
||||
if (non_tx_data) {
|
||||
/* this is a nontransmitting bss, we need to add it to
|
||||
* transmitting bss' list if it is not there
|
||||
*/
|
||||
@ -1659,6 +1669,8 @@ cfg80211_inform_bss_data(struct wiphy *wiphy,
|
||||
res = cfg80211_inform_single_bss_data(wiphy, data, ftype, bssid, tsf,
|
||||
capability, beacon_interval, ie,
|
||||
ielen, NULL, gfp);
|
||||
if (!res)
|
||||
return NULL;
|
||||
non_tx_data.tx_bss = res;
|
||||
cfg80211_parse_mbssid_data(wiphy, data, ftype, bssid, tsf,
|
||||
beacon_interval, ie, ielen, &non_tx_data,
|
||||
@ -1776,7 +1788,6 @@ static struct cfg80211_bss *
|
||||
cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
|
||||
struct cfg80211_inform_bss *data,
|
||||
struct ieee80211_mgmt *mgmt, size_t len,
|
||||
struct cfg80211_non_tx_bss *non_tx_data,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct cfg80211_internal_bss tmp = {}, *res;
|
||||
@ -1835,11 +1846,6 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
|
||||
tmp.pub.chains = data->chains;
|
||||
memcpy(tmp.pub.chain_signal, data->chain_signal, IEEE80211_MAX_CHAINS);
|
||||
ether_addr_copy(tmp.parent_bssid, data->parent_bssid);
|
||||
if (non_tx_data) {
|
||||
tmp.pub.transmitted_bss = non_tx_data->tx_bss;
|
||||
tmp.pub.bssid_index = non_tx_data->bssid_index;
|
||||
tmp.pub.max_bssid_indicator = non_tx_data->max_bssid_indicator;
|
||||
}
|
||||
|
||||
signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
|
||||
wiphy->max_adj_channel_rssi_comp;
|
||||
@ -1877,7 +1883,7 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
|
||||
struct cfg80211_non_tx_bss non_tx_data;
|
||||
|
||||
res = cfg80211_inform_single_bss_frame_data(wiphy, data, mgmt,
|
||||
len, NULL, gfp);
|
||||
len, gfp);
|
||||
if (!res || !wiphy->support_mbssid ||
|
||||
!cfg80211_find_ie(WLAN_EID_MULTIPLE_BSSID, ie, ielen))
|
||||
return res;
|
||||
@ -1995,6 +2001,85 @@ void cfg80211_bss_iter(struct wiphy *wiphy,
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_bss_iter);
|
||||
|
||||
void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
|
||||
struct ieee80211_channel *chan)
|
||||
{
|
||||
struct wiphy *wiphy = wdev->wiphy;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
struct cfg80211_internal_bss *cbss = wdev->current_bss;
|
||||
struct cfg80211_internal_bss *new = NULL;
|
||||
struct cfg80211_internal_bss *bss;
|
||||
struct cfg80211_bss *nontrans_bss;
|
||||
struct cfg80211_bss *tmp;
|
||||
|
||||
spin_lock_bh(&rdev->bss_lock);
|
||||
|
||||
if (WARN_ON(cbss->pub.channel == chan))
|
||||
goto done;
|
||||
|
||||
/* use transmitting bss */
|
||||
if (cbss->pub.transmitted_bss)
|
||||
cbss = container_of(cbss->pub.transmitted_bss,
|
||||
struct cfg80211_internal_bss,
|
||||
pub);
|
||||
|
||||
cbss->pub.channel = chan;
|
||||
|
||||
list_for_each_entry(bss, &rdev->bss_list, list) {
|
||||
if (!cfg80211_bss_type_match(bss->pub.capability,
|
||||
bss->pub.channel->band,
|
||||
wdev->conn_bss_type))
|
||||
continue;
|
||||
|
||||
if (bss == cbss)
|
||||
continue;
|
||||
|
||||
if (!cmp_bss(&bss->pub, &cbss->pub, BSS_CMP_REGULAR)) {
|
||||
new = bss;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (new) {
|
||||
/* to save time, update IEs for transmitting bss only */
|
||||
if (cfg80211_update_known_bss(rdev, cbss, new, false)) {
|
||||
new->pub.proberesp_ies = NULL;
|
||||
new->pub.beacon_ies = NULL;
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(nontrans_bss, tmp,
|
||||
&new->pub.nontrans_list,
|
||||
nontrans_list) {
|
||||
bss = container_of(nontrans_bss,
|
||||
struct cfg80211_internal_bss, pub);
|
||||
if (__cfg80211_unlink_bss(rdev, bss))
|
||||
rdev->bss_generation++;
|
||||
}
|
||||
|
||||
WARN_ON(atomic_read(&new->hold));
|
||||
if (!WARN_ON(!__cfg80211_unlink_bss(rdev, new)))
|
||||
rdev->bss_generation++;
|
||||
}
|
||||
|
||||
rb_erase(&cbss->rbn, &rdev->bss_tree);
|
||||
rb_insert_bss(rdev, cbss);
|
||||
rdev->bss_generation++;
|
||||
|
||||
list_for_each_entry_safe(nontrans_bss, tmp,
|
||||
&cbss->pub.nontrans_list,
|
||||
nontrans_list) {
|
||||
bss = container_of(nontrans_bss,
|
||||
struct cfg80211_internal_bss, pub);
|
||||
bss->pub.channel = chan;
|
||||
rb_erase(&bss->rbn, &rdev->bss_tree);
|
||||
rb_insert_bss(rdev, bss);
|
||||
rdev->bss_generation++;
|
||||
}
|
||||
|
||||
done:
|
||||
spin_unlock_bh(&rdev->bss_lock);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
static struct cfg80211_registered_device *
|
||||
cfg80211_get_dev_from_ifindex(struct net *net, int ifindex)
|
||||
|
Loading…
x
Reference in New Issue
Block a user