For the 4.6 cycle, we have a number of changes:
* Bob's mesh mode rhashtable conversion, this includes the rhashtable API change for allocation flags * BSSID scan, connect() command reassoc support (Jouni) * fast (optimised data only) and support for RSS in mac80211 (myself) * various smaller changes -----BEGIN PGP SIGNATURE----- iQIcBAABCgAGBQJXBQ4GAAoJEGt7eEactAAdWiMP/ibaP3I79NDc0s7wCDA+KRkm hx0Qx4a0wwm7lDFlnGBjY6yKr+XFDliCvdGX7XGpLSsTioNg7eXPpwx5FQoj6RiV 8+5RKE9fTguN9ofUzqAwHd9sVOaxvdlXbKfb/N93Gzjpw/meYk58wXdF7Almkroa ukgJeMzIlIh+6D96zFEA+Ofzp5chwh+x2Dn0wXutEe9P9fOERA859veAvx65b+Ql IRGTqyuY5B/wcbkr4o+DWQwgrdt7Vop9nYVPNWtMHm2JTzfuCSaQ2cD9TnVAK/bg /vtqC46KKNLyBRGexAPqdftY9PWcfipgE+n7k+Et4iGSmNm7Z3dEyewgXmqli7XJ X8Uiaq+N6Fpe06DVSU7aSRt8NLV64A44jXSfKRI9U2POUqKMn/PMdm8bhPW8qCdM ra6myWpQGHWK9e0TQQdShq0NQKGxCZAiSRiiIrbbvXl1CwXxkPCG39wAC3Sh1tEN ou4lGraeywGnTjaq+mwLEtHLoug8Y2x+Fz+Ze4Cu2enXxna9lp4lr+rFlc+2+0Er o9oPxkTk8krZGIj9M6PNc5W+InMwchaFX3076n67hnFHzFRlOQzkfffbPYlhKJDQ f8c9JiNZIoX/fD1TAKsrdO1+EKm/xo7w7pLgbMwQal8Jr88SkITDg0i3oXc56vNQ ZK2gUzwvrD/jh0AUyDfN =sj7y -----END PGP SIGNATURE----- Merge tag 'mac80211-next-for-davem-2016-04-06' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next Johannes Berg says: ==================== For the 4.7 cycle, we have a number of changes: * Bob's mesh mode rhashtable conversion, this includes the rhashtable API change for allocation flags * BSSID scan, connect() command reassoc support (Jouni) * fast (optimised data only) and support for RSS in mac80211 (myself) * various smaller changes ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
1089ac6977
|
@ -37,14 +37,27 @@ radiotap headers and used to control injection:
|
|||
HT rate for the transmission (only for devices without own rate control).
|
||||
Also some flags are parsed
|
||||
|
||||
IEEE80211_TX_RC_SHORT_GI: use short guard interval
|
||||
IEEE80211_TX_RC_40_MHZ_WIDTH: send in HT40 mode
|
||||
IEEE80211_RADIOTAP_MCS_SGI: use short guard interval
|
||||
IEEE80211_RADIOTAP_MCS_BW_40: send in HT40 mode
|
||||
|
||||
* IEEE80211_RADIOTAP_DATA_RETRIES
|
||||
|
||||
number of retries when either IEEE80211_RADIOTAP_RATE or
|
||||
IEEE80211_RADIOTAP_MCS was used
|
||||
|
||||
* IEEE80211_RADIOTAP_VHT
|
||||
|
||||
VHT mcs and number of streams used in the transmission (only for devices
|
||||
without own rate control). Also other fields are parsed
|
||||
|
||||
flags field
|
||||
IEEE80211_RADIOTAP_VHT_FLAG_SGI: use short guard interval
|
||||
|
||||
bandwidth field
|
||||
1: send using 40MHz channel width
|
||||
4: send using 80MHz channel width
|
||||
11: send using 160MHz channel width
|
||||
|
||||
The injection code can also skip all other currently defined radiotap fields
|
||||
facilitating replay of captured radiotap headers directly.
|
||||
|
||||
|
|
|
@ -979,7 +979,7 @@ static void ath10k_process_rx(struct ath10k *ar,
|
|||
*status = *rx_status;
|
||||
|
||||
ath10k_dbg(ar, ATH10K_DBG_DATA,
|
||||
"rx skb %p len %u peer %pM %s %s sn %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",
|
||||
"rx skb %p len %u peer %pM %s %s sn %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%llx fcs-err %i mic-err %i amsdu-more %i\n",
|
||||
skb,
|
||||
skb->len,
|
||||
ieee80211_get_SA(hdr),
|
||||
|
|
|
@ -57,7 +57,7 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb)
|
|||
RX_FLAG_MMIC_STRIPPED |
|
||||
RX_FLAG_DECRYPTED;
|
||||
|
||||
wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x\n", status.flag);
|
||||
wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%llx\n", status.flag);
|
||||
|
||||
memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
|
||||
|
||||
|
|
|
@ -686,7 +686,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,
|
|||
|
||||
memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
|
||||
|
||||
ieee80211_rx_napi(priv->hw, skb, priv->napi);
|
||||
ieee80211_rx_napi(priv->hw, NULL, skb, priv->napi);
|
||||
}
|
||||
|
||||
static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)
|
||||
|
|
|
@ -1499,5 +1499,5 @@ void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm,
|
|||
memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
|
||||
|
||||
/* pass it as regular rx to mac80211 */
|
||||
ieee80211_rx_napi(mvm->hw, skb, NULL);
|
||||
ieee80211_rx_napi(mvm->hw, NULL, skb, NULL);
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
|
|||
fraglen, rxb->truesize);
|
||||
}
|
||||
|
||||
ieee80211_rx_napi(mvm->hw, skb, napi);
|
||||
ieee80211_rx_napi(mvm->hw, NULL, skb, napi);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -210,7 +210,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
|
|||
if (iwl_mvm_check_pn(mvm, skb, queue, sta))
|
||||
kfree_skb(skb);
|
||||
else
|
||||
ieee80211_rx_napi(mvm->hw, skb, napi);
|
||||
ieee80211_rx_napi(mvm->hw, NULL, skb, napi);
|
||||
}
|
||||
|
||||
static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
|
||||
|
|
|
@ -1909,6 +1909,7 @@ static void hw_scan_work(struct work_struct *work)
|
|||
/* send probes */
|
||||
for (i = 0; i < req->n_ssids; i++) {
|
||||
struct sk_buff *probe;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
|
||||
probe = ieee80211_probereq_get(hwsim->hw,
|
||||
hwsim->scan_addr,
|
||||
|
@ -1918,6 +1919,10 @@ static void hw_scan_work(struct work_struct *work)
|
|||
if (!probe)
|
||||
continue;
|
||||
|
||||
mgmt = (struct ieee80211_mgmt *) probe->data;
|
||||
memcpy(mgmt->da, req->bssid, ETH_ALEN);
|
||||
memcpy(mgmt->bssid, req->bssid, ETH_ALEN);
|
||||
|
||||
if (req->ie_len)
|
||||
memcpy(skb_put(probe, req->ie_len), req->ie,
|
||||
req->ie_len);
|
||||
|
|
|
@ -1913,7 +1913,7 @@ static int gfs2_glocks_open(struct inode *inode, struct file *file)
|
|||
if (seq->buf)
|
||||
seq->size = GFS2_SEQ_GOODSIZE;
|
||||
gi->gl = NULL;
|
||||
ret = rhashtable_walk_init(&gl_hash_table, &gi->hti);
|
||||
ret = rhashtable_walk_init(&gl_hash_table, &gi->hti, GFP_KERNEL);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -1941,7 +1941,7 @@ static int gfs2_glstats_open(struct inode *inode, struct file *file)
|
|||
if (seq->buf)
|
||||
seq->size = GFS2_SEQ_GOODSIZE;
|
||||
gi->gl = NULL;
|
||||
ret = rhashtable_walk_init(&gl_hash_table, &gi->hti);
|
||||
ret = rhashtable_walk_init(&gl_hash_table, &gi->hti, GFP_KERNEL);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
* Copyright (c) 2005, Devicescape Software, Inc.
|
||||
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
|
||||
* Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH
|
||||
* Copyright (c) 2016 Intel Deutschland GmbH
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
|
@ -163,6 +164,9 @@ static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2)
|
|||
/* 30 byte 4 addr hdr, 2 byte QoS, 2304 byte MSDU, 12 byte crypt, 4 byte FCS */
|
||||
#define IEEE80211_MAX_FRAME_LEN 2352
|
||||
|
||||
/* Maximal size of an A-MSDU that can be transported in a HT BA session */
|
||||
#define IEEE80211_MAX_MPDU_LEN_HT_BA 4095
|
||||
|
||||
/* Maximal size of an A-MSDU */
|
||||
#define IEEE80211_MAX_MPDU_LEN_HT_3839 3839
|
||||
#define IEEE80211_MAX_MPDU_LEN_HT_7935 7935
|
||||
|
@ -637,6 +641,16 @@ static inline bool ieee80211_is_first_frag(__le16 seq_ctrl)
|
|||
return (seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_is_frag - check if a frame is a fragment
|
||||
* @hdr: 802.11 header of the frame
|
||||
*/
|
||||
static inline bool ieee80211_is_frag(struct ieee80211_hdr *hdr)
|
||||
{
|
||||
return ieee80211_has_morefrags(hdr->frame_control) ||
|
||||
hdr->seq_ctrl & cpu_to_le16(IEEE80211_SCTL_FRAG);
|
||||
}
|
||||
|
||||
struct ieee80211s_hdr {
|
||||
u8 flags;
|
||||
u8 ttl;
|
||||
|
@ -1011,6 +1025,16 @@ struct ieee80211_mgmt {
|
|||
u8 tpc_elem_length;
|
||||
struct ieee80211_tpc_report_ie tpc;
|
||||
} __packed tpc_report;
|
||||
struct {
|
||||
u8 action_code;
|
||||
u8 dialog_token;
|
||||
u8 follow_up;
|
||||
u8 tod[6];
|
||||
u8 toa[6];
|
||||
__le16 tod_error;
|
||||
__le16 toa_error;
|
||||
u8 variable[0];
|
||||
} __packed ftm;
|
||||
} u;
|
||||
} __packed action;
|
||||
} u;
|
||||
|
|
|
@ -346,7 +346,8 @@ struct bucket_table *rhashtable_insert_slow(struct rhashtable *ht,
|
|||
struct bucket_table *old_tbl);
|
||||
int rhashtable_insert_rehash(struct rhashtable *ht, struct bucket_table *tbl);
|
||||
|
||||
int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter);
|
||||
int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter,
|
||||
gfp_t gfp);
|
||||
void rhashtable_walk_exit(struct rhashtable_iter *iter);
|
||||
int rhashtable_walk_start(struct rhashtable_iter *iter) __acquires(RCU);
|
||||
void *rhashtable_walk_next(struct rhashtable_iter *iter);
|
||||
|
|
|
@ -816,6 +816,7 @@ enum station_parameters_apply_mask {
|
|||
* @supported_oper_classes_len: number of supported operating classes
|
||||
* @opmode_notif: operating mode field from Operating Mode Notification
|
||||
* @opmode_notif_used: information if operating mode field is used
|
||||
* @support_p2p_ps: information if station supports P2P PS mechanism
|
||||
*/
|
||||
struct station_parameters {
|
||||
const u8 *supported_rates;
|
||||
|
@ -841,6 +842,7 @@ struct station_parameters {
|
|||
u8 supported_oper_classes_len;
|
||||
u8 opmode_notif;
|
||||
bool opmode_notif_used;
|
||||
int support_p2p_ps;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1455,6 +1457,7 @@ struct cfg80211_ssid {
|
|||
* @mac_addr_mask: MAC address mask used with randomisation, bits that
|
||||
* are 0 in the mask should be randomised, bits that are 1 should
|
||||
* be taken from the @mac_addr
|
||||
* @bssid: BSSID to scan for (most commonly, the wildcard BSSID)
|
||||
*/
|
||||
struct cfg80211_scan_request {
|
||||
struct cfg80211_ssid *ssids;
|
||||
|
@ -1471,6 +1474,7 @@ struct cfg80211_scan_request {
|
|||
|
||||
u8 mac_addr[ETH_ALEN] __aligned(2);
|
||||
u8 mac_addr_mask[ETH_ALEN] __aligned(2);
|
||||
u8 bssid[ETH_ALEN] __aligned(2);
|
||||
|
||||
/* internal */
|
||||
struct wiphy *wiphy;
|
||||
|
@ -1617,7 +1621,7 @@ struct cfg80211_inform_bss {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_bss_ie_data - BSS entry IE data
|
||||
* struct cfg80211_bss_ies - BSS entry IE data
|
||||
* @tsf: TSF contained in the frame that carried these IEs
|
||||
* @rcu_head: internal use, for freeing
|
||||
* @len: length of the IEs
|
||||
|
@ -1856,6 +1860,33 @@ struct cfg80211_ibss_params {
|
|||
struct ieee80211_ht_cap ht_capa_mask;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_bss_select_adjust - BSS selection with RSSI adjustment.
|
||||
*
|
||||
* @band: band of BSS which should match for RSSI level adjustment.
|
||||
* @delta: value of RSSI level adjustment.
|
||||
*/
|
||||
struct cfg80211_bss_select_adjust {
|
||||
enum ieee80211_band band;
|
||||
s8 delta;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_bss_selection - connection parameters for BSS selection.
|
||||
*
|
||||
* @behaviour: requested BSS selection behaviour.
|
||||
* @param: parameters for requestion behaviour.
|
||||
* @band_pref: preferred band for %NL80211_BSS_SELECT_ATTR_BAND_PREF.
|
||||
* @adjust: parameters for %NL80211_BSS_SELECT_ATTR_RSSI_ADJUST.
|
||||
*/
|
||||
struct cfg80211_bss_selection {
|
||||
enum nl80211_bss_select_attr behaviour;
|
||||
union {
|
||||
enum ieee80211_band band_pref;
|
||||
struct cfg80211_bss_select_adjust adjust;
|
||||
} param;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_connect_params - Connection parameters
|
||||
*
|
||||
|
@ -1893,6 +1924,8 @@ struct cfg80211_ibss_params {
|
|||
* @vht_capa_mask: The bits of vht_capa which are to be used.
|
||||
* @pbss: if set, connect to a PCP instead of AP. Valid for DMG
|
||||
* networks.
|
||||
* @bss_select: criteria to be used for BSS selection.
|
||||
* @prev_bssid: previous BSSID, if not %NULL use reassociate frame
|
||||
*/
|
||||
struct cfg80211_connect_params {
|
||||
struct ieee80211_channel *channel;
|
||||
|
@ -1916,6 +1949,8 @@ struct cfg80211_connect_params {
|
|||
struct ieee80211_vht_cap vht_capa;
|
||||
struct ieee80211_vht_cap vht_capa_mask;
|
||||
bool pbss;
|
||||
struct cfg80211_bss_selection bss_select;
|
||||
const u8 *prev_bssid;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -3184,6 +3219,9 @@ struct wiphy_vendor_command {
|
|||
* low rssi when a frame is heard on different channel, then it should set
|
||||
* this variable to the maximal offset for which it can compensate.
|
||||
* This value should be set in MHz.
|
||||
* @bss_select_support: bitmask indicating the BSS selection criteria supported
|
||||
* by the driver in the .connect() callback. The bit position maps to the
|
||||
* attribute indices defined in &enum nl80211_bss_select_attr.
|
||||
*/
|
||||
struct wiphy {
|
||||
/* assign these fields before you register the wiphy */
|
||||
|
@ -3306,6 +3344,8 @@ struct wiphy {
|
|||
u8 max_num_csa_counters;
|
||||
u8 max_adj_channel_rssi_comp;
|
||||
|
||||
u32 bss_select_support;
|
||||
|
||||
char priv[0] __aligned(NETDEV_ALIGN);
|
||||
};
|
||||
|
||||
|
|
|
@ -291,7 +291,7 @@ struct ieee80211_vif_chanctx_switch {
|
|||
* @BSS_CHANGED_PS: PS changed for this BSS (STA mode)
|
||||
* @BSS_CHANGED_TXPOWER: TX power setting changed for this interface
|
||||
* @BSS_CHANGED_P2P_PS: P2P powersave settings (CTWindow, opportunistic PS)
|
||||
* changed (currently only in P2P client mode, GO mode will be later)
|
||||
* changed
|
||||
* @BSS_CHANGED_BEACON_INFO: Data from the AP's beacon became available:
|
||||
* currently dtim_period only is under consideration.
|
||||
* @BSS_CHANGED_BANDWIDTH: The bandwidth used by this interface changed,
|
||||
|
@ -526,6 +526,9 @@ struct ieee80211_mu_group_data {
|
|||
* userspace), whereas TPC is disabled if %txpower_type is set to
|
||||
* NL80211_TX_POWER_FIXED (use value configured from userspace)
|
||||
* @p2p_noa_attr: P2P NoA attribute for P2P powersave
|
||||
* @allow_p2p_go_ps: indication for AP or P2P GO interface, whether it's allowed
|
||||
* to use P2P PS mechanism or not. AP/P2P GO is not allowed to use P2P PS
|
||||
* if it has associated clients without P2P PS support.
|
||||
*/
|
||||
struct ieee80211_bss_conf {
|
||||
const u8 *bssid;
|
||||
|
@ -563,6 +566,7 @@ struct ieee80211_bss_conf {
|
|||
int txpower;
|
||||
enum nl80211_tx_power_setting txpower_type;
|
||||
struct ieee80211_p2p_noa_attr p2p_noa_attr;
|
||||
bool allow_p2p_go_ps;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -709,6 +713,7 @@ enum mac80211_tx_info_flags {
|
|||
* @IEEE80211_TX_CTRL_PS_RESPONSE: This frame is a response to a poll
|
||||
* frame (PS-Poll or uAPSD).
|
||||
* @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information
|
||||
* @IEEE80211_TX_CTRL_AMSDU: This frame is an A-MSDU frame
|
||||
*
|
||||
* These flags are used in tx_info->control.flags.
|
||||
*/
|
||||
|
@ -716,6 +721,7 @@ enum mac80211_tx_control_flags {
|
|||
IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0),
|
||||
IEEE80211_TX_CTRL_PS_RESPONSE = BIT(1),
|
||||
IEEE80211_TX_CTRL_RATE_INJECT = BIT(2),
|
||||
IEEE80211_TX_CTRL_AMSDU = BIT(3),
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1034,6 +1040,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
|
|||
* on this subframe
|
||||
* @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
|
||||
* is stored in the @ampdu_delimiter_crc field)
|
||||
* @RX_FLAG_MIC_STRIPPED: The mic was stripped of this packet. Decryption was
|
||||
* done by the hardware
|
||||
* @RX_FLAG_LDPC: LDPC was used
|
||||
* @RX_FLAG_ONLY_MONITOR: Report frame only to monitor interfaces without
|
||||
* processing it in any regular way.
|
||||
|
@ -1091,6 +1099,7 @@ enum mac80211_rx_flags {
|
|||
RX_FLAG_5MHZ = BIT(29),
|
||||
RX_FLAG_AMSDU_MORE = BIT(30),
|
||||
RX_FLAG_RADIOTAP_VENDOR_DATA = BIT(31),
|
||||
RX_FLAG_MIC_STRIPPED = BIT_ULL(32),
|
||||
};
|
||||
|
||||
#define RX_FLAG_STBC_SHIFT 26
|
||||
|
@ -1120,6 +1129,8 @@ enum mac80211_rx_vht_flags {
|
|||
*
|
||||
* @mactime: value in microseconds of the 64-bit Time Synchronization Function
|
||||
* (TSF) timer when the first data symbol (MPDU) arrived at the hardware.
|
||||
* @boottime_ns: CLOCK_BOOTTIME timestamp the frame was received at, this is
|
||||
* needed only for beacons and probe responses that update the scan cache.
|
||||
* @device_timestamp: arbitrary timestamp for the device, mac80211 doesn't use
|
||||
* it but can store it and pass it back to the driver for synchronisation
|
||||
* @band: the active band when this frame was received
|
||||
|
@ -1146,9 +1157,10 @@ enum mac80211_rx_vht_flags {
|
|||
*/
|
||||
struct ieee80211_rx_status {
|
||||
u64 mactime;
|
||||
u64 boottime_ns;
|
||||
u32 device_timestamp;
|
||||
u32 ampdu_reference;
|
||||
u32 flag;
|
||||
u64 flag;
|
||||
u16 freq;
|
||||
u8 vht_flag;
|
||||
u8 rate_idx;
|
||||
|
@ -1735,6 +1747,8 @@ struct ieee80211_sta_rates {
|
|||
* size is min(max_amsdu_len, 7935) bytes.
|
||||
* Both additional HT limits must be enforced by the low level driver.
|
||||
* This is defined by the spec (IEEE 802.11-2012 section 8.3.2.2 NOTE 2).
|
||||
* @support_p2p_ps: indicates whether the STA supports P2P PS mechanism or not.
|
||||
* @max_rc_amsdu_len: Maximum A-MSDU size in bytes recommended by rate control.
|
||||
* @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
|
||||
*/
|
||||
struct ieee80211_sta {
|
||||
|
@ -1755,6 +1769,8 @@ struct ieee80211_sta {
|
|||
bool mfp;
|
||||
u8 max_amsdu_subframes;
|
||||
u16 max_amsdu_len;
|
||||
bool support_p2p_ps;
|
||||
u16 max_rc_amsdu_len;
|
||||
|
||||
struct ieee80211_txq *txq[IEEE80211_NUM_TIDS];
|
||||
|
||||
|
@ -1968,6 +1984,18 @@ struct ieee80211_txq {
|
|||
* order and does not need to manage its own reorder buffer or BA session
|
||||
* timeout.
|
||||
*
|
||||
* @IEEE80211_HW_USES_RSS: The device uses RSS and thus requires parallel RX,
|
||||
* which implies using per-CPU station statistics.
|
||||
*
|
||||
* @IEEE80211_HW_TX_AMSDU: Hardware (or driver) supports software aggregated
|
||||
* A-MSDU frames. Requires software tx queueing and fast-xmit support.
|
||||
* When not using minstrel/minstrel_ht rate control, the driver must
|
||||
* limit the maximum A-MSDU size based on the current tx rate by setting
|
||||
* max_rc_amsdu_len in struct ieee80211_sta.
|
||||
*
|
||||
* @IEEE80211_HW_TX_FRAG_LIST: Hardware (or driver) supports sending frag_list
|
||||
* skbs, needed for zero-copy software A-MSDU.
|
||||
*
|
||||
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
|
||||
*/
|
||||
enum ieee80211_hw_flags {
|
||||
|
@ -2005,6 +2033,9 @@ enum ieee80211_hw_flags {
|
|||
IEEE80211_HW_BEACON_TX_STATUS,
|
||||
IEEE80211_HW_NEEDS_UNIQUE_STA_ADDR,
|
||||
IEEE80211_HW_SUPPORTS_REORDERING_BUFFER,
|
||||
IEEE80211_HW_USES_RSS,
|
||||
IEEE80211_HW_TX_AMSDU,
|
||||
IEEE80211_HW_TX_FRAG_LIST,
|
||||
|
||||
/* keep last, obviously */
|
||||
NUM_IEEE80211_HW_FLAGS
|
||||
|
@ -2077,6 +2108,9 @@ enum ieee80211_hw_flags {
|
|||
* size is smaller (an example is LinkSys WRT120N with FW v1.0.07
|
||||
* build 002 Jun 18 2012).
|
||||
*
|
||||
* @max_tx_fragments: maximum number of tx buffers per (A)-MSDU, sum
|
||||
* of 1 + skb_shinfo(skb)->nr_frags for each skb in the frag_list.
|
||||
*
|
||||
* @offchannel_tx_hw_queue: HW queue ID to use for offchannel TX
|
||||
* (if %IEEE80211_HW_QUEUE_CONTROL is set)
|
||||
*
|
||||
|
@ -2131,6 +2165,7 @@ struct ieee80211_hw {
|
|||
u8 max_rate_tries;
|
||||
u8 max_rx_aggregation_subframes;
|
||||
u8 max_tx_aggregation_subframes;
|
||||
u8 max_tx_fragments;
|
||||
u8 offchannel_tx_hw_queue;
|
||||
u8 radiotap_mcs_details;
|
||||
u16 radiotap_vht_details;
|
||||
|
@ -3348,6 +3383,10 @@ enum ieee80211_reconfig_type {
|
|||
* the function call.
|
||||
*
|
||||
* @wake_tx_queue: Called when new packets have been added to the queue.
|
||||
* @sync_rx_queues: Process all pending frames in RSS queues. This is a
|
||||
* synchronization which is needed in case driver has in its RSS queues
|
||||
* pending frames that were received prior to the control path action
|
||||
* currently taken (e.g. disassociation) but are not processed yet.
|
||||
*/
|
||||
struct ieee80211_ops {
|
||||
void (*tx)(struct ieee80211_hw *hw,
|
||||
|
@ -3585,6 +3624,7 @@ struct ieee80211_ops {
|
|||
|
||||
void (*wake_tx_queue)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_txq *txq);
|
||||
void (*sync_rx_queues)(struct ieee80211_hw *hw);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -3838,11 +3878,12 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw);
|
|||
* This function must be called with BHs disabled.
|
||||
*
|
||||
* @hw: the hardware this frame came in on
|
||||
* @sta: the station the frame was received from, or %NULL
|
||||
* @skb: the buffer to receive, owned by mac80211 after this call
|
||||
* @napi: the NAPI context
|
||||
*/
|
||||
void ieee80211_rx_napi(struct ieee80211_hw *hw, struct sk_buff *skb,
|
||||
struct napi_struct *napi);
|
||||
void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
|
||||
struct sk_buff *skb, struct napi_struct *napi);
|
||||
|
||||
/**
|
||||
* ieee80211_rx - receive frame
|
||||
|
@ -3866,7 +3907,7 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct sk_buff *skb,
|
|||
*/
|
||||
static inline void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
|
||||
{
|
||||
ieee80211_rx_napi(hw, skb, NULL);
|
||||
ieee80211_rx_napi(hw, NULL, skb, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -322,7 +322,9 @@
|
|||
* @NL80211_CMD_GET_SCAN: get scan results
|
||||
* @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
|
||||
* %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
|
||||
* probe requests at CCK rate or not.
|
||||
* probe requests at CCK rate or not. %NL80211_ATTR_MAC can be used to
|
||||
* specify a BSSID to scan for; if not included, the wildcard BSSID will
|
||||
* be used.
|
||||
* @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
|
||||
* NL80211_CMD_GET_SCAN and on the "scan" multicast group)
|
||||
* @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
|
||||
|
@ -1795,6 +1797,15 @@ enum nl80211_commands {
|
|||
* in a PBSS. Specified in %NL80211_CMD_CONNECT to request
|
||||
* connecting to a PCP, and in %NL80211_CMD_START_AP to start
|
||||
* a PCP instead of AP. Relevant for DMG networks only.
|
||||
* @NL80211_ATTR_BSS_SELECT: nested attribute for driver supporting the
|
||||
* BSS selection feature. When used with %NL80211_CMD_GET_WIPHY it contains
|
||||
* attributes according &enum nl80211_bss_select_attr to indicate what
|
||||
* BSS selection behaviours are supported. When used with %NL80211_CMD_CONNECT
|
||||
* it contains the behaviour-specific attribute containing the parameters for
|
||||
* BSS selection to be done by driver and/or firmware.
|
||||
*
|
||||
* @NL80211_ATTR_STA_SUPPORT_P2P_PS: whether P2P PS mechanism supported
|
||||
* or not. u8, one of the values of &enum nl80211_sta_p2p_ps_status
|
||||
*
|
||||
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
|
||||
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||||
|
@ -2172,6 +2183,10 @@ enum nl80211_attrs {
|
|||
|
||||
NL80211_ATTR_PBSS,
|
||||
|
||||
NL80211_ATTR_BSS_SELECT,
|
||||
|
||||
NL80211_ATTR_STA_SUPPORT_P2P_PS,
|
||||
|
||||
/* add attributes here, update the policy in nl80211.c */
|
||||
|
||||
__NL80211_ATTR_AFTER_LAST,
|
||||
|
@ -2315,6 +2330,20 @@ enum nl80211_sta_flags {
|
|||
NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl80211_sta_p2p_ps_status - station support of P2P PS
|
||||
*
|
||||
* @NL80211_P2P_PS_UNSUPPORTED: station doesn't support P2P PS mechanism
|
||||
* @@NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism
|
||||
* @NUM_NL80211_P2P_PS_STATUS: number of values
|
||||
*/
|
||||
enum nl80211_sta_p2p_ps_status {
|
||||
NL80211_P2P_PS_UNSUPPORTED = 0,
|
||||
NL80211_P2P_PS_SUPPORTED,
|
||||
|
||||
NUM_NL80211_P2P_PS_STATUS,
|
||||
};
|
||||
|
||||
#define NL80211_STA_FLAG_MAX_OLD_API NL80211_STA_FLAG_TDLS_PEER
|
||||
|
||||
/**
|
||||
|
@ -4665,4 +4694,48 @@ enum nl80211_sched_scan_plan {
|
|||
__NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nl80211_bss_select_rssi_adjust - RSSI adjustment parameters.
|
||||
*
|
||||
* @band: band of BSS that must match for RSSI value adjustment.
|
||||
* @delta: value used to adjust the RSSI value of matching BSS.
|
||||
*/
|
||||
struct nl80211_bss_select_rssi_adjust {
|
||||
__u8 band;
|
||||
__s8 delta;
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* enum nl80211_bss_select_attr - attributes for bss selection.
|
||||
*
|
||||
* @__NL80211_BSS_SELECT_ATTR_INVALID: reserved.
|
||||
* @NL80211_BSS_SELECT_ATTR_RSSI: Flag indicating only RSSI-based BSS selection
|
||||
* is requested.
|
||||
* @NL80211_BSS_SELECT_ATTR_BAND_PREF: attribute indicating BSS
|
||||
* selection should be done such that the specified band is preferred.
|
||||
* When there are multiple BSS-es in the preferred band, the driver
|
||||
* shall use RSSI-based BSS selection as a second step. The value of
|
||||
* this attribute is according to &enum nl80211_band (u32).
|
||||
* @NL80211_BSS_SELECT_ATTR_RSSI_ADJUST: When present the RSSI level for
|
||||
* BSS-es in the specified band is to be adjusted before doing
|
||||
* RSSI-based BSS selection. The attribute value is a packed structure
|
||||
* value as specified by &struct nl80211_bss_select_rssi_adjust.
|
||||
* @NL80211_BSS_SELECT_ATTR_MAX: highest bss select attribute number.
|
||||
* @__NL80211_BSS_SELECT_ATTR_AFTER_LAST: internal use.
|
||||
*
|
||||
* One and only one of these attributes are found within %NL80211_ATTR_BSS_SELECT
|
||||
* for %NL80211_CMD_CONNECT. It specifies the required BSS selection behaviour
|
||||
* which the driver shall use.
|
||||
*/
|
||||
enum nl80211_bss_select_attr {
|
||||
__NL80211_BSS_SELECT_ATTR_INVALID,
|
||||
NL80211_BSS_SELECT_ATTR_RSSI,
|
||||
NL80211_BSS_SELECT_ATTR_BAND_PREF,
|
||||
NL80211_BSS_SELECT_ATTR_RSSI_ADJUST,
|
||||
|
||||
/* keep last */
|
||||
__NL80211_BSS_SELECT_ATTR_AFTER_LAST,
|
||||
NL80211_BSS_SELECT_ATTR_MAX = __NL80211_BSS_SELECT_ATTR_AFTER_LAST - 1
|
||||
};
|
||||
|
||||
#endif /* __LINUX_NL80211_H */
|
||||
|
|
|
@ -487,6 +487,7 @@ EXPORT_SYMBOL_GPL(rhashtable_insert_slow);
|
|||
* rhashtable_walk_init - Initialise an iterator
|
||||
* @ht: Table to walk over
|
||||
* @iter: Hash table Iterator
|
||||
* @gfp: GFP flags for allocations
|
||||
*
|
||||
* This function prepares a hash table walk.
|
||||
*
|
||||
|
@ -504,14 +505,15 @@ EXPORT_SYMBOL_GPL(rhashtable_insert_slow);
|
|||
* You must call rhashtable_walk_exit if this function returns
|
||||
* successfully.
|
||||
*/
|
||||
int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter)
|
||||
int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter,
|
||||
gfp_t gfp)
|
||||
{
|
||||
iter->ht = ht;
|
||||
iter->p = NULL;
|
||||
iter->slot = 0;
|
||||
iter->skip = 0;
|
||||
|
||||
iter->walker = kmalloc(sizeof(*iter->walker), GFP_KERNEL);
|
||||
iter->walker = kmalloc(sizeof(*iter->walker), gfp);
|
||||
if (!iter->walker)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -143,7 +143,7 @@ static void test_bucket_stats(struct rhashtable *ht)
|
|||
struct rhashtable_iter hti;
|
||||
struct rhash_head *pos;
|
||||
|
||||
err = rhashtable_walk_init(ht, &hti);
|
||||
err = rhashtable_walk_init(ht, &hti, GFP_KERNEL);
|
||||
if (err) {
|
||||
pr_warn("Test failed: allocation error");
|
||||
return;
|
||||
|
|
|
@ -501,7 +501,8 @@ static int ila_nl_dump_start(struct netlink_callback *cb)
|
|||
struct ila_net *ilan = net_generic(net, ila_net_id);
|
||||
struct ila_dump_iter *iter = (struct ila_dump_iter *)cb->args;
|
||||
|
||||
return rhashtable_walk_init(&ilan->rhash_table, &iter->rhiter);
|
||||
return rhashtable_walk_init(&ilan->rhash_table, &iter->rhiter,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
|
||||
static int ila_nl_dump_done(struct netlink_callback *cb)
|
||||
|
|
|
@ -935,6 +935,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
|
|||
size_t len)
|
||||
{
|
||||
struct tid_ampdu_tx *tid_tx;
|
||||
struct ieee80211_txq *txq;
|
||||
u16 capab, tid;
|
||||
u8 buf_size;
|
||||
bool amsdu;
|
||||
|
@ -945,6 +946,10 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
|
|||
buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
|
||||
buf_size = min(buf_size, local->hw.max_tx_aggregation_subframes);
|
||||
|
||||
txq = sta->sta.txq[tid];
|
||||
if (!amsdu && txq)
|
||||
set_bit(IEEE80211_TXQ_NO_AMSDU, &to_txq_info(txq)->flags);
|
||||
|
||||
mutex_lock(&sta->ampdu_mlme.mtx);
|
||||
|
||||
tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
|
||||
|
|
|
@ -65,11 +65,13 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
|
|||
return ret;
|
||||
|
||||
if (type == NL80211_IFTYPE_AP_VLAN &&
|
||||
params && params->use_4addr == 0)
|
||||
params && params->use_4addr == 0) {
|
||||
RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
|
||||
else if (type == NL80211_IFTYPE_STATION &&
|
||||
params && params->use_4addr >= 0)
|
||||
ieee80211_check_fast_rx_iface(sdata);
|
||||
} else if (type == NL80211_IFTYPE_STATION &&
|
||||
params && params->use_4addr >= 0) {
|
||||
sdata->u.mgd.use_4addr = params->use_4addr;
|
||||
}
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_MONITOR && flags) {
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
|
@ -732,6 +734,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
|
|||
sdata->vif.bss_conf.beacon_int = params->beacon_interval;
|
||||
sdata->vif.bss_conf.dtim_period = params->dtim_period;
|
||||
sdata->vif.bss_conf.enable_beacon = true;
|
||||
sdata->vif.bss_conf.allow_p2p_go_ps = sdata->vif.p2p;
|
||||
|
||||
sdata->vif.bss_conf.ssid_len = params->ssid_len;
|
||||
if (params->ssid_len)
|
||||
|
@ -1202,6 +1205,9 @@ static int sta_apply_parameters(struct ieee80211_local *local,
|
|||
params->opmode_notif, band);
|
||||
}
|
||||
|
||||
if (params->support_p2p_ps >= 0)
|
||||
sta->sta.support_p2p_ps = params->support_p2p_ps;
|
||||
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif))
|
||||
sta_apply_mesh_params(local, sta, params);
|
||||
|
||||
|
@ -1363,6 +1369,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
|
|||
|
||||
rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
|
||||
new_4addr = true;
|
||||
__ieee80211_check_fast_rx_iface(vlansdata);
|
||||
}
|
||||
|
||||
if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
|
||||
|
@ -1499,7 +1506,7 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
|
|||
|
||||
memset(pinfo, 0, sizeof(*pinfo));
|
||||
|
||||
pinfo->generation = mesh_paths_generation;
|
||||
pinfo->generation = mpath->sdata->u.mesh.mesh_paths_generation;
|
||||
|
||||
pinfo->filled = MPATH_INFO_FRAME_QLEN |
|
||||
MPATH_INFO_SN |
|
||||
|
@ -1577,7 +1584,7 @@ static void mpp_set_pinfo(struct mesh_path *mpath, u8 *mpp,
|
|||
memset(pinfo, 0, sizeof(*pinfo));
|
||||
memcpy(mpp, mpath->mpp, ETH_ALEN);
|
||||
|
||||
pinfo->generation = mpp_paths_generation;
|
||||
pinfo->generation = mpath->sdata->u.mesh.mpp_paths_generation;
|
||||
}
|
||||
|
||||
static int ieee80211_get_mpp(struct wiphy *wiphy, struct net_device *dev,
|
||||
|
@ -1885,6 +1892,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
|
|||
sdata->flags |= IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
|
||||
else
|
||||
sdata->flags &= ~IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
|
||||
ieee80211_check_fast_rx_iface(sdata);
|
||||
}
|
||||
|
||||
if (params->ht_opmode >= 0) {
|
||||
|
|
|
@ -127,6 +127,9 @@ static const char *hw_flag_names[] = {
|
|||
FLAG(BEACON_TX_STATUS),
|
||||
FLAG(NEEDS_UNIQUE_STA_ADDR),
|
||||
FLAG(SUPPORTS_REORDERING_BUFFER),
|
||||
FLAG(USES_RSS),
|
||||
FLAG(TX_AMSDU),
|
||||
FLAG(TX_FRAG_LIST),
|
||||
#undef FLAG
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
|
||||
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright(c) 2016 Intel Deutschland GmbH
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
|
@ -51,31 +52,54 @@ static const struct file_operations sta_ ##name## _ops = { \
|
|||
|
||||
STA_FILE(aid, sta.aid, D);
|
||||
|
||||
static const char * const sta_flag_names[] = {
|
||||
#define FLAG(F) [WLAN_STA_##F] = #F
|
||||
FLAG(AUTH),
|
||||
FLAG(ASSOC),
|
||||
FLAG(PS_STA),
|
||||
FLAG(AUTHORIZED),
|
||||
FLAG(SHORT_PREAMBLE),
|
||||
FLAG(WDS),
|
||||
FLAG(CLEAR_PS_FILT),
|
||||
FLAG(MFP),
|
||||
FLAG(BLOCK_BA),
|
||||
FLAG(PS_DRIVER),
|
||||
FLAG(PSPOLL),
|
||||
FLAG(TDLS_PEER),
|
||||
FLAG(TDLS_PEER_AUTH),
|
||||
FLAG(TDLS_INITIATOR),
|
||||
FLAG(TDLS_CHAN_SWITCH),
|
||||
FLAG(TDLS_OFF_CHANNEL),
|
||||
FLAG(TDLS_WIDER_BW),
|
||||
FLAG(UAPSD),
|
||||
FLAG(SP),
|
||||
FLAG(4ADDR_EVENT),
|
||||
FLAG(INSERTED),
|
||||
FLAG(RATE_CONTROL),
|
||||
FLAG(TOFFSET_KNOWN),
|
||||
FLAG(MPSP_OWNER),
|
||||
FLAG(MPSP_RECIPIENT),
|
||||
FLAG(PS_DELIVER),
|
||||
#undef FLAG
|
||||
};
|
||||
|
||||
static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[121];
|
||||
char buf[16 * NUM_WLAN_STA_FLAGS], *pos = buf;
|
||||
char *end = buf + sizeof(buf) - 1;
|
||||
struct sta_info *sta = file->private_data;
|
||||
unsigned int flg;
|
||||
|
||||
#define TEST(flg) \
|
||||
test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : ""
|
||||
BUILD_BUG_ON(ARRAY_SIZE(sta_flag_names) != NUM_WLAN_STA_FLAGS);
|
||||
|
||||
int res = scnprintf(buf, sizeof(buf),
|
||||
"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
|
||||
TEST(AUTH), TEST(ASSOC), TEST(PS_STA),
|
||||
TEST(PS_DRIVER), TEST(AUTHORIZED),
|
||||
TEST(SHORT_PREAMBLE),
|
||||
sta->sta.wme ? "WME\n" : "",
|
||||
TEST(WDS), TEST(CLEAR_PS_FILT),
|
||||
TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL),
|
||||
TEST(UAPSD), TEST(SP), TEST(TDLS_PEER),
|
||||
TEST(TDLS_PEER_AUTH), TEST(TDLS_INITIATOR),
|
||||
TEST(TDLS_CHAN_SWITCH), TEST(TDLS_OFF_CHANNEL),
|
||||
TEST(4ADDR_EVENT), TEST(INSERTED),
|
||||
TEST(RATE_CONTROL), TEST(TOFFSET_KNOWN),
|
||||
TEST(MPSP_OWNER), TEST(MPSP_RECIPIENT));
|
||||
#undef TEST
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
|
||||
for (flg = 0; flg < NUM_WLAN_STA_FLAGS; flg++) {
|
||||
if (test_sta_flag(sta, flg))
|
||||
pos += scnprintf(pos, end - pos, "%s\n",
|
||||
sta_flag_names[flg]);
|
||||
}
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
|
||||
}
|
||||
STA_OPS(flags);
|
||||
|
||||
|
@ -151,11 +175,12 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
|
|||
static ssize_t sta_agg_status_write(struct file *file, const char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char _buf[12] = {}, *buf = _buf;
|
||||
char _buf[25] = {}, *buf = _buf;
|
||||
struct sta_info *sta = file->private_data;
|
||||
bool start, tx;
|
||||
unsigned long tid;
|
||||
int ret;
|
||||
char *pos;
|
||||
int ret, timeout = 5000;
|
||||
|
||||
if (count > sizeof(_buf))
|
||||
return -EINVAL;
|
||||
|
@ -164,37 +189,48 @@ static ssize_t sta_agg_status_write(struct file *file, const char __user *userbu
|
|||
return -EFAULT;
|
||||
|
||||
buf[sizeof(_buf) - 1] = '\0';
|
||||
|
||||
if (strncmp(buf, "tx ", 3) == 0) {
|
||||
buf += 3;
|
||||
tx = true;
|
||||
} else if (strncmp(buf, "rx ", 3) == 0) {
|
||||
buf += 3;
|
||||
tx = false;
|
||||
} else
|
||||
pos = buf;
|
||||
buf = strsep(&pos, " ");
|
||||
if (!buf)
|
||||
return -EINVAL;
|
||||
|
||||
if (strncmp(buf, "start ", 6) == 0) {
|
||||
buf += 6;
|
||||
if (!strcmp(buf, "tx"))
|
||||
tx = true;
|
||||
else if (!strcmp(buf, "rx"))
|
||||
tx = false;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
buf = strsep(&pos, " ");
|
||||
if (!buf)
|
||||
return -EINVAL;
|
||||
if (!strcmp(buf, "start")) {
|
||||
start = true;
|
||||
if (!tx)
|
||||
return -EINVAL;
|
||||
} else if (strncmp(buf, "stop ", 5) == 0) {
|
||||
buf += 5;
|
||||
} else if (!strcmp(buf, "stop")) {
|
||||
start = false;
|
||||
} else
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
buf = strsep(&pos, " ");
|
||||
if (!buf)
|
||||
return -EINVAL;
|
||||
if (sscanf(buf, "timeout=%d", &timeout) == 1) {
|
||||
buf = strsep(&pos, " ");
|
||||
if (!buf || !tx || !start)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = kstrtoul(buf, 0, &tid);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (tid >= IEEE80211_NUM_TIDS)
|
||||
if (ret || tid >= IEEE80211_NUM_TIDS)
|
||||
return -EINVAL;
|
||||
|
||||
if (tx) {
|
||||
if (start)
|
||||
ret = ieee80211_start_tx_ba_session(&sta->sta, tid, 5000);
|
||||
ret = ieee80211_start_tx_ba_session(&sta->sta, tid,
|
||||
timeout);
|
||||
else
|
||||
ret = ieee80211_stop_tx_ba_session(&sta->sta, tid);
|
||||
} else {
|
||||
|
@ -322,14 +358,14 @@ STA_OPS(vht_capa);
|
|||
|
||||
#define DEBUGFS_ADD(name) \
|
||||
debugfs_create_file(#name, 0400, \
|
||||
sta->debugfs.dir, sta, &sta_ ##name## _ops);
|
||||
sta->debugfs_dir, sta, &sta_ ##name## _ops);
|
||||
|
||||
#define DEBUGFS_ADD_COUNTER(name, field) \
|
||||
if (sizeof(sta->field) == sizeof(u32)) \
|
||||
debugfs_create_u32(#name, 0400, sta->debugfs.dir, \
|
||||
debugfs_create_u32(#name, 0400, sta->debugfs_dir, \
|
||||
(u32 *) &sta->field); \
|
||||
else \
|
||||
debugfs_create_u64(#name, 0400, sta->debugfs.dir, \
|
||||
debugfs_create_u64(#name, 0400, sta->debugfs_dir, \
|
||||
(u64 *) &sta->field);
|
||||
|
||||
void ieee80211_sta_debugfs_add(struct sta_info *sta)
|
||||
|
@ -339,8 +375,6 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
|
|||
struct dentry *stations_dir = sta->sdata->debugfs.subdir_stations;
|
||||
u8 mac[3*ETH_ALEN];
|
||||
|
||||
sta->debugfs.add_has_run = true;
|
||||
|
||||
if (!stations_dir)
|
||||
return;
|
||||
|
||||
|
@ -355,8 +389,8 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
|
|||
* destroyed quickly enough the old station's debugfs
|
||||
* dir might still be around.
|
||||
*/
|
||||
sta->debugfs.dir = debugfs_create_dir(mac, stations_dir);
|
||||
if (!sta->debugfs.dir)
|
||||
sta->debugfs_dir = debugfs_create_dir(mac, stations_dir);
|
||||
if (!sta->debugfs_dir)
|
||||
return;
|
||||
|
||||
DEBUGFS_ADD(flags);
|
||||
|
@ -372,14 +406,14 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
|
|||
|
||||
if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
|
||||
debugfs_create_x32("driver_buffered_tids", 0400,
|
||||
sta->debugfs.dir,
|
||||
sta->debugfs_dir,
|
||||
(u32 *)&sta->driver_buffered_tids);
|
||||
else
|
||||
debugfs_create_x64("driver_buffered_tids", 0400,
|
||||
sta->debugfs.dir,
|
||||
sta->debugfs_dir,
|
||||
(u64 *)&sta->driver_buffered_tids);
|
||||
|
||||
drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs.dir);
|
||||
drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs_dir);
|
||||
}
|
||||
|
||||
void ieee80211_sta_debugfs_remove(struct sta_info *sta)
|
||||
|
@ -387,7 +421,7 @@ void ieee80211_sta_debugfs_remove(struct sta_info *sta)
|
|||
struct ieee80211_local *local = sta->local;
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
|
||||
drv_sta_remove_debugfs(local, sdata, &sta->sta, sta->debugfs.dir);
|
||||
debugfs_remove_recursive(sta->debugfs.dir);
|
||||
sta->debugfs.dir = NULL;
|
||||
drv_sta_remove_debugfs(local, sdata, &sta->sta, sta->debugfs_dir);
|
||||
debugfs_remove_recursive(sta->debugfs_dir);
|
||||
sta->debugfs_dir = NULL;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*
|
||||
* Portions of this file
|
||||
* Copyright(c) 2016 Intel Deutschland GmbH
|
||||
*/
|
||||
|
||||
#ifndef __MAC80211_DRIVER_OPS
|
||||
#define __MAC80211_DRIVER_OPS
|
||||
|
||||
|
@ -29,6 +34,16 @@ static inline void drv_tx(struct ieee80211_local *local,
|
|||
local->ops->tx(&local->hw, control, skb);
|
||||
}
|
||||
|
||||
static inline void drv_sync_rx_queues(struct ieee80211_local *local,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
if (local->ops->sync_rx_queues) {
|
||||
trace_drv_sync_rx_queues(local, sta->sdata, &sta->sta);
|
||||
local->ops->sync_rx_queues(&local->hw);
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void drv_get_et_strings(struct ieee80211_sub_if_data *sdata,
|
||||
u32 sset, u8 *data)
|
||||
{
|
||||
|
|
|
@ -649,8 +649,6 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
sta->rx_stats.last_rx = jiffies;
|
||||
|
||||
/* make sure mandatory rates are always added */
|
||||
sband = local->hw.wiphy->bands[band];
|
||||
sta->sta.supp_rates[band] = supp_rates |
|
||||
|
@ -670,10 +668,11 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
|
|||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||
unsigned long last_active = ieee80211_sta_last_active(sta);
|
||||
|
||||
if (sta->sdata == sdata &&
|
||||
time_after(sta->rx_stats.last_rx +
|
||||
IEEE80211_IBSS_MERGE_INTERVAL,
|
||||
jiffies)) {
|
||||
time_is_after_jiffies(last_active +
|
||||
IEEE80211_IBSS_MERGE_INTERVAL)) {
|
||||
active++;
|
||||
break;
|
||||
}
|
||||
|
@ -1236,8 +1235,6 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
|
|||
if (!sta)
|
||||
return;
|
||||
|
||||
sta->rx_stats.last_rx = jiffies;
|
||||
|
||||
/* make sure mandatory rates are always added */
|
||||
sband = local->hw.wiphy->bands[band];
|
||||
sta->sta.supp_rates[band] = supp_rates |
|
||||
|
@ -1259,11 +1256,13 @@ static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)
|
|||
mutex_lock(&local->sta_mtx);
|
||||
|
||||
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
|
||||
unsigned long last_active = ieee80211_sta_last_active(sta);
|
||||
|
||||
if (sdata != sta->sdata)
|
||||
continue;
|
||||
|
||||
if (time_after(jiffies, sta->rx_stats.last_rx + exp_time) ||
|
||||
(time_after(jiffies, sta->rx_stats.last_rx + exp_rsn) &&
|
||||
if (time_is_before_jiffies(last_active + exp_time) ||
|
||||
(time_is_before_jiffies(last_active + exp_rsn) &&
|
||||
sta->sta_state != IEEE80211_STA_AUTHORIZED)) {
|
||||
sta_dbg(sta->sdata, "expiring inactive %sSTA %pM\n",
|
||||
sta->sta_state != IEEE80211_STA_AUTHORIZED ?
|
||||
|
|
|
@ -696,6 +696,11 @@ struct ieee80211_if_mesh {
|
|||
|
||||
/* offset from skb->data while building IE */
|
||||
int meshconf_offset;
|
||||
|
||||
struct mesh_table *mesh_paths;
|
||||
struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */
|
||||
int mesh_paths_generation;
|
||||
int mpp_paths_generation;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
|
@ -797,6 +802,7 @@ struct mac80211_qos_map {
|
|||
enum txq_info_flags {
|
||||
IEEE80211_TXQ_STOP,
|
||||
IEEE80211_TXQ_AMPDU,
|
||||
IEEE80211_TXQ_NO_AMSDU,
|
||||
};
|
||||
|
||||
struct txq_info {
|
||||
|
@ -1489,6 +1495,11 @@ u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local);
|
|||
int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb,
|
||||
u64 *cookie, gfp_t gfp);
|
||||
|
||||
void ieee80211_check_fast_rx(struct sta_info *sta);
|
||||
void __ieee80211_check_fast_rx_iface(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_check_fast_rx_iface(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_clear_fast_rx(struct sta_info *sta);
|
||||
|
||||
/* STA code */
|
||||
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata);
|
||||
int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
|
||||
|
|
|
@ -1093,7 +1093,7 @@ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
|
|||
sdata->fragment_next = 0;
|
||||
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif))
|
||||
mesh_rmc_free(sdata);
|
||||
ieee80211_mesh_teardown_sdata(sdata);
|
||||
}
|
||||
|
||||
static void ieee80211_uninit(struct net_device *dev)
|
||||
|
|
|
@ -338,6 +338,7 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
|
|||
} else {
|
||||
rcu_assign_pointer(sta->gtk[idx], new);
|
||||
}
|
||||
ieee80211_check_fast_rx(sta);
|
||||
} else {
|
||||
defunikey = old &&
|
||||
old == key_mtx_dereference(sdata->local,
|
||||
|
|
|
@ -558,6 +558,8 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
|
|||
if (!ops->set_key)
|
||||
wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
|
||||
|
||||
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_RRM);
|
||||
|
||||
wiphy->bss_priv_size = sizeof(struct ieee80211_bss);
|
||||
|
||||
local = wiphy_priv(wiphy);
|
||||
|
@ -854,7 +856,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
|||
/* Only HW csum features are currently compatible with mac80211 */
|
||||
feature_whitelist = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
|
||||
NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_HIGHDMA |
|
||||
NETIF_F_GSO_SOFTWARE;
|
||||
NETIF_F_GSO_SOFTWARE | NETIF_F_RXCSUM;
|
||||
if (WARN_ON(hw->netdev_features & ~feature_whitelist))
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt)
|
|||
|
||||
void ieee80211s_init(void)
|
||||
{
|
||||
mesh_pathtbl_init();
|
||||
mesh_allocated = 1;
|
||||
rm_cache = kmem_cache_create("mesh_rmc", sizeof(struct rmc_entry),
|
||||
0, 0, NULL);
|
||||
|
@ -35,7 +34,6 @@ void ieee80211s_stop(void)
|
|||
{
|
||||
if (!mesh_allocated)
|
||||
return;
|
||||
mesh_pathtbl_unregister();
|
||||
kmem_cache_destroy(rm_cache);
|
||||
}
|
||||
|
||||
|
@ -176,22 +174,23 @@ int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
|
|||
return -ENOMEM;
|
||||
sdata->u.mesh.rmc->idx_mask = RMC_BUCKETS - 1;
|
||||
for (i = 0; i < RMC_BUCKETS; i++)
|
||||
INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i]);
|
||||
INIT_HLIST_HEAD(&sdata->u.mesh.rmc->bucket[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mesh_rmc_free(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct mesh_rmc *rmc = sdata->u.mesh.rmc;
|
||||
struct rmc_entry *p, *n;
|
||||
struct rmc_entry *p;
|
||||
struct hlist_node *n;
|
||||
int i;
|
||||
|
||||
if (!sdata->u.mesh.rmc)
|
||||
return;
|
||||
|
||||
for (i = 0; i < RMC_BUCKETS; i++) {
|
||||
list_for_each_entry_safe(p, n, &rmc->bucket[i], list) {
|
||||
list_del(&p->list);
|
||||
hlist_for_each_entry_safe(p, n, &rmc->bucket[i], list) {
|
||||
hlist_del(&p->list);
|
||||
kmem_cache_free(rm_cache, p);
|
||||
}
|
||||
}
|
||||
|
@ -220,16 +219,20 @@ int mesh_rmc_check(struct ieee80211_sub_if_data *sdata,
|
|||
u32 seqnum = 0;
|
||||
int entries = 0;
|
||||
u8 idx;
|
||||
struct rmc_entry *p, *n;
|
||||
struct rmc_entry *p;
|
||||
struct hlist_node *n;
|
||||
|
||||
if (!rmc)
|
||||
return -1;
|
||||
|
||||
/* Don't care about endianness since only match matters */
|
||||
memcpy(&seqnum, &mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum));
|
||||
idx = le32_to_cpu(mesh_hdr->seqnum) & rmc->idx_mask;
|
||||
list_for_each_entry_safe(p, n, &rmc->bucket[idx], list) {
|
||||
hlist_for_each_entry_safe(p, n, &rmc->bucket[idx], list) {
|
||||
++entries;
|
||||
if (time_after(jiffies, p->exp_time) ||
|
||||
entries == RMC_QUEUE_MAX_LEN) {
|
||||
list_del(&p->list);
|
||||
hlist_del(&p->list);
|
||||
kmem_cache_free(rm_cache, p);
|
||||
--entries;
|
||||
} else if ((seqnum == p->seqnum) && ether_addr_equal(sa, p->sa))
|
||||
|
@ -243,7 +246,7 @@ int mesh_rmc_check(struct ieee80211_sub_if_data *sdata,
|
|||
p->seqnum = seqnum;
|
||||
p->exp_time = jiffies + RMC_TIMEOUT;
|
||||
memcpy(p->sa, sa, ETH_ALEN);
|
||||
list_add(&p->list, &rmc->bucket[idx]);
|
||||
hlist_add_head(&p->list, &rmc->bucket[idx]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1348,12 +1351,6 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)
|
|||
ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval)))
|
||||
mesh_path_start_discovery(sdata);
|
||||
|
||||
if (test_and_clear_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags))
|
||||
mesh_mpath_table_grow();
|
||||
|
||||
if (test_and_clear_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags))
|
||||
mesh_mpp_table_grow();
|
||||
|
||||
if (test_and_clear_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags))
|
||||
ieee80211_mesh_housekeeping(sdata);
|
||||
|
||||
|
@ -1388,6 +1385,9 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
|
|||
/* Allocate all mesh structures when creating the first mesh interface. */
|
||||
if (!mesh_allocated)
|
||||
ieee80211s_init();
|
||||
|
||||
mesh_pathtbl_init(sdata);
|
||||
|
||||
setup_timer(&ifmsh->mesh_path_timer,
|
||||
ieee80211_mesh_path_timer,
|
||||
(unsigned long) sdata);
|
||||
|
@ -1402,3 +1402,9 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
|
|||
|
||||
sdata->vif.bss_conf.bssid = zero_addr;
|
||||
}
|
||||
|
||||
void ieee80211_mesh_teardown_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
mesh_rmc_free(sdata);
|
||||
mesh_pathtbl_unregister(sdata);
|
||||
}
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
/**
|
||||
* enum mesh_path_flags - mac80211 mesh path flags
|
||||
*
|
||||
*
|
||||
*
|
||||
* @MESH_PATH_ACTIVE: the mesh path can be used for forwarding
|
||||
* @MESH_PATH_RESOLVING: the discovery process is running for this mesh path
|
||||
* @MESH_PATH_SN_VALID: the mesh path contains a valid destination sequence
|
||||
|
@ -32,6 +30,8 @@
|
|||
* @MESH_PATH_RESOLVED: the mesh path can has been resolved
|
||||
* @MESH_PATH_REQ_QUEUED: there is an unsent path request for this destination
|
||||
* already queued up, waiting for the discovery process to start.
|
||||
* @MESH_PATH_DELETED: the mesh path has been deleted and should no longer
|
||||
* be used
|
||||
*
|
||||
* MESH_PATH_RESOLVED is used by the mesh path timer to
|
||||
* decide when to stop or cancel the mesh path discovery.
|
||||
|
@ -43,6 +43,7 @@ enum mesh_path_flags {
|
|||
MESH_PATH_FIXED = BIT(3),
|
||||
MESH_PATH_RESOLVED = BIT(4),
|
||||
MESH_PATH_REQ_QUEUED = BIT(5),
|
||||
MESH_PATH_DELETED = BIT(6),
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -51,10 +52,6 @@ enum mesh_path_flags {
|
|||
*
|
||||
*
|
||||
* @MESH_WORK_HOUSEKEEPING: run the periodic mesh housekeeping tasks
|
||||
* @MESH_WORK_GROW_MPATH_TABLE: the mesh path table is full and needs
|
||||
* to grow.
|
||||
* @MESH_WORK_GROW_MPP_TABLE: the mesh portals table is full and needs to
|
||||
* grow
|
||||
* @MESH_WORK_ROOT: the mesh root station needs to send a frame
|
||||
* @MESH_WORK_DRIFT_ADJUST: time to compensate for clock drift relative to other
|
||||
* mesh nodes
|
||||
|
@ -62,8 +59,6 @@ enum mesh_path_flags {
|
|||
*/
|
||||
enum mesh_deferred_task_flags {
|
||||
MESH_WORK_HOUSEKEEPING,
|
||||
MESH_WORK_GROW_MPATH_TABLE,
|
||||
MESH_WORK_GROW_MPP_TABLE,
|
||||
MESH_WORK_ROOT,
|
||||
MESH_WORK_DRIFT_ADJUST,
|
||||
MESH_WORK_MBSS_CHANGED,
|
||||
|
@ -73,12 +68,16 @@ enum mesh_deferred_task_flags {
|
|||
* struct mesh_path - mac80211 mesh path structure
|
||||
*
|
||||
* @dst: mesh path destination mac address
|
||||
* @mpp: mesh proxy mac address
|
||||
* @rhash: rhashtable list pointer
|
||||
* @gate_list: list pointer for known gates list
|
||||
* @sdata: mesh subif
|
||||
* @next_hop: mesh neighbor to which frames for this destination will be
|
||||
* forwarded
|
||||
* @timer: mesh path discovery timer
|
||||
* @frame_queue: pending queue for frames sent to this destination while the
|
||||
* path is unresolved
|
||||
* @rcu: rcu head for freeing mesh path
|
||||
* @sn: target sequence number
|
||||
* @metric: current metric to this destination
|
||||
* @hop_count: hops to destination
|
||||
|
@ -97,14 +96,16 @@ enum mesh_deferred_task_flags {
|
|||
* @is_gate: the destination station of this path is a mesh gate
|
||||
*
|
||||
*
|
||||
* The combination of dst and sdata is unique in the mesh path table. Since the
|
||||
* next_hop STA is only protected by RCU as well, deleting the STA must also
|
||||
* remove/substitute the mesh_path structure and wait until that is no longer
|
||||
* reachable before destroying the STA completely.
|
||||
* The dst address is unique in the mesh path table. Since the mesh_path is
|
||||
* protected by RCU, deleting the next_hop STA must remove / substitute the
|
||||
* mesh_path structure and wait until that is no longer reachable before
|
||||
* destroying the STA completely.
|
||||
*/
|
||||
struct mesh_path {
|
||||
u8 dst[ETH_ALEN];
|
||||
u8 mpp[ETH_ALEN]; /* used for MPP or MAP */
|
||||
struct rhash_head rhash;
|
||||
struct hlist_node gate_list;
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct sta_info __rcu *next_hop;
|
||||
struct timer_list timer;
|
||||
|
@ -128,34 +129,17 @@ struct mesh_path {
|
|||
/**
|
||||
* struct mesh_table
|
||||
*
|
||||
* @hash_buckets: array of hash buckets of the table
|
||||
* @hashwlock: array of locks to protect write operations, one per bucket
|
||||
* @hash_mask: 2^size_order - 1, used to compute hash idx
|
||||
* @hash_rnd: random value used for hash computations
|
||||
* @entries: number of entries in the table
|
||||
* @free_node: function to free nodes of the table
|
||||
* @copy_node: function to copy nodes of the table
|
||||
* @size_order: determines size of the table, there will be 2^size_order hash
|
||||
* buckets
|
||||
* @known_gates: list of known mesh gates and their mpaths by the station. The
|
||||
* gate's mpath may or may not be resolved and active.
|
||||
*
|
||||
* rcu_head: RCU head to free the table
|
||||
* @gates_lock: protects updates to known_gates
|
||||
* @rhead: the rhashtable containing struct mesh_paths, keyed by dest addr
|
||||
* @entries: number of entries in the table
|
||||
*/
|
||||
struct mesh_table {
|
||||
/* Number of buckets will be 2^N */
|
||||
struct hlist_head *hash_buckets;
|
||||
spinlock_t *hashwlock; /* One per bucket, for add/del */
|
||||
unsigned int hash_mask; /* (2^size_order) - 1 */
|
||||
__u32 hash_rnd; /* Used for hash generation */
|
||||
atomic_t entries; /* Up to MAX_MESH_NEIGHBOURS */
|
||||
void (*free_node) (struct hlist_node *p, bool free_leafs);
|
||||
int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl);
|
||||
int size_order;
|
||||
struct hlist_head *known_gates;
|
||||
struct hlist_head known_gates;
|
||||
spinlock_t gates_lock;
|
||||
|
||||
struct rcu_head rcu_head;
|
||||
struct rhashtable rhead;
|
||||
atomic_t entries; /* Up to MAX_MESH_NEIGHBOURS */
|
||||
};
|
||||
|
||||
/* Recent multicast cache */
|
||||
|
@ -170,20 +154,21 @@ struct mesh_table {
|
|||
* @seqnum: mesh sequence number of the frame
|
||||
* @exp_time: expiration time of the entry, in jiffies
|
||||
* @sa: source address of the frame
|
||||
* @list: hashtable list pointer
|
||||
*
|
||||
* The Recent Multicast Cache keeps track of the latest multicast frames that
|
||||
* have been received by a mesh interface and discards received multicast frames
|
||||
* that are found in the cache.
|
||||
*/
|
||||
struct rmc_entry {
|
||||
struct list_head list;
|
||||
u32 seqnum;
|
||||
struct hlist_node list;
|
||||
unsigned long exp_time;
|
||||
u32 seqnum;
|
||||
u8 sa[ETH_ALEN];
|
||||
};
|
||||
|
||||
struct mesh_rmc {
|
||||
struct list_head bucket[RMC_BUCKETS];
|
||||
struct hlist_head bucket[RMC_BUCKETS];
|
||||
u32 idx_mask;
|
||||
};
|
||||
|
||||
|
@ -234,6 +219,7 @@ void ieee80211s_init(void);
|
|||
void ieee80211s_update_metric(struct ieee80211_local *local,
|
||||
struct sta_info *sta, struct sk_buff *skb);
|
||||
void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_mesh_teardown_sdata(struct ieee80211_sub_if_data *sdata);
|
||||
int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh);
|
||||
|
@ -299,9 +285,6 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
|
|||
void mesh_sta_cleanup(struct sta_info *sta);
|
||||
|
||||
/* Private interfaces */
|
||||
/* Mesh tables */
|
||||
void mesh_mpath_table_grow(void);
|
||||
void mesh_mpp_table_grow(void);
|
||||
/* Mesh paths */
|
||||
int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,
|
||||
u8 ttl, const u8 *target, u32 target_sn,
|
||||
|
@ -309,8 +292,8 @@ int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,
|
|||
void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta);
|
||||
void mesh_path_flush_pending(struct mesh_path *mpath);
|
||||
void mesh_path_tx_pending(struct mesh_path *mpath);
|
||||
int mesh_pathtbl_init(void);
|
||||
void mesh_pathtbl_unregister(void);
|
||||
int mesh_pathtbl_init(struct ieee80211_sub_if_data *sdata);
|
||||
void mesh_pathtbl_unregister(struct ieee80211_sub_if_data *sdata);
|
||||
int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr);
|
||||
void mesh_path_timer(unsigned long data);
|
||||
void mesh_path_flush_by_nexthop(struct sta_info *sta);
|
||||
|
@ -319,8 +302,6 @@ void mesh_path_discard_frame(struct ieee80211_sub_if_data *sdata,
|
|||
void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);
|
||||
extern int mesh_paths_generation;
|
||||
extern int mpp_paths_generation;
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
static inline
|
||||
|
|
|
@ -1012,6 +1012,10 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
|
|||
goto enddiscovery;
|
||||
|
||||
spin_lock_bh(&mpath->state_lock);
|
||||
if (mpath->flags & MESH_PATH_DELETED) {
|
||||
spin_unlock_bh(&mpath->state_lock);
|
||||
goto enddiscovery;
|
||||
}
|
||||
mpath->flags &= ~MESH_PATH_REQ_QUEUED;
|
||||
if (preq_node->flags & PREQ_Q_F_START) {
|
||||
if (mpath->flags & MESH_PATH_RESOLVING) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -61,7 +61,7 @@ static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata,
|
|||
s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold;
|
||||
return rssi_threshold == 0 ||
|
||||
(sta &&
|
||||
(s8)-ewma_signal_read(&sta->rx_stats.avg_signal) >
|
||||
(s8)-ewma_signal_read(&sta->rx_stats_avg.signal) >
|
||||
rssi_threshold);
|
||||
}
|
||||
|
||||
|
@ -331,7 +331,9 @@ free:
|
|||
*
|
||||
* @sta: mesh peer link to deactivate
|
||||
*
|
||||
* All mesh paths with this peer as next hop will be flushed
|
||||
* Mesh paths with this peer as next hop should be flushed
|
||||
* by the caller outside of plink_lock.
|
||||
*
|
||||
* Returns beacon changed flag if the beacon content changed.
|
||||
*
|
||||
* Locking: the caller must hold sta->mesh->plink_lock
|
||||
|
@ -346,7 +348,6 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)
|
|||
if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
|
||||
changed = mesh_plink_dec_estab_count(sdata);
|
||||
sta->mesh->plink_state = NL80211_PLINK_BLOCKED;
|
||||
mesh_path_flush_by_nexthop(sta);
|
||||
|
||||
ieee80211_mps_sta_status_update(sta);
|
||||
changed |= ieee80211_mps_set_sta_local_pm(sta,
|
||||
|
@ -374,6 +375,7 @@ u32 mesh_plink_deactivate(struct sta_info *sta)
|
|||
sta->sta.addr, sta->mesh->llid, sta->mesh->plid,
|
||||
sta->mesh->reason);
|
||||
spin_unlock_bh(&sta->mesh->plink_lock);
|
||||
mesh_path_flush_by_nexthop(sta);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
@ -748,6 +750,7 @@ u32 mesh_plink_block(struct sta_info *sta)
|
|||
changed = __mesh_plink_deactivate(sta);
|
||||
sta->mesh->plink_state = NL80211_PLINK_BLOCKED;
|
||||
spin_unlock_bh(&sta->mesh->plink_lock);
|
||||
mesh_path_flush_by_nexthop(sta);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
@ -797,6 +800,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
|
|||
struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
|
||||
enum ieee80211_self_protected_actioncode action = 0;
|
||||
u32 changed = 0;
|
||||
bool flush = false;
|
||||
|
||||
mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr,
|
||||
mplstates[sta->mesh->plink_state], mplevents[event]);
|
||||
|
@ -885,6 +889,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
|
|||
changed |= mesh_set_short_slot_time(sdata);
|
||||
mesh_plink_close(sdata, sta, event);
|
||||
action = WLAN_SP_MESH_PEERING_CLOSE;
|
||||
flush = true;
|
||||
break;
|
||||
case OPN_ACPT:
|
||||
action = WLAN_SP_MESH_PEERING_CONFIRM;
|
||||
|
@ -916,6 +921,8 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
|
|||
break;
|
||||
}
|
||||
spin_unlock_bh(&sta->mesh->plink_lock);
|
||||
if (flush)
|
||||
mesh_path_flush_by_nexthop(sta);
|
||||
if (action) {
|
||||
mesh_plink_frame_tx(sdata, sta, action, sta->sta.addr,
|
||||
sta->mesh->llid, sta->mesh->plid,
|
||||
|
|
|
@ -122,15 +122,16 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata)
|
|||
{
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
|
||||
if (unlikely(!sdata->u.mgd.associated))
|
||||
if (unlikely(!ifmgd->associated))
|
||||
return;
|
||||
|
||||
if (ifmgd->probe_send_count)
|
||||
ifmgd->probe_send_count = 0;
|
||||
|
||||
if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
|
||||
return;
|
||||
|
||||
mod_timer(&sdata->u.mgd.conn_mon_timer,
|
||||
mod_timer(&ifmgd->conn_mon_timer,
|
||||
round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
|
||||
}
|
||||
|
||||
|
@ -2216,6 +2217,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
|
|||
const u8 *ssid;
|
||||
u8 *dst = ifmgd->associated->bssid;
|
||||
u8 unicast_limit = max(1, max_probe_tries - 3);
|
||||
struct sta_info *sta;
|
||||
|
||||
/*
|
||||
* Try sending broadcast probe requests for the last three
|
||||
|
@ -2234,6 +2236,14 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
|
|||
*/
|
||||
ifmgd->probe_send_count++;
|
||||
|
||||
if (dst) {
|
||||
mutex_lock(&sdata->local->sta_mtx);
|
||||
sta = sta_info_get(sdata, dst);
|
||||
if (!WARN_ON(!sta))
|
||||
ieee80211_check_fast_rx(sta);
|
||||
mutex_unlock(&sdata->local->sta_mtx);
|
||||
}
|
||||
|
||||
if (ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) {
|
||||
ifmgd->nullfunc_failed = false;
|
||||
ieee80211_send_nullfunc(sdata->local, sdata, false);
|
||||
|
|
|
@ -75,8 +75,6 @@ void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
|
|||
if (!sta)
|
||||
return;
|
||||
|
||||
sta->rx_stats.last_rx = jiffies;
|
||||
|
||||
/* Add only mandatory rates for now */
|
||||
sband = local->hw.wiphy->bands[band];
|
||||
sta->sta.supp_rates[band] =
|
||||
|
|
|
@ -96,9 +96,9 @@ static inline void rate_control_add_sta_debugfs(struct sta_info *sta)
|
|||
{
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
struct rate_control_ref *ref = sta->rate_ctrl;
|
||||
if (ref && sta->debugfs.dir && ref->ops->add_sta_debugfs)
|
||||
if (ref && sta->debugfs_dir && ref->ops->add_sta_debugfs)
|
||||
ref->ops->add_sta_debugfs(ref->priv, sta->rate_ctrl_priv,
|
||||
sta->debugfs.dir);
|
||||
sta->debugfs_dir);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -883,6 +883,59 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
|||
ratetbl->rate[offset].flags = flags;
|
||||
}
|
||||
|
||||
static inline int
|
||||
minstrel_ht_get_prob_ewma(struct minstrel_ht_sta *mi, int rate)
|
||||
{
|
||||
int group = rate / MCS_GROUP_RATES;
|
||||
rate %= MCS_GROUP_RATES;
|
||||
return mi->groups[group].rates[rate].prob_ewma;
|
||||
}
|
||||
|
||||
static int
|
||||
minstrel_ht_get_max_amsdu_len(struct minstrel_ht_sta *mi)
|
||||
{
|
||||
int group = mi->max_prob_rate / MCS_GROUP_RATES;
|
||||
const struct mcs_group *g = &minstrel_mcs_groups[group];
|
||||
int rate = mi->max_prob_rate % MCS_GROUP_RATES;
|
||||
|
||||
/* Disable A-MSDU if max_prob_rate is bad */
|
||||
if (mi->groups[group].rates[rate].prob_ewma < MINSTREL_FRAC(50, 100))
|
||||
return 1;
|
||||
|
||||
/* If the rate is slower than single-stream MCS1, make A-MSDU limit small */
|
||||
if (g->duration[rate] > MCS_DURATION(1, 0, 52))
|
||||
return 500;
|
||||
|
||||
/*
|
||||
* If the rate is slower than single-stream MCS4, limit A-MSDU to usual
|
||||
* data packet size
|
||||
*/
|
||||
if (g->duration[rate] > MCS_DURATION(1, 0, 104))
|
||||
return 1600;
|
||||
|
||||
/*
|
||||
* If the rate is slower than single-stream MCS7, or if the max throughput
|
||||
* rate success probability is less than 75%, limit A-MSDU to twice the usual
|
||||
* data packet size
|
||||
*/
|
||||
if (g->duration[rate] > MCS_DURATION(1, 0, 260) ||
|
||||
(minstrel_ht_get_prob_ewma(mi, mi->max_tp_rate[0]) <
|
||||
MINSTREL_FRAC(75, 100)))
|
||||
return 3200;
|
||||
|
||||
/*
|
||||
* HT A-MPDU limits maximum MPDU size under BA agreement to 4095 bytes.
|
||||
* Since aggregation sessions are started/stopped without txq flush, use
|
||||
* the limit here to avoid the complexity of having to de-aggregate
|
||||
* packets in the queue.
|
||||
*/
|
||||
if (!mi->sta->vht_cap.vht_supported)
|
||||
return IEEE80211_MAX_MPDU_LEN_HT_BA;
|
||||
|
||||
/* unlimited */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
||||
{
|
||||
|
@ -907,6 +960,7 @@ minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
|||
minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_prob_rate);
|
||||
}
|
||||
|
||||
mi->sta->max_rc_amsdu_len = minstrel_ht_get_max_amsdu_len(mi);
|
||||
rates->rate[i].idx = -1;
|
||||
rate_control_set_rates(mp->hw, mi->sta, rates);
|
||||
}
|
||||
|
@ -924,6 +978,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
|||
struct minstrel_rate_stats *mrs;
|
||||
struct minstrel_mcs_group_data *mg;
|
||||
unsigned int sample_dur, sample_group, cur_max_tp_streams;
|
||||
int tp_rate1, tp_rate2;
|
||||
int sample_idx = 0;
|
||||
|
||||
if (mi->sample_wait > 0) {
|
||||
|
@ -945,14 +1000,22 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
|||
mrs = &mg->rates[sample_idx];
|
||||
sample_idx += sample_group * MCS_GROUP_RATES;
|
||||
|
||||
/* Set tp_rate1, tp_rate2 to the highest / second highest max_tp_rate */
|
||||
if (minstrel_get_duration(mi->max_tp_rate[0]) >
|
||||
minstrel_get_duration(mi->max_tp_rate[1])) {
|
||||
tp_rate1 = mi->max_tp_rate[1];
|
||||
tp_rate2 = mi->max_tp_rate[0];
|
||||
} else {
|
||||
tp_rate1 = mi->max_tp_rate[0];
|
||||
tp_rate2 = mi->max_tp_rate[1];
|
||||
}
|
||||
|
||||
/*
|
||||
* Sampling might add some overhead (RTS, no aggregation)
|
||||
* to the frame. Hence, don't use sampling for the currently
|
||||
* used rates.
|
||||
* to the frame. Hence, don't use sampling for the highest currently
|
||||
* used highest throughput or probability rate.
|
||||
*/
|
||||
if (sample_idx == mi->max_tp_rate[0] ||
|
||||
sample_idx == mi->max_tp_rate[1] ||
|
||||
sample_idx == mi->max_prob_rate)
|
||||
if (sample_idx == mi->max_tp_rate[0] || sample_idx == mi->max_prob_rate)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
|
@ -967,10 +1030,10 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
|||
* if the link is working perfectly.
|
||||
*/
|
||||
|
||||
cur_max_tp_streams = minstrel_mcs_groups[mi->max_tp_rate[0] /
|
||||
cur_max_tp_streams = minstrel_mcs_groups[tp_rate1 /
|
||||
MCS_GROUP_RATES].streams;
|
||||
sample_dur = minstrel_get_duration(sample_idx);
|
||||
if (sample_dur >= minstrel_get_duration(mi->max_tp_rate[1]) &&
|
||||
if (sample_dur >= minstrel_get_duration(tp_rate2) &&
|
||||
(cur_max_tp_streams - 1 <
|
||||
minstrel_mcs_groups[sample_group].streams ||
|
||||
sample_dur >= minstrel_get_duration(mi->max_prob_rate))) {
|
||||
|
|
|
@ -722,7 +722,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int iwl80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs,
|
||||
static int ieee80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
||||
|
@ -1421,16 +1421,9 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
|
|||
test_sta_flag(sta, WLAN_STA_AUTHORIZED)) {
|
||||
sta->rx_stats.last_rx = jiffies;
|
||||
if (ieee80211_is_data(hdr->frame_control) &&
|
||||
!is_multicast_ether_addr(hdr->addr1)) {
|
||||
sta->rx_stats.last_rate_idx =
|
||||
status->rate_idx;
|
||||
sta->rx_stats.last_rate_flag =
|
||||
status->flag;
|
||||
sta->rx_stats.last_rate_vht_flag =
|
||||
status->vht_flag;
|
||||
sta->rx_stats.last_rate_vht_nss =
|
||||
status->vht_nss;
|
||||
}
|
||||
!is_multicast_ether_addr(hdr->addr1))
|
||||
sta->rx_stats.last_rate =
|
||||
sta_stats_encode_rate(status);
|
||||
}
|
||||
} else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) {
|
||||
sta->rx_stats.last_rx = jiffies;
|
||||
|
@ -1440,22 +1433,22 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
|
|||
* match the current local configuration when processed.
|
||||
*/
|
||||
sta->rx_stats.last_rx = jiffies;
|
||||
if (ieee80211_is_data(hdr->frame_control)) {
|
||||
sta->rx_stats.last_rate_idx = status->rate_idx;
|
||||
sta->rx_stats.last_rate_flag = status->flag;
|
||||
sta->rx_stats.last_rate_vht_flag = status->vht_flag;
|
||||
sta->rx_stats.last_rate_vht_nss = status->vht_nss;
|
||||
}
|
||||
if (ieee80211_is_data(hdr->frame_control))
|
||||
sta->rx_stats.last_rate = sta_stats_encode_rate(status);
|
||||
}
|
||||
|
||||
if (rx->sdata->vif.type == NL80211_IFTYPE_STATION)
|
||||
ieee80211_sta_rx_notify(rx->sdata, hdr);
|
||||
|
||||
sta->rx_stats.fragments++;
|
||||
|
||||
u64_stats_update_begin(&rx->sta->rx_stats.syncp);
|
||||
sta->rx_stats.bytes += rx->skb->len;
|
||||
u64_stats_update_end(&rx->sta->rx_stats.syncp);
|
||||
|
||||
if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
|
||||
sta->rx_stats.last_signal = status->signal;
|
||||
ewma_signal_add(&sta->rx_stats.avg_signal, -status->signal);
|
||||
ewma_signal_add(&sta->rx_stats_avg.signal, -status->signal);
|
||||
}
|
||||
|
||||
if (status->chains) {
|
||||
|
@ -1467,7 +1460,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
|
|||
continue;
|
||||
|
||||
sta->rx_stats.chain_signal_last[i] = signal;
|
||||
ewma_signal_add(&sta->rx_stats.chain_signal_avg[i],
|
||||
ewma_signal_add(&sta->rx_stats_avg.chain_signal[i],
|
||||
-signal);
|
||||
}
|
||||
}
|
||||
|
@ -1586,7 +1579,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
|
|||
|
||||
if (ieee80211_has_protected(fc) && rx->sta->cipher_scheme) {
|
||||
cs = rx->sta->cipher_scheme;
|
||||
keyid = iwl80211_get_cs_keyid(cs, rx->skb);
|
||||
keyid = ieee80211_get_cs_keyid(cs, rx->skb);
|
||||
if (unlikely(keyid < 0))
|
||||
return RX_DROP_UNUSABLE;
|
||||
}
|
||||
|
@ -1670,7 +1663,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
|
|||
hdrlen = ieee80211_hdrlen(fc);
|
||||
|
||||
if (cs) {
|
||||
keyidx = iwl80211_get_cs_keyid(cs, rx->skb);
|
||||
keyidx = ieee80211_get_cs_keyid(cs, rx->skb);
|
||||
|
||||
if (unlikely(keyidx < 0))
|
||||
return RX_DROP_UNUSABLE;
|
||||
|
@ -2129,6 +2122,17 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
|
|||
|
||||
ieee80211_rx_stats(dev, skb->len);
|
||||
|
||||
if (rx->sta) {
|
||||
/* The seqno index has the same property as needed
|
||||
* for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
|
||||
* for non-QoS-data frames. Here we know it's a data
|
||||
* frame, so count MSDUs.
|
||||
*/
|
||||
u64_stats_update_begin(&rx->sta->rx_stats.syncp);
|
||||
rx->sta->rx_stats.msdu[rx->seqno_idx]++;
|
||||
u64_stats_update_end(&rx->sta->rx_stats.syncp);
|
||||
}
|
||||
|
||||
if ((sdata->vif.type == NL80211_IFTYPE_AP ||
|
||||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
|
||||
!(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) &&
|
||||
|
@ -2415,15 +2419,6 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
|
|||
if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
|
||||
return RX_DROP_MONITOR;
|
||||
|
||||
if (rx->sta) {
|
||||
/* The seqno index has the same property as needed
|
||||
* for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
|
||||
* for non-QoS-data frames. Here we know it's a data
|
||||
* frame, so count MSDUs.
|
||||
*/
|
||||
rx->sta->rx_stats.msdu[rx->seqno_idx]++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send unexpected-4addr-frame event to hostapd. For older versions,
|
||||
* also drop the frame to cooked monitor interfaces.
|
||||
|
@ -2474,14 +2469,14 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
|
|||
|
||||
rx->skb->dev = dev;
|
||||
|
||||
if (local->ps_sdata && local->hw.conf.dynamic_ps_timeout > 0 &&
|
||||
if (!ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS) &&
|
||||
local->ps_sdata && local->hw.conf.dynamic_ps_timeout > 0 &&
|
||||
!is_multicast_ether_addr(
|
||||
((struct ethhdr *)rx->skb->data)->h_dest) &&
|
||||
(!local->scanning &&
|
||||
!test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))) {
|
||||
!test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)))
|
||||
mod_timer(&local->dynamic_ps_timer, jiffies +
|
||||
msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
|
||||
}
|
||||
|
||||
ieee80211_deliver_skb(rx);
|
||||
|
||||
|
@ -3201,7 +3196,7 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
|
|||
res = rxh(rx); \
|
||||
if (res != RX_CONTINUE) \
|
||||
goto rxh_next; \
|
||||
} while (0);
|
||||
} while (0)
|
||||
|
||||
/* Lock here to avoid hitting all of the data used in the RX
|
||||
* path (e.g. key data, station data, ...) concurrently when
|
||||
|
@ -3219,30 +3214,30 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
|
|||
*/
|
||||
rx->skb = skb;
|
||||
|
||||
CALL_RXH(ieee80211_rx_h_check_more_data)
|
||||
CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll)
|
||||
CALL_RXH(ieee80211_rx_h_sta_process)
|
||||
CALL_RXH(ieee80211_rx_h_decrypt)
|
||||
CALL_RXH(ieee80211_rx_h_defragment)
|
||||
CALL_RXH(ieee80211_rx_h_michael_mic_verify)
|
||||
CALL_RXH(ieee80211_rx_h_check_more_data);
|
||||
CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll);
|
||||
CALL_RXH(ieee80211_rx_h_sta_process);
|
||||
CALL_RXH(ieee80211_rx_h_decrypt);
|
||||
CALL_RXH(ieee80211_rx_h_defragment);
|
||||
CALL_RXH(ieee80211_rx_h_michael_mic_verify);
|
||||
/* must be after MMIC verify so header is counted in MPDU mic */
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
if (ieee80211_vif_is_mesh(&rx->sdata->vif))
|
||||
CALL_RXH(ieee80211_rx_h_mesh_fwding);
|
||||
#endif
|
||||
CALL_RXH(ieee80211_rx_h_amsdu)
|
||||
CALL_RXH(ieee80211_rx_h_data)
|
||||
CALL_RXH(ieee80211_rx_h_amsdu);
|
||||
CALL_RXH(ieee80211_rx_h_data);
|
||||
|
||||
/* special treatment -- needs the queue */
|
||||
res = ieee80211_rx_h_ctrl(rx, frames);
|
||||
if (res != RX_CONTINUE)
|
||||
goto rxh_next;
|
||||
|
||||
CALL_RXH(ieee80211_rx_h_mgmt_check)
|
||||
CALL_RXH(ieee80211_rx_h_action)
|
||||
CALL_RXH(ieee80211_rx_h_userspace_mgmt)
|
||||
CALL_RXH(ieee80211_rx_h_action_return)
|
||||
CALL_RXH(ieee80211_rx_h_mgmt)
|
||||
CALL_RXH(ieee80211_rx_h_mgmt_check);
|
||||
CALL_RXH(ieee80211_rx_h_action);
|
||||
CALL_RXH(ieee80211_rx_h_userspace_mgmt);
|
||||
CALL_RXH(ieee80211_rx_h_action_return);
|
||||
CALL_RXH(ieee80211_rx_h_mgmt);
|
||||
|
||||
rxh_next:
|
||||
ieee80211_rx_handlers_result(rx, res);
|
||||
|
@ -3265,10 +3260,10 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
|
|||
res = rxh(rx); \
|
||||
if (res != RX_CONTINUE) \
|
||||
goto rxh_next; \
|
||||
} while (0);
|
||||
} while (0)
|
||||
|
||||
CALL_RXH(ieee80211_rx_h_check_dup)
|
||||
CALL_RXH(ieee80211_rx_h_check)
|
||||
CALL_RXH(ieee80211_rx_h_check_dup);
|
||||
CALL_RXH(ieee80211_rx_h_check);
|
||||
|
||||
ieee80211_rx_reorder_ampdu(rx, &reorder_release);
|
||||
|
||||
|
@ -3513,6 +3508,351 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
|
|||
return false;
|
||||
}
|
||||
|
||||
void ieee80211_check_fast_rx(struct sta_info *sta)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_key *key;
|
||||
struct ieee80211_fast_rx fastrx = {
|
||||
.dev = sdata->dev,
|
||||
.vif_type = sdata->vif.type,
|
||||
.control_port_protocol = sdata->control_port_protocol,
|
||||
}, *old, *new = NULL;
|
||||
bool assign = false;
|
||||
|
||||
/* use sparse to check that we don't return without updating */
|
||||
__acquire(check_fast_rx);
|
||||
|
||||
BUILD_BUG_ON(sizeof(fastrx.rfc1042_hdr) != sizeof(rfc1042_header));
|
||||
BUILD_BUG_ON(sizeof(fastrx.rfc1042_hdr) != ETH_ALEN);
|
||||
ether_addr_copy(fastrx.rfc1042_hdr, rfc1042_header);
|
||||
ether_addr_copy(fastrx.vif_addr, sdata->vif.addr);
|
||||
|
||||
fastrx.uses_rss = ieee80211_hw_check(&local->hw, USES_RSS);
|
||||
|
||||
/* fast-rx doesn't do reordering */
|
||||
if (ieee80211_hw_check(&local->hw, AMPDU_AGGREGATION) &&
|
||||
!ieee80211_hw_check(&local->hw, SUPPORTS_REORDERING_BUFFER))
|
||||
goto clear;
|
||||
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
/* 4-addr is harder to deal with, later maybe */
|
||||
if (sdata->u.mgd.use_4addr)
|
||||
goto clear;
|
||||
/* software powersave is a huge mess, avoid all of it */
|
||||
if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK))
|
||||
goto clear;
|
||||
if (ieee80211_hw_check(&local->hw, SUPPORTS_PS) &&
|
||||
!ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS))
|
||||
goto clear;
|
||||
if (sta->sta.tdls) {
|
||||
fastrx.da_offs = offsetof(struct ieee80211_hdr, addr1);
|
||||
fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr2);
|
||||
fastrx.expected_ds_bits = 0;
|
||||
} else {
|
||||
fastrx.sta_notify = sdata->u.mgd.probe_send_count > 0;
|
||||
fastrx.da_offs = offsetof(struct ieee80211_hdr, addr1);
|
||||
fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr3);
|
||||
fastrx.expected_ds_bits =
|
||||
cpu_to_le16(IEEE80211_FCTL_FROMDS);
|
||||
}
|
||||
break;
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
case NL80211_IFTYPE_AP:
|
||||
/* parallel-rx requires this, at least with calls to
|
||||
* ieee80211_sta_ps_transition()
|
||||
*/
|
||||
if (!ieee80211_hw_check(&local->hw, AP_LINK_PS))
|
||||
goto clear;
|
||||
fastrx.da_offs = offsetof(struct ieee80211_hdr, addr3);
|
||||
fastrx.sa_offs = offsetof(struct ieee80211_hdr, addr2);
|
||||
fastrx.expected_ds_bits = cpu_to_le16(IEEE80211_FCTL_TODS);
|
||||
|
||||
fastrx.internal_forward =
|
||||
!(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) &&
|
||||
(sdata->vif.type != NL80211_IFTYPE_AP_VLAN ||
|
||||
!sdata->u.vlan.sta);
|
||||
break;
|
||||
default:
|
||||
goto clear;
|
||||
}
|
||||
|
||||
if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
|
||||
goto clear;
|
||||
|
||||
rcu_read_lock();
|
||||
key = rcu_dereference(sta->ptk[sta->ptk_idx]);
|
||||
if (key) {
|
||||
switch (key->conf.cipher) {
|
||||
case WLAN_CIPHER_SUITE_TKIP:
|
||||
/* we don't want to deal with MMIC in fast-rx */
|
||||
goto clear_rcu;
|
||||
case WLAN_CIPHER_SUITE_CCMP:
|
||||
case WLAN_CIPHER_SUITE_CCMP_256:
|
||||
case WLAN_CIPHER_SUITE_GCMP:
|
||||
case WLAN_CIPHER_SUITE_GCMP_256:
|
||||
break;
|
||||
default:
|
||||
/* we also don't want to deal with WEP or cipher scheme
|
||||
* since those require looking up the key idx in the
|
||||
* frame, rather than assuming the PTK is used
|
||||
* (we need to revisit this once we implement the real
|
||||
* PTK index, which is now valid in the spec, but we
|
||||
* haven't implemented that part yet)
|
||||
*/
|
||||
goto clear_rcu;
|
||||
}
|
||||
|
||||
fastrx.key = true;
|
||||
fastrx.icv_len = key->conf.icv_len;
|
||||
}
|
||||
|
||||
assign = true;
|
||||
clear_rcu:
|
||||
rcu_read_unlock();
|
||||
clear:
|
||||
__release(check_fast_rx);
|
||||
|
||||
if (assign)
|
||||
new = kmemdup(&fastrx, sizeof(fastrx), GFP_KERNEL);
|
||||
|
||||
spin_lock_bh(&sta->lock);
|
||||
old = rcu_dereference_protected(sta->fast_rx, true);
|
||||
rcu_assign_pointer(sta->fast_rx, new);
|
||||
spin_unlock_bh(&sta->lock);
|
||||
|
||||
if (old)
|
||||
kfree_rcu(old, rcu_head);
|
||||
}
|
||||
|
||||
void ieee80211_clear_fast_rx(struct sta_info *sta)
|
||||
{
|
||||
struct ieee80211_fast_rx *old;
|
||||
|
||||
spin_lock_bh(&sta->lock);
|
||||
old = rcu_dereference_protected(sta->fast_rx, true);
|
||||
RCU_INIT_POINTER(sta->fast_rx, NULL);
|
||||
spin_unlock_bh(&sta->lock);
|
||||
|
||||
if (old)
|
||||
kfree_rcu(old, rcu_head);
|
||||
}
|
||||
|
||||
void __ieee80211_check_fast_rx_iface(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sta_info *sta;
|
||||
|
||||
lockdep_assert_held(&local->sta_mtx);
|
||||
|
||||
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||
if (sdata != sta->sdata &&
|
||||
(!sta->sdata->bss || sta->sdata->bss != sdata->bss))
|
||||
continue;
|
||||
ieee80211_check_fast_rx(sta);
|
||||
}
|
||||
}
|
||||
|
||||
void ieee80211_check_fast_rx_iface(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
|
||||
mutex_lock(&local->sta_mtx);
|
||||
__ieee80211_check_fast_rx_iface(sdata);
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
}
|
||||
|
||||
static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
|
||||
struct ieee80211_fast_rx *fast_rx)
|
||||
{
|
||||
struct sk_buff *skb = rx->skb;
|
||||
struct ieee80211_hdr *hdr = (void *)skb->data;
|
||||
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
||||
struct sta_info *sta = rx->sta;
|
||||
int orig_len = skb->len;
|
||||
int snap_offs = ieee80211_hdrlen(hdr->frame_control);
|
||||
struct {
|
||||
u8 snap[sizeof(rfc1042_header)];
|
||||
__be16 proto;
|
||||
} *payload __aligned(2);
|
||||
struct {
|
||||
u8 da[ETH_ALEN];
|
||||
u8 sa[ETH_ALEN];
|
||||
} addrs __aligned(2);
|
||||
struct ieee80211_sta_rx_stats *stats = &sta->rx_stats;
|
||||
|
||||
if (fast_rx->uses_rss)
|
||||
stats = this_cpu_ptr(sta->pcpu_rx_stats);
|
||||
|
||||
/* for parallel-rx, we need to have DUP_VALIDATED, otherwise we write
|
||||
* to a common data structure; drivers can implement that per queue
|
||||
* but we don't have that information in mac80211
|
||||
*/
|
||||
if (!(status->flag & RX_FLAG_DUP_VALIDATED))
|
||||
return false;
|
||||
|
||||
#define FAST_RX_CRYPT_FLAGS (RX_FLAG_PN_VALIDATED | RX_FLAG_DECRYPTED)
|
||||
|
||||
/* If using encryption, we also need to have:
|
||||
* - PN_VALIDATED: similar, but the implementation is tricky
|
||||
* - DECRYPTED: necessary for PN_VALIDATED
|
||||
*/
|
||||
if (fast_rx->key &&
|
||||
(status->flag & FAST_RX_CRYPT_FLAGS) != FAST_RX_CRYPT_FLAGS)
|
||||
return false;
|
||||
|
||||
/* we don't deal with A-MSDU deaggregation here */
|
||||
if (status->rx_flags & IEEE80211_RX_AMSDU)
|
||||
return false;
|
||||
|
||||
if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
|
||||
return false;
|
||||
|
||||
if (unlikely(ieee80211_is_frag(hdr)))
|
||||
return false;
|
||||
|
||||
/* Since our interface address cannot be multicast, this
|
||||
* implicitly also rejects multicast frames without the
|
||||
* explicit check.
|
||||
*
|
||||
* We shouldn't get any *data* frames not addressed to us
|
||||
* (AP mode will accept multicast *management* frames), but
|
||||
* punting here will make it go through the full checks in
|
||||
* ieee80211_accept_frame().
|
||||
*/
|
||||
if (!ether_addr_equal(fast_rx->vif_addr, hdr->addr1))
|
||||
return false;
|
||||
|
||||
if ((hdr->frame_control & cpu_to_le16(IEEE80211_FCTL_FROMDS |
|
||||
IEEE80211_FCTL_TODS)) !=
|
||||
fast_rx->expected_ds_bits)
|
||||
goto drop;
|
||||
|
||||
/* assign the key to drop unencrypted frames (later)
|
||||
* and strip the IV/MIC if necessary
|
||||
*/
|
||||
if (fast_rx->key && !(status->flag & RX_FLAG_IV_STRIPPED)) {
|
||||
/* GCMP header length is the same */
|
||||
snap_offs += IEEE80211_CCMP_HDR_LEN;
|
||||
}
|
||||
|
||||
if (!pskb_may_pull(skb, snap_offs + sizeof(*payload)))
|
||||
goto drop;
|
||||
payload = (void *)(skb->data + snap_offs);
|
||||
|
||||
if (!ether_addr_equal(payload->snap, fast_rx->rfc1042_hdr))
|
||||
return false;
|
||||
|
||||
/* Don't handle these here since they require special code.
|
||||
* Accept AARP and IPX even though they should come with a
|
||||
* bridge-tunnel header - but if we get them this way then
|
||||
* there's little point in discarding them.
|
||||
*/
|
||||
if (unlikely(payload->proto == cpu_to_be16(ETH_P_TDLS) ||
|
||||
payload->proto == fast_rx->control_port_protocol))
|
||||
return false;
|
||||
|
||||
/* after this point, don't punt to the slowpath! */
|
||||
|
||||
if (rx->key && !(status->flag & RX_FLAG_MIC_STRIPPED) &&
|
||||
pskb_trim(skb, skb->len - fast_rx->icv_len))
|
||||
goto drop;
|
||||
|
||||
if (unlikely(fast_rx->sta_notify)) {
|
||||
ieee80211_sta_rx_notify(rx->sdata, hdr);
|
||||
fast_rx->sta_notify = false;
|
||||
}
|
||||
|
||||
/* statistics part of ieee80211_rx_h_sta_process() */
|
||||
stats->last_rx = jiffies;
|
||||
stats->last_rate = sta_stats_encode_rate(status);
|
||||
|
||||
stats->fragments++;
|
||||
|
||||
if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
|
||||
stats->last_signal = status->signal;
|
||||
if (!fast_rx->uses_rss)
|
||||
ewma_signal_add(&sta->rx_stats_avg.signal,
|
||||
-status->signal);
|
||||
}
|
||||
|
||||
if (status->chains) {
|
||||
int i;
|
||||
|
||||
stats->chains = status->chains;
|
||||
for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) {
|
||||
int signal = status->chain_signal[i];
|
||||
|
||||
if (!(status->chains & BIT(i)))
|
||||
continue;
|
||||
|
||||
stats->chain_signal_last[i] = signal;
|
||||
if (!fast_rx->uses_rss)
|
||||
ewma_signal_add(&sta->rx_stats_avg.chain_signal[i],
|
||||
-signal);
|
||||
}
|
||||
}
|
||||
/* end of statistics */
|
||||
|
||||
if (rx->key && !ieee80211_has_protected(hdr->frame_control))
|
||||
goto drop;
|
||||
|
||||
/* do the header conversion - first grab the addresses */
|
||||
ether_addr_copy(addrs.da, skb->data + fast_rx->da_offs);
|
||||
ether_addr_copy(addrs.sa, skb->data + fast_rx->sa_offs);
|
||||
/* remove the SNAP but leave the ethertype */
|
||||
skb_pull(skb, snap_offs + sizeof(rfc1042_header));
|
||||
/* push the addresses in front */
|
||||
memcpy(skb_push(skb, sizeof(addrs)), &addrs, sizeof(addrs));
|
||||
|
||||
skb->dev = fast_rx->dev;
|
||||
|
||||
ieee80211_rx_stats(fast_rx->dev, skb->len);
|
||||
|
||||
/* The seqno index has the same property as needed
|
||||
* for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
|
||||
* for non-QoS-data frames. Here we know it's a data
|
||||
* frame, so count MSDUs.
|
||||
*/
|
||||
u64_stats_update_begin(&stats->syncp);
|
||||
stats->msdu[rx->seqno_idx]++;
|
||||
stats->bytes += orig_len;
|
||||
u64_stats_update_end(&stats->syncp);
|
||||
|
||||
if (fast_rx->internal_forward) {
|
||||
struct sta_info *dsta = sta_info_get(rx->sdata, skb->data);
|
||||
|
||||
if (dsta) {
|
||||
/*
|
||||
* Send to wireless media and increase priority by 256
|
||||
* to keep the received priority instead of
|
||||
* reclassifying the frame (see cfg80211_classify8021d).
|
||||
*/
|
||||
skb->priority += 256;
|
||||
skb->protocol = htons(ETH_P_802_3);
|
||||
skb_reset_network_header(skb);
|
||||
skb_reset_mac_header(skb);
|
||||
dev_queue_xmit(skb);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* deliver to local stack */
|
||||
skb->protocol = eth_type_trans(skb, fast_rx->dev);
|
||||
memset(skb->cb, 0, sizeof(skb->cb));
|
||||
if (rx->napi)
|
||||
napi_gro_receive(rx->napi, skb);
|
||||
else
|
||||
netif_receive_skb(skb);
|
||||
|
||||
return true;
|
||||
drop:
|
||||
dev_kfree_skb(skb);
|
||||
stats->dropped++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function returns whether or not the SKB
|
||||
* was destined for RX processing or not, which,
|
||||
|
@ -3527,6 +3867,21 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
|
|||
|
||||
rx->skb = skb;
|
||||
|
||||
/* See if we can do fast-rx; if we have to copy we already lost,
|
||||
* so punt in that case. We should never have to deliver a data
|
||||
* frame to multiple interfaces anyway.
|
||||
*
|
||||
* We skip the ieee80211_accept_frame() call and do the necessary
|
||||
* checking inside ieee80211_invoke_fast_rx().
|
||||
*/
|
||||
if (consume && rx->sta) {
|
||||
struct ieee80211_fast_rx *fast_rx;
|
||||
|
||||
fast_rx = rcu_dereference(rx->sta->fast_rx);
|
||||
if (fast_rx && ieee80211_invoke_fast_rx(rx, fast_rx))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ieee80211_accept_frame(rx))
|
||||
return false;
|
||||
|
||||
|
@ -3552,6 +3907,7 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
|
|||
* be called with rcu_read_lock protection.
|
||||
*/
|
||||
static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
|
||||
struct ieee80211_sta *pubsta,
|
||||
struct sk_buff *skb,
|
||||
struct napi_struct *napi)
|
||||
{
|
||||
|
@ -3561,7 +3917,6 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
|
|||
__le16 fc;
|
||||
struct ieee80211_rx_data rx;
|
||||
struct ieee80211_sub_if_data *prev;
|
||||
struct sta_info *sta, *prev_sta;
|
||||
struct rhash_head *tmp;
|
||||
int err = 0;
|
||||
|
||||
|
@ -3597,7 +3952,14 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
|
|||
ieee80211_is_beacon(hdr->frame_control)))
|
||||
ieee80211_scan_rx(local, skb);
|
||||
|
||||
if (ieee80211_is_data(fc)) {
|
||||
if (pubsta) {
|
||||
rx.sta = container_of(pubsta, struct sta_info, sta);
|
||||
rx.sdata = rx.sta->sdata;
|
||||
if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
|
||||
return;
|
||||
goto out;
|
||||
} else if (ieee80211_is_data(fc)) {
|
||||
struct sta_info *sta, *prev_sta;
|
||||
const struct bucket_table *tbl;
|
||||
|
||||
prev_sta = NULL;
|
||||
|
@ -3671,8 +4033,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
|
|||
* This is the receive path handler. It is called by a low level driver when an
|
||||
* 802.11 MPDU is received from the hardware.
|
||||
*/
|
||||
void ieee80211_rx_napi(struct ieee80211_hw *hw, struct sk_buff *skb,
|
||||
struct napi_struct *napi)
|
||||
void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
|
||||
struct sk_buff *skb, struct napi_struct *napi)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
struct ieee80211_rate *rate = NULL;
|
||||
|
@ -3771,7 +4133,8 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct sk_buff *skb,
|
|||
ieee80211_tpt_led_trig_rx(local,
|
||||
((struct ieee80211_hdr *)skb->data)->frame_control,
|
||||
skb->len);
|
||||
__ieee80211_rx_handle_packet(hw, skb, napi);
|
||||
|
||||
__ieee80211_rx_handle_packet(hw, pubsta, skb, napi);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
|
|
|
@ -66,7 +66,9 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
|
|||
struct cfg80211_bss *cbss;
|
||||
struct ieee80211_bss *bss;
|
||||
int clen, srlen;
|
||||
struct cfg80211_inform_bss bss_meta = {};
|
||||
struct cfg80211_inform_bss bss_meta = {
|
||||
.boottime_ns = rx_status->boottime_ns,
|
||||
};
|
||||
bool signal_valid;
|
||||
|
||||
if (ieee80211_hw_check(&local->hw, SIGNAL_DBM))
|
||||
|
@ -303,6 +305,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
|
|||
ether_addr_copy(local->hw_scan_req->req.mac_addr, req->mac_addr);
|
||||
ether_addr_copy(local->hw_scan_req->req.mac_addr_mask,
|
||||
req->mac_addr_mask);
|
||||
ether_addr_copy(local->hw_scan_req->req.bssid, req->bssid);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -497,7 +500,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
|
|||
|
||||
for (i = 0; i < scan_req->n_ssids; i++)
|
||||
ieee80211_send_probe_req(
|
||||
sdata, local->scan_addr, NULL,
|
||||
sdata, local->scan_addr, scan_req->bssid,
|
||||
scan_req->ssids[i].ssid, scan_req->ssids[i].ssid_len,
|
||||
scan_req->ie, scan_req->ie_len,
|
||||
scan_req->rates[band], false,
|
||||
|
@ -562,6 +565,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
|||
req->n_channels * sizeof(req->channels[0]);
|
||||
local->hw_scan_req->req.ie = ies;
|
||||
local->hw_scan_req->req.flags = req->flags;
|
||||
eth_broadcast_addr(local->hw_scan_req->req.bssid);
|
||||
|
||||
local->hw_scan_band = 0;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright 2002-2005, Instant802 Networks, Inc.
|
||||
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
|
||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2015 Intel Deutschland GmbH
|
||||
* Copyright (C) 2015 - 2016 Intel Deutschland GmbH
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
|
@ -254,6 +254,7 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
|
|||
#ifdef CONFIG_MAC80211_MESH
|
||||
kfree(sta->mesh);
|
||||
#endif
|
||||
free_percpu(sta->pcpu_rx_stats);
|
||||
kfree(sta);
|
||||
}
|
||||
|
||||
|
@ -311,6 +312,13 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
|||
if (!sta)
|
||||
return NULL;
|
||||
|
||||
if (ieee80211_hw_check(hw, USES_RSS)) {
|
||||
sta->pcpu_rx_stats =
|
||||
alloc_percpu(struct ieee80211_sta_rx_stats);
|
||||
if (!sta->pcpu_rx_stats)
|
||||
goto free;
|
||||
}
|
||||
|
||||
spin_lock_init(&sta->lock);
|
||||
spin_lock_init(&sta->ps_lock);
|
||||
INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
|
||||
|
@ -335,15 +343,17 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
|||
sta->sdata = sdata;
|
||||
sta->rx_stats.last_rx = jiffies;
|
||||
|
||||
u64_stats_init(&sta->rx_stats.syncp);
|
||||
|
||||
sta->sta_state = IEEE80211_STA_NONE;
|
||||
|
||||
/* Mark TID as unreserved */
|
||||
sta->reserved_tid = IEEE80211_TID_UNRESERVED;
|
||||
|
||||
sta->last_connected = ktime_get_seconds();
|
||||
ewma_signal_init(&sta->rx_stats.avg_signal);
|
||||
for (i = 0; i < ARRAY_SIZE(sta->rx_stats.chain_signal_avg); i++)
|
||||
ewma_signal_init(&sta->rx_stats.chain_signal_avg[i]);
|
||||
ewma_signal_init(&sta->rx_stats_avg.signal);
|
||||
for (i = 0; i < ARRAY_SIZE(sta->rx_stats_avg.chain_signal); i++)
|
||||
ewma_signal_init(&sta->rx_stats_avg.chain_signal[i]);
|
||||
|
||||
if (local->ops->wake_tx_queue) {
|
||||
void *txq_data;
|
||||
|
@ -406,6 +416,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
|||
}
|
||||
}
|
||||
|
||||
sta->sta.max_rc_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA;
|
||||
|
||||
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
|
||||
|
||||
return sta;
|
||||
|
@ -875,6 +887,13 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
|
|||
set_sta_flag(sta, WLAN_STA_BLOCK_BA);
|
||||
ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA);
|
||||
|
||||
/*
|
||||
* Before removing the station from the driver there might be pending
|
||||
* rx frames on RSS queues sent prior to the disassociation - wait for
|
||||
* all such frames to be processed.
|
||||
*/
|
||||
drv_sync_rx_queues(local, sta);
|
||||
|
||||
ret = sta_info_hash_del(local, sta);
|
||||
if (WARN_ON(ret))
|
||||
return ret;
|
||||
|
@ -1087,10 +1106,12 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
|
|||
mutex_lock(&local->sta_mtx);
|
||||
|
||||
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
|
||||
unsigned long last_active = ieee80211_sta_last_active(sta);
|
||||
|
||||
if (sdata != sta->sdata)
|
||||
continue;
|
||||
|
||||
if (time_after(jiffies, sta->rx_stats.last_rx + exp_time)) {
|
||||
if (time_is_before_jiffies(last_active + exp_time)) {
|
||||
sta_dbg(sta->sdata, "expiring inactive STA %pM\n",
|
||||
sta->sta.addr);
|
||||
|
||||
|
@ -1760,6 +1781,31 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
|
|||
}
|
||||
EXPORT_SYMBOL(ieee80211_sta_set_buffered);
|
||||
|
||||
static void
|
||||
ieee80211_recalc_p2p_go_ps_allowed(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
bool allow_p2p_go_ps = sdata->vif.p2p;
|
||||
struct sta_info *sta;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||
if (sdata != sta->sdata ||
|
||||
!test_sta_flag(sta, WLAN_STA_ASSOC))
|
||||
continue;
|
||||
if (!sta->sta.support_p2p_ps) {
|
||||
allow_p2p_go_ps = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (allow_p2p_go_ps != sdata->vif.bss_conf.allow_p2p_go_ps) {
|
||||
sdata->vif.bss_conf.allow_p2p_go_ps = allow_p2p_go_ps;
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_P2P_PS);
|
||||
}
|
||||
}
|
||||
|
||||
int sta_info_move_state(struct sta_info *sta,
|
||||
enum ieee80211_sta_state new_state)
|
||||
{
|
||||
|
@ -1821,12 +1867,16 @@ int sta_info_move_state(struct sta_info *sta,
|
|||
} else if (sta->sta_state == IEEE80211_STA_ASSOC) {
|
||||
clear_bit(WLAN_STA_ASSOC, &sta->_flags);
|
||||
ieee80211_recalc_min_chandef(sta->sdata);
|
||||
if (!sta->sta.support_p2p_ps)
|
||||
ieee80211_recalc_p2p_go_ps_allowed(sta->sdata);
|
||||
}
|
||||
break;
|
||||
case IEEE80211_STA_ASSOC:
|
||||
if (sta->sta_state == IEEE80211_STA_AUTH) {
|
||||
set_bit(WLAN_STA_ASSOC, &sta->_flags);
|
||||
ieee80211_recalc_min_chandef(sta->sdata);
|
||||
if (!sta->sta.support_p2p_ps)
|
||||
ieee80211_recalc_p2p_go_ps_allowed(sta->sdata);
|
||||
} else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) {
|
||||
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
|
||||
(sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
|
||||
|
@ -1834,6 +1884,7 @@ int sta_info_move_state(struct sta_info *sta,
|
|||
atomic_dec(&sta->sdata->bss->num_mcast_sta);
|
||||
clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
|
||||
ieee80211_clear_fast_xmit(sta);
|
||||
ieee80211_clear_fast_rx(sta);
|
||||
}
|
||||
break;
|
||||
case IEEE80211_STA_AUTHORIZED:
|
||||
|
@ -1844,6 +1895,7 @@ int sta_info_move_state(struct sta_info *sta,
|
|||
atomic_inc(&sta->sdata->bss->num_mcast_sta);
|
||||
set_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
|
||||
ieee80211_check_fast_xmit(sta);
|
||||
ieee80211_check_fast_rx(sta);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -1890,43 +1942,117 @@ u8 sta_info_tx_streams(struct sta_info *sta)
|
|||
>> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
|
||||
}
|
||||
|
||||
static void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
|
||||
static struct ieee80211_sta_rx_stats *
|
||||
sta_get_last_rx_stats(struct sta_info *sta)
|
||||
{
|
||||
rinfo->flags = 0;
|
||||
struct ieee80211_sta_rx_stats *stats = &sta->rx_stats;
|
||||
struct ieee80211_local *local = sta->local;
|
||||
int cpu;
|
||||
|
||||
if (sta->rx_stats.last_rate_flag & RX_FLAG_HT) {
|
||||
rinfo->flags |= RATE_INFO_FLAGS_MCS;
|
||||
rinfo->mcs = sta->rx_stats.last_rate_idx;
|
||||
} else if (sta->rx_stats.last_rate_flag & RX_FLAG_VHT) {
|
||||
rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
|
||||
rinfo->nss = sta->rx_stats.last_rate_vht_nss;
|
||||
rinfo->mcs = sta->rx_stats.last_rate_idx;
|
||||
} else {
|
||||
if (!ieee80211_hw_check(&local->hw, USES_RSS))
|
||||
return stats;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct ieee80211_sta_rx_stats *cpustats;
|
||||
|
||||
cpustats = per_cpu_ptr(sta->pcpu_rx_stats, cpu);
|
||||
|
||||
if (time_after(cpustats->last_rx, stats->last_rx))
|
||||
stats = cpustats;
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate,
|
||||
struct rate_info *rinfo)
|
||||
{
|
||||
rinfo->bw = (rate & STA_STATS_RATE_BW_MASK) >>
|
||||
STA_STATS_RATE_BW_SHIFT;
|
||||
|
||||
if (rate & STA_STATS_RATE_VHT) {
|
||||
rinfo->flags = RATE_INFO_FLAGS_VHT_MCS;
|
||||
rinfo->mcs = rate & 0xf;
|
||||
rinfo->nss = (rate & 0xf0) >> 4;
|
||||
} else if (rate & STA_STATS_RATE_HT) {
|
||||
rinfo->flags = RATE_INFO_FLAGS_MCS;
|
||||
rinfo->mcs = rate & 0xff;
|
||||
} else if (rate & STA_STATS_RATE_LEGACY) {
|
||||
struct ieee80211_supported_band *sband;
|
||||
int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
|
||||
u16 brate;
|
||||
unsigned int shift;
|
||||
|
||||
sband = sta->local->hw.wiphy->bands[
|
||||
ieee80211_get_sdata_band(sta->sdata)];
|
||||
brate = sband->bitrates[sta->rx_stats.last_rate_idx].bitrate;
|
||||
sband = local->hw.wiphy->bands[(rate >> 4) & 0xf];
|
||||
brate = sband->bitrates[rate & 0xf].bitrate;
|
||||
if (rinfo->bw == RATE_INFO_BW_5)
|
||||
shift = 2;
|
||||
else if (rinfo->bw == RATE_INFO_BW_10)
|
||||
shift = 1;
|
||||
else
|
||||
shift = 0;
|
||||
rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
|
||||
}
|
||||
|
||||
if (sta->rx_stats.last_rate_flag & RX_FLAG_SHORT_GI)
|
||||
if (rate & STA_STATS_RATE_SGI)
|
||||
rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
|
||||
}
|
||||
|
||||
if (sta->rx_stats.last_rate_flag & RX_FLAG_5MHZ)
|
||||
rinfo->bw = RATE_INFO_BW_5;
|
||||
else if (sta->rx_stats.last_rate_flag & RX_FLAG_10MHZ)
|
||||
rinfo->bw = RATE_INFO_BW_10;
|
||||
else if (sta->rx_stats.last_rate_flag & RX_FLAG_40MHZ)
|
||||
rinfo->bw = RATE_INFO_BW_40;
|
||||
else if (sta->rx_stats.last_rate_vht_flag & RX_VHT_FLAG_80MHZ)
|
||||
rinfo->bw = RATE_INFO_BW_80;
|
||||
else if (sta->rx_stats.last_rate_vht_flag & RX_VHT_FLAG_160MHZ)
|
||||
rinfo->bw = RATE_INFO_BW_160;
|
||||
static void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
|
||||
{
|
||||
u16 rate = ACCESS_ONCE(sta_get_last_rx_stats(sta)->last_rate);
|
||||
|
||||
if (rate == STA_STATS_RATE_INVALID)
|
||||
rinfo->flags = 0;
|
||||
else
|
||||
rinfo->bw = RATE_INFO_BW_20;
|
||||
sta_stats_decode_rate(sta->local, rate, rinfo);
|
||||
}
|
||||
|
||||
static void sta_set_tidstats(struct sta_info *sta,
|
||||
struct cfg80211_tid_stats *tidstats,
|
||||
int tid)
|
||||
{
|
||||
struct ieee80211_local *local = sta->local;
|
||||
|
||||
if (!(tidstats->filled & BIT(NL80211_TID_STATS_RX_MSDU))) {
|
||||
unsigned int start;
|
||||
|
||||
do {
|
||||
start = u64_stats_fetch_begin(&sta->rx_stats.syncp);
|
||||
tidstats->rx_msdu = sta->rx_stats.msdu[tid];
|
||||
} while (u64_stats_fetch_retry(&sta->rx_stats.syncp, start));
|
||||
|
||||
tidstats->filled |= BIT(NL80211_TID_STATS_RX_MSDU);
|
||||
}
|
||||
|
||||
if (!(tidstats->filled & BIT(NL80211_TID_STATS_TX_MSDU))) {
|
||||
tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU);
|
||||
tidstats->tx_msdu = sta->tx_stats.msdu[tid];
|
||||
}
|
||||
|
||||
if (!(tidstats->filled & BIT(NL80211_TID_STATS_TX_MSDU_RETRIES)) &&
|
||||
ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
|
||||
tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU_RETRIES);
|
||||
tidstats->tx_msdu_retries = sta->status_stats.msdu_retries[tid];
|
||||
}
|
||||
|
||||
if (!(tidstats->filled & BIT(NL80211_TID_STATS_TX_MSDU_FAILED)) &&
|
||||
ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
|
||||
tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU_FAILED);
|
||||
tidstats->tx_msdu_failed = sta->status_stats.msdu_failed[tid];
|
||||
}
|
||||
}
|
||||
|
||||
static inline u64 sta_get_stats_bytes(struct ieee80211_sta_rx_stats *rxstats)
|
||||
{
|
||||
unsigned int start;
|
||||
u64 value;
|
||||
|
||||
do {
|
||||
start = u64_stats_fetch_begin(&rxstats->syncp);
|
||||
value = rxstats->bytes;
|
||||
} while (u64_stats_fetch_retry(&rxstats->syncp, start));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
||||
|
@ -1935,7 +2061,10 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
|||
struct ieee80211_local *local = sdata->local;
|
||||
struct rate_control_ref *ref = NULL;
|
||||
u32 thr = 0;
|
||||
int i, ac;
|
||||
int i, ac, cpu;
|
||||
struct ieee80211_sta_rx_stats *last_rxstats;
|
||||
|
||||
last_rxstats = sta_get_last_rx_stats(sta);
|
||||
|
||||
if (test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
|
||||
ref = local->rate_ctrl;
|
||||
|
@ -1964,7 +2093,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
|||
|
||||
sinfo->connected_time = ktime_get_seconds() - sta->last_connected;
|
||||
sinfo->inactive_time =
|
||||
jiffies_to_msecs(jiffies - sta->rx_stats.last_rx);
|
||||
jiffies_to_msecs(jiffies - ieee80211_sta_last_active(sta));
|
||||
|
||||
if (!(sinfo->filled & (BIT(NL80211_STA_INFO_TX_BYTES64) |
|
||||
BIT(NL80211_STA_INFO_TX_BYTES)))) {
|
||||
|
@ -1983,12 +2112,30 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
|||
|
||||
if (!(sinfo->filled & (BIT(NL80211_STA_INFO_RX_BYTES64) |
|
||||
BIT(NL80211_STA_INFO_RX_BYTES)))) {
|
||||
sinfo->rx_bytes = sta->rx_stats.bytes;
|
||||
sinfo->rx_bytes += sta_get_stats_bytes(&sta->rx_stats);
|
||||
|
||||
if (sta->pcpu_rx_stats) {
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct ieee80211_sta_rx_stats *cpurxs;
|
||||
|
||||
cpurxs = per_cpu_ptr(sta->pcpu_rx_stats, cpu);
|
||||
sinfo->rx_bytes += sta_get_stats_bytes(cpurxs);
|
||||
}
|
||||
}
|
||||
|
||||
sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES64);
|
||||
}
|
||||
|
||||
if (!(sinfo->filled & BIT(NL80211_STA_INFO_RX_PACKETS))) {
|
||||
sinfo->rx_packets = sta->rx_stats.packets;
|
||||
if (sta->pcpu_rx_stats) {
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct ieee80211_sta_rx_stats *cpurxs;
|
||||
|
||||
cpurxs = per_cpu_ptr(sta->pcpu_rx_stats, cpu);
|
||||
sinfo->rx_packets += cpurxs->packets;
|
||||
}
|
||||
}
|
||||
sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
|
||||
}
|
||||
|
||||
|
@ -2003,6 +2150,14 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
|||
}
|
||||
|
||||
sinfo->rx_dropped_misc = sta->rx_stats.dropped;
|
||||
if (sta->pcpu_rx_stats) {
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct ieee80211_sta_rx_stats *cpurxs;
|
||||
|
||||
cpurxs = per_cpu_ptr(sta->pcpu_rx_stats, cpu);
|
||||
sinfo->rx_packets += cpurxs->dropped;
|
||||
}
|
||||
}
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
|
||||
!(sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)) {
|
||||
|
@ -2014,29 +2169,36 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
|||
if (ieee80211_hw_check(&sta->local->hw, SIGNAL_DBM) ||
|
||||
ieee80211_hw_check(&sta->local->hw, SIGNAL_UNSPEC)) {
|
||||
if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL))) {
|
||||
sinfo->signal = (s8)sta->rx_stats.last_signal;
|
||||
sinfo->signal = (s8)last_rxstats->last_signal;
|
||||
sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
|
||||
}
|
||||
|
||||
if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))) {
|
||||
if (!sta->pcpu_rx_stats &&
|
||||
!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))) {
|
||||
sinfo->signal_avg =
|
||||
-ewma_signal_read(&sta->rx_stats.avg_signal);
|
||||
-ewma_signal_read(&sta->rx_stats_avg.signal);
|
||||
sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG);
|
||||
}
|
||||
}
|
||||
|
||||
if (sta->rx_stats.chains &&
|
||||
/* for the average - if pcpu_rx_stats isn't set - rxstats must point to
|
||||
* the sta->rx_stats struct, so the check here is fine with and without
|
||||
* pcpu statistics
|
||||
*/
|
||||
if (last_rxstats->chains &&
|
||||
!(sinfo->filled & (BIT(NL80211_STA_INFO_CHAIN_SIGNAL) |
|
||||
BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)))) {
|
||||
sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL) |
|
||||
BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);
|
||||
sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL);
|
||||
if (!sta->pcpu_rx_stats)
|
||||
sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);
|
||||
|
||||
sinfo->chains = last_rxstats->chains;
|
||||
|
||||
sinfo->chains = sta->rx_stats.chains;
|
||||
for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
|
||||
sinfo->chain_signal[i] =
|
||||
sta->rx_stats.chain_signal_last[i];
|
||||
last_rxstats->chain_signal_last[i];
|
||||
sinfo->chain_signal_avg[i] =
|
||||
-ewma_signal_read(&sta->rx_stats.chain_signal_avg[i]);
|
||||
-ewma_signal_read(&sta->rx_stats_avg.chain_signal[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2055,33 +2217,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
|||
for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
|
||||
struct cfg80211_tid_stats *tidstats = &sinfo->pertid[i];
|
||||
|
||||
if (!(tidstats->filled & BIT(NL80211_TID_STATS_RX_MSDU))) {
|
||||
tidstats->filled |= BIT(NL80211_TID_STATS_RX_MSDU);
|
||||
tidstats->rx_msdu = sta->rx_stats.msdu[i];
|
||||
}
|
||||
|
||||
if (!(tidstats->filled & BIT(NL80211_TID_STATS_TX_MSDU))) {
|
||||
tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU);
|
||||
tidstats->tx_msdu = sta->tx_stats.msdu[i];
|
||||
}
|
||||
|
||||
if (!(tidstats->filled &
|
||||
BIT(NL80211_TID_STATS_TX_MSDU_RETRIES)) &&
|
||||
ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
|
||||
tidstats->filled |=
|
||||
BIT(NL80211_TID_STATS_TX_MSDU_RETRIES);
|
||||
tidstats->tx_msdu_retries =
|
||||
sta->status_stats.msdu_retries[i];
|
||||
}
|
||||
|
||||
if (!(tidstats->filled &
|
||||
BIT(NL80211_TID_STATS_TX_MSDU_FAILED)) &&
|
||||
ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
|
||||
tidstats->filled |=
|
||||
BIT(NL80211_TID_STATS_TX_MSDU_FAILED);
|
||||
tidstats->tx_msdu_failed =
|
||||
sta->status_stats.msdu_failed[i];
|
||||
}
|
||||
sta_set_tidstats(sta, tidstats, i);
|
||||
}
|
||||
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
|
@ -2150,3 +2286,12 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
|||
sinfo->expected_throughput = thr;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long ieee80211_sta_last_active(struct sta_info *sta)
|
||||
{
|
||||
struct ieee80211_sta_rx_stats *stats = sta_get_last_rx_stats(sta);
|
||||
|
||||
if (time_after(stats->last_rx, sta->status_stats.last_ack))
|
||||
return stats->last_rx;
|
||||
return sta->status_stats.last_ack;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* Copyright 2002-2005, Devicescape Software, Inc.
|
||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright(c) 2015 Intel Deutschland GmbH
|
||||
* Copyright(c) 2015-2016 Intel Deutschland GmbH
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
|
@ -18,6 +18,7 @@
|
|||
#include <linux/average.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/rhashtable.h>
|
||||
#include <linux/u64_stats_sync.h>
|
||||
#include "key.h"
|
||||
|
||||
/**
|
||||
|
@ -69,6 +70,8 @@
|
|||
* @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP.
|
||||
* @WLAN_STA_PS_DELIVER: station woke up, but we're still blocking TX
|
||||
* until pending frames are delivered
|
||||
*
|
||||
* @NUM_WLAN_STA_FLAGS: number of defined flags
|
||||
*/
|
||||
enum ieee80211_sta_info_flags {
|
||||
WLAN_STA_AUTH,
|
||||
|
@ -97,6 +100,8 @@ enum ieee80211_sta_info_flags {
|
|||
WLAN_STA_MPSP_OWNER,
|
||||
WLAN_STA_MPSP_RECIPIENT,
|
||||
WLAN_STA_PS_DELIVER,
|
||||
|
||||
NUM_WLAN_STA_FLAGS,
|
||||
};
|
||||
|
||||
#define ADDBA_RESP_INTERVAL HZ
|
||||
|
@ -280,6 +285,40 @@ struct ieee80211_fast_tx {
|
|||
struct rcu_head rcu_head;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ieee80211_fast_rx - RX fastpath information
|
||||
* @dev: netdevice for reporting the SKB
|
||||
* @vif_type: (P2P-less) interface type of the original sdata (sdata->vif.type)
|
||||
* @vif_addr: interface address
|
||||
* @rfc1042_hdr: copy of the RFC 1042 SNAP header (to have in cache)
|
||||
* @control_port_protocol: control port protocol copied from sdata
|
||||
* @expected_ds_bits: from/to DS bits expected
|
||||
* @icv_len: length of the MIC if present
|
||||
* @key: bool indicating encryption is expected (key is set)
|
||||
* @sta_notify: notify the MLME code (once)
|
||||
* @internal_forward: forward froms internally on AP/VLAN type interfaces
|
||||
* @uses_rss: copy of USES_RSS hw flag
|
||||
* @da_offs: offset of the DA in the header (for header conversion)
|
||||
* @sa_offs: offset of the SA in the header (for header conversion)
|
||||
* @rcu_head: RCU head for freeing this structure
|
||||
*/
|
||||
struct ieee80211_fast_rx {
|
||||
struct net_device *dev;
|
||||
enum nl80211_iftype vif_type;
|
||||
u8 vif_addr[ETH_ALEN] __aligned(2);
|
||||
u8 rfc1042_hdr[6] __aligned(2);
|
||||
__be16 control_port_protocol;
|
||||
__le16 expected_ds_bits;
|
||||
u8 icv_len;
|
||||
u8 key:1,
|
||||
sta_notify:1,
|
||||
internal_forward:1,
|
||||
uses_rss:1;
|
||||
u8 da_offs, sa_offs;
|
||||
|
||||
struct rcu_head rcu_head;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mesh_sta - mesh STA information
|
||||
* @plink_lock: serialize access to plink fields
|
||||
|
@ -330,6 +369,21 @@ struct mesh_sta {
|
|||
|
||||
DECLARE_EWMA(signal, 1024, 8)
|
||||
|
||||
struct ieee80211_sta_rx_stats {
|
||||
unsigned long packets;
|
||||
unsigned long last_rx;
|
||||
unsigned long num_duplicates;
|
||||
unsigned long fragments;
|
||||
unsigned long dropped;
|
||||
int last_signal;
|
||||
u8 chains;
|
||||
s8 chain_signal_last[IEEE80211_MAX_CHAINS];
|
||||
u16 last_rate;
|
||||
struct u64_stats_sync syncp;
|
||||
u64 bytes;
|
||||
u64 msdu[IEEE80211_NUM_TIDS + 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sta_info - STA information
|
||||
*
|
||||
|
@ -371,7 +425,7 @@ DECLARE_EWMA(signal, 1024, 8)
|
|||
* @ampdu_mlme: A-MPDU state machine state
|
||||
* @timer_to_tid: identity mapping to ID timers
|
||||
* @mesh: mesh STA information
|
||||
* @debugfs: debug filesystem info
|
||||
* @debugfs_dir: debug filesystem directory dentry
|
||||
* @dead: set to true when sta is unlinked
|
||||
* @removed: set to true when sta is being removed from sta_list
|
||||
* @uploaded: set to true when sta is uploaded to the driver
|
||||
|
@ -386,10 +440,13 @@ DECLARE_EWMA(signal, 1024, 8)
|
|||
* @cipher_scheme: optional cipher scheme for this station
|
||||
* @reserved_tid: reserved TID (if any, otherwise IEEE80211_TID_UNRESERVED)
|
||||
* @fast_tx: TX fastpath information
|
||||
* @fast_rx: RX fastpath information
|
||||
* @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to
|
||||
* the BSS one.
|
||||
* @tx_stats: TX statistics
|
||||
* @rx_stats: RX statistics
|
||||
* @pcpu_rx_stats: per-CPU RX statistics, assigned only if the driver needs
|
||||
* this (by advertising the USES_RSS hw flag)
|
||||
* @status_stats: TX status statistics
|
||||
*/
|
||||
struct sta_info {
|
||||
|
@ -409,6 +466,8 @@ struct sta_info {
|
|||
spinlock_t lock;
|
||||
|
||||
struct ieee80211_fast_tx __rcu *fast_tx;
|
||||
struct ieee80211_fast_rx __rcu *fast_rx;
|
||||
struct ieee80211_sta_rx_stats __percpu *pcpu_rx_stats;
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
struct mesh_sta *mesh;
|
||||
|
@ -438,24 +497,11 @@ struct sta_info {
|
|||
long last_connected;
|
||||
|
||||
/* Updated from RX path only, no locking requirements */
|
||||
struct ieee80211_sta_rx_stats rx_stats;
|
||||
struct {
|
||||
unsigned long packets;
|
||||
u64 bytes;
|
||||
unsigned long last_rx;
|
||||
unsigned long num_duplicates;
|
||||
unsigned long fragments;
|
||||
unsigned long dropped;
|
||||
int last_signal;
|
||||
struct ewma_signal avg_signal;
|
||||
u8 chains;
|
||||
s8 chain_signal_last[IEEE80211_MAX_CHAINS];
|
||||
struct ewma_signal chain_signal_avg[IEEE80211_MAX_CHAINS];
|
||||
int last_rate_idx;
|
||||
u32 last_rate_flag;
|
||||
u32 last_rate_vht_flag;
|
||||
u8 last_rate_vht_nss;
|
||||
u64 msdu[IEEE80211_NUM_TIDS + 1];
|
||||
} rx_stats;
|
||||
struct ewma_signal signal;
|
||||
struct ewma_signal chain_signal[IEEE80211_MAX_CHAINS];
|
||||
} rx_stats_avg;
|
||||
|
||||
/* Plus 1 for non-QoS frames */
|
||||
__le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];
|
||||
|
@ -468,6 +514,7 @@ struct sta_info {
|
|||
unsigned long last_tdls_pkt_time;
|
||||
u64 msdu_retries[IEEE80211_NUM_TIDS + 1];
|
||||
u64 msdu_failed[IEEE80211_NUM_TIDS + 1];
|
||||
unsigned long last_ack;
|
||||
} status_stats;
|
||||
|
||||
/* Updated from TX path only, no locking requirements */
|
||||
|
@ -486,10 +533,7 @@ struct sta_info {
|
|||
u8 timer_to_tid[IEEE80211_NUM_TIDS];
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
struct sta_info_debugfsdentries {
|
||||
struct dentry *dir;
|
||||
bool add_has_run;
|
||||
} debugfs;
|
||||
struct dentry *debugfs_dir;
|
||||
#endif
|
||||
|
||||
enum ieee80211_sta_rx_bandwidth cur_max_bandwidth;
|
||||
|
@ -677,4 +721,44 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
|
|||
void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
|
||||
void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta);
|
||||
|
||||
unsigned long ieee80211_sta_last_active(struct sta_info *sta);
|
||||
|
||||
#define STA_STATS_RATE_INVALID 0
|
||||
#define STA_STATS_RATE_VHT 0x8000
|
||||
#define STA_STATS_RATE_HT 0x4000
|
||||
#define STA_STATS_RATE_LEGACY 0x2000
|
||||
#define STA_STATS_RATE_SGI 0x1000
|
||||
#define STA_STATS_RATE_BW_SHIFT 9
|
||||
#define STA_STATS_RATE_BW_MASK (0x7 << STA_STATS_RATE_BW_SHIFT)
|
||||
|
||||
static inline u16 sta_stats_encode_rate(struct ieee80211_rx_status *s)
|
||||
{
|
||||
u16 r = s->rate_idx;
|
||||
|
||||
if (s->vht_flag & RX_VHT_FLAG_80MHZ)
|
||||
r |= RATE_INFO_BW_80 << STA_STATS_RATE_BW_SHIFT;
|
||||
else if (s->vht_flag & RX_VHT_FLAG_160MHZ)
|
||||
r |= RATE_INFO_BW_160 << STA_STATS_RATE_BW_SHIFT;
|
||||
else if (s->flag & RX_FLAG_40MHZ)
|
||||
r |= RATE_INFO_BW_40 << STA_STATS_RATE_BW_SHIFT;
|
||||
else if (s->flag & RX_FLAG_10MHZ)
|
||||
r |= RATE_INFO_BW_10 << STA_STATS_RATE_BW_SHIFT;
|
||||
else if (s->flag & RX_FLAG_5MHZ)
|
||||
r |= RATE_INFO_BW_5 << STA_STATS_RATE_BW_SHIFT;
|
||||
else
|
||||
r |= RATE_INFO_BW_20 << STA_STATS_RATE_BW_SHIFT;
|
||||
|
||||
if (s->flag & RX_FLAG_SHORT_GI)
|
||||
r |= STA_STATS_RATE_SGI;
|
||||
|
||||
if (s->flag & RX_FLAG_VHT)
|
||||
r |= STA_STATS_RATE_VHT | (s->vht_nss << 4);
|
||||
else if (s->flag & RX_FLAG_HT)
|
||||
r |= STA_STATS_RATE_HT;
|
||||
else
|
||||
r |= STA_STATS_RATE_LEGACY | (s->band << 4);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#endif /* STA_INFO_H */
|
||||
|
|
|
@ -188,7 +188,7 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
|
|||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
|
||||
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
|
||||
sta->rx_stats.last_rx = jiffies;
|
||||
sta->status_stats.last_ack = jiffies;
|
||||
|
||||
if (ieee80211_is_data_qos(mgmt->frame_control)) {
|
||||
struct ieee80211_hdr *hdr = (void *) skb->data;
|
||||
|
@ -647,7 +647,7 @@ void ieee80211_tx_status_noskb(struct ieee80211_hw *hw,
|
|||
sta->status_stats.retry_count += retry_count;
|
||||
|
||||
if (acked) {
|
||||
sta->rx_stats.last_rx = jiffies;
|
||||
sta->status_stats.last_ack = jiffies;
|
||||
|
||||
if (sta->status_stats.lost_packets)
|
||||
sta->status_stats.lost_packets = 0;
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*
|
||||
* Portions of this file
|
||||
* Copyright(c) 2016 Intel Deutschland GmbH
|
||||
*/
|
||||
|
||||
#if !defined(__MAC80211_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define __MAC80211_DRIVER_TRACE
|
||||
|
||||
|
@ -899,6 +904,13 @@ DEFINE_EVENT(sta_event, drv_sta_pre_rcu_remove,
|
|||
TP_ARGS(local, sdata, sta)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(sta_event, drv_sync_rx_queues,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_sta *sta),
|
||||
TP_ARGS(local, sdata, sta)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(sta_event, drv_sta_rate_tbl_update,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
|
|
|
@ -1324,6 +1324,10 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
|
|||
out:
|
||||
spin_unlock_bh(&txqi->queue.lock);
|
||||
|
||||
if (skb && skb_has_frag_list(skb) &&
|
||||
!ieee80211_hw_check(&local->hw, TX_FRAG_LIST))
|
||||
skb_linearize(skb);
|
||||
|
||||
return skb;
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_tx_dequeue);
|
||||
|
@ -1691,7 +1695,9 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
|
|||
bool rate_found = false;
|
||||
u8 rate_retries = 0;
|
||||
u16 rate_flags = 0;
|
||||
u8 mcs_known, mcs_flags;
|
||||
u8 mcs_known, mcs_flags, mcs_bw;
|
||||
u16 vht_known;
|
||||
u8 vht_mcs = 0, vht_nss = 0;
|
||||
int i;
|
||||
|
||||
info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
|
||||
|
@ -1767,11 +1773,38 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
|
|||
mcs_flags & IEEE80211_RADIOTAP_MCS_SGI)
|
||||
rate_flags |= IEEE80211_TX_RC_SHORT_GI;
|
||||
|
||||
mcs_bw = mcs_flags & IEEE80211_RADIOTAP_MCS_BW_MASK;
|
||||
if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_BW &&
|
||||
mcs_flags & IEEE80211_RADIOTAP_MCS_BW_40)
|
||||
mcs_bw == IEEE80211_RADIOTAP_MCS_BW_40)
|
||||
rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
|
||||
break;
|
||||
|
||||
case IEEE80211_RADIOTAP_VHT:
|
||||
vht_known = get_unaligned_le16(iterator.this_arg);
|
||||
rate_found = true;
|
||||
|
||||
rate_flags = IEEE80211_TX_RC_VHT_MCS;
|
||||
if ((vht_known & IEEE80211_RADIOTAP_VHT_KNOWN_GI) &&
|
||||
(iterator.this_arg[2] &
|
||||
IEEE80211_RADIOTAP_VHT_FLAG_SGI))
|
||||
rate_flags |= IEEE80211_TX_RC_SHORT_GI;
|
||||
if (vht_known &
|
||||
IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH) {
|
||||
if (iterator.this_arg[3] == 1)
|
||||
rate_flags |=
|
||||
IEEE80211_TX_RC_40_MHZ_WIDTH;
|
||||
else if (iterator.this_arg[3] == 4)
|
||||
rate_flags |=
|
||||
IEEE80211_TX_RC_80_MHZ_WIDTH;
|
||||
else if (iterator.this_arg[3] == 11)
|
||||
rate_flags |=
|
||||
IEEE80211_TX_RC_160_MHZ_WIDTH;
|
||||
}
|
||||
|
||||
vht_mcs = iterator.this_arg[4] >> 4;
|
||||
vht_nss = iterator.this_arg[4] & 0xF;
|
||||
break;
|
||||
|
||||
/*
|
||||
* Please update the file
|
||||
* Documentation/networking/mac80211-injection.txt
|
||||
|
@ -1797,6 +1830,9 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
|
|||
|
||||
if (rate_flags & IEEE80211_TX_RC_MCS) {
|
||||
info->control.rates[0].idx = rate;
|
||||
} else if (rate_flags & IEEE80211_TX_RC_VHT_MCS) {
|
||||
ieee80211_rate_set_vht(info->control.rates, vht_mcs,
|
||||
vht_nss);
|
||||
} else {
|
||||
for (i = 0; i < sband->n_bitrates; i++) {
|
||||
if (rate * 5 != sband->bitrates[i].bitrate)
|
||||
|
@ -1807,6 +1843,9 @@ static bool ieee80211_parse_tx_radiotap(struct ieee80211_local *local,
|
|||
}
|
||||
}
|
||||
|
||||
if (info->control.rates[0].idx < 0)
|
||||
info->control.flags &= ~IEEE80211_TX_CTRL_RATE_INJECT;
|
||||
|
||||
info->control.rates[0].flags = rate_flags;
|
||||
info->control.rates[0].count = min_t(u8, rate_retries + 1,
|
||||
local->hw.max_rate_tries);
|
||||
|
@ -2181,7 +2220,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
|
|||
}
|
||||
|
||||
if (mppath && mpath)
|
||||
mesh_path_del(mpath->sdata, mpath->dst);
|
||||
mesh_path_del(sdata, mpath->dst);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2767,6 +2806,154 @@ void ieee80211_clear_fast_xmit(struct sta_info *sta)
|
|||
kfree_rcu(fast_tx, rcu_head);
|
||||
}
|
||||
|
||||
static bool ieee80211_amsdu_realloc_pad(struct ieee80211_local *local,
|
||||
struct sk_buff *skb, int headroom,
|
||||
int *subframe_len)
|
||||
{
|
||||
int amsdu_len = *subframe_len + sizeof(struct ethhdr);
|
||||
int padding = (4 - amsdu_len) & 3;
|
||||
|
||||
if (skb_headroom(skb) < headroom || skb_tailroom(skb) < padding) {
|
||||
I802_DEBUG_INC(local->tx_expand_skb_head);
|
||||
|
||||
if (pskb_expand_head(skb, headroom, padding, GFP_ATOMIC)) {
|
||||
wiphy_debug(local->hw.wiphy,
|
||||
"failed to reallocate TX buffer\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (padding) {
|
||||
*subframe_len += padding;
|
||||
memset(skb_put(skb, padding), 0, padding);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ieee80211_amsdu_prepare_head(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_fast_tx *fast_tx,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_hdr *hdr;
|
||||
struct ethhdr amsdu_hdr;
|
||||
int hdr_len = fast_tx->hdr_len - sizeof(rfc1042_header);
|
||||
int subframe_len = skb->len - hdr_len;
|
||||
void *data;
|
||||
u8 *qc;
|
||||
|
||||
if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
|
||||
return false;
|
||||
|
||||
if (info->control.flags & IEEE80211_TX_CTRL_AMSDU)
|
||||
return true;
|
||||
|
||||
if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(amsdu_hdr),
|
||||
&subframe_len))
|
||||
return false;
|
||||
|
||||
amsdu_hdr.h_proto = cpu_to_be16(subframe_len);
|
||||
memcpy(amsdu_hdr.h_source, skb->data + fast_tx->sa_offs, ETH_ALEN);
|
||||
memcpy(amsdu_hdr.h_dest, skb->data + fast_tx->da_offs, ETH_ALEN);
|
||||
|
||||
data = skb_push(skb, sizeof(amsdu_hdr));
|
||||
memmove(data, data + sizeof(amsdu_hdr), hdr_len);
|
||||
memcpy(data + hdr_len, &amsdu_hdr, sizeof(amsdu_hdr));
|
||||
|
||||
hdr = data;
|
||||
qc = ieee80211_get_qos_ctl(hdr);
|
||||
*qc |= IEEE80211_QOS_CTL_A_MSDU_PRESENT;
|
||||
|
||||
info->control.flags |= IEEE80211_TX_CTRL_AMSDU;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
|
||||
struct sta_info *sta,
|
||||
struct ieee80211_fast_tx *fast_tx,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
u8 tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
|
||||
struct ieee80211_txq *txq = sta->sta.txq[tid];
|
||||
struct txq_info *txqi;
|
||||
struct sk_buff **frag_tail, *head;
|
||||
int subframe_len = skb->len - ETH_ALEN;
|
||||
u8 max_subframes = sta->sta.max_amsdu_subframes;
|
||||
int max_frags = local->hw.max_tx_fragments;
|
||||
int max_amsdu_len = sta->sta.max_amsdu_len;
|
||||
__be16 len;
|
||||
void *data;
|
||||
bool ret = false;
|
||||
int n = 1, nfrags;
|
||||
|
||||
if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
|
||||
return false;
|
||||
|
||||
if (!txq)
|
||||
return false;
|
||||
|
||||
txqi = to_txq_info(txq);
|
||||
if (test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags))
|
||||
return false;
|
||||
|
||||
if (sta->sta.max_rc_amsdu_len)
|
||||
max_amsdu_len = min_t(int, max_amsdu_len,
|
||||
sta->sta.max_rc_amsdu_len);
|
||||
|
||||
spin_lock_bh(&txqi->queue.lock);
|
||||
|
||||
head = skb_peek_tail(&txqi->queue);
|
||||
if (!head)
|
||||
goto out;
|
||||
|
||||
if (skb->len + head->len > max_amsdu_len)
|
||||
goto out;
|
||||
|
||||
if (!ieee80211_amsdu_prepare_head(sdata, fast_tx, head))
|
||||
goto out;
|
||||
|
||||
nfrags = 1 + skb_shinfo(skb)->nr_frags;
|
||||
nfrags += 1 + skb_shinfo(head)->nr_frags;
|
||||
frag_tail = &skb_shinfo(head)->frag_list;
|
||||
while (*frag_tail) {
|
||||
nfrags += 1 + skb_shinfo(*frag_tail)->nr_frags;
|
||||
frag_tail = &(*frag_tail)->next;
|
||||
n++;
|
||||
}
|
||||
|
||||
if (max_subframes && n > max_subframes)
|
||||
goto out;
|
||||
|
||||
if (max_frags && nfrags > max_frags)
|
||||
goto out;
|
||||
|
||||
if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(rfc1042_header) + 2,
|
||||
&subframe_len))
|
||||
goto out;
|
||||
|
||||
ret = true;
|
||||
data = skb_push(skb, ETH_ALEN + 2);
|
||||
memmove(data, data + ETH_ALEN + 2, 2 * ETH_ALEN);
|
||||
|
||||
data += 2 * ETH_ALEN;
|
||||
len = cpu_to_be16(subframe_len);
|
||||
memcpy(data, &len, 2);
|
||||
memcpy(data + 2, rfc1042_header, sizeof(rfc1042_header));
|
||||
|
||||
head->len += skb->len;
|
||||
head->data_len += skb->len;
|
||||
*frag_tail = skb;
|
||||
|
||||
out:
|
||||
spin_unlock_bh(&txqi->queue.lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
|
||||
struct net_device *dev, struct sta_info *sta,
|
||||
struct ieee80211_fast_tx *fast_tx,
|
||||
|
@ -2821,6 +3008,10 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
ieee80211_tx_stats(dev, skb->len + extra_head);
|
||||
|
||||
if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
|
||||
ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
|
||||
return true;
|
||||
|
||||
/* will not be crypto-handled beyond what we do here, so use false
|
||||
* as the may-encrypt argument for the resize to not account for
|
||||
* more room than we already have in 'extra_head'
|
||||
|
|
|
@ -2724,8 +2724,9 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
|
|||
|
||||
rate = cfg80211_calculate_bitrate(&ri);
|
||||
if (WARN_ONCE(!rate,
|
||||
"Invalid bitrate: flags=0x%x, idx=%d, vht_nss=%d\n",
|
||||
status->flag, status->rate_idx, status->vht_nss))
|
||||
"Invalid bitrate: flags=0x%llx, idx=%d, vht_nss=%d\n",
|
||||
(unsigned long long)status->flag, status->rate_idx,
|
||||
status->vht_nss))
|
||||
return 0;
|
||||
|
||||
/* rewind from end of MPDU */
|
||||
|
|
|
@ -504,18 +504,20 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
|
|||
!ieee80211_is_robust_mgmt_frame(skb))
|
||||
return RX_CONTINUE;
|
||||
|
||||
data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - mic_len;
|
||||
if (!rx->sta || data_len < 0)
|
||||
return RX_DROP_UNUSABLE;
|
||||
|
||||
if (status->flag & RX_FLAG_DECRYPTED) {
|
||||
if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_CCMP_HDR_LEN))
|
||||
return RX_DROP_UNUSABLE;
|
||||
if (status->flag & RX_FLAG_MIC_STRIPPED)
|
||||
mic_len = 0;
|
||||
} else {
|
||||
if (skb_linearize(rx->skb))
|
||||
return RX_DROP_UNUSABLE;
|
||||
}
|
||||
|
||||
data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - mic_len;
|
||||
if (!rx->sta || data_len < 0)
|
||||
return RX_DROP_UNUSABLE;
|
||||
|
||||
if (!(status->flag & RX_FLAG_PN_VALIDATED)) {
|
||||
ccmp_hdr2pn(pn, skb->data + hdrlen);
|
||||
|
||||
|
@ -720,8 +722,7 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
|
|||
struct sk_buff *skb = rx->skb;
|
||||
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
||||
u8 pn[IEEE80211_GCMP_PN_LEN];
|
||||
int data_len;
|
||||
int queue;
|
||||
int data_len, queue, mic_len = IEEE80211_GCMP_MIC_LEN;
|
||||
|
||||
hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
|
||||
|
@ -729,19 +730,20 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
|
|||
!ieee80211_is_robust_mgmt_frame(skb))
|
||||
return RX_CONTINUE;
|
||||
|
||||
data_len = skb->len - hdrlen - IEEE80211_GCMP_HDR_LEN -
|
||||
IEEE80211_GCMP_MIC_LEN;
|
||||
if (!rx->sta || data_len < 0)
|
||||
return RX_DROP_UNUSABLE;
|
||||
|
||||
if (status->flag & RX_FLAG_DECRYPTED) {
|
||||
if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_GCMP_HDR_LEN))
|
||||
return RX_DROP_UNUSABLE;
|
||||
if (status->flag & RX_FLAG_MIC_STRIPPED)
|
||||
mic_len = 0;
|
||||
} else {
|
||||
if (skb_linearize(rx->skb))
|
||||
return RX_DROP_UNUSABLE;
|
||||
}
|
||||
|
||||
data_len = skb->len - hdrlen - IEEE80211_GCMP_HDR_LEN - mic_len;
|
||||
if (!rx->sta || data_len < 0)
|
||||
return RX_DROP_UNUSABLE;
|
||||
|
||||
if (!(status->flag & RX_FLAG_PN_VALIDATED)) {
|
||||
gcmp_hdr2pn(pn, skb->data + hdrlen);
|
||||
|
||||
|
@ -772,7 +774,7 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
|
|||
}
|
||||
|
||||
/* Remove GCMP header and MIC */
|
||||
if (pskb_trim(skb, skb->len - IEEE80211_GCMP_MIC_LEN))
|
||||
if (pskb_trim(skb, skb->len - mic_len))
|
||||
return RX_DROP_UNUSABLE;
|
||||
memmove(skb->data + IEEE80211_GCMP_HDR_LEN, skb->data, hdrlen);
|
||||
skb_pull(skb, IEEE80211_GCMP_HDR_LEN);
|
||||
|
|
|
@ -192,7 +192,7 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
|
|||
u8 genmask = nft_genmask_cur(read_pnet(&set->pnet));
|
||||
int err;
|
||||
|
||||
err = rhashtable_walk_init(&priv->ht, &hti);
|
||||
err = rhashtable_walk_init(&priv->ht, &hti, GFP_KERNEL);
|
||||
iter->err = err;
|
||||
if (err)
|
||||
return;
|
||||
|
@ -248,7 +248,7 @@ static void nft_hash_gc(struct work_struct *work)
|
|||
priv = container_of(work, struct nft_hash, gc_work.work);
|
||||
set = nft_set_container_of(priv);
|
||||
|
||||
err = rhashtable_walk_init(&priv->ht, &hti);
|
||||
err = rhashtable_walk_init(&priv->ht, &hti, GFP_KERNEL);
|
||||
if (err)
|
||||
goto schedule;
|
||||
|
||||
|
|
|
@ -2343,7 +2343,8 @@ static int netlink_walk_start(struct nl_seq_iter *iter)
|
|||
{
|
||||
int err;
|
||||
|
||||
err = rhashtable_walk_init(&nl_table[iter->link].hash, &iter->hti);
|
||||
err = rhashtable_walk_init(&nl_table[iter->link].hash, &iter->hti,
|
||||
GFP_KERNEL);
|
||||
if (err) {
|
||||
iter->link = MAX_LINKS;
|
||||
return err;
|
||||
|
|
|
@ -1141,6 +1141,7 @@ static ssize_t rfkill_fop_write(struct file *file, const char __user *buf,
|
|||
{
|
||||
struct rfkill *rfkill;
|
||||
struct rfkill_event ev;
|
||||
int ret;
|
||||
|
||||
/* we don't need the 'hard' variable but accept it */
|
||||
if (count < RFKILL_EVENT_SIZE_V1 - 1)
|
||||
|
@ -1155,29 +1156,36 @@ static ssize_t rfkill_fop_write(struct file *file, const char __user *buf,
|
|||
if (copy_from_user(&ev, buf, count))
|
||||
return -EFAULT;
|
||||
|
||||
if (ev.op != RFKILL_OP_CHANGE && ev.op != RFKILL_OP_CHANGE_ALL)
|
||||
return -EINVAL;
|
||||
|
||||
if (ev.type >= NUM_RFKILL_TYPES)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&rfkill_global_mutex);
|
||||
|
||||
if (ev.op == RFKILL_OP_CHANGE_ALL)
|
||||
switch (ev.op) {
|
||||
case RFKILL_OP_CHANGE_ALL:
|
||||
rfkill_update_global_state(ev.type, ev.soft);
|
||||
|
||||
list_for_each_entry(rfkill, &rfkill_list, node) {
|
||||
if (rfkill->idx != ev.idx && ev.op != RFKILL_OP_CHANGE_ALL)
|
||||
continue;
|
||||
|
||||
if (rfkill->type != ev.type && ev.type != RFKILL_TYPE_ALL)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(rfkill, &rfkill_list, node)
|
||||
if (rfkill->type == ev.type ||
|
||||
ev.type == RFKILL_TYPE_ALL)
|
||||
rfkill_set_block(rfkill, ev.soft);
|
||||
ret = 0;
|
||||
break;
|
||||
case RFKILL_OP_CHANGE:
|
||||
list_for_each_entry(rfkill, &rfkill_list, node)
|
||||
if (rfkill->idx == ev.idx &&
|
||||
(rfkill->type == ev.type ||
|
||||
ev.type == RFKILL_TYPE_ALL))
|
||||
rfkill_set_block(rfkill, ev.soft);
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&rfkill_global_mutex);
|
||||
|
||||
return count;
|
||||
return ret ?: count;
|
||||
}
|
||||
|
||||
static int rfkill_fop_release(struct inode *inode, struct file *file)
|
||||
|
|
|
@ -319,7 +319,8 @@ static int sctp_transport_walk_start(struct seq_file *seq)
|
|||
struct sctp_ht_iter *iter = seq->private;
|
||||
int err;
|
||||
|
||||
err = rhashtable_walk_init(&sctp_transport_hashtable, &iter->hti);
|
||||
err = rhashtable_walk_init(&sctp_transport_hashtable, &iter->hti,
|
||||
GFP_KERNEL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
|
@ -626,6 +626,13 @@ int wiphy_register(struct wiphy *wiphy)
|
|||
!rdev->ops->set_mac_acl)))
|
||||
return -EINVAL;
|
||||
|
||||
/* assure only valid behaviours are flagged by driver
|
||||
* hence subtract 2 as bit 0 is invalid.
|
||||
*/
|
||||
if (WARN_ON(wiphy->bss_select_support &&
|
||||
(wiphy->bss_select_support & ~(BIT(__NL80211_BSS_SELECT_ATTR_AFTER_LAST) - 2))))
|
||||
return -EINVAL;
|
||||
|
||||
if (wiphy->addresses)
|
||||
memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);
|
||||
|
||||
|
|
|
@ -402,6 +402,8 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
|||
[NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG },
|
||||
[NL80211_ATTR_PBSS] = { .type = NLA_FLAG },
|
||||
[NL80211_ATTR_BSS_SELECT] = { .type = NLA_NESTED },
|
||||
[NL80211_ATTR_STA_SUPPORT_P2P_PS] = { .type = NLA_U8 },
|
||||
};
|
||||
|
||||
/* policy for the key attributes */
|
||||
|
@ -486,6 +488,15 @@ nl80211_plan_policy[NL80211_SCHED_SCAN_PLAN_MAX + 1] = {
|
|||
[NL80211_SCHED_SCAN_PLAN_ITERATIONS] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
static const struct nla_policy
|
||||
nl80211_bss_select_policy[NL80211_BSS_SELECT_ATTR_MAX + 1] = {
|
||||
[NL80211_BSS_SELECT_ATTR_RSSI] = { .type = NLA_FLAG },
|
||||
[NL80211_BSS_SELECT_ATTR_BAND_PREF] = { .type = NLA_U32 },
|
||||
[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST] = {
|
||||
.len = sizeof(struct nl80211_bss_select_rssi_adjust)
|
||||
},
|
||||
};
|
||||
|
||||
static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
|
||||
struct netlink_callback *cb,
|
||||
struct cfg80211_registered_device **rdev,
|
||||
|
@ -1731,6 +1742,25 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
|||
rdev->wiphy.ext_features))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (rdev->wiphy.bss_select_support) {
|
||||
struct nlattr *nested;
|
||||
u32 bss_select_support = rdev->wiphy.bss_select_support;
|
||||
|
||||
nested = nla_nest_start(msg, NL80211_ATTR_BSS_SELECT);
|
||||
if (!nested)
|
||||
goto nla_put_failure;
|
||||
|
||||
i = 0;
|
||||
while (bss_select_support) {
|
||||
if ((bss_select_support & 1) &&
|
||||
nla_put_flag(msg, i))
|
||||
goto nla_put_failure;
|
||||
i++;
|
||||
bss_select_support >>= 1;
|
||||
}
|
||||
nla_nest_end(msg, nested);
|
||||
}
|
||||
|
||||
/* done */
|
||||
state->split_start = 0;
|
||||
break;
|
||||
|
@ -3977,6 +4007,10 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
|
|||
statype != CFG80211_STA_AP_CLIENT_UNASSOC)
|
||||
return -EINVAL;
|
||||
|
||||
if (params->support_p2p_ps != -1 &&
|
||||
statype != CFG80211_STA_AP_CLIENT_UNASSOC)
|
||||
return -EINVAL;
|
||||
|
||||
if (params->aid &&
|
||||
!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
|
||||
statype != CFG80211_STA_AP_CLIENT_UNASSOC)
|
||||
|
@ -4270,6 +4304,18 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|||
else
|
||||
params.listen_interval = -1;
|
||||
|
||||
if (info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]) {
|
||||
u8 tmp;
|
||||
|
||||
tmp = nla_get_u8(info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]);
|
||||
if (tmp >= NUM_NL80211_P2P_PS_STATUS)
|
||||
return -EINVAL;
|
||||
|
||||
params.support_p2p_ps = tmp;
|
||||
} else {
|
||||
params.support_p2p_ps = -1;
|
||||
}
|
||||
|
||||
if (!info->attrs[NL80211_ATTR_MAC])
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -4393,6 +4439,23 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
|||
params.listen_interval =
|
||||
nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
|
||||
|
||||
if (info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]) {
|
||||
u8 tmp;
|
||||
|
||||
tmp = nla_get_u8(info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]);
|
||||
if (tmp >= NUM_NL80211_P2P_PS_STATUS)
|
||||
return -EINVAL;
|
||||
|
||||
params.support_p2p_ps = tmp;
|
||||
} else {
|
||||
/*
|
||||
* if not specified, assume it's supported for P2P GO interface,
|
||||
* and is NOT supported for AP interface
|
||||
*/
|
||||
params.support_p2p_ps =
|
||||
dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_PEER_AID])
|
||||
params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
|
||||
else
|
||||
|
@ -5758,6 +5821,73 @@ static int validate_scan_freqs(struct nlattr *freqs)
|
|||
return n_channels;
|
||||
}
|
||||
|
||||
static bool is_band_valid(struct wiphy *wiphy, enum ieee80211_band b)
|
||||
{
|
||||
return b < IEEE80211_NUM_BANDS && wiphy->bands[b];
|
||||
}
|
||||
|
||||
static int parse_bss_select(struct nlattr *nla, struct wiphy *wiphy,
|
||||
struct cfg80211_bss_selection *bss_select)
|
||||
{
|
||||
struct nlattr *attr[NL80211_BSS_SELECT_ATTR_MAX + 1];
|
||||
struct nlattr *nest;
|
||||
int err;
|
||||
bool found = false;
|
||||
int i;
|
||||
|
||||
/* only process one nested attribute */
|
||||
nest = nla_data(nla);
|
||||
if (!nla_ok(nest, nla_len(nest)))
|
||||
return -EINVAL;
|
||||
|
||||
err = nla_parse(attr, NL80211_BSS_SELECT_ATTR_MAX, nla_data(nest),
|
||||
nla_len(nest), nl80211_bss_select_policy);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* only one attribute may be given */
|
||||
for (i = 0; i <= NL80211_BSS_SELECT_ATTR_MAX; i++) {
|
||||
if (attr[i]) {
|
||||
if (found)
|
||||
return -EINVAL;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
bss_select->behaviour = __NL80211_BSS_SELECT_ATTR_INVALID;
|
||||
|
||||
if (attr[NL80211_BSS_SELECT_ATTR_RSSI])
|
||||
bss_select->behaviour = NL80211_BSS_SELECT_ATTR_RSSI;
|
||||
|
||||
if (attr[NL80211_BSS_SELECT_ATTR_BAND_PREF]) {
|
||||
bss_select->behaviour = NL80211_BSS_SELECT_ATTR_BAND_PREF;
|
||||
bss_select->param.band_pref =
|
||||
nla_get_u32(attr[NL80211_BSS_SELECT_ATTR_BAND_PREF]);
|
||||
if (!is_band_valid(wiphy, bss_select->param.band_pref))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (attr[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST]) {
|
||||
struct nl80211_bss_select_rssi_adjust *adj_param;
|
||||
|
||||
adj_param = nla_data(attr[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST]);
|
||||
bss_select->behaviour = NL80211_BSS_SELECT_ATTR_RSSI_ADJUST;
|
||||
bss_select->param.adjust.band = adj_param->band;
|
||||
bss_select->param.adjust.delta = adj_param->delta;
|
||||
if (!is_band_valid(wiphy, bss_select->param.adjust.band))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* user-space did not provide behaviour attribute */
|
||||
if (bss_select->behaviour == __NL80211_BSS_SELECT_ATTR_INVALID)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(wiphy->bss_select_support & BIT(bss_select->behaviour)))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_parse_random_mac(struct nlattr **attrs,
|
||||
u8 *mac_addr, u8 *mac_addr_mask)
|
||||
{
|
||||
|
@ -5996,6 +6126,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
|
|||
request->no_cck =
|
||||
nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
|
||||
|
||||
if (info->attrs[NL80211_ATTR_MAC])
|
||||
memcpy(request->bssid, nla_data(info->attrs[NL80211_ATTR_MAC]),
|
||||
ETH_ALEN);
|
||||
else
|
||||
eth_broadcast_addr(request->bssid);
|
||||
|
||||
request->wdev = wdev;
|
||||
request->wiphy = &rdev->wiphy;
|
||||
request->scan_start = jiffies;
|
||||
|
@ -7922,6 +8058,10 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
|
|||
connect.mfp = NL80211_MFP_NO;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_PREV_BSSID])
|
||||
connect.prev_bssid =
|
||||
nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
|
||||
|
||||
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
||||
connect.channel = nl80211_get_valid_chan(
|
||||
wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ]);
|
||||
|
@ -7995,8 +8135,24 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_BSS_SELECT]) {
|
||||
/* bss selection makes no sense if bssid is set */
|
||||
if (connect.bssid) {
|
||||
kzfree(connkeys);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = parse_bss_select(info->attrs[NL80211_ATTR_BSS_SELECT],
|
||||
wiphy, &connect.bss_select);
|
||||
if (err) {
|
||||
kzfree(connkeys);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
wdev_lock(dev->ieee80211_ptr);
|
||||
err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL);
|
||||
err = cfg80211_connect(rdev, dev, &connect, connkeys,
|
||||
connect.prev_bssid);
|
||||
wdev_unlock(dev->ieee80211_ptr);
|
||||
if (err)
|
||||
kzfree(connkeys);
|
||||
|
|
|
@ -1293,6 +1293,8 @@ int cfg80211_wext_siwscan(struct net_device *dev,
|
|||
if (wiphy->bands[i])
|
||||
creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
|
||||
|
||||
eth_broadcast_addr(creq->bssid);
|
||||
|
||||
rdev->scan_req = creq;
|
||||
err = rdev_scan(rdev, creq);
|
||||
if (err) {
|
||||
|
|
|
@ -119,6 +119,8 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
|
|||
wdev->conn->params.ssid_len);
|
||||
request->ssids[0].ssid_len = wdev->conn->params.ssid_len;
|
||||
|
||||
eth_broadcast_addr(request->bssid);
|
||||
|
||||
request->wdev = wdev;
|
||||
request->wiphy = &rdev->wiphy;
|
||||
request->scan_start = jiffies;
|
||||
|
@ -490,8 +492,18 @@ static int cfg80211_sme_connect(struct wireless_dev *wdev,
|
|||
if (!rdev->ops->auth || !rdev->ops->assoc)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (wdev->current_bss)
|
||||
if (wdev->current_bss) {
|
||||
if (!prev_bssid)
|
||||
return -EALREADY;
|
||||
if (prev_bssid &&
|
||||
!ether_addr_equal(prev_bssid, wdev->current_bss->pub.bssid))
|
||||
return -ENOTCONN;
|
||||
cfg80211_unhold_bss(wdev->current_bss);
|
||||
cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
|
||||
wdev->current_bss = NULL;
|
||||
|
||||
cfg80211_sme_free(wdev);
|
||||
}
|
||||
|
||||
if (WARN_ON(wdev->conn))
|
||||
return -EINPROGRESS;
|
||||
|
|
|
@ -1259,6 +1259,7 @@ TRACE_EVENT(rdev_connect,
|
|||
__field(bool, privacy)
|
||||
__field(u32, wpa_versions)
|
||||
__field(u32, flags)
|
||||
MAC_ENTRY(prev_bssid)
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
|
@ -1270,13 +1271,14 @@ TRACE_EVENT(rdev_connect,
|
|||
__entry->privacy = sme->privacy;
|
||||
__entry->wpa_versions = sme->crypto.wpa_versions;
|
||||
__entry->flags = sme->flags;
|
||||
MAC_ASSIGN(prev_bssid, sme->prev_bssid);
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
|
||||
", ssid: %s, auth type: %d, privacy: %s, wpa versions: %u, "
|
||||
"flags: %u",
|
||||
"flags: %u, previous bssid: " MAC_PR_FMT,
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid,
|
||||
__entry->auth_type, BOOL_TO_STR(__entry->privacy),
|
||||
__entry->wpa_versions, __entry->flags)
|
||||
__entry->wpa_versions, __entry->flags, MAC_PR_ARG(prev_bssid))
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_set_cqm_rssi_config,
|
||||
|
|
|
@ -399,7 +399,10 @@ static int __init wireless_nlevent_init(void)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
return register_netdevice_notifier(&wext_netdev_notifier);
|
||||
err = register_netdevice_notifier(&wext_netdev_notifier);
|
||||
if (err)
|
||||
unregister_pernet_subsys(&wext_pernet_ops);
|
||||
return err;
|
||||
}
|
||||
|
||||
subsys_initcall(wireless_nlevent_init);
|
||||
|
|
Loading…
Reference in New Issue