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:
commit
6bd2bd27ba
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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++)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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, ¶ms);
|
||||
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 @@ nla_put_failure:
|
|||
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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 = ®_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(®_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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 *
|
||||
*************************************************************/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue