Another pull request for the next cycle, this time with quite

a bit of content:
  * mesh fixes/improvements from Alexis, Bob, Chun-Yeow and Jesse
  * TDLS higher bandwidth support (Arik)
  * OCB fixes from Bertold Van den Bergh
  * suspend/resume fixes from Eliad
  * dynamic SMPS support for minstrel-HT (Krishna Chaitanya)
  * VHT bitrate mask support (Lorenzo Bianconi)
  * better regulatory support for 5/10 MHz channels (Matthias May)
  * basic support for MU-MIMO to avoid the multi-vif issue (Sara Sharon)
 along with a number of other cleanups.
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABCAAGBQJVzg5bAAoJEDBSmw7B7bqr3PAP/1r8wyZXxtySzz6P5Z9k0+2I
 52NiSUISgmtnaQUyahf4n90eMU+gGJWQwPwIZFvMKg6bD4RW2XI4MdKmviKx8skU
 4sDlDxMFrVMfV/ySwiPDAONWPtwwgKllIt0IDDnKs6kPdDlUcbKOTEFYhzZ1HhTZ
 7Og4rJm7M90QpdMU7hmxmE5KRkp1hW0Yce1KPTW5U0j9yl9zbi4eLVWT+ac1WnZs
 GpItajd0BFtBy7DRHzX8RiRJ4pi+aWxhuYNqiSxUm0BqPWCzT7PP15M1kCGwrXtm
 /TTSVJl7WkLbOYI0PE0Y0XcJfZUg1c9aecCR3ubmRrQrGfOBFpN01jUANIRwqvZ3
 3QRq1RZNLac0+zlBPjoFdOHmoaVX6UcJQKSgOhcfuM1BcNFnXZEcHFN4/SaEUfvJ
 1ltybEeOEAckCMqqfHb1g/nVfJnlBjy811GzIrsHXqKqb7rRfGkfxmBxLrRzVknS
 PC970pbuhxICeeryKdVgK5BClWeT3TB1srt6OZ0QR1zlcfZbLZ8jqJlHJcy3szFi
 P43X9w8I6ZNTzkBU+lsCt9gbveYS+rSaJ+zm/SaF21ro33+FEdZ+p1ujjzp729Tz
 PnKobaOrku38Be7CSwJ760WvngC7gbZqGybGknBsws4dqDXJste0UjxulZeyaOkN
 nVmHDL45jc5rd8qjoPQV
 =kV1a
 -----END PGP SIGNATURE-----

Merge tag 'mac80211-next-for-davem-2015-08-14' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next

Johannes Berg says:

====================
Another pull request for the next cycle, this time with quite
a bit of content:
 * mesh fixes/improvements from Alexis, Bob, Chun-Yeow and Jesse
 * TDLS higher bandwidth support (Arik)
 * OCB fixes from Bertold Van den Bergh
 * suspend/resume fixes from Eliad
 * dynamic SMPS support for minstrel-HT (Krishna Chaitanya)
 * VHT bitrate mask support (Lorenzo Bianconi)
 * better regulatory support for 5/10 MHz channels (Matthias May)
 * basic support for MU-MIMO to avoid the multi-vif issue (Sara Sharon)
along with a number of other cleanups.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2015-08-17 14:25:04 -07:00
commit 2bd736fa0d
54 changed files with 1513 additions and 849 deletions

View file

@ -669,6 +669,8 @@ struct iwl_priv {
/* ieee device used by generic ieee processing code */ /* ieee device used by generic ieee processing code */
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
struct napi_struct *napi;
struct list_head calib_results; struct list_head calib_results;
struct workqueue_struct *workqueue; struct workqueue_struct *workqueue;

View file

@ -2037,7 +2037,8 @@ static void iwl_napi_add(struct iwl_op_mode *op_mode,
{ {
struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
ieee80211_napi_add(priv->hw, napi, napi_dev, poll, weight); netif_napi_add(napi_dev, napi, poll, weight);
priv->napi = napi;
} }
static const struct iwl_op_mode_ops iwl_dvm_ops = { static const struct iwl_op_mode_ops iwl_dvm_ops = {

View file

@ -786,7 +786,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,
memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
ieee80211_rx(priv->hw, skb); ieee80211_rx_napi(priv->hw, skb, priv->napi);
} }
static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in) static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)

View file

@ -366,8 +366,8 @@ struct iwl_mvm_rm_sta_cmd {
* ( MGMT_MCAST_KEY = 0x1f ) * ( MGMT_MCAST_KEY = 0x1f )
* @ctrl_flags: %iwl_sta_key_flag * @ctrl_flags: %iwl_sta_key_flag
* @IGTK: * @IGTK:
* @K1: IGTK master key * @K1: unused
* @K2: IGTK sub key * @K2: unused
* @sta_id: station ID that support IGTK * @sta_id: station ID that support IGTK
* @key_id: * @key_id:
* @receive_seq_cnt: initial RSC/PN needed for replay check * @receive_seq_cnt: initial RSC/PN needed for replay check

View file

@ -559,6 +559,7 @@ struct iwl_mvm {
const struct iwl_cfg *cfg; const struct iwl_cfg *cfg;
struct iwl_phy_db *phy_db; struct iwl_phy_db *phy_db;
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
struct napi_struct *napi;
/* for protecting access to iwl_mvm */ /* for protecting access to iwl_mvm */
struct mutex mutex; struct mutex mutex;

View file

@ -1316,7 +1316,8 @@ static void iwl_mvm_napi_add(struct iwl_op_mode *op_mode,
{ {
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
ieee80211_napi_add(mvm->hw, napi, napi_dev, poll, weight); netif_napi_add(napi_dev, napi, poll, weight);
mvm->napi = napi;
} }
static const struct iwl_op_mode_ops iwl_mvm_ops = { static const struct iwl_op_mode_ops iwl_mvm_ops = {

View file

@ -129,7 +129,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
fraglen, rxb->truesize); fraglen, rxb->truesize);
} }
ieee80211_rx(mvm->hw, skb); ieee80211_rx_napi(mvm->hw, skb, mvm->napi);
} }
/* /*

View file

@ -1277,8 +1277,6 @@ static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm,
const u8 *pn; const u8 *pn;
memcpy(igtk_cmd.IGTK, keyconf->key, keyconf->keylen); memcpy(igtk_cmd.IGTK, keyconf->key, keyconf->keylen);
ieee80211_aes_cmac_calculate_k1_k2(keyconf,
igtk_cmd.K1, igtk_cmd.K2);
ieee80211_get_key_rx_seq(keyconf, 0, &seq); ieee80211_get_key_rx_seq(keyconf, 0, &seq);
pn = seq.aes_cmac.pn; pn = seq.aes_cmac.pn;
igtk_cmd.receive_seq_cnt = cpu_to_le64(((u64) pn[5] << 0) | igtk_cmd.receive_seq_cnt = cpu_to_le64(((u64) pn[5] << 0) |

View file

@ -2399,6 +2399,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
ieee80211_hw_set(hw, AMPDU_AGGREGATION); ieee80211_hw_set(hw, AMPDU_AGGREGATION);
ieee80211_hw_set(hw, MFP_CAPABLE); ieee80211_hw_set(hw, MFP_CAPABLE);
ieee80211_hw_set(hw, SIGNAL_DBM); ieee80211_hw_set(hw, SIGNAL_DBM);
ieee80211_hw_set(hw, TDLS_WIDER_BW);
if (rctbl) if (rctbl)
ieee80211_hw_set(hw, SUPPORTS_RC_TABLE); ieee80211_hw_set(hw, SUPPORTS_RC_TABLE);
@ -3120,8 +3121,10 @@ static int hwsim_init_netlink(void)
goto failure; goto failure;
rc = netlink_register_notifier(&hwsim_netlink_notifier); rc = netlink_register_notifier(&hwsim_netlink_notifier);
if (rc) if (rc) {
genl_unregister_family(&hwsim_genl_family);
goto failure; goto failure;
}
return 0; return 0;

View file

@ -27,4 +27,43 @@ static inline unsigned long ewma_read(const struct ewma *avg)
return avg->internal >> avg->factor; return avg->internal >> avg->factor;
} }
#define DECLARE_EWMA(name, _factor, _weight) \
struct ewma_##name { \
unsigned long internal; \
}; \
static inline void ewma_##name##_init(struct ewma_##name *e) \
{ \
BUILD_BUG_ON(!__builtin_constant_p(_factor)); \
BUILD_BUG_ON(!__builtin_constant_p(_weight)); \
BUILD_BUG_ON_NOT_POWER_OF_2(_factor); \
BUILD_BUG_ON_NOT_POWER_OF_2(_weight); \
e->internal = 0; \
} \
static inline unsigned long \
ewma_##name##_read(struct ewma_##name *e) \
{ \
BUILD_BUG_ON(!__builtin_constant_p(_factor)); \
BUILD_BUG_ON(!__builtin_constant_p(_weight)); \
BUILD_BUG_ON_NOT_POWER_OF_2(_factor); \
BUILD_BUG_ON_NOT_POWER_OF_2(_weight); \
return e->internal >> ilog2(_factor); \
} \
static inline void ewma_##name##_add(struct ewma_##name *e, \
unsigned long val) \
{ \
unsigned long internal = ACCESS_ONCE(e->internal); \
unsigned long weight = ilog2(_weight); \
unsigned long factor = ilog2(_factor); \
\
BUILD_BUG_ON(!__builtin_constant_p(_factor)); \
BUILD_BUG_ON(!__builtin_constant_p(_weight)); \
BUILD_BUG_ON_NOT_POWER_OF_2(_factor); \
BUILD_BUG_ON_NOT_POWER_OF_2(_weight); \
\
ACCESS_ONCE(e->internal) = internal ? \
(((internal << weight) - internal) + \
(val << factor)) >> weight : \
(val << factor); \
}
#endif /* _LINUX_AVERAGE_H */ #endif /* _LINUX_AVERAGE_H */

View file

@ -2074,8 +2074,8 @@ enum ieee80211_tdls_actioncode {
#define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6) #define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6)
#define WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED BIT(7) #define WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED BIT(7)
#define WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED BIT(5)
#define WLAN_EXT_CAPA8_OPMODE_NOTIF BIT(6) #define WLAN_EXT_CAPA8_OPMODE_NOTIF BIT(6)
#define WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED BIT(7)
/* TDLS specific payload type in the LLC/SNAP header */ /* TDLS specific payload type in the LLC/SNAP header */
#define WLAN_TDLS_SNAP_RFTYPE 0x2 #define WLAN_TDLS_SNAP_RFTYPE 0x2

View file

@ -2369,8 +2369,7 @@ struct cfg80211_qos_map {
* method returns 0.) * method returns 0.)
* *
* @mgmt_frame_register: Notify driver that a management frame type was * @mgmt_frame_register: Notify driver that a management frame type was
* registered. Note that this callback may not sleep, and cannot run * registered. The callback is allowed to sleep.
* concurrently with itself.
* *
* @set_antenna: Set antenna configuration (tx_ant, rx_ant) on the device. * @set_antenna: Set antenna configuration (tx_ant, rx_ant) on the device.
* Parameters are bitmaps of allowed antennas to use for TX/RX. Drivers may * Parameters are bitmaps of allowed antennas to use for TX/RX. Drivers may

View file

@ -973,6 +973,10 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* @RX_FLAG_IV_STRIPPED: The IV/ICV are stripped from this frame. * @RX_FLAG_IV_STRIPPED: The IV/ICV are stripped from this frame.
* If this flag is set, the stack cannot do any replay detection * If this flag is set, the stack cannot do any replay detection
* hence the driver or hardware will have to do that. * hence the driver or hardware will have to do that.
* @RX_FLAG_PN_VALIDATED: Currently only valid for CCMP/GCMP frames, this
* flag indicates that the PN was verified for replay protection.
* Note that this flag is also currently only supported when a frame
* is also decrypted (ie. @RX_FLAG_DECRYPTED must be set)
* @RX_FLAG_FAILED_FCS_CRC: Set this flag if the FCS check failed on * @RX_FLAG_FAILED_FCS_CRC: Set this flag if the FCS check failed on
* the frame. * the frame.
* @RX_FLAG_FAILED_PLCP_CRC: Set this flag if the PCLP check failed on * @RX_FLAG_FAILED_PLCP_CRC: Set this flag if the PCLP check failed on
@ -997,9 +1001,6 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* @RX_FLAG_AMPDU_DETAILS: A-MPDU details are known, in particular the reference * @RX_FLAG_AMPDU_DETAILS: A-MPDU details are known, in particular the reference
* number (@ampdu_reference) must be populated and be a distinct number for * number (@ampdu_reference) must be populated and be a distinct number for
* each A-MPDU * each A-MPDU
* @RX_FLAG_AMPDU_REPORT_ZEROLEN: driver reports 0-length subframes
* @RX_FLAG_AMPDU_IS_ZEROLEN: This is a zero-length subframe, for
* monitoring purposes only
* @RX_FLAG_AMPDU_LAST_KNOWN: last subframe is known, should be set on all * @RX_FLAG_AMPDU_LAST_KNOWN: last subframe is known, should be set on all
* subframes of a single A-MPDU * subframes of a single A-MPDU
* @RX_FLAG_AMPDU_IS_LAST: this subframe is the last subframe of the A-MPDU * @RX_FLAG_AMPDU_IS_LAST: this subframe is the last subframe of the A-MPDU
@ -1039,8 +1040,8 @@ enum mac80211_rx_flags {
RX_FLAG_NO_SIGNAL_VAL = BIT(12), RX_FLAG_NO_SIGNAL_VAL = BIT(12),
RX_FLAG_HT_GF = BIT(13), RX_FLAG_HT_GF = BIT(13),
RX_FLAG_AMPDU_DETAILS = BIT(14), RX_FLAG_AMPDU_DETAILS = BIT(14),
RX_FLAG_AMPDU_REPORT_ZEROLEN = BIT(15), RX_FLAG_PN_VALIDATED = BIT(15),
RX_FLAG_AMPDU_IS_ZEROLEN = BIT(16), /* bit 16 free */
RX_FLAG_AMPDU_LAST_KNOWN = BIT(17), RX_FLAG_AMPDU_LAST_KNOWN = BIT(17),
RX_FLAG_AMPDU_IS_LAST = BIT(18), RX_FLAG_AMPDU_IS_LAST = BIT(18),
RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(19), RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(19),
@ -1491,8 +1492,10 @@ enum ieee80211_key_flags {
* - Temporal Authenticator Rx MIC Key (64 bits) * - Temporal Authenticator Rx MIC Key (64 bits)
* @icv_len: The ICV length for this key type * @icv_len: The ICV length for this key type
* @iv_len: The IV length for this key type * @iv_len: The IV length for this key type
* @drv_priv: pointer for driver use
*/ */
struct ieee80211_key_conf { struct ieee80211_key_conf {
void *drv_priv;
atomic64_t tx_pn; atomic64_t tx_pn;
u32 cipher; u32 cipher;
u8 icv_len; u8 icv_len;
@ -1675,7 +1678,6 @@ struct ieee80211_sta_rates {
* @tdls: indicates whether the STA is a TDLS peer * @tdls: indicates whether the STA is a TDLS peer
* @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only * @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only
* valid if the STA is a TDLS peer in the first place. * valid if the STA is a TDLS peer in the first place.
* @mfp: indicates whether the STA uses management frame protection or not.
* @txq: per-TID data TX queues (if driver uses the TXQ abstraction) * @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
*/ */
struct ieee80211_sta { struct ieee80211_sta {
@ -1693,7 +1695,6 @@ struct ieee80211_sta {
struct ieee80211_sta_rates __rcu *rates; struct ieee80211_sta_rates __rcu *rates;
bool tdls; bool tdls;
bool tdls_initiator; bool tdls_initiator;
bool mfp;
struct ieee80211_txq *txq[IEEE80211_NUM_TIDS]; struct ieee80211_txq *txq[IEEE80211_NUM_TIDS];
@ -1888,6 +1889,9 @@ struct ieee80211_txq {
* @IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands * @IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands
* in one command, mac80211 doesn't have to run separate scans per band. * in one command, mac80211 doesn't have to run separate scans per band.
* *
* @IEEE80211_HW_TDLS_WIDER_BW: The device/driver supports wider bandwidth
* than then BSS bandwidth for a TDLS link on the base channel.
*
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
*/ */
enum ieee80211_hw_flags { enum ieee80211_hw_flags {
@ -1920,6 +1924,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_CHANCTX_STA_CSA, IEEE80211_HW_CHANCTX_STA_CSA,
IEEE80211_HW_SUPPORTS_CLONED_SKBS, IEEE80211_HW_SUPPORTS_CLONED_SKBS,
IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS, IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS,
IEEE80211_HW_TDLS_WIDER_BW,
/* keep last, obviously */ /* keep last, obviously */
NUM_IEEE80211_HW_FLAGS NUM_IEEE80211_HW_FLAGS
@ -3696,20 +3701,28 @@ void ieee80211_free_hw(struct ieee80211_hw *hw);
void ieee80211_restart_hw(struct ieee80211_hw *hw); void ieee80211_restart_hw(struct ieee80211_hw *hw);
/** /**
* ieee80211_napi_add - initialize mac80211 NAPI context * ieee80211_rx_napi - receive frame from NAPI context
* @hw: the hardware to initialize the NAPI context on
* @napi: the NAPI context to initialize
* @napi_dev: dummy NAPI netdevice, here to not waste the space if the
* driver doesn't use NAPI
* @poll: poll function
* @weight: default weight
* *
* See also netif_napi_add(). * Use this function to hand received frames to mac80211. The receive
* buffer in @skb must start with an IEEE 802.11 header. In case of a
* paged @skb is used, the driver is recommended to put the ieee80211
* header of the frame on the linear part of the @skb to avoid memory
* allocation and/or memcpy by the stack.
*
* This function may not be called in IRQ context. Calls to this function
* for a single hardware must be synchronized against each other. Calls to
* this function, ieee80211_rx_ni() and ieee80211_rx_irqsafe() may not be
* mixed for a single hardware. Must not run concurrently with
* ieee80211_tx_status() or ieee80211_tx_status_ni().
*
* This function must be called with BHs disabled.
*
* @hw: the hardware this frame came in on
* @skb: the buffer to receive, owned by mac80211 after this call
* @napi: the NAPI context
*/ */
void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi, void ieee80211_rx_napi(struct ieee80211_hw *hw, struct sk_buff *skb,
struct net_device *napi_dev, struct napi_struct *napi);
int (*poll)(struct napi_struct *, int),
int weight);
/** /**
* ieee80211_rx - receive frame * ieee80211_rx - receive frame
@ -3731,7 +3744,10 @@ void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi,
* @hw: the hardware this frame came in on * @hw: the hardware this frame came in on
* @skb: the buffer to receive, owned by mac80211 after this call * @skb: the buffer to receive, owned by mac80211 after this call
*/ */
void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb); static inline void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
ieee80211_rx_napi(hw, skb, NULL);
}
/** /**
* ieee80211_rx_irqsafe - receive frame * ieee80211_rx_irqsafe - receive frame
@ -4314,19 +4330,6 @@ void ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf,
void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf, void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf,
struct sk_buff *skb, u8 *p2k); struct sk_buff *skb, u8 *p2k);
/**
* ieee80211_aes_cmac_calculate_k1_k2 - calculate the AES-CMAC sub keys
*
* This function computes the two AES-CMAC sub-keys, based on the
* previously installed master key.
*
* @keyconf: the parameter passed with the set key
* @k1: a buffer to be filled with the 1st sub-key
* @k2: a buffer to be filled with the 2nd sub-key
*/
void ieee80211_aes_cmac_calculate_k1_k2(struct ieee80211_key_conf *keyconf,
u8 *k1, u8 *k2);
/** /**
* ieee80211_get_key_tx_seq - get key TX sequence counter * ieee80211_get_key_tx_seq - get key TX sequence counter
* *

View file

@ -7,7 +7,6 @@ config MAC80211
select CRYPTO_CCM select CRYPTO_CCM
select CRYPTO_GCM select CRYPTO_GCM
select CRC32 select CRC32
select AVERAGE
---help--- ---help---
This option enables the hardware independent IEEE 802.11 This option enables the hardware independent IEEE 802.11
networking stack. networking stack.

View file

@ -3,6 +3,7 @@ obj-$(CONFIG_MAC80211) += mac80211.o
# mac80211 objects # mac80211 objects
mac80211-y := \ mac80211-y := \
main.o status.o \ main.o status.o \
driver-ops.o \
sta_info.o \ sta_info.o \
wep.o \ wep.o \
wpa.o \ wpa.o \

View file

@ -145,20 +145,3 @@ void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm)
{ {
crypto_free_cipher(tfm); crypto_free_cipher(tfm);
} }
void ieee80211_aes_cmac_calculate_k1_k2(struct ieee80211_key_conf *keyconf,
u8 *k1, u8 *k2)
{
u8 l[AES_BLOCK_SIZE] = {};
struct ieee80211_key *key =
container_of(keyconf, struct ieee80211_key, conf);
crypto_cipher_encrypt_one(key->u.aes_cmac.tfm, l, l);
memcpy(k1, l, AES_BLOCK_SIZE);
gf_mulx(k1);
memcpy(k2, k1, AES_BLOCK_SIZE);
gf_mulx(k2);
}
EXPORT_SYMBOL(ieee80211_aes_cmac_calculate_k1_k2);

View file

@ -1019,6 +1019,65 @@ static int sta_apply_auth_flags(struct ieee80211_local *local,
return 0; return 0;
} }
static void sta_apply_mesh_params(struct ieee80211_local *local,
struct sta_info *sta,
struct station_parameters *params)
{
#ifdef CONFIG_MAC80211_MESH
struct ieee80211_sub_if_data *sdata = sta->sdata;
u32 changed = 0;
if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
switch (params->plink_state) {
case NL80211_PLINK_ESTAB:
if (sta->mesh->plink_state != NL80211_PLINK_ESTAB)
changed = mesh_plink_inc_estab_count(sdata);
sta->mesh->plink_state = params->plink_state;
ieee80211_mps_sta_status_update(sta);
changed |= ieee80211_mps_set_sta_local_pm(sta,
sdata->u.mesh.mshcfg.power_mode);
break;
case NL80211_PLINK_LISTEN:
case NL80211_PLINK_BLOCKED:
case NL80211_PLINK_OPN_SNT:
case NL80211_PLINK_OPN_RCVD:
case NL80211_PLINK_CNF_RCVD:
case NL80211_PLINK_HOLDING:
if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
changed = mesh_plink_dec_estab_count(sdata);
sta->mesh->plink_state = params->plink_state;
ieee80211_mps_sta_status_update(sta);
changed |= ieee80211_mps_set_sta_local_pm(sta,
NL80211_MESH_POWER_UNKNOWN);
break;
default:
/* nothing */
break;
}
}
switch (params->plink_action) {
case NL80211_PLINK_ACTION_NO_ACTION:
/* nothing */
break;
case NL80211_PLINK_ACTION_OPEN:
changed |= mesh_plink_open(sta);
break;
case NL80211_PLINK_ACTION_BLOCK:
changed |= mesh_plink_block(sta);
break;
}
if (params->local_pm)
changed |= ieee80211_mps_set_sta_local_pm(sta,
params->local_pm);
ieee80211_mbss_info_change_notify(sdata, changed);
#endif
}
static int sta_apply_parameters(struct ieee80211_local *local, static int sta_apply_parameters(struct ieee80211_local *local,
struct sta_info *sta, struct sta_info *sta,
struct station_parameters *params) struct station_parameters *params)
@ -1076,7 +1135,6 @@ static int sta_apply_parameters(struct ieee80211_local *local,
} }
if (mask & BIT(NL80211_STA_FLAG_MFP)) { if (mask & BIT(NL80211_STA_FLAG_MFP)) {
sta->sta.mfp = !!(set & BIT(NL80211_STA_FLAG_MFP));
if (set & BIT(NL80211_STA_FLAG_MFP)) if (set & BIT(NL80211_STA_FLAG_MFP))
set_sta_flag(sta, WLAN_STA_MFP); set_sta_flag(sta, WLAN_STA_MFP);
else else
@ -1097,6 +1155,12 @@ static int sta_apply_parameters(struct ieee80211_local *local,
params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)
set_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH); set_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH);
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) &&
params->ext_capab_len >= 8 &&
params->ext_capab[7] & WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED)
set_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW);
if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) { if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) {
sta->sta.uapsd_queues = params->uapsd_queues; sta->sta.uapsd_queues = params->uapsd_queues;
sta->sta.max_sp = params->max_sp; sta->sta.max_sp = params->max_sp;
@ -1144,62 +1208,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
band, false); band, false);
} }
if (ieee80211_vif_is_mesh(&sdata->vif)) { if (ieee80211_vif_is_mesh(&sdata->vif))
#ifdef CONFIG_MAC80211_MESH sta_apply_mesh_params(local, sta, params);
u32 changed = 0;
if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
switch (params->plink_state) {
case NL80211_PLINK_ESTAB:
if (sta->plink_state != NL80211_PLINK_ESTAB)
changed = mesh_plink_inc_estab_count(
sdata);
sta->plink_state = params->plink_state;
ieee80211_mps_sta_status_update(sta);
changed |= ieee80211_mps_set_sta_local_pm(sta,
sdata->u.mesh.mshcfg.power_mode);
break;
case NL80211_PLINK_LISTEN:
case NL80211_PLINK_BLOCKED:
case NL80211_PLINK_OPN_SNT:
case NL80211_PLINK_OPN_RCVD:
case NL80211_PLINK_CNF_RCVD:
case NL80211_PLINK_HOLDING:
if (sta->plink_state == NL80211_PLINK_ESTAB)
changed = mesh_plink_dec_estab_count(
sdata);
sta->plink_state = params->plink_state;
ieee80211_mps_sta_status_update(sta);
changed |= ieee80211_mps_set_sta_local_pm(sta,
NL80211_MESH_POWER_UNKNOWN);
break;
default:
/* nothing */
break;
}
}
switch (params->plink_action) {
case NL80211_PLINK_ACTION_NO_ACTION:
/* nothing */
break;
case NL80211_PLINK_ACTION_OPEN:
changed |= mesh_plink_open(sta);
break;
case NL80211_PLINK_ACTION_BLOCK:
changed |= mesh_plink_block(sta);
break;
}
if (params->local_pm)
changed |=
ieee80211_mps_set_sta_local_pm(sta,
params->local_pm);
ieee80211_mbss_info_change_notify(sdata, changed);
#endif
}
/* set the STA state after all sta info from usermode has been set */ /* set the STA state after all sta info from usermode has been set */
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
@ -2358,6 +2368,8 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
const u8 *ap; const u8 *ap;
enum ieee80211_smps_mode old_req; enum ieee80211_smps_mode old_req;
int err; int err;
struct sta_info *sta;
bool tdls_peer_found = false;
lockdep_assert_held(&sdata->wdev.mtx); lockdep_assert_held(&sdata->wdev.mtx);
@ -2382,11 +2394,22 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
ap = sdata->u.mgd.associated->bssid; ap = sdata->u.mgd.associated->bssid;
rcu_read_lock();
list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
continue;
tdls_peer_found = true;
break;
}
rcu_read_unlock();
if (smps_mode == IEEE80211_SMPS_AUTOMATIC) { if (smps_mode == IEEE80211_SMPS_AUTOMATIC) {
if (sdata->u.mgd.powersave) if (tdls_peer_found || !sdata->u.mgd.powersave)
smps_mode = IEEE80211_SMPS_DYNAMIC;
else
smps_mode = IEEE80211_SMPS_OFF; smps_mode = IEEE80211_SMPS_OFF;
else
smps_mode = IEEE80211_SMPS_DYNAMIC;
} }
/* send SM PS frame to AP */ /* send SM PS frame to AP */
@ -2394,6 +2417,8 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
ap, ap); ap, ap);
if (err) if (err)
sdata->u.mgd.req_smps = old_req; sdata->u.mgd.req_smps = old_req;
else if (smps_mode != IEEE80211_SMPS_OFF && tdls_peer_found)
ieee80211_teardown_tdls_peers(sdata);
return err; return err;
} }
@ -2479,16 +2504,26 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
sdata->rc_rateidx_mask[i] = mask->control[i].legacy; sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs, memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs,
sizeof(mask->control[i].ht_mcs)); sizeof(mask->control[i].ht_mcs));
memcpy(sdata->rc_rateidx_vht_mcs_mask[i],
mask->control[i].vht_mcs,
sizeof(mask->control[i].vht_mcs));
sdata->rc_has_mcs_mask[i] = false; sdata->rc_has_mcs_mask[i] = false;
sdata->rc_has_vht_mcs_mask[i] = false;
if (!sband) if (!sband)
continue; continue;
for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++) for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++) {
if (~sdata->rc_rateidx_mcs_mask[i][j]) { if (~sdata->rc_rateidx_mcs_mask[i][j])
sdata->rc_has_mcs_mask[i] = true; sdata->rc_has_mcs_mask[i] = true;
if (~sdata->rc_rateidx_vht_mcs_mask[i][j])
sdata->rc_has_vht_mcs_mask[i] = true;
if (sdata->rc_has_mcs_mask[i] &&
sdata->rc_has_vht_mcs_mask[i])
break; break;
} }
} }
return 0; return 0;

View file

@ -190,7 +190,7 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
return NULL; return NULL;
} }
static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta) enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta)
{ {
switch (sta->bandwidth) { switch (sta->bandwidth) {
case IEEE80211_STA_RX_BW_20: case IEEE80211_STA_RX_BW_20:
@ -264,9 +264,17 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP_VLAN:
width = ieee80211_get_max_required_bw(sdata); width = ieee80211_get_max_required_bw(sdata);
break; break;
case NL80211_IFTYPE_STATION:
/*
* The ap's sta->bandwidth is not set yet at this
* point, so take the width from the chandef, but
* account also for TDLS peers
*/
width = max(vif->bss_conf.chandef.width,
ieee80211_get_max_required_bw(sdata));
break;
case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_P2P_DEVICE:
continue; continue;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MESH_POINT:
@ -554,12 +562,13 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local,
kfree_rcu(ctx, rcu_head); kfree_rcu(ctx, rcu_head);
} }
static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx) struct ieee80211_chanctx *ctx)
{ {
struct ieee80211_chanctx_conf *conf = &ctx->conf; struct ieee80211_chanctx_conf *conf = &ctx->conf;
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
const struct cfg80211_chan_def *compat = NULL; const struct cfg80211_chan_def *compat = NULL;
struct sta_info *sta;
lockdep_assert_held(&local->chanctx_mtx); lockdep_assert_held(&local->chanctx_mtx);
@ -581,6 +590,20 @@ static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
if (WARN_ON_ONCE(!compat)) if (WARN_ON_ONCE(!compat))
break; break;
} }
/* TDLS peers can sometimes affect the chandef width */
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (!sta->uploaded ||
!test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) ||
!test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
!sta->tdls_chandef.chan)
continue;
compat = cfg80211_chandef_compatible(&sta->tdls_chandef,
compat);
if (WARN_ON_ONCE(!compat))
break;
}
rcu_read_unlock(); rcu_read_unlock();
if (!compat) if (!compat)

View file

@ -122,6 +122,7 @@ static const char *hw_flag_names[NUM_IEEE80211_HW_FLAGS + 1] = {
FLAG(CHANCTX_STA_CSA), FLAG(CHANCTX_STA_CSA),
FLAG(SUPPORTS_CLONED_SKBS), FLAG(SUPPORTS_CLONED_SKBS),
FLAG(SINGLE_SCAN_ON_ALL_BANDS), FLAG(SINGLE_SCAN_ON_ALL_BANDS),
FLAG(TDLS_WIDER_BW),
/* keep last for the build bug below */ /* keep last for the build bug below */
(void *)0x1 (void *)0x1
@ -277,7 +278,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_STATS_ADD(rx_handlers_queued); DEBUGFS_STATS_ADD(rx_handlers_queued);
DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc); DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc);
DEBUGFS_STATS_ADD(rx_handlers_drop_defrag); DEBUGFS_STATS_ADD(rx_handlers_drop_defrag);
DEBUGFS_STATS_ADD(rx_handlers_drop_short);
DEBUGFS_STATS_ADD(tx_expand_skb_head); DEBUGFS_STATS_ADD(tx_expand_skb_head);
DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned); DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned);
DEBUGFS_STATS_ADD(rx_expand_skb_head_defrag); DEBUGFS_STATS_ADD(rx_expand_skb_head_defrag);

View file

@ -57,7 +57,6 @@ KEY_CONF_FILE(keylen, D);
KEY_CONF_FILE(keyidx, D); KEY_CONF_FILE(keyidx, D);
KEY_CONF_FILE(hw_key_idx, D); KEY_CONF_FILE(hw_key_idx, D);
KEY_FILE(flags, X); KEY_FILE(flags, X);
KEY_FILE(tx_rx_count, D);
KEY_READ(ifindex, sdata->name, "%s\n"); KEY_READ(ifindex, sdata->name, "%s\n");
KEY_OPS(ifindex); KEY_OPS(ifindex);
@ -310,7 +309,6 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
DEBUGFS_ADD(flags); DEBUGFS_ADD(flags);
DEBUGFS_ADD(keyidx); DEBUGFS_ADD(keyidx);
DEBUGFS_ADD(hw_key_idx); DEBUGFS_ADD(hw_key_idx);
DEBUGFS_ADD(tx_rx_count);
DEBUGFS_ADD(algorithm); DEBUGFS_ADD(algorithm);
DEBUGFS_ADD(tx_spec); DEBUGFS_ADD(tx_spec);
DEBUGFS_ADD(rx_spec); DEBUGFS_ADD(rx_spec);

View file

@ -186,6 +186,38 @@ IEEE80211_IF_FILE(rc_rateidx_mcs_mask_2ghz,
IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz, IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz,
rc_rateidx_mcs_mask[IEEE80211_BAND_5GHZ], HEXARRAY); rc_rateidx_mcs_mask[IEEE80211_BAND_5GHZ], HEXARRAY);
static ssize_t ieee80211_if_fmt_rc_rateidx_vht_mcs_mask_2ghz(
const struct ieee80211_sub_if_data *sdata,
char *buf, int buflen)
{
int i, len = 0;
const u16 *mask = sdata->rc_rateidx_vht_mcs_mask[IEEE80211_BAND_2GHZ];
for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
len += scnprintf(buf + len, buflen - len, "%04x ", mask[i]);
len += scnprintf(buf + len, buflen - len, "\n");
return len;
}
IEEE80211_IF_FILE_R(rc_rateidx_vht_mcs_mask_2ghz);
static ssize_t ieee80211_if_fmt_rc_rateidx_vht_mcs_mask_5ghz(
const struct ieee80211_sub_if_data *sdata,
char *buf, int buflen)
{
int i, len = 0;
const u16 *mask = sdata->rc_rateidx_vht_mcs_mask[IEEE80211_BAND_5GHZ];
for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
len += scnprintf(buf + len, buflen - len, "%04x ", mask[i]);
len += scnprintf(buf + len, buflen - len, "\n");
return len;
}
IEEE80211_IF_FILE_R(rc_rateidx_vht_mcs_mask_5ghz);
IEEE80211_IF_FILE(flags, flags, HEX); IEEE80211_IF_FILE(flags, flags, HEX);
IEEE80211_IF_FILE(state, state, LHEX); IEEE80211_IF_FILE(state, state, LHEX);
IEEE80211_IF_FILE(txpower, vif.bss_conf.txpower, DEC); IEEE80211_IF_FILE(txpower, vif.bss_conf.txpower, DEC);
@ -565,6 +597,8 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(rc_rateidx_mask_5ghz); DEBUGFS_ADD(rc_rateidx_mask_5ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz); DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_5ghz);
DEBUGFS_ADD(hw_queues); DEBUGFS_ADD(hw_queues);
} }

41
net/mac80211/driver-ops.c Normal file
View file

@ -0,0 +1,41 @@
/*
* 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
* published by the Free Software Foundation.
*/
#include <net/mac80211.h>
#include "ieee80211_i.h"
#include "trace.h"
#include "driver-ops.h"
__must_check
int drv_sta_state(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
enum ieee80211_sta_state old_state,
enum ieee80211_sta_state new_state)
{
int ret = 0;
might_sleep();
sdata = get_bss_sdata(sdata);
if (!check_sdata_in_driver(sdata))
return -EIO;
trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state);
if (local->ops->sta_state) {
ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta,
old_state, new_state);
} else if (old_state == IEEE80211_STA_AUTH &&
new_state == IEEE80211_STA_ASSOC) {
ret = drv_sta_add(local, sdata, &sta->sta);
if (ret == 0)
sta->uploaded = true;
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTH) {
drv_sta_remove(local, sdata, &sta->sta);
}
trace_drv_return_int(local, ret);
return ret;
}

View file

@ -573,37 +573,12 @@ static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local,
trace_drv_return_void(local); trace_drv_return_void(local);
} }
static inline __must_check __must_check
int drv_sta_state(struct ieee80211_local *local, int drv_sta_state(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata, struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, struct sta_info *sta,
enum ieee80211_sta_state old_state, enum ieee80211_sta_state old_state,
enum ieee80211_sta_state new_state) enum ieee80211_sta_state new_state);
{
int ret = 0;
might_sleep();
sdata = get_bss_sdata(sdata);
if (!check_sdata_in_driver(sdata))
return -EIO;
trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state);
if (local->ops->sta_state) {
ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta,
old_state, new_state);
} else if (old_state == IEEE80211_STA_AUTH &&
new_state == IEEE80211_STA_ASSOC) {
ret = drv_sta_add(local, sdata, &sta->sta);
if (ret == 0)
sta->uploaded = true;
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTH) {
drv_sta_remove(local, sdata, &sta->sta);
}
trace_drv_return_int(local, ret);
return ret;
}
static inline void drv_sta_rc_update(struct ieee80211_local *local, static inline void drv_sta_rc_update(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata, struct ieee80211_sub_if_data *sdata,

View file

@ -84,13 +84,13 @@ struct ieee80211_local;
#define IEEE80211_DEAUTH_FRAME_LEN (24 /* hdr */ + 2 /* reason */) #define IEEE80211_DEAUTH_FRAME_LEN (24 /* hdr */ + 2 /* reason */)
struct ieee80211_fragment_entry { struct ieee80211_fragment_entry {
unsigned long first_frag_time;
unsigned int seq;
unsigned int rx_queue;
unsigned int last_frag;
unsigned int extra_len;
struct sk_buff_head skb_list; struct sk_buff_head skb_list;
int ccmp; /* Whether fragments were encrypted with CCMP */ unsigned long first_frag_time;
u16 seq;
u16 extra_len;
u16 last_frag;
u8 rx_queue;
bool ccmp; /* Whether fragments were encrypted with CCMP */
u8 last_pn[6]; /* PN of the last fragment if CCMP was used */ u8 last_pn[6]; /* PN of the last fragment if CCMP was used */
}; };
@ -181,7 +181,6 @@ typedef unsigned __bitwise__ ieee80211_rx_result;
/** /**
* enum ieee80211_packet_rx_flags - packet RX flags * enum ieee80211_packet_rx_flags - packet RX flags
* @IEEE80211_RX_FRAGMENTED: fragmented frame
* @IEEE80211_RX_AMSDU: a-MSDU packet * @IEEE80211_RX_AMSDU: a-MSDU packet
* @IEEE80211_RX_MALFORMED_ACTION_FRM: action frame is malformed * @IEEE80211_RX_MALFORMED_ACTION_FRM: action frame is malformed
* @IEEE80211_RX_DEFERRED_RELEASE: frame was subjected to receive reordering * @IEEE80211_RX_DEFERRED_RELEASE: frame was subjected to receive reordering
@ -190,7 +189,6 @@ typedef unsigned __bitwise__ ieee80211_rx_result;
* @rx_flags field of &struct ieee80211_rx_status. * @rx_flags field of &struct ieee80211_rx_status.
*/ */
enum ieee80211_packet_rx_flags { enum ieee80211_packet_rx_flags {
IEEE80211_RX_FRAGMENTED = BIT(2),
IEEE80211_RX_AMSDU = BIT(3), IEEE80211_RX_AMSDU = BIT(3),
IEEE80211_RX_MALFORMED_ACTION_FRM = BIT(4), IEEE80211_RX_MALFORMED_ACTION_FRM = BIT(4),
IEEE80211_RX_DEFERRED_RELEASE = BIT(5), IEEE80211_RX_DEFERRED_RELEASE = BIT(5),
@ -202,8 +200,6 @@ enum ieee80211_packet_rx_flags {
* @IEEE80211_RX_CMNTR: received on cooked monitor already * @IEEE80211_RX_CMNTR: received on cooked monitor already
* @IEEE80211_RX_BEACON_REPORTED: This frame was already reported * @IEEE80211_RX_BEACON_REPORTED: This frame was already reported
* to cfg80211_report_obss_beacon(). * to cfg80211_report_obss_beacon().
* @IEEE80211_RX_REORDER_TIMER: this frame is released by the
* reorder buffer timeout timer, not the normal RX path
* *
* These flags are used across handling multiple interfaces * These flags are used across handling multiple interfaces
* for a single frame. * for a single frame.
@ -211,10 +207,10 @@ enum ieee80211_packet_rx_flags {
enum ieee80211_rx_flags { enum ieee80211_rx_flags {
IEEE80211_RX_CMNTR = BIT(0), IEEE80211_RX_CMNTR = BIT(0),
IEEE80211_RX_BEACON_REPORTED = BIT(1), IEEE80211_RX_BEACON_REPORTED = BIT(1),
IEEE80211_RX_REORDER_TIMER = BIT(2),
}; };
struct ieee80211_rx_data { struct ieee80211_rx_data {
struct napi_struct *napi;
struct sk_buff *skb; struct sk_buff *skb;
struct ieee80211_local *local; struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
@ -725,6 +721,7 @@ struct ieee80211_if_mesh {
* back to wireless media and to the local net stack. * back to wireless media and to the local net stack.
* @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume. * @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume.
* @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver * @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver
* @IEEE80211_SDATA_MU_MIMO_OWNER: indicates interface owns MU-MIMO capability
*/ */
enum ieee80211_sub_if_data_flags { enum ieee80211_sub_if_data_flags {
IEEE80211_SDATA_ALLMULTI = BIT(0), IEEE80211_SDATA_ALLMULTI = BIT(0),
@ -732,6 +729,7 @@ enum ieee80211_sub_if_data_flags {
IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3), IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3),
IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4), IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4),
IEEE80211_SDATA_IN_DRIVER = BIT(5), IEEE80211_SDATA_IN_DRIVER = BIT(5),
IEEE80211_SDATA_MU_MIMO_OWNER = BIT(6),
}; };
/** /**
@ -903,6 +901,9 @@ struct ieee80211_sub_if_data {
bool rc_has_mcs_mask[IEEE80211_NUM_BANDS]; bool rc_has_mcs_mask[IEEE80211_NUM_BANDS];
u8 rc_rateidx_mcs_mask[IEEE80211_NUM_BANDS][IEEE80211_HT_MCS_MASK_LEN]; u8 rc_rateidx_mcs_mask[IEEE80211_NUM_BANDS][IEEE80211_HT_MCS_MASK_LEN];
bool rc_has_vht_mcs_mask[IEEE80211_NUM_BANDS];
u16 rc_rateidx_vht_mcs_mask[IEEE80211_NUM_BANDS][NL80211_VHT_NSS_MAX];
union { union {
struct ieee80211_if_ap ap; struct ieee80211_if_ap ap;
struct ieee80211_if_wds wds; struct ieee80211_if_wds wds;
@ -1010,7 +1011,6 @@ enum sdata_queue_type {
IEEE80211_SDATA_QUEUE_AGG_STOP = 2, IEEE80211_SDATA_QUEUE_AGG_STOP = 2,
IEEE80211_SDATA_QUEUE_RX_AGG_START = 3, IEEE80211_SDATA_QUEUE_RX_AGG_START = 3,
IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4, IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4,
IEEE80211_SDATA_QUEUE_TDLS_CHSW = 5,
}; };
enum { enum {
@ -1286,7 +1286,6 @@ struct ieee80211_local {
unsigned int rx_handlers_queued; unsigned int rx_handlers_queued;
unsigned int rx_handlers_drop_nullfunc; unsigned int rx_handlers_drop_nullfunc;
unsigned int rx_handlers_drop_defrag; unsigned int rx_handlers_drop_defrag;
unsigned int rx_handlers_drop_short;
unsigned int tx_expand_skb_head; unsigned int tx_expand_skb_head;
unsigned int tx_expand_skb_head_cloned; unsigned int tx_expand_skb_head_cloned;
unsigned int rx_expand_skb_head_defrag; unsigned int rx_expand_skb_head_defrag;
@ -1348,14 +1347,16 @@ struct ieee80211_local {
struct ieee80211_sub_if_data __rcu *p2p_sdata; struct ieee80211_sub_if_data __rcu *p2p_sdata;
struct napi_struct *napi;
/* virtual monitor interface */ /* virtual monitor interface */
struct ieee80211_sub_if_data __rcu *monitor_sdata; struct ieee80211_sub_if_data __rcu *monitor_sdata;
struct cfg80211_chan_def monitor_chandef; struct cfg80211_chan_def monitor_chandef;
/* extended capabilities provided by mac80211 */ /* extended capabilities provided by mac80211 */
u8 ext_capa[8]; u8 ext_capa[8];
/* TDLS channel switch */
struct work_struct tdls_chsw_work;
struct sk_buff_head skb_queue_tdls_chsw;
}; };
static inline struct ieee80211_sub_if_data * static inline struct ieee80211_sub_if_data *
@ -1715,6 +1716,8 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
enum ieee80211_band band, bool nss_only); enum ieee80211_band band, bool nss_only);
void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata, void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta_vht_cap *vht_cap); struct ieee80211_sta_vht_cap *vht_cap);
void ieee80211_get_vht_mask_from_cap(__le16 vht_cap,
u16 vht_mask[NL80211_VHT_NSS_MAX]);
/* Spectrum management */ /* Spectrum management */
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
@ -1763,8 +1766,6 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
/* utility functions/constants */ /* utility functions/constants */
extern const void *const mac80211_wiphy_privid; /* for wiphy privid */ extern const void *const mac80211_wiphy_privid; /* for wiphy privid */
u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
enum nl80211_iftype type);
int ieee80211_frame_duration(enum ieee80211_band band, size_t len, int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
int rate, int erp, int short_preamble, int rate, int erp, int short_preamble,
int shift); int shift);
@ -2042,6 +2043,9 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
enum ieee80211_chanctx_mode chanmode, enum ieee80211_chanctx_mode chanmode,
u8 radar_detect); u8 radar_detect);
int ieee80211_max_num_channels(struct ieee80211_local *local); int ieee80211_max_num_channels(struct ieee80211_local *local);
enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta);
void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx);
/* TDLS */ /* TDLS */
int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
@ -2058,8 +2062,8 @@ int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy, void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
struct net_device *dev, struct net_device *dev,
const u8 *addr); const u8 *addr);
void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata, void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata);
struct sk_buff *skb); void ieee80211_tdls_chsw_work(struct work_struct *wk);
extern const struct ethtool_ops ieee80211_ethtool_ops; extern const struct ethtool_ops ieee80211_ethtool_ops;

View file

@ -1242,8 +1242,6 @@ static void ieee80211_iface_work(struct work_struct *work)
WLAN_BACK_RECIPIENT, 0, WLAN_BACK_RECIPIENT, 0,
false); false);
mutex_unlock(&local->sta_mtx); mutex_unlock(&local->sta_mtx);
} else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_TDLS_CHSW) {
ieee80211_process_tdls_channel_switch(sdata, skb);
} else if (ieee80211_is_action(mgmt->frame_control) && } else if (ieee80211_is_action(mgmt->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_BACK) { mgmt->u.action.category == WLAN_CATEGORY_BACK) {
int len = skb->len; int len = skb->len;
@ -1790,13 +1788,23 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
sband = local->hw.wiphy->bands[i]; sband = local->hw.wiphy->bands[i];
sdata->rc_rateidx_mask[i] = sdata->rc_rateidx_mask[i] =
sband ? (1 << sband->n_bitrates) - 1 : 0; sband ? (1 << sband->n_bitrates) - 1 : 0;
if (sband) if (sband) {
__le16 cap;
u16 *vht_rate_mask;
memcpy(sdata->rc_rateidx_mcs_mask[i], memcpy(sdata->rc_rateidx_mcs_mask[i],
sband->ht_cap.mcs.rx_mask, sband->ht_cap.mcs.rx_mask,
sizeof(sdata->rc_rateidx_mcs_mask[i])); sizeof(sdata->rc_rateidx_mcs_mask[i]));
else
cap = sband->vht_cap.vht_mcs.rx_mcs_map;
vht_rate_mask = sdata->rc_rateidx_vht_mcs_mask[i];
ieee80211_get_vht_mask_from_cap(cap, vht_rate_mask);
} else {
memset(sdata->rc_rateidx_mcs_mask[i], 0, memset(sdata->rc_rateidx_mcs_mask[i], 0,
sizeof(sdata->rc_rateidx_mcs_mask[i])); sizeof(sdata->rc_rateidx_mcs_mask[i]));
memset(sdata->rc_rateidx_vht_mcs_mask[i], 0,
sizeof(sdata->rc_rateidx_vht_mcs_mask[i]));
}
} }
ieee80211_set_default_queues(sdata); ieee80211_set_default_queues(sdata);

View file

@ -336,7 +336,6 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
ieee80211_check_fast_xmit(sta); ieee80211_check_fast_xmit(sta);
} else { } else {
rcu_assign_pointer(sta->gtk[idx], new); rcu_assign_pointer(sta->gtk[idx], new);
sta->gtk_idx = idx;
} }
} else { } else {
defunikey = old && defunikey = old &&

View file

@ -115,9 +115,6 @@ struct ieee80211_key {
} gen; } gen;
} u; } u;
/* number of times this key has been used */
int tx_rx_count;
#ifdef CONFIG_MAC80211_DEBUGFS #ifdef CONFIG_MAC80211_DEBUGFS
struct { struct {
struct dentry *stalink; struct dentry *stalink;

View file

@ -629,6 +629,8 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
INIT_WORK(&local->sched_scan_stopped_work, INIT_WORK(&local->sched_scan_stopped_work,
ieee80211_sched_scan_stopped_work); ieee80211_sched_scan_stopped_work);
INIT_WORK(&local->tdls_chsw_work, ieee80211_tdls_chsw_work);
spin_lock_init(&local->ack_status_lock); spin_lock_init(&local->ack_status_lock);
idr_init(&local->ack_status_frames); idr_init(&local->ack_status_frames);
@ -645,6 +647,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
skb_queue_head_init(&local->skb_queue); skb_queue_head_init(&local->skb_queue);
skb_queue_head_init(&local->skb_queue_unreliable); skb_queue_head_init(&local->skb_queue_unreliable);
skb_queue_head_init(&local->skb_queue_tdls_chsw);
ieee80211_alloc_led_names(local); ieee80211_alloc_led_names(local);
@ -1132,18 +1135,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
} }
EXPORT_SYMBOL(ieee80211_register_hw); EXPORT_SYMBOL(ieee80211_register_hw);
void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi,
struct net_device *napi_dev,
int (*poll)(struct napi_struct *, int),
int weight)
{
struct ieee80211_local *local = hw_to_local(hw);
netif_napi_add(napi_dev, napi, poll, weight);
local->napi = napi;
}
EXPORT_SYMBOL_GPL(ieee80211_napi_add);
void ieee80211_unregister_hw(struct ieee80211_hw *hw) void ieee80211_unregister_hw(struct ieee80211_hw *hw)
{ {
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
@ -1173,6 +1164,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
cancel_work_sync(&local->restart_work); cancel_work_sync(&local->restart_work);
cancel_work_sync(&local->reconfig_filter); cancel_work_sync(&local->reconfig_filter);
cancel_work_sync(&local->tdls_chsw_work);
flush_work(&local->sched_scan_stopped_work); flush_work(&local->sched_scan_stopped_work);
ieee80211_clear_tx_pending(local); ieee80211_clear_tx_pending(local);
@ -1183,6 +1175,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
wiphy_warn(local->hw.wiphy, "skb_queue not empty\n"); wiphy_warn(local->hw.wiphy, "skb_queue not empty\n");
skb_queue_purge(&local->skb_queue); skb_queue_purge(&local->skb_queue);
skb_queue_purge(&local->skb_queue_unreliable); skb_queue_purge(&local->skb_queue_unreliable);
skb_queue_purge(&local->skb_queue_tdls_chsw);
destroy_workqueue(local->workqueue); destroy_workqueue(local->workqueue);
wiphy_unregister(local->hw.wiphy); wiphy_unregister(local->hw.wiphy);

View file

@ -158,7 +158,7 @@ void mesh_sta_cleanup(struct sta_info *sta)
changed = mesh_accept_plinks_update(sdata); changed = mesh_accept_plinks_update(sdata);
if (!sdata->u.mesh.user_mpm) { if (!sdata->u.mesh.user_mpm) {
changed |= mesh_plink_deactivate(sta); changed |= mesh_plink_deactivate(sta);
del_timer_sync(&sta->plink_timer); del_timer_sync(&sta->mesh->plink_timer);
} }
if (changed) if (changed)

View file

@ -19,15 +19,6 @@
#define MAX_PREQ_QUEUE_LEN 64 #define MAX_PREQ_QUEUE_LEN 64
/* Destination only */
#define MP_F_DO 0x1
/* Reply and forward */
#define MP_F_RF 0x2
/* Unknown Sequence Number */
#define MP_F_USN 0x01
/* Reason code Present */
#define MP_F_RCODE 0x02
static void mesh_queue_preq(struct mesh_path *, u8); static void mesh_queue_preq(struct mesh_path *, u8);
static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae) static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae)
@ -79,6 +70,12 @@ static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)
#define MSEC_TO_TU(x) (x*1000/1024) #define MSEC_TO_TU(x) (x*1000/1024)
#define SN_GT(x, y) ((s32)(y - x) < 0) #define SN_GT(x, y) ((s32)(y - x) < 0)
#define SN_LT(x, y) ((s32)(x - y) < 0) #define SN_LT(x, y) ((s32)(x - y) < 0)
#define MAX_SANE_SN_DELTA 32
static inline u32 SN_DELTA(u32 x, u32 y)
{
return x >= y ? x - y : y - x;
}
#define net_traversal_jiffies(s) \ #define net_traversal_jiffies(s) \
msecs_to_jiffies(s->u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime) msecs_to_jiffies(s->u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime)
@ -279,15 +276,10 @@ int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,
*pos++ = ttl; *pos++ = ttl;
/* number of destinations */ /* number of destinations */
*pos++ = 1; *pos++ = 1;
/* /* Flags field has AE bit only as defined in
* flags bit, bit 1 is unset if we know the sequence number and * sec 8.4.2.117 IEEE802.11-2012
* bit 2 is set if we have a reason code
*/ */
*pos = 0; *pos = 0;
if (!target_sn)
*pos |= MP_F_USN;
if (target_rcode)
*pos |= MP_F_RCODE;
pos++; pos++;
memcpy(pos, target, ETH_ALEN); memcpy(pos, target, ETH_ALEN);
pos += ETH_ALEN; pos += ETH_ALEN;
@ -316,8 +308,9 @@ void ieee80211s_update_metric(struct ieee80211_local *local,
failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK); failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK);
/* moving average, scaled to 100 */ /* moving average, scaled to 100 */
sta->fail_avg = ((80 * sta->fail_avg + 5) / 100 + 20 * failed); sta->mesh->fail_avg =
if (sta->fail_avg > 95) ((80 * sta->mesh->fail_avg + 5) / 100 + 20 * failed);
if (sta->mesh->fail_avg > 95)
mesh_plink_broken(sta); mesh_plink_broken(sta);
} }
@ -333,7 +326,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
u32 tx_time, estimated_retx; u32 tx_time, estimated_retx;
u64 result; u64 result;
if (sta->fail_avg >= 100) if (sta->mesh->fail_avg >= 100)
return MAX_METRIC; return MAX_METRIC;
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo); sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo);
@ -341,7 +334,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
if (WARN_ON(!rate)) if (WARN_ON(!rate))
return MAX_METRIC; return MAX_METRIC;
err = (sta->fail_avg << ARITH_SHIFT) / 100; err = (sta->mesh->fail_avg << ARITH_SHIFT) / 100;
/* bitrate is in units of 100 Kbps, while we need rate in units of /* bitrate is in units of 100 Kbps, while we need rate in units of
* 1Mbps. This will be corrected on tx_time computation. * 1Mbps. This will be corrected on tx_time computation.
@ -441,6 +434,26 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
process = false; process = false;
fresh_info = false; fresh_info = false;
} }
} else if (!(mpath->flags & MESH_PATH_ACTIVE)) {
bool have_sn, newer_sn, bounced;
have_sn = mpath->flags & MESH_PATH_SN_VALID;
newer_sn = have_sn && SN_GT(orig_sn, mpath->sn);
bounced = have_sn &&
(SN_DELTA(orig_sn, mpath->sn) >
MAX_SANE_SN_DELTA);
if (!have_sn || newer_sn) {
/* if SN is newer than what we had
* then we can take it */;
} else if (bounced) {
/* if SN is way different than what
* we had then assume the other side
* rebooted or restarted */;
} else {
process = false;
fresh_info = false;
}
} }
} else { } else {
mpath = mesh_path_add(sdata, orig_addr); mpath = mesh_path_add(sdata, orig_addr);
@ -570,15 +583,13 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
SN_LT(mpath->sn, target_sn)) { SN_LT(mpath->sn, target_sn)) {
mpath->sn = target_sn; mpath->sn = target_sn;
mpath->flags |= MESH_PATH_SN_VALID; mpath->flags |= MESH_PATH_SN_VALID;
} else if ((!(target_flags & MP_F_DO)) && } else if ((!(target_flags & IEEE80211_PREQ_TO_FLAG)) &&
(mpath->flags & MESH_PATH_ACTIVE)) { (mpath->flags & MESH_PATH_ACTIVE)) {
reply = true; reply = true;
target_metric = mpath->metric; target_metric = mpath->metric;
target_sn = mpath->sn; target_sn = mpath->sn;
if (target_flags & MP_F_RF) /* Case E2 of sec 13.10.9.3 IEEE 802.11-2012*/
target_flags |= MP_F_DO; target_flags |= IEEE80211_PREQ_TO_FLAG;
else
forward = false;
} }
} }
rcu_read_unlock(); rcu_read_unlock();
@ -736,9 +747,12 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
if (mpath->flags & MESH_PATH_ACTIVE && if (mpath->flags & MESH_PATH_ACTIVE &&
ether_addr_equal(ta, sta->sta.addr) && ether_addr_equal(ta, sta->sta.addr) &&
(!(mpath->flags & MESH_PATH_SN_VALID) || (!(mpath->flags & MESH_PATH_SN_VALID) ||
SN_GT(target_sn, mpath->sn))) { SN_GT(target_sn, mpath->sn) || target_sn == 0)) {
mpath->flags &= ~MESH_PATH_ACTIVE; mpath->flags &= ~MESH_PATH_ACTIVE;
mpath->sn = target_sn; if (target_sn != 0)
mpath->sn = target_sn;
else
mpath->sn += 1;
spin_unlock_bh(&mpath->state_lock); spin_unlock_bh(&mpath->state_lock);
if (!ifmsh->mshcfg.dot11MeshForwarding) if (!ifmsh->mshcfg.dot11MeshForwarding)
goto endperr; goto endperr;
@ -862,7 +876,7 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
rcu_read_lock(); rcu_read_lock();
sta = sta_info_get(sdata, mgmt->sa); sta = sta_info_get(sdata, mgmt->sa);
if (!sta || sta->plink_state != NL80211_PLINK_ESTAB) { if (!sta || sta->mesh->plink_state != NL80211_PLINK_ESTAB) {
rcu_read_unlock(); rcu_read_unlock();
return; return;
} }
@ -974,7 +988,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_preq_queue *preq_node; struct mesh_preq_queue *preq_node;
struct mesh_path *mpath; struct mesh_path *mpath;
u8 ttl, target_flags; u8 ttl, target_flags = 0;
const u8 *da; const u8 *da;
u32 lifetime; u32 lifetime;
@ -1033,9 +1047,9 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
} }
if (preq_node->flags & PREQ_Q_F_REFRESH) if (preq_node->flags & PREQ_Q_F_REFRESH)
target_flags = MP_F_DO; target_flags |= IEEE80211_PREQ_TO_FLAG;
else else
target_flags = MP_F_RF; target_flags &= ~IEEE80211_PREQ_TO_FLAG;
spin_unlock_bh(&mpath->state_lock); spin_unlock_bh(&mpath->state_lock);
da = (mpath->is_root) ? mpath->rann_snd_addr : broadcast_addr; da = (mpath->is_root) ? mpath->rann_snd_addr : broadcast_addr;
@ -1176,7 +1190,9 @@ void mesh_path_timer(unsigned long data)
spin_unlock_bh(&mpath->state_lock); spin_unlock_bh(&mpath->state_lock);
mesh_queue_preq(mpath, 0); mesh_queue_preq(mpath, 0);
} else { } else {
mpath->flags = 0; mpath->flags &= ~(MESH_PATH_RESOLVING |
MESH_PATH_RESOLVED |
MESH_PATH_REQ_QUEUED);
mpath->exp_time = jiffies; mpath->exp_time = jiffies;
spin_unlock_bh(&mpath->state_lock); spin_unlock_bh(&mpath->state_lock);
if (!mpath->is_gate && mesh_gate_num(sdata) > 0) { if (!mpath->is_gate && mesh_gate_num(sdata) > 0) {

View file

@ -13,10 +13,11 @@
#include "rate.h" #include "rate.h"
#include "mesh.h" #include "mesh.h"
#define PLINK_CNF_AID(mgmt) ((mgmt)->u.action.u.self_prot.variable + 2)
#define PLINK_GET_LLID(p) (p + 2) #define PLINK_GET_LLID(p) (p + 2)
#define PLINK_GET_PLID(p) (p + 4) #define PLINK_GET_PLID(p) (p + 4)
#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \ #define mod_plink_timer(s, t) (mod_timer(&s->mesh->plink_timer, \
jiffies + msecs_to_jiffies(t))) jiffies + msecs_to_jiffies(t)))
enum plink_event { enum plink_event {
@ -53,18 +54,13 @@ static const char * const mplevents[] = {
[CLS_IGNR] = "CLS_IGNR" [CLS_IGNR] = "CLS_IGNR"
}; };
static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
enum ieee80211_self_protected_actioncode action,
u8 *da, u16 llid, u16 plid, u16 reason);
/* We only need a valid sta if user configured a minimum rssi_threshold. */ /* We only need a valid sta if user configured a minimum rssi_threshold. */
static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata, static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta) struct sta_info *sta)
{ {
s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold; s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold;
return rssi_threshold == 0 || return rssi_threshold == 0 ||
(sta && (s8) -ewma_read(&sta->avg_signal) > rssi_threshold); (sta && (s8) -ewma_signal_read(&sta->avg_signal) > rssi_threshold);
} }
/** /**
@ -72,14 +68,14 @@ static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata,
* *
* @sta: mesh peer link to restart * @sta: mesh peer link to restart
* *
* Locking: this function must be called holding sta->plink_lock * Locking: this function must be called holding sta->mesh->plink_lock
*/ */
static inline void mesh_plink_fsm_restart(struct sta_info *sta) static inline void mesh_plink_fsm_restart(struct sta_info *sta)
{ {
lockdep_assert_held(&sta->plink_lock); lockdep_assert_held(&sta->mesh->plink_lock);
sta->plink_state = NL80211_PLINK_LISTEN; sta->mesh->plink_state = NL80211_PLINK_LISTEN;
sta->llid = sta->plid = sta->reason = 0; sta->mesh->llid = sta->mesh->plid = sta->mesh->reason = 0;
sta->plink_retries = 0; sta->mesh->plink_retries = 0;
} }
/* /*
@ -119,7 +115,7 @@ static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata)
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) { list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sdata != sta->sdata || if (sdata != sta->sdata ||
sta->plink_state != NL80211_PLINK_ESTAB) sta->mesh->plink_state != NL80211_PLINK_ESTAB)
continue; continue;
short_slot = false; short_slot = false;
@ -169,7 +165,7 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) { list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sdata != sta->sdata || if (sdata != sta->sdata ||
sta->plink_state != NL80211_PLINK_ESTAB) sta->mesh->plink_state != NL80211_PLINK_ESTAB)
continue; continue;
if (sta->sta.bandwidth > IEEE80211_STA_RX_BW_20) if (sta->sta.bandwidth > IEEE80211_STA_RX_BW_20)
@ -204,59 +200,8 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
return BSS_CHANGED_HT; return BSS_CHANGED_HT;
} }
/**
* __mesh_plink_deactivate - deactivate mesh peer link
*
* @sta: mesh peer link to deactivate
*
* All mesh paths with this peer as next hop will be flushed
* Returns beacon changed flag if the beacon content changed.
*
* Locking: the caller must hold sta->plink_lock
*/
static u32 __mesh_plink_deactivate(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
u32 changed = 0;
lockdep_assert_held(&sta->plink_lock);
if (sta->plink_state == NL80211_PLINK_ESTAB)
changed = mesh_plink_dec_estab_count(sdata);
sta->plink_state = NL80211_PLINK_BLOCKED;
mesh_path_flush_by_nexthop(sta);
ieee80211_mps_sta_status_update(sta);
changed |= ieee80211_mps_set_sta_local_pm(sta,
NL80211_MESH_POWER_UNKNOWN);
return changed;
}
/**
* mesh_plink_deactivate - deactivate mesh peer link
*
* @sta: mesh peer link to deactivate
*
* All mesh paths with this peer as next hop will be flushed
*/
u32 mesh_plink_deactivate(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
u32 changed;
spin_lock_bh(&sta->plink_lock);
changed = __mesh_plink_deactivate(sta);
sta->reason = WLAN_REASON_MESH_PEER_CANCELED;
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
sta->sta.addr, sta->llid, sta->plid,
sta->reason);
spin_unlock_bh(&sta->plink_lock);
return changed;
}
static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
enum ieee80211_self_protected_actioncode action, enum ieee80211_self_protected_actioncode action,
u8 *da, u16 llid, u16 plid, u16 reason) u8 *da, u16 llid, u16 plid, u16 reason)
{ {
@ -306,7 +251,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
if (action == WLAN_SP_MESH_PEERING_CONFIRM) { if (action == WLAN_SP_MESH_PEERING_CONFIRM) {
/* AID */ /* AID */
pos = skb_put(skb, 2); pos = skb_put(skb, 2);
put_unaligned_le16(plid, pos); put_unaligned_le16(sta->sta.aid, pos);
} }
if (ieee80211_add_srates_ie(sdata, skb, true, band) || if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
ieee80211_add_ext_srates_ie(sdata, skb, true, band) || ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
@ -375,6 +320,58 @@ free:
return err; return err;
} }
/**
* __mesh_plink_deactivate - deactivate mesh peer link
*
* @sta: mesh peer link to deactivate
*
* All mesh paths with this peer as next hop will be flushed
* Returns beacon changed flag if the beacon content changed.
*
* Locking: the caller must hold sta->mesh->plink_lock
*/
static u32 __mesh_plink_deactivate(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
u32 changed = 0;
lockdep_assert_held(&sta->mesh->plink_lock);
if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
changed = mesh_plink_dec_estab_count(sdata);
sta->mesh->plink_state = NL80211_PLINK_BLOCKED;
mesh_path_flush_by_nexthop(sta);
ieee80211_mps_sta_status_update(sta);
changed |= ieee80211_mps_set_sta_local_pm(sta,
NL80211_MESH_POWER_UNKNOWN);
return changed;
}
/**
* mesh_plink_deactivate - deactivate mesh peer link
*
* @sta: mesh peer link to deactivate
*
* All mesh paths with this peer as next hop will be flushed
*/
u32 mesh_plink_deactivate(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
u32 changed;
spin_lock_bh(&sta->mesh->plink_lock);
changed = __mesh_plink_deactivate(sta);
sta->mesh->reason = WLAN_REASON_MESH_PEER_CANCELED;
mesh_plink_frame_tx(sdata, sta, WLAN_SP_MESH_PEERING_CLOSE,
sta->sta.addr, sta->mesh->llid, sta->mesh->plid,
sta->mesh->reason);
spin_unlock_bh(&sta->mesh->plink_lock);
return changed;
}
static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, struct sta_info *sta,
struct ieee802_11_elems *elems, bool insert) struct ieee802_11_elems *elems, bool insert)
@ -388,13 +385,14 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates); rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
spin_lock_bh(&sta->plink_lock); spin_lock_bh(&sta->mesh->plink_lock);
sta->last_rx = jiffies; sta->last_rx = jiffies;
/* rates and capabilities don't change during peering */ /* rates and capabilities don't change during peering */
if (sta->plink_state == NL80211_PLINK_ESTAB && sta->processed_beacon) if (sta->mesh->plink_state == NL80211_PLINK_ESTAB &&
sta->mesh->processed_beacon)
goto out; goto out;
sta->processed_beacon = true; sta->mesh->processed_beacon = true;
if (sta->sta.supp_rates[band] != rates) if (sta->sta.supp_rates[band] != rates)
changed |= IEEE80211_RC_SUPP_RATES_CHANGED; changed |= IEEE80211_RC_SUPP_RATES_CHANGED;
@ -421,23 +419,57 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
else else
rate_control_rate_update(local, sband, sta, changed); rate_control_rate_update(local, sband, sta, changed);
out: out:
spin_unlock_bh(&sta->plink_lock); spin_unlock_bh(&sta->mesh->plink_lock);
}
static int mesh_allocate_aid(struct ieee80211_sub_if_data *sdata)
{
struct sta_info *sta;
unsigned long *aid_map;
int aid;
aid_map = kcalloc(BITS_TO_LONGS(IEEE80211_MAX_AID + 1),
sizeof(*aid_map), GFP_KERNEL);
if (!aid_map)
return -ENOMEM;
/* reserve aid 0 for mcast indication */
__set_bit(0, aid_map);
rcu_read_lock();
list_for_each_entry_rcu(sta, &sdata->local->sta_list, list)
__set_bit(sta->sta.aid, aid_map);
rcu_read_unlock();
aid = find_first_zero_bit(aid_map, IEEE80211_MAX_AID + 1);
kfree(aid_map);
if (aid > IEEE80211_MAX_AID)
return -ENOBUFS;
return aid;
} }
static struct sta_info * static struct sta_info *
__mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr) __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
{ {
struct sta_info *sta; struct sta_info *sta;
int aid;
if (sdata->local->num_sta >= MESH_MAX_PLINKS) if (sdata->local->num_sta >= MESH_MAX_PLINKS)
return NULL; return NULL;
aid = mesh_allocate_aid(sdata);
if (aid < 0)
return NULL;
sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
if (!sta) if (!sta)
return NULL; return NULL;
sta->plink_state = NL80211_PLINK_LISTEN; sta->mesh->plink_state = NL80211_PLINK_LISTEN;
sta->sta.wme = true; sta->sta.wme = true;
sta->sta.aid = aid;
sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
@ -524,7 +556,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
goto out; goto out;
if (mesh_peer_accepts_plinks(elems) && if (mesh_peer_accepts_plinks(elems) &&
sta->plink_state == NL80211_PLINK_LISTEN && sta->mesh->plink_state == NL80211_PLINK_LISTEN &&
sdata->u.mesh.accepting_plinks && sdata->u.mesh.accepting_plinks &&
sdata->u.mesh.mshcfg.auto_open_plinks && sdata->u.mesh.mshcfg.auto_open_plinks &&
rssi_threshold_check(sdata, sta)) rssi_threshold_check(sdata, sta))
@ -554,52 +586,52 @@ static void mesh_plink_timer(unsigned long data)
if (sta->sdata->local->quiescing) if (sta->sdata->local->quiescing)
return; return;
spin_lock_bh(&sta->plink_lock); spin_lock_bh(&sta->mesh->plink_lock);
/* If a timer fires just before a state transition on another CPU, /* If a timer fires just before a state transition on another CPU,
* we may have already extended the timeout and changed state by the * we may have already extended the timeout and changed state by the
* time we've acquired the lock and arrived here. In that case, * time we've acquired the lock and arrived here. In that case,
* skip this timer and wait for the new one. * skip this timer and wait for the new one.
*/ */
if (time_before(jiffies, sta->plink_timer.expires)) { if (time_before(jiffies, sta->mesh->plink_timer.expires)) {
mpl_dbg(sta->sdata, mpl_dbg(sta->sdata,
"Ignoring timer for %pM in state %s (timer adjusted)", "Ignoring timer for %pM in state %s (timer adjusted)",
sta->sta.addr, mplstates[sta->plink_state]); sta->sta.addr, mplstates[sta->mesh->plink_state]);
spin_unlock_bh(&sta->plink_lock); spin_unlock_bh(&sta->mesh->plink_lock);
return; return;
} }
/* del_timer() and handler may race when entering these states */ /* del_timer() and handler may race when entering these states */
if (sta->plink_state == NL80211_PLINK_LISTEN || if (sta->mesh->plink_state == NL80211_PLINK_LISTEN ||
sta->plink_state == NL80211_PLINK_ESTAB) { sta->mesh->plink_state == NL80211_PLINK_ESTAB) {
mpl_dbg(sta->sdata, mpl_dbg(sta->sdata,
"Ignoring timer for %pM in state %s (timer deleted)", "Ignoring timer for %pM in state %s (timer deleted)",
sta->sta.addr, mplstates[sta->plink_state]); sta->sta.addr, mplstates[sta->mesh->plink_state]);
spin_unlock_bh(&sta->plink_lock); spin_unlock_bh(&sta->mesh->plink_lock);
return; return;
} }
mpl_dbg(sta->sdata, mpl_dbg(sta->sdata,
"Mesh plink timer for %pM fired on state %s\n", "Mesh plink timer for %pM fired on state %s\n",
sta->sta.addr, mplstates[sta->plink_state]); sta->sta.addr, mplstates[sta->mesh->plink_state]);
sdata = sta->sdata; sdata = sta->sdata;
mshcfg = &sdata->u.mesh.mshcfg; mshcfg = &sdata->u.mesh.mshcfg;
switch (sta->plink_state) { switch (sta->mesh->plink_state) {
case NL80211_PLINK_OPN_RCVD: case NL80211_PLINK_OPN_RCVD:
case NL80211_PLINK_OPN_SNT: case NL80211_PLINK_OPN_SNT:
/* retry timer */ /* retry timer */
if (sta->plink_retries < mshcfg->dot11MeshMaxRetries) { if (sta->mesh->plink_retries < mshcfg->dot11MeshMaxRetries) {
u32 rand; u32 rand;
mpl_dbg(sta->sdata, mpl_dbg(sta->sdata,
"Mesh plink for %pM (retry, timeout): %d %d\n", "Mesh plink for %pM (retry, timeout): %d %d\n",
sta->sta.addr, sta->plink_retries, sta->sta.addr, sta->mesh->plink_retries,
sta->plink_timeout); sta->mesh->plink_timeout);
get_random_bytes(&rand, sizeof(u32)); get_random_bytes(&rand, sizeof(u32));
sta->plink_timeout = sta->plink_timeout + sta->mesh->plink_timeout = sta->mesh->plink_timeout +
rand % sta->plink_timeout; rand % sta->mesh->plink_timeout;
++sta->plink_retries; ++sta->mesh->plink_retries;
mod_plink_timer(sta, sta->plink_timeout); mod_plink_timer(sta, sta->mesh->plink_timeout);
action = WLAN_SP_MESH_PEERING_OPEN; action = WLAN_SP_MESH_PEERING_OPEN;
break; break;
} }
@ -609,31 +641,31 @@ static void mesh_plink_timer(unsigned long data)
/* confirm timer */ /* confirm timer */
if (!reason) if (!reason)
reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT; reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT;
sta->plink_state = NL80211_PLINK_HOLDING; sta->mesh->plink_state = NL80211_PLINK_HOLDING;
mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
action = WLAN_SP_MESH_PEERING_CLOSE; action = WLAN_SP_MESH_PEERING_CLOSE;
break; break;
case NL80211_PLINK_HOLDING: case NL80211_PLINK_HOLDING:
/* holding timer */ /* holding timer */
del_timer(&sta->plink_timer); del_timer(&sta->mesh->plink_timer);
mesh_plink_fsm_restart(sta); mesh_plink_fsm_restart(sta);
break; break;
default: default:
break; break;
} }
spin_unlock_bh(&sta->plink_lock); spin_unlock_bh(&sta->mesh->plink_lock);
if (action) if (action)
mesh_plink_frame_tx(sdata, action, sta->sta.addr, mesh_plink_frame_tx(sdata, sta, action, sta->sta.addr,
sta->llid, sta->plid, reason); sta->mesh->llid, sta->mesh->plid, reason);
} }
static inline void mesh_plink_timer_set(struct sta_info *sta, u32 timeout) static inline void mesh_plink_timer_set(struct sta_info *sta, u32 timeout)
{ {
sta->plink_timer.expires = jiffies + msecs_to_jiffies(timeout); sta->mesh->plink_timer.expires = jiffies + msecs_to_jiffies(timeout);
sta->plink_timer.data = (unsigned long) sta; sta->mesh->plink_timer.data = (unsigned long) sta;
sta->plink_timer.function = mesh_plink_timer; sta->mesh->plink_timer.function = mesh_plink_timer;
sta->plink_timeout = timeout; sta->mesh->plink_timeout = timeout;
add_timer(&sta->plink_timer); add_timer(&sta->mesh->plink_timer);
} }
static bool llid_in_use(struct ieee80211_sub_if_data *sdata, static bool llid_in_use(struct ieee80211_sub_if_data *sdata,
@ -645,7 +677,7 @@ static bool llid_in_use(struct ieee80211_sub_if_data *sdata,
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) { list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (!memcmp(&sta->llid, &llid, sizeof(llid))) { if (!memcmp(&sta->mesh->llid, &llid, sizeof(llid))) {
in_use = true; in_use = true;
break; break;
} }
@ -661,8 +693,6 @@ static u16 mesh_get_new_llid(struct ieee80211_sub_if_data *sdata)
do { do {
get_random_bytes(&llid, sizeof(llid)); get_random_bytes(&llid, sizeof(llid));
/* for mesh PS we still only have the AID range for TIM bits */
llid = (llid % IEEE80211_MAX_AID) + 1;
} while (llid_in_use(sdata, llid)); } while (llid_in_use(sdata, llid));
return llid; return llid;
@ -676,16 +706,16 @@ u32 mesh_plink_open(struct sta_info *sta)
if (!test_sta_flag(sta, WLAN_STA_AUTH)) if (!test_sta_flag(sta, WLAN_STA_AUTH))
return 0; return 0;
spin_lock_bh(&sta->plink_lock); spin_lock_bh(&sta->mesh->plink_lock);
sta->llid = mesh_get_new_llid(sdata); sta->mesh->llid = mesh_get_new_llid(sdata);
if (sta->plink_state != NL80211_PLINK_LISTEN && if (sta->mesh->plink_state != NL80211_PLINK_LISTEN &&
sta->plink_state != NL80211_PLINK_BLOCKED) { sta->mesh->plink_state != NL80211_PLINK_BLOCKED) {
spin_unlock_bh(&sta->plink_lock); spin_unlock_bh(&sta->mesh->plink_lock);
return 0; return 0;
} }
sta->plink_state = NL80211_PLINK_OPN_SNT; sta->mesh->plink_state = NL80211_PLINK_OPN_SNT;
mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout); mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout);
spin_unlock_bh(&sta->plink_lock); spin_unlock_bh(&sta->mesh->plink_lock);
mpl_dbg(sdata, mpl_dbg(sdata,
"Mesh plink: starting establishment with %pM\n", "Mesh plink: starting establishment with %pM\n",
sta->sta.addr); sta->sta.addr);
@ -693,8 +723,8 @@ u32 mesh_plink_open(struct sta_info *sta)
/* set the non-peer mode to active during peering */ /* set the non-peer mode to active during peering */
changed = ieee80211_mps_local_status_update(sdata); changed = ieee80211_mps_local_status_update(sdata);
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, mesh_plink_frame_tx(sdata, sta, WLAN_SP_MESH_PEERING_OPEN,
sta->sta.addr, sta->llid, 0, 0); sta->sta.addr, sta->mesh->llid, 0, 0);
return changed; return changed;
} }
@ -702,10 +732,10 @@ u32 mesh_plink_block(struct sta_info *sta)
{ {
u32 changed; u32 changed;
spin_lock_bh(&sta->plink_lock); spin_lock_bh(&sta->mesh->plink_lock);
changed = __mesh_plink_deactivate(sta); changed = __mesh_plink_deactivate(sta);
sta->plink_state = NL80211_PLINK_BLOCKED; sta->mesh->plink_state = NL80211_PLINK_BLOCKED;
spin_unlock_bh(&sta->plink_lock); spin_unlock_bh(&sta->mesh->plink_lock);
return changed; return changed;
} }
@ -715,12 +745,11 @@ static void mesh_plink_close(struct ieee80211_sub_if_data *sdata,
enum plink_event event) enum plink_event event)
{ {
struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
u16 reason = (event == CLS_ACPT) ? u16 reason = (event == CLS_ACPT) ?
WLAN_REASON_MESH_CLOSE : WLAN_REASON_MESH_CONFIG; WLAN_REASON_MESH_CLOSE : WLAN_REASON_MESH_CONFIG;
sta->reason = reason; sta->mesh->reason = reason;
sta->plink_state = NL80211_PLINK_HOLDING; sta->mesh->plink_state = NL80211_PLINK_HOLDING;
mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
} }
@ -730,8 +759,8 @@ static u32 mesh_plink_establish(struct ieee80211_sub_if_data *sdata,
struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
u32 changed = 0; u32 changed = 0;
del_timer(&sta->plink_timer); del_timer(&sta->mesh->plink_timer);
sta->plink_state = NL80211_PLINK_ESTAB; sta->mesh->plink_state = NL80211_PLINK_ESTAB;
changed |= mesh_plink_inc_estab_count(sdata); changed |= mesh_plink_inc_estab_count(sdata);
changed |= mesh_set_ht_prot_mode(sdata); changed |= mesh_set_ht_prot_mode(sdata);
changed |= mesh_set_short_slot_time(sdata); changed |= mesh_set_short_slot_time(sdata);
@ -758,18 +787,18 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
u32 changed = 0; u32 changed = 0;
mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr, mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr,
mplstates[sta->plink_state], mplevents[event]); mplstates[sta->mesh->plink_state], mplevents[event]);
spin_lock_bh(&sta->plink_lock); spin_lock_bh(&sta->mesh->plink_lock);
switch (sta->plink_state) { switch (sta->mesh->plink_state) {
case NL80211_PLINK_LISTEN: case NL80211_PLINK_LISTEN:
switch (event) { switch (event) {
case CLS_ACPT: case CLS_ACPT:
mesh_plink_fsm_restart(sta); mesh_plink_fsm_restart(sta);
break; break;
case OPN_ACPT: case OPN_ACPT:
sta->plink_state = NL80211_PLINK_OPN_RCVD; sta->mesh->plink_state = NL80211_PLINK_OPN_RCVD;
sta->llid = mesh_get_new_llid(sdata); sta->mesh->llid = mesh_get_new_llid(sdata);
mesh_plink_timer_set(sta, mesh_plink_timer_set(sta,
mshcfg->dot11MeshRetryTimeout); mshcfg->dot11MeshRetryTimeout);
@ -791,11 +820,11 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
break; break;
case OPN_ACPT: case OPN_ACPT:
/* retry timer is left untouched */ /* retry timer is left untouched */
sta->plink_state = NL80211_PLINK_OPN_RCVD; sta->mesh->plink_state = NL80211_PLINK_OPN_RCVD;
action = WLAN_SP_MESH_PEERING_CONFIRM; action = WLAN_SP_MESH_PEERING_CONFIRM;
break; break;
case CNF_ACPT: case CNF_ACPT:
sta->plink_state = NL80211_PLINK_CNF_RCVD; sta->mesh->plink_state = NL80211_PLINK_CNF_RCVD;
mod_plink_timer(sta, mshcfg->dot11MeshConfirmTimeout); mod_plink_timer(sta, mshcfg->dot11MeshConfirmTimeout);
break; break;
default: default:
@ -855,7 +884,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
case NL80211_PLINK_HOLDING: case NL80211_PLINK_HOLDING:
switch (event) { switch (event) {
case CLS_ACPT: case CLS_ACPT:
del_timer(&sta->plink_timer); del_timer(&sta->mesh->plink_timer);
mesh_plink_fsm_restart(sta); mesh_plink_fsm_restart(sta);
break; break;
case OPN_ACPT: case OPN_ACPT:
@ -874,17 +903,18 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
*/ */
break; break;
} }
spin_unlock_bh(&sta->plink_lock); spin_unlock_bh(&sta->mesh->plink_lock);
if (action) { if (action) {
mesh_plink_frame_tx(sdata, action, sta->sta.addr, mesh_plink_frame_tx(sdata, sta, action, sta->sta.addr,
sta->llid, sta->plid, sta->reason); sta->mesh->llid, sta->mesh->plid,
sta->mesh->reason);
/* also send confirm in open case */ /* also send confirm in open case */
if (action == WLAN_SP_MESH_PEERING_OPEN) { if (action == WLAN_SP_MESH_PEERING_OPEN) {
mesh_plink_frame_tx(sdata, mesh_plink_frame_tx(sdata, sta,
WLAN_SP_MESH_PEERING_CONFIRM, WLAN_SP_MESH_PEERING_CONFIRM,
sta->sta.addr, sta->llid, sta->sta.addr, sta->mesh->llid,
sta->plid, 0); sta->mesh->plid, 0);
} }
} }
@ -939,7 +969,7 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n"); mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n");
goto out; goto out;
} }
if (sta->plink_state == NL80211_PLINK_BLOCKED) if (sta->mesh->plink_state == NL80211_PLINK_BLOCKED)
goto out; goto out;
} }
@ -954,7 +984,7 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
if (!matches_local) if (!matches_local)
event = OPN_RJCT; event = OPN_RJCT;
if (!mesh_plink_free_count(sdata) || if (!mesh_plink_free_count(sdata) ||
(sta->plid && sta->plid != plid)) (sta->mesh->plid && sta->mesh->plid != plid))
event = OPN_IGNR; event = OPN_IGNR;
else else
event = OPN_ACPT; event = OPN_ACPT;
@ -963,14 +993,14 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
if (!matches_local) if (!matches_local)
event = CNF_RJCT; event = CNF_RJCT;
if (!mesh_plink_free_count(sdata) || if (!mesh_plink_free_count(sdata) ||
sta->llid != llid || sta->mesh->llid != llid ||
(sta->plid && sta->plid != plid)) (sta->mesh->plid && sta->mesh->plid != plid))
event = CNF_IGNR; event = CNF_IGNR;
else else
event = CNF_ACPT; event = CNF_ACPT;
break; break;
case WLAN_SP_MESH_PEERING_CLOSE: case WLAN_SP_MESH_PEERING_CLOSE:
if (sta->plink_state == NL80211_PLINK_ESTAB) if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
/* Do not check for llid or plid. This does not /* Do not check for llid or plid. This does not
* follow the standard but since multiple plinks * follow the standard but since multiple plinks
* per sta are not supported, it is necessary in * per sta are not supported, it is necessary in
@ -981,9 +1011,9 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
* restarted. * restarted.
*/ */
event = CLS_ACPT; event = CLS_ACPT;
else if (sta->plid != plid) else if (sta->mesh->plid != plid)
event = CLS_IGNR; event = CLS_IGNR;
else if (ie_len == 8 && sta->llid != llid) else if (ie_len == 8 && sta->mesh->llid != llid)
event = CLS_IGNR; event = CLS_IGNR;
else else
event = CLS_ACPT; event = CLS_ACPT;
@ -1070,9 +1100,9 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); mpl_dbg(sdata, "Mesh plink: failed to init peer!\n");
goto unlock_rcu; goto unlock_rcu;
} }
sta->plid = plid; sta->mesh->plid = plid;
} else if (!sta && event == OPN_RJCT) { } else if (!sta && event == OPN_RJCT) {
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, mesh_plink_frame_tx(sdata, NULL, WLAN_SP_MESH_PEERING_CLOSE,
mgmt->sa, 0, plid, mgmt->sa, 0, plid,
WLAN_REASON_MESH_CONFIG); WLAN_REASON_MESH_CONFIG);
goto unlock_rcu; goto unlock_rcu;
@ -1081,9 +1111,13 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
goto unlock_rcu; goto unlock_rcu;
} }
/* 802.11-2012 13.3.7.2 - update plid on CNF if not set */ if (event == CNF_ACPT) {
if (!sta->plid && event == CNF_ACPT) /* 802.11-2012 13.3.7.2 - update plid on CNF if not set */
sta->plid = plid; if (!sta->mesh->plid)
sta->mesh->plid = plid;
sta->mesh->aid = get_unaligned_le16(PLINK_CNF_AID(mgmt));
}
changed |= mesh_plink_fsm(sdata, sta, event); changed |= mesh_plink_fsm(sdata, sta, event);

View file

@ -92,16 +92,16 @@ u32 ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata)
if (sdata != sta->sdata) if (sdata != sta->sdata)
continue; continue;
switch (sta->plink_state) { switch (sta->mesh->plink_state) {
case NL80211_PLINK_OPN_SNT: case NL80211_PLINK_OPN_SNT:
case NL80211_PLINK_OPN_RCVD: case NL80211_PLINK_OPN_RCVD:
case NL80211_PLINK_CNF_RCVD: case NL80211_PLINK_CNF_RCVD:
peering = true; peering = true;
break; break;
case NL80211_PLINK_ESTAB: case NL80211_PLINK_ESTAB:
if (sta->local_pm == NL80211_MESH_POWER_LIGHT_SLEEP) if (sta->mesh->local_pm == NL80211_MESH_POWER_LIGHT_SLEEP)
light_sleep_cnt++; light_sleep_cnt++;
else if (sta->local_pm == NL80211_MESH_POWER_DEEP_SLEEP) else if (sta->mesh->local_pm == NL80211_MESH_POWER_DEEP_SLEEP)
deep_sleep_cnt++; deep_sleep_cnt++;
break; break;
default: default:
@ -153,19 +153,19 @@ u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
{ {
struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_sub_if_data *sdata = sta->sdata;
if (sta->local_pm == pm) if (sta->mesh->local_pm == pm)
return 0; return 0;
mps_dbg(sdata, "local STA operates in mode %d with %pM\n", mps_dbg(sdata, "local STA operates in mode %d with %pM\n",
pm, sta->sta.addr); pm, sta->sta.addr);
sta->local_pm = pm; sta->mesh->local_pm = pm;
/* /*
* announce peer-specific power mode transition * announce peer-specific power mode transition
* (see IEEE802.11-2012 13.14.3.2 and 13.14.3.3) * (see IEEE802.11-2012 13.14.3.2 and 13.14.3.3)
*/ */
if (sta->plink_state == NL80211_PLINK_ESTAB) if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
mps_qos_null_tx(sta); mps_qos_null_tx(sta);
return ieee80211_mps_local_status_update(sdata); return ieee80211_mps_local_status_update(sdata);
@ -197,8 +197,8 @@ void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata,
if (is_unicast_ether_addr(hdr->addr1) && if (is_unicast_ether_addr(hdr->addr1) &&
ieee80211_is_data_qos(hdr->frame_control) && ieee80211_is_data_qos(hdr->frame_control) &&
sta->plink_state == NL80211_PLINK_ESTAB) sta->mesh->plink_state == NL80211_PLINK_ESTAB)
pm = sta->local_pm; pm = sta->mesh->local_pm;
else else
pm = sdata->u.mesh.nonpeer_pm; pm = sdata->u.mesh.nonpeer_pm;
@ -241,16 +241,16 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta)
* use peer-specific power mode if peering is established and the * use peer-specific power mode if peering is established and the
* peer's power mode is known * peer's power mode is known
*/ */
if (sta->plink_state == NL80211_PLINK_ESTAB && if (sta->mesh->plink_state == NL80211_PLINK_ESTAB &&
sta->peer_pm != NL80211_MESH_POWER_UNKNOWN) sta->mesh->peer_pm != NL80211_MESH_POWER_UNKNOWN)
pm = sta->peer_pm; pm = sta->mesh->peer_pm;
else else
pm = sta->nonpeer_pm; pm = sta->mesh->nonpeer_pm;
do_buffer = (pm != NL80211_MESH_POWER_ACTIVE); do_buffer = (pm != NL80211_MESH_POWER_ACTIVE);
/* clear the MPSP flags for non-peers or active STA */ /* clear the MPSP flags for non-peers or active STA */
if (sta->plink_state != NL80211_PLINK_ESTAB) { if (sta->mesh->plink_state != NL80211_PLINK_ESTAB) {
clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
} else if (!do_buffer) { } else if (!do_buffer) {
@ -296,13 +296,13 @@ static void mps_set_sta_peer_pm(struct sta_info *sta,
pm = NL80211_MESH_POWER_ACTIVE; pm = NL80211_MESH_POWER_ACTIVE;
} }
if (sta->peer_pm == pm) if (sta->mesh->peer_pm == pm)
return; return;
mps_dbg(sta->sdata, "STA %pM enters mode %d\n", mps_dbg(sta->sdata, "STA %pM enters mode %d\n",
sta->sta.addr, pm); sta->sta.addr, pm);
sta->peer_pm = pm; sta->mesh->peer_pm = pm;
ieee80211_mps_sta_status_update(sta); ieee80211_mps_sta_status_update(sta);
} }
@ -317,13 +317,13 @@ static void mps_set_sta_nonpeer_pm(struct sta_info *sta,
else else
pm = NL80211_MESH_POWER_ACTIVE; pm = NL80211_MESH_POWER_ACTIVE;
if (sta->nonpeer_pm == pm) if (sta->mesh->nonpeer_pm == pm)
return; return;
mps_dbg(sta->sdata, "STA %pM sets non-peer mode to %d\n", mps_dbg(sta->sdata, "STA %pM sets non-peer mode to %d\n",
sta->sta.addr, pm); sta->sta.addr, pm);
sta->nonpeer_pm = pm; sta->mesh->nonpeer_pm = pm;
ieee80211_mps_sta_status_update(sta); ieee80211_mps_sta_status_update(sta);
} }
@ -552,7 +552,7 @@ void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,
} else { } else {
if (eosp) if (eosp)
clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
else if (sta->local_pm != NL80211_MESH_POWER_ACTIVE) else if (sta->mesh->local_pm != NL80211_MESH_POWER_ACTIVE)
set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
if (rspi && !test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER)) if (rspi && !test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER))
@ -577,9 +577,9 @@ void ieee80211_mps_frame_release(struct sta_info *sta,
int ac, buffer_local = 0; int ac, buffer_local = 0;
bool has_buffered = false; bool has_buffered = false;
if (sta->plink_state == NL80211_PLINK_ESTAB) if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
has_buffered = ieee80211_check_tim(elems->tim, elems->tim_len, has_buffered = ieee80211_check_tim(elems->tim, elems->tim_len,
sta->llid); sta->mesh->aid);
if (has_buffered) if (has_buffered)
mps_dbg(sta->sdata, "%pM indicates buffered frames\n", mps_dbg(sta->sdata, "%pM indicates buffered frames\n",
@ -598,7 +598,7 @@ void ieee80211_mps_frame_release(struct sta_info *sta,
if (!has_buffered && !buffer_local) if (!has_buffered && !buffer_local)
return; return;
if (sta->plink_state == NL80211_PLINK_ESTAB) if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
mpsp_trigger_send(sta, has_buffered, !buffer_local); mpsp_trigger_send(sta, has_buffered, !buffer_local);
else else
mps_frame_deliver(sta, 1); mps_frame_deliver(sta, 1);

View file

@ -127,14 +127,14 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
/* Timing offset calculation (see 13.13.2.2.2) */ /* Timing offset calculation (see 13.13.2.2.2) */
t_t = le64_to_cpu(mgmt->u.beacon.timestamp); t_t = le64_to_cpu(mgmt->u.beacon.timestamp);
sta->t_offset = t_t - t_r; sta->mesh->t_offset = t_t - t_r;
if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
s64 t_clockdrift = sta->t_offset_setpoint - sta->t_offset; s64 t_clockdrift = sta->mesh->t_offset_setpoint - sta->mesh->t_offset;
msync_dbg(sdata, msync_dbg(sdata,
"STA %pM : sta->t_offset=%lld, sta->t_offset_setpoint=%lld, t_clockdrift=%lld\n", "STA %pM : t_offset=%lld, t_offset_setpoint=%lld, t_clockdrift=%lld\n",
sta->sta.addr, (long long) sta->t_offset, sta->sta.addr, (long long) sta->mesh->t_offset,
(long long) sta->t_offset_setpoint, (long long) sta->mesh->t_offset_setpoint,
(long long) t_clockdrift); (long long) t_clockdrift);
if (t_clockdrift > TOFFSET_MAXIMUM_ADJUSTMENT || if (t_clockdrift > TOFFSET_MAXIMUM_ADJUSTMENT ||
@ -152,12 +152,12 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
ifmsh->sync_offset_clockdrift_max = t_clockdrift; ifmsh->sync_offset_clockdrift_max = t_clockdrift;
spin_unlock_bh(&ifmsh->sync_offset_lock); spin_unlock_bh(&ifmsh->sync_offset_lock);
} else { } else {
sta->t_offset_setpoint = sta->t_offset - TOFFSET_SET_MARGIN; sta->mesh->t_offset_setpoint = sta->mesh->t_offset - TOFFSET_SET_MARGIN;
set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN); set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN);
msync_dbg(sdata, msync_dbg(sdata,
"STA %pM : offset was invalid, sta->t_offset=%lld\n", "STA %pM : offset was invalid, t_offset=%lld\n",
sta->sta.addr, sta->sta.addr,
(long long) sta->t_offset); (long long) sta->mesh->t_offset);
} }
no_sync: no_sync:

View file

@ -6,6 +6,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007, Michael Wu <flamingice@sourmilk.net> * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015 Intel Deutschland GmbH
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
@ -538,11 +539,16 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
ieee80211_ie_build_ht_cap(pos, &ht_cap, cap); ieee80211_ie_build_ht_cap(pos, &ht_cap, cap);
} }
/* This function determines vht capability flags for the association
* and builds the IE.
* Note - the function may set the owner of the MU-MIMO capability
*/
static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, struct sk_buff *skb,
struct ieee80211_supported_band *sband, struct ieee80211_supported_band *sband,
struct ieee80211_vht_cap *ap_vht_cap) struct ieee80211_vht_cap *ap_vht_cap)
{ {
struct ieee80211_local *local = sdata->local;
u8 *pos; u8 *pos;
u32 cap; u32 cap;
struct ieee80211_sta_vht_cap vht_cap; struct ieee80211_sta_vht_cap vht_cap;
@ -576,7 +582,34 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
*/ */
if (!(ap_vht_cap->vht_cap_info & if (!(ap_vht_cap->vht_cap_info &
cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE))) cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)))
cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; cap &= ~(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
else if (!(ap_vht_cap->vht_cap_info &
cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)))
cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
/*
* If some other vif is using the MU-MIMO capablity we cannot associate
* using MU-MIMO - this will lead to contradictions in the group-id
* mechanism.
* Ownership is defined since association request, in order to avoid
* simultaneous associations with MU-MIMO.
*/
if (cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) {
bool disable_mu_mimo = false;
struct ieee80211_sub_if_data *other;
list_for_each_entry_rcu(other, &local->interfaces, list) {
if (other->flags & IEEE80211_SDATA_MU_MIMO_OWNER) {
disable_mu_mimo = true;
break;
}
}
if (disable_mu_mimo)
cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
else
sdata->flags |= IEEE80211_SDATA_MU_MIMO_OWNER;
}
mask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; mask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
@ -1096,24 +1129,6 @@ static void ieee80211_chswitch_timer(unsigned long data)
ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work); ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work);
} }
static void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata)
{
struct sta_info *sta;
u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
rcu_read_lock();
list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
continue;
ieee80211_tdls_oper_request(&sdata->vif, sta->sta.addr,
NL80211_TDLS_TEARDOWN, reason,
GFP_ATOMIC);
}
rcu_read_unlock();
}
static void static void
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
u64 timestamp, u32 device_timestamp, u64 timestamp, u32 device_timestamp,
@ -2076,6 +2091,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa)); memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa));
memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask)); memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask));
sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER;
sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL; sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
@ -2538,6 +2554,7 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
eth_zero_addr(sdata->u.mgd.bssid); eth_zero_addr(sdata->u.mgd.bssid);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
sdata->u.mgd.flags = 0; sdata->u.mgd.flags = 0;
sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER;
mutex_lock(&sdata->local->mtx); mutex_lock(&sdata->local->mtx);
ieee80211_vif_release_channel(sdata); ieee80211_vif_release_channel(sdata);
mutex_unlock(&sdata->local->mtx); mutex_unlock(&sdata->local->mtx);
@ -3034,12 +3051,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
rate_control_rate_init(sta); rate_control_rate_init(sta);
if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) { if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
set_sta_flag(sta, WLAN_STA_MFP); set_sta_flag(sta, WLAN_STA_MFP);
sta->sta.mfp = true;
} else {
sta->sta.mfp = false;
}
sta->sta.wme = elems.wmm_param && local->hw.queues >= IEEE80211_NUM_ACS; sta->sta.wme = elems.wmm_param && local->hw.queues >= IEEE80211_NUM_ACS;

View file

@ -179,7 +179,7 @@ int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
u32 changed = BSS_CHANGED_OCB; u32 changed = BSS_CHANGED_OCB | BSS_CHANGED_BSSID;
int err; int err;
if (ifocb->joined == true) if (ifocb->joined == true)

View file

@ -29,6 +29,65 @@ module_param(ieee80211_default_rc_algo, charp, 0644);
MODULE_PARM_DESC(ieee80211_default_rc_algo, MODULE_PARM_DESC(ieee80211_default_rc_algo,
"Default rate control algorithm for mac80211 to use"); "Default rate control algorithm for mac80211 to use");
void rate_control_rate_init(struct sta_info *sta)
{
struct ieee80211_local *local = sta->sdata->local;
struct rate_control_ref *ref = sta->rate_ctrl;
struct ieee80211_sta *ista = &sta->sta;
void *priv_sta = sta->rate_ctrl_priv;
struct ieee80211_supported_band *sband;
struct ieee80211_chanctx_conf *chanctx_conf;
ieee80211_sta_set_rx_nss(sta);
if (!ref)
return;
rcu_read_lock();
chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
if (WARN_ON(!chanctx_conf)) {
rcu_read_unlock();
return;
}
sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
spin_lock_bh(&sta->rate_ctrl_lock);
ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
priv_sta);
spin_unlock_bh(&sta->rate_ctrl_lock);
rcu_read_unlock();
set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
}
void rate_control_rate_update(struct ieee80211_local *local,
struct ieee80211_supported_band *sband,
struct sta_info *sta, u32 changed)
{
struct rate_control_ref *ref = local->rate_ctrl;
struct ieee80211_sta *ista = &sta->sta;
void *priv_sta = sta->rate_ctrl_priv;
struct ieee80211_chanctx_conf *chanctx_conf;
if (ref && ref->ops->rate_update) {
rcu_read_lock();
chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
if (WARN_ON(!chanctx_conf)) {
rcu_read_unlock();
return;
}
spin_lock_bh(&sta->rate_ctrl_lock);
ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
ista, priv_sta, changed);
spin_unlock_bh(&sta->rate_ctrl_lock);
rcu_read_unlock();
}
drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
}
int ieee80211_rate_control_register(const struct rate_control_ops *ops) int ieee80211_rate_control_register(const struct rate_control_ops *ops)
{ {
struct rate_control_alg *alg; struct rate_control_alg *alg;
@ -294,39 +353,37 @@ bool rate_control_send_low(struct ieee80211_sta *pubsta,
} }
EXPORT_SYMBOL(rate_control_send_low); EXPORT_SYMBOL(rate_control_send_low);
static bool rate_idx_match_legacy_mask(struct ieee80211_tx_rate *rate, static bool rate_idx_match_legacy_mask(s8 *rate_idx, int n_bitrates, u32 mask)
int n_bitrates, u32 mask)
{ {
int j; int j;
/* See whether the selected rate or anything below it is allowed. */ /* See whether the selected rate or anything below it is allowed. */
for (j = rate->idx; j >= 0; j--) { for (j = *rate_idx; j >= 0; j--) {
if (mask & (1 << j)) { if (mask & (1 << j)) {
/* Okay, found a suitable rate. Use it. */ /* Okay, found a suitable rate. Use it. */
rate->idx = j; *rate_idx = j;
return true; return true;
} }
} }
/* Try to find a higher rate that would be allowed */ /* Try to find a higher rate that would be allowed */
for (j = rate->idx + 1; j < n_bitrates; j++) { for (j = *rate_idx + 1; j < n_bitrates; j++) {
if (mask & (1 << j)) { if (mask & (1 << j)) {
/* Okay, found a suitable rate. Use it. */ /* Okay, found a suitable rate. Use it. */
rate->idx = j; *rate_idx = j;
return true; return true;
} }
} }
return false; return false;
} }
static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate, static bool rate_idx_match_mcs_mask(s8 *rate_idx, u8 *mcs_mask)
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
{ {
int i, j; int i, j;
int ridx, rbit; int ridx, rbit;
ridx = rate->idx / 8; ridx = *rate_idx / 8;
rbit = rate->idx % 8; rbit = *rate_idx % 8;
/* sanity check */ /* sanity check */
if (ridx < 0 || ridx >= IEEE80211_HT_MCS_MASK_LEN) if (ridx < 0 || ridx >= IEEE80211_HT_MCS_MASK_LEN)
@ -336,20 +393,20 @@ static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate,
for (i = ridx; i >= 0; i--) { for (i = ridx; i >= 0; i--) {
for (j = rbit; j >= 0; j--) for (j = rbit; j >= 0; j--)
if (mcs_mask[i] & BIT(j)) { if (mcs_mask[i] & BIT(j)) {
rate->idx = i * 8 + j; *rate_idx = i * 8 + j;
return true; return true;
} }
rbit = 7; rbit = 7;
} }
/* Try to find a higher rate that would be allowed */ /* Try to find a higher rate that would be allowed */
ridx = (rate->idx + 1) / 8; ridx = (*rate_idx + 1) / 8;
rbit = (rate->idx + 1) % 8; rbit = (*rate_idx + 1) % 8;
for (i = ridx; i < IEEE80211_HT_MCS_MASK_LEN; i++) { for (i = ridx; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
for (j = rbit; j < 8; j++) for (j = rbit; j < 8; j++)
if (mcs_mask[i] & BIT(j)) { if (mcs_mask[i] & BIT(j)) {
rate->idx = i * 8 + j; *rate_idx = i * 8 + j;
return true; return true;
} }
rbit = 0; rbit = 0;
@ -357,37 +414,93 @@ static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate,
return false; return false;
} }
static bool rate_idx_match_vht_mcs_mask(s8 *rate_idx, u16 *vht_mask)
{
int i, j;
int ridx, rbit;
ridx = *rate_idx >> 4;
rbit = *rate_idx & 0xf;
static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, if (ridx < 0 || ridx >= NL80211_VHT_NSS_MAX)
return false;
/* See whether the selected rate or anything below it is allowed. */
for (i = ridx; i >= 0; i--) {
for (j = rbit; j >= 0; j--) {
if (vht_mask[i] & BIT(j)) {
*rate_idx = (i << 4) | j;
return true;
}
}
rbit = 15;
}
/* Try to find a higher rate that would be allowed */
ridx = (*rate_idx + 1) >> 4;
rbit = (*rate_idx + 1) & 0xf;
for (i = ridx; i < NL80211_VHT_NSS_MAX; i++) {
for (j = rbit; j < 16; j++) {
if (vht_mask[i] & BIT(j)) {
*rate_idx = (i << 4) | j;
return true;
}
}
rbit = 0;
}
return false;
}
static void rate_idx_match_mask(s8 *rate_idx, u16 *rate_flags,
struct ieee80211_supported_band *sband, struct ieee80211_supported_band *sband,
enum nl80211_chan_width chan_width, enum nl80211_chan_width chan_width,
u32 mask, u32 mask,
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]) u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN],
u16 vht_mask[NL80211_VHT_NSS_MAX])
{ {
struct ieee80211_tx_rate alt_rate; if (*rate_flags & IEEE80211_TX_RC_VHT_MCS) {
/* handle VHT rates */
if (rate_idx_match_vht_mcs_mask(rate_idx, vht_mask))
return;
/* handle HT rates */ *rate_idx = 0;
if (rate->flags & IEEE80211_TX_RC_MCS) { /* keep protection flags */
if (rate_idx_match_mcs_mask(rate, mcs_mask)) *rate_flags &= (IEEE80211_TX_RC_USE_RTS_CTS |
IEEE80211_TX_RC_USE_CTS_PROTECT |
IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
*rate_flags |= IEEE80211_TX_RC_MCS;
if (chan_width == NL80211_CHAN_WIDTH_40)
*rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
if (rate_idx_match_mcs_mask(rate_idx, mcs_mask))
return; return;
/* also try the legacy rates. */ /* also try the legacy rates. */
alt_rate.idx = 0; *rate_flags &= ~(IEEE80211_TX_RC_MCS |
/* keep protection flags */ IEEE80211_TX_RC_40_MHZ_WIDTH);
alt_rate.flags = rate->flags & if (rate_idx_match_legacy_mask(rate_idx, sband->n_bitrates,
(IEEE80211_TX_RC_USE_RTS_CTS | mask))
IEEE80211_TX_RC_USE_CTS_PROTECT |
IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
alt_rate.count = rate->count;
if (rate_idx_match_legacy_mask(&alt_rate,
sband->n_bitrates, mask)) {
*rate = alt_rate;
return; return;
} } else if (*rate_flags & IEEE80211_TX_RC_MCS) {
} else if (!(rate->flags & IEEE80211_TX_RC_VHT_MCS)) { /* handle HT rates */
if (rate_idx_match_mcs_mask(rate_idx, mcs_mask))
return;
/* also try the legacy rates. */
*rate_idx = 0;
/* keep protection flags */
*rate_flags &= (IEEE80211_TX_RC_USE_RTS_CTS |
IEEE80211_TX_RC_USE_CTS_PROTECT |
IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
if (rate_idx_match_legacy_mask(rate_idx, sband->n_bitrates,
mask))
return;
} else {
/* handle legacy rates */ /* handle legacy rates */
if (rate_idx_match_legacy_mask(rate, sband->n_bitrates, mask)) if (rate_idx_match_legacy_mask(rate_idx, sband->n_bitrates,
mask))
return; return;
/* if HT BSS, and we handle a data frame, also try HT rates */ /* if HT BSS, and we handle a data frame, also try HT rates */
@ -400,23 +513,19 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
break; break;
} }
alt_rate.idx = 0; *rate_idx = 0;
/* keep protection flags */ /* keep protection flags */
alt_rate.flags = rate->flags & *rate_flags &= (IEEE80211_TX_RC_USE_RTS_CTS |
(IEEE80211_TX_RC_USE_RTS_CTS | IEEE80211_TX_RC_USE_CTS_PROTECT |
IEEE80211_TX_RC_USE_CTS_PROTECT | IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
alt_rate.count = rate->count;
alt_rate.flags |= IEEE80211_TX_RC_MCS; *rate_flags |= IEEE80211_TX_RC_MCS;
if (chan_width == NL80211_CHAN_WIDTH_40) if (chan_width == NL80211_CHAN_WIDTH_40)
alt_rate.flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; *rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
if (rate_idx_match_mcs_mask(&alt_rate, mcs_mask)) { if (rate_idx_match_mcs_mask(rate_idx, mcs_mask))
*rate = alt_rate;
return; return;
}
} }
/* /*
@ -569,18 +678,92 @@ static void rate_control_fill_sta_table(struct ieee80211_sta *sta,
} }
} }
static bool rate_control_cap_mask(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, u32 *mask,
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN],
u16 vht_mask[NL80211_VHT_NSS_MAX])
{
u32 i, flags;
*mask = sdata->rc_rateidx_mask[sband->band];
flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
for (i = 0; i < sband->n_bitrates; i++) {
if ((flags & sband->bitrates[i].flags) != flags)
*mask &= ~BIT(i);
}
if (*mask == (1 << sband->n_bitrates) - 1 &&
!sdata->rc_has_mcs_mask[sband->band] &&
!sdata->rc_has_vht_mcs_mask[sband->band])
return false;
if (sdata->rc_has_mcs_mask[sband->band])
memcpy(mcs_mask, sdata->rc_rateidx_mcs_mask[sband->band],
IEEE80211_HT_MCS_MASK_LEN);
else
memset(mcs_mask, 0xff, IEEE80211_HT_MCS_MASK_LEN);
if (sdata->rc_has_vht_mcs_mask[sband->band])
memcpy(vht_mask, sdata->rc_rateidx_vht_mcs_mask[sband->band],
sizeof(u16) * NL80211_VHT_NSS_MAX);
else
memset(vht_mask, 0xff, sizeof(u16) * NL80211_VHT_NSS_MAX);
if (sta) {
__le16 sta_vht_cap;
u16 sta_vht_mask[NL80211_VHT_NSS_MAX];
/* Filter out rates that the STA does not support */
*mask &= sta->supp_rates[sband->band];
for (i = 0; i < sizeof(mcs_mask); i++)
mcs_mask[i] &= sta->ht_cap.mcs.rx_mask[i];
sta_vht_cap = sta->vht_cap.vht_mcs.rx_mcs_map;
ieee80211_get_vht_mask_from_cap(sta_vht_cap, sta_vht_mask);
for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
vht_mask[i] &= sta_vht_mask[i];
}
return true;
}
static void
rate_control_apply_mask_ratetbl(struct sta_info *sta,
struct ieee80211_supported_band *sband,
struct ieee80211_sta_rates *rates)
{
int i;
u32 mask;
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
u16 vht_mask[NL80211_VHT_NSS_MAX];
enum nl80211_chan_width chan_width;
if (!rate_control_cap_mask(sta->sdata, sband, &sta->sta, &mask,
mcs_mask, vht_mask))
return;
chan_width = sta->sdata->vif.bss_conf.chandef.width;
for (i = 0; i < IEEE80211_TX_RATE_TABLE_SIZE; i++) {
if (rates->rate[i].idx < 0)
break;
rate_idx_match_mask(&rates->rate[i].idx, &rates->rate[i].flags,
sband, chan_width, mask, mcs_mask,
vht_mask);
}
}
static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata, static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta, struct ieee80211_sta *sta,
struct ieee80211_supported_band *sband, struct ieee80211_supported_band *sband,
struct ieee80211_tx_info *info,
struct ieee80211_tx_rate *rates, struct ieee80211_tx_rate *rates,
int max_rates) int max_rates)
{ {
enum nl80211_chan_width chan_width; enum nl80211_chan_width chan_width;
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN]; u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
bool has_mcs_mask;
u32 mask; u32 mask;
u32 rate_flags; u16 rate_flags, vht_mask[NL80211_VHT_NSS_MAX];
int i; int i;
/* /*
@ -588,30 +771,10 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
* default mask (allow all rates) is used to save some processing for * default mask (allow all rates) is used to save some processing for
* the common case. * the common case.
*/ */
mask = sdata->rc_rateidx_mask[info->band]; if (!rate_control_cap_mask(sdata, sband, sta, &mask, mcs_mask,
has_mcs_mask = sdata->rc_has_mcs_mask[info->band]; vht_mask))
rate_flags =
ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
for (i = 0; i < sband->n_bitrates; i++)
if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
mask &= ~BIT(i);
if (mask == (1 << sband->n_bitrates) - 1 && !has_mcs_mask)
return; return;
if (has_mcs_mask)
memcpy(mcs_mask, sdata->rc_rateidx_mcs_mask[info->band],
sizeof(mcs_mask));
else
memset(mcs_mask, 0xff, sizeof(mcs_mask));
if (sta) {
/* Filter out rates that the STA does not support */
mask &= sta->supp_rates[info->band];
for (i = 0; i < sizeof(mcs_mask); i++)
mcs_mask[i] &= sta->ht_cap.mcs.rx_mask[i];
}
/* /*
* Make sure the rate index selected for each TX rate is * Make sure the rate index selected for each TX rate is
* included in the configured mask and change the rate indexes * included in the configured mask and change the rate indexes
@ -623,8 +786,10 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
if (rates[i].idx < 0) if (rates[i].idx < 0)
break; break;
rate_idx_match_mask(&rates[i], sband, chan_width, mask, rate_flags = rates[i].flags;
mcs_mask); rate_idx_match_mask(&rates[i].idx, &rate_flags, sband,
chan_width, mask, mcs_mask, vht_mask);
rates[i].flags = rate_flags;
} }
} }
@ -648,7 +813,7 @@ void ieee80211_get_tx_rates(struct ieee80211_vif *vif,
sband = sdata->local->hw.wiphy->bands[info->band]; sband = sdata->local->hw.wiphy->bands[info->band];
if (ieee80211_is_data(hdr->frame_control)) if (ieee80211_is_data(hdr->frame_control))
rate_control_apply_mask(sdata, sta, sband, info, dest, max_rates); rate_control_apply_mask(sdata, sta, sband, dest, max_rates);
if (dest[0].idx < 0) if (dest[0].idx < 0)
__rate_control_send_low(&sdata->local->hw, sband, sta, info, __rate_control_send_low(&sdata->local->hw, sband, sta, info,
@ -705,7 +870,10 @@ int rate_control_set_rates(struct ieee80211_hw *hw,
{ {
struct sta_info *sta = container_of(pubsta, struct sta_info, sta); struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
struct ieee80211_sta_rates *old; struct ieee80211_sta_rates *old;
struct ieee80211_supported_band *sband;
sband = hw->wiphy->bands[ieee80211_get_sdata_band(sta->sdata)];
rate_control_apply_mask_ratetbl(sta, sband, rates);
/* /*
* mac80211 guarantees that this function will not be called * mac80211 guarantees that this function will not be called
* concurrently, so the following RCU access is safe, even without * concurrently, so the following RCU access is safe, even without

View file

@ -71,64 +71,10 @@ rate_control_tx_status_noskb(struct ieee80211_local *local,
spin_unlock_bh(&sta->rate_ctrl_lock); spin_unlock_bh(&sta->rate_ctrl_lock);
} }
static inline void rate_control_rate_init(struct sta_info *sta) void rate_control_rate_init(struct sta_info *sta);
{ void rate_control_rate_update(struct ieee80211_local *local,
struct ieee80211_local *local = sta->sdata->local;
struct rate_control_ref *ref = sta->rate_ctrl;
struct ieee80211_sta *ista = &sta->sta;
void *priv_sta = sta->rate_ctrl_priv;
struct ieee80211_supported_band *sband;
struct ieee80211_chanctx_conf *chanctx_conf;
ieee80211_sta_set_rx_nss(sta);
if (!ref)
return;
rcu_read_lock();
chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
if (WARN_ON(!chanctx_conf)) {
rcu_read_unlock();
return;
}
sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
spin_lock_bh(&sta->rate_ctrl_lock);
ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
priv_sta);
spin_unlock_bh(&sta->rate_ctrl_lock);
rcu_read_unlock();
set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
}
static inline void rate_control_rate_update(struct ieee80211_local *local,
struct ieee80211_supported_band *sband, struct ieee80211_supported_band *sband,
struct sta_info *sta, u32 changed) struct sta_info *sta, u32 changed);
{
struct rate_control_ref *ref = local->rate_ctrl;
struct ieee80211_sta *ista = &sta->sta;
void *priv_sta = sta->rate_ctrl_priv;
struct ieee80211_chanctx_conf *chanctx_conf;
if (ref && ref->ops->rate_update) {
rcu_read_lock();
chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
if (WARN_ON(!chanctx_conf)) {
rcu_read_unlock();
return;
}
spin_lock_bh(&sta->rate_ctrl_lock);
ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
ista, priv_sta, changed);
spin_unlock_bh(&sta->rate_ctrl_lock);
rcu_read_unlock();
}
drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
}
static inline void *rate_control_alloc_sta(struct rate_control_ref *ref, static inline void *rate_control_alloc_sta(struct rate_control_ref *ref,
struct sta_info *sta, gfp_t gfp) struct sta_info *sta, gfp_t gfp)

View file

@ -867,7 +867,13 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
else else
idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8; idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8;
if (offset > 0) { /* enable RTS/CTS if needed:
* - if station is in dynamic SMPS (and streams > 1)
* - for fallback rates, to increase chances of getting through
*/
if (offset > 0 &&
(mi->sta->smps_mode == IEEE80211_SMPS_DYNAMIC &&
group->streams > 1)) {
ratetbl->rate[offset].count = ratetbl->rate[offset].count_rts; ratetbl->rate[offset].count = ratetbl->rate[offset].count_rts;
flags |= IEEE80211_TX_RC_USE_RTS_CTS; flags |= IEEE80211_TX_RC_USE_RTS_CTS;
} }

View file

@ -42,6 +42,51 @@ static inline void ieee80211_rx_stats(struct net_device *dev, u32 len)
u64_stats_update_end(&tstats->syncp); u64_stats_update_end(&tstats->syncp);
} }
static u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
enum nl80211_iftype type)
{
__le16 fc = hdr->frame_control;
if (ieee80211_is_data(fc)) {
if (len < 24) /* drop incorrect hdr len (data) */
return NULL;
if (ieee80211_has_a4(fc))
return NULL;
if (ieee80211_has_tods(fc))
return hdr->addr1;
if (ieee80211_has_fromds(fc))
return hdr->addr2;
return hdr->addr3;
}
if (ieee80211_is_mgmt(fc)) {
if (len < 24) /* drop incorrect hdr len (mgmt) */
return NULL;
return hdr->addr3;
}
if (ieee80211_is_ctl(fc)) {
if (ieee80211_is_pspoll(fc))
return hdr->addr1;
if (ieee80211_is_back_req(fc)) {
switch (type) {
case NL80211_IFTYPE_STATION:
return hdr->addr2;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
return hdr->addr1;
default:
break; /* fall through to the return */
}
}
}
return NULL;
}
/* /*
* monitor mode reception * monitor mode reception
* *
@ -77,8 +122,7 @@ static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len,
hdr = (void *)(skb->data + rtap_vendor_space); hdr = (void *)(skb->data + rtap_vendor_space);
if (status->flag & (RX_FLAG_FAILED_FCS_CRC | if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
RX_FLAG_FAILED_PLCP_CRC | RX_FLAG_FAILED_PLCP_CRC))
RX_FLAG_AMPDU_IS_ZEROLEN))
return true; return true;
if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space)) if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space))
@ -346,10 +390,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS); cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS);
put_unaligned_le32(status->ampdu_reference, pos); put_unaligned_le32(status->ampdu_reference, pos);
pos += 4; pos += 4;
if (status->flag & RX_FLAG_AMPDU_REPORT_ZEROLEN)
flags |= IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN;
if (status->flag & RX_FLAG_AMPDU_IS_ZEROLEN)
flags |= IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN;
if (status->flag & RX_FLAG_AMPDU_LAST_KNOWN) if (status->flag & RX_FLAG_AMPDU_LAST_KNOWN)
flags |= IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN; flags |= IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN;
if (status->flag & RX_FLAG_AMPDU_IS_LAST) if (status->flag & RX_FLAG_AMPDU_IS_LAST)
@ -1093,11 +1133,6 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
{ {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
if (unlikely(rx->skb->len < 16)) {
I802_DEBUG_INC(rx->local->rx_handlers_drop_short);
return RX_DROP_MONITOR;
}
/* Drop disallowed frame classes based on STA auth/assoc state; /* Drop disallowed frame classes based on STA auth/assoc state;
* IEEE 802.11, Chap 5.5. * IEEE 802.11, Chap 5.5.
* *
@ -1240,22 +1275,22 @@ static void sta_ps_end(struct sta_info *sta)
ieee80211_sta_ps_deliver_wakeup(sta); ieee80211_sta_ps_deliver_wakeup(sta);
} }
int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start) int ieee80211_sta_ps_transition(struct ieee80211_sta *pubsta, bool start)
{ {
struct sta_info *sta_inf = container_of(sta, struct sta_info, sta); struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
bool in_ps; bool in_ps;
WARN_ON(!ieee80211_hw_check(&sta_inf->local->hw, AP_LINK_PS)); WARN_ON(!ieee80211_hw_check(&sta->local->hw, AP_LINK_PS));
/* Don't let the same PS state be set twice */ /* Don't let the same PS state be set twice */
in_ps = test_sta_flag(sta_inf, WLAN_STA_PS_STA); in_ps = test_sta_flag(sta, WLAN_STA_PS_STA);
if ((start && in_ps) || (!start && !in_ps)) if ((start && in_ps) || (!start && !in_ps))
return -EINVAL; return -EINVAL;
if (start) if (start)
sta_ps_start(sta_inf); sta_ps_start(sta);
else else
sta_ps_end(sta_inf); sta_ps_end(sta);
return 0; return 0;
} }
@ -1393,7 +1428,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
sta->rx_bytes += rx->skb->len; sta->rx_bytes += rx->skb->len;
if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) { if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
sta->last_signal = status->signal; sta->last_signal = status->signal;
ewma_add(&sta->avg_signal, -status->signal); ewma_signal_add(&sta->avg_signal, -status->signal);
} }
if (status->chains) { if (status->chains) {
@ -1405,7 +1440,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
continue; continue;
sta->chain_signal_last[i] = signal; sta->chain_signal_last[i] = signal;
ewma_add(&sta->chain_signal_avg[i], -signal); ewma_signal_add(&sta->chain_signal_avg[i], -signal);
} }
} }
@ -1647,7 +1682,6 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
if (unlikely(rx->key->flags & KEY_FLAG_TAINTED)) if (unlikely(rx->key->flags & KEY_FLAG_TAINTED))
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
rx->key->tx_rx_count++;
/* TODO: add threshold stuff again */ /* TODO: add threshold stuff again */
} else { } else {
return RX_DROP_MONITOR; return RX_DROP_MONITOR;
@ -1883,7 +1917,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
/* Complete frame has been reassembled - process it now */ /* Complete frame has been reassembled - process it now */
status = IEEE80211_SKB_RXCB(rx->skb); status = IEEE80211_SKB_RXCB(rx->skb);
status->rx_flags |= IEEE80211_RX_FRAGMENTED;
out: out:
ieee80211_led_rx(rx->local); ieee80211_led_rx(rx->local);
@ -2108,9 +2141,8 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
/* deliver to local stack */ /* deliver to local stack */
skb->protocol = eth_type_trans(skb, dev); skb->protocol = eth_type_trans(skb, dev);
memset(skb->cb, 0, sizeof(skb->cb)); memset(skb->cb, 0, sizeof(skb->cb));
if (!(rx->flags & IEEE80211_RX_REORDER_TIMER) && if (rx->napi)
rx->local->napi) napi_gro_receive(rx->napi, skb);
napi_gro_receive(rx->local->napi, skb);
else else
netif_receive_skb(skb); netif_receive_skb(skb);
} }
@ -2378,9 +2410,8 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
tf->category == WLAN_CATEGORY_TDLS && tf->category == WLAN_CATEGORY_TDLS &&
(tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST || (tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) { tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TDLS_CHSW; skb_queue_tail(&local->skb_queue_tdls_chsw, rx->skb);
skb_queue_tail(&sdata->skb_queue, rx->skb); schedule_work(&local->tdls_chsw_work);
ieee80211_queue_work(&rx->local->hw, &sdata->work);
if (rx->sta) if (rx->sta)
rx->sta->rx_packets++; rx->sta->rx_packets++;
@ -3004,7 +3035,6 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
return RX_QUEUED; return RX_QUEUED;
} }
/* TODO: use IEEE80211_RX_FRAGMENTED */
static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx, static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
struct ieee80211_rate *rate) struct ieee80211_rate *rate)
{ {
@ -3216,7 +3246,7 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
/* This is OK -- must be QoS data frame */ /* This is OK -- must be QoS data frame */
.security_idx = tid, .security_idx = tid,
.seqno_idx = tid, .seqno_idx = tid,
.flags = IEEE80211_RX_REORDER_TIMER, .napi = NULL, /* must be NULL to not have races */
}; };
struct tid_ampdu_rx *tid_agg_rx; struct tid_ampdu_rx *tid_agg_rx;
@ -3286,7 +3316,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_OCB:
if (!bssid) if (!bssid)
return false; return false;
if (ieee80211_is_beacon(hdr->frame_control)) if (!ieee80211_is_data_present(hdr->frame_control))
return false; return false;
if (!is_broadcast_ether_addr(bssid)) if (!is_broadcast_ether_addr(bssid))
return false; return false;
@ -3393,7 +3423,8 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
* be called with rcu_read_lock protection. * be called with rcu_read_lock protection.
*/ */
static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
struct sk_buff *skb) struct sk_buff *skb,
struct napi_struct *napi)
{ {
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
@ -3409,6 +3440,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
memset(&rx, 0, sizeof(rx)); memset(&rx, 0, sizeof(rx));
rx.skb = skb; rx.skb = skb;
rx.local = local; rx.local = local;
rx.napi = napi;
if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc)) if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))
I802_DEBUG_INC(local->dot11ReceivedFragmentCount); I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
@ -3510,7 +3542,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
* This is the receive path handler. It is called by a low level driver when an * This is the receive path handler. It is called by a low level driver when an
* 802.11 MPDU is received from the hardware. * 802.11 MPDU is received from the hardware.
*/ */
void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) void ieee80211_rx_napi(struct ieee80211_hw *hw, struct sk_buff *skb,
struct napi_struct *napi)
{ {
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_rate *rate = NULL; struct ieee80211_rate *rate = NULL;
@ -3609,7 +3642,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
ieee80211_tpt_led_trig_rx(local, ieee80211_tpt_led_trig_rx(local,
((struct ieee80211_hdr *)skb->data)->frame_control, ((struct ieee80211_hdr *)skb->data)->frame_control,
skb->len); skb->len);
__ieee80211_rx_handle_packet(hw, skb); __ieee80211_rx_handle_packet(hw, skb, napi);
rcu_read_unlock(); rcu_read_unlock();
@ -3617,7 +3650,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
drop: drop:
kfree_skb(skb); kfree_skb(skb);
} }
EXPORT_SYMBOL(ieee80211_rx); EXPORT_SYMBOL(ieee80211_rx_napi);
/* This is a version of the rx handler that can be called from hard irq /* This is a version of the rx handler that can be called from hard irq
* context. Post the skb on the queue and schedule the tasklet */ * context. Post the skb on the queue and schedule the tasklet */

View file

@ -68,7 +68,7 @@ static const struct rhashtable_params sta_rht_params = {
.nelem_hint = 3, /* start small */ .nelem_hint = 3, /* start small */
.automatic_shrinking = true, .automatic_shrinking = true,
.head_offset = offsetof(struct sta_info, hash_node), .head_offset = offsetof(struct sta_info, hash_node),
.key_offset = offsetof(struct sta_info, sta.addr), .key_offset = offsetof(struct sta_info, addr),
.key_len = ETH_ALEN, .key_len = ETH_ALEN,
.hashfn = sta_addr_hash, .hashfn = sta_addr_hash,
.max_size = CONFIG_MAC80211_STA_HASH_MAX_SIZE, .max_size = CONFIG_MAC80211_STA_HASH_MAX_SIZE,
@ -249,6 +249,9 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
if (sta->sta.txq[0]) if (sta->sta.txq[0])
kfree(to_txq_info(sta->sta.txq[0])); kfree(to_txq_info(sta->sta.txq[0]));
kfree(rcu_dereference_raw(sta->sta.rates)); kfree(rcu_dereference_raw(sta->sta.rates));
#ifdef CONFIG_MAC80211_MESH
kfree(sta->mesh);
#endif
kfree(sta); kfree(sta);
} }
@ -313,13 +316,19 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
mutex_init(&sta->ampdu_mlme.mtx); mutex_init(&sta->ampdu_mlme.mtx);
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
spin_lock_init(&sta->plink_lock); if (ieee80211_vif_is_mesh(&sdata->vif)) {
if (ieee80211_vif_is_mesh(&sdata->vif) && sta->mesh = kzalloc(sizeof(*sta->mesh), gfp);
!sdata->u.mesh.user_mpm) if (!sta->mesh)
init_timer(&sta->plink_timer); goto free;
sta->nonpeer_pm = NL80211_MESH_POWER_ACTIVE; spin_lock_init(&sta->mesh->plink_lock);
if (ieee80211_vif_is_mesh(&sdata->vif) &&
!sdata->u.mesh.user_mpm)
init_timer(&sta->mesh->plink_timer);
sta->mesh->nonpeer_pm = NL80211_MESH_POWER_ACTIVE;
}
#endif #endif
memcpy(sta->addr, addr, ETH_ALEN);
memcpy(sta->sta.addr, addr, ETH_ALEN); memcpy(sta->sta.addr, addr, ETH_ALEN);
sta->local = local; sta->local = local;
sta->sdata = sdata; sta->sdata = sdata;
@ -332,9 +341,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
ktime_get_ts(&uptime); ktime_get_ts(&uptime);
sta->last_connected = uptime.tv_sec; sta->last_connected = uptime.tv_sec;
ewma_init(&sta->avg_signal, 1024, 8); ewma_signal_init(&sta->avg_signal);
for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++) for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
ewma_init(&sta->chain_signal_avg[i], 1024, 8); ewma_signal_init(&sta->chain_signal_avg[i]);
if (local->ops->wake_tx_queue) { if (local->ops->wake_tx_queue) {
void *txq_data; void *txq_data;
@ -405,6 +414,9 @@ free_txq:
if (sta->sta.txq[0]) if (sta->sta.txq[0])
kfree(to_txq_info(sta->sta.txq[0])); kfree(to_txq_info(sta->sta.txq[0]));
free: free:
#ifdef CONFIG_MAC80211_MESH
kfree(sta->mesh);
#endif
kfree(sta); kfree(sta);
return NULL; return NULL;
} }
@ -623,7 +635,7 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending)
bool indicate_tim = false; bool indicate_tim = false;
u8 ignore_for_tim = sta->sta.uapsd_queues; u8 ignore_for_tim = sta->sta.uapsd_queues;
int ac; int ac;
u16 id; u16 id = sta->sta.aid;
if (sta->sdata->vif.type == NL80211_IFTYPE_AP || if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
@ -631,12 +643,9 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending)
return; return;
ps = &sta->sdata->bss->ps; ps = &sta->sdata->bss->ps;
id = sta->sta.aid;
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
} else if (ieee80211_vif_is_mesh(&sta->sdata->vif)) { } else if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
ps = &sta->sdata->u.mesh.ps; ps = &sta->sdata->u.mesh.ps;
/* TIM map only for 1 <= PLID <= IEEE80211_MAX_AID */
id = sta->plid % (IEEE80211_MAX_AID + 1);
#endif #endif
} else { } else {
return; return;
@ -1887,7 +1896,8 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
} }
if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))) { if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))) {
sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal); sinfo->signal_avg =
(s8) -ewma_signal_read(&sta->avg_signal);
sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG); sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG);
} }
} }
@ -1902,7 +1912,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) { for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
sinfo->chain_signal[i] = sta->chain_signal_last[i]; sinfo->chain_signal[i] = sta->chain_signal_last[i];
sinfo->chain_signal_avg[i] = sinfo->chain_signal_avg[i] =
(s8) -ewma_read(&sta->chain_signal_avg[i]); (s8) -ewma_signal_read(&sta->chain_signal_avg[i]);
} }
} }
@ -1956,16 +1966,16 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
BIT(NL80211_STA_INFO_PEER_PM) | BIT(NL80211_STA_INFO_PEER_PM) |
BIT(NL80211_STA_INFO_NONPEER_PM); BIT(NL80211_STA_INFO_NONPEER_PM);
sinfo->llid = sta->llid; sinfo->llid = sta->mesh->llid;
sinfo->plid = sta->plid; sinfo->plid = sta->mesh->plid;
sinfo->plink_state = sta->plink_state; sinfo->plink_state = sta->mesh->plink_state;
if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) { if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
sinfo->filled |= BIT(NL80211_STA_INFO_T_OFFSET); sinfo->filled |= BIT(NL80211_STA_INFO_T_OFFSET);
sinfo->t_offset = sta->t_offset; sinfo->t_offset = sta->mesh->t_offset;
} }
sinfo->local_pm = sta->local_pm; sinfo->local_pm = sta->mesh->local_pm;
sinfo->peer_pm = sta->peer_pm; sinfo->peer_pm = sta->mesh->peer_pm;
sinfo->nonpeer_pm = sta->nonpeer_pm; sinfo->nonpeer_pm = sta->mesh->nonpeer_pm;
#endif #endif
} }

View file

@ -53,6 +53,8 @@
* @WLAN_STA_TDLS_CHAN_SWITCH: This TDLS peer supports TDLS channel-switching * @WLAN_STA_TDLS_CHAN_SWITCH: This TDLS peer supports TDLS channel-switching
* @WLAN_STA_TDLS_OFF_CHANNEL: The local STA is currently off-channel with this * @WLAN_STA_TDLS_OFF_CHANNEL: The local STA is currently off-channel with this
* TDLS peer * TDLS peer
* @WLAN_STA_TDLS_WIDER_BW: This TDLS peer supports working on a wider bw on
* the BSS base channel.
* @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
* keeping station in power-save mode, reply when the driver * keeping station in power-save mode, reply when the driver
* unblocks the station. * unblocks the station.
@ -84,6 +86,7 @@ enum ieee80211_sta_info_flags {
WLAN_STA_TDLS_INITIATOR, WLAN_STA_TDLS_INITIATOR,
WLAN_STA_TDLS_CHAN_SWITCH, WLAN_STA_TDLS_CHAN_SWITCH,
WLAN_STA_TDLS_OFF_CHANNEL, WLAN_STA_TDLS_OFF_CHANNEL,
WLAN_STA_TDLS_WIDER_BW,
WLAN_STA_UAPSD, WLAN_STA_UAPSD,
WLAN_STA_SP, WLAN_STA_SP,
WLAN_STA_4ADDR_EVENT, WLAN_STA_4ADDR_EVENT,
@ -269,6 +272,56 @@ struct ieee80211_fast_tx {
struct rcu_head rcu_head; struct rcu_head rcu_head;
}; };
/**
* struct mesh_sta - mesh STA information
* @plink_lock: serialize access to plink fields
* @llid: Local link ID
* @plid: Peer link ID
* @aid: local aid supplied by peer
* @reason: Cancel reason on PLINK_HOLDING state
* @plink_retries: Retries in establishment
* @plink_state: peer link state
* @plink_timeout: timeout of peer link
* @plink_timer: peer link watch timer
* @t_offset: timing offset relative to this host
* @t_offset_setpoint: reference timing offset of this sta to be used when
* calculating clockdrift
* @local_pm: local link-specific power save mode
* @peer_pm: peer-specific power save mode towards local STA
* @nonpeer_pm: STA power save mode towards non-peer neighbors
* @processed_beacon: set to true after peer rates and capabilities are
* processed
* @fail_avg: moving percentage of failed MSDUs
*/
struct mesh_sta {
struct timer_list plink_timer;
s64 t_offset;
s64 t_offset_setpoint;
spinlock_t plink_lock;
u16 llid;
u16 plid;
u16 aid;
u16 reason;
u8 plink_retries;
bool processed_beacon;
enum nl80211_plink_state plink_state;
u32 plink_timeout;
/* mesh power save */
enum nl80211_mesh_power_mode local_pm;
enum nl80211_mesh_power_mode peer_pm;
enum nl80211_mesh_power_mode nonpeer_pm;
/* moving percentage of failed MSDUs */
unsigned int fail_avg;
};
DECLARE_EWMA(signal, 1024, 8)
/** /**
* struct sta_info - STA information * struct sta_info - STA information
* *
@ -278,12 +331,13 @@ struct ieee80211_fast_tx {
* @list: global linked list entry * @list: global linked list entry
* @free_list: list entry for keeping track of stations to free * @free_list: list entry for keeping track of stations to free
* @hash_node: hash node for rhashtable * @hash_node: hash node for rhashtable
* @addr: station's MAC address - duplicated from public part to
* let the hash table work with just a single cacheline
* @local: pointer to the global information * @local: pointer to the global information
* @sdata: virtual interface this station belongs to * @sdata: virtual interface this station belongs to
* @ptk: peer keys negotiated with this station, if any * @ptk: peer keys negotiated with this station, if any
* @ptk_idx: last installed peer key index * @ptk_idx: last installed peer key index
* @gtk: group keys negotiated with this station, if any * @gtk: group keys negotiated with this station, if any
* @gtk_idx: last installed group key index
* @rate_ctrl: rate control algorithm reference * @rate_ctrl: rate control algorithm reference
* @rate_ctrl_lock: spinlock used to protect rate control data * @rate_ctrl_lock: spinlock used to protect rate control data
* (data inside the algorithm, so serializes calls there) * (data inside the algorithm, so serializes calls there)
@ -318,30 +372,17 @@ struct ieee80211_fast_tx {
* @last_signal: signal of last received frame from this STA * @last_signal: signal of last received frame from this STA
* @avg_signal: moving average of signal of received frames from this STA * @avg_signal: moving average of signal of received frames from this STA
* @last_ack_signal: signal of last received Ack frame from this STA * @last_ack_signal: signal of last received Ack frame from this STA
* @last_seq_ctrl: last received seq/frag number from this STA (per RX queue) * @last_seq_ctrl: last received seq/frag number from this STA (per TID
* plus one for non-QoS frames)
* @tx_filtered_count: number of frames the hardware filtered for this STA * @tx_filtered_count: number of frames the hardware filtered for this STA
* @tx_retry_failed: number of frames that failed retry * @tx_retry_failed: number of frames that failed retry
* @tx_retry_count: total number of retries for frames to this STA * @tx_retry_count: total number of retries for frames to this STA
* @fail_avg: moving percentage of failed MSDUs
* @tx_packets: number of RX/TX MSDUs * @tx_packets: number of RX/TX MSDUs
* @tx_bytes: number of bytes transmitted to this STA * @tx_bytes: number of bytes transmitted to this STA
* @tid_seq: per-TID sequence numbers for sending to this STA * @tid_seq: per-TID sequence numbers for sending to this STA
* @ampdu_mlme: A-MPDU state machine state * @ampdu_mlme: A-MPDU state machine state
* @timer_to_tid: identity mapping to ID timers * @timer_to_tid: identity mapping to ID timers
* @plink_lock: serialize access to plink fields * @mesh: mesh STA information
* @llid: Local link ID
* @plid: Peer link ID
* @reason: Cancel reason on PLINK_HOLDING state
* @plink_retries: Retries in establishment
* @plink_state: peer link state
* @plink_timeout: timeout of peer link
* @plink_timer: peer link watch timer
* @t_offset: timing offset relative to this host
* @t_offset_setpoint: reference timing offset of this sta to be used when
* calculating clockdrift
* @local_pm: local link-specific power save mode
* @peer_pm: peer-specific power save mode towards local STA
* @nonpeer_pm: STA power save mode towards non-peer neighbors
* @debugfs: debug filesystem info * @debugfs: debug filesystem info
* @dead: set to true when sta is unlinked * @dead: set to true when sta is unlinked
* @uploaded: set to true when sta is uploaded to the driver * @uploaded: set to true when sta is uploaded to the driver
@ -369,19 +410,19 @@ struct ieee80211_fast_tx {
* @rx_msdu: MSDUs received from this station, using IEEE80211_NUM_TID * @rx_msdu: MSDUs received from this station, using IEEE80211_NUM_TID
* entry for non-QoS frames * entry for non-QoS frames
* @fast_tx: TX fastpath information * @fast_tx: TX fastpath information
* @processed_beacon: set to true after peer rates and capabilities are * @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to
* processed * the BSS one.
*/ */
struct sta_info { struct sta_info {
/* General information, mostly static */ /* General information, mostly static */
struct list_head list, free_list; struct list_head list, free_list;
struct rcu_head rcu_head; struct rcu_head rcu_head;
struct rhash_head hash_node; struct rhash_head hash_node;
u8 addr[ETH_ALEN];
struct ieee80211_local *local; struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
struct ieee80211_key __rcu *ptk[NUM_DEFAULT_KEYS]; struct ieee80211_key __rcu *ptk[NUM_DEFAULT_KEYS];
u8 gtk_idx;
u8 ptk_idx; u8 ptk_idx;
struct rate_control_ref *rate_ctrl; struct rate_control_ref *rate_ctrl;
void *rate_ctrl_priv; void *rate_ctrl_priv;
@ -390,6 +431,10 @@ struct sta_info {
struct ieee80211_fast_tx __rcu *fast_tx; struct ieee80211_fast_tx __rcu *fast_tx;
#ifdef CONFIG_MAC80211_MESH
struct mesh_sta *mesh;
#endif
struct work_struct drv_deliver_wk; struct work_struct drv_deliver_wk;
u16 listen_interval; u16 listen_interval;
@ -419,12 +464,12 @@ struct sta_info {
unsigned long rx_fragments; unsigned long rx_fragments;
unsigned long rx_dropped; unsigned long rx_dropped;
int last_signal; int last_signal;
struct ewma avg_signal; struct ewma_signal avg_signal;
int last_ack_signal; int last_ack_signal;
u8 chains; u8 chains;
s8 chain_signal_last[IEEE80211_MAX_CHAINS]; s8 chain_signal_last[IEEE80211_MAX_CHAINS];
struct ewma chain_signal_avg[IEEE80211_MAX_CHAINS]; struct ewma_signal chain_signal_avg[IEEE80211_MAX_CHAINS];
/* Plus 1 for non-QoS frames */ /* Plus 1 for non-QoS frames */
__le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1]; __le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];
@ -432,8 +477,6 @@ struct sta_info {
/* Updated from TX status path only, no locking requirements */ /* Updated from TX status path only, no locking requirements */
unsigned long tx_filtered_count; unsigned long tx_filtered_count;
unsigned long tx_retry_failed, tx_retry_count; unsigned long tx_retry_failed, tx_retry_count;
/* moving percentage of failed MSDUs */
unsigned int fail_avg;
/* Updated from TX path only, no locking requirements */ /* Updated from TX path only, no locking requirements */
u64 tx_packets[IEEE80211_NUM_ACS]; u64 tx_packets[IEEE80211_NUM_ACS];
@ -455,29 +498,6 @@ struct sta_info {
struct sta_ampdu_mlme ampdu_mlme; struct sta_ampdu_mlme ampdu_mlme;
u8 timer_to_tid[IEEE80211_NUM_TIDS]; u8 timer_to_tid[IEEE80211_NUM_TIDS];
#ifdef CONFIG_MAC80211_MESH
/*
* Mesh peer link attributes, protected by plink_lock.
* TODO: move to a sub-structure that is referenced with pointer?
*/
spinlock_t plink_lock;
u16 llid;
u16 plid;
u16 reason;
u8 plink_retries;
enum nl80211_plink_state plink_state;
u32 plink_timeout;
struct timer_list plink_timer;
s64 t_offset;
s64 t_offset_setpoint;
/* mesh power save */
enum nl80211_mesh_power_mode local_pm;
enum nl80211_mesh_power_mode peer_pm;
enum nl80211_mesh_power_mode nonpeer_pm;
bool processed_beacon;
#endif
#ifdef CONFIG_MAC80211_DEBUGFS #ifdef CONFIG_MAC80211_DEBUGFS
struct sta_info_debugfsdentries { struct sta_info_debugfsdentries {
struct dentry *dir; struct dentry *dir;
@ -498,6 +518,8 @@ struct sta_info {
u8 reserved_tid; u8 reserved_tid;
struct cfg80211_chan_def tdls_chandef;
/* keep last! */ /* keep last! */
struct ieee80211_sta sta; struct ieee80211_sta sta;
}; };
@ -505,7 +527,7 @@ struct sta_info {
static inline enum nl80211_plink_state sta_plink_state(struct sta_info *sta) static inline enum nl80211_plink_state sta_plink_state(struct sta_info *sta)
{ {
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
return sta->plink_state; return sta->mesh->plink_state;
#endif #endif
return NL80211_PLINK_LISTEN; return NL80211_PLINK_LISTEN;
} }
@ -608,7 +630,7 @@ u32 sta_addr_hash(const void *key, u32 length, u32 seed);
_sta_bucket_idx(tbl, _addr), \ _sta_bucket_idx(tbl, _addr), \
hash_node) \ hash_node) \
/* compare address and run code only if it matches */ \ /* compare address and run code only if it matches */ \
if (ether_addr_equal(_sta->sta.addr, (_addr))) if (ether_addr_equal(_sta->addr, (_addr)))
/* /*
* Get STA info by index, BROKEN! * Get STA info by index, BROKEN!

View file

@ -515,7 +515,7 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
if (!sdata) { if (!sdata) {
skb->dev = NULL; skb->dev = NULL;
} else if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) { } else {
unsigned int hdr_size = unsigned int hdr_size =
ieee80211_hdrlen(hdr->frame_control); ieee80211_hdrlen(hdr->frame_control);
@ -529,9 +529,6 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
ieee80211_mgd_conn_tx_status(sdata, ieee80211_mgd_conn_tx_status(sdata,
hdr->frame_control, hdr->frame_control,
acked); acked);
} else {
/* we assign ack frame ID for the others */
WARN_ON(1);
} }
rcu_read_unlock(); rcu_read_unlock();

View file

@ -4,6 +4,7 @@
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2014, Intel Corporation * Copyright 2014, Intel Corporation
* Copyright 2014 Intel Mobile Communications GmbH * Copyright 2014 Intel Mobile Communications GmbH
* Copyright 2015 Intel Deutschland GmbH
* *
* This file is GPLv2 as found in COPYING. * This file is GPLv2 as found in COPYING.
*/ */
@ -11,6 +12,7 @@
#include <linux/ieee80211.h> #include <linux/ieee80211.h>
#include <linux/log2.h> #include <linux/log2.h>
#include <net/cfg80211.h> #include <net/cfg80211.h>
#include <linux/rtnetlink.h>
#include "ieee80211_i.h" #include "ieee80211_i.h"
#include "driver-ops.h" #include "driver-ops.h"
@ -35,20 +37,28 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk)
mutex_unlock(&local->mtx); mutex_unlock(&local->mtx);
} }
static void ieee80211_tdls_add_ext_capab(struct ieee80211_local *local, static void ieee80211_tdls_add_ext_capab(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb) struct sk_buff *skb)
{ {
u8 *pos = (void *)skb_put(skb, 7); struct ieee80211_local *local = sdata->local;
bool chan_switch = local->hw.wiphy->features & bool chan_switch = local->hw.wiphy->features &
NL80211_FEATURE_TDLS_CHANNEL_SWITCH; NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
bool wider_band = ieee80211_hw_check(&local->hw, TDLS_WIDER_BW);
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
bool vht = sband && sband->vht_cap.vht_supported;
u8 *pos = (void *)skb_put(skb, 10);
*pos++ = WLAN_EID_EXT_CAPABILITY; *pos++ = WLAN_EID_EXT_CAPABILITY;
*pos++ = 5; /* len */ *pos++ = 8; /* len */
*pos++ = 0x0; *pos++ = 0x0;
*pos++ = 0x0; *pos++ = 0x0;
*pos++ = 0x0; *pos++ = 0x0;
*pos++ = chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0; *pos++ = chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0;
*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
*pos++ = 0;
*pos++ = 0;
*pos++ = (vht && wider_band) ? WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED : 0;
} }
static u8 static u8
@ -283,6 +293,60 @@ static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata,
} }
} }
static void
ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta)
{
/* IEEE802.11ac-2013 Table E-4 */
u16 centers_80mhz[] = { 5210, 5290, 5530, 5610, 5690, 5775 };
struct cfg80211_chan_def uc = sta->tdls_chandef;
enum nl80211_chan_width max_width = ieee80211_get_sta_bw(&sta->sta);
int i;
/* only support upgrading non-narrow channels up to 80Mhz */
if (max_width == NL80211_CHAN_WIDTH_5 ||
max_width == NL80211_CHAN_WIDTH_10)
return;
if (max_width > NL80211_CHAN_WIDTH_80)
max_width = NL80211_CHAN_WIDTH_80;
if (uc.width == max_width)
return;
/*
* Channel usage constrains in the IEEE802.11ac-2013 specification only
* allow expanding a 20MHz channel to 80MHz in a single way. In
* addition, there are no 40MHz allowed channels that are not part of
* the allowed 80MHz range in the 5GHz spectrum (the relevant one here).
*/
for (i = 0; i < ARRAY_SIZE(centers_80mhz); i++)
if (abs(uc.chan->center_freq - centers_80mhz[i]) <= 30) {
uc.center_freq1 = centers_80mhz[i];
uc.width = NL80211_CHAN_WIDTH_80;
break;
}
if (!uc.center_freq1)
return;
/* proceed to downgrade the chandef until usable or the same */
while (uc.width > max_width &&
!cfg80211_reg_can_beacon(sdata->local->hw.wiphy,
&uc, sdata->wdev.iftype))
ieee80211_chandef_downgrade(&uc);
if (!cfg80211_chandef_identical(&uc, &sta->tdls_chandef)) {
tdls_dbg(sdata, "TDLS ch width upgraded %d -> %d\n",
sta->tdls_chandef.width, uc.width);
/*
* the station is not yet authorized when BW upgrade is done,
* locking is not required
*/
sta->tdls_chandef = uc;
}
}
static void static void
ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, const u8 *peer, struct sk_buff *skb, const u8 *peer,
@ -320,7 +384,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
offset = noffset; offset = noffset;
} }
ieee80211_tdls_add_ext_capab(local, skb); ieee80211_tdls_add_ext_capab(sdata, skb);
/* add the QoS element if we support it */ /* add the QoS element if we support it */
if (local->hw.queues >= IEEE80211_NUM_ACS && if (local->hw.queues >= IEEE80211_NUM_ACS &&
@ -350,15 +414,17 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
offset = noffset; offset = noffset;
} }
rcu_read_lock(); mutex_lock(&local->sta_mtx);
/* we should have the peer STA if we're already responding */ /* we should have the peer STA if we're already responding */
if (action_code == WLAN_TDLS_SETUP_RESPONSE) { if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
sta = sta_info_get(sdata, peer); sta = sta_info_get(sdata, peer);
if (WARN_ON_ONCE(!sta)) { if (WARN_ON_ONCE(!sta)) {
rcu_read_unlock(); mutex_unlock(&local->sta_mtx);
return; return;
} }
sta->tdls_chandef = sdata->vif.bss_conf.chandef;
} }
ieee80211_tdls_add_oper_classes(sdata, skb); ieee80211_tdls_add_oper_classes(sdata, skb);
@ -384,10 +450,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap); ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
} else if (action_code == WLAN_TDLS_SETUP_RESPONSE && } else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) { ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
/* disable SMPS in TDLS responder */
sta->sta.ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED
<< IEEE80211_HT_CAP_SM_PS_SHIFT;
/* the peer caps are already intersected with our own */ /* the peer caps are already intersected with our own */
memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap)); memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
@ -448,9 +510,16 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap); ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
/*
* if both peers support WIDER_BW, we can expand the chandef to
* a wider compatible one, up to 80MHz
*/
if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
} }
rcu_read_unlock(); mutex_unlock(&local->sta_mtx);
/* add any remaining IEs */ /* add any remaining IEs */
if (extra_ies_len) { if (extra_ies_len) {
@ -474,15 +543,17 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
enum ieee80211_band band = ieee80211_get_sdata_band(sdata); enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
u8 *pos; u8 *pos;
rcu_read_lock(); mutex_lock(&local->sta_mtx);
sta = sta_info_get(sdata, peer); sta = sta_info_get(sdata, peer);
ap_sta = sta_info_get(sdata, ifmgd->bssid); ap_sta = sta_info_get(sdata, ifmgd->bssid);
if (WARN_ON_ONCE(!sta || !ap_sta)) { if (WARN_ON_ONCE(!sta || !ap_sta)) {
rcu_read_unlock(); mutex_unlock(&local->sta_mtx);
return; return;
} }
sta->tdls_chandef = sdata->vif.bss_conf.chandef;
/* add any custom IEs that go before the QoS IE */ /* add any custom IEs that go before the QoS IE */
if (extra_ies_len) { if (extra_ies_len) {
static const u8 before_qos[] = { static const u8 before_qos[] = {
@ -530,12 +601,19 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
/* only include VHT-operation if not on the 2.4GHz band */ /* only include VHT-operation if not on the 2.4GHz band */
if (band != IEEE80211_BAND_2GHZ && sta->sta.vht_cap.vht_supported) { if (band != IEEE80211_BAND_2GHZ && sta->sta.vht_cap.vht_supported) {
/*
* if both peers support WIDER_BW, we can expand the chandef to
* a wider compatible one, up to 80MHz
*/
if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation)); pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation));
ieee80211_ie_build_vht_oper(pos, &sta->sta.vht_cap, ieee80211_ie_build_vht_oper(pos, &sta->sta.vht_cap,
&sdata->vif.bss_conf.chandef); &sta->tdls_chandef);
} }
rcu_read_unlock(); mutex_unlock(&local->sta_mtx);
/* add any remaining IEs */ /* add any remaining IEs */
if (extra_ies_len) { if (extra_ies_len) {
@ -784,7 +862,7 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
max(sizeof(struct ieee80211_mgmt), max(sizeof(struct ieee80211_mgmt),
sizeof(struct ieee80211_tdls_data)) + sizeof(struct ieee80211_tdls_data)) +
50 + /* supported rates */ 50 + /* supported rates */
7 + /* ext capab */ 10 + /* ext capab */
26 + /* max(WMM-info, WMM-param) */ 26 + /* max(WMM-info, WMM-param) */
2 + max(sizeof(struct ieee80211_ht_cap), 2 + max(sizeof(struct ieee80211_ht_cap),
sizeof(struct ieee80211_ht_operation)) + sizeof(struct ieee80211_ht_operation)) +
@ -983,8 +1061,17 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
{ {
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
enum ieee80211_smps_mode smps_mode = sdata->u.mgd.driver_smps_mode;
int ret; int ret;
/* don't support setup with forced SMPS mode that's not off */
if (smps_mode != IEEE80211_SMPS_AUTOMATIC &&
smps_mode != IEEE80211_SMPS_OFF) {
tdls_dbg(sdata, "Aborting TDLS setup due to SMPS mode %d\n",
smps_mode);
return -ENOTSUPP;
}
mutex_lock(&local->mtx); mutex_lock(&local->mtx);
/* we don't support concurrent TDLS peer setups */ /* we don't support concurrent TDLS peer setups */
@ -1146,6 +1233,22 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
return ret; return ret;
} }
static void iee80211_tdls_recalc_chanctx(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_chanctx_conf *conf;
struct ieee80211_chanctx *ctx;
mutex_lock(&local->chanctx_mtx);
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
lockdep_is_held(&local->chanctx_mtx));
if (conf) {
ctx = container_of(conf, struct ieee80211_chanctx, conf);
ieee80211_recalc_chanctx_chantype(local, ctx);
}
mutex_unlock(&local->chanctx_mtx);
}
int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
const u8 *peer, enum nl80211_tdls_operation oper) const u8 *peer, enum nl80211_tdls_operation oper)
{ {
@ -1182,6 +1285,8 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
break; break;
} }
iee80211_tdls_recalc_chanctx(sdata);
rcu_read_lock(); rcu_read_lock();
sta = sta_info_get(sdata, peer); sta = sta_info_get(sdata, peer);
if (!sta) { if (!sta) {
@ -1213,6 +1318,7 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
ieee80211_flush_queues(local, sdata, false); ieee80211_flush_queues(local, sdata, false);
ret = sta_info_destroy_addr(sdata, peer); ret = sta_info_destroy_addr(sdata, peer);
iee80211_tdls_recalc_chanctx(sdata);
break; break;
default: default:
ret = -ENOTSUPP; ret = -ENOTSUPP;
@ -1224,6 +1330,10 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
eth_zero_addr(sdata->u.mgd.tdls_peer); eth_zero_addr(sdata->u.mgd.tdls_peer);
} }
if (ret == 0)
ieee80211_queue_work(&sdata->local->hw,
&sdata->u.mgd.request_smps_work);
mutex_unlock(&local->mtx); mutex_unlock(&local->mtx);
return ret; return ret;
} }
@ -1627,6 +1737,31 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
return -EINVAL; return -EINVAL;
} }
if (!elems.sec_chan_offs) {
chan_type = NL80211_CHAN_HT20;
} else {
switch (elems.sec_chan_offs->sec_chan_offs) {
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
chan_type = NL80211_CHAN_HT40PLUS;
break;
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
chan_type = NL80211_CHAN_HT40MINUS;
break;
default:
chan_type = NL80211_CHAN_HT20;
break;
}
}
cfg80211_chandef_create(&chandef, chan, chan_type);
/* we will be active on the TDLS link */
if (!cfg80211_reg_can_beacon_relax(sdata->local->hw.wiphy, &chandef,
sdata->wdev.iftype)) {
tdls_dbg(sdata, "TDLS chan switch to forbidden channel\n");
return -EINVAL;
}
mutex_lock(&local->sta_mtx); mutex_lock(&local->sta_mtx);
sta = sta_info_get(sdata, tf->sa); sta = sta_info_get(sdata, tf->sa);
if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) { if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) {
@ -1647,27 +1782,15 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
goto out; goto out;
} }
if (!sta->sta.ht_cap.ht_supported) { /* peer should have known better */
chan_type = NL80211_CHAN_NO_HT; if (!sta->sta.ht_cap.ht_supported && elems.sec_chan_offs &&
} else if (!elems.sec_chan_offs) { elems.sec_chan_offs->sec_chan_offs) {
chan_type = NL80211_CHAN_HT20; tdls_dbg(sdata, "TDLS chan switch - wide chan unsupported\n");
} else { ret = -ENOTSUPP;
switch (elems.sec_chan_offs->sec_chan_offs) { goto out;
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
chan_type = NL80211_CHAN_HT40PLUS;
break;
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
chan_type = NL80211_CHAN_HT40MINUS;
break;
default:
chan_type = NL80211_CHAN_HT20;
break;
}
} }
cfg80211_chandef_create(&chandef, chan, chan_type);
params.chandef = &chandef; params.chandef = &chandef;
params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time); params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time);
params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout); params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout);
@ -1691,12 +1814,15 @@ out:
return ret; return ret;
} }
void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata, static void
struct sk_buff *skb) ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{ {
struct ieee80211_tdls_data *tf = (void *)skb->data; struct ieee80211_tdls_data *tf = (void *)skb->data;
struct wiphy *wiphy = sdata->local->hw.wiphy; struct wiphy *wiphy = sdata->local->hw.wiphy;
ASSERT_RTNL();
/* make sure the driver supports it */ /* make sure the driver supports it */
if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH)) if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
return; return;
@ -1720,3 +1846,47 @@ void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
return; return;
} }
} }
void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata)
{
struct sta_info *sta;
u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
rcu_read_lock();
list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
continue;
ieee80211_tdls_oper_request(&sdata->vif, sta->sta.addr,
NL80211_TDLS_TEARDOWN, reason,
GFP_ATOMIC);
}
rcu_read_unlock();
}
void ieee80211_tdls_chsw_work(struct work_struct *wk)
{
struct ieee80211_local *local =
container_of(wk, struct ieee80211_local, tdls_chsw_work);
struct ieee80211_sub_if_data *sdata;
struct sk_buff *skb;
struct ieee80211_tdls_data *tf;
rtnl_lock();
while ((skb = skb_dequeue(&local->skb_queue_tdls_chsw))) {
tf = (struct ieee80211_tdls_data *)skb->data;
list_for_each_entry(sdata, &local->interfaces, list) {
if (!ieee80211_sdata_running(sdata) ||
sdata->vif.type != NL80211_IFTYPE_STATION ||
!ether_addr_equal(tf->da, sdata->vif.addr))
continue;
ieee80211_process_tdls_channel_switch(sdata, skb);
break;
}
kfree_skb(skb);
}
rtnl_unlock();
}

View file

@ -311,9 +311,6 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
if (tx->sdata->vif.type == NL80211_IFTYPE_WDS) if (tx->sdata->vif.type == NL80211_IFTYPE_WDS)
return TX_CONTINUE; return TX_CONTINUE;
if (tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
return TX_CONTINUE;
if (tx->flags & IEEE80211_TX_PS_BUFFERED) if (tx->flags & IEEE80211_TX_PS_BUFFERED)
return TX_CONTINUE; return TX_CONTINUE;
@ -610,7 +607,6 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
if (tx->key) { if (tx->key) {
bool skip_hw = false; bool skip_hw = false;
tx->key->tx_rx_count++;
/* TODO: add threshold stuff again */ /* TODO: add threshold stuff again */
switch (tx->key->conf.cipher) { switch (tx->key->conf.cipher) {
@ -690,7 +686,8 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
txrc.bss = (tx->sdata->vif.type == NL80211_IFTYPE_AP || txrc.bss = (tx->sdata->vif.type == NL80211_IFTYPE_AP ||
tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT || tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
tx->sdata->vif.type == NL80211_IFTYPE_ADHOC); tx->sdata->vif.type == NL80211_IFTYPE_ADHOC ||
tx->sdata->vif.type == NL80211_IFTYPE_OCB);
/* set up RTS protection if desired */ /* set up RTS protection if desired */
if (len > tx->local->hw.wiphy->rts_threshold) { if (len > tx->local->hw.wiphy->rts_threshold) {
@ -2777,7 +2774,11 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
sdata->sequence_number += 0x10; sdata->sequence_number += 0x10;
} }
sta->tx_msdu[tid]++; if (skb_shinfo(skb)->gso_size)
sta->tx_msdu[tid] +=
DIV_ROUND_UP(skb->len, skb_shinfo(skb)->gso_size);
else
sta->tx_msdu[tid]++;
info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)]; info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
@ -3213,6 +3214,16 @@ static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock(); rcu_read_unlock();
} }
static u8 __ieee80211_csa_update_counter(struct beacon_data *beacon)
{
beacon->csa_current_counter--;
/* the counter should never reach 0 */
WARN_ON_ONCE(!beacon->csa_current_counter);
return beacon->csa_current_counter;
}
u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif) u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif)
{ {
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
@ -3231,11 +3242,7 @@ u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif)
if (!beacon) if (!beacon)
goto unlock; goto unlock;
beacon->csa_current_counter--; count = __ieee80211_csa_update_counter(beacon);
/* the counter should never reach 0 */
WARN_ON_ONCE(!beacon->csa_current_counter);
count = beacon->csa_current_counter;
unlock: unlock:
rcu_read_unlock(); rcu_read_unlock();
@ -3335,7 +3342,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
if (beacon) { if (beacon) {
if (beacon->csa_counter_offsets[0]) { if (beacon->csa_counter_offsets[0]) {
if (!is_template) if (!is_template)
ieee80211_csa_update_counter(vif); __ieee80211_csa_update_counter(beacon);
ieee80211_set_csa(sdata, beacon); ieee80211_set_csa(sdata, beacon);
} }
@ -3381,7 +3388,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
if (beacon->csa_counter_offsets[0]) { if (beacon->csa_counter_offsets[0]) {
if (!is_template) if (!is_template)
ieee80211_csa_update_counter(vif); __ieee80211_csa_update_counter(beacon);
ieee80211_set_csa(sdata, beacon); ieee80211_set_csa(sdata, beacon);
} }
@ -3411,7 +3418,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
* for now we leave it consistent with overall * for now we leave it consistent with overall
* mac80211's behavior. * mac80211's behavior.
*/ */
ieee80211_csa_update_counter(vif); __ieee80211_csa_update_counter(beacon);
ieee80211_set_csa(sdata, beacon); ieee80211_set_csa(sdata, beacon);
} }

View file

@ -47,55 +47,6 @@ struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
} }
EXPORT_SYMBOL(wiphy_to_ieee80211_hw); EXPORT_SYMBOL(wiphy_to_ieee80211_hw);
u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
enum nl80211_iftype type)
{
__le16 fc = hdr->frame_control;
/* drop ACK/CTS frames and incorrect hdr len (ctrl) */
if (len < 16)
return NULL;
if (ieee80211_is_data(fc)) {
if (len < 24) /* drop incorrect hdr len (data) */
return NULL;
if (ieee80211_has_a4(fc))
return NULL;
if (ieee80211_has_tods(fc))
return hdr->addr1;
if (ieee80211_has_fromds(fc))
return hdr->addr2;
return hdr->addr3;
}
if (ieee80211_is_mgmt(fc)) {
if (len < 24) /* drop incorrect hdr len (mgmt) */
return NULL;
return hdr->addr3;
}
if (ieee80211_is_ctl(fc)) {
if (ieee80211_is_pspoll(fc))
return hdr->addr1;
if (ieee80211_is_back_req(fc)) {
switch (type) {
case NL80211_IFTYPE_STATION:
return hdr->addr2;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
return hdr->addr1;
default:
break; /* fall through to the return */
}
}
}
return NULL;
}
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx) void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
{ {
struct sk_buff *skb; struct sk_buff *skb;
@ -752,7 +703,12 @@ EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);
struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif) struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
{ {
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_sub_if_data *sdata;
if (!vif)
return NULL;
sdata = vif_to_sdata(vif);
if (!ieee80211_sdata_running(sdata) || if (!ieee80211_sdata_running(sdata) ||
!(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
@ -1709,6 +1665,7 @@ static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
local->resuming = false; local->resuming = false;
local->suspended = false; local->suspended = false;
local->started = false; local->started = false;
local->in_reconfig = false;
/* scheduled scan clearly can't be running any more, but tell /* scheduled scan clearly can't be running any more, but tell
* cfg80211 and clear local state * cfg80211 and clear local state
@ -1759,16 +1716,24 @@ int ieee80211_reconfig(struct ieee80211_local *local)
struct ieee80211_sub_if_data *sched_scan_sdata; struct ieee80211_sub_if_data *sched_scan_sdata;
struct cfg80211_sched_scan_request *sched_scan_req; struct cfg80211_sched_scan_request *sched_scan_req;
bool sched_scan_stopped = false; bool sched_scan_stopped = false;
bool suspended = local->suspended;
/* nothing to do if HW shouldn't run */ /* nothing to do if HW shouldn't run */
if (!local->open_count) if (!local->open_count)
goto wake_up; goto wake_up;
#ifdef CONFIG_PM #ifdef CONFIG_PM
if (local->suspended) if (suspended)
local->resuming = true; local->resuming = true;
if (local->wowlan) { if (local->wowlan) {
/*
* In the wowlan case, both mac80211 and the device
* are functional when the resume op is called, so
* clear local->suspended so the device could operate
* normally (e.g. pass rx frames).
*/
local->suspended = false;
res = drv_resume(local); res = drv_resume(local);
local->wowlan = false; local->wowlan = false;
if (res < 0) { if (res < 0) {
@ -1781,8 +1746,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
/* /*
* res is 1, which means the driver requested * res is 1, which means the driver requested
* to go through a regular reset on wakeup. * to go through a regular reset on wakeup.
* restore local->suspended in this case.
*/ */
reconfig_due_to_wowlan = true; reconfig_due_to_wowlan = true;
local->suspended = true;
} }
#endif #endif
@ -1794,7 +1761,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
*/ */
res = drv_start(local); res = drv_start(local);
if (res) { if (res) {
if (local->suspended) if (suspended)
WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n"); WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n");
else else
WARN(1, "Hardware became unavailable during restart.\n"); WARN(1, "Hardware became unavailable during restart.\n");
@ -2088,10 +2055,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
* If this is for hw restart things are still running. * If this is for hw restart things are still running.
* We may want to change that later, however. * We may want to change that later, however.
*/ */
if (local->open_count && (!local->suspended || reconfig_due_to_wowlan)) if (local->open_count && (!suspended || reconfig_due_to_wowlan))
drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART); drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART);
if (!local->suspended) if (!suspended)
return 0; return 0;
#ifdef CONFIG_PM #ifdef CONFIG_PM

View file

@ -308,11 +308,15 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
{ {
struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_sub_if_data *sdata = sta->sdata;
enum ieee80211_sta_rx_bandwidth bw; enum ieee80211_sta_rx_bandwidth bw;
enum nl80211_chan_width bss_width = sdata->vif.bss_conf.chandef.width;
bw = ieee80211_chan_width_to_rx_bw(sdata->vif.bss_conf.chandef.width); bw = ieee80211_sta_cap_rx_bw(sta);
bw = min(bw, ieee80211_sta_cap_rx_bw(sta));
bw = min(bw, sta->cur_max_bandwidth); bw = min(bw, sta->cur_max_bandwidth);
/* do not cap the BW of TDLS WIDER_BW peers by the bss */
if (!test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
bw = min(bw, ieee80211_chan_width_to_rx_bw(bss_width));
return bw; return bw;
} }
@ -422,3 +426,29 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
if (changed > 0) if (changed > 0)
rate_control_rate_update(local, sband, sta, changed); rate_control_rate_update(local, sband, sta, changed);
} }
void ieee80211_get_vht_mask_from_cap(__le16 vht_cap,
u16 vht_mask[NL80211_VHT_NSS_MAX])
{
int i;
u16 mask, cap = le16_to_cpu(vht_cap);
for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
mask = (cap >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
switch (mask) {
case IEEE80211_VHT_MCS_SUPPORT_0_7:
vht_mask[i] = 0x00FF;
break;
case IEEE80211_VHT_MCS_SUPPORT_0_8:
vht_mask[i] = 0x01FF;
break;
case IEEE80211_VHT_MCS_SUPPORT_0_9:
vht_mask[i] = 0x03FF;
break;
case IEEE80211_VHT_MCS_NOT_SUPPORTED:
default:
vht_mask[i] = 0;
break;
}
}
}

View file

@ -516,30 +516,33 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
} }
ccmp_hdr2pn(pn, skb->data + hdrlen); if (!(status->flag & RX_FLAG_PN_VALIDATED)) {
ccmp_hdr2pn(pn, skb->data + hdrlen);
queue = rx->security_idx; queue = rx->security_idx;
if (memcmp(pn, key->u.ccmp.rx_pn[queue], IEEE80211_CCMP_PN_LEN) <= 0) { if (memcmp(pn, key->u.ccmp.rx_pn[queue],
key->u.ccmp.replays++; IEEE80211_CCMP_PN_LEN) <= 0) {
return RX_DROP_UNUSABLE; key->u.ccmp.replays++;
}
if (!(status->flag & RX_FLAG_DECRYPTED)) {
u8 aad[2 * AES_BLOCK_SIZE];
u8 b_0[AES_BLOCK_SIZE];
/* hardware didn't decrypt/verify MIC */
ccmp_special_blocks(skb, pn, b_0, aad);
if (ieee80211_aes_ccm_decrypt(
key->u.ccmp.tfm, b_0, aad,
skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
data_len,
skb->data + skb->len - mic_len, mic_len))
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
} }
memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN); if (!(status->flag & RX_FLAG_DECRYPTED)) {
u8 aad[2 * AES_BLOCK_SIZE];
u8 b_0[AES_BLOCK_SIZE];
/* hardware didn't decrypt/verify MIC */
ccmp_special_blocks(skb, pn, b_0, aad);
if (ieee80211_aes_ccm_decrypt(
key->u.ccmp.tfm, b_0, aad,
skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
data_len,
skb->data + skb->len - mic_len, mic_len))
return RX_DROP_UNUSABLE;
}
memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN);
}
/* Remove CCMP header and MIC */ /* Remove CCMP header and MIC */
if (pskb_trim(skb, skb->len - mic_len)) if (pskb_trim(skb, skb->len - mic_len))
@ -739,30 +742,34 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
} }
gcmp_hdr2pn(pn, skb->data + hdrlen); if (!(status->flag & RX_FLAG_PN_VALIDATED)) {
gcmp_hdr2pn(pn, skb->data + hdrlen);
queue = rx->security_idx; queue = rx->security_idx;
if (memcmp(pn, key->u.gcmp.rx_pn[queue], IEEE80211_GCMP_PN_LEN) <= 0) { if (memcmp(pn, key->u.gcmp.rx_pn[queue],
key->u.gcmp.replays++; IEEE80211_GCMP_PN_LEN) <= 0) {
return RX_DROP_UNUSABLE; key->u.gcmp.replays++;
}
if (!(status->flag & RX_FLAG_DECRYPTED)) {
u8 aad[2 * AES_BLOCK_SIZE];
u8 j_0[AES_BLOCK_SIZE];
/* hardware didn't decrypt/verify MIC */
gcmp_special_blocks(skb, pn, j_0, aad);
if (ieee80211_aes_gcm_decrypt(
key->u.gcmp.tfm, j_0, aad,
skb->data + hdrlen + IEEE80211_GCMP_HDR_LEN,
data_len,
skb->data + skb->len - IEEE80211_GCMP_MIC_LEN))
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
} }
memcpy(key->u.gcmp.rx_pn[queue], pn, IEEE80211_GCMP_PN_LEN); if (!(status->flag & RX_FLAG_DECRYPTED)) {
u8 aad[2 * AES_BLOCK_SIZE];
u8 j_0[AES_BLOCK_SIZE];
/* hardware didn't decrypt/verify MIC */
gcmp_special_blocks(skb, pn, j_0, aad);
if (ieee80211_aes_gcm_decrypt(
key->u.gcmp.tfm, j_0, aad,
skb->data + hdrlen + IEEE80211_GCMP_HDR_LEN,
data_len,
skb->data + skb->len -
IEEE80211_GCMP_MIC_LEN))
return RX_DROP_UNUSABLE;
}
memcpy(key->u.gcmp.rx_pn[queue], pn, IEEE80211_GCMP_PN_LEN);
}
/* Remove GCMP header and MIC */ /* Remove GCMP header and MIC */
if (pskb_trim(skb, skb->len - IEEE80211_GCMP_MIC_LEN)) if (pskb_trim(skb, skb->len - IEEE80211_GCMP_MIC_LEN))

View file

@ -36,7 +36,8 @@ config RFKILL_REGULATOR
config RFKILL_GPIO config RFKILL_GPIO
tristate "GPIO RFKILL driver" tristate "GPIO RFKILL driver"
depends on RFKILL && GPIOLIB depends on RFKILL
depends on GPIOLIB || COMPILE_TEST
default n default n
help help
If you say yes here you get support of a generic gpio RFKILL If you say yes here you get support of a generic gpio RFKILL

View file

@ -407,6 +407,9 @@ use_default_name:
INIT_LIST_HEAD(&rdev->bss_list); INIT_LIST_HEAD(&rdev->bss_list);
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results);
INIT_LIST_HEAD(&rdev->mlme_unreg);
spin_lock_init(&rdev->mlme_unreg_lock);
INIT_WORK(&rdev->mlme_unreg_wk, cfg80211_mlme_unreg_wk);
INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk, INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk,
cfg80211_dfs_channels_update_work); cfg80211_dfs_channels_update_work);
#ifdef CONFIG_CFG80211_WEXT #ifdef CONFIG_CFG80211_WEXT
@ -802,6 +805,7 @@ void wiphy_unregister(struct wiphy *wiphy)
cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
flush_work(&rdev->destroy_work); flush_work(&rdev->destroy_work);
flush_work(&rdev->sched_scan_stop_wk); flush_work(&rdev->sched_scan_stop_wk);
flush_work(&rdev->mlme_unreg_wk);
#ifdef CONFIG_PM #ifdef CONFIG_PM
if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
@ -855,6 +859,7 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
switch (wdev->iftype) { switch (wdev->iftype) {
case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_P2P_DEVICE:
cfg80211_mlme_purge_registrations(wdev);
cfg80211_stop_p2p_device(rdev, wdev); cfg80211_stop_p2p_device(rdev, wdev);
break; break;
default: default:

View file

@ -59,6 +59,10 @@ struct cfg80211_registered_device {
struct list_head beacon_registrations; struct list_head beacon_registrations;
spinlock_t beacon_registrations_lock; spinlock_t beacon_registrations_lock;
struct list_head mlme_unreg;
spinlock_t mlme_unreg_lock;
struct work_struct mlme_unreg_wk;
/* protected by RTNL only */ /* protected by RTNL only */
int num_running_ifaces; int num_running_ifaces;
int num_running_monitor_ifaces; int num_running_monitor_ifaces;
@ -348,6 +352,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
u16 frame_type, const u8 *match_data, u16 frame_type, const u8 *match_data,
int match_len); int match_len);
void cfg80211_mlme_unreg_wk(struct work_struct *wk);
void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid); void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid);
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,

View file

@ -2,6 +2,7 @@
* cfg80211 MLME SAP interface * cfg80211 MLME SAP interface
* *
* Copyright (c) 2009, Jouni Malinen <j@w1.fi> * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2015 Intel Deutschland GmbH
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
@ -389,6 +390,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
struct cfg80211_mgmt_registration { struct cfg80211_mgmt_registration {
struct list_head list; struct list_head list;
struct wireless_dev *wdev;
u32 nlportid; u32 nlportid;
@ -399,6 +401,46 @@ struct cfg80211_mgmt_registration {
u8 match[]; u8 match[];
}; };
static void
cfg80211_process_mlme_unregistrations(struct cfg80211_registered_device *rdev)
{
struct cfg80211_mgmt_registration *reg;
ASSERT_RTNL();
spin_lock_bh(&rdev->mlme_unreg_lock);
while ((reg = list_first_entry_or_null(&rdev->mlme_unreg,
struct cfg80211_mgmt_registration,
list))) {
list_del(&reg->list);
spin_unlock_bh(&rdev->mlme_unreg_lock);
if (rdev->ops->mgmt_frame_register) {
u16 frame_type = le16_to_cpu(reg->frame_type);
rdev_mgmt_frame_register(rdev, reg->wdev,
frame_type, false);
}
kfree(reg);
spin_lock_bh(&rdev->mlme_unreg_lock);
}
spin_unlock_bh(&rdev->mlme_unreg_lock);
}
void cfg80211_mlme_unreg_wk(struct work_struct *wk)
{
struct cfg80211_registered_device *rdev;
rdev = container_of(wk, struct cfg80211_registered_device,
mlme_unreg_wk);
rtnl_lock();
cfg80211_process_mlme_unregistrations(rdev);
rtnl_unlock();
}
int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
u16 frame_type, const u8 *match_data, u16 frame_type, const u8 *match_data,
int match_len) int match_len)
@ -449,11 +491,18 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
nreg->match_len = match_len; nreg->match_len = match_len;
nreg->nlportid = snd_portid; nreg->nlportid = snd_portid;
nreg->frame_type = cpu_to_le16(frame_type); nreg->frame_type = cpu_to_le16(frame_type);
nreg->wdev = wdev;
list_add(&nreg->list, &wdev->mgmt_registrations); list_add(&nreg->list, &wdev->mgmt_registrations);
spin_unlock_bh(&wdev->mgmt_registrations_lock);
/* process all unregistrations to avoid driver confusion */
cfg80211_process_mlme_unregistrations(rdev);
if (rdev->ops->mgmt_frame_register) if (rdev->ops->mgmt_frame_register)
rdev_mgmt_frame_register(rdev, wdev, frame_type, true); rdev_mgmt_frame_register(rdev, wdev, frame_type, true);
return 0;
out: out:
spin_unlock_bh(&wdev->mgmt_registrations_lock); spin_unlock_bh(&wdev->mgmt_registrations_lock);
@ -472,15 +521,12 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
if (reg->nlportid != nlportid) if (reg->nlportid != nlportid)
continue; continue;
if (rdev->ops->mgmt_frame_register) {
u16 frame_type = le16_to_cpu(reg->frame_type);
rdev_mgmt_frame_register(rdev, wdev,
frame_type, false);
}
list_del(&reg->list); list_del(&reg->list);
kfree(reg); spin_lock(&rdev->mlme_unreg_lock);
list_add_tail(&reg->list, &rdev->mlme_unreg);
spin_unlock(&rdev->mlme_unreg_lock);
schedule_work(&rdev->mlme_unreg_wk);
} }
spin_unlock_bh(&wdev->mgmt_registrations_lock); spin_unlock_bh(&wdev->mgmt_registrations_lock);
@ -496,16 +542,15 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
{ {
struct cfg80211_mgmt_registration *reg, *tmp; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
spin_lock_bh(&wdev->mgmt_registrations_lock); spin_lock_bh(&wdev->mgmt_registrations_lock);
spin_lock(&rdev->mlme_unreg_lock);
list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { list_splice_tail_init(&wdev->mgmt_registrations, &rdev->mlme_unreg);
list_del(&reg->list); spin_unlock(&rdev->mlme_unreg_lock);
kfree(reg);
}
spin_unlock_bh(&wdev->mgmt_registrations_lock); spin_unlock_bh(&wdev->mgmt_registrations_lock);
cfg80211_process_mlme_unregistrations(rdev);
} }
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,

View file

@ -2321,6 +2321,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
rdev->wiphy.frag_threshold = old_frag_threshold; rdev->wiphy.frag_threshold = old_frag_threshold;
rdev->wiphy.rts_threshold = old_rts_threshold; rdev->wiphy.rts_threshold = old_rts_threshold;
rdev->wiphy.coverage_class = old_coverage_class; rdev->wiphy.coverage_class = old_coverage_class;
return result;
} }
} }
return 0; return 0;
@ -7390,7 +7391,8 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
int err; int err;
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (!rdev->ops->set_mcast_rate) if (!rdev->ops->set_mcast_rate)

View file

@ -733,6 +733,8 @@ static inline void
rdev_mgmt_frame_register(struct cfg80211_registered_device *rdev, rdev_mgmt_frame_register(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, u16 frame_type, bool reg) struct wireless_dev *wdev, u16 frame_type, bool reg)
{ {
might_sleep();
trace_rdev_mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg); trace_rdev_mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
rdev->ops->mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg); rdev->ops->mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
trace_rdev_return_void(&rdev->wiphy); trace_rdev_return_void(&rdev->wiphy);

View file

@ -1004,7 +1004,7 @@ static u32 map_regdom_flags(u32 rd_flags)
static const struct ieee80211_reg_rule * static const struct ieee80211_reg_rule *
freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
const struct ieee80211_regdomain *regd) const struct ieee80211_regdomain *regd, u32 bw)
{ {
int i; int i;
bool band_rule_found = false; bool band_rule_found = false;
@ -1028,7 +1028,7 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
if (!band_rule_found) if (!band_rule_found)
band_rule_found = freq_in_rule_band(fr, center_freq); band_rule_found = freq_in_rule_band(fr, center_freq);
bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20)); bw_fits = reg_does_bw_fit(fr, center_freq, bw);
if (band_rule_found && bw_fits) if (band_rule_found && bw_fits)
return rr; return rr;
@ -1040,14 +1040,26 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
const struct ieee80211_reg_rule *__freq_reg_info(struct wiphy *wiphy,
u32 center_freq, u32 min_bw)
{
const struct ieee80211_regdomain *regd = reg_get_regdomain(wiphy);
const struct ieee80211_reg_rule *reg_rule = NULL;
u32 bw;
for (bw = MHZ_TO_KHZ(20); bw >= min_bw; bw = bw / 2) {
reg_rule = freq_reg_info_regd(wiphy, center_freq, regd, bw);
if (!IS_ERR(reg_rule))
return reg_rule;
}
return reg_rule;
}
const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
u32 center_freq) u32 center_freq)
{ {
const struct ieee80211_regdomain *regd; return __freq_reg_info(wiphy, center_freq, MHZ_TO_KHZ(20));
regd = reg_get_regdomain(wiphy);
return freq_reg_info_regd(wiphy, center_freq, regd);
} }
EXPORT_SYMBOL(freq_reg_info); EXPORT_SYMBOL(freq_reg_info);
@ -1176,8 +1188,20 @@ static void handle_channel(struct wiphy *wiphy,
if (reg_rule->flags & NL80211_RRF_AUTO_BW) if (reg_rule->flags & NL80211_RRF_AUTO_BW)
max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule); max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
/* If we get a reg_rule we can assume that at least 5Mhz fit */
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
MHZ_TO_KHZ(10)))
bw_flags |= IEEE80211_CHAN_NO_10MHZ;
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
MHZ_TO_KHZ(20)))
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(10))
bw_flags |= IEEE80211_CHAN_NO_10MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(20))
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(40)) if (max_bandwidth_khz < MHZ_TO_KHZ(40))
bw_flags = IEEE80211_CHAN_NO_HT40; bw_flags |= IEEE80211_CHAN_NO_HT40;
if (max_bandwidth_khz < MHZ_TO_KHZ(80)) if (max_bandwidth_khz < MHZ_TO_KHZ(80))
bw_flags |= IEEE80211_CHAN_NO_80MHZ; bw_flags |= IEEE80211_CHAN_NO_80MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(160)) if (max_bandwidth_khz < MHZ_TO_KHZ(160))
@ -1695,9 +1719,15 @@ static void handle_channel_custom(struct wiphy *wiphy,
const struct ieee80211_power_rule *power_rule = NULL; const struct ieee80211_power_rule *power_rule = NULL;
const struct ieee80211_freq_range *freq_range = NULL; const struct ieee80211_freq_range *freq_range = NULL;
u32 max_bandwidth_khz; u32 max_bandwidth_khz;
u32 bw;
reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), for (bw = MHZ_TO_KHZ(20); bw >= MHZ_TO_KHZ(5); bw = bw / 2) {
regd); reg_rule = freq_reg_info_regd(wiphy,
MHZ_TO_KHZ(chan->center_freq),
regd, bw);
if (!IS_ERR(reg_rule))
break;
}
if (IS_ERR(reg_rule)) { if (IS_ERR(reg_rule)) {
REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n", REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
@ -1721,8 +1751,20 @@ static void handle_channel_custom(struct wiphy *wiphy,
if (reg_rule->flags & NL80211_RRF_AUTO_BW) if (reg_rule->flags & NL80211_RRF_AUTO_BW)
max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule); max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
/* If we get a reg_rule we can assume that at least 5Mhz fit */
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
MHZ_TO_KHZ(10)))
bw_flags |= IEEE80211_CHAN_NO_10MHZ;
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
MHZ_TO_KHZ(20)))
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(10))
bw_flags |= IEEE80211_CHAN_NO_10MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(20))
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(40)) if (max_bandwidth_khz < MHZ_TO_KHZ(40))
bw_flags = IEEE80211_CHAN_NO_HT40; bw_flags |= IEEE80211_CHAN_NO_HT40;
if (max_bandwidth_khz < MHZ_TO_KHZ(80)) if (max_bandwidth_khz < MHZ_TO_KHZ(80))
bw_flags |= IEEE80211_CHAN_NO_80MHZ; bw_flags |= IEEE80211_CHAN_NO_80MHZ;
if (max_bandwidth_khz < MHZ_TO_KHZ(160)) if (max_bandwidth_khz < MHZ_TO_KHZ(160))
@ -2079,10 +2121,7 @@ static void reg_process_hint(struct regulatory_request *reg_request)
reg_process_hint_core(reg_request); reg_process_hint_core(reg_request);
return; return;
case NL80211_REGDOM_SET_BY_USER: case NL80211_REGDOM_SET_BY_USER:
treatment = reg_process_hint_user(reg_request); reg_process_hint_user(reg_request);
if (treatment == REG_REQ_IGNORE ||
treatment == REG_REQ_ALREADY_SET)
return;
return; return;
case NL80211_REGDOM_SET_BY_DRIVER: case NL80211_REGDOM_SET_BY_DRIVER:
if (!wiphy) if (!wiphy)
@ -2099,7 +2138,9 @@ static void reg_process_hint(struct regulatory_request *reg_request)
goto out_free; goto out_free;
} }
/* This is required so that the orig_* parameters are saved */ /* This is required so that the orig_* parameters are saved.
* NOTE: treatment must be set for any case that reaches here!
*/
if (treatment == REG_REQ_ALREADY_SET && wiphy && if (treatment == REG_REQ_ALREADY_SET && wiphy &&
wiphy->regulatory_flags & REGULATORY_STRICT_REG) { wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
wiphy_update_regulatory(wiphy, reg_request->initiator); wiphy_update_regulatory(wiphy, reg_request->initiator);