This time, I have some rate minstrel improvements, support for a very

small feature from CCX that Steinar reverse-engineered, dynamic ACK
 timeout support, a number of changes for TDLS, early support for radio
 resource measurement and many fixes. Also, I'm changing a number of
 places to clear key memory when it's freed and Intel claims copyright
 for code they developed.
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABCAAGBQJUEpv0AAoJEDBSmw7B7bqr6CMP/2CXvWr/98AY2Flt74KDNyaE
 vmJBVCsu+eT0G9FL6YxbVU5+rvInGDHd9qTHkU4ljd+uXwnG8XAT+WHFlhBjzm+V
 juXPWblbSdMzwpWDfq7Kbk134b9ALTEUqekhqSFvhPA5h0Dq0/8lDK9CFyfwKWbN
 07PwUv0VUUEHKVqQoVSNJu9Szi5NvZvDcN7Jwg1Cpnv0sUOeH7J2Kz1OUT4RaEhI
 c/UJjCQV4ssXaEkTDIxciQ62HrglZanMqyx4a9LGbrxLdw1KJ19CNmSkwB5mQuZg
 LhV05Y0Gv4tkRC8sCo7HF7cqgjBfjTNiEjZYfbExW0QFOMKIgKmmjYIEezVdbrk7
 gFIyhTRE595UtztUJV0dcitoOlybbRf3OdEwAIJD6fc0vhoe/rSjUIyS7/CZisMT
 9zg33JvtK3eYPSJS1jy4lk2yZ5alhLoPMQTNmsEuyOGcU3sH9vTGMjONPffOlcH9
 nzj7aUS2Qvwn3H+4CIaZbZhySpa0B9zkGL3oxeaEBmLJbFMTo5ua2FNGhubC2O+O
 BwNULDBEMwsGHKMUCWCLmQwACWdVdNxYYWtXbWfxdmC/CJoXgdLCJIUfoa1aOf2A
 DyCqUFvG/n8ObHVy+P3RU6poQFj0M/yclJAMHRW6x2qzNvAkDb0G6TVeIlgN5dG8
 jLoZPL5OH0wb0BPVNEH8
 =OPIp
 -----END PGP SIGNATURE-----

Merge tag 'mac80211-next-for-john-2014-09-12' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next

Johannes Berg <johannes@sipsolutions.net> says:

"This time, I have some rate minstrel improvements, support for a very
small feature from CCX that Steinar reverse-engineered, dynamic ACK
timeout support, a number of changes for TDLS, early support for radio
resource measurement and many fixes. Also, I'm changing a number of
places to clear key memory when it's freed and Intel claims copyright
for code they developed."

Conflicts:
	net/mac80211/iface.c

Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
John W. Linville 2014-09-15 14:51:23 -04:00
commit 6bd2bd27ba
56 changed files with 1115 additions and 399 deletions

View file

@ -4838,7 +4838,6 @@ int ath10k_mac_register(struct ath10k *ar)
IEEE80211_HW_MFP_CAPABLE |
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_HAS_RATE_CONTROL |
IEEE80211_HW_SUPPORTS_STATIC_SMPS |
IEEE80211_HW_AP_LINK_PS |
IEEE80211_HW_SPECTRUM_MGMT;
@ -4846,8 +4845,10 @@ int ath10k_mac_register(struct ath10k *ar)
* bytes is used for padding/alignment if necessary. */
ar->hw->extra_tx_headroom += sizeof(struct htt_data_tx_desc_frag)*2 + 4;
ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS;
if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
ar->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS;
ar->hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS;
if (ar->ht_cap_info & WMI_HT_CAP_ENABLED) {
ar->hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;

View file

@ -704,7 +704,7 @@ ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey)
* reset.
*/
static void
ath5k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
ath5k_set_coverage_class(struct ieee80211_hw *hw, s16 coverage_class)
{
struct ath5k_hw *ah = hw->priv;

View file

@ -1722,7 +1722,7 @@ static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
}
static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
u8 coverage_class)
s16 coverage_class)
{
struct ath9k_htc_priv *priv = hw->priv;

View file

@ -1860,7 +1860,8 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx,
return 0;
}
static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
static void ath9k_set_coverage_class(struct ieee80211_hw *hw,
s16 coverage_class)
{
struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;

View file

@ -5757,9 +5757,8 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length)
IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
if (il->cfg->sku & IL_SKU_N)
hw->flags |=
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
IEEE80211_HW_SUPPORTS_STATIC_SMPS;
hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS |
NL80211_FEATURE_STATIC_SMPS;
hw->sta_data_size = sizeof(struct il_station_priv);
hw->vif_data_size = sizeof(struct il_vif_priv);

View file

@ -125,8 +125,8 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
*/
if (priv->nvm_data->sku_cap_11n_enable)
hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
IEEE80211_HW_SUPPORTS_STATIC_SMPS;
hw->wiphy->features |= NL80211_FEATURE_DYNAMIC_SMPS |
NL80211_FEATURE_STATIC_SMPS;
/*
* Enable 11w if advertised by firmware and software crypto

View file

@ -303,9 +303,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
IEEE80211_HW_AMPDU_AGGREGATION |
IEEE80211_HW_TIMING_BEACON_ONLY |
IEEE80211_HW_CONNECTION_MONITOR |
IEEE80211_HW_CHANCTX_STA_CSA |
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
IEEE80211_HW_SUPPORTS_STATIC_SMPS;
IEEE80211_HW_CHANCTX_STA_CSA;
hw->queues = mvm->first_agg_queue;
hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
@ -409,7 +407,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
NL80211_FEATURE_LOW_PRIORITY_SCAN |
NL80211_FEATURE_P2P_GO_OPPPS;
NL80211_FEATURE_P2P_GO_OPPPS |
NL80211_FEATURE_DYNAMIC_SMPS |
NL80211_FEATURE_STATIC_SMPS;
mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;

View file

@ -2045,8 +2045,6 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
hw->flags = IEEE80211_HW_MFP_CAPABLE |
IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_SUPPORTS_STATIC_SMPS |
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
IEEE80211_HW_AMPDU_AGGREGATION |
IEEE80211_HW_WANT_MONITOR_VIF |
IEEE80211_HW_QUEUE_CONTROL |
@ -2059,8 +2057,10 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
WIPHY_FLAG_AP_UAPSD |
WIPHY_FLAG_HAS_CHANNEL_SWITCH;
hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE;
hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR |
NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
NL80211_FEATURE_STATIC_SMPS |
NL80211_FEATURE_DYNAMIC_SMPS;
/* ask mac80211 to reserve space for magic */
hw->vif_data_size = sizeof(struct hwsim_vif_priv);

View file

@ -696,7 +696,8 @@ static void p54_flush(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
WARN(total, "tx flush timeout, unresponsive firmware");
}
static void p54_set_coverage_class(struct ieee80211_hw *dev, u8 coverage_class)
static void p54_set_coverage_class(struct ieee80211_hw *dev,
s16 coverage_class)
{
struct p54_common *priv = dev->priv;

View file

@ -6,6 +6,7 @@
* Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
* Copyright (c) 2005, Devicescape Software, Inc.
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
* Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -165,8 +166,12 @@ static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2)
#define IEEE80211_MAX_MESH_ID_LEN 32
#define IEEE80211_FIRST_TSPEC_TSID 8
#define IEEE80211_NUM_TIDS 16
/* number of user priorities 802.11 uses */
#define IEEE80211_NUM_UPS 8
#define IEEE80211_QOS_CTL_LEN 2
/* 1d tag mask */
#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007
@ -1823,7 +1828,8 @@ enum ieee80211_eid {
WLAN_EID_DMG_TSPEC = 146,
WLAN_EID_DMG_AT = 147,
WLAN_EID_DMG_CAP = 148,
/* 149-150 reserved for Cisco */
/* 149 reserved for Cisco */
WLAN_EID_CISCO_VENDOR_SPECIFIC = 150,
WLAN_EID_DMG_OPERATION = 151,
WLAN_EID_DMG_BSS_PARAM_CHANGE = 152,
WLAN_EID_DMG_BEAM_REFINEMENT = 153,

View file

@ -4,6 +4,7 @@
* 802.11 device and configuration interface
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -663,6 +664,7 @@ struct cfg80211_acl_data {
* @crypto: crypto settings
* @privacy: the BSS uses privacy
* @auth_type: Authentication type (algorithm)
* @smps_mode: SMPS mode
* @inactivity_timeout: time in seconds to determine station's inactivity.
* @p2p_ctwindow: P2P CT Window
* @p2p_opp_ps: P2P opportunistic PS
@ -681,6 +683,7 @@ struct cfg80211_ap_settings {
struct cfg80211_crypto_settings crypto;
bool privacy;
enum nl80211_auth_type auth_type;
enum nl80211_smps_mode smps_mode;
int inactivity_timeout;
u8 p2p_ctwindow;
bool p2p_opp_ps;
@ -1607,10 +1610,12 @@ struct cfg80211_auth_request {
*
* @ASSOC_REQ_DISABLE_HT: Disable HT (802.11n)
* @ASSOC_REQ_DISABLE_VHT: Disable VHT
* @ASSOC_REQ_USE_RRM: Declare RRM capability in this association
*/
enum cfg80211_assoc_req_flags {
ASSOC_REQ_DISABLE_HT = BIT(0),
ASSOC_REQ_DISABLE_VHT = BIT(1),
ASSOC_REQ_USE_RRM = BIT(2),
};
/**
@ -1802,6 +1807,7 @@ struct cfg80211_connect_params {
* @WIPHY_PARAM_FRAG_THRESHOLD: wiphy->frag_threshold has changed
* @WIPHY_PARAM_RTS_THRESHOLD: wiphy->rts_threshold has changed
* @WIPHY_PARAM_COVERAGE_CLASS: coverage class changed
* @WIPHY_PARAM_DYN_ACK: dynack has been enabled
*/
enum wiphy_params_flags {
WIPHY_PARAM_RETRY_SHORT = 1 << 0,
@ -1809,6 +1815,7 @@ enum wiphy_params_flags {
WIPHY_PARAM_FRAG_THRESHOLD = 1 << 2,
WIPHY_PARAM_RTS_THRESHOLD = 1 << 3,
WIPHY_PARAM_COVERAGE_CLASS = 1 << 4,
WIPHY_PARAM_DYN_ACK = 1 << 5,
};
/*
@ -1975,14 +1982,12 @@ struct cfg80211_wowlan_wakeup {
/**
* struct cfg80211_gtk_rekey_data - rekey data
* @kek: key encryption key
* @kck: key confirmation key
* @replay_ctr: replay counter
* @kek: key encryption key (NL80211_KEK_LEN bytes)
* @kck: key confirmation key (NL80211_KCK_LEN bytes)
* @replay_ctr: replay counter (NL80211_REPLAY_CTR_LEN bytes)
*/
struct cfg80211_gtk_rekey_data {
u8 kek[NL80211_KEK_LEN];
u8 kck[NL80211_KCK_LEN];
u8 replay_ctr[NL80211_REPLAY_CTR_LEN];
const u8 *kek, *kck, *replay_ctr;
};
/**
@ -2315,6 +2320,17 @@ struct cfg80211_qos_map {
* @set_ap_chanwidth: Set the AP (including P2P GO) mode channel width for the
* given interface This is used e.g. for dynamic HT 20/40 MHz channel width
* changes during the lifetime of the BSS.
*
* @add_tx_ts: validate (if admitted_time is 0) or add a TX TS to the device
* with the given parameters; action frame exchange has been handled by
* userspace so this just has to modify the TX path to take the TS into
* account.
* If the admitted time is 0 just validate the parameters to make sure
* the session can be created at all; it is valid to just always return
* success for that but that may result in inefficient behaviour (handshake
* with the peer followed by immediate teardown when the addition is later
* rejected)
* @del_tx_ts: remove an existing TX TS
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@ -2555,6 +2571,12 @@ struct cfg80211_ops {
int (*set_ap_chanwidth)(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_chan_def *chandef);
int (*add_tx_ts)(struct wiphy *wiphy, struct net_device *dev,
u8 tsid, const u8 *peer, u8 user_prio,
u16 admitted_time);
int (*del_tx_ts)(struct wiphy *wiphy, struct net_device *dev,
u8 tsid, const u8 *peer);
};
/*
@ -2601,9 +2623,13 @@ struct cfg80211_ops {
* @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
* @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in
* beaconing mode (AP, IBSS, Mesh, ...).
* @WIPHY_FLAG_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM
* TSPEC sessions (TID aka TSID 0-7) with the NL80211_CMD_ADD_TX_TS
* command. Standard IEEE 802.11 TSPEC setup is not yet supported, it
* needs to be able to handle Block-Ack agreements and other things.
*/
enum wiphy_flags {
/* use hole at 0 */
WIPHY_FLAG_SUPPORTS_WMM_ADMISSION = BIT(0),
/* use hole at 1 */
/* use hole at 2 */
WIPHY_FLAG_NETNS_OK = BIT(3),
@ -3920,6 +3946,7 @@ void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr);
* moves to cfg80211 in this call
* @buf: authentication frame (header + body)
* @len: length of the frame data
* @uapsd_queues: bitmap of ACs configured to uapsd. -1 if n/a.
*
* After being asked to associate via cfg80211_ops::assoc() the driver must
* call either this function or cfg80211_auth_timeout().
@ -3928,7 +3955,8 @@ void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr);
*/
void cfg80211_rx_assoc_resp(struct net_device *dev,
struct cfg80211_bss *bss,
const u8 *buf, size_t len);
const u8 *buf, size_t len,
int uapsd_queues);
/**
* cfg80211_assoc_timeout - notification of timed out association

View file

@ -4,6 +4,7 @@
* Copyright 2002-2005, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -1536,16 +1537,6 @@ struct ieee80211_tx_control {
* @IEEE80211_HW_MFP_CAPABLE:
* Hardware supports management frame protection (MFP, IEEE 802.11w).
*
* @IEEE80211_HW_SUPPORTS_STATIC_SMPS:
* Hardware supports static spatial multiplexing powersave,
* ie. can turn off all but one chain even on HT connections
* that should be using more chains.
*
* @IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS:
* Hardware supports dynamic spatial multiplexing powersave,
* ie. can turn off all but one chain and then wake the rest
* up as required after, for example, rts/cts handshake.
*
* @IEEE80211_HW_SUPPORTS_UAPSD:
* Hardware supports Unscheduled Automatic Power Save Delivery
* (U-APSD) in managed mode. The mode is configured with
@ -1631,8 +1622,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12,
IEEE80211_HW_MFP_CAPABLE = 1<<13,
IEEE80211_HW_WANT_MONITOR_VIF = 1<<14,
IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15,
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
/* free slots */
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
IEEE80211_HW_CONNECTION_MONITOR = 1<<19,
@ -2672,7 +2662,9 @@ enum ieee80211_roc_type {
*
* @set_coverage_class: Set slot time for given coverage class as specified
* in IEEE 802.11-2007 section 17.3.8.6 and modify ACK timeout
* accordingly. This callback is not required and may sleep.
* accordingly; coverage class equals to -1 to enable ACK timeout
* estimation algorithm (dynack). To disable dynack set valid value for
* coverage class. This callback is not required and may sleep.
*
* @testmode_cmd: Implement a cfg80211 test mode command. The passed @vif may
* be %NULL. The callback can sleep.
@ -2956,7 +2948,7 @@ struct ieee80211_ops {
int (*get_survey)(struct ieee80211_hw *hw, int idx,
struct survey_info *survey);
void (*rfkill_poll)(struct ieee80211_hw *hw);
void (*set_coverage_class)(struct ieee80211_hw *hw, u8 coverage_class);
void (*set_coverage_class)(struct ieee80211_hw *hw, s16 coverage_class);
#ifdef CONFIG_NL80211_TESTMODE
int (*testmode_cmd)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len);

View file

@ -722,6 +722,22 @@
* QoS mapping is relevant for IP packets, it is only valid during an
* association. This is cleared on disassociation and AP restart.
*
* @NL80211_CMD_ADD_TX_TS: Ask the kernel to add a traffic stream for the given
* %NL80211_ATTR_TSID and %NL80211_ATTR_MAC with %NL80211_ATTR_USER_PRIO
* and %NL80211_ATTR_ADMITTED_TIME parameters.
* Note that the action frame handshake with the AP shall be handled by
* userspace via the normal management RX/TX framework, this only sets
* up the TX TS in the driver/device.
* If the admitted time attribute is not added then the request just checks
* if a subsequent setup could be successful, the intent is to use this to
* avoid setting up a session with the AP when local restrictions would
* make that impossible. However, the subsequent "real" setup may still
* fail even if the check was successful.
* @NL80211_CMD_DEL_TX_TS: Remove an existing TS with the %NL80211_ATTR_TSID
* and %NL80211_ATTR_MAC parameters. It isn't necessary to call this
* before removing a station entry entirely, or before disassociating
* or similar, cleanup will happen in the driver/device in this case.
*
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@ -893,6 +909,9 @@ enum nl80211_commands {
NL80211_CMD_SET_QOS_MAP,
NL80211_CMD_ADD_TX_TS,
NL80211_CMD_DEL_TX_TS,
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@ -1594,6 +1613,31 @@ enum nl80211_commands {
* @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
* the TDLS link initiator.
*
* @NL80211_ATTR_USE_RRM: flag for indicating whether the current connection
* shall support Radio Resource Measurements (11k). This attribute can be
* used with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests.
* User space applications are expected to use this flag only if the
* underlying device supports these minimal RRM features:
* %NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES,
* %NL80211_FEATURE_QUIET,
* If this flag is used, driver must add the Power Capabilities IE to the
* association request. In addition, it must also set the RRM capability
* flag in the association request's Capability Info field.
*
* @NL80211_ATTR_WIPHY_DYN_ACK: flag attribute used to enable ACK timeout
* estimation algorithm (dynack). In order to activate dynack
* %NL80211_FEATURE_ACKTO_ESTIMATION feature flag must be set by lower
* drivers to indicate dynack capability. Dynack is automatically disabled
* setting valid value for coverage class.
*
* @NL80211_ATTR_TSID: a TSID value (u8 attribute)
* @NL80211_ATTR_USER_PRIO: user priority value (u8 attribute)
* @NL80211_ATTR_ADMITTED_TIME: admitted time in units of 32 microseconds
* (per second) (u16 attribute)
*
* @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see
* &enum nl80211_smps_mode.
*
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@ -1936,6 +1980,16 @@ enum nl80211_attrs {
NL80211_ATTR_TDLS_INITIATOR,
NL80211_ATTR_USE_RRM,
NL80211_ATTR_WIPHY_DYN_ACK,
NL80211_ATTR_TSID,
NL80211_ATTR_USER_PRIO,
NL80211_ATTR_ADMITTED_TIME,
NL80211_ATTR_SMPS_MODE,
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@ -3968,6 +4022,26 @@ enum nl80211_ap_sme_features {
* @NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: This driver supports dynamic
* channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the
* lifetime of a BSS.
* @NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES: This device adds a DS Parameter
* Set IE to probe requests.
* @NL80211_FEATURE_WFA_TPC_IE_IN_PROBES: This device adds a WFA TPC Report IE
* to probe requests.
* @NL80211_FEATURE_QUIET: This device, in client mode, supports Quiet Period
* requests sent to it by an AP.
* @NL80211_FEATURE_TX_POWER_INSERTION: This device is capable of inserting the
* current tx power value into the TPC Report IE in the spectrum
* management TPC Report action frame, and in the Radio Measurement Link
* Measurement Report action frame.
* @NL80211_FEATURE_ACKTO_ESTIMATION: This driver supports dynamic ACK timeout
* estimation (dynack). %NL80211_ATTR_WIPHY_DYN_ACK flag attribute is used
* to enable dynack.
* @NL80211_FEATURE_STATIC_SMPS: Device supports static spatial
* multiplexing powersave, ie. can turn off all but one chain
* even on HT connections that should be using more chains.
* @NL80211_FEATURE_DYNAMIC_SMPS: Device supports dynamic spatial
* multiplexing powersave, ie. can turn off all but one chain
* and then wake the rest up as required after, for example,
* rts/cts handshake.
*/
enum nl80211_feature_flags {
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
@ -3989,6 +4063,13 @@ enum nl80211_feature_flags {
NL80211_FEATURE_USERSPACE_MPM = 1 << 16,
NL80211_FEATURE_ACTIVE_MONITOR = 1 << 17,
NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE = 1 << 18,
NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES = 1 << 19,
NL80211_FEATURE_WFA_TPC_IE_IN_PROBES = 1 << 20,
NL80211_FEATURE_QUIET = 1 << 21,
NL80211_FEATURE_TX_POWER_INSERTION = 1 << 22,
NL80211_FEATURE_ACKTO_ESTIMATION = 1 << 23,
NL80211_FEATURE_STATIC_SMPS = 1 << 24,
NL80211_FEATURE_DYNAMIC_SMPS = 1 << 25,
};
/**
@ -4062,6 +4143,25 @@ enum nl80211_acl_policy {
NL80211_ACL_POLICY_DENY_UNLESS_LISTED,
};
/**
* enum nl80211_smps_mode - SMPS mode
*
* Requested SMPS mode (for AP mode)
*
* @NL80211_SMPS_OFF: SMPS off (use all antennas).
* @NL80211_SMPS_STATIC: static SMPS (use a single antenna)
* @NL80211_SMPS_DYNAMIC: dynamic smps (start with a single antenna and
* turn on other antennas after CTS/RTS).
*/
enum nl80211_smps_mode {
NL80211_SMPS_OFF,
NL80211_SMPS_STATIC,
NL80211_SMPS_DYNAMIC,
__NL80211_SMPS_AFTER_LAST,
NL80211_SMPS_MAX = __NL80211_SMPS_AFTER_LAST - 1
};
/**
* enum nl80211_radar_event - type of radar event for DFS operation
*

View file

@ -227,7 +227,7 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
void __ieee80211_start_rx_ba_session(struct sta_info *sta,
u8 dialog_token, u16 timeout,
u16 start_seq_num, u16 ba_policy, u16 tid,
u16 buf_size, bool tx)
u16 buf_size, bool tx, bool auto_seq)
{
struct ieee80211_local *local = sta->sdata->local;
struct tid_ampdu_rx *tid_agg_rx;
@ -326,6 +326,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
tid_agg_rx->buf_size = buf_size;
tid_agg_rx->timeout = timeout;
tid_agg_rx->stored_mpdu_num = 0;
tid_agg_rx->auto_seq = auto_seq;
status = WLAN_STATUS_SUCCESS;
/* activate it for RX */
@ -367,7 +368,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
__ieee80211_start_rx_ba_session(sta, dialog_token, timeout,
start_seq_num, ba_policy, tid,
buf_size, true);
buf_size, true, false);
}
void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif,

View file

@ -2,6 +2,7 @@
* mac80211 configuration hooks for cfg80211
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This file is GPLv2 as found in COPYING.
*/
@ -682,8 +683,19 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
if (old)
return -EALREADY;
/* TODO: make hostapd tell us what it wants */
sdata->smps_mode = IEEE80211_SMPS_OFF;
switch (params->smps_mode) {
case NL80211_SMPS_OFF:
sdata->smps_mode = IEEE80211_SMPS_OFF;
break;
case NL80211_SMPS_STATIC:
sdata->smps_mode = IEEE80211_SMPS_STATIC;
break;
case NL80211_SMPS_DYNAMIC:
sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
break;
default:
return -EINVAL;
}
sdata->needed_rx_chains = sdata->local->rx_chains;
mutex_lock(&local->mtx);
@ -1977,8 +1989,13 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
return err;
}
if (changed & WIPHY_PARAM_COVERAGE_CLASS) {
err = drv_set_coverage_class(local, wiphy->coverage_class);
if ((changed & WIPHY_PARAM_COVERAGE_CLASS) ||
(changed & WIPHY_PARAM_DYN_ACK)) {
s16 coverage_class;
coverage_class = changed & WIPHY_PARAM_COVERAGE_CLASS ?
wiphy->coverage_class : -1;
err = drv_set_coverage_class(local, coverage_class);
if (err)
return err;
@ -2351,6 +2368,58 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
return 0;
}
static bool ieee80211_coalesce_started_roc(struct ieee80211_local *local,
struct ieee80211_roc_work *new_roc,
struct ieee80211_roc_work *cur_roc)
{
unsigned long j = jiffies;
unsigned long cur_roc_end = cur_roc->hw_start_time +
msecs_to_jiffies(cur_roc->duration);
struct ieee80211_roc_work *next_roc;
int new_dur;
if (WARN_ON(!cur_roc->started || !cur_roc->hw_begun))
return false;
if (time_after(j + IEEE80211_ROC_MIN_LEFT, cur_roc_end))
return false;
ieee80211_handle_roc_started(new_roc);
new_dur = new_roc->duration - jiffies_to_msecs(cur_roc_end - j);
/* cur_roc is long enough - add new_roc to the dependents list. */
if (new_dur <= 0) {
list_add_tail(&new_roc->list, &cur_roc->dependents);
return true;
}
new_roc->duration = new_dur;
/*
* if cur_roc was already coalesced before, we might
* want to extend the next roc instead of adding
* a new one.
*/
next_roc = list_entry(cur_roc->list.next,
struct ieee80211_roc_work, list);
if (&next_roc->list != &local->roc_list &&
next_roc->chan == new_roc->chan &&
next_roc->sdata == new_roc->sdata &&
!WARN_ON(next_roc->started)) {
list_add_tail(&new_roc->list, &next_roc->dependents);
next_roc->duration = max(next_roc->duration,
new_roc->duration);
next_roc->type = max(next_roc->type, new_roc->type);
return true;
}
/* add right after cur_roc */
list_add(&new_roc->list, &cur_roc->list);
return true;
}
static int ieee80211_start_roc_work(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel,
@ -2456,8 +2525,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
/* If it has already started, it's more difficult ... */
if (local->ops->remain_on_channel) {
unsigned long j = jiffies;
/*
* In the offloaded ROC case, if it hasn't begun, add
* this new one to the dependent list to be handled
@ -2480,28 +2547,8 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
break;
}
if (time_before(j + IEEE80211_ROC_MIN_LEFT,
tmp->hw_start_time +
msecs_to_jiffies(tmp->duration))) {
int new_dur;
ieee80211_handle_roc_started(roc);
new_dur = roc->duration -
jiffies_to_msecs(tmp->hw_start_time +
msecs_to_jiffies(
tmp->duration) -
j);
if (new_dur > 0) {
/* add right after tmp */
list_add(&roc->list, &tmp->list);
} else {
list_add_tail(&roc->list,
&tmp->dependents);
}
if (ieee80211_coalesce_started_roc(local, roc, tmp))
queued = true;
}
} else if (del_timer_sync(&tmp->work.timer)) {
unsigned long new_end;

View file

@ -3,6 +3,7 @@
* mac80211 debugfs for wireless PHYs
*
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* GPLv2
*
@ -302,11 +303,6 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf,
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");
if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE)
sf += scnprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");
if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS)
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n");
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
sf += scnprintf(buf + sf, mxln - sf,
"SUPPORTS_DYNAMIC_SMPS\n");
if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n");
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)

View file

@ -226,12 +226,12 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
int err;
if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) &&
if (!(local->hw.wiphy->features & NL80211_FEATURE_STATIC_SMPS) &&
smps_mode == IEEE80211_SMPS_STATIC)
return -EINVAL;
/* auto should be dynamic if in PS mode */
if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) &&
if (!(local->hw.wiphy->features & NL80211_FEATURE_DYNAMIC_SMPS) &&
(smps_mode == IEEE80211_SMPS_DYNAMIC ||
smps_mode == IEEE80211_SMPS_AUTOMATIC))
return -EINVAL;

View file

@ -2,6 +2,7 @@
* Copyright 2003-2005 Devicescape Software, Inc.
* Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as

View file

@ -450,7 +450,7 @@ static inline int drv_set_rts_threshold(struct ieee80211_local *local,
}
static inline int drv_set_coverage_class(struct ieee80211_local *local,
u8 value)
s16 value)
{
int ret = 0;
might_sleep();

View file

@ -6,6 +6,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2009, Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as

View file

@ -3,6 +3,7 @@
* Copyright 2005, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -354,6 +355,7 @@ enum ieee80211_sta_flags {
IEEE80211_STA_DISABLE_80P80MHZ = BIT(12),
IEEE80211_STA_DISABLE_160MHZ = BIT(13),
IEEE80211_STA_DISABLE_WMM = BIT(14),
IEEE80211_STA_ENABLE_RRM = BIT(15),
};
struct ieee80211_mgd_auth_data {
@ -1367,6 +1369,7 @@ struct ieee802_11_elems {
const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
const u8 *country_elem;
const u8 *pwr_constr_elem;
const u8 *cisco_dtpc_elem;
const struct ieee80211_timeout_interval_ie *timeout_int;
const u8 *opmode_notif;
const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
@ -1587,7 +1590,7 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
void __ieee80211_start_rx_ba_session(struct sta_info *sta,
u8 dialog_token, u16 timeout,
u16 start_seq_num, u16 ba_policy, u16 tid,
u16 buf_size, bool tx);
u16 buf_size, bool tx, bool auto_seq);
void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
enum ieee80211_agg_stop_reason reason);
void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
@ -1917,7 +1920,7 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
size_t extra_ies_len);
int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
const u8 *peer, enum nl80211_tdls_operation oper);
void ieee80211_tdls_peer_del_work(struct work_struct *wk);
extern const struct ethtool_ops ieee80211_ethtool_ops;
@ -1928,4 +1931,3 @@ extern const struct ethtool_ops ieee80211_ethtool_ops;
#endif
#endif /* IEEE80211_I_H */
void ieee80211_tdls_peer_del_work(struct work_struct *wk);

View file

@ -5,6 +5,7 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -1172,19 +1173,11 @@ static void ieee80211_iface_work(struct work_struct *work)
rx_agg = (void *)&skb->cb;
mutex_lock(&local->sta_mtx);
sta = sta_info_get_bss(sdata, rx_agg->addr);
if (sta) {
u16 last_seq;
last_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(
sta->last_seq_ctrl[rx_agg->tid]));
if (sta)
__ieee80211_start_rx_ba_session(sta,
0, 0,
ieee80211_sn_inc(last_seq),
1, rx_agg->tid,
0, 0, 0, 1, rx_agg->tid,
IEEE80211_MAX_AMPDU_BUF,
false);
}
false, true);
mutex_unlock(&local->sta_mtx);
} else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_STOP) {
rx_agg = (void *)&skb->cb;

View file

@ -3,6 +3,7 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2008 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -421,7 +422,7 @@ static void ieee80211_key_free_common(struct ieee80211_key *key)
ieee80211_aes_key_free(key->u.ccmp.tfm);
if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC)
ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm);
kfree(key);
kzfree(key);
}
static void __ieee80211_key_destroy(struct ieee80211_key *key,

View file

@ -2,6 +2,7 @@
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as

View file

@ -5,6 +5,7 @@
* Copyright 2005, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -172,7 +173,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
if (!(ht_cap->cap_info &
cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40))) {
ret = IEEE80211_STA_DISABLE_40MHZ | IEEE80211_STA_DISABLE_VHT;
ret = IEEE80211_STA_DISABLE_40MHZ;
goto out;
}
@ -672,6 +673,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
(local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
if (ifmgd->flags & IEEE80211_STA_ENABLE_RRM)
capab |= WLAN_CAPABILITY_RADIO_MEASURE;
mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
memset(mgmt, 0, 24);
memcpy(mgmt->da, assoc_data->bss->bssid, ETH_ALEN);
@ -737,16 +741,17 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
}
}
if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) {
/* 1. power capabilities */
if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT ||
capab & WLAN_CAPABILITY_RADIO_MEASURE) {
pos = skb_put(skb, 4);
*pos++ = WLAN_EID_PWR_CAPABILITY;
*pos++ = 2;
*pos++ = 0; /* min tx power */
/* max tx power */
*pos++ = ieee80211_chandef_max_power(&chanctx_conf->def);
}
/* 2. supported channels */
if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) {
/* TODO: get this in reg domain format */
pos = skb_put(skb, 2 * sband->n_channels + 2);
*pos++ = WLAN_EID_SUPPORTED_CHANNELS;
@ -1166,19 +1171,21 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
}
static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel,
const u8 *country_ie, u8 country_ie_len,
const u8 *pwr_constr_elem)
static bool
ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel,
const u8 *country_ie, u8 country_ie_len,
const u8 *pwr_constr_elem,
int *chan_pwr, int *pwr_reduction)
{
struct ieee80211_country_ie_triplet *triplet;
int chan = ieee80211_frequency_to_channel(channel->center_freq);
int i, chan_pwr, chan_increment, new_ap_level;
int i, chan_increment;
bool have_chan_pwr = false;
/* Invalid IE */
if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
return 0;
return false;
triplet = (void *)(country_ie + 3);
country_ie_len -= 3;
@ -1206,7 +1213,7 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
for (i = 0; i < triplet->chans.num_channels; i++) {
if (first_channel + i * chan_increment == chan) {
have_chan_pwr = true;
chan_pwr = triplet->chans.max_power;
*chan_pwr = triplet->chans.max_power;
break;
}
}
@ -1218,18 +1225,76 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
country_ie_len -= 3;
}
if (!have_chan_pwr)
if (have_chan_pwr)
*pwr_reduction = *pwr_constr_elem;
return have_chan_pwr;
}
static void ieee80211_find_cisco_dtpc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel,
const u8 *cisco_dtpc_ie,
int *pwr_level)
{
/* From practical testing, the first data byte of the DTPC element
* seems to contain the requested dBm level, and the CLI on Cisco
* APs clearly state the range is -127 to 127 dBm, which indicates
* a signed byte, although it seemingly never actually goes negative.
* The other byte seems to always be zero.
*/
*pwr_level = (__s8)cisco_dtpc_ie[4];
}
static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel,
struct ieee80211_mgmt *mgmt,
const u8 *country_ie, u8 country_ie_len,
const u8 *pwr_constr_ie,
const u8 *cisco_dtpc_ie)
{
bool has_80211h_pwr = false, has_cisco_pwr = false;
int chan_pwr = 0, pwr_reduction_80211h = 0;
int pwr_level_cisco, pwr_level_80211h;
int new_ap_level;
if (country_ie && pwr_constr_ie &&
mgmt->u.probe_resp.capab_info &
cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT)) {
has_80211h_pwr = ieee80211_find_80211h_pwr_constr(
sdata, channel, country_ie, country_ie_len,
pwr_constr_ie, &chan_pwr, &pwr_reduction_80211h);
pwr_level_80211h =
max_t(int, 0, chan_pwr - pwr_reduction_80211h);
}
if (cisco_dtpc_ie) {
ieee80211_find_cisco_dtpc(
sdata, channel, cisco_dtpc_ie, &pwr_level_cisco);
has_cisco_pwr = true;
}
if (!has_80211h_pwr && !has_cisco_pwr)
return 0;
new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem);
/* If we have both 802.11h and Cisco DTPC, apply both limits
* by picking the smallest of the two power levels advertised.
*/
if (has_80211h_pwr &&
(!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) {
sdata_info(sdata,
"Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
sdata->u.mgd.bssid);
new_ap_level = pwr_level_80211h;
} else { /* has_cisco_pwr is always true here. */
sdata_info(sdata,
"Limiting TX power to %d dBm as advertised by %pM\n",
pwr_level_cisco, sdata->u.mgd.bssid);
new_ap_level = pwr_level_cisco;
}
if (sdata->ap_power_level == new_ap_level)
return 0;
sdata_info(sdata,
"Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
new_ap_level, chan_pwr, *pwr_constr_elem,
sdata->u.mgd.bssid);
sdata->ap_power_level = new_ap_level;
if (__ieee80211_recalc_txpower(sdata))
return BSS_CHANGED_TXPOWER;
@ -2752,6 +2817,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
u16 capab_info, status_code, aid;
struct ieee802_11_elems elems;
int ac, uapsd_queues = -1;
u8 *pos;
bool reassoc;
struct cfg80211_bss *bss;
@ -2821,9 +2887,15 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
* is set can cause the interface to go idle
*/
ieee80211_destroy_assoc_data(sdata, true);
/* get uapsd queues configuration */
uapsd_queues = 0;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
if (sdata->tx_conf[ac].uapsd)
uapsd_queues |= BIT(ac);
}
cfg80211_rx_assoc_resp(sdata->dev, bss, (u8 *)mgmt, len);
cfg80211_rx_assoc_resp(sdata->dev, bss, (u8 *)mgmt, len, uapsd_queues);
}
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
@ -2893,7 +2965,9 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
/*
* This is the canonical list of information elements we care about,
* the filter code also gives us all changes to the Microsoft OUI
* (00:50:F2) vendor IE which is used for WMM which we need to track.
* (00:50:F2) vendor IE which is used for WMM which we need to track,
* as well as the DTPC IE (part of the Cisco OUI) used for signaling
* changes to requested client power.
*
* We implement beacon filtering in software since that means we can
* avoid processing the frame here and in cfg80211, and userspace
@ -3199,13 +3273,11 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
rx_status->band, true);
mutex_unlock(&local->sta_mtx);
if (elems.country_elem && elems.pwr_constr_elem &&
mgmt->u.probe_resp.capab_info &
cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT))
changed |= ieee80211_handle_pwr_constr(sdata, chan,
elems.country_elem,
elems.country_elem_len,
elems.pwr_constr_elem);
changed |= ieee80211_handle_pwr_constr(sdata, chan, mgmt,
elems.country_elem,
elems.country_elem_len,
elems.pwr_constr_elem,
elems.cisco_dtpc_elem);
ieee80211_bss_info_change_notify(sdata, changed);
}
@ -3733,7 +3805,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len;
ifmgd->p2p_noa_index = -1;
if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
if (sdata->local->hw.wiphy->features & NL80211_FEATURE_DYNAMIC_SMPS)
ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC;
else
ifmgd->req_smps = IEEE80211_SMPS_OFF;
@ -4408,6 +4480,11 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED;
}
if (req->flags & ASSOC_REQ_USE_RRM)
ifmgd->flags |= IEEE80211_STA_ENABLE_RRM;
else
ifmgd->flags &= ~IEEE80211_STA_ENABLE_RRM;
if (req->crypto.control_port)
ifmgd->flags |= IEEE80211_STA_CONTROL_PORT;
else

View file

@ -75,7 +75,7 @@ minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list)
{
int j = MAX_THR_RATES;
while (j > 0 && mi->r[i].cur_tp > mi->r[tp_list[j - 1]].cur_tp)
while (j > 0 && mi->r[i].stats.cur_tp > mi->r[tp_list[j - 1]].stats.cur_tp)
j--;
if (j < MAX_THR_RATES - 1)
memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1));
@ -92,7 +92,7 @@ minstrel_set_rate(struct minstrel_sta_info *mi, struct ieee80211_sta_rates *rate
ratetbl->rate[offset].idx = r->rix;
ratetbl->rate[offset].count = r->adjusted_retry_count;
ratetbl->rate[offset].count_cts = r->retry_count_cts;
ratetbl->rate[offset].count_rts = r->retry_count_rtscts;
ratetbl->rate[offset].count_rts = r->stats.retry_count_rtscts;
}
static void
@ -140,44 +140,46 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
for (i = 0; i < mi->n_rates; i++) {
struct minstrel_rate *mr = &mi->r[i];
struct minstrel_rate_stats *mrs = &mi->r[i].stats;
usecs = mr->perfect_tx_time;
if (!usecs)
usecs = 1000000;
if (unlikely(mr->attempts > 0)) {
mr->sample_skipped = 0;
mr->cur_prob = MINSTREL_FRAC(mr->success, mr->attempts);
mr->succ_hist += mr->success;
mr->att_hist += mr->attempts;
mr->probability = minstrel_ewma(mr->probability,
mr->cur_prob,
EWMA_LEVEL);
if (unlikely(mrs->attempts > 0)) {
mrs->sample_skipped = 0;
mrs->cur_prob = MINSTREL_FRAC(mrs->success,
mrs->attempts);
mrs->succ_hist += mrs->success;
mrs->att_hist += mrs->attempts;
mrs->probability = minstrel_ewma(mrs->probability,
mrs->cur_prob,
EWMA_LEVEL);
} else
mr->sample_skipped++;
mrs->sample_skipped++;
mr->last_success = mr->success;
mr->last_attempts = mr->attempts;
mr->success = 0;
mr->attempts = 0;
mrs->last_success = mrs->success;
mrs->last_attempts = mrs->attempts;
mrs->success = 0;
mrs->attempts = 0;
/* Update throughput per rate, reset thr. below 10% success */
if (mr->probability < MINSTREL_FRAC(10, 100))
mr->cur_tp = 0;
if (mrs->probability < MINSTREL_FRAC(10, 100))
mrs->cur_tp = 0;
else
mr->cur_tp = mr->probability * (1000000 / usecs);
mrs->cur_tp = mrs->probability * (1000000 / usecs);
/* Sample less often below the 10% chance of success.
* Sample less often above the 95% chance of success. */
if (mr->probability > MINSTREL_FRAC(95, 100) ||
mr->probability < MINSTREL_FRAC(10, 100)) {
mr->adjusted_retry_count = mr->retry_count >> 1;
if (mrs->probability > MINSTREL_FRAC(95, 100) ||
mrs->probability < MINSTREL_FRAC(10, 100)) {
mr->adjusted_retry_count = mrs->retry_count >> 1;
if (mr->adjusted_retry_count > 2)
mr->adjusted_retry_count = 2;
mr->sample_limit = 4;
} else {
mr->sample_limit = -1;
mr->adjusted_retry_count = mr->retry_count;
mr->adjusted_retry_count = mrs->retry_count;
}
if (!mr->adjusted_retry_count)
mr->adjusted_retry_count = 2;
@ -190,11 +192,11 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
* choose the maximum throughput rate as max_prob_rate
* (2) if all success probabilities < 95%, the rate with
* highest success probability is choosen as max_prob_rate */
if (mr->probability >= MINSTREL_FRAC(95, 100)) {
if (mr->cur_tp >= mi->r[tmp_prob_rate].cur_tp)
if (mrs->probability >= MINSTREL_FRAC(95, 100)) {
if (mrs->cur_tp >= mi->r[tmp_prob_rate].stats.cur_tp)
tmp_prob_rate = i;
} else {
if (mr->probability >= mi->r[tmp_prob_rate].probability)
if (mrs->probability >= mi->r[tmp_prob_rate].stats.probability)
tmp_prob_rate = i;
}
}
@ -240,14 +242,14 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,
if (ndx < 0)
continue;
mi->r[ndx].attempts += ar[i].count;
mi->r[ndx].stats.attempts += ar[i].count;
if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0))
mi->r[ndx].success += success;
mi->r[ndx].stats.success += success;
}
if ((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) && (i >= 0))
mi->sample_count++;
mi->sample_packets++;
if (mi->sample_deferred > 0)
mi->sample_deferred--;
@ -265,7 +267,7 @@ minstrel_get_retry_count(struct minstrel_rate *mr,
unsigned int retry = mr->adjusted_retry_count;
if (info->control.use_rts)
retry = max(2U, min(mr->retry_count_rtscts, retry));
retry = max(2U, min(mr->stats.retry_count_rtscts, retry));
else if (info->control.use_cts_prot)
retry = max(2U, min(mr->retry_count_cts, retry));
return retry;
@ -317,15 +319,15 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
sampling_ratio = mp->lookaround_rate;
/* increase sum packet counter */
mi->packet_count++;
mi->total_packets++;
#ifdef CONFIG_MAC80211_DEBUGFS
if (mp->fixed_rate_idx != -1)
return;
#endif
delta = (mi->packet_count * sampling_ratio / 100) -
(mi->sample_count + mi->sample_deferred / 2);
delta = (mi->total_packets * sampling_ratio / 100) -
(mi->sample_packets + mi->sample_deferred / 2);
/* delta < 0: no sampling required */
prev_sample = mi->prev_sample;
@ -333,10 +335,10 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
if (delta < 0 || (!mrr_capable && prev_sample))
return;
if (mi->packet_count >= 10000) {
if (mi->total_packets >= 10000) {
mi->sample_deferred = 0;
mi->sample_count = 0;
mi->packet_count = 0;
mi->sample_packets = 0;
mi->total_packets = 0;
} else if (delta > mi->n_rates * 2) {
/* With multi-rate retry, not every planned sample
* attempt actually gets used, due to the way the retry
@ -347,7 +349,7 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
* starts getting worse, minstrel would start bursting
* out lots of sampling frames, which would result
* in a large throughput loss. */
mi->sample_count += (delta - mi->n_rates * 2);
mi->sample_packets += (delta - mi->n_rates * 2);
}
/* get next random rate sample */
@ -361,7 +363,7 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
*/
if (mrr_capable &&
msr->perfect_tx_time > mr->perfect_tx_time &&
msr->sample_skipped < 20) {
msr->stats.sample_skipped < 20) {
/* Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark
* packets that have the sampling rate deferred to the
* second MRR stage. Increase the sample counter only
@ -375,7 +377,7 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
if (!msr->sample_limit != 0)
return;
mi->sample_count++;
mi->sample_packets++;
if (msr->sample_limit > 0)
msr->sample_limit--;
}
@ -384,7 +386,7 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
* has a probability of >95%, we shouldn't be attempting
* to use it, as this only wastes precious airtime */
if (!mrr_capable &&
(mi->r[ndx].probability > MINSTREL_FRAC(95, 100)))
(mi->r[ndx].stats.probability > MINSTREL_FRAC(95, 100)))
return;
mi->prev_sample = true;
@ -459,6 +461,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
for (i = 0; i < sband->n_bitrates; i++) {
struct minstrel_rate *mr = &mi->r[n];
struct minstrel_rate_stats *mrs = &mi->r[n].stats;
unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0;
unsigned int tx_time_single;
unsigned int cw = mp->cw_min;
@ -471,6 +474,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
n++;
memset(mr, 0, sizeof(*mr));
memset(mrs, 0, sizeof(*mrs));
mr->rix = i;
shift = ieee80211_chandef_get_shift(chandef);
@ -482,9 +486,9 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
/* calculate maximum number of retransmissions before
* fallback (based on maximum segment size) */
mr->sample_limit = -1;
mr->retry_count = 1;
mrs->retry_count = 1;
mr->retry_count_cts = 1;
mr->retry_count_rtscts = 1;
mrs->retry_count_rtscts = 1;
tx_time = mr->perfect_tx_time + mi->sp_ack_dur;
do {
/* add one retransmission */
@ -501,13 +505,13 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
(mr->retry_count_cts < mp->max_retry))
mr->retry_count_cts++;
if ((tx_time_rtscts < mp->segment_size) &&
(mr->retry_count_rtscts < mp->max_retry))
mr->retry_count_rtscts++;
(mrs->retry_count_rtscts < mp->max_retry))
mrs->retry_count_rtscts++;
} while ((tx_time < mp->segment_size) &&
(++mr->retry_count < mp->max_retry));
mr->adjusted_retry_count = mr->retry_count;
(++mr->stats.retry_count < mp->max_retry));
mr->adjusted_retry_count = mrs->retry_count;
if (!(sband->bitrates[i].flags & IEEE80211_RATE_ERP_G))
mr->retry_count_cts = mr->retry_count;
mr->retry_count_cts = mrs->retry_count;
}
for (i = n; i < sband->n_bitrates; i++) {
@ -665,7 +669,7 @@ static u32 minstrel_get_expected_throughput(void *priv_sta)
/* convert pkt per sec in kbps (1200 is the average pkt size used for
* computing cur_tp
*/
return MINSTREL_TRUNC(mi->r[idx].cur_tp) * 1200 * 8 / 1024;
return MINSTREL_TRUNC(mi->r[idx].stats.cur_tp) * 1200 * 8 / 1024;
}
const struct rate_control_ops mac80211_minstrel = {

View file

@ -31,6 +31,27 @@ minstrel_ewma(int old, int new, int weight)
return (new * (EWMA_DIV - weight) + old * weight) / EWMA_DIV;
}
struct minstrel_rate_stats {
/* current / last sampling period attempts/success counters */
unsigned int attempts, last_attempts;
unsigned int success, last_success;
/* total attempts/success counters */
u64 att_hist, succ_hist;
/* current throughput */
unsigned int cur_tp;
/* packet delivery probabilities */
unsigned int cur_prob, probability;
/* maximum retry counts */
unsigned int retry_count;
unsigned int retry_count_rtscts;
u8 sample_skipped;
bool retry_updated;
};
struct minstrel_rate {
int bitrate;
@ -40,26 +61,10 @@ struct minstrel_rate {
unsigned int ack_time;
int sample_limit;
unsigned int retry_count;
unsigned int retry_count_cts;
unsigned int retry_count_rtscts;
unsigned int adjusted_retry_count;
u32 success;
u32 attempts;
u32 last_attempts;
u32 last_success;
u8 sample_skipped;
/* parts per thousand */
u32 cur_prob;
u32 probability;
/* per-rate throughput */
u32 cur_tp;
u64 succ_hist;
u64 att_hist;
struct minstrel_rate_stats stats;
};
struct minstrel_sta_info {
@ -73,8 +78,8 @@ struct minstrel_sta_info {
u8 max_tp_rate[MAX_THR_RATES];
u8 max_prob_rate;
unsigned int packet_count;
unsigned int sample_count;
unsigned int total_packets;
unsigned int sample_packets;
int sample_deferred;
unsigned int sample_row;

View file

@ -72,6 +72,7 @@ minstrel_stats_open(struct inode *inode, struct file *file)
"this succ/attempt success attempts\n");
for (i = 0; i < mi->n_rates; i++) {
struct minstrel_rate *mr = &mi->r[i];
struct minstrel_rate_stats *mrs = &mi->r[i].stats;
*(p++) = (i == mi->max_tp_rate[0]) ? 'A' : ' ';
*(p++) = (i == mi->max_tp_rate[1]) ? 'B' : ' ';
@ -81,24 +82,24 @@ minstrel_stats_open(struct inode *inode, struct file *file)
p += sprintf(p, "%3u%s", mr->bitrate / 2,
(mr->bitrate & 1 ? ".5" : " "));
tp = MINSTREL_TRUNC(mr->cur_tp / 10);
prob = MINSTREL_TRUNC(mr->cur_prob * 1000);
eprob = MINSTREL_TRUNC(mr->probability * 1000);
tp = MINSTREL_TRUNC(mrs->cur_tp / 10);
prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
eprob = MINSTREL_TRUNC(mrs->probability * 1000);
p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u "
" %3u(%3u) %8llu %8llu\n",
tp / 10, tp % 10,
eprob / 10, eprob % 10,
prob / 10, prob % 10,
mr->last_success,
mr->last_attempts,
(unsigned long long)mr->succ_hist,
(unsigned long long)mr->att_hist);
mrs->last_success,
mrs->last_attempts,
(unsigned long long)mrs->succ_hist,
(unsigned long long)mrs->att_hist);
}
p += sprintf(p, "\nTotal packet count:: ideal %d "
"lookaround %d\n\n",
mi->packet_count - mi->sample_count,
mi->sample_count);
mi->total_packets - mi->sample_packets,
mi->sample_packets);
ms->len = p - ms->buf;
return 0;

View file

@ -135,7 +135,7 @@ minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);
static int
minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate)
{
return GROUP_IDX((rate->idx / 8) + 1,
return GROUP_IDX((rate->idx / MCS_GROUP_RATES) + 1,
!!(rate->flags & IEEE80211_TX_RC_SHORT_GI),
!!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH));
}
@ -232,13 +232,152 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
mr->cur_tp = MINSTREL_TRUNC(tp);
}
/*
* Find & sort topmost throughput rates
*
* If multiple rates provide equal throughput the sorting is based on their
* current success probability. Higher success probability is preferred among
* MCS groups, CCK rates do not provide aggregation and are therefore at last.
*/
static void
minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index,
u8 *tp_list)
{
int cur_group, cur_idx, cur_thr, cur_prob;
int tmp_group, tmp_idx, tmp_thr, tmp_prob;
int j = MAX_THR_RATES;
cur_group = index / MCS_GROUP_RATES;
cur_idx = index % MCS_GROUP_RATES;
cur_thr = mi->groups[cur_group].rates[cur_idx].cur_tp;
cur_prob = mi->groups[cur_group].rates[cur_idx].probability;
tmp_group = tp_list[j - 1] / MCS_GROUP_RATES;
tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES;
tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
while (j > 0 && (cur_thr > tmp_thr ||
(cur_thr == tmp_thr && cur_prob > tmp_prob))) {
j--;
tmp_group = tp_list[j - 1] / MCS_GROUP_RATES;
tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES;
tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
}
if (j < MAX_THR_RATES - 1) {
memmove(&tp_list[j + 1], &tp_list[j], (sizeof(*tp_list) *
(MAX_THR_RATES - (j + 1))));
}
if (j < MAX_THR_RATES)
tp_list[j] = index;
}
/*
* Find and set the topmost probability rate per sta and per group
*/
static void
minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u8 index)
{
struct minstrel_mcs_group_data *mg;
struct minstrel_rate_stats *mr;
int tmp_group, tmp_idx, tmp_tp, tmp_prob, max_tp_group;
mg = &mi->groups[index / MCS_GROUP_RATES];
mr = &mg->rates[index % MCS_GROUP_RATES];
tmp_group = mi->max_prob_rate / MCS_GROUP_RATES;
tmp_idx = mi->max_prob_rate % MCS_GROUP_RATES;
tmp_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
/* if max_tp_rate[0] is from MCS_GROUP max_prob_rate get selected from
* MCS_GROUP as well as CCK_GROUP rates do not allow aggregation */
max_tp_group = mi->max_tp_rate[0] / MCS_GROUP_RATES;
if((index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) &&
(max_tp_group != MINSTREL_CCK_GROUP))
return;
if (mr->probability > MINSTREL_FRAC(75, 100)) {
if (mr->cur_tp > tmp_tp)
mi->max_prob_rate = index;
if (mr->cur_tp > mg->rates[mg->max_group_prob_rate].cur_tp)
mg->max_group_prob_rate = index;
} else {
if (mr->probability > tmp_prob)
mi->max_prob_rate = index;
if (mr->probability > mg->rates[mg->max_group_prob_rate].probability)
mg->max_group_prob_rate = index;
}
}
/*
* Assign new rate set per sta and use CCK rates only if the fastest
* rate (max_tp_rate[0]) is from CCK group. This prohibits such sorted
* rate sets where MCS and CCK rates are mixed, because CCK rates can
* not use aggregation.
*/
static void
minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
u8 tmp_mcs_tp_rate[MAX_THR_RATES],
u8 tmp_cck_tp_rate[MAX_THR_RATES])
{
unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp;
int i;
tmp_group = tmp_cck_tp_rate[0] / MCS_GROUP_RATES;
tmp_idx = tmp_cck_tp_rate[0] % MCS_GROUP_RATES;
tmp_cck_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
tmp_group = tmp_mcs_tp_rate[0] / MCS_GROUP_RATES;
tmp_idx = tmp_mcs_tp_rate[0] % MCS_GROUP_RATES;
tmp_mcs_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
if (tmp_cck_tp > tmp_mcs_tp) {
for(i = 0; i < MAX_THR_RATES; i++) {
minstrel_ht_sort_best_tp_rates(mi, tmp_cck_tp_rate[i],
tmp_mcs_tp_rate);
}
}
}
/*
* Try to increase robustness of max_prob rate by decrease number of
* streams if possible.
*/
static inline void
minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
{
struct minstrel_mcs_group_data *mg;
struct minstrel_rate_stats *mr;
int tmp_max_streams, group;
int tmp_tp = 0;
tmp_max_streams = minstrel_mcs_groups[mi->max_tp_rate[0] /
MCS_GROUP_RATES].streams;
for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
mg = &mi->groups[group];
if (!mg->supported || group == MINSTREL_CCK_GROUP)
continue;
mr = minstrel_get_ratestats(mi, mg->max_group_prob_rate);
if (tmp_tp < mr->cur_tp &&
(minstrel_mcs_groups[group].streams < tmp_max_streams)) {
mi->max_prob_rate = mg->max_group_prob_rate;
tmp_tp = mr->cur_tp;
}
}
}
/*
* Update rate statistics and select new primary rates
*
* Rules for rate selection:
* - max_prob_rate must use only one stream, as a tradeoff between delivery
* probability and throughput during strong fluctuations
* - as long as the max prob rate has a probability of more than 3/4, pick
* - as long as the max prob rate has a probability of more than 75%, pick
* higher throughput rates, even if the probablity is a bit lower
*/
static void
@ -246,9 +385,9 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
{
struct minstrel_mcs_group_data *mg;
struct minstrel_rate_stats *mr;
int cur_prob, cur_prob_tp, cur_tp, cur_tp2;
int group, i, index;
bool mi_rates_valid = false;
int group, i, j;
u8 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
u8 tmp_cck_tp_rate[MAX_THR_RATES], index;
if (mi->ampdu_packets > 0) {
mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len,
@ -260,13 +399,14 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
mi->sample_slow = 0;
mi->sample_count = 0;
for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
bool mg_rates_valid = false;
/* Initialize global rate indexes */
for(j = 0; j < MAX_THR_RATES; j++){
tmp_mcs_tp_rate[j] = 0;
tmp_cck_tp_rate[j] = 0;
}
cur_prob = 0;
cur_prob_tp = 0;
cur_tp = 0;
cur_tp2 = 0;
/* Find best rate sets within all MCS groups*/
for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
mg = &mi->groups[group];
if (!mg->supported)
@ -274,24 +414,16 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
mi->sample_count++;
/* (re)Initialize group rate indexes */
for(j = 0; j < MAX_THR_RATES; j++)
tmp_group_tp_rate[j] = group;
for (i = 0; i < MCS_GROUP_RATES; i++) {
if (!(mg->supported & BIT(i)))
continue;
index = MCS_GROUP_RATES * group + i;
/* initialize rates selections starting indexes */
if (!mg_rates_valid) {
mg->max_tp_rate = mg->max_tp_rate2 =
mg->max_prob_rate = i;
if (!mi_rates_valid) {
mi->max_tp_rate = mi->max_tp_rate2 =
mi->max_prob_rate = index;
mi_rates_valid = true;
}
mg_rates_valid = true;
}
mr = &mg->rates[i];
mr->retry_updated = false;
minstrel_calc_rate_ewma(mr);
@ -300,82 +432,47 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
if (!mr->cur_tp)
continue;
if ((mr->cur_tp > cur_prob_tp && mr->probability >
MINSTREL_FRAC(3, 4)) || mr->probability > cur_prob) {
mg->max_prob_rate = index;
cur_prob = mr->probability;
cur_prob_tp = mr->cur_tp;
/* Find max throughput rate set */
if (group != MINSTREL_CCK_GROUP) {
minstrel_ht_sort_best_tp_rates(mi, index,
tmp_mcs_tp_rate);
} else if (group == MINSTREL_CCK_GROUP) {
minstrel_ht_sort_best_tp_rates(mi, index,
tmp_cck_tp_rate);
}
if (mr->cur_tp > cur_tp) {
swap(index, mg->max_tp_rate);
cur_tp = mr->cur_tp;
mr = minstrel_get_ratestats(mi, index);
}
/* Find max throughput rate set within a group */
minstrel_ht_sort_best_tp_rates(mi, index,
tmp_group_tp_rate);
if (index >= mg->max_tp_rate)
continue;
if (mr->cur_tp > cur_tp2) {
mg->max_tp_rate2 = index;
cur_tp2 = mr->cur_tp;
}
/* Find max probability rate per group and global */
minstrel_ht_set_best_prob_rate(mi, index);
}
memcpy(mg->max_group_tp_rate, tmp_group_tp_rate,
sizeof(mg->max_group_tp_rate));
}
/* Assign new rate set per sta */
minstrel_ht_assign_best_tp_rates(mi, tmp_mcs_tp_rate, tmp_cck_tp_rate);
memcpy(mi->max_tp_rate, tmp_mcs_tp_rate, sizeof(mi->max_tp_rate));
/* Try to increase robustness of max_prob_rate*/
minstrel_ht_prob_rate_reduce_streams(mi);
/* try to sample all available rates during each interval */
mi->sample_count *= 8;
cur_prob = 0;
cur_prob_tp = 0;
cur_tp = 0;
cur_tp2 = 0;
for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
mg = &mi->groups[group];
if (!mg->supported)
continue;
mr = minstrel_get_ratestats(mi, mg->max_tp_rate);
if (cur_tp < mr->cur_tp) {
mi->max_tp_rate2 = mi->max_tp_rate;
cur_tp2 = cur_tp;
mi->max_tp_rate = mg->max_tp_rate;
cur_tp = mr->cur_tp;
mi->max_prob_streams = minstrel_mcs_groups[group].streams - 1;
}
mr = minstrel_get_ratestats(mi, mg->max_tp_rate2);
if (cur_tp2 < mr->cur_tp) {
mi->max_tp_rate2 = mg->max_tp_rate2;
cur_tp2 = mr->cur_tp;
}
}
if (mi->max_prob_streams < 1)
mi->max_prob_streams = 1;
for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
mg = &mi->groups[group];
if (!mg->supported)
continue;
mr = minstrel_get_ratestats(mi, mg->max_prob_rate);
if (cur_prob_tp < mr->cur_tp &&
minstrel_mcs_groups[group].streams <= mi->max_prob_streams) {
mi->max_prob_rate = mg->max_prob_rate;
cur_prob = mr->cur_prob;
cur_prob_tp = mr->cur_tp;
}
}
#ifdef CONFIG_MAC80211_DEBUGFS
/* use fixed index if set */
if (mp->fixed_rate_idx != -1) {
mi->max_tp_rate = mp->fixed_rate_idx;
mi->max_tp_rate2 = mp->fixed_rate_idx;
for (i = 0; i < 4; i++)
mi->max_tp_rate[i] = mp->fixed_rate_idx;
mi->max_prob_rate = mp->fixed_rate_idx;
}
#endif
/* Reset update timer */
mi->stats_update = jiffies;
}
@ -420,8 +517,7 @@ minstrel_next_sample_idx(struct minstrel_ht_sta *mi)
}
static void
minstrel_downgrade_rate(struct minstrel_ht_sta *mi, unsigned int *idx,
bool primary)
minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u8 *idx, bool primary)
{
int group, orig_group;
@ -437,9 +533,9 @@ minstrel_downgrade_rate(struct minstrel_ht_sta *mi, unsigned int *idx,
continue;
if (primary)
*idx = mi->groups[group].max_tp_rate;
*idx = mi->groups[group].max_group_tp_rate[0];
else
*idx = mi->groups[group].max_tp_rate2;
*idx = mi->groups[group].max_group_tp_rate[1];
break;
}
}
@ -524,19 +620,19 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
* check for sudden death of spatial multiplexing,
* downgrade to a lower number of streams if necessary.
*/
rate = minstrel_get_ratestats(mi, mi->max_tp_rate);
rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
if (rate->attempts > 30 &&
MINSTREL_FRAC(rate->success, rate->attempts) <
MINSTREL_FRAC(20, 100)) {
minstrel_downgrade_rate(mi, &mi->max_tp_rate, true);
minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true);
update = true;
}
rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate2);
rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]);
if (rate2->attempts > 30 &&
MINSTREL_FRAC(rate2->success, rate2->attempts) <
MINSTREL_FRAC(20, 100)) {
minstrel_downgrade_rate(mi, &mi->max_tp_rate2, false);
minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false);
update = true;
}
@ -661,12 +757,12 @@ minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
if (!rates)
return;
/* Start with max_tp_rate */
minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate);
/* Start with max_tp_rate[0] */
minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate[0]);
if (mp->hw->max_rates >= 3) {
/* At least 3 tx rates supported, use max_tp_rate2 next */
minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate2);
/* At least 3 tx rates supported, use max_tp_rate[1] next */
minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate[1]);
}
if (mp->hw->max_rates >= 2) {
@ -691,7 +787,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
{
struct minstrel_rate_stats *mr;
struct minstrel_mcs_group_data *mg;
unsigned int sample_dur, sample_group;
unsigned int sample_dur, sample_group, cur_max_tp_streams;
int sample_idx = 0;
if (mi->sample_wait > 0) {
@ -718,8 +814,8 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
* to the frame. Hence, don't use sampling for the currently
* used rates.
*/
if (sample_idx == mi->max_tp_rate ||
sample_idx == mi->max_tp_rate2 ||
if (sample_idx == mi->max_tp_rate[0] ||
sample_idx == mi->max_tp_rate[1] ||
sample_idx == mi->max_prob_rate)
return -1;
@ -734,9 +830,12 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
* Make sure that lower rates get sampled only occasionally,
* if the link is working perfectly.
*/
cur_max_tp_streams = minstrel_mcs_groups[mi->max_tp_rate[0] /
MCS_GROUP_RATES].streams;
sample_dur = minstrel_get_duration(sample_idx);
if (sample_dur >= minstrel_get_duration(mi->max_tp_rate2) &&
(mi->max_prob_streams <
if (sample_dur >= minstrel_get_duration(mi->max_tp_rate[1]) &&
(cur_max_tp_streams - 1 <
minstrel_mcs_groups[sample_group].streams ||
sample_dur >= minstrel_get_duration(mi->max_prob_rate))) {
if (mr->sample_skipped < 20)
@ -1041,8 +1140,8 @@ static u32 minstrel_ht_get_expected_throughput(void *priv_sta)
if (!msp->is_ht)
return mac80211_minstrel.get_expected_throughput(priv_sta);
i = mi->max_tp_rate / MCS_GROUP_RATES;
j = mi->max_tp_rate % MCS_GROUP_RATES;
i = mi->max_tp_rate[0] / MCS_GROUP_RATES;
j = mi->max_tp_rate[0] % MCS_GROUP_RATES;
/* convert cur_tp from pkt per second in kbps */
return mi->groups[i].rates[j].cur_tp * AVG_PKT_SIZE * 8 / 1024;

View file

@ -26,28 +26,6 @@ struct mcs_group {
extern const struct mcs_group minstrel_mcs_groups[];
struct minstrel_rate_stats {
/* current / last sampling period attempts/success counters */
unsigned int attempts, last_attempts;
unsigned int success, last_success;
/* total attempts/success counters */
u64 att_hist, succ_hist;
/* current throughput */
unsigned int cur_tp;
/* packet delivery probabilities */
unsigned int cur_prob, probability;
/* maximum retry counts */
unsigned int retry_count;
unsigned int retry_count_rtscts;
bool retry_updated;
u8 sample_skipped;
};
struct minstrel_mcs_group_data {
u8 index;
u8 column;
@ -55,10 +33,9 @@ struct minstrel_mcs_group_data {
/* bitfield of supported MCS rates of this group */
u8 supported;
/* selected primary rates */
unsigned int max_tp_rate;
unsigned int max_tp_rate2;
unsigned int max_prob_rate;
/* sorted rate set within a MCS group*/
u8 max_group_tp_rate[MAX_THR_RATES];
u8 max_group_prob_rate;
/* MCS rate statistics */
struct minstrel_rate_stats rates[MCS_GROUP_RATES];
@ -74,15 +51,9 @@ struct minstrel_ht_sta {
/* ampdu length (EWMA) */
unsigned int avg_ampdu_len;
/* best throughput rate */
unsigned int max_tp_rate;
/* second best throughput rate */
unsigned int max_tp_rate2;
/* best probability rate */
unsigned int max_prob_rate;
unsigned int max_prob_streams;
/* overall sorted rate set */
u8 max_tp_rate[MAX_THR_RATES];
u8 max_prob_rate;
/* time of last status update */
unsigned long stats_update;

View file

@ -46,8 +46,10 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
else
p += sprintf(p, "HT%c0/%cGI ", htmode, gimode);
*(p++) = (idx == mi->max_tp_rate) ? 'T' : ' ';
*(p++) = (idx == mi->max_tp_rate2) ? 't' : ' ';
*(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' ';
*(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' ';
*(p++) = (idx == mi->max_tp_rate[2]) ? 'C' : ' ';
*(p++) = (idx == mi->max_tp_rate[3]) ? 'D' : ' ';
*(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';
if (i == max_mcs) {
@ -100,8 +102,8 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
file->private_data = ms;
p = ms->buf;
p += sprintf(p, "type rate throughput ewma prob this prob "
"retry this succ/attempt success attempts\n");
p += sprintf(p, "type rate throughput ewma prob "
"this prob retry this succ/attempt success attempts\n");
p = minstrel_ht_stats_dump(mi, max_mcs, p);
for (i = 0; i < max_mcs; i++)

View file

@ -3,6 +3,7 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -835,6 +836,16 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
spin_lock(&tid_agg_rx->reorder_lock);
/*
* Offloaded BA sessions have no known starting sequence number so pick
* one from first Rxed frame for this tid after BA was started.
*/
if (unlikely(tid_agg_rx->auto_seq)) {
tid_agg_rx->auto_seq = false;
tid_agg_rx->ssn = mpdu_seq_num;
tid_agg_rx->head_seq_num = mpdu_seq_num;
}
buf_size = tid_agg_rx->buf_size;
head_seq_num = tid_agg_rx->head_seq_num;

View file

@ -6,6 +6,7 @@
* Copyright 2005, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as

View file

@ -1,6 +1,7 @@
/*
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as

View file

@ -1,5 +1,6 @@
/*
* Copyright 2002-2005, Devicescape Software, Inc.
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -167,6 +168,8 @@ struct tid_ampdu_tx {
* @dialog_token: dialog token for aggregation session
* @rcu_head: RCU head used for freeing this struct
* @reorder_lock: serializes access to reorder buffer, see below.
* @auto_seq: used for offloaded BA sessions to automatically pick head_seq_and
* and ssn.
*
* This structure's lifetime is managed by RCU, assignments to
* the array holding it must hold the aggregation mutex.
@ -190,6 +193,7 @@ struct tid_ampdu_rx {
u16 buf_size;
u16 timeout;
u8 dialog_token;
bool auto_seq;
};
/**
@ -446,6 +450,9 @@ struct sta_info {
enum ieee80211_smps_mode known_smps_mode;
const struct ieee80211_cipher_scheme *cipher_scheme;
/* TDLS timeout data */
unsigned long last_tdls_pkt_time;
/* keep last! */
struct ieee80211_sta sta;
};

View file

@ -3,6 +3,7 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2008-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -537,6 +538,8 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
* - current throughput (higher value for higher tpt)?
*/
#define STA_LOST_PKT_THRESHOLD 50
#define STA_LOST_TDLS_PKT_THRESHOLD 10
#define STA_LOST_TDLS_PKT_TIME (10*HZ) /* 10secs since last ACK */
static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb)
{
@ -547,7 +550,20 @@ static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb)
!(info->flags & IEEE80211_TX_STAT_AMPDU))
return;
if (++sta->lost_packets < STA_LOST_PKT_THRESHOLD)
sta->lost_packets++;
if (!sta->sta.tdls && sta->lost_packets < STA_LOST_PKT_THRESHOLD)
return;
/*
* If we're in TDLS mode, make sure that all STA_LOST_TDLS_PKT_THRESHOLD
* of the last packets were lost, and that no ACK was received in the
* last STA_LOST_TDLS_PKT_TIME ms, before triggering the CQM packet-loss
* mechanism.
*/
if (sta->sta.tdls &&
(sta->lost_packets < STA_LOST_TDLS_PKT_THRESHOLD ||
time_before(jiffies,
sta->last_tdls_pkt_time + STA_LOST_TDLS_PKT_TIME)))
return;
cfg80211_cqm_pktloss_notify(sta->sdata->dev, sta->sta.addr,
@ -694,6 +710,10 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
if (info->flags & IEEE80211_TX_STAT_ACK) {
if (sta->lost_packets)
sta->lost_packets = 0;
/* Track when last TDLS packet was ACKed */
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
sta->last_tdls_pkt_time = jiffies;
} else {
ieee80211_lost_packet(sta, skb);
}

View file

@ -3,6 +3,7 @@
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2014, Intel Corporation
* Copyright 2014 Intel Mobile Communications GmbH
*
* This file is GPLv2 as found in COPYING.
*/
@ -411,6 +412,9 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
tf->ether_type = cpu_to_be16(ETH_P_TDLS);
tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
/* network header is after the ethernet header */
skb_set_network_header(skb, ETH_HLEN);
switch (action_code) {
case WLAN_TDLS_SETUP_REQUEST:
tf->category = WLAN_CATEGORY_TDLS;

View file

@ -672,13 +672,13 @@ DEFINE_EVENT(local_u32_evt, drv_set_rts_threshold,
);
TRACE_EVENT(drv_set_coverage_class,
TP_PROTO(struct ieee80211_local *local, u8 value),
TP_PROTO(struct ieee80211_local *local, s16 value),
TP_ARGS(local, value),
TP_STRUCT__entry(
LOCAL_ENTRY
__field(u8, value)
__field(s16, value)
),
TP_fast_assign(

View file

@ -3,6 +3,7 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -1788,9 +1789,8 @@ static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local,
* @skb: packet to be sent
* @dev: incoming interface
*
* Returns: 0 on success (and frees skb in this case) or 1 on failure (skb will
* not be freed, and caller is responsible for either retrying later or freeing
* skb).
* Returns: NETDEV_TX_OK both on success and on failure. On failure skb will
* be freed.
*
* This function takes in an Ethernet header and encapsulates it with suitable
* IEEE 802.11 header based on which interface the packet is coming in. The

View file

@ -3,6 +3,7 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -1014,6 +1015,31 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
}
elems->pwr_constr_elem = pos;
break;
case WLAN_EID_CISCO_VENDOR_SPECIFIC:
/* Lots of different options exist, but we only care
* about the Dynamic Transmit Power Control element.
* First check for the Cisco OUI, then for the DTPC
* tag (0x00).
*/
if (elen < 4) {
elem_parse_failed = true;
break;
}
if (pos[0] != 0x00 || pos[1] != 0x40 ||
pos[2] != 0x96 || pos[3] != 0x00)
break;
if (elen != 6) {
elem_parse_failed = true;
break;
}
if (calc_crc)
crc = crc32_be(crc, pos - 2, elen + 2);
elems->cisco_dtpc_elem = pos;
break;
case WLAN_EID_TIMEOUT_INTERVAL:
if (elen >= sizeof(struct ieee80211_timeout_interval_ie))
elems->timeout_int = (void *)pos;

View file

@ -1,5 +1,6 @@
/*
* Copyright 2004, Instant802 Networks, Inc.
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as

View file

@ -64,8 +64,11 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
if (!info->control.hw_key)
tail += IEEE80211_TKIP_ICV_LEN;
if (WARN_ON(skb_tailroom(skb) < tail ||
skb_headroom(skb) < IEEE80211_TKIP_IV_LEN))
if (WARN(skb_tailroom(skb) < tail ||
skb_headroom(skb) < IEEE80211_TKIP_IV_LEN,
"mmic: not enough head/tail (%d/%d,%d/%d)\n",
skb_headroom(skb), IEEE80211_TKIP_IV_LEN,
skb_tailroom(skb), tail))
return TX_DROP;
key = &tx->key->conf.key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY];

View file

@ -4,6 +4,7 @@
* any point in time.
*
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*/
#include <linux/export.h>

View file

@ -2,6 +2,7 @@
* This is the linux wireless configuration interface.
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@ -1005,7 +1006,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
rdev->devlist_generation++;
cfg80211_mlme_purge_registrations(wdev);
#ifdef CONFIG_CFG80211_WEXT
kfree(wdev->wext.keys);
kzfree(wdev->wext.keys);
#endif
}
/*

View file

@ -115,7 +115,7 @@ static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
}
if (WARN_ON(wdev->connect_keys))
kfree(wdev->connect_keys);
kzfree(wdev->connect_keys);
wdev->connect_keys = connkeys;
wdev->ibss_fixed = params->channel_fixed;
@ -161,7 +161,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
ASSERT_WDEV_LOCK(wdev);
kfree(wdev->connect_keys);
kzfree(wdev->connect_keys);
wdev->connect_keys = NULL;
rdev_set_qos_map(rdev, dev, NULL);

View file

@ -19,7 +19,7 @@
void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
const u8 *buf, size_t len)
const u8 *buf, size_t len, int uapsd_queues)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
@ -43,7 +43,7 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
return;
}
nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL);
nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues);
/* update current_bss etc., consumes the bss reference */
__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
status_code,

View file

@ -2,6 +2,7 @@
* This is the new netlink-based wireless configuration interface.
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*/
#include <linux/if.h>
@ -225,6 +226,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
[NL80211_ATTR_WIPHY_DYN_ACK] = { .type = NLA_FLAG },
[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
@ -388,6 +390,11 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
[NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG },
[NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY },
[NL80211_ATTR_USE_RRM] = { .type = NLA_FLAG },
[NL80211_ATTR_TSID] = { .type = NLA_U8 },
[NL80211_ATTR_USER_PRIO] = { .type = NLA_U8 },
[NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
};
/* policy for the key attributes */
@ -1507,6 +1514,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
CMD(channel_switch, CHANNEL_SWITCH);
CMD(set_qos_map, SET_QOS_MAP);
if (rdev->wiphy.flags &
WIPHY_FLAG_SUPPORTS_WMM_ADMISSION)
CMD(add_tx_ts, ADD_TX_TS);
}
/* add into the if now */
#undef CMD
@ -2237,11 +2247,21 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
}
if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK])
return -EINVAL;
coverage_class = nla_get_u8(
info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
changed |= WIPHY_PARAM_COVERAGE_CLASS;
}
if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK]) {
if (!(rdev->wiphy.features & NL80211_FEATURE_ACKTO_ESTIMATION))
return -EOPNOTSUPP;
changed |= WIPHY_PARAM_DYN_ACK;
}
if (changed) {
u8 old_retry_short, old_retry_long;
u32 old_frag_threshold, old_rts_threshold;
@ -3326,6 +3346,29 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
return PTR_ERR(params.acl);
}
if (info->attrs[NL80211_ATTR_SMPS_MODE]) {
params.smps_mode =
nla_get_u8(info->attrs[NL80211_ATTR_SMPS_MODE]);
switch (params.smps_mode) {
case NL80211_SMPS_OFF:
break;
case NL80211_SMPS_STATIC:
if (!(rdev->wiphy.features &
NL80211_FEATURE_STATIC_SMPS))
return -EINVAL;
break;
case NL80211_SMPS_DYNAMIC:
if (!(rdev->wiphy.features &
NL80211_FEATURE_DYNAMIC_SMPS))
return -EINVAL;
break;
default:
return -EINVAL;
}
} else {
params.smps_mode = NL80211_SMPS_OFF;
}
wdev_lock(wdev);
err = rdev_start_ap(rdev, dev, &params);
if (!err) {
@ -6583,6 +6626,14 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
sizeof(req.vht_capa));
}
if (nla_get_flag(info->attrs[NL80211_ATTR_USE_RRM])) {
if (!(rdev->wiphy.features &
NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) ||
!(rdev->wiphy.features & NL80211_FEATURE_QUIET))
return -EINVAL;
req.flags |= ASSOC_REQ_USE_RRM;
}
err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
if (!err) {
wdev_lock(dev->ieee80211_ptr);
@ -6845,7 +6896,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
if (err)
kfree(connkeys);
kzfree(connkeys);
return err;
}
@ -7214,7 +7265,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) {
kfree(connkeys);
kzfree(connkeys);
return -EINVAL;
}
memcpy(&connect.ht_capa,
@ -7232,7 +7283,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) {
kfree(connkeys);
kzfree(connkeys);
return -EINVAL;
}
memcpy(&connect.vht_capa,
@ -7240,11 +7291,19 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
sizeof(connect.vht_capa));
}
if (nla_get_flag(info->attrs[NL80211_ATTR_USE_RRM])) {
if (!(rdev->wiphy.features &
NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) ||
!(rdev->wiphy.features & NL80211_FEATURE_QUIET))
return -EINVAL;
connect.flags |= ASSOC_REQ_USE_RRM;
}
wdev_lock(dev->ieee80211_ptr);
err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL);
wdev_unlock(dev->ieee80211_ptr);
if (err)
kfree(connkeys);
kzfree(connkeys);
return err;
}
@ -8930,13 +8989,9 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
return -ERANGE;
memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]),
NL80211_KEK_LEN);
memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]),
NL80211_KCK_LEN);
memcpy(rekey_data.replay_ctr,
nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]),
NL80211_REPLAY_CTR_LEN);
rekey_data.kek = nla_data(tb[NL80211_REKEY_DATA_KEK]);
rekey_data.kck = nla_data(tb[NL80211_REKEY_DATA_KCK]);
rekey_data.replay_ctr = nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]);
wdev_lock(wdev);
if (!wdev->current_bss) {
@ -9365,6 +9420,93 @@ static int nl80211_set_qos_map(struct sk_buff *skb,
return ret;
}
static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
struct wireless_dev *wdev = dev->ieee80211_ptr;
const u8 *peer;
u8 tsid, up;
u16 admitted_time = 0;
int err;
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION))
return -EOPNOTSUPP;
if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC] ||
!info->attrs[NL80211_ATTR_USER_PRIO])
return -EINVAL;
tsid = nla_get_u8(info->attrs[NL80211_ATTR_TSID]);
if (tsid >= IEEE80211_NUM_TIDS)
return -EINVAL;
up = nla_get_u8(info->attrs[NL80211_ATTR_USER_PRIO]);
if (up >= IEEE80211_NUM_UPS)
return -EINVAL;
/* WMM uses TIDs 0-7 even for TSPEC */
if (tsid < IEEE80211_FIRST_TSPEC_TSID) {
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION))
return -EINVAL;
} else {
/* TODO: handle 802.11 TSPEC/admission control
* need more attributes for that (e.g. BA session requirement)
*/
return -EINVAL;
}
peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
if (info->attrs[NL80211_ATTR_ADMITTED_TIME]) {
admitted_time =
nla_get_u16(info->attrs[NL80211_ATTR_ADMITTED_TIME]);
if (!admitted_time)
return -EINVAL;
}
wdev_lock(wdev);
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
if (wdev->current_bss)
break;
err = -ENOTCONN;
goto out;
default:
err = -EOPNOTSUPP;
goto out;
}
err = rdev_add_tx_ts(rdev, dev, tsid, peer, up, admitted_time);
out:
wdev_unlock(wdev);
return err;
}
static int nl80211_del_tx_ts(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
struct wireless_dev *wdev = dev->ieee80211_ptr;
const u8 *peer;
u8 tsid;
int err;
if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC])
return -EINVAL;
tsid = nla_get_u8(info->attrs[NL80211_ATTR_TSID]);
peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
wdev_lock(wdev);
err = rdev_del_tx_ts(rdev, dev, tsid, peer);
wdev_unlock(wdev);
return err;
}
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@ -9375,6 +9517,7 @@ static int nl80211_set_qos_map(struct sk_buff *skb,
/* If a netdev is associated, it must be UP, P2P must be started */
#define NL80211_FLAG_NEED_WDEV_UP (NL80211_FLAG_NEED_WDEV |\
NL80211_FLAG_CHECK_NETDEV_UP)
#define NL80211_FLAG_CLEAR_SKB 0x20
static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
struct genl_info *info)
@ -9458,8 +9601,20 @@ static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
dev_put(info->user_ptr[1]);
}
}
if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
rtnl_unlock();
/* If needed, clear the netlink message payload from the SKB
* as it might contain key data that shouldn't stick around on
* the heap after the SKB is freed. The netlink message header
* is still needed for further processing, so leave it intact.
*/
if (ops->internal_flags & NL80211_FLAG_CLEAR_SKB) {
struct nlmsghdr *nlh = nlmsg_hdr(skb);
memset(nlmsg_data(nlh), 0, nlmsg_len(nlh));
}
}
static const struct genl_ops nl80211_ops[] = {
@ -9527,7 +9682,8 @@ static const struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
NL80211_FLAG_NEED_RTNL |
NL80211_FLAG_CLEAR_SKB,
},
{
.cmd = NL80211_CMD_NEW_KEY,
@ -9535,7 +9691,8 @@ static const struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
NL80211_FLAG_NEED_RTNL |
NL80211_FLAG_CLEAR_SKB,
},
{
.cmd = NL80211_CMD_DEL_KEY,
@ -9713,7 +9870,8 @@ static const struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
NL80211_FLAG_NEED_RTNL |
NL80211_FLAG_CLEAR_SKB,
},
{
.cmd = NL80211_CMD_ASSOCIATE,
@ -9947,7 +10105,8 @@ static const struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
NL80211_FLAG_NEED_RTNL |
NL80211_FLAG_CLEAR_SKB,
},
{
.cmd = NL80211_CMD_TDLS_MGMT,
@ -10105,6 +10264,22 @@ static const struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_ADD_TX_TS,
.doit = nl80211_add_tx_ts,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
{
.cmd = NL80211_CMD_DEL_TX_TS,
.doit = nl80211_del_tx_ts,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
};
/* notification functions */
@ -10373,7 +10548,8 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)
static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
const u8 *buf, size_t len,
enum nl80211_commands cmd, gfp_t gfp)
enum nl80211_commands cmd, gfp_t gfp,
int uapsd_queues)
{
struct sk_buff *msg;
void *hdr;
@ -10393,6 +10569,19 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
nla_put(msg, NL80211_ATTR_FRAME, len, buf))
goto nla_put_failure;
if (uapsd_queues >= 0) {
struct nlattr *nla_wmm =
nla_nest_start(msg, NL80211_ATTR_STA_WME);
if (!nla_wmm)
goto nla_put_failure;
if (nla_put_u8(msg, NL80211_STA_WME_UAPSD_QUEUES,
uapsd_queues))
goto nla_put_failure;
nla_nest_end(msg, nla_wmm);
}
genlmsg_end(msg, hdr);
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
@ -10409,15 +10598,15 @@ void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
size_t len, gfp_t gfp)
{
nl80211_send_mlme_event(rdev, netdev, buf, len,
NL80211_CMD_AUTHENTICATE, gfp);
NL80211_CMD_AUTHENTICATE, gfp, -1);
}
void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *buf,
size_t len, gfp_t gfp)
size_t len, gfp_t gfp, int uapsd_queues)
{
nl80211_send_mlme_event(rdev, netdev, buf, len,
NL80211_CMD_ASSOCIATE, gfp);
NL80211_CMD_ASSOCIATE, gfp, uapsd_queues);
}
void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
@ -10425,7 +10614,7 @@ void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
size_t len, gfp_t gfp)
{
nl80211_send_mlme_event(rdev, netdev, buf, len,
NL80211_CMD_DEAUTHENTICATE, gfp);
NL80211_CMD_DEAUTHENTICATE, gfp, -1);
}
void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
@ -10433,7 +10622,7 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
size_t len, gfp_t gfp)
{
nl80211_send_mlme_event(rdev, netdev, buf, len,
NL80211_CMD_DISASSOCIATE, gfp);
NL80211_CMD_DISASSOCIATE, gfp, -1);
}
void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf,
@ -10454,7 +10643,7 @@ void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf,
cmd = NL80211_CMD_UNPROT_DISASSOCIATE;
trace_cfg80211_rx_unprot_mlme_mgmt(dev, buf, len);
nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC);
nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC, -1);
}
EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt);

View file

@ -23,7 +23,8 @@ void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
const u8 *buf, size_t len, gfp_t gfp);
void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
const u8 *buf, size_t len, gfp_t gfp);
const u8 *buf, size_t len, gfp_t gfp,
int uapsd_queues);
void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
const u8 *buf, size_t len, gfp_t gfp);

View file

@ -915,4 +915,35 @@ rdev_set_ap_chanwidth(struct cfg80211_registered_device *rdev,
return ret;
}
static inline int
rdev_add_tx_ts(struct cfg80211_registered_device *rdev,
struct net_device *dev, u8 tsid, const u8 *peer,
u8 user_prio, u16 admitted_time)
{
int ret = -EOPNOTSUPP;
trace_rdev_add_tx_ts(&rdev->wiphy, dev, tsid, peer,
user_prio, admitted_time);
if (rdev->ops->add_tx_ts)
ret = rdev->ops->add_tx_ts(&rdev->wiphy, dev, tsid, peer,
user_prio, admitted_time);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
static inline int
rdev_del_tx_ts(struct cfg80211_registered_device *rdev,
struct net_device *dev, u8 tsid, const u8 *peer)
{
int ret = -EOPNOTSUPP;
trace_rdev_del_tx_ts(&rdev->wiphy, dev, tsid, peer);
if (rdev->ops->del_tx_ts)
ret = rdev->ops->del_tx_ts(&rdev->wiphy, dev, tsid, peer);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
#endif /* __CFG80211_RDEV_OPS */

View file

@ -3,6 +3,7 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -798,6 +799,57 @@ static int reg_rules_intersect(const struct ieee80211_regdomain *rd1,
return 0;
}
/* check whether old rule contains new rule */
static bool rule_contains(struct ieee80211_reg_rule *r1,
struct ieee80211_reg_rule *r2)
{
/* for simplicity, currently consider only same flags */
if (r1->flags != r2->flags)
return false;
/* verify r1 is more restrictive */
if ((r1->power_rule.max_antenna_gain >
r2->power_rule.max_antenna_gain) ||
r1->power_rule.max_eirp > r2->power_rule.max_eirp)
return false;
/* make sure r2's range is contained within r1 */
if (r1->freq_range.start_freq_khz > r2->freq_range.start_freq_khz ||
r1->freq_range.end_freq_khz < r2->freq_range.end_freq_khz)
return false;
/* and finally verify that r1.max_bw >= r2.max_bw */
if (r1->freq_range.max_bandwidth_khz <
r2->freq_range.max_bandwidth_khz)
return false;
return true;
}
/* add or extend current rules. do nothing if rule is already contained */
static void add_rule(struct ieee80211_reg_rule *rule,
struct ieee80211_reg_rule *reg_rules, u32 *n_rules)
{
struct ieee80211_reg_rule *tmp_rule;
int i;
for (i = 0; i < *n_rules; i++) {
tmp_rule = &reg_rules[i];
/* rule is already contained - do nothing */
if (rule_contains(tmp_rule, rule))
return;
/* extend rule if possible */
if (rule_contains(rule, tmp_rule)) {
memcpy(tmp_rule, rule, sizeof(*rule));
return;
}
}
memcpy(&reg_rules[*n_rules], rule, sizeof(*rule));
(*n_rules)++;
}
/**
* regdom_intersect - do the intersection between two regulatory domains
* @rd1: first regulatory domain
@ -817,12 +869,10 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
{
int r, size_of_regd;
unsigned int x, y;
unsigned int num_rules = 0, rule_idx = 0;
unsigned int num_rules = 0;
const struct ieee80211_reg_rule *rule1, *rule2;
struct ieee80211_reg_rule *intersected_rule;
struct ieee80211_reg_rule intersected_rule;
struct ieee80211_regdomain *rd;
/* This is just a dummy holder to help us count */
struct ieee80211_reg_rule dummy_rule;
if (!rd1 || !rd2)
return NULL;
@ -840,7 +890,7 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
for (y = 0; y < rd2->n_reg_rules; y++) {
rule2 = &rd2->reg_rules[y];
if (!reg_rules_intersect(rd1, rd2, rule1, rule2,
&dummy_rule))
&intersected_rule))
num_rules++;
}
}
@ -855,34 +905,24 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
if (!rd)
return NULL;
for (x = 0; x < rd1->n_reg_rules && rule_idx < num_rules; x++) {
for (x = 0; x < rd1->n_reg_rules; x++) {
rule1 = &rd1->reg_rules[x];
for (y = 0; y < rd2->n_reg_rules && rule_idx < num_rules; y++) {
for (y = 0; y < rd2->n_reg_rules; y++) {
rule2 = &rd2->reg_rules[y];
/*
* This time around instead of using the stack lets
* write to the target rule directly saving ourselves
* a memcpy()
*/
intersected_rule = &rd->reg_rules[rule_idx];
r = reg_rules_intersect(rd1, rd2, rule1, rule2,
intersected_rule);
&intersected_rule);
/*
* No need to memset here the intersected rule here as
* we're not using the stack anymore
*/
if (r)
continue;
rule_idx++;
add_rule(&intersected_rule, rd->reg_rules,
&rd->n_reg_rules);
}
}
if (rule_idx != num_rules) {
kfree(rd);
return NULL;
}
rd->n_reg_rules = num_rules;
rd->alpha2[0] = '9';
rd->alpha2[1] = '8';
rd->dfs_region = reg_intersect_dfs_region(rd1->dfs_region,

View file

@ -2,6 +2,7 @@
* cfg80211 scan result handling
*
* Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*/
#include <linux/kernel.h>
#include <linux/slab.h>

View file

@ -641,7 +641,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
}
if (status != WLAN_STATUS_SUCCESS) {
kfree(wdev->connect_keys);
kzfree(wdev->connect_keys);
wdev->connect_keys = NULL;
wdev->ssid_len = 0;
if (bss) {
@ -918,7 +918,7 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
ASSERT_WDEV_LOCK(wdev);
if (WARN_ON(wdev->connect_keys)) {
kfree(wdev->connect_keys);
kzfree(wdev->connect_keys);
wdev->connect_keys = NULL;
}
@ -978,7 +978,7 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
ASSERT_WDEV_LOCK(wdev);
kfree(wdev->connect_keys);
kzfree(wdev->connect_keys);
wdev->connect_keys = NULL;
if (wdev->conn)

View file

@ -1896,6 +1896,51 @@ TRACE_EVENT(rdev_set_ap_chanwidth,
WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
);
TRACE_EVENT(rdev_add_tx_ts,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
u8 tsid, const u8 *peer, u8 user_prio, u16 admitted_time),
TP_ARGS(wiphy, netdev, tsid, peer, user_prio, admitted_time),
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
MAC_ENTRY(peer)
__field(u8, tsid)
__field(u8, user_prio)
__field(u16, admitted_time)
),
TP_fast_assign(
WIPHY_ASSIGN;
NETDEV_ASSIGN;
MAC_ASSIGN(peer, peer);
__entry->tsid = tsid;
__entry->user_prio = user_prio;
__entry->admitted_time = admitted_time;
),
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", TSID %d, UP %d, time %d",
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer),
__entry->tsid, __entry->user_prio, __entry->admitted_time)
);
TRACE_EVENT(rdev_del_tx_ts,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
u8 tsid, const u8 *peer),
TP_ARGS(wiphy, netdev, tsid, peer),
TP_STRUCT__entry(
WIPHY_ENTRY
NETDEV_ENTRY
MAC_ENTRY(peer)
__field(u8, tsid)
),
TP_fast_assign(
WIPHY_ASSIGN;
NETDEV_ASSIGN;
MAC_ASSIGN(peer, peer);
__entry->tsid = tsid;
),
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", TSID %d",
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->tsid)
);
/*************************************************************
* cfg80211 exported functions traces *
*************************************************************/

View file

@ -2,6 +2,7 @@
* Wireless utility functions
*
* Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
*/
#include <linux/export.h>
#include <linux/bitops.h>
@ -796,7 +797,7 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev)
netdev_err(dev, "failed to set mgtdef %d\n", i);
}
kfree(wdev->connect_keys);
kzfree(wdev->connect_keys);
wdev->connect_keys = NULL;
}

View file

@ -496,6 +496,8 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
err = 0;
if (!err) {
if (!addr) {
memset(wdev->wext.keys->data[idx], 0,
sizeof(wdev->wext.keys->data[idx]));
wdev->wext.keys->params[idx].key_len = 0;
wdev->wext.keys->params[idx].cipher = 0;
}

View file

@ -57,7 +57,7 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
err = cfg80211_connect(rdev, wdev->netdev,
&wdev->wext.connect, ck, prev_bssid);
if (err)
kfree(ck);
kzfree(ck);
return err;
}