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 */
struct ieee80211_hw *hw;
struct napi_struct *napi;
struct list_head calib_results;
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);
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 = {

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));
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)

View File

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

View File

@ -559,6 +559,7 @@ struct iwl_mvm {
const struct iwl_cfg *cfg;
struct iwl_phy_db *phy_db;
struct ieee80211_hw *hw;
struct napi_struct *napi;
/* for protecting access to iwl_mvm */
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);
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 = {

View File

@ -129,7 +129,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
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;
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);
pn = seq.aes_cmac.pn;
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, MFP_CAPABLE);
ieee80211_hw_set(hw, SIGNAL_DBM);
ieee80211_hw_set(hw, TDLS_WIDER_BW);
if (rctbl)
ieee80211_hw_set(hw, SUPPORTS_RC_TABLE);
@ -3120,8 +3121,10 @@ static int hwsim_init_netlink(void)
goto failure;
rc = netlink_register_notifier(&hwsim_netlink_notifier);
if (rc)
if (rc) {
genl_unregister_family(&hwsim_genl_family);
goto failure;
}
return 0;

View File

@ -27,4 +27,43 @@ static inline unsigned long ewma_read(const struct ewma *avg)
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 */

View File

@ -2074,8 +2074,8 @@ enum ieee80211_tdls_actioncode {
#define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6)
#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_TDLS_WIDE_BW_ENABLED BIT(7)
/* TDLS specific payload type in the LLC/SNAP header */
#define WLAN_TDLS_SNAP_RFTYPE 0x2

View File

@ -2369,8 +2369,7 @@ struct cfg80211_qos_map {
* method returns 0.)
*
* @mgmt_frame_register: Notify driver that a management frame type was
* registered. Note that this callback may not sleep, and cannot run
* concurrently with itself.
* registered. The callback is allowed to sleep.
*
* @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

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.
* If this flag is set, the stack cannot do any replay detection
* 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
* the frame.
* @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
* number (@ampdu_reference) must be populated and be a distinct number for
* 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
* subframes of a single 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_HT_GF = BIT(13),
RX_FLAG_AMPDU_DETAILS = BIT(14),
RX_FLAG_AMPDU_REPORT_ZEROLEN = BIT(15),
RX_FLAG_AMPDU_IS_ZEROLEN = BIT(16),
RX_FLAG_PN_VALIDATED = BIT(15),
/* bit 16 free */
RX_FLAG_AMPDU_LAST_KNOWN = BIT(17),
RX_FLAG_AMPDU_IS_LAST = BIT(18),
RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(19),
@ -1491,8 +1492,10 @@ enum ieee80211_key_flags {
* - Temporal Authenticator Rx MIC Key (64 bits)
* @icv_len: The ICV length for this key type
* @iv_len: The IV length for this key type
* @drv_priv: pointer for driver use
*/
struct ieee80211_key_conf {
void *drv_priv;
atomic64_t tx_pn;
u32 cipher;
u8 icv_len;
@ -1675,7 +1678,6 @@ struct ieee80211_sta_rates {
* @tdls: indicates whether the STA is a TDLS peer
* @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.
* @mfp: indicates whether the STA uses management frame protection or not.
* @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
*/
struct ieee80211_sta {
@ -1693,7 +1695,6 @@ struct ieee80211_sta {
struct ieee80211_sta_rates __rcu *rates;
bool tdls;
bool tdls_initiator;
bool mfp;
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
* 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
*/
enum ieee80211_hw_flags {
@ -1920,6 +1924,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_CHANCTX_STA_CSA,
IEEE80211_HW_SUPPORTS_CLONED_SKBS,
IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS,
IEEE80211_HW_TDLS_WIDER_BW,
/* keep last, obviously */
NUM_IEEE80211_HW_FLAGS
@ -3696,20 +3701,28 @@ void ieee80211_free_hw(struct ieee80211_hw *hw);
void ieee80211_restart_hw(struct ieee80211_hw *hw);
/**
* ieee80211_napi_add - initialize mac80211 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
* ieee80211_rx_napi - receive frame from NAPI context
*
* 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,
struct net_device *napi_dev,
int (*poll)(struct napi_struct *, int),
int weight);
void ieee80211_rx_napi(struct ieee80211_hw *hw, struct sk_buff *skb,
struct napi_struct *napi);
/**
* 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
* @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
@ -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,
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
*

View File

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

View File

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

View File

@ -145,20 +145,3 @@ void ieee80211_aes_cmac_key_free(struct crypto_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;
}
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,
struct sta_info *sta,
struct station_parameters *params)
@ -1076,7 +1135,6 @@ static int sta_apply_parameters(struct ieee80211_local *local,
}
if (mask & BIT(NL80211_STA_FLAG_MFP)) {
sta->sta.mfp = !!(set & BIT(NL80211_STA_FLAG_MFP));
if (set & BIT(NL80211_STA_FLAG_MFP))
set_sta_flag(sta, WLAN_STA_MFP);
else
@ -1097,6 +1155,12 @@ static int sta_apply_parameters(struct ieee80211_local *local,
params->ext_capab[3] & WLAN_EXT_CAPA4_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) {
sta->sta.uapsd_queues = params->uapsd_queues;
sta->sta.max_sp = params->max_sp;
@ -1144,62 +1208,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
band, false);
}
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH
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
}
if (ieee80211_vif_is_mesh(&sdata->vif))
sta_apply_mesh_params(local, sta, params);
/* set the STA state after all sta info from usermode has been set */
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;
enum ieee80211_smps_mode old_req;
int err;
struct sta_info *sta;
bool tdls_peer_found = false;
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;
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 (sdata->u.mgd.powersave)
smps_mode = IEEE80211_SMPS_DYNAMIC;
else
if (tdls_peer_found || !sdata->u.mgd.powersave)
smps_mode = IEEE80211_SMPS_OFF;
else
smps_mode = IEEE80211_SMPS_DYNAMIC;
}
/* send SM PS frame to AP */
@ -2394,6 +2417,8 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
ap, ap);
if (err)
sdata->u.mgd.req_smps = old_req;
else if (smps_mode != IEEE80211_SMPS_OFF && tdls_peer_found)
ieee80211_teardown_tdls_peers(sdata);
return err;
}
@ -2479,16 +2504,26 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
memcpy(sdata->rc_rateidx_mcs_mask[i], 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_vht_mcs_mask[i] = false;
if (!sband)
continue;
for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++)
if (~sdata->rc_rateidx_mcs_mask[i][j]) {
for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++) {
if (~sdata->rc_rateidx_mcs_mask[i][j])
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;
}
}
}
return 0;

View File

@ -190,7 +190,7 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
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) {
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:
width = ieee80211_get_max_required_bw(sdata);
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:
continue;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_MESH_POINT:
@ -554,12 +562,13 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local,
kfree_rcu(ctx, rcu_head);
}
static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx)
void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx)
{
struct ieee80211_chanctx_conf *conf = &ctx->conf;
struct ieee80211_sub_if_data *sdata;
const struct cfg80211_chan_def *compat = NULL;
struct sta_info *sta;
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))
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();
if (!compat)

View File

@ -122,6 +122,7 @@ static const char *hw_flag_names[NUM_IEEE80211_HW_FLAGS + 1] = {
FLAG(CHANCTX_STA_CSA),
FLAG(SUPPORTS_CLONED_SKBS),
FLAG(SINGLE_SCAN_ON_ALL_BANDS),
FLAG(TDLS_WIDER_BW),
/* keep last for the build bug below */
(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_drop_nullfunc);
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_cloned);
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(hw_key_idx, D);
KEY_FILE(flags, X);
KEY_FILE(tx_rx_count, D);
KEY_READ(ifindex, sdata->name, "%s\n");
KEY_OPS(ifindex);
@ -310,7 +309,6 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
DEBUGFS_ADD(flags);
DEBUGFS_ADD(keyidx);
DEBUGFS_ADD(hw_key_idx);
DEBUGFS_ADD(tx_rx_count);
DEBUGFS_ADD(algorithm);
DEBUGFS_ADD(tx_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,
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(state, state, LHEX);
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_mcs_mask_2ghz);
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);
}

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);
}
static inline __must_check
__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;
}
enum ieee80211_sta_state new_state);
static inline void drv_sta_rc_update(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,

View File

@ -84,13 +84,13 @@ struct ieee80211_local;
#define IEEE80211_DEAUTH_FRAME_LEN (24 /* hdr */ + 2 /* reason */)
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;
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 */
};
@ -181,7 +181,6 @@ typedef unsigned __bitwise__ ieee80211_rx_result;
/**
* enum ieee80211_packet_rx_flags - packet RX flags
* @IEEE80211_RX_FRAGMENTED: fragmented frame
* @IEEE80211_RX_AMSDU: a-MSDU packet
* @IEEE80211_RX_MALFORMED_ACTION_FRM: action frame is malformed
* @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.
*/
enum ieee80211_packet_rx_flags {
IEEE80211_RX_FRAGMENTED = BIT(2),
IEEE80211_RX_AMSDU = BIT(3),
IEEE80211_RX_MALFORMED_ACTION_FRM = BIT(4),
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_BEACON_REPORTED: This frame was already reported
* 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
* for a single frame.
@ -211,10 +207,10 @@ enum ieee80211_packet_rx_flags {
enum ieee80211_rx_flags {
IEEE80211_RX_CMNTR = BIT(0),
IEEE80211_RX_BEACON_REPORTED = BIT(1),
IEEE80211_RX_REORDER_TIMER = BIT(2),
};
struct ieee80211_rx_data {
struct napi_struct *napi;
struct sk_buff *skb;
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
@ -725,6 +721,7 @@ struct ieee80211_if_mesh {
* back to wireless media and to the local net stack.
* @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume.
* @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 {
IEEE80211_SDATA_ALLMULTI = BIT(0),
@ -732,6 +729,7 @@ enum ieee80211_sub_if_data_flags {
IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3),
IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4),
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];
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 {
struct ieee80211_if_ap ap;
struct ieee80211_if_wds wds;
@ -1010,7 +1011,6 @@ enum sdata_queue_type {
IEEE80211_SDATA_QUEUE_AGG_STOP = 2,
IEEE80211_SDATA_QUEUE_RX_AGG_START = 3,
IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4,
IEEE80211_SDATA_QUEUE_TDLS_CHSW = 5,
};
enum {
@ -1286,7 +1286,6 @@ struct ieee80211_local {
unsigned int rx_handlers_queued;
unsigned int rx_handlers_drop_nullfunc;
unsigned int rx_handlers_drop_defrag;
unsigned int rx_handlers_drop_short;
unsigned int tx_expand_skb_head;
unsigned int tx_expand_skb_head_cloned;
unsigned int rx_expand_skb_head_defrag;
@ -1348,14 +1347,16 @@ struct ieee80211_local {
struct ieee80211_sub_if_data __rcu *p2p_sdata;
struct napi_struct *napi;
/* virtual monitor interface */
struct ieee80211_sub_if_data __rcu *monitor_sdata;
struct cfg80211_chan_def monitor_chandef;
/* extended capabilities provided by mac80211 */
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 *
@ -1715,6 +1716,8 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
enum ieee80211_band band, bool nss_only);
void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
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 */
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 */
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 rate, int erp, int short_preamble,
int shift);
@ -2042,6 +2043,9 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
enum ieee80211_chanctx_mode chanmode,
u8 radar_detect);
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 */
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,
struct net_device *dev,
const u8 *addr);
void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata);
void ieee80211_tdls_chsw_work(struct work_struct *wk);
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,
false);
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) &&
mgmt->u.action.category == WLAN_CATEGORY_BACK) {
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];
sdata->rc_rateidx_mask[i] =
sband ? (1 << sband->n_bitrates) - 1 : 0;
if (sband)
if (sband) {
__le16 cap;
u16 *vht_rate_mask;
memcpy(sdata->rc_rateidx_mcs_mask[i],
sband->ht_cap.mcs.rx_mask,
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,
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);

View File

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

View File

@ -115,9 +115,6 @@ struct ieee80211_key {
} gen;
} u;
/* number of times this key has been used */
int tx_rx_count;
#ifdef CONFIG_MAC80211_DEBUGFS
struct {
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,
ieee80211_sched_scan_stopped_work);
INIT_WORK(&local->tdls_chsw_work, ieee80211_tdls_chsw_work);
spin_lock_init(&local->ack_status_lock);
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_unreliable);
skb_queue_head_init(&local->skb_queue_tdls_chsw);
ieee80211_alloc_led_names(local);
@ -1132,18 +1135,6 @@ int ieee80211_register_hw(struct ieee80211_hw *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)
{
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->reconfig_filter);
cancel_work_sync(&local->tdls_chsw_work);
flush_work(&local->sched_scan_stopped_work);
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");
skb_queue_purge(&local->skb_queue);
skb_queue_purge(&local->skb_queue_unreliable);
skb_queue_purge(&local->skb_queue_tdls_chsw);
destroy_workqueue(local->workqueue);
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);
if (!sdata->u.mesh.user_mpm) {
changed |= mesh_plink_deactivate(sta);
del_timer_sync(&sta->plink_timer);
del_timer_sync(&sta->mesh->plink_timer);
}
if (changed)

View File

@ -19,15 +19,6 @@
#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 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 SN_GT(x, y) ((s32)(y - x) < 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) \
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;
/* number of destinations */
*pos++ = 1;
/*
* flags bit, bit 1 is unset if we know the sequence number and
* bit 2 is set if we have a reason code
/* Flags field has AE bit only as defined in
* sec 8.4.2.117 IEEE802.11-2012
*/
*pos = 0;
if (!target_sn)
*pos |= MP_F_USN;
if (target_rcode)
*pos |= MP_F_RCODE;
pos++;
memcpy(pos, target, ETH_ALEN);
pos += ETH_ALEN;
@ -316,8 +308,9 @@ void ieee80211s_update_metric(struct ieee80211_local *local,
failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK);
/* moving average, scaled to 100 */
sta->fail_avg = ((80 * sta->fail_avg + 5) / 100 + 20 * failed);
if (sta->fail_avg > 95)
sta->mesh->fail_avg =
((80 * sta->mesh->fail_avg + 5) / 100 + 20 * failed);
if (sta->mesh->fail_avg > 95)
mesh_plink_broken(sta);
}
@ -333,7 +326,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
u32 tx_time, estimated_retx;
u64 result;
if (sta->fail_avg >= 100)
if (sta->mesh->fail_avg >= 100)
return MAX_METRIC;
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))
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
* 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;
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 {
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)) {
mpath->sn = target_sn;
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)) {
reply = true;
target_metric = mpath->metric;
target_sn = mpath->sn;
if (target_flags & MP_F_RF)
target_flags |= MP_F_DO;
else
forward = false;
/* Case E2 of sec 13.10.9.3 IEEE 802.11-2012*/
target_flags |= IEEE80211_PREQ_TO_FLAG;
}
}
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 &&
ether_addr_equal(ta, sta->sta.addr) &&
(!(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->sn = target_sn;
if (target_sn != 0)
mpath->sn = target_sn;
else
mpath->sn += 1;
spin_unlock_bh(&mpath->state_lock);
if (!ifmsh->mshcfg.dot11MeshForwarding)
goto endperr;
@ -862,7 +876,7 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
rcu_read_lock();
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();
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 mesh_preq_queue *preq_node;
struct mesh_path *mpath;
u8 ttl, target_flags;
u8 ttl, target_flags = 0;
const u8 *da;
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)
target_flags = MP_F_DO;
target_flags |= IEEE80211_PREQ_TO_FLAG;
else
target_flags = MP_F_RF;
target_flags &= ~IEEE80211_PREQ_TO_FLAG;
spin_unlock_bh(&mpath->state_lock);
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);
mesh_queue_preq(mpath, 0);
} else {
mpath->flags = 0;
mpath->flags &= ~(MESH_PATH_RESOLVING |
MESH_PATH_RESOLVED |
MESH_PATH_REQ_QUEUED);
mpath->exp_time = jiffies;
spin_unlock_bh(&mpath->state_lock);
if (!mpath->is_gate && mesh_gate_num(sdata) > 0) {

View File

@ -13,10 +13,11 @@
#include "rate.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_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)))
enum plink_event {
@ -53,18 +54,13 @@ static const char * const mplevents[] = {
[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. */
static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta)
{
s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold;
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
*
* 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)
{
lockdep_assert_held(&sta->plink_lock);
sta->plink_state = NL80211_PLINK_LISTEN;
sta->llid = sta->plid = sta->reason = 0;
sta->plink_retries = 0;
lockdep_assert_held(&sta->mesh->plink_lock);
sta->mesh->plink_state = NL80211_PLINK_LISTEN;
sta->mesh->llid = sta->mesh->plid = sta->mesh->reason = 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();
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sdata != sta->sdata ||
sta->plink_state != NL80211_PLINK_ESTAB)
sta->mesh->plink_state != NL80211_PLINK_ESTAB)
continue;
short_slot = false;
@ -169,7 +165,7 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sdata != sta->sdata ||
sta->plink_state != NL80211_PLINK_ESTAB)
sta->mesh->plink_state != NL80211_PLINK_ESTAB)
continue;
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;
}
/**
* __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,
struct sta_info *sta,
enum ieee80211_self_protected_actioncode action,
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) {
/* AID */
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) ||
ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
@ -375,6 +320,58 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
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,
struct sta_info *sta,
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];
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;
/* 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;
sta->processed_beacon = true;
sta->mesh->processed_beacon = true;
if (sta->sta.supp_rates[band] != rates)
changed |= IEEE80211_RC_SUPP_RATES_CHANGED;
@ -421,23 +419,57 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
else
rate_control_rate_update(local, sband, sta, changed);
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 *
__mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
{
struct sta_info *sta;
int aid;
if (sdata->local->num_sta >= MESH_MAX_PLINKS)
return NULL;
aid = mesh_allocate_aid(sdata);
if (aid < 0)
return NULL;
sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
if (!sta)
return NULL;
sta->plink_state = NL80211_PLINK_LISTEN;
sta->mesh->plink_state = NL80211_PLINK_LISTEN;
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_ASSOC);
@ -524,7 +556,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
goto out;
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.mshcfg.auto_open_plinks &&
rssi_threshold_check(sdata, sta))
@ -554,52 +586,52 @@ static void mesh_plink_timer(unsigned long data)
if (sta->sdata->local->quiescing)
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,
* we may have already extended the timeout and changed state by the
* time we've acquired the lock and arrived here. In that case,
* 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,
"Ignoring timer for %pM in state %s (timer adjusted)",
sta->sta.addr, mplstates[sta->plink_state]);
spin_unlock_bh(&sta->plink_lock);
sta->sta.addr, mplstates[sta->mesh->plink_state]);
spin_unlock_bh(&sta->mesh->plink_lock);
return;
}
/* del_timer() and handler may race when entering these states */
if (sta->plink_state == NL80211_PLINK_LISTEN ||
sta->plink_state == NL80211_PLINK_ESTAB) {
if (sta->mesh->plink_state == NL80211_PLINK_LISTEN ||
sta->mesh->plink_state == NL80211_PLINK_ESTAB) {
mpl_dbg(sta->sdata,
"Ignoring timer for %pM in state %s (timer deleted)",
sta->sta.addr, mplstates[sta->plink_state]);
spin_unlock_bh(&sta->plink_lock);
sta->sta.addr, mplstates[sta->mesh->plink_state]);
spin_unlock_bh(&sta->mesh->plink_lock);
return;
}
mpl_dbg(sta->sdata,
"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;
mshcfg = &sdata->u.mesh.mshcfg;
switch (sta->plink_state) {
switch (sta->mesh->plink_state) {
case NL80211_PLINK_OPN_RCVD:
case NL80211_PLINK_OPN_SNT:
/* retry timer */
if (sta->plink_retries < mshcfg->dot11MeshMaxRetries) {
if (sta->mesh->plink_retries < mshcfg->dot11MeshMaxRetries) {
u32 rand;
mpl_dbg(sta->sdata,
"Mesh plink for %pM (retry, timeout): %d %d\n",
sta->sta.addr, sta->plink_retries,
sta->plink_timeout);
sta->sta.addr, sta->mesh->plink_retries,
sta->mesh->plink_timeout);
get_random_bytes(&rand, sizeof(u32));
sta->plink_timeout = sta->plink_timeout +
rand % sta->plink_timeout;
++sta->plink_retries;
mod_plink_timer(sta, sta->plink_timeout);
sta->mesh->plink_timeout = sta->mesh->plink_timeout +
rand % sta->mesh->plink_timeout;
++sta->mesh->plink_retries;
mod_plink_timer(sta, sta->mesh->plink_timeout);
action = WLAN_SP_MESH_PEERING_OPEN;
break;
}
@ -609,31 +641,31 @@ static void mesh_plink_timer(unsigned long data)
/* confirm timer */
if (!reason)
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);
action = WLAN_SP_MESH_PEERING_CLOSE;
break;
case NL80211_PLINK_HOLDING:
/* holding timer */
del_timer(&sta->plink_timer);
del_timer(&sta->mesh->plink_timer);
mesh_plink_fsm_restart(sta);
break;
default:
break;
}
spin_unlock_bh(&sta->plink_lock);
spin_unlock_bh(&sta->mesh->plink_lock);
if (action)
mesh_plink_frame_tx(sdata, action, sta->sta.addr,
sta->llid, sta->plid, reason);
mesh_plink_frame_tx(sdata, sta, action, sta->sta.addr,
sta->mesh->llid, sta->mesh->plid, reason);
}
static inline void mesh_plink_timer_set(struct sta_info *sta, u32 timeout)
{
sta->plink_timer.expires = jiffies + msecs_to_jiffies(timeout);
sta->plink_timer.data = (unsigned long) sta;
sta->plink_timer.function = mesh_plink_timer;
sta->plink_timeout = timeout;
add_timer(&sta->plink_timer);
sta->mesh->plink_timer.expires = jiffies + msecs_to_jiffies(timeout);
sta->mesh->plink_timer.data = (unsigned long) sta;
sta->mesh->plink_timer.function = mesh_plink_timer;
sta->mesh->plink_timeout = timeout;
add_timer(&sta->mesh->plink_timer);
}
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();
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;
break;
}
@ -661,8 +693,6 @@ static u16 mesh_get_new_llid(struct ieee80211_sub_if_data *sdata)
do {
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));
return llid;
@ -676,16 +706,16 @@ u32 mesh_plink_open(struct sta_info *sta)
if (!test_sta_flag(sta, WLAN_STA_AUTH))
return 0;
spin_lock_bh(&sta->plink_lock);
sta->llid = mesh_get_new_llid(sdata);
if (sta->plink_state != NL80211_PLINK_LISTEN &&
sta->plink_state != NL80211_PLINK_BLOCKED) {
spin_unlock_bh(&sta->plink_lock);
spin_lock_bh(&sta->mesh->plink_lock);
sta->mesh->llid = mesh_get_new_llid(sdata);
if (sta->mesh->plink_state != NL80211_PLINK_LISTEN &&
sta->mesh->plink_state != NL80211_PLINK_BLOCKED) {
spin_unlock_bh(&sta->mesh->plink_lock);
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);
spin_unlock_bh(&sta->plink_lock);
spin_unlock_bh(&sta->mesh->plink_lock);
mpl_dbg(sdata,
"Mesh plink: starting establishment with %pM\n",
sta->sta.addr);
@ -693,8 +723,8 @@ u32 mesh_plink_open(struct sta_info *sta)
/* set the non-peer mode to active during peering */
changed = ieee80211_mps_local_status_update(sdata);
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
sta->sta.addr, sta->llid, 0, 0);
mesh_plink_frame_tx(sdata, sta, WLAN_SP_MESH_PEERING_OPEN,
sta->sta.addr, sta->mesh->llid, 0, 0);
return changed;
}
@ -702,10 +732,10 @@ u32 mesh_plink_block(struct sta_info *sta)
{
u32 changed;
spin_lock_bh(&sta->plink_lock);
spin_lock_bh(&sta->mesh->plink_lock);
changed = __mesh_plink_deactivate(sta);
sta->plink_state = NL80211_PLINK_BLOCKED;
spin_unlock_bh(&sta->plink_lock);
sta->mesh->plink_state = NL80211_PLINK_BLOCKED;
spin_unlock_bh(&sta->mesh->plink_lock);
return changed;
}
@ -715,12 +745,11 @@ static void mesh_plink_close(struct ieee80211_sub_if_data *sdata,
enum plink_event event)
{
struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
u16 reason = (event == CLS_ACPT) ?
WLAN_REASON_MESH_CLOSE : WLAN_REASON_MESH_CONFIG;
sta->reason = reason;
sta->plink_state = NL80211_PLINK_HOLDING;
sta->mesh->reason = reason;
sta->mesh->plink_state = NL80211_PLINK_HOLDING;
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;
u32 changed = 0;
del_timer(&sta->plink_timer);
sta->plink_state = NL80211_PLINK_ESTAB;
del_timer(&sta->mesh->plink_timer);
sta->mesh->plink_state = NL80211_PLINK_ESTAB;
changed |= mesh_plink_inc_estab_count(sdata);
changed |= mesh_set_ht_prot_mode(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;
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);
switch (sta->plink_state) {
spin_lock_bh(&sta->mesh->plink_lock);
switch (sta->mesh->plink_state) {
case NL80211_PLINK_LISTEN:
switch (event) {
case CLS_ACPT:
mesh_plink_fsm_restart(sta);
break;
case OPN_ACPT:
sta->plink_state = NL80211_PLINK_OPN_RCVD;
sta->llid = mesh_get_new_llid(sdata);
sta->mesh->plink_state = NL80211_PLINK_OPN_RCVD;
sta->mesh->llid = mesh_get_new_llid(sdata);
mesh_plink_timer_set(sta,
mshcfg->dot11MeshRetryTimeout);
@ -791,11 +820,11 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
break;
case OPN_ACPT:
/* 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;
break;
case CNF_ACPT:
sta->plink_state = NL80211_PLINK_CNF_RCVD;
sta->mesh->plink_state = NL80211_PLINK_CNF_RCVD;
mod_plink_timer(sta, mshcfg->dot11MeshConfirmTimeout);
break;
default:
@ -855,7 +884,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
case NL80211_PLINK_HOLDING:
switch (event) {
case CLS_ACPT:
del_timer(&sta->plink_timer);
del_timer(&sta->mesh->plink_timer);
mesh_plink_fsm_restart(sta);
break;
case OPN_ACPT:
@ -874,17 +903,18 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
*/
break;
}
spin_unlock_bh(&sta->plink_lock);
spin_unlock_bh(&sta->mesh->plink_lock);
if (action) {
mesh_plink_frame_tx(sdata, action, sta->sta.addr,
sta->llid, sta->plid, sta->reason);
mesh_plink_frame_tx(sdata, sta, action, sta->sta.addr,
sta->mesh->llid, sta->mesh->plid,
sta->mesh->reason);
/* also send confirm in open case */
if (action == WLAN_SP_MESH_PEERING_OPEN) {
mesh_plink_frame_tx(sdata,
mesh_plink_frame_tx(sdata, sta,
WLAN_SP_MESH_PEERING_CONFIRM,
sta->sta.addr, sta->llid,
sta->plid, 0);
sta->sta.addr, sta->mesh->llid,
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");
goto out;
}
if (sta->plink_state == NL80211_PLINK_BLOCKED)
if (sta->mesh->plink_state == NL80211_PLINK_BLOCKED)
goto out;
}
@ -954,7 +984,7 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
if (!matches_local)
event = OPN_RJCT;
if (!mesh_plink_free_count(sdata) ||
(sta->plid && sta->plid != plid))
(sta->mesh->plid && sta->mesh->plid != plid))
event = OPN_IGNR;
else
event = OPN_ACPT;
@ -963,14 +993,14 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
if (!matches_local)
event = CNF_RJCT;
if (!mesh_plink_free_count(sdata) ||
sta->llid != llid ||
(sta->plid && sta->plid != plid))
sta->mesh->llid != llid ||
(sta->mesh->plid && sta->mesh->plid != plid))
event = CNF_IGNR;
else
event = CNF_ACPT;
break;
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
* follow the standard but since multiple plinks
* per sta are not supported, it is necessary in
@ -981,9 +1011,9 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
* restarted.
*/
event = CLS_ACPT;
else if (sta->plid != plid)
else if (sta->mesh->plid != plid)
event = CLS_IGNR;
else if (ie_len == 8 && sta->llid != llid)
else if (ie_len == 8 && sta->mesh->llid != llid)
event = CLS_IGNR;
else
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");
goto unlock_rcu;
}
sta->plid = plid;
sta->mesh->plid = plid;
} 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,
WLAN_REASON_MESH_CONFIG);
goto unlock_rcu;
@ -1081,9 +1111,13 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
goto unlock_rcu;
}
/* 802.11-2012 13.3.7.2 - update plid on CNF if not set */
if (!sta->plid && event == CNF_ACPT)
sta->plid = plid;
if (event == CNF_ACPT) {
/* 802.11-2012 13.3.7.2 - update plid on CNF if not set */
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);

View File

@ -92,16 +92,16 @@ u32 ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata)
if (sdata != sta->sdata)
continue;
switch (sta->plink_state) {
switch (sta->mesh->plink_state) {
case NL80211_PLINK_OPN_SNT:
case NL80211_PLINK_OPN_RCVD:
case NL80211_PLINK_CNF_RCVD:
peering = true;
break;
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++;
else if (sta->local_pm == NL80211_MESH_POWER_DEEP_SLEEP)
else if (sta->mesh->local_pm == NL80211_MESH_POWER_DEEP_SLEEP)
deep_sleep_cnt++;
break;
default:
@ -153,19 +153,19 @@ u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
if (sta->local_pm == pm)
if (sta->mesh->local_pm == pm)
return 0;
mps_dbg(sdata, "local STA operates in mode %d with %pM\n",
pm, sta->sta.addr);
sta->local_pm = pm;
sta->mesh->local_pm = pm;
/*
* announce peer-specific power mode transition
* (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);
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) &&
ieee80211_is_data_qos(hdr->frame_control) &&
sta->plink_state == NL80211_PLINK_ESTAB)
pm = sta->local_pm;
sta->mesh->plink_state == NL80211_PLINK_ESTAB)
pm = sta->mesh->local_pm;
else
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
* peer's power mode is known
*/
if (sta->plink_state == NL80211_PLINK_ESTAB &&
sta->peer_pm != NL80211_MESH_POWER_UNKNOWN)
pm = sta->peer_pm;
if (sta->mesh->plink_state == NL80211_PLINK_ESTAB &&
sta->mesh->peer_pm != NL80211_MESH_POWER_UNKNOWN)
pm = sta->mesh->peer_pm;
else
pm = sta->nonpeer_pm;
pm = sta->mesh->nonpeer_pm;
do_buffer = (pm != NL80211_MESH_POWER_ACTIVE);
/* 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_RECIPIENT);
} else if (!do_buffer) {
@ -296,13 +296,13 @@ static void mps_set_sta_peer_pm(struct sta_info *sta,
pm = NL80211_MESH_POWER_ACTIVE;
}
if (sta->peer_pm == pm)
if (sta->mesh->peer_pm == pm)
return;
mps_dbg(sta->sdata, "STA %pM enters mode %d\n",
sta->sta.addr, pm);
sta->peer_pm = pm;
sta->mesh->peer_pm = pm;
ieee80211_mps_sta_status_update(sta);
}
@ -317,13 +317,13 @@ static void mps_set_sta_nonpeer_pm(struct sta_info *sta,
else
pm = NL80211_MESH_POWER_ACTIVE;
if (sta->nonpeer_pm == pm)
if (sta->mesh->nonpeer_pm == pm)
return;
mps_dbg(sta->sdata, "STA %pM sets non-peer mode to %d\n",
sta->sta.addr, pm);
sta->nonpeer_pm = pm;
sta->mesh->nonpeer_pm = pm;
ieee80211_mps_sta_status_update(sta);
}
@ -552,7 +552,7 @@ void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,
} else {
if (eosp)
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);
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;
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,
sta->llid);
sta->mesh->aid);
if (has_buffered)
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)
return;
if (sta->plink_state == NL80211_PLINK_ESTAB)
if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
mpsp_trigger_send(sta, has_buffered, !buffer_local);
else
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) */
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)) {
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,
"STA %pM : sta->t_offset=%lld, sta->t_offset_setpoint=%lld, t_clockdrift=%lld\n",
sta->sta.addr, (long long) sta->t_offset,
(long long) sta->t_offset_setpoint,
"STA %pM : t_offset=%lld, t_offset_setpoint=%lld, t_clockdrift=%lld\n",
sta->sta.addr, (long long) sta->mesh->t_offset,
(long long) sta->mesh->t_offset_setpoint,
(long long) t_clockdrift);
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;
spin_unlock_bh(&ifmsh->sync_offset_lock);
} 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);
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,
(long long) sta->t_offset);
(long long) sta->mesh->t_offset);
}
no_sync:

View File

@ -6,6 +6,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* 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
* 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);
}
/* 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,
struct sk_buff *skb,
struct ieee80211_supported_band *sband,
struct ieee80211_vht_cap *ap_vht_cap)
{
struct ieee80211_local *local = sdata->local;
u8 *pos;
u32 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 &
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;
@ -1096,24 +1129,6 @@ static void ieee80211_chswitch_timer(unsigned long data)
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
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
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->vht_capa, 0, sizeof(ifmgd->vht_capa));
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;
@ -2538,6 +2554,7 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
eth_zero_addr(sdata->u.mgd.bssid);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
sdata->u.mgd.flags = 0;
sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER;
mutex_lock(&sdata->local->mtx);
ieee80211_vif_release_channel(sdata);
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);
if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) {
if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
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;

View File

@ -179,7 +179,7 @@ int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
u32 changed = BSS_CHANGED_OCB;
u32 changed = BSS_CHANGED_OCB | BSS_CHANGED_BSSID;
int err;
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,
"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)
{
struct rate_control_alg *alg;
@ -294,39 +353,37 @@ bool rate_control_send_low(struct ieee80211_sta *pubsta,
}
EXPORT_SYMBOL(rate_control_send_low);
static bool rate_idx_match_legacy_mask(struct ieee80211_tx_rate *rate,
int n_bitrates, u32 mask)
static bool rate_idx_match_legacy_mask(s8 *rate_idx, int n_bitrates, u32 mask)
{
int j;
/* 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)) {
/* Okay, found a suitable rate. Use it. */
rate->idx = j;
*rate_idx = j;
return true;
}
}
/* 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)) {
/* Okay, found a suitable rate. Use it. */
rate->idx = j;
*rate_idx = j;
return true;
}
}
return false;
}
static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate,
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
static bool rate_idx_match_mcs_mask(s8 *rate_idx, u8 *mcs_mask)
{
int i, j;
int ridx, rbit;
ridx = rate->idx / 8;
rbit = rate->idx % 8;
ridx = *rate_idx / 8;
rbit = *rate_idx % 8;
/* sanity check */
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 (j = rbit; j >= 0; j--)
if (mcs_mask[i] & BIT(j)) {
rate->idx = i * 8 + j;
*rate_idx = i * 8 + j;
return true;
}
rbit = 7;
}
/* Try to find a higher rate that would be allowed */
ridx = (rate->idx + 1) / 8;
rbit = (rate->idx + 1) % 8;
ridx = (*rate_idx + 1) / 8;
rbit = (*rate_idx + 1) % 8;
for (i = ridx; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
for (j = rbit; j < 8; j++)
if (mcs_mask[i] & BIT(j)) {
rate->idx = i * 8 + j;
*rate_idx = i * 8 + j;
return true;
}
rbit = 0;
@ -357,37 +414,93 @@ static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate,
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,
enum nl80211_chan_width chan_width,
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 */
if (rate->flags & IEEE80211_TX_RC_MCS) {
if (rate_idx_match_mcs_mask(rate, mcs_mask))
*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);
*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;
/* also try the legacy rates. */
alt_rate.idx = 0;
/* keep protection flags */
alt_rate.flags = rate->flags &
(IEEE80211_TX_RC_USE_RTS_CTS |
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;
*rate_flags &= ~(IEEE80211_TX_RC_MCS |
IEEE80211_TX_RC_40_MHZ_WIDTH);
if (rate_idx_match_legacy_mask(rate_idx, sband->n_bitrates,
mask))
return;
}
} else if (!(rate->flags & IEEE80211_TX_RC_VHT_MCS)) {
} else if (*rate_flags & IEEE80211_TX_RC_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 */
if (rate_idx_match_legacy_mask(rate, sband->n_bitrates, mask))
if (rate_idx_match_legacy_mask(rate_idx, sband->n_bitrates,
mask))
return;
/* 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;
}
alt_rate.idx = 0;
*rate_idx = 0;
/* keep protection flags */
alt_rate.flags = rate->flags &
(IEEE80211_TX_RC_USE_RTS_CTS |
IEEE80211_TX_RC_USE_CTS_PROTECT |
IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
alt_rate.count = rate->count;
*rate_flags &= (IEEE80211_TX_RC_USE_RTS_CTS |
IEEE80211_TX_RC_USE_CTS_PROTECT |
IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
alt_rate.flags |= IEEE80211_TX_RC_MCS;
*rate_flags |= IEEE80211_TX_RC_MCS;
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)) {
*rate = alt_rate;
if (rate_idx_match_mcs_mask(rate_idx, mcs_mask))
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,
struct ieee80211_sta *sta,
struct ieee80211_supported_band *sband,
struct ieee80211_tx_info *info,
struct ieee80211_tx_rate *rates,
int max_rates)
{
enum nl80211_chan_width chan_width;
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
bool has_mcs_mask;
u32 mask;
u32 rate_flags;
u16 rate_flags, vht_mask[NL80211_VHT_NSS_MAX];
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
* the common case.
*/
mask = sdata->rc_rateidx_mask[info->band];
has_mcs_mask = sdata->rc_has_mcs_mask[info->band];
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)
if (!rate_control_cap_mask(sdata, sband, sta, &mask, mcs_mask,
vht_mask))
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
* 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)
break;
rate_idx_match_mask(&rates[i], sband, chan_width, mask,
mcs_mask);
rate_flags = rates[i].flags;
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];
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)
__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 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
* 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);
}
static inline 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);
}
static inline void rate_control_rate_update(struct ieee80211_local *local,
void rate_control_rate_init(struct sta_info *sta);
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);
}
struct sta_info *sta, u32 changed);
static inline void *rate_control_alloc_sta(struct rate_control_ref *ref,
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
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;
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);
}
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
*
@ -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);
if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
RX_FLAG_FAILED_PLCP_CRC |
RX_FLAG_AMPDU_IS_ZEROLEN))
RX_FLAG_FAILED_PLCP_CRC))
return true;
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);
put_unaligned_le32(status->ampdu_reference, pos);
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)
flags |= IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN;
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;
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;
* 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);
}
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;
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 */
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))
return -EINVAL;
if (start)
sta_ps_start(sta_inf);
sta_ps_start(sta);
else
sta_ps_end(sta_inf);
sta_ps_end(sta);
return 0;
}
@ -1393,7 +1428,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
sta->rx_bytes += rx->skb->len;
if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
sta->last_signal = status->signal;
ewma_add(&sta->avg_signal, -status->signal);
ewma_signal_add(&sta->avg_signal, -status->signal);
}
if (status->chains) {
@ -1405,7 +1440,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
continue;
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))
return RX_DROP_MONITOR;
rx->key->tx_rx_count++;
/* TODO: add threshold stuff again */
} else {
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 */
status = IEEE80211_SKB_RXCB(rx->skb);
status->rx_flags |= IEEE80211_RX_FRAGMENTED;
out:
ieee80211_led_rx(rx->local);
@ -2108,9 +2141,8 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
/* deliver to local stack */
skb->protocol = eth_type_trans(skb, dev);
memset(skb->cb, 0, sizeof(skb->cb));
if (!(rx->flags & IEEE80211_RX_REORDER_TIMER) &&
rx->local->napi)
napi_gro_receive(rx->local->napi, skb);
if (rx->napi)
napi_gro_receive(rx->napi, skb);
else
netif_receive_skb(skb);
}
@ -2378,9 +2410,8 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
tf->category == WLAN_CATEGORY_TDLS &&
(tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TDLS_CHSW;
skb_queue_tail(&sdata->skb_queue, rx->skb);
ieee80211_queue_work(&rx->local->hw, &sdata->work);
skb_queue_tail(&local->skb_queue_tdls_chsw, rx->skb);
schedule_work(&local->tdls_chsw_work);
if (rx->sta)
rx->sta->rx_packets++;
@ -3004,7 +3035,6 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
return RX_QUEUED;
}
/* TODO: use IEEE80211_RX_FRAGMENTED */
static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
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 */
.security_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;
@ -3286,7 +3316,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
case NL80211_IFTYPE_OCB:
if (!bssid)
return false;
if (ieee80211_is_beacon(hdr->frame_control))
if (!ieee80211_is_data_present(hdr->frame_control))
return false;
if (!is_broadcast_ether_addr(bssid))
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.
*/
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_sub_if_data *sdata;
@ -3409,6 +3440,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
memset(&rx, 0, sizeof(rx));
rx.skb = skb;
rx.local = local;
rx.napi = napi;
if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))
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
* 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_rate *rate = NULL;
@ -3609,7 +3642,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
ieee80211_tpt_led_trig_rx(local,
((struct ieee80211_hdr *)skb->data)->frame_control,
skb->len);
__ieee80211_rx_handle_packet(hw, skb);
__ieee80211_rx_handle_packet(hw, skb, napi);
rcu_read_unlock();
@ -3617,7 +3650,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
drop:
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
* 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 */
.automatic_shrinking = true,
.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,
.hashfn = sta_addr_hash,
.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])
kfree(to_txq_info(sta->sta.txq[0]));
kfree(rcu_dereference_raw(sta->sta.rates));
#ifdef CONFIG_MAC80211_MESH
kfree(sta->mesh);
#endif
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);
mutex_init(&sta->ampdu_mlme.mtx);
#ifdef CONFIG_MAC80211_MESH
spin_lock_init(&sta->plink_lock);
if (ieee80211_vif_is_mesh(&sdata->vif) &&
!sdata->u.mesh.user_mpm)
init_timer(&sta->plink_timer);
sta->nonpeer_pm = NL80211_MESH_POWER_ACTIVE;
if (ieee80211_vif_is_mesh(&sdata->vif)) {
sta->mesh = kzalloc(sizeof(*sta->mesh), gfp);
if (!sta->mesh)
goto free;
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
memcpy(sta->addr, addr, ETH_ALEN);
memcpy(sta->sta.addr, addr, ETH_ALEN);
sta->local = local;
sta->sdata = sdata;
@ -332,9 +341,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
ktime_get_ts(&uptime);
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++)
ewma_init(&sta->chain_signal_avg[i], 1024, 8);
ewma_signal_init(&sta->chain_signal_avg[i]);
if (local->ops->wake_tx_queue) {
void *txq_data;
@ -405,6 +414,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
if (sta->sta.txq[0])
kfree(to_txq_info(sta->sta.txq[0]));
free:
#ifdef CONFIG_MAC80211_MESH
kfree(sta->mesh);
#endif
kfree(sta);
return NULL;
}
@ -623,7 +635,7 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending)
bool indicate_tim = false;
u8 ignore_for_tim = sta->sta.uapsd_queues;
int ac;
u16 id;
u16 id = sta->sta.aid;
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
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;
ps = &sta->sdata->bss->ps;
id = sta->sta.aid;
#ifdef CONFIG_MAC80211_MESH
} else if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
ps = &sta->sdata->u.mesh.ps;
/* TIM map only for 1 <= PLID <= IEEE80211_MAX_AID */
id = sta->plid % (IEEE80211_MAX_AID + 1);
#endif
} else {
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))) {
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);
}
}
@ -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++) {
sinfo->chain_signal[i] = sta->chain_signal_last[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_NONPEER_PM);
sinfo->llid = sta->llid;
sinfo->plid = sta->plid;
sinfo->plink_state = sta->plink_state;
sinfo->llid = sta->mesh->llid;
sinfo->plid = sta->mesh->plid;
sinfo->plink_state = sta->mesh->plink_state;
if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
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->peer_pm = sta->peer_pm;
sinfo->nonpeer_pm = sta->nonpeer_pm;
sinfo->local_pm = sta->mesh->local_pm;
sinfo->peer_pm = sta->mesh->peer_pm;
sinfo->nonpeer_pm = sta->mesh->nonpeer_pm;
#endif
}

View File

@ -53,6 +53,8 @@
* @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
* 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
* keeping station in power-save mode, reply when the driver
* unblocks the station.
@ -84,6 +86,7 @@ enum ieee80211_sta_info_flags {
WLAN_STA_TDLS_INITIATOR,
WLAN_STA_TDLS_CHAN_SWITCH,
WLAN_STA_TDLS_OFF_CHANNEL,
WLAN_STA_TDLS_WIDER_BW,
WLAN_STA_UAPSD,
WLAN_STA_SP,
WLAN_STA_4ADDR_EVENT,
@ -269,6 +272,56 @@ struct ieee80211_fast_tx {
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
*
@ -278,12 +331,13 @@ struct ieee80211_fast_tx {
* @list: global linked list entry
* @free_list: list entry for keeping track of stations to free
* @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
* @sdata: virtual interface this station belongs to
* @ptk: peer keys negotiated with this station, if any
* @ptk_idx: last installed peer key index
* @gtk: group keys negotiated with this station, if any
* @gtk_idx: last installed group key index
* @rate_ctrl: rate control algorithm reference
* @rate_ctrl_lock: spinlock used to protect rate control data
* (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
* @avg_signal: moving average of signal of received frames from this STA
* @last_ack_signal: signal of last received Ack frame from this STA
* @last_seq_ctrl: last received seq/frag number from this STA (per RX queue)
* @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_retry_failed: number of frames that failed retry
* @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_bytes: number of bytes transmitted to this STA
* @tid_seq: per-TID sequence numbers for sending to this STA
* @ampdu_mlme: A-MPDU state machine state
* @timer_to_tid: identity mapping to ID timers
* @plink_lock: serialize access to plink fields
* @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
* @mesh: mesh STA information
* @debugfs: debug filesystem info
* @dead: set to true when sta is unlinked
* @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
* entry for non-QoS frames
* @fast_tx: TX fastpath information
* @processed_beacon: set to true after peer rates and capabilities are
* processed
* @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to
* the BSS one.
*/
struct sta_info {
/* General information, mostly static */
struct list_head list, free_list;
struct rcu_head rcu_head;
struct rhash_head hash_node;
u8 addr[ETH_ALEN];
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
struct ieee80211_key __rcu *ptk[NUM_DEFAULT_KEYS];
u8 gtk_idx;
u8 ptk_idx;
struct rate_control_ref *rate_ctrl;
void *rate_ctrl_priv;
@ -390,6 +431,10 @@ struct sta_info {
struct ieee80211_fast_tx __rcu *fast_tx;
#ifdef CONFIG_MAC80211_MESH
struct mesh_sta *mesh;
#endif
struct work_struct drv_deliver_wk;
u16 listen_interval;
@ -419,12 +464,12 @@ struct sta_info {
unsigned long rx_fragments;
unsigned long rx_dropped;
int last_signal;
struct ewma avg_signal;
struct ewma_signal avg_signal;
int last_ack_signal;
u8 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 */
__le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];
@ -432,8 +477,6 @@ struct sta_info {
/* Updated from TX status path only, no locking requirements */
unsigned long tx_filtered_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 */
u64 tx_packets[IEEE80211_NUM_ACS];
@ -455,29 +498,6 @@ struct sta_info {
struct sta_ampdu_mlme ampdu_mlme;
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
struct sta_info_debugfsdentries {
struct dentry *dir;
@ -498,6 +518,8 @@ struct sta_info {
u8 reserved_tid;
struct cfg80211_chan_def tdls_chandef;
/* keep last! */
struct ieee80211_sta sta;
};
@ -505,7 +527,7 @@ struct sta_info {
static inline enum nl80211_plink_state sta_plink_state(struct sta_info *sta)
{
#ifdef CONFIG_MAC80211_MESH
return sta->plink_state;
return sta->mesh->plink_state;
#endif
return NL80211_PLINK_LISTEN;
}
@ -608,7 +630,7 @@ u32 sta_addr_hash(const void *key, u32 length, u32 seed);
_sta_bucket_idx(tbl, _addr), \
hash_node) \
/* 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!

View File

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

View File

@ -4,6 +4,7 @@
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2014, Intel Corporation
* Copyright 2014 Intel Mobile Communications GmbH
* Copyright 2015 Intel Deutschland GmbH
*
* This file is GPLv2 as found in COPYING.
*/
@ -11,6 +12,7 @@
#include <linux/ieee80211.h>
#include <linux/log2.h>
#include <net/cfg80211.h>
#include <linux/rtnetlink.h>
#include "ieee80211_i.h"
#include "driver-ops.h"
@ -35,20 +37,28 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk)
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)
{
u8 *pos = (void *)skb_put(skb, 7);
struct ieee80211_local *local = sdata->local;
bool chan_switch = local->hw.wiphy->features &
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++ = 5; /* len */
*pos++ = 8; /* len */
*pos++ = 0x0;
*pos++ = 0x0;
*pos++ = 0x0;
*pos++ = chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0;
*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
*pos++ = 0;
*pos++ = 0;
*pos++ = (vht && wider_band) ? WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED : 0;
}
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
ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
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;
}
ieee80211_tdls_add_ext_capab(local, skb);
ieee80211_tdls_add_ext_capab(sdata, skb);
/* add the QoS element if we support it */
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;
}
rcu_read_lock();
mutex_lock(&local->sta_mtx);
/* we should have the peer STA if we're already responding */
if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
sta = sta_info_get(sdata, peer);
if (WARN_ON_ONCE(!sta)) {
rcu_read_unlock();
mutex_unlock(&local->sta_mtx);
return;
}
sta->tdls_chandef = sdata->vif.bss_conf.chandef;
}
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);
} else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
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 */
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);
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 */
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);
u8 *pos;
rcu_read_lock();
mutex_lock(&local->sta_mtx);
sta = sta_info_get(sdata, peer);
ap_sta = sta_info_get(sdata, ifmgd->bssid);
if (WARN_ON_ONCE(!sta || !ap_sta)) {
rcu_read_unlock();
mutex_unlock(&local->sta_mtx);
return;
}
sta->tdls_chandef = sdata->vif.bss_conf.chandef;
/* add any custom IEs that go before the QoS IE */
if (extra_ies_len) {
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 */
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));
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 */
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),
sizeof(struct ieee80211_tdls_data)) +
50 + /* supported rates */
7 + /* ext capab */
10 + /* ext capab */
26 + /* max(WMM-info, WMM-param) */
2 + max(sizeof(struct ieee80211_ht_cap),
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_local *local = sdata->local;
enum ieee80211_smps_mode smps_mode = sdata->u.mgd.driver_smps_mode;
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);
/* 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;
}
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,
const u8 *peer, enum nl80211_tdls_operation oper)
{
@ -1182,6 +1285,8 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
break;
}
iee80211_tdls_recalc_chanctx(sdata);
rcu_read_lock();
sta = sta_info_get(sdata, peer);
if (!sta) {
@ -1213,6 +1318,7 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
ieee80211_flush_queues(local, sdata, false);
ret = sta_info_destroy_addr(sdata, peer);
iee80211_tdls_recalc_chanctx(sdata);
break;
default:
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);
}
if (ret == 0)
ieee80211_queue_work(&sdata->local->hw,
&sdata->u.mgd.request_smps_work);
mutex_unlock(&local->mtx);
return ret;
}
@ -1627,6 +1737,31 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
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);
sta = sta_info_get(sdata, tf->sa);
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;
}
if (!sta->sta.ht_cap.ht_supported) {
chan_type = NL80211_CHAN_NO_HT;
} else 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;
}
/* peer should have known better */
if (!sta->sta.ht_cap.ht_supported && elems.sec_chan_offs &&
elems.sec_chan_offs->sec_chan_offs) {
tdls_dbg(sdata, "TDLS chan switch - wide chan unsupported\n");
ret = -ENOTSUPP;
goto out;
}
cfg80211_chandef_create(&chandef, chan, chan_type);
params.chandef = &chandef;
params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time);
params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout);
@ -1691,12 +1814,15 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
return ret;
}
void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
static void
ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
struct ieee80211_tdls_data *tf = (void *)skb->data;
struct wiphy *wiphy = sdata->local->hw.wiphy;
ASSERT_RTNL();
/* make sure the driver supports it */
if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
return;
@ -1720,3 +1846,47 @@ void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
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)
return TX_CONTINUE;
if (tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
return TX_CONTINUE;
if (tx->flags & IEEE80211_TX_PS_BUFFERED)
return TX_CONTINUE;
@ -610,7 +607,6 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
if (tx->key) {
bool skip_hw = false;
tx->key->tx_rx_count++;
/* TODO: add threshold stuff again */
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 ||
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 */
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;
}
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)];
@ -3213,6 +3214,16 @@ static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata,
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)
{
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)
goto unlock;
beacon->csa_current_counter--;
/* the counter should never reach 0 */
WARN_ON_ONCE(!beacon->csa_current_counter);
count = beacon->csa_current_counter;
count = __ieee80211_csa_update_counter(beacon);
unlock:
rcu_read_unlock();
@ -3335,7 +3342,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
if (beacon) {
if (beacon->csa_counter_offsets[0]) {
if (!is_template)
ieee80211_csa_update_counter(vif);
__ieee80211_csa_update_counter(beacon);
ieee80211_set_csa(sdata, beacon);
}
@ -3381,7 +3388,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
if (beacon->csa_counter_offsets[0]) {
if (!is_template)
ieee80211_csa_update_counter(vif);
__ieee80211_csa_update_counter(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
* mac80211's behavior.
*/
ieee80211_csa_update_counter(vif);
__ieee80211_csa_update_counter(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);
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)
{
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 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) ||
!(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
@ -1709,6 +1665,7 @@ static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
local->resuming = false;
local->suspended = false;
local->started = false;
local->in_reconfig = false;
/* scheduled scan clearly can't be running any more, but tell
* 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 cfg80211_sched_scan_request *sched_scan_req;
bool sched_scan_stopped = false;
bool suspended = local->suspended;
/* nothing to do if HW shouldn't run */
if (!local->open_count)
goto wake_up;
#ifdef CONFIG_PM
if (local->suspended)
if (suspended)
local->resuming = true;
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);
local->wowlan = false;
if (res < 0) {
@ -1781,8 +1746,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
/*
* res is 1, which means the driver requested
* to go through a regular reset on wakeup.
* restore local->suspended in this case.
*/
reconfig_due_to_wowlan = true;
local->suspended = true;
}
#endif
@ -1794,7 +1761,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
*/
res = drv_start(local);
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");
else
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.
* 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);
if (!local->suspended)
if (!suspended)
return 0;
#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;
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 = min(bw, ieee80211_sta_cap_rx_bw(sta));
bw = ieee80211_sta_cap_rx_bw(sta);
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;
}
@ -422,3 +426,29 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
if (changed > 0)
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;
}
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) {
key->u.ccmp.replays++;
return RX_DROP_UNUSABLE;
}
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))
if (memcmp(pn, key->u.ccmp.rx_pn[queue],
IEEE80211_CCMP_PN_LEN) <= 0) {
key->u.ccmp.replays++;
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 */
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;
}
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) {
key->u.gcmp.replays++;
return RX_DROP_UNUSABLE;
}
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))
if (memcmp(pn, key->u.gcmp.rx_pn[queue],
IEEE80211_GCMP_PN_LEN) <= 0) {
key->u.gcmp.replays++;
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 */
if (pskb_trim(skb, skb->len - IEEE80211_GCMP_MIC_LEN))

View File

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

View File

@ -407,6 +407,9 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
INIT_LIST_HEAD(&rdev->bss_list);
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
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,
cfg80211_dfs_channels_update_work);
#ifdef CONFIG_CFG80211_WEXT
@ -802,6 +805,7 @@ void wiphy_unregister(struct wiphy *wiphy)
cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
flush_work(&rdev->destroy_work);
flush_work(&rdev->sched_scan_stop_wk);
flush_work(&rdev->mlme_unreg_wk);
#ifdef CONFIG_PM
if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
@ -855,6 +859,7 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
switch (wdev->iftype) {
case NL80211_IFTYPE_P2P_DEVICE:
cfg80211_mlme_purge_registrations(wdev);
cfg80211_stop_p2p_device(rdev, wdev);
break;
default:

View File

@ -59,6 +59,10 @@ struct cfg80211_registered_device {
struct list_head beacon_registrations;
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 */
int num_running_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,
u16 frame_type, const u8 *match_data,
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_purge_registrations(struct wireless_dev *wdev);
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,

View File

@ -2,6 +2,7 @@
* cfg80211 MLME SAP interface
*
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2015 Intel Deutschland GmbH
*/
#include <linux/kernel.h>
@ -389,6 +390,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
struct cfg80211_mgmt_registration {
struct list_head list;
struct wireless_dev *wdev;
u32 nlportid;
@ -399,6 +401,46 @@ struct cfg80211_mgmt_registration {
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,
u16 frame_type, const u8 *match_data,
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->nlportid = snd_portid;
nreg->frame_type = cpu_to_le16(frame_type);
nreg->wdev = wdev;
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)
rdev_mgmt_frame_register(rdev, wdev, frame_type, true);
return 0;
out:
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)
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);
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);
@ -496,16 +542,15 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
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);
list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
list_del(&reg->list);
kfree(reg);
}
spin_lock(&rdev->mlme_unreg_lock);
list_splice_tail_init(&wdev->mgmt_registrations, &rdev->mlme_unreg);
spin_unlock(&rdev->mlme_unreg_lock);
spin_unlock_bh(&wdev->mgmt_registrations_lock);
cfg80211_process_mlme_unregistrations(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.rts_threshold = old_rts_threshold;
rdev->wiphy.coverage_class = old_coverage_class;
return result;
}
}
return 0;
@ -7390,7 +7391,8 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
int err;
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;
if (!rdev->ops->set_mcast_rate)

View File

@ -733,6 +733,8 @@ static inline void
rdev_mgmt_frame_register(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, u16 frame_type, bool reg)
{
might_sleep();
trace_rdev_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);

View File

@ -1004,7 +1004,7 @@ static u32 map_regdom_flags(u32 rd_flags)
static const struct ieee80211_reg_rule *
freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
const struct ieee80211_regdomain *regd)
const struct ieee80211_regdomain *regd, u32 bw)
{
int i;
bool band_rule_found = false;
@ -1028,7 +1028,7 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
if (!band_rule_found)
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)
return rr;
@ -1040,14 +1040,26 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
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,
u32 center_freq)
{
const struct ieee80211_regdomain *regd;
regd = reg_get_regdomain(wiphy);
return freq_reg_info_regd(wiphy, center_freq, regd);
return __freq_reg_info(wiphy, center_freq, MHZ_TO_KHZ(20));
}
EXPORT_SYMBOL(freq_reg_info);
@ -1176,8 +1188,20 @@ static void handle_channel(struct wiphy *wiphy,
if (reg_rule->flags & NL80211_RRF_AUTO_BW)
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))
bw_flags = IEEE80211_CHAN_NO_HT40;
bw_flags |= IEEE80211_CHAN_NO_HT40;
if (max_bandwidth_khz < MHZ_TO_KHZ(80))
bw_flags |= IEEE80211_CHAN_NO_80MHZ;
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_freq_range *freq_range = NULL;
u32 max_bandwidth_khz;
u32 bw;
reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),
regd);
for (bw = MHZ_TO_KHZ(20); bw >= MHZ_TO_KHZ(5); bw = bw / 2) {
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)) {
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)
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))
bw_flags = IEEE80211_CHAN_NO_HT40;
bw_flags |= IEEE80211_CHAN_NO_HT40;
if (max_bandwidth_khz < MHZ_TO_KHZ(80))
bw_flags |= IEEE80211_CHAN_NO_80MHZ;
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);
return;
case NL80211_REGDOM_SET_BY_USER:
treatment = reg_process_hint_user(reg_request);
if (treatment == REG_REQ_IGNORE ||
treatment == REG_REQ_ALREADY_SET)
return;
reg_process_hint_user(reg_request);
return;
case NL80211_REGDOM_SET_BY_DRIVER:
if (!wiphy)
@ -2099,7 +2138,9 @@ static void reg_process_hint(struct regulatory_request *reg_request)
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 &&
wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
wiphy_update_regulatory(wiphy, reg_request->initiator);