Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Conflicts: drivers/net/wireless/mwifiex/sta_ioctl.c net/mac80211/scan.c
This commit is contained in:
Коммит
403e16731f
|
@ -298,6 +298,7 @@ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss)
|
|||
const u8 *rates_eid, *ext_rates_eid;
|
||||
int n = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
|
||||
ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
|
||||
|
||||
|
@ -325,6 +326,7 @@ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss)
|
|||
*tlv++ = 0x96;
|
||||
n = 4;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
rate_tlv->header.len = cpu_to_le16(n);
|
||||
return sizeof(rate_tlv->header) + n;
|
||||
|
@ -1140,11 +1142,13 @@ static int lbs_associate(struct lbs_private *priv,
|
|||
cmd->capability = cpu_to_le16(bss->capability);
|
||||
|
||||
/* add SSID TLV */
|
||||
rcu_read_lock();
|
||||
ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
|
||||
if (ssid_eid)
|
||||
pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]);
|
||||
else
|
||||
lbs_deb_assoc("no SSID\n");
|
||||
rcu_read_unlock();
|
||||
|
||||
/* add DS param TLV */
|
||||
if (bss->channel)
|
||||
|
@ -1782,7 +1786,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv,
|
|||
struct cfg80211_ibss_params *params,
|
||||
struct cfg80211_bss *bss)
|
||||
{
|
||||
const u8 *rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
|
||||
const u8 *rates_eid;
|
||||
struct cmd_ds_802_11_ad_hoc_join cmd;
|
||||
u8 preamble = RADIO_PREAMBLE_SHORT;
|
||||
int ret = 0;
|
||||
|
@ -1841,6 +1845,8 @@ static int lbs_ibss_join_existing(struct lbs_private *priv,
|
|||
|
||||
/* set rates to the intersection of our rates and the rates in the
|
||||
bss */
|
||||
rcu_read_lock();
|
||||
rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
|
||||
if (!rates_eid) {
|
||||
lbs_add_rates(cmd.bss.rates);
|
||||
} else {
|
||||
|
@ -1860,6 +1866,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv,
|
|||
}
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
/* Only v8 and below support setting this */
|
||||
if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) {
|
||||
|
|
|
@ -1347,9 +1347,14 @@ static void hw_scan_work(struct work_struct *work)
|
|||
hwsim->hw_scan_vif,
|
||||
req->ssids[i].ssid,
|
||||
req->ssids[i].ssid_len,
|
||||
req->ie, req->ie_len);
|
||||
req->ie_len);
|
||||
if (!probe)
|
||||
continue;
|
||||
|
||||
if (req->ie_len)
|
||||
memcpy(skb_put(probe, req->ie_len), req->ie,
|
||||
req->ie_len);
|
||||
|
||||
local_bh_disable();
|
||||
mac80211_hwsim_tx_frame(hwsim->hw, probe,
|
||||
hwsim->tmp_chan);
|
||||
|
|
|
@ -160,11 +160,21 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
|
|||
{
|
||||
int ret;
|
||||
u8 *beacon_ie;
|
||||
size_t beacon_ie_len;
|
||||
struct mwifiex_bss_priv *bss_priv = (void *)bss->priv;
|
||||
size_t beacon_ie_len = bss->len_information_elements;
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
|
||||
rcu_read_lock();
|
||||
ies = rcu_dereference(bss->ies);
|
||||
if (WARN_ON(!ies)) {
|
||||
/* should never happen */
|
||||
rcu_read_unlock();
|
||||
return -EINVAL;
|
||||
}
|
||||
beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC);
|
||||
beacon_ie_len = ies->len;
|
||||
rcu_read_unlock();
|
||||
|
||||
beacon_ie = kmemdup(bss->information_elements, beacon_ie_len,
|
||||
GFP_KERNEL);
|
||||
if (!beacon_ie) {
|
||||
dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n");
|
||||
return -ENOMEM;
|
||||
|
@ -199,18 +209,23 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
|
|||
static int mwifiex_process_country_ie(struct mwifiex_private *priv,
|
||||
struct cfg80211_bss *bss)
|
||||
{
|
||||
u8 *country_ie, country_ie_len;
|
||||
const u8 *country_ie;
|
||||
u8 country_ie_len;
|
||||
struct mwifiex_802_11d_domain_reg *domain_info =
|
||||
&priv->adapter->domain_reg;
|
||||
|
||||
country_ie = (u8 *)ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
|
||||
|
||||
if (!country_ie)
|
||||
rcu_read_lock();
|
||||
country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
|
||||
if (!country_ie) {
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
country_ie_len = country_ie[1];
|
||||
if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
|
||||
if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) {
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
domain_info->country_code[0] = country_ie[2];
|
||||
domain_info->country_code[1] = country_ie[3];
|
||||
|
@ -224,6 +239,8 @@ static int mwifiex_process_country_ie(struct mwifiex_private *priv,
|
|||
memcpy((u8 *)domain_info->triplet,
|
||||
&country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
|
||||
HostCmd_ACT_GEN_SET, 0, NULL)) {
|
||||
wiphy_err(priv->adapter->wiphy,
|
||||
|
|
|
@ -896,11 +896,13 @@ static int wl1251_op_hw_scan(struct ieee80211_hw *hw,
|
|||
goto out;
|
||||
|
||||
skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len,
|
||||
req->ie, req->ie_len);
|
||||
req->ie_len);
|
||||
if (!skb) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (req->ie_len)
|
||||
memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len);
|
||||
|
||||
ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, skb->data,
|
||||
skb->len);
|
||||
|
|
|
@ -1038,11 +1038,13 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|||
u16 template_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
|
||||
|
||||
skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
|
||||
ie, ie_len);
|
||||
ie_len);
|
||||
if (!skb) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (ie_len)
|
||||
memcpy(skb_put(skb, ie_len), ie, ie_len);
|
||||
|
||||
wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len);
|
||||
|
||||
|
|
|
@ -1212,6 +1212,21 @@ struct ieee80211_vht_cap {
|
|||
struct ieee80211_vht_mcs_info supp_mcs;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* enum ieee80211_vht_chanwidth - VHT channel width
|
||||
* @IEEE80211_VHT_CHANWIDTH_USE_HT: use the HT operation IE to
|
||||
* determine the channel width (20 or 40 MHz)
|
||||
* @IEEE80211_VHT_CHANWIDTH_80MHZ: 80 MHz bandwidth
|
||||
* @IEEE80211_VHT_CHANWIDTH_160MHZ: 160 MHz bandwidth
|
||||
* @IEEE80211_VHT_CHANWIDTH_80P80MHZ: 80+80 MHz bandwidth
|
||||
*/
|
||||
enum ieee80211_vht_chanwidth {
|
||||
IEEE80211_VHT_CHANWIDTH_USE_HT = 0,
|
||||
IEEE80211_VHT_CHANWIDTH_80MHZ = 1,
|
||||
IEEE80211_VHT_CHANWIDTH_160MHZ = 2,
|
||||
IEEE80211_VHT_CHANWIDTH_80P80MHZ = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ieee80211_vht_operation - VHT operation IE
|
||||
*
|
||||
|
|
|
@ -58,6 +58,8 @@
|
|||
* structures here describe these capabilities in detail.
|
||||
*/
|
||||
|
||||
struct wiphy;
|
||||
|
||||
/*
|
||||
* wireless hardware capability structures
|
||||
*/
|
||||
|
@ -387,6 +389,22 @@ const struct cfg80211_chan_def *
|
|||
cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1,
|
||||
const struct cfg80211_chan_def *chandef2);
|
||||
|
||||
/**
|
||||
* cfg80211_chandef_valid - check if a channel definition is valid
|
||||
* @chandef: the channel definition to check
|
||||
*/
|
||||
bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef);
|
||||
|
||||
/**
|
||||
* cfg80211_chandef_usable - check if secondary channels can be used
|
||||
* @wiphy: the wiphy to validate against
|
||||
* @chandef: the channel definition to check
|
||||
* @prohibited_flags: the regulatory chanenl flags that must not be set
|
||||
*/
|
||||
bool cfg80211_chandef_usable(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
u32 prohibited_flags);
|
||||
|
||||
/**
|
||||
* enum survey_info_flags - survey information flags
|
||||
*
|
||||
|
@ -520,6 +538,8 @@ struct cfg80211_beacon_data {
|
|||
* @privacy: the BSS uses privacy
|
||||
* @auth_type: Authentication type (algorithm)
|
||||
* @inactivity_timeout: time in seconds to determine station's inactivity.
|
||||
* @p2p_ctwindow: P2P CT Window
|
||||
* @p2p_opp_ps: P2P opportunistic PS
|
||||
*/
|
||||
struct cfg80211_ap_settings {
|
||||
struct cfg80211_chan_def chandef;
|
||||
|
@ -534,6 +554,8 @@ struct cfg80211_ap_settings {
|
|||
bool privacy;
|
||||
enum nl80211_auth_type auth_type;
|
||||
int inactivity_timeout;
|
||||
u8 p2p_ctwindow;
|
||||
bool p2p_opp_ps;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -895,6 +917,8 @@ struct mpath_info {
|
|||
* @ap_isolate: do not forward packets between connected stations
|
||||
* @ht_opmode: HT Operation mode
|
||||
* (u16 = opmode, -1 = do not change)
|
||||
* @p2p_ctwindow: P2P CT Window (-1 = no change)
|
||||
* @p2p_opp_ps: P2P opportunistic PS (-1 = no change)
|
||||
*/
|
||||
struct bss_parameters {
|
||||
int use_cts_prot;
|
||||
|
@ -904,6 +928,7 @@ struct bss_parameters {
|
|||
u8 basic_rates_len;
|
||||
int ap_isolate;
|
||||
int ht_opmode;
|
||||
s8 p2p_ctwindow, p2p_opp_ps;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1045,9 +1070,6 @@ struct ieee80211_txq_params {
|
|||
u8 aifs;
|
||||
};
|
||||
|
||||
/* from net/wireless.h */
|
||||
struct wiphy;
|
||||
|
||||
/**
|
||||
* DOC: Scanning and BSS list handling
|
||||
*
|
||||
|
@ -1183,6 +1205,18 @@ enum cfg80211_signal_type {
|
|||
CFG80211_SIGNAL_TYPE_UNSPEC,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_bss_ie_data - BSS entry IE data
|
||||
* @rcu_head: internal use, for freeing
|
||||
* @len: length of the IEs
|
||||
* @data: IE data
|
||||
*/
|
||||
struct cfg80211_bss_ies {
|
||||
struct rcu_head rcu_head;
|
||||
int len;
|
||||
u8 data[];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_bss - BSS description
|
||||
*
|
||||
|
@ -1194,36 +1228,34 @@ enum cfg80211_signal_type {
|
|||
* @tsf: timestamp of last received update
|
||||
* @beacon_interval: the beacon interval as from the frame
|
||||
* @capability: the capability field in host byte order
|
||||
* @information_elements: the information elements (Note that there
|
||||
* @ies: the information elements (Note that there
|
||||
* is no guarantee that these are well-formed!); this is a pointer to
|
||||
* either the beacon_ies or proberesp_ies depending on whether Probe
|
||||
* Response frame has been received
|
||||
* @len_information_elements: total length of the information elements
|
||||
* @beacon_ies: the information elements from the last Beacon frame
|
||||
* @len_beacon_ies: total length of the beacon_ies
|
||||
* @proberesp_ies: the information elements from the last Probe Response frame
|
||||
* @len_proberesp_ies: total length of the proberesp_ies
|
||||
* @signal: signal strength value (type depends on the wiphy's signal_type)
|
||||
* @free_priv: function pointer to free private data
|
||||
* @priv: private area for driver use, has at least wiphy->bss_priv_size bytes
|
||||
*/
|
||||
struct cfg80211_bss {
|
||||
u64 tsf;
|
||||
|
||||
struct ieee80211_channel *channel;
|
||||
|
||||
u8 bssid[ETH_ALEN];
|
||||
u64 tsf;
|
||||
u16 beacon_interval;
|
||||
u16 capability;
|
||||
u8 *information_elements;
|
||||
size_t len_information_elements;
|
||||
u8 *beacon_ies;
|
||||
size_t len_beacon_ies;
|
||||
u8 *proberesp_ies;
|
||||
size_t len_proberesp_ies;
|
||||
const struct cfg80211_bss_ies __rcu *ies;
|
||||
const struct cfg80211_bss_ies __rcu *beacon_ies;
|
||||
const struct cfg80211_bss_ies __rcu *proberesp_ies;
|
||||
|
||||
void (*free_priv)(struct cfg80211_bss *bss);
|
||||
|
||||
s32 signal;
|
||||
|
||||
void (*free_priv)(struct cfg80211_bss *bss);
|
||||
u16 beacon_interval;
|
||||
u16 capability;
|
||||
|
||||
u8 bssid[ETH_ALEN];
|
||||
|
||||
u8 priv[0] __attribute__((__aligned__(sizeof(void *))));
|
||||
};
|
||||
|
||||
|
@ -1231,6 +1263,9 @@ struct cfg80211_bss {
|
|||
* ieee80211_bss_get_ie - find IE with given ID
|
||||
* @bss: the bss to search
|
||||
* @ie: the IE ID
|
||||
*
|
||||
* Note that the return value is an RCU-protected pointer, so
|
||||
* rcu_read_lock() must be held when calling this function.
|
||||
* Returns %NULL if not found.
|
||||
*/
|
||||
const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie);
|
||||
|
|
|
@ -186,6 +186,10 @@ struct ieee80211_radiotap_header {
|
|||
* IEEE80211_RADIOTAP_AMPDU_STATUS u32, u16, u8, u8 unitless
|
||||
*
|
||||
* Contains the AMPDU information for the subframe.
|
||||
*
|
||||
* IEEE80211_RADIOTAP_VHT u16, u8, u8, u8[4], u8, u8, u16
|
||||
*
|
||||
* Contains VHT information about this frame.
|
||||
*/
|
||||
enum ieee80211_radiotap_type {
|
||||
IEEE80211_RADIOTAP_TSFT = 0,
|
||||
|
@ -209,6 +213,7 @@ enum ieee80211_radiotap_type {
|
|||
|
||||
IEEE80211_RADIOTAP_MCS = 19,
|
||||
IEEE80211_RADIOTAP_AMPDU_STATUS = 20,
|
||||
IEEE80211_RADIOTAP_VHT = 21,
|
||||
|
||||
/* valid in every it_present bitmap, even vendor namespaces */
|
||||
IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29,
|
||||
|
@ -282,6 +287,25 @@ enum ieee80211_radiotap_type {
|
|||
#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR 0x0010
|
||||
#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN 0x0020
|
||||
|
||||
/* For IEEE80211_RADIOTAP_VHT */
|
||||
#define IEEE80211_RADIOTAP_VHT_KNOWN_STBC 0x0001
|
||||
#define IEEE80211_RADIOTAP_VHT_KNOWN_TXOP_PS_NA 0x0002
|
||||
#define IEEE80211_RADIOTAP_VHT_KNOWN_GI 0x0004
|
||||
#define IEEE80211_RADIOTAP_VHT_KNOWN_SGI_NSYM_DIS 0x0008
|
||||
#define IEEE80211_RADIOTAP_VHT_KNOWN_LDPC_EXTRA_OFDM_SYM 0x0010
|
||||
#define IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED 0x0020
|
||||
#define IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH 0x0040
|
||||
#define IEEE80211_RADIOTAP_VHT_KNOWN_GROUP_ID 0x0080
|
||||
#define IEEE80211_RADIOTAP_VHT_KNOWN_PARTIAL_AID 0x0100
|
||||
|
||||
#define IEEE80211_RADIOTAP_VHT_FLAG_STBC 0x01
|
||||
#define IEEE80211_RADIOTAP_VHT_FLAG_TXOP_PS_NA 0x02
|
||||
#define IEEE80211_RADIOTAP_VHT_FLAG_SGI 0x04
|
||||
#define IEEE80211_RADIOTAP_VHT_FLAG_SGI_NSYM_M10_9 0x08
|
||||
#define IEEE80211_RADIOTAP_VHT_FLAG_LDPC_EXTRA_OFDM_SYM 0x10
|
||||
#define IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED 0x20
|
||||
|
||||
|
||||
/* helpers */
|
||||
static inline int ieee80211_get_radiotap_len(unsigned char *data)
|
||||
{
|
||||
|
|
|
@ -164,7 +164,7 @@ enum ieee80211_chanctx_change {
|
|||
* active on the channel to receive MIMO transmissions
|
||||
* @rx_chains_dynamic: The number of RX chains that must be enabled
|
||||
* after RTS/CTS handshake to receive SMPS MIMO transmissions;
|
||||
* this will always be >= @rx_chains_always.
|
||||
* this will always be >= @rx_chains_static.
|
||||
* @drv_priv: data area for driver use, will always be aligned to
|
||||
* sizeof(void *), size is determined in hw information.
|
||||
*/
|
||||
|
@ -1473,6 +1473,10 @@ enum ieee80211_hw_flags {
|
|||
* include _FMT. Use %IEEE80211_RADIOTAP_MCS_HAVE_* values, only
|
||||
* adding _BW is supported today.
|
||||
*
|
||||
* @radiotap_vht_details: lists which VHT MCS information the HW reports,
|
||||
* the default is _GI | _BANDWIDTH.
|
||||
* Use the %IEEE80211_RADIOTAP_VHT_KNOWN_* values.
|
||||
*
|
||||
* @netdev_features: netdev features to be set in each netdev created
|
||||
* from this HW. Note only HW checksum features are currently
|
||||
* compatible with mac80211. Other feature bits will be rejected.
|
||||
|
@ -1499,6 +1503,7 @@ struct ieee80211_hw {
|
|||
u8 max_tx_aggregation_subframes;
|
||||
u8 offchannel_tx_hw_queue;
|
||||
u8 radiotap_mcs_details;
|
||||
u16 radiotap_vht_details;
|
||||
netdev_features_t netdev_features;
|
||||
};
|
||||
|
||||
|
@ -3139,8 +3144,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
|
|||
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
|
||||
* @ssid: SSID buffer
|
||||
* @ssid_len: length of SSID
|
||||
* @ie: buffer containing all IEs except SSID for the template
|
||||
* @ie_len: length of the IE buffer
|
||||
* @tailroom: tailroom to reserve at end of SKB for IEs
|
||||
*
|
||||
* Creates a Probe Request template which can, for example, be uploaded to
|
||||
* hardware.
|
||||
|
@ -3148,7 +3152,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
|
|||
struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len);
|
||||
size_t tailroom);
|
||||
|
||||
/**
|
||||
* ieee80211_rts_get - RTS frame generation function
|
||||
|
|
|
@ -1303,6 +1303,13 @@ enum nl80211_commands {
|
|||
*
|
||||
* @NL80211_ATTR_SCAN_FLAGS: scan request control flags (u32)
|
||||
*
|
||||
* @NL80211_ATTR_P2P_CTWINDOW: P2P GO Client Traffic Window (u8), used with
|
||||
* the START_AP and SET_BSS commands
|
||||
* @NL80211_ATTR_P2P_OPPPS: P2P GO opportunistic PS (u8), used with the
|
||||
* START_AP and SET_BSS commands. This can have the values 0 or 1;
|
||||
* if not given in START_AP 0 is assumed, if not given in SET_BSS
|
||||
* no change is made.
|
||||
*
|
||||
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||||
* @__NL80211_ATTR_AFTER_LAST: internal use
|
||||
*/
|
||||
|
@ -1570,6 +1577,9 @@ enum nl80211_attrs {
|
|||
NL80211_ATTR_CENTER_FREQ1,
|
||||
NL80211_ATTR_CENTER_FREQ2,
|
||||
|
||||
NL80211_ATTR_P2P_CTWINDOW,
|
||||
NL80211_ATTR_P2P_OPPPS,
|
||||
|
||||
/* add attributes here, update the policy in nl80211.c */
|
||||
|
||||
__NL80211_ATTR_AFTER_LAST,
|
||||
|
@ -3126,6 +3136,10 @@ enum nl80211_ap_sme_features {
|
|||
* @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform
|
||||
* OBSS scans and generate 20/40 BSS coex reports. This flag is used only
|
||||
* for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied.
|
||||
* @NL80211_FEATURE_P2P_GO_CTWIN: P2P GO implementation supports CT Window
|
||||
* setting
|
||||
* @NL80211_FEATURE_P2P_GO_OPPPS: P2P GO implementation supports opportunistic
|
||||
* powersave
|
||||
*/
|
||||
enum nl80211_feature_flags {
|
||||
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
|
||||
|
@ -3139,6 +3153,8 @@ enum nl80211_feature_flags {
|
|||
NL80211_FEATURE_AP_SCAN = 1 << 8,
|
||||
NL80211_FEATURE_VIF_TXPOWER = 1 << 9,
|
||||
NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10,
|
||||
NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11,
|
||||
NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -398,6 +398,38 @@ void sta_set_rate_info_tx(struct sta_info *sta,
|
|||
rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
|
||||
}
|
||||
|
||||
void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
|
||||
{
|
||||
rinfo->flags = 0;
|
||||
|
||||
if (sta->last_rx_rate_flag & RX_FLAG_HT) {
|
||||
rinfo->flags |= RATE_INFO_FLAGS_MCS;
|
||||
rinfo->mcs = sta->last_rx_rate_idx;
|
||||
} else if (sta->last_rx_rate_flag & RX_FLAG_VHT) {
|
||||
rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
|
||||
rinfo->nss = sta->last_rx_rate_vht_nss;
|
||||
rinfo->mcs = sta->last_rx_rate_idx;
|
||||
} else {
|
||||
struct ieee80211_supported_band *sband;
|
||||
|
||||
sband = sta->local->hw.wiphy->bands[
|
||||
ieee80211_get_sdata_band(sta->sdata)];
|
||||
rinfo->legacy =
|
||||
sband->bitrates[sta->last_rx_rate_idx].bitrate;
|
||||
}
|
||||
|
||||
if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
|
||||
rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
|
||||
if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
|
||||
rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
|
||||
if (sta->last_rx_rate_flag & RX_FLAG_80MHZ)
|
||||
rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
|
||||
if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ)
|
||||
rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
|
||||
if (sta->last_rx_rate_flag & RX_FLAG_160MHZ)
|
||||
rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
|
||||
}
|
||||
|
||||
static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
|
@ -444,34 +476,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
|||
}
|
||||
|
||||
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
|
||||
|
||||
sinfo->rxrate.flags = 0;
|
||||
if (sta->last_rx_rate_flag & RX_FLAG_HT) {
|
||||
sinfo->rxrate.flags |= RATE_INFO_FLAGS_MCS;
|
||||
sinfo->rxrate.mcs = sta->last_rx_rate_idx;
|
||||
} else if (sta->last_rx_rate_flag & RX_FLAG_VHT) {
|
||||
sinfo->rxrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
|
||||
sinfo->rxrate.nss = sta->last_rx_rate_vht_nss;
|
||||
sinfo->rxrate.mcs = sta->last_rx_rate_idx;
|
||||
} else {
|
||||
struct ieee80211_supported_band *sband;
|
||||
|
||||
sband = sta->local->hw.wiphy->bands[
|
||||
ieee80211_get_sdata_band(sta->sdata)];
|
||||
sinfo->rxrate.legacy =
|
||||
sband->bitrates[sta->last_rx_rate_idx].bitrate;
|
||||
}
|
||||
|
||||
if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
|
||||
sinfo->rxrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
|
||||
if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
|
||||
sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
|
||||
if (sta->last_rx_rate_flag & RX_FLAG_80MHZ)
|
||||
sinfo->rxrate.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
|
||||
if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ)
|
||||
sinfo->rxrate.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
|
||||
if (sta->last_rx_rate_flag & RX_FLAG_160MHZ)
|
||||
sinfo->rxrate.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
|
||||
sta_set_rate_info_rx(sta, &sinfo->rxrate);
|
||||
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
|
@ -893,7 +898,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
|
|||
u32 changed = BSS_CHANGED_BEACON_INT |
|
||||
BSS_CHANGED_BEACON_ENABLED |
|
||||
BSS_CHANGED_BEACON |
|
||||
BSS_CHANGED_SSID;
|
||||
BSS_CHANGED_SSID |
|
||||
BSS_CHANGED_P2P_PS;
|
||||
int err;
|
||||
|
||||
old = rtnl_dereference(sdata->u.ap.beacon);
|
||||
|
@ -932,6 +938,9 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
|
|||
sdata->vif.bss_conf.hidden_ssid =
|
||||
(params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE);
|
||||
|
||||
sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow;
|
||||
sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps;
|
||||
|
||||
err = ieee80211_assign_beacon(sdata, ¶ms->beacon);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -1807,6 +1816,16 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
|
|||
changed |= BSS_CHANGED_HT;
|
||||
}
|
||||
|
||||
if (params->p2p_ctwindow >= 0) {
|
||||
sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow;
|
||||
changed |= BSS_CHANGED_P2P_PS;
|
||||
}
|
||||
|
||||
if (params->p2p_opp_ps >= 0) {
|
||||
sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps;
|
||||
changed |= BSS_CHANGED_P2P_PS;
|
||||
}
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -53,6 +53,7 @@ static const struct file_operations sta_ ##name## _ops = { \
|
|||
STA_FILE(aid, sta.aid, D);
|
||||
STA_FILE(dev, sdata->name, S);
|
||||
STA_FILE(last_signal, last_signal, D);
|
||||
STA_FILE(last_ack_signal, last_ack_signal, D);
|
||||
|
||||
static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
|
@ -321,6 +322,38 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,
|
|||
}
|
||||
STA_OPS(ht_capa);
|
||||
|
||||
static ssize_t sta_current_tx_rate_read(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct sta_info *sta = file->private_data;
|
||||
struct rate_info rinfo;
|
||||
u16 rate;
|
||||
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo);
|
||||
rate = cfg80211_calculate_bitrate(&rinfo);
|
||||
|
||||
return mac80211_format_buffer(userbuf, count, ppos,
|
||||
"%d.%d MBit/s\n",
|
||||
rate/10, rate%10);
|
||||
}
|
||||
STA_OPS(current_tx_rate);
|
||||
|
||||
static ssize_t sta_last_rx_rate_read(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct sta_info *sta = file->private_data;
|
||||
struct rate_info rinfo;
|
||||
u16 rate;
|
||||
|
||||
sta_set_rate_info_rx(sta, &rinfo);
|
||||
|
||||
rate = cfg80211_calculate_bitrate(&rinfo);
|
||||
|
||||
return mac80211_format_buffer(userbuf, count, ppos,
|
||||
"%d.%d MBit/s\n",
|
||||
rate/10, rate%10);
|
||||
}
|
||||
STA_OPS(last_rx_rate);
|
||||
|
||||
#define DEBUGFS_ADD(name) \
|
||||
debugfs_create_file(#name, 0400, \
|
||||
sta->debugfs.dir, sta, &sta_ ##name## _ops);
|
||||
|
@ -369,6 +402,9 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
|
|||
DEBUGFS_ADD(dev);
|
||||
DEBUGFS_ADD(last_signal);
|
||||
DEBUGFS_ADD(ht_capa);
|
||||
DEBUGFS_ADD(last_ack_signal);
|
||||
DEBUGFS_ADD(current_tx_rate);
|
||||
DEBUGFS_ADD(last_rx_rate);
|
||||
|
||||
DEBUGFS_ADD_COUNTER(rx_packets, rx_packets);
|
||||
DEBUGFS_ADD_COUNTER(tx_packets, tx_packets);
|
||||
|
|
|
@ -371,6 +371,8 @@ enum ieee80211_sta_flags {
|
|||
IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9),
|
||||
IEEE80211_STA_DISABLE_40MHZ = BIT(10),
|
||||
IEEE80211_STA_DISABLE_VHT = BIT(11),
|
||||
IEEE80211_STA_DISABLE_80P80MHZ = BIT(12),
|
||||
IEEE80211_STA_DISABLE_160MHZ = BIT(13),
|
||||
};
|
||||
|
||||
struct ieee80211_mgd_auth_data {
|
||||
|
@ -1032,6 +1034,7 @@ struct ieee80211_local {
|
|||
enum ieee80211_band hw_scan_band;
|
||||
int scan_channel_idx;
|
||||
int scan_ies_len;
|
||||
int hw_scan_ies_bufsize;
|
||||
|
||||
struct work_struct sched_scan_stopped_work;
|
||||
struct ieee80211_sub_if_data __rcu *sched_scan_sdata;
|
||||
|
@ -1573,7 +1576,7 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
|
|||
const u8 *bssid, u16 stype, u16 reason,
|
||||
bool send_frame, u8 *frame_buf);
|
||||
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
||||
const u8 *ie, size_t ie_len,
|
||||
size_t buffer_len, const u8 *ie, size_t ie_len,
|
||||
enum ieee80211_band band, u32 rate_mask,
|
||||
u8 channel);
|
||||
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
|
|
|
@ -223,6 +223,47 @@ static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_verify_mac(struct ieee80211_local *local, u8 *addr)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
u64 new, mask, tmp;
|
||||
u8 *m;
|
||||
int ret = 0;
|
||||
|
||||
if (is_zero_ether_addr(local->hw.wiphy->addr_mask))
|
||||
return 0;
|
||||
|
||||
m = addr;
|
||||
new = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
|
||||
((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
|
||||
((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
|
||||
|
||||
m = local->hw.wiphy->addr_mask;
|
||||
mask = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
|
||||
((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
|
||||
((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
|
||||
|
||||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
|
||||
continue;
|
||||
|
||||
m = sdata->vif.addr;
|
||||
tmp = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
|
||||
((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
|
||||
((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
|
||||
|
||||
if ((new & ~mask) != (tmp & ~mask)) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ieee80211_change_mac(struct net_device *dev, void *addr)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
@ -232,6 +273,10 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr)
|
|||
if (ieee80211_sdata_running(sdata))
|
||||
return -EBUSY;
|
||||
|
||||
ret = ieee80211_verify_mac(sdata->local, sa->sa_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = eth_mac_addr(dev, sa);
|
||||
|
||||
if (ret == 0)
|
||||
|
|
|
@ -474,7 +474,8 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
|
|||
.tx = 0xffff,
|
||||
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
|
||||
BIT(IEEE80211_STYPE_AUTH >> 4) |
|
||||
BIT(IEEE80211_STYPE_DEAUTH >> 4),
|
||||
BIT(IEEE80211_STYPE_DEAUTH >> 4) |
|
||||
BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
|
||||
},
|
||||
[NL80211_IFTYPE_STATION] = {
|
||||
.tx = 0xffff,
|
||||
|
@ -638,6 +639,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
|||
local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
|
||||
IEEE80211_RADIOTAP_MCS_HAVE_GI |
|
||||
IEEE80211_RADIOTAP_MCS_HAVE_BW;
|
||||
local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |
|
||||
IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
|
||||
local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
|
||||
wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
|
||||
|
||||
|
|
|
@ -391,7 +391,8 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
|
|||
sta->ch_width = chandef.width;
|
||||
}
|
||||
|
||||
rate_control_rate_init(sta);
|
||||
if (insert)
|
||||
rate_control_rate_init(sta);
|
||||
spin_unlock_bh(&sta->lock);
|
||||
|
||||
if (insert && sta_info_insert(sta))
|
||||
|
|
|
@ -195,11 +195,15 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
|
|||
ifmsh->sync_offset_clockdrift_max);
|
||||
set_bit(MESH_WORK_DRIFT_ADJUST,
|
||||
&ifmsh->wrkq_flags);
|
||||
|
||||
ifmsh->adjusting_tbtt = true;
|
||||
} else {
|
||||
msync_dbg(sdata,
|
||||
"TBTT : max clockdrift=%lld; too small to adjust\n",
|
||||
(long long)ifmsh->sync_offset_clockdrift_max);
|
||||
ifmsh->sync_offset_clockdrift_max = 0;
|
||||
|
||||
ifmsh->adjusting_tbtt = false;
|
||||
}
|
||||
spin_unlock_bh(&ifmsh->sync_offset_lock);
|
||||
}
|
||||
|
|
|
@ -354,6 +354,16 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
|
|||
/* determine capability flags */
|
||||
cap = vht_cap.cap;
|
||||
|
||||
if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) {
|
||||
cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
|
||||
cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
|
||||
}
|
||||
|
||||
if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) {
|
||||
cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160;
|
||||
cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
|
||||
}
|
||||
|
||||
/* reserve and fill IE */
|
||||
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
|
||||
ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
|
||||
|
@ -543,6 +553,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|
|||
offset = noffset;
|
||||
}
|
||||
|
||||
if (WARN_ON_ONCE((ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
|
||||
!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)))
|
||||
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
|
||||
|
||||
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
|
||||
ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param,
|
||||
sband, chan, sdata->smps_mode);
|
||||
|
@ -775,6 +789,7 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
|||
"not handling channel switch with channel contexts\n");
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&sdata->local->chanctx_mtx);
|
||||
|
@ -1368,19 +1383,26 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
|
|||
sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
|
||||
|
||||
if (sdata->vif.p2p) {
|
||||
u8 noa[2];
|
||||
int ret;
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
|
||||
ret = cfg80211_get_p2p_attr(cbss->information_elements,
|
||||
cbss->len_information_elements,
|
||||
IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
|
||||
noa, sizeof(noa));
|
||||
if (ret >= 2) {
|
||||
bss_conf->p2p_oppps = noa[1] & 0x80;
|
||||
bss_conf->p2p_ctwindow = noa[1] & 0x7f;
|
||||
bss_info_changed |= BSS_CHANGED_P2P_PS;
|
||||
sdata->u.mgd.p2p_noa_index = noa[0];
|
||||
rcu_read_lock();
|
||||
ies = rcu_dereference(cbss->ies);
|
||||
if (ies) {
|
||||
u8 noa[2];
|
||||
int ret;
|
||||
|
||||
ret = cfg80211_get_p2p_attr(
|
||||
ies->data, ies->len,
|
||||
IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
|
||||
noa, sizeof(noa));
|
||||
if (ret >= 2) {
|
||||
bss_conf->p2p_oppps = noa[1] & 0x80;
|
||||
bss_conf->p2p_ctwindow = noa[1] & 0x7f;
|
||||
bss_info_changed |= BSS_CHANGED_P2P_PS;
|
||||
sdata->u.mgd.p2p_noa_index = noa[0];
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/* just to be sure */
|
||||
|
@ -1645,6 +1667,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
|
|||
} else {
|
||||
int ssid_len;
|
||||
|
||||
rcu_read_lock();
|
||||
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
|
||||
if (WARN_ON_ONCE(ssid == NULL))
|
||||
ssid_len = 0;
|
||||
|
@ -1654,6 +1677,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
|
|||
ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
|
||||
0, (u32) -1, true, false,
|
||||
ifmgd->associated->channel, false);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
|
||||
|
@ -1749,6 +1773,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
|
|||
else
|
||||
return NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID);
|
||||
if (WARN_ON_ONCE(ssid == NULL))
|
||||
ssid_len = 0;
|
||||
|
@ -1759,6 +1784,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
|
|||
(u32) -1, cbss->channel,
|
||||
ssid + 2, ssid_len,
|
||||
NULL, 0, true);
|
||||
rcu_read_unlock();
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
@ -2844,9 +2870,12 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
|
|||
auth_data->bss->bssid, auth_data->tries,
|
||||
IEEE80211_AUTH_MAX_TRIES);
|
||||
|
||||
rcu_read_lock();
|
||||
ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID);
|
||||
if (!ssidie)
|
||||
if (!ssidie) {
|
||||
rcu_read_unlock();
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* Direct probe is sent to broadcast address as some APs
|
||||
* will not answer to direct packet in unassociated state.
|
||||
|
@ -2854,6 +2883,7 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
|
|||
ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1],
|
||||
NULL, 0, (u32) -1, true, false,
|
||||
auth_data->bss->channel, false);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
|
||||
|
@ -3183,106 +3213,313 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u32 chandef_downgrade(struct cfg80211_chan_def *c)
|
||||
{
|
||||
u32 ret;
|
||||
int tmp;
|
||||
|
||||
switch (c->width) {
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
c->width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
c->width = NL80211_CHAN_WIDTH_20;
|
||||
c->center_freq1 = c->chan->center_freq;
|
||||
ret = IEEE80211_STA_DISABLE_40MHZ |
|
||||
IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
|
||||
/* n_P40 */
|
||||
tmp /= 2;
|
||||
/* freq_P40 */
|
||||
c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
|
||||
c->width = NL80211_CHAN_WIDTH_40;
|
||||
ret = IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
c->center_freq2 = 0;
|
||||
c->width = NL80211_CHAN_WIDTH_80;
|
||||
ret = IEEE80211_STA_DISABLE_80P80MHZ |
|
||||
IEEE80211_STA_DISABLE_160MHZ;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
/* n_P20 */
|
||||
tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
|
||||
/* n_P80 */
|
||||
tmp /= 4;
|
||||
c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
|
||||
c->width = NL80211_CHAN_WIDTH_80;
|
||||
ret = IEEE80211_STA_DISABLE_80P80MHZ |
|
||||
IEEE80211_STA_DISABLE_160MHZ;
|
||||
break;
|
||||
default:
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
WARN_ON_ONCE(1);
|
||||
c->width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
}
|
||||
|
||||
WARN_ON_ONCE(!cfg80211_chandef_valid(c));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32
|
||||
ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_channel *channel,
|
||||
const struct ieee80211_ht_operation *ht_oper,
|
||||
const struct ieee80211_vht_operation *vht_oper,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct cfg80211_chan_def vht_chandef;
|
||||
u32 ht_cfreq, ret;
|
||||
|
||||
chandef->chan = channel;
|
||||
chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
chandef->center_freq1 = channel->center_freq;
|
||||
chandef->center_freq2 = 0;
|
||||
|
||||
if (!ht_oper || !sband->ht_cap.ht_supported) {
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
chandef->width = NL80211_CHAN_WIDTH_20;
|
||||
|
||||
ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
|
||||
channel->band);
|
||||
/* check that channel matches the right operating channel */
|
||||
if (channel->center_freq != ht_cfreq) {
|
||||
/*
|
||||
* It's possible that some APs are confused here;
|
||||
* Netgear WNDR3700 sometimes reports 4 higher than
|
||||
* the actual channel in association responses, but
|
||||
* since we look at probe response/beacon data here
|
||||
* it should be OK.
|
||||
*/
|
||||
sdata_info(sdata,
|
||||
"Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
|
||||
channel->center_freq, ht_cfreq,
|
||||
ht_oper->primary_chan, channel->band);
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* check 40 MHz support, if we have it */
|
||||
if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
|
||||
switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||
chandef->width = NL80211_CHAN_WIDTH_40;
|
||||
chandef->center_freq1 += 10;
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||
chandef->width = NL80211_CHAN_WIDTH_40;
|
||||
chandef->center_freq1 -= 10;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* 40 MHz (and 80 MHz) must be supported for VHT */
|
||||
ret = IEEE80211_STA_DISABLE_VHT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!vht_oper || !sband->vht_cap.vht_supported) {
|
||||
ret = IEEE80211_STA_DISABLE_VHT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
vht_chandef.chan = channel;
|
||||
vht_chandef.center_freq1 =
|
||||
ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx,
|
||||
channel->band);
|
||||
vht_chandef.center_freq2 = 0;
|
||||
|
||||
if (vht_oper->center_freq_seg2_idx)
|
||||
vht_chandef.center_freq2 =
|
||||
ieee80211_channel_to_frequency(
|
||||
vht_oper->center_freq_seg2_idx,
|
||||
channel->band);
|
||||
|
||||
switch (vht_oper->chan_width) {
|
||||
case IEEE80211_VHT_CHANWIDTH_USE_HT:
|
||||
vht_chandef.width = chandef->width;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_80MHZ:
|
||||
vht_chandef.width = NL80211_CHAN_WIDTH_80;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_160MHZ:
|
||||
vht_chandef.width = NL80211_CHAN_WIDTH_160;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
|
||||
vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
|
||||
break;
|
||||
default:
|
||||
sdata_info(sdata,
|
||||
"AP VHT operation IE has invalid channel width (%d), disable VHT\n",
|
||||
vht_oper->chan_width);
|
||||
ret = IEEE80211_STA_DISABLE_VHT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!cfg80211_chandef_valid(&vht_chandef)) {
|
||||
sdata_info(sdata,
|
||||
"AP VHT information is invalid, disable VHT\n");
|
||||
ret = IEEE80211_STA_DISABLE_VHT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cfg80211_chandef_identical(chandef, &vht_chandef)) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) {
|
||||
sdata_info(sdata,
|
||||
"AP VHT information doesn't match HT, disable VHT\n");
|
||||
ret = IEEE80211_STA_DISABLE_VHT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*chandef = vht_chandef;
|
||||
|
||||
ret = 0;
|
||||
|
||||
while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
|
||||
IEEE80211_CHAN_DISABLED)) {
|
||||
if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) {
|
||||
ret = IEEE80211_STA_DISABLE_HT |
|
||||
IEEE80211_STA_DISABLE_VHT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = chandef_downgrade(chandef);
|
||||
}
|
||||
|
||||
if (chandef->width != vht_chandef.width)
|
||||
sdata_info(sdata,
|
||||
"local regulatory prevented using AP HT/VHT configuration, downgraded\n");
|
||||
|
||||
out:
|
||||
WARN_ON_ONCE(!cfg80211_chandef_valid(chandef));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_bss *cbss)
|
||||
{
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
const u8 *ht_cap_ie, *vht_cap_ie;
|
||||
const struct ieee80211_ht_cap *ht_cap;
|
||||
const struct ieee80211_vht_cap *vht_cap;
|
||||
u8 chains = 1;
|
||||
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_HT)
|
||||
return chains;
|
||||
|
||||
ht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY);
|
||||
if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) {
|
||||
ht_cap = (void *)(ht_cap_ie + 2);
|
||||
chains = ieee80211_mcs_to_chains(&ht_cap->mcs);
|
||||
/*
|
||||
* TODO: use "Tx Maximum Number Spatial Streams Supported" and
|
||||
* "Tx Unequal Modulation Supported" fields.
|
||||
*/
|
||||
}
|
||||
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
|
||||
return chains;
|
||||
|
||||
vht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_CAPABILITY);
|
||||
if (vht_cap_ie && vht_cap_ie[1] >= sizeof(*vht_cap)) {
|
||||
u8 nss;
|
||||
u16 tx_mcs_map;
|
||||
|
||||
vht_cap = (void *)(vht_cap_ie + 2);
|
||||
tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
|
||||
for (nss = 8; nss > 0; nss--) {
|
||||
if (((tx_mcs_map >> (2 * (nss - 1))) & 3) !=
|
||||
IEEE80211_VHT_MCS_NOT_SUPPORTED)
|
||||
break;
|
||||
}
|
||||
/* TODO: use "Tx Highest Supported Long GI Data Rate" field? */
|
||||
chains = max(chains, nss);
|
||||
}
|
||||
|
||||
return chains;
|
||||
}
|
||||
|
||||
static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_bss *cbss)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
int ht_cfreq;
|
||||
enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
|
||||
const u8 *ht_oper_ie;
|
||||
const struct ieee80211_ht_operation *ht_oper = NULL;
|
||||
const struct ieee80211_vht_operation *vht_oper = NULL;
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct cfg80211_chan_def chandef;
|
||||
int ret;
|
||||
|
||||
sband = local->hw.wiphy->bands[cbss->channel->band];
|
||||
|
||||
ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ;
|
||||
ifmgd->flags &= ~(IEEE80211_STA_DISABLE_40MHZ |
|
||||
IEEE80211_STA_DISABLE_80P80MHZ |
|
||||
IEEE80211_STA_DISABLE_160MHZ);
|
||||
|
||||
if (sband->ht_cap.ht_supported) {
|
||||
ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION,
|
||||
cbss->information_elements,
|
||||
cbss->len_information_elements);
|
||||
rcu_read_lock();
|
||||
|
||||
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
|
||||
sband->ht_cap.ht_supported) {
|
||||
const u8 *ht_oper_ie;
|
||||
|
||||
ht_oper_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_OPERATION);
|
||||
if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper))
|
||||
ht_oper = (void *)(ht_oper_ie + 2);
|
||||
}
|
||||
|
||||
if (ht_oper) {
|
||||
ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
|
||||
cbss->channel->band);
|
||||
/* check that channel matches the right operating channel */
|
||||
if (cbss->channel->center_freq != ht_cfreq) {
|
||||
/*
|
||||
* It's possible that some APs are confused here;
|
||||
* Netgear WNDR3700 sometimes reports 4 higher than
|
||||
* the actual channel in association responses, but
|
||||
* since we look at probe response/beacon data here
|
||||
* it should be OK.
|
||||
*/
|
||||
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
|
||||
sband->vht_cap.vht_supported) {
|
||||
const u8 *vht_oper_ie;
|
||||
|
||||
vht_oper_ie = ieee80211_bss_get_ie(cbss,
|
||||
WLAN_EID_VHT_OPERATION);
|
||||
if (vht_oper_ie && vht_oper_ie[1] >= sizeof(*vht_oper))
|
||||
vht_oper = (void *)(vht_oper_ie + 2);
|
||||
if (vht_oper && !ht_oper) {
|
||||
vht_oper = NULL;
|
||||
sdata_info(sdata,
|
||||
"Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
|
||||
cbss->channel->center_freq,
|
||||
ht_cfreq, ht_oper->primary_chan,
|
||||
cbss->channel->band);
|
||||
ht_oper = NULL;
|
||||
"AP advertised VHT without HT, disabling both\n");
|
||||
sdata->flags |= IEEE80211_STA_DISABLE_HT;
|
||||
sdata->flags |= IEEE80211_STA_DISABLE_VHT;
|
||||
}
|
||||
}
|
||||
|
||||
if (ht_oper) {
|
||||
/*
|
||||
* cfg80211 already verified that the channel itself can
|
||||
* be used, but it didn't check that we can do the right
|
||||
* HT type, so do that here as well. If HT40 isn't allowed
|
||||
* on this channel, disable 40 MHz operation.
|
||||
*/
|
||||
const u8 *ht_cap_ie;
|
||||
const struct ieee80211_ht_cap *ht_cap;
|
||||
u8 chains = 1;
|
||||
ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
|
||||
cbss->channel,
|
||||
ht_oper, vht_oper,
|
||||
&chandef);
|
||||
|
||||
channel_type = NL80211_CHAN_HT20;
|
||||
sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
|
||||
local->rx_chains);
|
||||
|
||||
if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
|
||||
switch (ht_oper->ht_param &
|
||||
IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||
if (cbss->channel->flags &
|
||||
IEEE80211_CHAN_NO_HT40PLUS)
|
||||
ifmgd->flags |=
|
||||
IEEE80211_STA_DISABLE_40MHZ;
|
||||
else
|
||||
channel_type = NL80211_CHAN_HT40PLUS;
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||
if (cbss->channel->flags &
|
||||
IEEE80211_CHAN_NO_HT40MINUS)
|
||||
ifmgd->flags |=
|
||||
IEEE80211_STA_DISABLE_40MHZ;
|
||||
else
|
||||
channel_type = NL80211_CHAN_HT40MINUS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY,
|
||||
cbss->information_elements,
|
||||
cbss->len_information_elements);
|
||||
if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) {
|
||||
ht_cap = (void *)(ht_cap_ie + 2);
|
||||
chains = ieee80211_mcs_to_chains(&ht_cap->mcs);
|
||||
}
|
||||
sdata->needed_rx_chains = min(chains, local->rx_chains);
|
||||
} else {
|
||||
sdata->needed_rx_chains = 1;
|
||||
sdata->u.mgd.flags |= IEEE80211_STA_DISABLE_HT;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
/* will change later if needed */
|
||||
sdata->smps_mode = IEEE80211_SMPS_OFF;
|
||||
|
||||
ieee80211_vif_release_channel(sdata);
|
||||
cfg80211_chandef_create(&chandef, cbss->channel, channel_type);
|
||||
return ieee80211_vif_use_channel(sdata, &chandef,
|
||||
IEEE80211_CHANCTX_SHARED);
|
||||
/*
|
||||
* If this fails (possibly due to channel context sharing
|
||||
* on incompatible channels, e.g. 80+80 and 160 sharing the
|
||||
* same control channel) try to use a smaller bandwidth.
|
||||
*/
|
||||
ret = ieee80211_vif_use_channel(sdata, &chandef,
|
||||
IEEE80211_CHANCTX_SHARED);
|
||||
while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
|
||||
ifmgd->flags |= chandef_downgrade(&chandef);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
|
||||
|
@ -3510,14 +3747,21 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
|||
const u8 *ssidie, *ht_ie;
|
||||
int i, err;
|
||||
|
||||
ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
|
||||
if (!ssidie)
|
||||
return -EINVAL;
|
||||
|
||||
assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL);
|
||||
if (!assoc_data)
|
||||
return -ENOMEM;
|
||||
|
||||
rcu_read_lock();
|
||||
ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
|
||||
if (!ssidie) {
|
||||
rcu_read_unlock();
|
||||
kfree(assoc_data);
|
||||
return -EINVAL;
|
||||
}
|
||||
memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]);
|
||||
assoc_data->ssid_len = ssidie[1];
|
||||
rcu_read_unlock();
|
||||
|
||||
mutex_lock(&ifmgd->mtx);
|
||||
|
||||
if (ifmgd->associated)
|
||||
|
@ -3612,12 +3856,14 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
|||
assoc_data->supp_rates = bss->supp_rates;
|
||||
assoc_data->supp_rates_len = bss->supp_rates_len;
|
||||
|
||||
rcu_read_lock();
|
||||
ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION);
|
||||
if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation))
|
||||
assoc_data->ap_ht_param =
|
||||
((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param;
|
||||
else
|
||||
ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
|
||||
rcu_read_unlock();
|
||||
|
||||
if (bss->wmm_used && bss->uapsd_supported &&
|
||||
(sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
|
||||
|
@ -3628,9 +3874,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
|||
ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED;
|
||||
}
|
||||
|
||||
memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]);
|
||||
assoc_data->ssid_len = ssidie[1];
|
||||
|
||||
if (req->prev_bssid)
|
||||
memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN);
|
||||
|
||||
|
|
|
@ -389,9 +389,9 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
|
|||
struct ieee80211_tx_rate *ar = info->status.rates;
|
||||
struct minstrel_rate_stats *rate, *rate2;
|
||||
struct minstrel_priv *mp = priv;
|
||||
bool last = false;
|
||||
bool last;
|
||||
int group;
|
||||
int i = 0;
|
||||
int i;
|
||||
|
||||
if (!msp->is_ht)
|
||||
return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb);
|
||||
|
@ -419,13 +419,11 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
|
|||
if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
|
||||
mi->sample_packets += info->status.ampdu_len;
|
||||
|
||||
last = !minstrel_ht_txstat_valid(&ar[0]);
|
||||
for (i = 0; !last; i++) {
|
||||
last = (i == IEEE80211_TX_MAX_RATES - 1) ||
|
||||
!minstrel_ht_txstat_valid(&ar[i + 1]);
|
||||
|
||||
if (!minstrel_ht_txstat_valid(&ar[i]))
|
||||
break;
|
||||
|
||||
group = minstrel_ht_get_group_idx(&ar[i]);
|
||||
rate = &mi->groups[group].rates[ar[i].idx % 8];
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
|
|||
/* driver bug */
|
||||
WARN_ON(1);
|
||||
dev_kfree_skb(skb);
|
||||
skb = NULL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,6 +111,11 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
|
|||
len += 8;
|
||||
}
|
||||
|
||||
if (status->flag & RX_FLAG_VHT) {
|
||||
len = ALIGN(len, 2);
|
||||
len += 12;
|
||||
}
|
||||
|
||||
if (status->vendor_radiotap_len) {
|
||||
if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
|
||||
status->vendor_radiotap_align = 1;
|
||||
|
@ -297,6 +302,41 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
|
|||
*pos++ = 0;
|
||||
}
|
||||
|
||||
if (status->flag & RX_FLAG_VHT) {
|
||||
u16 known = local->hw.radiotap_vht_details;
|
||||
|
||||
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
|
||||
/* known field - how to handle 80+80? */
|
||||
if (status->flag & RX_FLAG_80P80MHZ)
|
||||
known &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
|
||||
put_unaligned_le16(known, pos);
|
||||
pos += 2;
|
||||
/* flags */
|
||||
if (status->flag & RX_FLAG_SHORT_GI)
|
||||
*pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
|
||||
pos++;
|
||||
/* bandwidth */
|
||||
if (status->flag & RX_FLAG_80MHZ)
|
||||
*pos++ = 4;
|
||||
else if (status->flag & RX_FLAG_80P80MHZ)
|
||||
*pos++ = 0; /* marked not known above */
|
||||
else if (status->flag & RX_FLAG_160MHZ)
|
||||
*pos++ = 11;
|
||||
else if (status->flag & RX_FLAG_40MHZ)
|
||||
*pos++ = 1;
|
||||
else /* 20 MHz */
|
||||
*pos++ = 0;
|
||||
/* MCS/NSS */
|
||||
*pos = (status->rate_idx << 4) | status->vht_nss;
|
||||
pos += 4;
|
||||
/* coding field */
|
||||
pos++;
|
||||
/* group ID */
|
||||
pos++;
|
||||
/* partial_aid */
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
if (status->vendor_radiotap_len) {
|
||||
/* ensure 2 byte alignment for the vendor field as required */
|
||||
if ((pos - (u8 *)rthdr) & 1)
|
||||
|
|
|
@ -247,6 +247,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
|
|||
local->hw_scan_req->n_channels = n_chans;
|
||||
|
||||
ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
|
||||
local->hw_scan_ies_bufsize,
|
||||
req->ie, req->ie_len, band,
|
||||
req->rates[band], 0);
|
||||
local->hw_scan_req->ie_len = ielen;
|
||||
|
@ -445,11 +446,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
|||
if (local->ops->hw_scan) {
|
||||
u8 *ies;
|
||||
|
||||
local->hw_scan_ies_bufsize = 2 + IEEE80211_MAX_SSID_LEN +
|
||||
local->scan_ies_len +
|
||||
req->ie_len;
|
||||
local->hw_scan_req = kmalloc(
|
||||
sizeof(*local->hw_scan_req) +
|
||||
req->n_channels * sizeof(req->channels[0]) +
|
||||
2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len +
|
||||
req->ie_len, GFP_KERNEL);
|
||||
local->hw_scan_ies_bufsize, GFP_KERNEL);
|
||||
if (!local->hw_scan_req)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -928,7 +931,10 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
|
|||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_sched_scan_ies sched_scan_ies = {};
|
||||
int ret, i;
|
||||
int ret, i, iebufsz;
|
||||
|
||||
iebufsz = 2 + IEEE80211_MAX_SSID_LEN +
|
||||
local->scan_ies_len + req->ie_len;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
|
||||
|
@ -946,10 +952,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
|
|||
if (!local->hw.wiphy->bands[i])
|
||||
continue;
|
||||
|
||||
sched_scan_ies.ie[i] = kzalloc(2 + IEEE80211_MAX_SSID_LEN +
|
||||
local->scan_ies_len +
|
||||
req->ie_len,
|
||||
GFP_KERNEL);
|
||||
sched_scan_ies.ie[i] = kzalloc(iebufsz, GFP_KERNEL);
|
||||
if (!sched_scan_ies.ie[i]) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free;
|
||||
|
@ -957,8 +960,8 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
sched_scan_ies.len[i] =
|
||||
ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
|
||||
req->ie, req->ie_len, i,
|
||||
(u32) -1, 0);
|
||||
iebufsz, req->ie, req->ie_len,
|
||||
i, (u32) -1, 0);
|
||||
}
|
||||
|
||||
ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
|
||||
|
|
|
@ -250,6 +250,7 @@ struct sta_ampdu_mlme {
|
|||
* @rx_dropped: number of dropped MPDUs from this STA
|
||||
* @last_signal: signal of last received frame from this STA
|
||||
* @avg_signal: moving average of signal of received frames from this STA
|
||||
* @last_ack_signal: signal of last received Ack frame from this STA
|
||||
* @last_seq_ctrl: last received seq/frag number from this STA (per RX queue)
|
||||
* @tx_filtered_count: number of frames the hardware filtered for this STA
|
||||
* @tx_retry_failed: number of frames that failed retry
|
||||
|
@ -329,6 +330,7 @@ struct sta_info {
|
|||
unsigned long rx_dropped;
|
||||
int last_signal;
|
||||
struct ewma avg_signal;
|
||||
int last_ack_signal;
|
||||
/* Plus 1 for non-QoS frames */
|
||||
__le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];
|
||||
|
||||
|
@ -552,6 +554,8 @@ int sta_info_flush(struct ieee80211_local *local,
|
|||
void sta_set_rate_info_tx(struct sta_info *sta,
|
||||
const struct ieee80211_tx_rate *rate,
|
||||
struct rate_info *rinfo);
|
||||
void sta_set_rate_info_rx(struct sta_info *sta,
|
||||
struct rate_info *rinfo);
|
||||
void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
|
||||
unsigned long exp_time);
|
||||
|
||||
|
|
|
@ -539,6 +539,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
|||
sta->lost_packets = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (acked)
|
||||
sta->last_ack_signal = info->status.ack_signal;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
|
|
@ -2623,7 +2623,7 @@ EXPORT_SYMBOL(ieee80211_nullfunc_get);
|
|||
struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len)
|
||||
size_t tailroom)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct ieee80211_local *local;
|
||||
|
@ -2637,7 +2637,7 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
|
|||
ie_ssid_len = 2 + ssid_len;
|
||||
|
||||
skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) +
|
||||
ie_ssid_len + ie_len);
|
||||
ie_ssid_len + tailroom);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
|
@ -2658,11 +2658,6 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
|
|||
memcpy(pos, ssid, ssid_len);
|
||||
pos += ssid_len;
|
||||
|
||||
if (ie) {
|
||||
pos = skb_put(skb, ie_len);
|
||||
memcpy(pos, ie, ie_len);
|
||||
}
|
||||
|
||||
return skb;
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_probereq_get);
|
||||
|
|
|
@ -1107,12 +1107,12 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
|
|||
}
|
||||
|
||||
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
||||
const u8 *ie, size_t ie_len,
|
||||
size_t buffer_len, const u8 *ie, size_t ie_len,
|
||||
enum ieee80211_band band, u32 rate_mask,
|
||||
u8 channel)
|
||||
{
|
||||
struct ieee80211_supported_band *sband;
|
||||
u8 *pos;
|
||||
u8 *pos = buffer, *end = buffer + buffer_len;
|
||||
size_t offset = 0, noffset;
|
||||
int supp_rates_len, i;
|
||||
u8 rates[32];
|
||||
|
@ -1123,8 +1123,6 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
|||
if (WARN_ON_ONCE(!sband))
|
||||
return 0;
|
||||
|
||||
pos = buffer;
|
||||
|
||||
num_rates = 0;
|
||||
for (i = 0; i < sband->n_bitrates; i++) {
|
||||
if ((BIT(i) & rate_mask) == 0)
|
||||
|
@ -1134,6 +1132,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
|||
|
||||
supp_rates_len = min_t(int, num_rates, 8);
|
||||
|
||||
if (end - pos < 2 + supp_rates_len)
|
||||
goto out_err;
|
||||
*pos++ = WLAN_EID_SUPP_RATES;
|
||||
*pos++ = supp_rates_len;
|
||||
memcpy(pos, rates, supp_rates_len);
|
||||
|
@ -1150,6 +1150,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
|||
before_extrates,
|
||||
ARRAY_SIZE(before_extrates),
|
||||
offset);
|
||||
if (end - pos < noffset - offset)
|
||||
goto out_err;
|
||||
memcpy(pos, ie + offset, noffset - offset);
|
||||
pos += noffset - offset;
|
||||
offset = noffset;
|
||||
|
@ -1157,6 +1159,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
|||
|
||||
ext_rates_len = num_rates - supp_rates_len;
|
||||
if (ext_rates_len > 0) {
|
||||
if (end - pos < 2 + ext_rates_len)
|
||||
goto out_err;
|
||||
*pos++ = WLAN_EID_EXT_SUPP_RATES;
|
||||
*pos++ = ext_rates_len;
|
||||
memcpy(pos, rates + supp_rates_len, ext_rates_len);
|
||||
|
@ -1164,6 +1168,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
|||
}
|
||||
|
||||
if (channel && sband->band == IEEE80211_BAND_2GHZ) {
|
||||
if (end - pos < 3)
|
||||
goto out_err;
|
||||
*pos++ = WLAN_EID_DS_PARAMS;
|
||||
*pos++ = 1;
|
||||
*pos++ = channel;
|
||||
|
@ -1182,14 +1188,19 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
|||
noffset = ieee80211_ie_split(ie, ie_len,
|
||||
before_ht, ARRAY_SIZE(before_ht),
|
||||
offset);
|
||||
if (end - pos < noffset - offset)
|
||||
goto out_err;
|
||||
memcpy(pos, ie + offset, noffset - offset);
|
||||
pos += noffset - offset;
|
||||
offset = noffset;
|
||||
}
|
||||
|
||||
if (sband->ht_cap.ht_supported)
|
||||
if (sband->ht_cap.ht_supported) {
|
||||
if (end - pos < 2 + sizeof(struct ieee80211_ht_cap))
|
||||
goto out_err;
|
||||
pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
|
||||
sband->ht_cap.cap);
|
||||
}
|
||||
|
||||
/*
|
||||
* If adding more here, adjust code in main.c
|
||||
|
@ -1199,14 +1210,22 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
|||
/* add any remaining custom IEs */
|
||||
if (ie && ie_len) {
|
||||
noffset = ie_len;
|
||||
if (end - pos < noffset - offset)
|
||||
goto out_err;
|
||||
memcpy(pos, ie + offset, noffset - offset);
|
||||
pos += noffset - offset;
|
||||
}
|
||||
|
||||
if (sband->vht_cap.vht_supported)
|
||||
if (sband->vht_cap.vht_supported) {
|
||||
if (end - pos < 2 + sizeof(struct ieee80211_vht_cap))
|
||||
goto out_err;
|
||||
pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
|
||||
sband->vht_cap.cap);
|
||||
}
|
||||
|
||||
return pos - buffer;
|
||||
out_err:
|
||||
WARN_ONCE(1, "not enough space for preq IEs\n");
|
||||
return pos - buffer;
|
||||
}
|
||||
|
||||
|
@ -1220,14 +1239,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
|
|||
struct ieee80211_local *local = sdata->local;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
size_t buf_len;
|
||||
u8 *buf;
|
||||
u8 chan_no;
|
||||
|
||||
/* FIXME: come up with a proper value */
|
||||
buf = kmalloc(200 + ie_len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
int ies_len;
|
||||
|
||||
/*
|
||||
* Do not send DS Channel parameter for directed probe requests
|
||||
|
@ -1239,14 +1252,16 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
|
|||
else
|
||||
chan_no = ieee80211_frequency_to_channel(chan->center_freq);
|
||||
|
||||
buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, chan->band,
|
||||
ratemask, chan_no);
|
||||
|
||||
skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
|
||||
ssid, ssid_len,
|
||||
buf, buf_len);
|
||||
ssid, ssid_len, 100 + ie_len);
|
||||
if (!skb)
|
||||
goto out;
|
||||
return NULL;
|
||||
|
||||
ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
|
||||
skb_tailroom(skb),
|
||||
ie, ie_len, chan->band,
|
||||
ratemask, chan_no);
|
||||
skb_put(skb, ies_len);
|
||||
|
||||
if (dst) {
|
||||
mgmt = (struct ieee80211_mgmt *) skb->data;
|
||||
|
@ -1256,9 +1271,6 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
||||
|
||||
out:
|
||||
kfree(buf);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
|
@ -1527,7 +1539,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
|||
changed |= BSS_CHANGED_IBSS;
|
||||
/* fall through */
|
||||
case NL80211_IFTYPE_AP:
|
||||
changed |= BSS_CHANGED_SSID;
|
||||
changed |= BSS_CHANGED_SSID | BSS_CHANGED_P2P_PS;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP) {
|
||||
changed |= BSS_CHANGED_AP_PROBE_RESP;
|
||||
|
|
|
@ -44,7 +44,7 @@ void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
|
|||
}
|
||||
EXPORT_SYMBOL(cfg80211_chandef_create);
|
||||
|
||||
bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef)
|
||||
bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
u32 control_freq;
|
||||
|
||||
|
@ -105,6 +105,7 @@ bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef)
|
|||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_chandef_valid);
|
||||
|
||||
static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
|
||||
int *pri40, int *pri80)
|
||||
|
@ -187,9 +188,9 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
|
|||
}
|
||||
EXPORT_SYMBOL(cfg80211_chandef_compatible);
|
||||
|
||||
bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
|
||||
u32 center_freq, u32 bandwidth,
|
||||
u32 prohibited_flags)
|
||||
static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
|
||||
u32 center_freq, u32 bandwidth,
|
||||
u32 prohibited_flags)
|
||||
{
|
||||
struct ieee80211_channel *c;
|
||||
u32 freq;
|
||||
|
@ -205,55 +206,88 @@ bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool cfg80211_check_beacon_chans(struct wiphy *wiphy,
|
||||
u32 center_freq, u32 bw)
|
||||
bool cfg80211_chandef_usable(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
u32 prohibited_flags)
|
||||
{
|
||||
return cfg80211_secondary_chans_ok(wiphy, center_freq, bw,
|
||||
IEEE80211_CHAN_DISABLED |
|
||||
IEEE80211_CHAN_PASSIVE_SCAN |
|
||||
IEEE80211_CHAN_NO_IBSS |
|
||||
IEEE80211_CHAN_RADAR);
|
||||
}
|
||||
struct ieee80211_sta_ht_cap *ht_cap;
|
||||
struct ieee80211_sta_vht_cap *vht_cap;
|
||||
u32 width, control_freq;
|
||||
|
||||
bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
u32 width;
|
||||
bool res;
|
||||
|
||||
trace_cfg80211_reg_can_beacon(wiphy, chandef);
|
||||
|
||||
if (WARN_ON(!cfg80211_chan_def_valid(chandef))) {
|
||||
trace_cfg80211_return_bool(false);
|
||||
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
||||
return false;
|
||||
}
|
||||
|
||||
ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
|
||||
vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
|
||||
|
||||
control_freq = chandef->chan->center_freq;
|
||||
|
||||
switch (chandef->width) {
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
if (!ht_cap->ht_supported)
|
||||
return false;
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
width = 20;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
width = 40;
|
||||
if (!ht_cap->ht_supported)
|
||||
return false;
|
||||
if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
|
||||
ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
|
||||
return false;
|
||||
if (chandef->center_freq1 < control_freq &&
|
||||
chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
|
||||
return false;
|
||||
if (chandef->center_freq1 > control_freq &&
|
||||
chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
|
||||
return false;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
|
||||
return false;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
if (!vht_cap->vht_supported)
|
||||
return false;
|
||||
width = 80;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
if (!vht_cap->vht_supported)
|
||||
return false;
|
||||
if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
|
||||
return false;
|
||||
width = 160;
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
trace_cfg80211_return_bool(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq1, width);
|
||||
/* TODO: missing regulatory check on 80/160 bandwidth */
|
||||
|
||||
if (res && chandef->center_freq2)
|
||||
res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq2,
|
||||
width);
|
||||
if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1,
|
||||
width, prohibited_flags))
|
||||
return false;
|
||||
|
||||
if (!chandef->center_freq2)
|
||||
return true;
|
||||
return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2,
|
||||
width, prohibited_flags);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_chandef_usable);
|
||||
|
||||
bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
bool res;
|
||||
|
||||
trace_cfg80211_reg_can_beacon(wiphy, chandef);
|
||||
|
||||
res = cfg80211_chandef_usable(wiphy, chandef,
|
||||
IEEE80211_CHAN_DISABLED |
|
||||
IEEE80211_CHAN_PASSIVE_SCAN |
|
||||
IEEE80211_CHAN_NO_IBSS |
|
||||
IEEE80211_CHAN_RADAR);
|
||||
|
||||
trace_cfg80211_return_bool(res);
|
||||
return res;
|
||||
|
|
|
@ -138,8 +138,6 @@ struct cfg80211_internal_bss {
|
|||
unsigned long ts;
|
||||
struct kref ref;
|
||||
atomic_t hold;
|
||||
bool beacon_ies_allocated;
|
||||
bool proberesp_ies_allocated;
|
||||
|
||||
/* must be last because of priv member */
|
||||
struct cfg80211_bss pub;
|
||||
|
@ -483,12 +481,6 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
|
|||
void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
|
||||
enum nl80211_iftype iftype, int num);
|
||||
|
||||
bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef);
|
||||
|
||||
bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
|
||||
u32 center_freq, u32 bandwidth,
|
||||
u32 prohibited_flags);
|
||||
|
||||
#define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10
|
||||
|
||||
#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
|
||||
|
|
|
@ -146,7 +146,8 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
|
|||
if (!setup->chandef.chan)
|
||||
return -EINVAL;
|
||||
|
||||
setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT;;
|
||||
setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
setup->chandef.center_freq1 = setup->chandef.chan->center_freq;
|
||||
}
|
||||
|
||||
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
|
||||
|
|
|
@ -363,6 +363,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
|
|||
[NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, },
|
||||
[NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN },
|
||||
[NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 },
|
||||
[NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 },
|
||||
};
|
||||
|
||||
/* policy for the key attributes */
|
||||
|
@ -1369,9 +1371,7 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
|
|||
struct genl_info *info,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct ieee80211_sta_ht_cap *ht_cap;
|
||||
struct ieee80211_sta_vht_cap *vht_cap;
|
||||
u32 control_freq, width;
|
||||
u32 control_freq;
|
||||
|
||||
if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
|
||||
return -EINVAL;
|
||||
|
@ -1417,66 +1417,12 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
|
|||
info->attrs[NL80211_ATTR_CENTER_FREQ2]);
|
||||
}
|
||||
|
||||
ht_cap = &rdev->wiphy.bands[chandef->chan->band]->ht_cap;
|
||||
vht_cap = &rdev->wiphy.bands[chandef->chan->band]->vht_cap;
|
||||
|
||||
if (!cfg80211_chan_def_valid(chandef))
|
||||
if (!cfg80211_chandef_valid(chandef))
|
||||
return -EINVAL;
|
||||
|
||||
switch (chandef->width) {
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
if (!ht_cap->ht_supported)
|
||||
return -EINVAL;
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
width = 20;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
width = 40;
|
||||
/* quick early regulatory check */
|
||||
if (chandef->center_freq1 < control_freq &&
|
||||
chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
|
||||
return -EINVAL;
|
||||
if (chandef->center_freq1 > control_freq &&
|
||||
chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
|
||||
return -EINVAL;
|
||||
if (!ht_cap->ht_supported)
|
||||
return -EINVAL;
|
||||
if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
|
||||
ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
width = 80;
|
||||
if (!vht_cap->vht_supported)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
width = 80;
|
||||
if (!vht_cap->vht_supported)
|
||||
return -EINVAL;
|
||||
if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
width = 160;
|
||||
if (!vht_cap->vht_supported)
|
||||
return -EINVAL;
|
||||
if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
if (!cfg80211_chandef_usable(&rdev->wiphy, chandef,
|
||||
IEEE80211_CHAN_DISABLED))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq1,
|
||||
width, IEEE80211_CHAN_DISABLED))
|
||||
return -EINVAL;
|
||||
if (chandef->center_freq2 &&
|
||||
!cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq2,
|
||||
width, IEEE80211_CHAN_DISABLED))
|
||||
return -EINVAL;
|
||||
|
||||
/* TODO: missing regulatory check on bandwidth */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1841,7 +1787,7 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
|
|||
static int nl80211_send_chandef(struct sk_buff *msg,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
WARN_ON(!cfg80211_chan_def_valid(chandef));
|
||||
WARN_ON(!cfg80211_chandef_valid(chandef));
|
||||
|
||||
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
|
||||
chandef->chan->center_freq))
|
||||
|
@ -2732,6 +2678,32 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
|
|||
info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
||||
return -EINVAL;
|
||||
params.p2p_ctwindow =
|
||||
nla_get_u8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
|
||||
if (params.p2p_ctwindow > 127)
|
||||
return -EINVAL;
|
||||
if (params.p2p_ctwindow != 0 &&
|
||||
!(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
|
||||
u8 tmp;
|
||||
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
||||
return -EINVAL;
|
||||
tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
|
||||
if (tmp > 1)
|
||||
return -EINVAL;
|
||||
params.p2p_opp_ps = tmp;
|
||||
if (params.p2p_opp_ps != 0 &&
|
||||
!(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
||||
err = nl80211_parse_chandef(rdev, info, ¶ms.chandef);
|
||||
if (err)
|
||||
|
@ -3698,6 +3670,8 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
|
|||
params.use_short_slot_time = -1;
|
||||
params.ap_isolate = -1;
|
||||
params.ht_opmode = -1;
|
||||
params.p2p_ctwindow = -1;
|
||||
params.p2p_opp_ps = -1;
|
||||
|
||||
if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
|
||||
params.use_cts_prot =
|
||||
|
@ -3720,6 +3694,32 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
|
|||
params.ht_opmode =
|
||||
nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
|
||||
|
||||
if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
||||
return -EINVAL;
|
||||
params.p2p_ctwindow =
|
||||
nla_get_s8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
|
||||
if (params.p2p_ctwindow < 0)
|
||||
return -EINVAL;
|
||||
if (params.p2p_ctwindow != 0 &&
|
||||
!(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
|
||||
u8 tmp;
|
||||
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
||||
return -EINVAL;
|
||||
tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
|
||||
if (tmp > 1)
|
||||
return -EINVAL;
|
||||
params.p2p_opp_ps = tmp;
|
||||
if (params.p2p_opp_ps &&
|
||||
!(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!rdev->ops->change_bss)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
|
@ -4808,6 +4808,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
|
|||
struct cfg80211_internal_bss *intbss)
|
||||
{
|
||||
struct cfg80211_bss *res = &intbss->pub;
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
void *hdr;
|
||||
struct nlattr *bss;
|
||||
|
||||
|
@ -4828,16 +4829,24 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
|
|||
if (!bss)
|
||||
goto nla_put_failure;
|
||||
if ((!is_zero_ether_addr(res->bssid) &&
|
||||
nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)) ||
|
||||
(res->information_elements && res->len_information_elements &&
|
||||
nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
|
||||
res->len_information_elements,
|
||||
res->information_elements)) ||
|
||||
(res->beacon_ies && res->len_beacon_ies &&
|
||||
res->beacon_ies != res->information_elements &&
|
||||
nla_put(msg, NL80211_BSS_BEACON_IES,
|
||||
res->len_beacon_ies, res->beacon_ies)))
|
||||
nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)))
|
||||
goto nla_put_failure;
|
||||
|
||||
rcu_read_lock();
|
||||
ies = rcu_dereference(res->ies);
|
||||
if (ies && ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
|
||||
ies->len, ies->data)) {
|
||||
rcu_read_unlock();
|
||||
goto nla_put_failure;
|
||||
}
|
||||
ies = rcu_dereference(res->beacon_ies);
|
||||
if (ies && ies->len && nla_put(msg, NL80211_BSS_BEACON_IES,
|
||||
ies->len, ies->data)) {
|
||||
rcu_read_unlock();
|
||||
goto nla_put_failure;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (res->tsf &&
|
||||
nla_put_u64(msg, NL80211_BSS_TSF, res->tsf))
|
||||
goto nla_put_failure;
|
||||
|
@ -5502,6 +5511,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
|
|||
return -EINVAL;
|
||||
if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
|
||||
!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
|
||||
return -EINVAL;
|
||||
|
||||
ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
|
||||
ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
|
||||
|
@ -6529,14 +6539,13 @@ nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
|
|||
};
|
||||
|
||||
static int nl80211_set_cqm_txe(struct genl_info *info,
|
||||
u32 rate, u32 pkts, u32 intvl)
|
||||
u32 rate, u32 pkts, u32 intvl)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct wireless_dev *wdev;
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
|
||||
if ((rate < 0 || rate > 100) ||
|
||||
(intvl < 0 || intvl > NL80211_CQM_TXE_MAX_INTVL))
|
||||
if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL)
|
||||
return -EINVAL;
|
||||
|
||||
wdev = dev->ieee80211_ptr;
|
||||
|
|
|
@ -1796,7 +1796,7 @@ EXPORT_SYMBOL(regulatory_hint);
|
|||
*/
|
||||
void regulatory_hint_11d(struct wiphy *wiphy,
|
||||
enum ieee80211_band band,
|
||||
u8 *country_ie,
|
||||
const u8 *country_ie,
|
||||
u8 country_ie_len)
|
||||
{
|
||||
char alpha2[2];
|
||||
|
|
|
@ -81,7 +81,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
|
|||
*/
|
||||
void regulatory_hint_11d(struct wiphy *wiphy,
|
||||
enum ieee80211_band band,
|
||||
u8 *country_ie,
|
||||
const u8 *country_ie,
|
||||
u8 country_ie_len);
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,18 +23,23 @@
|
|||
|
||||
static void bss_release(struct kref *ref)
|
||||
{
|
||||
struct cfg80211_bss_ies *ies;
|
||||
struct cfg80211_internal_bss *bss;
|
||||
|
||||
bss = container_of(ref, struct cfg80211_internal_bss, ref);
|
||||
|
||||
if (WARN_ON(atomic_read(&bss->hold)))
|
||||
return;
|
||||
|
||||
if (bss->pub.free_priv)
|
||||
bss->pub.free_priv(&bss->pub);
|
||||
|
||||
if (bss->beacon_ies_allocated)
|
||||
kfree(bss->pub.beacon_ies);
|
||||
if (bss->proberesp_ies_allocated)
|
||||
kfree(bss->pub.proberesp_ies);
|
||||
|
||||
BUG_ON(atomic_read(&bss->hold));
|
||||
ies = (void *)rcu_access_pointer(bss->pub.beacon_ies);
|
||||
if (ies)
|
||||
kfree_rcu(ies, rcu_head);
|
||||
ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies);
|
||||
if (ies)
|
||||
kfree_rcu(ies, rcu_head);
|
||||
|
||||
kfree(bss);
|
||||
}
|
||||
|
@ -236,9 +241,8 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev,
|
|||
struct cfg80211_internal_bss *bss;
|
||||
unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
|
||||
|
||||
list_for_each_entry(bss, &dev->bss_list, list) {
|
||||
list_for_each_entry(bss, &dev->bss_list, list)
|
||||
bss->ts -= age_jiffies;
|
||||
}
|
||||
}
|
||||
|
||||
void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
|
||||
|
@ -287,7 +291,7 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type,
|
|||
}
|
||||
EXPORT_SYMBOL(cfg80211_find_vendor_ie);
|
||||
|
||||
static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
|
||||
static int cmp_ies(u8 num, const u8 *ies1, int len1, const u8 *ies2, int len2)
|
||||
{
|
||||
const u8 *ie1 = cfg80211_find_ie(num, ies1, len1);
|
||||
const u8 *ie2 = cfg80211_find_ie(num, ies2, len2);
|
||||
|
@ -307,10 +311,10 @@ static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
|
|||
return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
|
||||
}
|
||||
|
||||
static bool is_bss(struct cfg80211_bss *a,
|
||||
const u8 *bssid,
|
||||
static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
|
||||
const u8 *ssid, size_t ssid_len)
|
||||
{
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
const u8 *ssidie;
|
||||
|
||||
if (bssid && !ether_addr_equal(a->bssid, bssid))
|
||||
|
@ -319,9 +323,10 @@ static bool is_bss(struct cfg80211_bss *a,
|
|||
if (!ssid)
|
||||
return true;
|
||||
|
||||
ssidie = cfg80211_find_ie(WLAN_EID_SSID,
|
||||
a->information_elements,
|
||||
a->len_information_elements);
|
||||
ies = rcu_access_pointer(a->ies);
|
||||
if (!ies)
|
||||
return false;
|
||||
ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
|
||||
if (!ssidie)
|
||||
return false;
|
||||
if (ssidie[1] != ssid_len)
|
||||
|
@ -331,20 +336,21 @@ static bool is_bss(struct cfg80211_bss *a,
|
|||
|
||||
static bool is_mesh_bss(struct cfg80211_bss *a)
|
||||
{
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
const u8 *ie;
|
||||
|
||||
if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
|
||||
return false;
|
||||
|
||||
ie = cfg80211_find_ie(WLAN_EID_MESH_ID,
|
||||
a->information_elements,
|
||||
a->len_information_elements);
|
||||
ies = rcu_access_pointer(a->ies);
|
||||
if (!ies)
|
||||
return false;
|
||||
|
||||
ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len);
|
||||
if (!ie)
|
||||
return false;
|
||||
|
||||
ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
|
||||
a->information_elements,
|
||||
a->len_information_elements);
|
||||
ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len);
|
||||
if (!ie)
|
||||
return false;
|
||||
|
||||
|
@ -355,14 +361,17 @@ static bool is_mesh(struct cfg80211_bss *a,
|
|||
const u8 *meshid, size_t meshidlen,
|
||||
const u8 *meshcfg)
|
||||
{
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
const u8 *ie;
|
||||
|
||||
if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
|
||||
return false;
|
||||
|
||||
ie = cfg80211_find_ie(WLAN_EID_MESH_ID,
|
||||
a->information_elements,
|
||||
a->len_information_elements);
|
||||
ies = rcu_access_pointer(a->ies);
|
||||
if (!ies)
|
||||
return false;
|
||||
|
||||
ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len);
|
||||
if (!ie)
|
||||
return false;
|
||||
if (ie[1] != meshidlen)
|
||||
|
@ -370,9 +379,7 @@ static bool is_mesh(struct cfg80211_bss *a,
|
|||
if (memcmp(ie + 2, meshid, meshidlen))
|
||||
return false;
|
||||
|
||||
ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
|
||||
a->information_elements,
|
||||
a->len_information_elements);
|
||||
ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len);
|
||||
if (!ie)
|
||||
return false;
|
||||
if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
|
||||
|
@ -384,30 +391,33 @@ static bool is_mesh(struct cfg80211_bss *a,
|
|||
* part in the same mesh.
|
||||
*/
|
||||
return memcmp(ie + 2, meshcfg,
|
||||
sizeof(struct ieee80211_meshconf_ie) - 2) == 0;
|
||||
sizeof(struct ieee80211_meshconf_ie) - 2) == 0;
|
||||
}
|
||||
|
||||
static int cmp_bss_core(struct cfg80211_bss *a,
|
||||
struct cfg80211_bss *b)
|
||||
static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b)
|
||||
{
|
||||
const struct cfg80211_bss_ies *a_ies, *b_ies;
|
||||
int r;
|
||||
|
||||
if (a->channel != b->channel)
|
||||
return b->channel->center_freq - a->channel->center_freq;
|
||||
|
||||
if (is_mesh_bss(a) && is_mesh_bss(b)) {
|
||||
a_ies = rcu_access_pointer(a->ies);
|
||||
if (!a_ies)
|
||||
return -1;
|
||||
b_ies = rcu_access_pointer(b->ies);
|
||||
if (!b_ies)
|
||||
return 1;
|
||||
|
||||
r = cmp_ies(WLAN_EID_MESH_ID,
|
||||
a->information_elements,
|
||||
a->len_information_elements,
|
||||
b->information_elements,
|
||||
b->len_information_elements);
|
||||
a_ies->data, a_ies->len,
|
||||
b_ies->data, b_ies->len);
|
||||
if (r)
|
||||
return r;
|
||||
return cmp_ies(WLAN_EID_MESH_CONFIG,
|
||||
a->information_elements,
|
||||
a->len_information_elements,
|
||||
b->information_elements,
|
||||
b->len_information_elements);
|
||||
a_ies->data, a_ies->len,
|
||||
b_ies->data, b_ies->len);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -420,22 +430,28 @@ static int cmp_bss_core(struct cfg80211_bss *a,
|
|||
static int cmp_bss(struct cfg80211_bss *a,
|
||||
struct cfg80211_bss *b)
|
||||
{
|
||||
const struct cfg80211_bss_ies *a_ies, *b_ies;
|
||||
int r;
|
||||
|
||||
r = cmp_bss_core(a, b);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
a_ies = rcu_access_pointer(a->ies);
|
||||
if (!a_ies)
|
||||
return -1;
|
||||
b_ies = rcu_access_pointer(b->ies);
|
||||
if (!b_ies)
|
||||
return 1;
|
||||
|
||||
return cmp_ies(WLAN_EID_SSID,
|
||||
a->information_elements,
|
||||
a->len_information_elements,
|
||||
b->information_elements,
|
||||
b->len_information_elements);
|
||||
a_ies->data, a_ies->len,
|
||||
b_ies->data, b_ies->len);
|
||||
}
|
||||
|
||||
static int cmp_hidden_bss(struct cfg80211_bss *a,
|
||||
struct cfg80211_bss *b)
|
||||
static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b)
|
||||
{
|
||||
const struct cfg80211_bss_ies *a_ies, *b_ies;
|
||||
const u8 *ie1;
|
||||
const u8 *ie2;
|
||||
int i;
|
||||
|
@ -445,17 +461,26 @@ static int cmp_hidden_bss(struct cfg80211_bss *a,
|
|||
if (r)
|
||||
return r;
|
||||
|
||||
ie1 = cfg80211_find_ie(WLAN_EID_SSID,
|
||||
a->information_elements,
|
||||
a->len_information_elements);
|
||||
ie2 = cfg80211_find_ie(WLAN_EID_SSID,
|
||||
b->information_elements,
|
||||
b->len_information_elements);
|
||||
a_ies = rcu_access_pointer(a->ies);
|
||||
if (!a_ies)
|
||||
return -1;
|
||||
b_ies = rcu_access_pointer(b->ies);
|
||||
if (!b_ies)
|
||||
return 1;
|
||||
|
||||
/* Key comparator must use same algorithm in any rb-tree
|
||||
ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len);
|
||||
ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len);
|
||||
|
||||
/*
|
||||
* Key comparator must use same algorithm in any rb-tree
|
||||
* search function (order is important), otherwise ordering
|
||||
* of items in the tree is broken and search gives incorrect
|
||||
* results. This code uses same order as cmp_ies() does. */
|
||||
* results. This code uses same order as cmp_ies() does.
|
||||
*
|
||||
* Note that due to the differring behaviour with hidden SSIDs
|
||||
* this function only works when "b" is the tree element and
|
||||
* "a" is the key we're looking for.
|
||||
*/
|
||||
|
||||
/* sort missing IE before (left of) present IE */
|
||||
if (!ie1)
|
||||
|
@ -471,10 +496,14 @@ static int cmp_hidden_bss(struct cfg80211_bss *a,
|
|||
if (ie1[1] != ie2[1])
|
||||
return ie2[1] - ie1[1];
|
||||
|
||||
/* zeroed SSID ie is another indication of a hidden bss */
|
||||
/*
|
||||
* zeroed SSID ie is another indication of a hidden bss;
|
||||
* if it isn't zeroed just return the regular sort value
|
||||
* to find the next candidate
|
||||
*/
|
||||
for (i = 0; i < ie2[1]; i++)
|
||||
if (ie2[i + 2])
|
||||
return -1;
|
||||
return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -600,7 +629,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,
|
|||
|
||||
static struct cfg80211_internal_bss *
|
||||
rb_find_hidden_bss(struct cfg80211_registered_device *dev,
|
||||
struct cfg80211_internal_bss *res)
|
||||
struct cfg80211_internal_bss *res)
|
||||
{
|
||||
struct rb_node *n = dev->bss_tree.rb_node;
|
||||
struct cfg80211_internal_bss *bss;
|
||||
|
@ -623,127 +652,86 @@ rb_find_hidden_bss(struct cfg80211_registered_device *dev,
|
|||
|
||||
static void
|
||||
copy_hidden_ies(struct cfg80211_internal_bss *res,
|
||||
struct cfg80211_internal_bss *hidden)
|
||||
struct cfg80211_internal_bss *hidden)
|
||||
{
|
||||
if (unlikely(res->pub.beacon_ies))
|
||||
return;
|
||||
if (WARN_ON(!hidden->pub.beacon_ies))
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
|
||||
if (rcu_access_pointer(res->pub.beacon_ies))
|
||||
return;
|
||||
|
||||
res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC);
|
||||
if (unlikely(!res->pub.beacon_ies))
|
||||
ies = rcu_access_pointer(hidden->pub.beacon_ies);
|
||||
if (WARN_ON(!ies))
|
||||
return;
|
||||
|
||||
res->beacon_ies_allocated = true;
|
||||
res->pub.len_beacon_ies = hidden->pub.len_beacon_ies;
|
||||
memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies,
|
||||
res->pub.len_beacon_ies);
|
||||
ies = kmemdup(ies, sizeof(*ies) + ies->len, GFP_ATOMIC);
|
||||
if (unlikely(!ies))
|
||||
return;
|
||||
rcu_assign_pointer(res->pub.beacon_ies, ies);
|
||||
}
|
||||
|
||||
static struct cfg80211_internal_bss *
|
||||
cfg80211_bss_update(struct cfg80211_registered_device *dev,
|
||||
struct cfg80211_internal_bss *res)
|
||||
struct cfg80211_internal_bss *tmp)
|
||||
{
|
||||
struct cfg80211_internal_bss *found = NULL;
|
||||
|
||||
/*
|
||||
* The reference to "res" is donated to this function.
|
||||
*/
|
||||
|
||||
if (WARN_ON(!res->pub.channel)) {
|
||||
kref_put(&res->ref, bss_release);
|
||||
if (WARN_ON(!tmp->pub.channel))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res->ts = jiffies;
|
||||
tmp->ts = jiffies;
|
||||
|
||||
spin_lock_bh(&dev->bss_lock);
|
||||
|
||||
found = rb_find_bss(dev, res);
|
||||
if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) {
|
||||
spin_unlock_bh(&dev->bss_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
found = rb_find_bss(dev, tmp);
|
||||
|
||||
if (found) {
|
||||
found->pub.beacon_interval = res->pub.beacon_interval;
|
||||
found->pub.tsf = res->pub.tsf;
|
||||
found->pub.signal = res->pub.signal;
|
||||
found->pub.capability = res->pub.capability;
|
||||
found->ts = res->ts;
|
||||
found->pub.beacon_interval = tmp->pub.beacon_interval;
|
||||
found->pub.tsf = tmp->pub.tsf;
|
||||
found->pub.signal = tmp->pub.signal;
|
||||
found->pub.capability = tmp->pub.capability;
|
||||
found->ts = tmp->ts;
|
||||
|
||||
/* Update IEs */
|
||||
if (res->pub.proberesp_ies) {
|
||||
size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
|
||||
size_t ielen = res->pub.len_proberesp_ies;
|
||||
if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
|
||||
const struct cfg80211_bss_ies *old;
|
||||
|
||||
if (found->pub.proberesp_ies &&
|
||||
!found->proberesp_ies_allocated &&
|
||||
ksize(found) >= used + ielen) {
|
||||
memcpy(found->pub.proberesp_ies,
|
||||
res->pub.proberesp_ies, ielen);
|
||||
found->pub.len_proberesp_ies = ielen;
|
||||
} else {
|
||||
u8 *ies = found->pub.proberesp_ies;
|
||||
|
||||
if (found->proberesp_ies_allocated)
|
||||
ies = krealloc(ies, ielen, GFP_ATOMIC);
|
||||
else
|
||||
ies = kmalloc(ielen, GFP_ATOMIC);
|
||||
|
||||
if (ies) {
|
||||
memcpy(ies, res->pub.proberesp_ies,
|
||||
ielen);
|
||||
found->proberesp_ies_allocated = true;
|
||||
found->pub.proberesp_ies = ies;
|
||||
found->pub.len_proberesp_ies = ielen;
|
||||
}
|
||||
}
|
||||
old = rcu_access_pointer(found->pub.proberesp_ies);
|
||||
|
||||
rcu_assign_pointer(found->pub.proberesp_ies,
|
||||
tmp->pub.proberesp_ies);
|
||||
/* Override possible earlier Beacon frame IEs */
|
||||
found->pub.information_elements =
|
||||
found->pub.proberesp_ies;
|
||||
found->pub.len_information_elements =
|
||||
found->pub.len_proberesp_ies;
|
||||
}
|
||||
if (res->pub.beacon_ies) {
|
||||
size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
|
||||
size_t ielen = res->pub.len_beacon_ies;
|
||||
bool information_elements_is_beacon_ies =
|
||||
(found->pub.information_elements ==
|
||||
found->pub.beacon_ies);
|
||||
rcu_assign_pointer(found->pub.ies,
|
||||
tmp->pub.proberesp_ies);
|
||||
if (old)
|
||||
kfree_rcu((struct cfg80211_bss_ies *)old,
|
||||
rcu_head);
|
||||
} else if (rcu_access_pointer(tmp->pub.beacon_ies)) {
|
||||
const struct cfg80211_bss_ies *old, *ies;
|
||||
|
||||
if (found->pub.beacon_ies &&
|
||||
!found->beacon_ies_allocated &&
|
||||
ksize(found) >= used + ielen) {
|
||||
memcpy(found->pub.beacon_ies,
|
||||
res->pub.beacon_ies, ielen);
|
||||
found->pub.len_beacon_ies = ielen;
|
||||
} else {
|
||||
u8 *ies = found->pub.beacon_ies;
|
||||
old = rcu_access_pointer(found->pub.beacon_ies);
|
||||
ies = rcu_access_pointer(found->pub.ies);
|
||||
|
||||
if (found->beacon_ies_allocated)
|
||||
ies = krealloc(ies, ielen, GFP_ATOMIC);
|
||||
else
|
||||
ies = kmalloc(ielen, GFP_ATOMIC);
|
||||
|
||||
if (ies) {
|
||||
memcpy(ies, res->pub.beacon_ies,
|
||||
ielen);
|
||||
found->beacon_ies_allocated = true;
|
||||
found->pub.beacon_ies = ies;
|
||||
found->pub.len_beacon_ies = ielen;
|
||||
}
|
||||
}
|
||||
rcu_assign_pointer(found->pub.beacon_ies,
|
||||
tmp->pub.beacon_ies);
|
||||
|
||||
/* Override IEs if they were from a beacon before */
|
||||
if (information_elements_is_beacon_ies) {
|
||||
found->pub.information_elements =
|
||||
found->pub.beacon_ies;
|
||||
found->pub.len_information_elements =
|
||||
found->pub.len_beacon_ies;
|
||||
}
|
||||
}
|
||||
if (old == ies)
|
||||
rcu_assign_pointer(found->pub.ies,
|
||||
tmp->pub.beacon_ies);
|
||||
|
||||
kref_put(&res->ref, bss_release);
|
||||
if (old)
|
||||
kfree_rcu((struct cfg80211_bss_ies *)old,
|
||||
rcu_head);
|
||||
}
|
||||
} else {
|
||||
struct cfg80211_internal_bss *new;
|
||||
struct cfg80211_internal_bss *hidden;
|
||||
struct cfg80211_bss_ies *ies;
|
||||
|
||||
/* First check if the beacon is a probe response from
|
||||
* a hidden bss. If so, copy beacon ies (with nullified
|
||||
|
@ -754,14 +742,32 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
|
|||
/* TODO: The code is not trying to update existing probe
|
||||
* response bss entries when beacon ies are
|
||||
* getting changed. */
|
||||
hidden = rb_find_hidden_bss(dev, res);
|
||||
hidden = rb_find_hidden_bss(dev, tmp);
|
||||
if (hidden)
|
||||
copy_hidden_ies(res, hidden);
|
||||
copy_hidden_ies(tmp, hidden);
|
||||
|
||||
/* this "consumes" the reference */
|
||||
list_add_tail(&res->list, &dev->bss_list);
|
||||
rb_insert_bss(dev, res);
|
||||
found = res;
|
||||
/*
|
||||
* create a copy -- the "res" variable that is passed in
|
||||
* is allocated on the stack since it's not needed in the
|
||||
* more common case of an update
|
||||
*/
|
||||
new = kzalloc(sizeof(*new) + dev->wiphy.bss_priv_size,
|
||||
GFP_ATOMIC);
|
||||
if (!new) {
|
||||
ies = (void *)rcu_dereference(tmp->pub.beacon_ies);
|
||||
if (ies)
|
||||
kfree_rcu(ies, rcu_head);
|
||||
ies = (void *)rcu_dereference(tmp->pub.proberesp_ies);
|
||||
if (ies)
|
||||
kfree_rcu(ies, rcu_head);
|
||||
spin_unlock_bh(&dev->bss_lock);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(new, tmp, sizeof(*new));
|
||||
kref_init(&new->ref);
|
||||
list_add_tail(&new->list, &dev->bss_list);
|
||||
rb_insert_bss(dev, new);
|
||||
found = new;
|
||||
}
|
||||
|
||||
dev->bss_generation++;
|
||||
|
@ -810,14 +816,12 @@ cfg80211_inform_bss(struct wiphy *wiphy,
|
|||
u16 beacon_interval, const u8 *ie, size_t ielen,
|
||||
s32 signal, gfp_t gfp)
|
||||
{
|
||||
struct cfg80211_internal_bss *res;
|
||||
size_t privsz;
|
||||
struct cfg80211_bss_ies *ies;
|
||||
struct cfg80211_internal_bss tmp = {}, *res;
|
||||
|
||||
if (WARN_ON(!wiphy))
|
||||
return NULL;
|
||||
|
||||
privsz = wiphy->bss_priv_size;
|
||||
|
||||
if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
|
||||
(signal < 0 || signal > 100)))
|
||||
return NULL;
|
||||
|
@ -826,36 +830,33 @@ cfg80211_inform_bss(struct wiphy *wiphy,
|
|||
if (!channel)
|
||||
return NULL;
|
||||
|
||||
res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
|
||||
if (!res)
|
||||
return NULL;
|
||||
|
||||
memcpy(res->pub.bssid, bssid, ETH_ALEN);
|
||||
res->pub.channel = channel;
|
||||
res->pub.signal = signal;
|
||||
res->pub.tsf = tsf;
|
||||
res->pub.beacon_interval = beacon_interval;
|
||||
res->pub.capability = capability;
|
||||
memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
|
||||
tmp.pub.channel = channel;
|
||||
tmp.pub.signal = signal;
|
||||
tmp.pub.tsf = tsf;
|
||||
tmp.pub.beacon_interval = beacon_interval;
|
||||
tmp.pub.capability = capability;
|
||||
/*
|
||||
* Since we do not know here whether the IEs are from a Beacon or Probe
|
||||
* Response frame, we need to pick one of the options and only use it
|
||||
* with the driver that does not provide the full Beacon/Probe Response
|
||||
* frame. Use Beacon frame pointer to avoid indicating that this should
|
||||
* override the information_elements pointer should we have received an
|
||||
* earlier indication of Probe Response data.
|
||||
* override the iies pointer should we have received an earlier
|
||||
* indication of Probe Response data.
|
||||
*
|
||||
* The initial buffer for the IEs is allocated with the BSS entry and
|
||||
* is located after the private area.
|
||||
*/
|
||||
res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz;
|
||||
memcpy(res->pub.beacon_ies, ie, ielen);
|
||||
res->pub.len_beacon_ies = ielen;
|
||||
res->pub.information_elements = res->pub.beacon_ies;
|
||||
res->pub.len_information_elements = res->pub.len_beacon_ies;
|
||||
ies = kmalloc(sizeof(*ies) + ielen, gfp);
|
||||
if (!ies)
|
||||
return NULL;
|
||||
ies->len = ielen;
|
||||
memcpy(ies->data, ie, ielen);
|
||||
|
||||
kref_init(&res->ref);
|
||||
rcu_assign_pointer(tmp.pub.beacon_ies, ies);
|
||||
rcu_assign_pointer(tmp.pub.ies, ies);
|
||||
|
||||
res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
|
||||
res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp);
|
||||
if (!res)
|
||||
return NULL;
|
||||
|
||||
|
@ -874,10 +875,10 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
|
|||
struct ieee80211_mgmt *mgmt, size_t len,
|
||||
s32 signal, gfp_t gfp)
|
||||
{
|
||||
struct cfg80211_internal_bss *res;
|
||||
struct cfg80211_internal_bss tmp = {}, *res;
|
||||
struct cfg80211_bss_ies *ies;
|
||||
size_t ielen = len - offsetof(struct ieee80211_mgmt,
|
||||
u.probe_resp.variable);
|
||||
size_t privsz;
|
||||
|
||||
BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
|
||||
offsetof(struct ieee80211_mgmt, u.beacon.variable));
|
||||
|
@ -897,45 +898,31 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
|
|||
if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
|
||||
return NULL;
|
||||
|
||||
privsz = wiphy->bss_priv_size;
|
||||
|
||||
channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
|
||||
ielen, channel);
|
||||
if (!channel)
|
||||
return NULL;
|
||||
|
||||
res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
|
||||
if (!res)
|
||||
ies = kmalloc(sizeof(*ies) + ielen, gfp);
|
||||
if (!ies)
|
||||
return NULL;
|
||||
ies->len = ielen;
|
||||
memcpy(ies->data, mgmt->u.probe_resp.variable, ielen);
|
||||
|
||||
memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN);
|
||||
res->pub.channel = channel;
|
||||
res->pub.signal = signal;
|
||||
res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
|
||||
res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
|
||||
res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
|
||||
/*
|
||||
* The initial buffer for the IEs is allocated with the BSS entry and
|
||||
* is located after the private area.
|
||||
*/
|
||||
if (ieee80211_is_probe_resp(mgmt->frame_control)) {
|
||||
res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz;
|
||||
memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable,
|
||||
ielen);
|
||||
res->pub.len_proberesp_ies = ielen;
|
||||
res->pub.information_elements = res->pub.proberesp_ies;
|
||||
res->pub.len_information_elements = res->pub.len_proberesp_ies;
|
||||
} else {
|
||||
res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz;
|
||||
memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen);
|
||||
res->pub.len_beacon_ies = ielen;
|
||||
res->pub.information_elements = res->pub.beacon_ies;
|
||||
res->pub.len_information_elements = res->pub.len_beacon_ies;
|
||||
}
|
||||
if (ieee80211_is_probe_resp(mgmt->frame_control))
|
||||
rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
|
||||
else
|
||||
rcu_assign_pointer(tmp.pub.beacon_ies, ies);
|
||||
rcu_assign_pointer(tmp.pub.ies, ies);
|
||||
|
||||
memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
|
||||
tmp.pub.channel = channel;
|
||||
tmp.pub.signal = signal;
|
||||
tmp.pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
|
||||
tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
|
||||
tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
|
||||
|
||||
kref_init(&res->ref);
|
||||
|
||||
res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
|
||||
res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp);
|
||||
if (!res)
|
||||
return NULL;
|
||||
|
||||
|
@ -1127,22 +1114,21 @@ int cfg80211_wext_siwscan(struct net_device *dev,
|
|||
EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan);
|
||||
|
||||
static void ieee80211_scan_add_ies(struct iw_request_info *info,
|
||||
struct cfg80211_bss *bss,
|
||||
const struct cfg80211_bss_ies *ies,
|
||||
char **current_ev, char *end_buf)
|
||||
{
|
||||
u8 *pos, *end, *next;
|
||||
const u8 *pos, *end, *next;
|
||||
struct iw_event iwe;
|
||||
|
||||
if (!bss->information_elements ||
|
||||
!bss->len_information_elements)
|
||||
if (!ies)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If needed, fragment the IEs buffer (at IE boundaries) into short
|
||||
* enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
|
||||
*/
|
||||
pos = bss->information_elements;
|
||||
end = pos + bss->len_information_elements;
|
||||
pos = ies->data;
|
||||
end = pos + ies->len;
|
||||
|
||||
while (end - pos > IW_GENERIC_IE_MAX) {
|
||||
next = pos + 2 + pos[1];
|
||||
|
@ -1153,7 +1139,8 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
|
|||
iwe.cmd = IWEVGENIE;
|
||||
iwe.u.data.length = next - pos;
|
||||
*current_ev = iwe_stream_add_point(info, *current_ev,
|
||||
end_buf, &iwe, pos);
|
||||
end_buf, &iwe,
|
||||
(void *)pos);
|
||||
|
||||
pos = next;
|
||||
}
|
||||
|
@ -1163,7 +1150,8 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
|
|||
iwe.cmd = IWEVGENIE;
|
||||
iwe.u.data.length = end - pos;
|
||||
*current_ev = iwe_stream_add_point(info, *current_ev,
|
||||
end_buf, &iwe, pos);
|
||||
end_buf, &iwe,
|
||||
(void *)pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1182,10 +1170,11 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
|
|||
struct cfg80211_internal_bss *bss, char *current_ev,
|
||||
char *end_buf)
|
||||
{
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
struct iw_event iwe;
|
||||
const u8 *ie;
|
||||
u8 *buf, *cfg, *p;
|
||||
u8 *ie = bss->pub.information_elements;
|
||||
int rem = bss->pub.len_information_elements, i, sig;
|
||||
int rem, i, sig;
|
||||
bool ismesh = false;
|
||||
|
||||
memset(&iwe, 0, sizeof(iwe));
|
||||
|
@ -1250,7 +1239,17 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
|
|||
current_ev = iwe_stream_add_point(info, current_ev, end_buf,
|
||||
&iwe, "");
|
||||
|
||||
while (rem >= 2) {
|
||||
rcu_read_lock();
|
||||
ies = rcu_dereference(bss->pub.ies);
|
||||
if (ies) {
|
||||
rem = ies->len;
|
||||
ie = ies->data;
|
||||
} else {
|
||||
rem = 0;
|
||||
ie = NULL;
|
||||
}
|
||||
|
||||
while (ies && rem >= 2) {
|
||||
/* invalid data */
|
||||
if (ie[1] > rem - 2)
|
||||
break;
|
||||
|
@ -1262,7 +1261,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
|
|||
iwe.u.data.length = ie[1];
|
||||
iwe.u.data.flags = 1;
|
||||
current_ev = iwe_stream_add_point(info, current_ev, end_buf,
|
||||
&iwe, ie + 2);
|
||||
&iwe, (u8 *)ie + 2);
|
||||
break;
|
||||
case WLAN_EID_MESH_ID:
|
||||
memset(&iwe, 0, sizeof(iwe));
|
||||
|
@ -1270,7 +1269,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
|
|||
iwe.u.data.length = ie[1];
|
||||
iwe.u.data.flags = 1;
|
||||
current_ev = iwe_stream_add_point(info, current_ev, end_buf,
|
||||
&iwe, ie + 2);
|
||||
&iwe, (u8 *)ie + 2);
|
||||
break;
|
||||
case WLAN_EID_MESH_CONFIG:
|
||||
ismesh = true;
|
||||
|
@ -1279,7 +1278,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
|
|||
buf = kmalloc(50, GFP_ATOMIC);
|
||||
if (!buf)
|
||||
break;
|
||||
cfg = ie + 2;
|
||||
cfg = (u8 *)ie + 2;
|
||||
memset(&iwe, 0, sizeof(iwe));
|
||||
iwe.cmd = IWEVCUSTOM;
|
||||
sprintf(buf, "Mesh Network Path Selection Protocol ID: "
|
||||
|
@ -1377,7 +1376,8 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
|
|||
kfree(buf);
|
||||
}
|
||||
|
||||
ieee80211_scan_add_ies(info, &bss->pub, ¤t_ev, end_buf);
|
||||
ieee80211_scan_add_ies(info, ies, ¤t_ev, end_buf);
|
||||
rcu_read_unlock();
|
||||
|
||||
return current_ev;
|
||||
}
|
||||
|
|
|
@ -417,7 +417,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
|
|||
struct cfg80211_bss *bss)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
u8 *country_ie;
|
||||
const u8 *country_ie;
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
union iwreq_data wrqu;
|
||||
#endif
|
||||
|
@ -501,7 +501,15 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
|
|||
wdev->sme_state = CFG80211_SME_CONNECTED;
|
||||
cfg80211_upload_connect_keys(wdev);
|
||||
|
||||
country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
|
||||
rcu_read_lock();
|
||||
country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
|
||||
if (!country_ie) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
country_ie = kmemdup(country_ie, 2 + country_ie[1], GFP_ATOMIC);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!country_ie)
|
||||
return;
|
||||
|
@ -515,6 +523,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
|
|||
bss->channel->band,
|
||||
country_ie + 2,
|
||||
country_ie[1]);
|
||||
kfree(country_ie);
|
||||
}
|
||||
|
||||
void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
|
||||
|
|
|
@ -688,10 +688,13 @@ EXPORT_SYMBOL(cfg80211_classify8021d);
|
|||
|
||||
const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie)
|
||||
{
|
||||
if (bss->information_elements == NULL)
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
|
||||
ies = rcu_dereference(bss->ies);
|
||||
if (!ies)
|
||||
return NULL;
|
||||
return cfg80211_find_ie(ie, bss->information_elements,
|
||||
bss->len_information_elements);
|
||||
|
||||
return cfg80211_find_ie(ie, ies->data, ies->len);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_bss_get_ie);
|
||||
|
||||
|
|
|
@ -242,13 +242,17 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev,
|
|||
|
||||
wdev_lock(wdev);
|
||||
if (wdev->current_bss) {
|
||||
const u8 *ie = ieee80211_bss_get_ie(&wdev->current_bss->pub,
|
||||
WLAN_EID_SSID);
|
||||
const u8 *ie;
|
||||
|
||||
rcu_read_lock();
|
||||
ie = ieee80211_bss_get_ie(&wdev->current_bss->pub,
|
||||
WLAN_EID_SSID);
|
||||
if (ie) {
|
||||
data->flags = 1;
|
||||
data->length = ie[1];
|
||||
memcpy(ssid, ie + 2, data->length);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
} else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
|
||||
data->flags = 1;
|
||||
data->length = wdev->wext.connect.ssid_len;
|
||||
|
|
Загрузка…
Ссылка в новой задаче