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:
commit
2bd736fa0d
|
@ -669,6 +669,8 @@ struct iwl_priv {
|
||||||
/* ieee device used by generic ieee processing code */
|
/* ieee device used by generic ieee processing code */
|
||||||
struct ieee80211_hw *hw;
|
struct ieee80211_hw *hw;
|
||||||
|
|
||||||
|
struct napi_struct *napi;
|
||||||
|
|
||||||
struct list_head calib_results;
|
struct list_head calib_results;
|
||||||
|
|
||||||
struct workqueue_struct *workqueue;
|
struct workqueue_struct *workqueue;
|
||||||
|
|
|
@ -2037,7 +2037,8 @@ static void iwl_napi_add(struct iwl_op_mode *op_mode,
|
||||||
{
|
{
|
||||||
struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
|
struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
|
||||||
|
|
||||||
ieee80211_napi_add(priv->hw, napi, napi_dev, poll, weight);
|
netif_napi_add(napi_dev, napi, poll, weight);
|
||||||
|
priv->napi = napi;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct iwl_op_mode_ops iwl_dvm_ops = {
|
static const struct iwl_op_mode_ops iwl_dvm_ops = {
|
||||||
|
|
|
@ -786,7 +786,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,
|
||||||
|
|
||||||
memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
|
memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
|
||||||
|
|
||||||
ieee80211_rx(priv->hw, skb);
|
ieee80211_rx_napi(priv->hw, skb, priv->napi);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)
|
static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)
|
||||||
|
|
|
@ -366,8 +366,8 @@ struct iwl_mvm_rm_sta_cmd {
|
||||||
* ( MGMT_MCAST_KEY = 0x1f )
|
* ( MGMT_MCAST_KEY = 0x1f )
|
||||||
* @ctrl_flags: %iwl_sta_key_flag
|
* @ctrl_flags: %iwl_sta_key_flag
|
||||||
* @IGTK:
|
* @IGTK:
|
||||||
* @K1: IGTK master key
|
* @K1: unused
|
||||||
* @K2: IGTK sub key
|
* @K2: unused
|
||||||
* @sta_id: station ID that support IGTK
|
* @sta_id: station ID that support IGTK
|
||||||
* @key_id:
|
* @key_id:
|
||||||
* @receive_seq_cnt: initial RSC/PN needed for replay check
|
* @receive_seq_cnt: initial RSC/PN needed for replay check
|
||||||
|
|
|
@ -559,6 +559,7 @@ struct iwl_mvm {
|
||||||
const struct iwl_cfg *cfg;
|
const struct iwl_cfg *cfg;
|
||||||
struct iwl_phy_db *phy_db;
|
struct iwl_phy_db *phy_db;
|
||||||
struct ieee80211_hw *hw;
|
struct ieee80211_hw *hw;
|
||||||
|
struct napi_struct *napi;
|
||||||
|
|
||||||
/* for protecting access to iwl_mvm */
|
/* for protecting access to iwl_mvm */
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
|
|
|
@ -1316,7 +1316,8 @@ static void iwl_mvm_napi_add(struct iwl_op_mode *op_mode,
|
||||||
{
|
{
|
||||||
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
|
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
|
||||||
|
|
||||||
ieee80211_napi_add(mvm->hw, napi, napi_dev, poll, weight);
|
netif_napi_add(napi_dev, napi, poll, weight);
|
||||||
|
mvm->napi = napi;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct iwl_op_mode_ops iwl_mvm_ops = {
|
static const struct iwl_op_mode_ops iwl_mvm_ops = {
|
||||||
|
|
|
@ -129,7 +129,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
|
||||||
fraglen, rxb->truesize);
|
fraglen, rxb->truesize);
|
||||||
}
|
}
|
||||||
|
|
||||||
ieee80211_rx(mvm->hw, skb);
|
ieee80211_rx_napi(mvm->hw, skb, mvm->napi);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1277,8 +1277,6 @@ static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm,
|
||||||
const u8 *pn;
|
const u8 *pn;
|
||||||
|
|
||||||
memcpy(igtk_cmd.IGTK, keyconf->key, keyconf->keylen);
|
memcpy(igtk_cmd.IGTK, keyconf->key, keyconf->keylen);
|
||||||
ieee80211_aes_cmac_calculate_k1_k2(keyconf,
|
|
||||||
igtk_cmd.K1, igtk_cmd.K2);
|
|
||||||
ieee80211_get_key_rx_seq(keyconf, 0, &seq);
|
ieee80211_get_key_rx_seq(keyconf, 0, &seq);
|
||||||
pn = seq.aes_cmac.pn;
|
pn = seq.aes_cmac.pn;
|
||||||
igtk_cmd.receive_seq_cnt = cpu_to_le64(((u64) pn[5] << 0) |
|
igtk_cmd.receive_seq_cnt = cpu_to_le64(((u64) pn[5] << 0) |
|
||||||
|
|
|
@ -2399,6 +2399,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||||
ieee80211_hw_set(hw, AMPDU_AGGREGATION);
|
ieee80211_hw_set(hw, AMPDU_AGGREGATION);
|
||||||
ieee80211_hw_set(hw, MFP_CAPABLE);
|
ieee80211_hw_set(hw, MFP_CAPABLE);
|
||||||
ieee80211_hw_set(hw, SIGNAL_DBM);
|
ieee80211_hw_set(hw, SIGNAL_DBM);
|
||||||
|
ieee80211_hw_set(hw, TDLS_WIDER_BW);
|
||||||
if (rctbl)
|
if (rctbl)
|
||||||
ieee80211_hw_set(hw, SUPPORTS_RC_TABLE);
|
ieee80211_hw_set(hw, SUPPORTS_RC_TABLE);
|
||||||
|
|
||||||
|
@ -3120,8 +3121,10 @@ static int hwsim_init_netlink(void)
|
||||||
goto failure;
|
goto failure;
|
||||||
|
|
||||||
rc = netlink_register_notifier(&hwsim_netlink_notifier);
|
rc = netlink_register_notifier(&hwsim_netlink_notifier);
|
||||||
if (rc)
|
if (rc) {
|
||||||
|
genl_unregister_family(&hwsim_genl_family);
|
||||||
goto failure;
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
@ -27,4 +27,43 @@ static inline unsigned long ewma_read(const struct ewma *avg)
|
||||||
return avg->internal >> avg->factor;
|
return avg->internal >> avg->factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define DECLARE_EWMA(name, _factor, _weight) \
|
||||||
|
struct ewma_##name { \
|
||||||
|
unsigned long internal; \
|
||||||
|
}; \
|
||||||
|
static inline void ewma_##name##_init(struct ewma_##name *e) \
|
||||||
|
{ \
|
||||||
|
BUILD_BUG_ON(!__builtin_constant_p(_factor)); \
|
||||||
|
BUILD_BUG_ON(!__builtin_constant_p(_weight)); \
|
||||||
|
BUILD_BUG_ON_NOT_POWER_OF_2(_factor); \
|
||||||
|
BUILD_BUG_ON_NOT_POWER_OF_2(_weight); \
|
||||||
|
e->internal = 0; \
|
||||||
|
} \
|
||||||
|
static inline unsigned long \
|
||||||
|
ewma_##name##_read(struct ewma_##name *e) \
|
||||||
|
{ \
|
||||||
|
BUILD_BUG_ON(!__builtin_constant_p(_factor)); \
|
||||||
|
BUILD_BUG_ON(!__builtin_constant_p(_weight)); \
|
||||||
|
BUILD_BUG_ON_NOT_POWER_OF_2(_factor); \
|
||||||
|
BUILD_BUG_ON_NOT_POWER_OF_2(_weight); \
|
||||||
|
return e->internal >> ilog2(_factor); \
|
||||||
|
} \
|
||||||
|
static inline void ewma_##name##_add(struct ewma_##name *e, \
|
||||||
|
unsigned long val) \
|
||||||
|
{ \
|
||||||
|
unsigned long internal = ACCESS_ONCE(e->internal); \
|
||||||
|
unsigned long weight = ilog2(_weight); \
|
||||||
|
unsigned long factor = ilog2(_factor); \
|
||||||
|
\
|
||||||
|
BUILD_BUG_ON(!__builtin_constant_p(_factor)); \
|
||||||
|
BUILD_BUG_ON(!__builtin_constant_p(_weight)); \
|
||||||
|
BUILD_BUG_ON_NOT_POWER_OF_2(_factor); \
|
||||||
|
BUILD_BUG_ON_NOT_POWER_OF_2(_weight); \
|
||||||
|
\
|
||||||
|
ACCESS_ONCE(e->internal) = internal ? \
|
||||||
|
(((internal << weight) - internal) + \
|
||||||
|
(val << factor)) >> weight : \
|
||||||
|
(val << factor); \
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* _LINUX_AVERAGE_H */
|
#endif /* _LINUX_AVERAGE_H */
|
||||||
|
|
|
@ -2074,8 +2074,8 @@ enum ieee80211_tdls_actioncode {
|
||||||
#define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6)
|
#define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6)
|
||||||
#define WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED BIT(7)
|
#define WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED BIT(7)
|
||||||
|
|
||||||
|
#define WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED BIT(5)
|
||||||
#define WLAN_EXT_CAPA8_OPMODE_NOTIF BIT(6)
|
#define WLAN_EXT_CAPA8_OPMODE_NOTIF BIT(6)
|
||||||
#define WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED BIT(7)
|
|
||||||
|
|
||||||
/* TDLS specific payload type in the LLC/SNAP header */
|
/* TDLS specific payload type in the LLC/SNAP header */
|
||||||
#define WLAN_TDLS_SNAP_RFTYPE 0x2
|
#define WLAN_TDLS_SNAP_RFTYPE 0x2
|
||||||
|
|
|
@ -2369,8 +2369,7 @@ struct cfg80211_qos_map {
|
||||||
* method returns 0.)
|
* method returns 0.)
|
||||||
*
|
*
|
||||||
* @mgmt_frame_register: Notify driver that a management frame type was
|
* @mgmt_frame_register: Notify driver that a management frame type was
|
||||||
* registered. Note that this callback may not sleep, and cannot run
|
* registered. The callback is allowed to sleep.
|
||||||
* concurrently with itself.
|
|
||||||
*
|
*
|
||||||
* @set_antenna: Set antenna configuration (tx_ant, rx_ant) on the device.
|
* @set_antenna: Set antenna configuration (tx_ant, rx_ant) on the device.
|
||||||
* Parameters are bitmaps of allowed antennas to use for TX/RX. Drivers may
|
* Parameters are bitmaps of allowed antennas to use for TX/RX. Drivers may
|
||||||
|
|
|
@ -973,6 +973,10 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
|
||||||
* @RX_FLAG_IV_STRIPPED: The IV/ICV are stripped from this frame.
|
* @RX_FLAG_IV_STRIPPED: The IV/ICV are stripped from this frame.
|
||||||
* If this flag is set, the stack cannot do any replay detection
|
* If this flag is set, the stack cannot do any replay detection
|
||||||
* hence the driver or hardware will have to do that.
|
* hence the driver or hardware will have to do that.
|
||||||
|
* @RX_FLAG_PN_VALIDATED: Currently only valid for CCMP/GCMP frames, this
|
||||||
|
* flag indicates that the PN was verified for replay protection.
|
||||||
|
* Note that this flag is also currently only supported when a frame
|
||||||
|
* is also decrypted (ie. @RX_FLAG_DECRYPTED must be set)
|
||||||
* @RX_FLAG_FAILED_FCS_CRC: Set this flag if the FCS check failed on
|
* @RX_FLAG_FAILED_FCS_CRC: Set this flag if the FCS check failed on
|
||||||
* the frame.
|
* the frame.
|
||||||
* @RX_FLAG_FAILED_PLCP_CRC: Set this flag if the PCLP check failed on
|
* @RX_FLAG_FAILED_PLCP_CRC: Set this flag if the PCLP check failed on
|
||||||
|
@ -997,9 +1001,6 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
|
||||||
* @RX_FLAG_AMPDU_DETAILS: A-MPDU details are known, in particular the reference
|
* @RX_FLAG_AMPDU_DETAILS: A-MPDU details are known, in particular the reference
|
||||||
* number (@ampdu_reference) must be populated and be a distinct number for
|
* number (@ampdu_reference) must be populated and be a distinct number for
|
||||||
* each A-MPDU
|
* each A-MPDU
|
||||||
* @RX_FLAG_AMPDU_REPORT_ZEROLEN: driver reports 0-length subframes
|
|
||||||
* @RX_FLAG_AMPDU_IS_ZEROLEN: This is a zero-length subframe, for
|
|
||||||
* monitoring purposes only
|
|
||||||
* @RX_FLAG_AMPDU_LAST_KNOWN: last subframe is known, should be set on all
|
* @RX_FLAG_AMPDU_LAST_KNOWN: last subframe is known, should be set on all
|
||||||
* subframes of a single A-MPDU
|
* subframes of a single A-MPDU
|
||||||
* @RX_FLAG_AMPDU_IS_LAST: this subframe is the last subframe of the A-MPDU
|
* @RX_FLAG_AMPDU_IS_LAST: this subframe is the last subframe of the A-MPDU
|
||||||
|
@ -1039,8 +1040,8 @@ enum mac80211_rx_flags {
|
||||||
RX_FLAG_NO_SIGNAL_VAL = BIT(12),
|
RX_FLAG_NO_SIGNAL_VAL = BIT(12),
|
||||||
RX_FLAG_HT_GF = BIT(13),
|
RX_FLAG_HT_GF = BIT(13),
|
||||||
RX_FLAG_AMPDU_DETAILS = BIT(14),
|
RX_FLAG_AMPDU_DETAILS = BIT(14),
|
||||||
RX_FLAG_AMPDU_REPORT_ZEROLEN = BIT(15),
|
RX_FLAG_PN_VALIDATED = BIT(15),
|
||||||
RX_FLAG_AMPDU_IS_ZEROLEN = BIT(16),
|
/* bit 16 free */
|
||||||
RX_FLAG_AMPDU_LAST_KNOWN = BIT(17),
|
RX_FLAG_AMPDU_LAST_KNOWN = BIT(17),
|
||||||
RX_FLAG_AMPDU_IS_LAST = BIT(18),
|
RX_FLAG_AMPDU_IS_LAST = BIT(18),
|
||||||
RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(19),
|
RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(19),
|
||||||
|
@ -1491,8 +1492,10 @@ enum ieee80211_key_flags {
|
||||||
* - Temporal Authenticator Rx MIC Key (64 bits)
|
* - Temporal Authenticator Rx MIC Key (64 bits)
|
||||||
* @icv_len: The ICV length for this key type
|
* @icv_len: The ICV length for this key type
|
||||||
* @iv_len: The IV length for this key type
|
* @iv_len: The IV length for this key type
|
||||||
|
* @drv_priv: pointer for driver use
|
||||||
*/
|
*/
|
||||||
struct ieee80211_key_conf {
|
struct ieee80211_key_conf {
|
||||||
|
void *drv_priv;
|
||||||
atomic64_t tx_pn;
|
atomic64_t tx_pn;
|
||||||
u32 cipher;
|
u32 cipher;
|
||||||
u8 icv_len;
|
u8 icv_len;
|
||||||
|
@ -1675,7 +1678,6 @@ struct ieee80211_sta_rates {
|
||||||
* @tdls: indicates whether the STA is a TDLS peer
|
* @tdls: indicates whether the STA is a TDLS peer
|
||||||
* @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only
|
* @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only
|
||||||
* valid if the STA is a TDLS peer in the first place.
|
* valid if the STA is a TDLS peer in the first place.
|
||||||
* @mfp: indicates whether the STA uses management frame protection or not.
|
|
||||||
* @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
|
* @txq: per-TID data TX queues (if driver uses the TXQ abstraction)
|
||||||
*/
|
*/
|
||||||
struct ieee80211_sta {
|
struct ieee80211_sta {
|
||||||
|
@ -1693,7 +1695,6 @@ struct ieee80211_sta {
|
||||||
struct ieee80211_sta_rates __rcu *rates;
|
struct ieee80211_sta_rates __rcu *rates;
|
||||||
bool tdls;
|
bool tdls;
|
||||||
bool tdls_initiator;
|
bool tdls_initiator;
|
||||||
bool mfp;
|
|
||||||
|
|
||||||
struct ieee80211_txq *txq[IEEE80211_NUM_TIDS];
|
struct ieee80211_txq *txq[IEEE80211_NUM_TIDS];
|
||||||
|
|
||||||
|
@ -1888,6 +1889,9 @@ struct ieee80211_txq {
|
||||||
* @IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands
|
* @IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS: The HW supports scanning on all bands
|
||||||
* in one command, mac80211 doesn't have to run separate scans per band.
|
* in one command, mac80211 doesn't have to run separate scans per band.
|
||||||
*
|
*
|
||||||
|
* @IEEE80211_HW_TDLS_WIDER_BW: The device/driver supports wider bandwidth
|
||||||
|
* than then BSS bandwidth for a TDLS link on the base channel.
|
||||||
|
*
|
||||||
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
|
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
|
||||||
*/
|
*/
|
||||||
enum ieee80211_hw_flags {
|
enum ieee80211_hw_flags {
|
||||||
|
@ -1920,6 +1924,7 @@ enum ieee80211_hw_flags {
|
||||||
IEEE80211_HW_CHANCTX_STA_CSA,
|
IEEE80211_HW_CHANCTX_STA_CSA,
|
||||||
IEEE80211_HW_SUPPORTS_CLONED_SKBS,
|
IEEE80211_HW_SUPPORTS_CLONED_SKBS,
|
||||||
IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS,
|
IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS,
|
||||||
|
IEEE80211_HW_TDLS_WIDER_BW,
|
||||||
|
|
||||||
/* keep last, obviously */
|
/* keep last, obviously */
|
||||||
NUM_IEEE80211_HW_FLAGS
|
NUM_IEEE80211_HW_FLAGS
|
||||||
|
@ -3696,20 +3701,28 @@ void ieee80211_free_hw(struct ieee80211_hw *hw);
|
||||||
void ieee80211_restart_hw(struct ieee80211_hw *hw);
|
void ieee80211_restart_hw(struct ieee80211_hw *hw);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ieee80211_napi_add - initialize mac80211 NAPI context
|
* ieee80211_rx_napi - receive frame from NAPI context
|
||||||
* @hw: the hardware to initialize the NAPI context on
|
|
||||||
* @napi: the NAPI context to initialize
|
|
||||||
* @napi_dev: dummy NAPI netdevice, here to not waste the space if the
|
|
||||||
* driver doesn't use NAPI
|
|
||||||
* @poll: poll function
|
|
||||||
* @weight: default weight
|
|
||||||
*
|
*
|
||||||
* See also netif_napi_add().
|
* Use this function to hand received frames to mac80211. The receive
|
||||||
|
* buffer in @skb must start with an IEEE 802.11 header. In case of a
|
||||||
|
* paged @skb is used, the driver is recommended to put the ieee80211
|
||||||
|
* header of the frame on the linear part of the @skb to avoid memory
|
||||||
|
* allocation and/or memcpy by the stack.
|
||||||
|
*
|
||||||
|
* This function may not be called in IRQ context. Calls to this function
|
||||||
|
* for a single hardware must be synchronized against each other. Calls to
|
||||||
|
* this function, ieee80211_rx_ni() and ieee80211_rx_irqsafe() may not be
|
||||||
|
* mixed for a single hardware. Must not run concurrently with
|
||||||
|
* ieee80211_tx_status() or ieee80211_tx_status_ni().
|
||||||
|
*
|
||||||
|
* This function must be called with BHs disabled.
|
||||||
|
*
|
||||||
|
* @hw: the hardware this frame came in on
|
||||||
|
* @skb: the buffer to receive, owned by mac80211 after this call
|
||||||
|
* @napi: the NAPI context
|
||||||
*/
|
*/
|
||||||
void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi,
|
void ieee80211_rx_napi(struct ieee80211_hw *hw, struct sk_buff *skb,
|
||||||
struct net_device *napi_dev,
|
struct napi_struct *napi);
|
||||||
int (*poll)(struct napi_struct *, int),
|
|
||||||
int weight);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ieee80211_rx - receive frame
|
* ieee80211_rx - receive frame
|
||||||
|
@ -3731,7 +3744,10 @@ void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi,
|
||||||
* @hw: the hardware this frame came in on
|
* @hw: the hardware this frame came in on
|
||||||
* @skb: the buffer to receive, owned by mac80211 after this call
|
* @skb: the buffer to receive, owned by mac80211 after this call
|
||||||
*/
|
*/
|
||||||
void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb);
|
static inline void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
ieee80211_rx_napi(hw, skb, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ieee80211_rx_irqsafe - receive frame
|
* ieee80211_rx_irqsafe - receive frame
|
||||||
|
@ -4314,19 +4330,6 @@ void ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf,
|
||||||
void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf,
|
void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf,
|
||||||
struct sk_buff *skb, u8 *p2k);
|
struct sk_buff *skb, u8 *p2k);
|
||||||
|
|
||||||
/**
|
|
||||||
* ieee80211_aes_cmac_calculate_k1_k2 - calculate the AES-CMAC sub keys
|
|
||||||
*
|
|
||||||
* This function computes the two AES-CMAC sub-keys, based on the
|
|
||||||
* previously installed master key.
|
|
||||||
*
|
|
||||||
* @keyconf: the parameter passed with the set key
|
|
||||||
* @k1: a buffer to be filled with the 1st sub-key
|
|
||||||
* @k2: a buffer to be filled with the 2nd sub-key
|
|
||||||
*/
|
|
||||||
void ieee80211_aes_cmac_calculate_k1_k2(struct ieee80211_key_conf *keyconf,
|
|
||||||
u8 *k1, u8 *k2);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ieee80211_get_key_tx_seq - get key TX sequence counter
|
* ieee80211_get_key_tx_seq - get key TX sequence counter
|
||||||
*
|
*
|
||||||
|
|
|
@ -7,7 +7,6 @@ config MAC80211
|
||||||
select CRYPTO_CCM
|
select CRYPTO_CCM
|
||||||
select CRYPTO_GCM
|
select CRYPTO_GCM
|
||||||
select CRC32
|
select CRC32
|
||||||
select AVERAGE
|
|
||||||
---help---
|
---help---
|
||||||
This option enables the hardware independent IEEE 802.11
|
This option enables the hardware independent IEEE 802.11
|
||||||
networking stack.
|
networking stack.
|
||||||
|
|
|
@ -3,6 +3,7 @@ obj-$(CONFIG_MAC80211) += mac80211.o
|
||||||
# mac80211 objects
|
# mac80211 objects
|
||||||
mac80211-y := \
|
mac80211-y := \
|
||||||
main.o status.o \
|
main.o status.o \
|
||||||
|
driver-ops.o \
|
||||||
sta_info.o \
|
sta_info.o \
|
||||||
wep.o \
|
wep.o \
|
||||||
wpa.o \
|
wpa.o \
|
||||||
|
|
|
@ -145,20 +145,3 @@ void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm)
|
||||||
{
|
{
|
||||||
crypto_free_cipher(tfm);
|
crypto_free_cipher(tfm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ieee80211_aes_cmac_calculate_k1_k2(struct ieee80211_key_conf *keyconf,
|
|
||||||
u8 *k1, u8 *k2)
|
|
||||||
{
|
|
||||||
u8 l[AES_BLOCK_SIZE] = {};
|
|
||||||
struct ieee80211_key *key =
|
|
||||||
container_of(keyconf, struct ieee80211_key, conf);
|
|
||||||
|
|
||||||
crypto_cipher_encrypt_one(key->u.aes_cmac.tfm, l, l);
|
|
||||||
|
|
||||||
memcpy(k1, l, AES_BLOCK_SIZE);
|
|
||||||
gf_mulx(k1);
|
|
||||||
|
|
||||||
memcpy(k2, k1, AES_BLOCK_SIZE);
|
|
||||||
gf_mulx(k2);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(ieee80211_aes_cmac_calculate_k1_k2);
|
|
||||||
|
|
|
@ -1019,6 +1019,65 @@ static int sta_apply_auth_flags(struct ieee80211_local *local,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sta_apply_mesh_params(struct ieee80211_local *local,
|
||||||
|
struct sta_info *sta,
|
||||||
|
struct station_parameters *params)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_MAC80211_MESH
|
||||||
|
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||||
|
u32 changed = 0;
|
||||||
|
|
||||||
|
if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
|
||||||
|
switch (params->plink_state) {
|
||||||
|
case NL80211_PLINK_ESTAB:
|
||||||
|
if (sta->mesh->plink_state != NL80211_PLINK_ESTAB)
|
||||||
|
changed = mesh_plink_inc_estab_count(sdata);
|
||||||
|
sta->mesh->plink_state = params->plink_state;
|
||||||
|
|
||||||
|
ieee80211_mps_sta_status_update(sta);
|
||||||
|
changed |= ieee80211_mps_set_sta_local_pm(sta,
|
||||||
|
sdata->u.mesh.mshcfg.power_mode);
|
||||||
|
break;
|
||||||
|
case NL80211_PLINK_LISTEN:
|
||||||
|
case NL80211_PLINK_BLOCKED:
|
||||||
|
case NL80211_PLINK_OPN_SNT:
|
||||||
|
case NL80211_PLINK_OPN_RCVD:
|
||||||
|
case NL80211_PLINK_CNF_RCVD:
|
||||||
|
case NL80211_PLINK_HOLDING:
|
||||||
|
if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
|
||||||
|
changed = mesh_plink_dec_estab_count(sdata);
|
||||||
|
sta->mesh->plink_state = params->plink_state;
|
||||||
|
|
||||||
|
ieee80211_mps_sta_status_update(sta);
|
||||||
|
changed |= ieee80211_mps_set_sta_local_pm(sta,
|
||||||
|
NL80211_MESH_POWER_UNKNOWN);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* nothing */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (params->plink_action) {
|
||||||
|
case NL80211_PLINK_ACTION_NO_ACTION:
|
||||||
|
/* nothing */
|
||||||
|
break;
|
||||||
|
case NL80211_PLINK_ACTION_OPEN:
|
||||||
|
changed |= mesh_plink_open(sta);
|
||||||
|
break;
|
||||||
|
case NL80211_PLINK_ACTION_BLOCK:
|
||||||
|
changed |= mesh_plink_block(sta);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params->local_pm)
|
||||||
|
changed |= ieee80211_mps_set_sta_local_pm(sta,
|
||||||
|
params->local_pm);
|
||||||
|
|
||||||
|
ieee80211_mbss_info_change_notify(sdata, changed);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static int sta_apply_parameters(struct ieee80211_local *local,
|
static int sta_apply_parameters(struct ieee80211_local *local,
|
||||||
struct sta_info *sta,
|
struct sta_info *sta,
|
||||||
struct station_parameters *params)
|
struct station_parameters *params)
|
||||||
|
@ -1076,7 +1135,6 @@ static int sta_apply_parameters(struct ieee80211_local *local,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mask & BIT(NL80211_STA_FLAG_MFP)) {
|
if (mask & BIT(NL80211_STA_FLAG_MFP)) {
|
||||||
sta->sta.mfp = !!(set & BIT(NL80211_STA_FLAG_MFP));
|
|
||||||
if (set & BIT(NL80211_STA_FLAG_MFP))
|
if (set & BIT(NL80211_STA_FLAG_MFP))
|
||||||
set_sta_flag(sta, WLAN_STA_MFP);
|
set_sta_flag(sta, WLAN_STA_MFP);
|
||||||
else
|
else
|
||||||
|
@ -1097,6 +1155,12 @@ static int sta_apply_parameters(struct ieee80211_local *local,
|
||||||
params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)
|
params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)
|
||||||
set_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH);
|
set_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH);
|
||||||
|
|
||||||
|
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
|
||||||
|
ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) &&
|
||||||
|
params->ext_capab_len >= 8 &&
|
||||||
|
params->ext_capab[7] & WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED)
|
||||||
|
set_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW);
|
||||||
|
|
||||||
if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) {
|
if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) {
|
||||||
sta->sta.uapsd_queues = params->uapsd_queues;
|
sta->sta.uapsd_queues = params->uapsd_queues;
|
||||||
sta->sta.max_sp = params->max_sp;
|
sta->sta.max_sp = params->max_sp;
|
||||||
|
@ -1144,62 +1208,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
|
||||||
band, false);
|
band, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
if (ieee80211_vif_is_mesh(&sdata->vif))
|
||||||
#ifdef CONFIG_MAC80211_MESH
|
sta_apply_mesh_params(local, sta, params);
|
||||||
u32 changed = 0;
|
|
||||||
|
|
||||||
if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
|
|
||||||
switch (params->plink_state) {
|
|
||||||
case NL80211_PLINK_ESTAB:
|
|
||||||
if (sta->plink_state != NL80211_PLINK_ESTAB)
|
|
||||||
changed = mesh_plink_inc_estab_count(
|
|
||||||
sdata);
|
|
||||||
sta->plink_state = params->plink_state;
|
|
||||||
|
|
||||||
ieee80211_mps_sta_status_update(sta);
|
|
||||||
changed |= ieee80211_mps_set_sta_local_pm(sta,
|
|
||||||
sdata->u.mesh.mshcfg.power_mode);
|
|
||||||
break;
|
|
||||||
case NL80211_PLINK_LISTEN:
|
|
||||||
case NL80211_PLINK_BLOCKED:
|
|
||||||
case NL80211_PLINK_OPN_SNT:
|
|
||||||
case NL80211_PLINK_OPN_RCVD:
|
|
||||||
case NL80211_PLINK_CNF_RCVD:
|
|
||||||
case NL80211_PLINK_HOLDING:
|
|
||||||
if (sta->plink_state == NL80211_PLINK_ESTAB)
|
|
||||||
changed = mesh_plink_dec_estab_count(
|
|
||||||
sdata);
|
|
||||||
sta->plink_state = params->plink_state;
|
|
||||||
|
|
||||||
ieee80211_mps_sta_status_update(sta);
|
|
||||||
changed |= ieee80211_mps_set_sta_local_pm(sta,
|
|
||||||
NL80211_MESH_POWER_UNKNOWN);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* nothing */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (params->plink_action) {
|
|
||||||
case NL80211_PLINK_ACTION_NO_ACTION:
|
|
||||||
/* nothing */
|
|
||||||
break;
|
|
||||||
case NL80211_PLINK_ACTION_OPEN:
|
|
||||||
changed |= mesh_plink_open(sta);
|
|
||||||
break;
|
|
||||||
case NL80211_PLINK_ACTION_BLOCK:
|
|
||||||
changed |= mesh_plink_block(sta);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params->local_pm)
|
|
||||||
changed |=
|
|
||||||
ieee80211_mps_set_sta_local_pm(sta,
|
|
||||||
params->local_pm);
|
|
||||||
ieee80211_mbss_info_change_notify(sdata, changed);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set the STA state after all sta info from usermode has been set */
|
/* set the STA state after all sta info from usermode has been set */
|
||||||
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
|
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
|
||||||
|
@ -2358,6 +2368,8 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
|
||||||
const u8 *ap;
|
const u8 *ap;
|
||||||
enum ieee80211_smps_mode old_req;
|
enum ieee80211_smps_mode old_req;
|
||||||
int err;
|
int err;
|
||||||
|
struct sta_info *sta;
|
||||||
|
bool tdls_peer_found = false;
|
||||||
|
|
||||||
lockdep_assert_held(&sdata->wdev.mtx);
|
lockdep_assert_held(&sdata->wdev.mtx);
|
||||||
|
|
||||||
|
@ -2382,11 +2394,22 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
|
||||||
|
|
||||||
ap = sdata->u.mgd.associated->bssid;
|
ap = sdata->u.mgd.associated->bssid;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
|
||||||
|
if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
|
||||||
|
!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tdls_peer_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
if (smps_mode == IEEE80211_SMPS_AUTOMATIC) {
|
if (smps_mode == IEEE80211_SMPS_AUTOMATIC) {
|
||||||
if (sdata->u.mgd.powersave)
|
if (tdls_peer_found || !sdata->u.mgd.powersave)
|
||||||
smps_mode = IEEE80211_SMPS_DYNAMIC;
|
|
||||||
else
|
|
||||||
smps_mode = IEEE80211_SMPS_OFF;
|
smps_mode = IEEE80211_SMPS_OFF;
|
||||||
|
else
|
||||||
|
smps_mode = IEEE80211_SMPS_DYNAMIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send SM PS frame to AP */
|
/* send SM PS frame to AP */
|
||||||
|
@ -2394,6 +2417,8 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
|
||||||
ap, ap);
|
ap, ap);
|
||||||
if (err)
|
if (err)
|
||||||
sdata->u.mgd.req_smps = old_req;
|
sdata->u.mgd.req_smps = old_req;
|
||||||
|
else if (smps_mode != IEEE80211_SMPS_OFF && tdls_peer_found)
|
||||||
|
ieee80211_teardown_tdls_peers(sdata);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -2479,16 +2504,26 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
|
||||||
sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
|
sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
|
||||||
memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs,
|
memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs,
|
||||||
sizeof(mask->control[i].ht_mcs));
|
sizeof(mask->control[i].ht_mcs));
|
||||||
|
memcpy(sdata->rc_rateidx_vht_mcs_mask[i],
|
||||||
|
mask->control[i].vht_mcs,
|
||||||
|
sizeof(mask->control[i].vht_mcs));
|
||||||
|
|
||||||
sdata->rc_has_mcs_mask[i] = false;
|
sdata->rc_has_mcs_mask[i] = false;
|
||||||
|
sdata->rc_has_vht_mcs_mask[i] = false;
|
||||||
if (!sband)
|
if (!sband)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++)
|
for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++) {
|
||||||
if (~sdata->rc_rateidx_mcs_mask[i][j]) {
|
if (~sdata->rc_rateidx_mcs_mask[i][j])
|
||||||
sdata->rc_has_mcs_mask[i] = true;
|
sdata->rc_has_mcs_mask[i] = true;
|
||||||
|
|
||||||
|
if (~sdata->rc_rateidx_vht_mcs_mask[i][j])
|
||||||
|
sdata->rc_has_vht_mcs_mask[i] = true;
|
||||||
|
|
||||||
|
if (sdata->rc_has_mcs_mask[i] &&
|
||||||
|
sdata->rc_has_vht_mcs_mask[i])
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -190,7 +190,7 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta)
|
enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta)
|
||||||
{
|
{
|
||||||
switch (sta->bandwidth) {
|
switch (sta->bandwidth) {
|
||||||
case IEEE80211_STA_RX_BW_20:
|
case IEEE80211_STA_RX_BW_20:
|
||||||
|
@ -264,9 +264,17 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
|
||||||
case NL80211_IFTYPE_AP_VLAN:
|
case NL80211_IFTYPE_AP_VLAN:
|
||||||
width = ieee80211_get_max_required_bw(sdata);
|
width = ieee80211_get_max_required_bw(sdata);
|
||||||
break;
|
break;
|
||||||
|
case NL80211_IFTYPE_STATION:
|
||||||
|
/*
|
||||||
|
* The ap's sta->bandwidth is not set yet at this
|
||||||
|
* point, so take the width from the chandef, but
|
||||||
|
* account also for TDLS peers
|
||||||
|
*/
|
||||||
|
width = max(vif->bss_conf.chandef.width,
|
||||||
|
ieee80211_get_max_required_bw(sdata));
|
||||||
|
break;
|
||||||
case NL80211_IFTYPE_P2P_DEVICE:
|
case NL80211_IFTYPE_P2P_DEVICE:
|
||||||
continue;
|
continue;
|
||||||
case NL80211_IFTYPE_STATION:
|
|
||||||
case NL80211_IFTYPE_ADHOC:
|
case NL80211_IFTYPE_ADHOC:
|
||||||
case NL80211_IFTYPE_WDS:
|
case NL80211_IFTYPE_WDS:
|
||||||
case NL80211_IFTYPE_MESH_POINT:
|
case NL80211_IFTYPE_MESH_POINT:
|
||||||
|
@ -554,12 +562,13 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local,
|
||||||
kfree_rcu(ctx, rcu_head);
|
kfree_rcu(ctx, rcu_head);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
|
void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
|
||||||
struct ieee80211_chanctx *ctx)
|
struct ieee80211_chanctx *ctx)
|
||||||
{
|
{
|
||||||
struct ieee80211_chanctx_conf *conf = &ctx->conf;
|
struct ieee80211_chanctx_conf *conf = &ctx->conf;
|
||||||
struct ieee80211_sub_if_data *sdata;
|
struct ieee80211_sub_if_data *sdata;
|
||||||
const struct cfg80211_chan_def *compat = NULL;
|
const struct cfg80211_chan_def *compat = NULL;
|
||||||
|
struct sta_info *sta;
|
||||||
|
|
||||||
lockdep_assert_held(&local->chanctx_mtx);
|
lockdep_assert_held(&local->chanctx_mtx);
|
||||||
|
|
||||||
|
@ -581,6 +590,20 @@ static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
|
||||||
if (WARN_ON_ONCE(!compat))
|
if (WARN_ON_ONCE(!compat))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TDLS peers can sometimes affect the chandef width */
|
||||||
|
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||||
|
if (!sta->uploaded ||
|
||||||
|
!test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) ||
|
||||||
|
!test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
|
||||||
|
!sta->tdls_chandef.chan)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
compat = cfg80211_chandef_compatible(&sta->tdls_chandef,
|
||||||
|
compat);
|
||||||
|
if (WARN_ON_ONCE(!compat))
|
||||||
|
break;
|
||||||
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
if (!compat)
|
if (!compat)
|
||||||
|
|
|
@ -122,6 +122,7 @@ static const char *hw_flag_names[NUM_IEEE80211_HW_FLAGS + 1] = {
|
||||||
FLAG(CHANCTX_STA_CSA),
|
FLAG(CHANCTX_STA_CSA),
|
||||||
FLAG(SUPPORTS_CLONED_SKBS),
|
FLAG(SUPPORTS_CLONED_SKBS),
|
||||||
FLAG(SINGLE_SCAN_ON_ALL_BANDS),
|
FLAG(SINGLE_SCAN_ON_ALL_BANDS),
|
||||||
|
FLAG(TDLS_WIDER_BW),
|
||||||
|
|
||||||
/* keep last for the build bug below */
|
/* keep last for the build bug below */
|
||||||
(void *)0x1
|
(void *)0x1
|
||||||
|
@ -277,7 +278,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
|
||||||
DEBUGFS_STATS_ADD(rx_handlers_queued);
|
DEBUGFS_STATS_ADD(rx_handlers_queued);
|
||||||
DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc);
|
DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc);
|
||||||
DEBUGFS_STATS_ADD(rx_handlers_drop_defrag);
|
DEBUGFS_STATS_ADD(rx_handlers_drop_defrag);
|
||||||
DEBUGFS_STATS_ADD(rx_handlers_drop_short);
|
|
||||||
DEBUGFS_STATS_ADD(tx_expand_skb_head);
|
DEBUGFS_STATS_ADD(tx_expand_skb_head);
|
||||||
DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned);
|
DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned);
|
||||||
DEBUGFS_STATS_ADD(rx_expand_skb_head_defrag);
|
DEBUGFS_STATS_ADD(rx_expand_skb_head_defrag);
|
||||||
|
|
|
@ -57,7 +57,6 @@ KEY_CONF_FILE(keylen, D);
|
||||||
KEY_CONF_FILE(keyidx, D);
|
KEY_CONF_FILE(keyidx, D);
|
||||||
KEY_CONF_FILE(hw_key_idx, D);
|
KEY_CONF_FILE(hw_key_idx, D);
|
||||||
KEY_FILE(flags, X);
|
KEY_FILE(flags, X);
|
||||||
KEY_FILE(tx_rx_count, D);
|
|
||||||
KEY_READ(ifindex, sdata->name, "%s\n");
|
KEY_READ(ifindex, sdata->name, "%s\n");
|
||||||
KEY_OPS(ifindex);
|
KEY_OPS(ifindex);
|
||||||
|
|
||||||
|
@ -310,7 +309,6 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
|
||||||
DEBUGFS_ADD(flags);
|
DEBUGFS_ADD(flags);
|
||||||
DEBUGFS_ADD(keyidx);
|
DEBUGFS_ADD(keyidx);
|
||||||
DEBUGFS_ADD(hw_key_idx);
|
DEBUGFS_ADD(hw_key_idx);
|
||||||
DEBUGFS_ADD(tx_rx_count);
|
|
||||||
DEBUGFS_ADD(algorithm);
|
DEBUGFS_ADD(algorithm);
|
||||||
DEBUGFS_ADD(tx_spec);
|
DEBUGFS_ADD(tx_spec);
|
||||||
DEBUGFS_ADD(rx_spec);
|
DEBUGFS_ADD(rx_spec);
|
||||||
|
|
|
@ -186,6 +186,38 @@ IEEE80211_IF_FILE(rc_rateidx_mcs_mask_2ghz,
|
||||||
IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz,
|
IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz,
|
||||||
rc_rateidx_mcs_mask[IEEE80211_BAND_5GHZ], HEXARRAY);
|
rc_rateidx_mcs_mask[IEEE80211_BAND_5GHZ], HEXARRAY);
|
||||||
|
|
||||||
|
static ssize_t ieee80211_if_fmt_rc_rateidx_vht_mcs_mask_2ghz(
|
||||||
|
const struct ieee80211_sub_if_data *sdata,
|
||||||
|
char *buf, int buflen)
|
||||||
|
{
|
||||||
|
int i, len = 0;
|
||||||
|
const u16 *mask = sdata->rc_rateidx_vht_mcs_mask[IEEE80211_BAND_2GHZ];
|
||||||
|
|
||||||
|
for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
|
||||||
|
len += scnprintf(buf + len, buflen - len, "%04x ", mask[i]);
|
||||||
|
len += scnprintf(buf + len, buflen - len, "\n");
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEEE80211_IF_FILE_R(rc_rateidx_vht_mcs_mask_2ghz);
|
||||||
|
|
||||||
|
static ssize_t ieee80211_if_fmt_rc_rateidx_vht_mcs_mask_5ghz(
|
||||||
|
const struct ieee80211_sub_if_data *sdata,
|
||||||
|
char *buf, int buflen)
|
||||||
|
{
|
||||||
|
int i, len = 0;
|
||||||
|
const u16 *mask = sdata->rc_rateidx_vht_mcs_mask[IEEE80211_BAND_5GHZ];
|
||||||
|
|
||||||
|
for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
|
||||||
|
len += scnprintf(buf + len, buflen - len, "%04x ", mask[i]);
|
||||||
|
len += scnprintf(buf + len, buflen - len, "\n");
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEEE80211_IF_FILE_R(rc_rateidx_vht_mcs_mask_5ghz);
|
||||||
|
|
||||||
IEEE80211_IF_FILE(flags, flags, HEX);
|
IEEE80211_IF_FILE(flags, flags, HEX);
|
||||||
IEEE80211_IF_FILE(state, state, LHEX);
|
IEEE80211_IF_FILE(state, state, LHEX);
|
||||||
IEEE80211_IF_FILE(txpower, vif.bss_conf.txpower, DEC);
|
IEEE80211_IF_FILE(txpower, vif.bss_conf.txpower, DEC);
|
||||||
|
@ -565,6 +597,8 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata)
|
||||||
DEBUGFS_ADD(rc_rateidx_mask_5ghz);
|
DEBUGFS_ADD(rc_rateidx_mask_5ghz);
|
||||||
DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
|
DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
|
||||||
DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
|
DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
|
||||||
|
DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_2ghz);
|
||||||
|
DEBUGFS_ADD(rc_rateidx_vht_mcs_mask_5ghz);
|
||||||
DEBUGFS_ADD(hw_queues);
|
DEBUGFS_ADD(hw_queues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
41
net/mac80211/driver-ops.c
Normal file
41
net/mac80211/driver-ops.c
Normal 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;
|
||||||
|
}
|
|
@ -573,37 +573,12 @@ static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local,
|
||||||
trace_drv_return_void(local);
|
trace_drv_return_void(local);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline __must_check
|
__must_check
|
||||||
int drv_sta_state(struct ieee80211_local *local,
|
int drv_sta_state(struct ieee80211_local *local,
|
||||||
struct ieee80211_sub_if_data *sdata,
|
struct ieee80211_sub_if_data *sdata,
|
||||||
struct sta_info *sta,
|
struct sta_info *sta,
|
||||||
enum ieee80211_sta_state old_state,
|
enum ieee80211_sta_state old_state,
|
||||||
enum ieee80211_sta_state new_state)
|
enum ieee80211_sta_state new_state);
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
might_sleep();
|
|
||||||
|
|
||||||
sdata = get_bss_sdata(sdata);
|
|
||||||
if (!check_sdata_in_driver(sdata))
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state);
|
|
||||||
if (local->ops->sta_state) {
|
|
||||||
ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta,
|
|
||||||
old_state, new_state);
|
|
||||||
} else if (old_state == IEEE80211_STA_AUTH &&
|
|
||||||
new_state == IEEE80211_STA_ASSOC) {
|
|
||||||
ret = drv_sta_add(local, sdata, &sta->sta);
|
|
||||||
if (ret == 0)
|
|
||||||
sta->uploaded = true;
|
|
||||||
} else if (old_state == IEEE80211_STA_ASSOC &&
|
|
||||||
new_state == IEEE80211_STA_AUTH) {
|
|
||||||
drv_sta_remove(local, sdata, &sta->sta);
|
|
||||||
}
|
|
||||||
trace_drv_return_int(local, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void drv_sta_rc_update(struct ieee80211_local *local,
|
static inline void drv_sta_rc_update(struct ieee80211_local *local,
|
||||||
struct ieee80211_sub_if_data *sdata,
|
struct ieee80211_sub_if_data *sdata,
|
||||||
|
|
|
@ -84,13 +84,13 @@ struct ieee80211_local;
|
||||||
#define IEEE80211_DEAUTH_FRAME_LEN (24 /* hdr */ + 2 /* reason */)
|
#define IEEE80211_DEAUTH_FRAME_LEN (24 /* hdr */ + 2 /* reason */)
|
||||||
|
|
||||||
struct ieee80211_fragment_entry {
|
struct ieee80211_fragment_entry {
|
||||||
unsigned long first_frag_time;
|
|
||||||
unsigned int seq;
|
|
||||||
unsigned int rx_queue;
|
|
||||||
unsigned int last_frag;
|
|
||||||
unsigned int extra_len;
|
|
||||||
struct sk_buff_head skb_list;
|
struct sk_buff_head skb_list;
|
||||||
int ccmp; /* Whether fragments were encrypted with CCMP */
|
unsigned long first_frag_time;
|
||||||
|
u16 seq;
|
||||||
|
u16 extra_len;
|
||||||
|
u16 last_frag;
|
||||||
|
u8 rx_queue;
|
||||||
|
bool ccmp; /* Whether fragments were encrypted with CCMP */
|
||||||
u8 last_pn[6]; /* PN of the last fragment if CCMP was used */
|
u8 last_pn[6]; /* PN of the last fragment if CCMP was used */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -181,7 +181,6 @@ typedef unsigned __bitwise__ ieee80211_rx_result;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enum ieee80211_packet_rx_flags - packet RX flags
|
* enum ieee80211_packet_rx_flags - packet RX flags
|
||||||
* @IEEE80211_RX_FRAGMENTED: fragmented frame
|
|
||||||
* @IEEE80211_RX_AMSDU: a-MSDU packet
|
* @IEEE80211_RX_AMSDU: a-MSDU packet
|
||||||
* @IEEE80211_RX_MALFORMED_ACTION_FRM: action frame is malformed
|
* @IEEE80211_RX_MALFORMED_ACTION_FRM: action frame is malformed
|
||||||
* @IEEE80211_RX_DEFERRED_RELEASE: frame was subjected to receive reordering
|
* @IEEE80211_RX_DEFERRED_RELEASE: frame was subjected to receive reordering
|
||||||
|
@ -190,7 +189,6 @@ typedef unsigned __bitwise__ ieee80211_rx_result;
|
||||||
* @rx_flags field of &struct ieee80211_rx_status.
|
* @rx_flags field of &struct ieee80211_rx_status.
|
||||||
*/
|
*/
|
||||||
enum ieee80211_packet_rx_flags {
|
enum ieee80211_packet_rx_flags {
|
||||||
IEEE80211_RX_FRAGMENTED = BIT(2),
|
|
||||||
IEEE80211_RX_AMSDU = BIT(3),
|
IEEE80211_RX_AMSDU = BIT(3),
|
||||||
IEEE80211_RX_MALFORMED_ACTION_FRM = BIT(4),
|
IEEE80211_RX_MALFORMED_ACTION_FRM = BIT(4),
|
||||||
IEEE80211_RX_DEFERRED_RELEASE = BIT(5),
|
IEEE80211_RX_DEFERRED_RELEASE = BIT(5),
|
||||||
|
@ -202,8 +200,6 @@ enum ieee80211_packet_rx_flags {
|
||||||
* @IEEE80211_RX_CMNTR: received on cooked monitor already
|
* @IEEE80211_RX_CMNTR: received on cooked monitor already
|
||||||
* @IEEE80211_RX_BEACON_REPORTED: This frame was already reported
|
* @IEEE80211_RX_BEACON_REPORTED: This frame was already reported
|
||||||
* to cfg80211_report_obss_beacon().
|
* to cfg80211_report_obss_beacon().
|
||||||
* @IEEE80211_RX_REORDER_TIMER: this frame is released by the
|
|
||||||
* reorder buffer timeout timer, not the normal RX path
|
|
||||||
*
|
*
|
||||||
* These flags are used across handling multiple interfaces
|
* These flags are used across handling multiple interfaces
|
||||||
* for a single frame.
|
* for a single frame.
|
||||||
|
@ -211,10 +207,10 @@ enum ieee80211_packet_rx_flags {
|
||||||
enum ieee80211_rx_flags {
|
enum ieee80211_rx_flags {
|
||||||
IEEE80211_RX_CMNTR = BIT(0),
|
IEEE80211_RX_CMNTR = BIT(0),
|
||||||
IEEE80211_RX_BEACON_REPORTED = BIT(1),
|
IEEE80211_RX_BEACON_REPORTED = BIT(1),
|
||||||
IEEE80211_RX_REORDER_TIMER = BIT(2),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ieee80211_rx_data {
|
struct ieee80211_rx_data {
|
||||||
|
struct napi_struct *napi;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
struct ieee80211_local *local;
|
struct ieee80211_local *local;
|
||||||
struct ieee80211_sub_if_data *sdata;
|
struct ieee80211_sub_if_data *sdata;
|
||||||
|
@ -725,6 +721,7 @@ struct ieee80211_if_mesh {
|
||||||
* back to wireless media and to the local net stack.
|
* back to wireless media and to the local net stack.
|
||||||
* @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume.
|
* @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume.
|
||||||
* @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver
|
* @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver
|
||||||
|
* @IEEE80211_SDATA_MU_MIMO_OWNER: indicates interface owns MU-MIMO capability
|
||||||
*/
|
*/
|
||||||
enum ieee80211_sub_if_data_flags {
|
enum ieee80211_sub_if_data_flags {
|
||||||
IEEE80211_SDATA_ALLMULTI = BIT(0),
|
IEEE80211_SDATA_ALLMULTI = BIT(0),
|
||||||
|
@ -732,6 +729,7 @@ enum ieee80211_sub_if_data_flags {
|
||||||
IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3),
|
IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3),
|
||||||
IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4),
|
IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4),
|
||||||
IEEE80211_SDATA_IN_DRIVER = BIT(5),
|
IEEE80211_SDATA_IN_DRIVER = BIT(5),
|
||||||
|
IEEE80211_SDATA_MU_MIMO_OWNER = BIT(6),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -903,6 +901,9 @@ struct ieee80211_sub_if_data {
|
||||||
bool rc_has_mcs_mask[IEEE80211_NUM_BANDS];
|
bool rc_has_mcs_mask[IEEE80211_NUM_BANDS];
|
||||||
u8 rc_rateidx_mcs_mask[IEEE80211_NUM_BANDS][IEEE80211_HT_MCS_MASK_LEN];
|
u8 rc_rateidx_mcs_mask[IEEE80211_NUM_BANDS][IEEE80211_HT_MCS_MASK_LEN];
|
||||||
|
|
||||||
|
bool rc_has_vht_mcs_mask[IEEE80211_NUM_BANDS];
|
||||||
|
u16 rc_rateidx_vht_mcs_mask[IEEE80211_NUM_BANDS][NL80211_VHT_NSS_MAX];
|
||||||
|
|
||||||
union {
|
union {
|
||||||
struct ieee80211_if_ap ap;
|
struct ieee80211_if_ap ap;
|
||||||
struct ieee80211_if_wds wds;
|
struct ieee80211_if_wds wds;
|
||||||
|
@ -1010,7 +1011,6 @@ enum sdata_queue_type {
|
||||||
IEEE80211_SDATA_QUEUE_AGG_STOP = 2,
|
IEEE80211_SDATA_QUEUE_AGG_STOP = 2,
|
||||||
IEEE80211_SDATA_QUEUE_RX_AGG_START = 3,
|
IEEE80211_SDATA_QUEUE_RX_AGG_START = 3,
|
||||||
IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4,
|
IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4,
|
||||||
IEEE80211_SDATA_QUEUE_TDLS_CHSW = 5,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -1286,7 +1286,6 @@ struct ieee80211_local {
|
||||||
unsigned int rx_handlers_queued;
|
unsigned int rx_handlers_queued;
|
||||||
unsigned int rx_handlers_drop_nullfunc;
|
unsigned int rx_handlers_drop_nullfunc;
|
||||||
unsigned int rx_handlers_drop_defrag;
|
unsigned int rx_handlers_drop_defrag;
|
||||||
unsigned int rx_handlers_drop_short;
|
|
||||||
unsigned int tx_expand_skb_head;
|
unsigned int tx_expand_skb_head;
|
||||||
unsigned int tx_expand_skb_head_cloned;
|
unsigned int tx_expand_skb_head_cloned;
|
||||||
unsigned int rx_expand_skb_head_defrag;
|
unsigned int rx_expand_skb_head_defrag;
|
||||||
|
@ -1348,14 +1347,16 @@ struct ieee80211_local {
|
||||||
|
|
||||||
struct ieee80211_sub_if_data __rcu *p2p_sdata;
|
struct ieee80211_sub_if_data __rcu *p2p_sdata;
|
||||||
|
|
||||||
struct napi_struct *napi;
|
|
||||||
|
|
||||||
/* virtual monitor interface */
|
/* virtual monitor interface */
|
||||||
struct ieee80211_sub_if_data __rcu *monitor_sdata;
|
struct ieee80211_sub_if_data __rcu *monitor_sdata;
|
||||||
struct cfg80211_chan_def monitor_chandef;
|
struct cfg80211_chan_def monitor_chandef;
|
||||||
|
|
||||||
/* extended capabilities provided by mac80211 */
|
/* extended capabilities provided by mac80211 */
|
||||||
u8 ext_capa[8];
|
u8 ext_capa[8];
|
||||||
|
|
||||||
|
/* TDLS channel switch */
|
||||||
|
struct work_struct tdls_chsw_work;
|
||||||
|
struct sk_buff_head skb_queue_tdls_chsw;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct ieee80211_sub_if_data *
|
static inline struct ieee80211_sub_if_data *
|
||||||
|
@ -1715,6 +1716,8 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
|
||||||
enum ieee80211_band band, bool nss_only);
|
enum ieee80211_band band, bool nss_only);
|
||||||
void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
|
void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
|
||||||
struct ieee80211_sta_vht_cap *vht_cap);
|
struct ieee80211_sta_vht_cap *vht_cap);
|
||||||
|
void ieee80211_get_vht_mask_from_cap(__le16 vht_cap,
|
||||||
|
u16 vht_mask[NL80211_VHT_NSS_MAX]);
|
||||||
|
|
||||||
/* Spectrum management */
|
/* Spectrum management */
|
||||||
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
||||||
|
@ -1763,8 +1766,6 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
|
||||||
|
|
||||||
/* utility functions/constants */
|
/* utility functions/constants */
|
||||||
extern const void *const mac80211_wiphy_privid; /* for wiphy privid */
|
extern const void *const mac80211_wiphy_privid; /* for wiphy privid */
|
||||||
u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
|
|
||||||
enum nl80211_iftype type);
|
|
||||||
int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
|
int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
|
||||||
int rate, int erp, int short_preamble,
|
int rate, int erp, int short_preamble,
|
||||||
int shift);
|
int shift);
|
||||||
|
@ -2042,6 +2043,9 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
|
||||||
enum ieee80211_chanctx_mode chanmode,
|
enum ieee80211_chanctx_mode chanmode,
|
||||||
u8 radar_detect);
|
u8 radar_detect);
|
||||||
int ieee80211_max_num_channels(struct ieee80211_local *local);
|
int ieee80211_max_num_channels(struct ieee80211_local *local);
|
||||||
|
enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta);
|
||||||
|
void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
|
||||||
|
struct ieee80211_chanctx *ctx);
|
||||||
|
|
||||||
/* TDLS */
|
/* TDLS */
|
||||||
int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
||||||
|
@ -2058,8 +2062,8 @@ int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||||
void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
|
void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
|
||||||
struct net_device *dev,
|
struct net_device *dev,
|
||||||
const u8 *addr);
|
const u8 *addr);
|
||||||
void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
|
void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata);
|
||||||
struct sk_buff *skb);
|
void ieee80211_tdls_chsw_work(struct work_struct *wk);
|
||||||
|
|
||||||
extern const struct ethtool_ops ieee80211_ethtool_ops;
|
extern const struct ethtool_ops ieee80211_ethtool_ops;
|
||||||
|
|
||||||
|
|
|
@ -1242,8 +1242,6 @@ static void ieee80211_iface_work(struct work_struct *work)
|
||||||
WLAN_BACK_RECIPIENT, 0,
|
WLAN_BACK_RECIPIENT, 0,
|
||||||
false);
|
false);
|
||||||
mutex_unlock(&local->sta_mtx);
|
mutex_unlock(&local->sta_mtx);
|
||||||
} else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_TDLS_CHSW) {
|
|
||||||
ieee80211_process_tdls_channel_switch(sdata, skb);
|
|
||||||
} else if (ieee80211_is_action(mgmt->frame_control) &&
|
} else if (ieee80211_is_action(mgmt->frame_control) &&
|
||||||
mgmt->u.action.category == WLAN_CATEGORY_BACK) {
|
mgmt->u.action.category == WLAN_CATEGORY_BACK) {
|
||||||
int len = skb->len;
|
int len = skb->len;
|
||||||
|
@ -1790,13 +1788,23 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
|
||||||
sband = local->hw.wiphy->bands[i];
|
sband = local->hw.wiphy->bands[i];
|
||||||
sdata->rc_rateidx_mask[i] =
|
sdata->rc_rateidx_mask[i] =
|
||||||
sband ? (1 << sband->n_bitrates) - 1 : 0;
|
sband ? (1 << sband->n_bitrates) - 1 : 0;
|
||||||
if (sband)
|
if (sband) {
|
||||||
|
__le16 cap;
|
||||||
|
u16 *vht_rate_mask;
|
||||||
|
|
||||||
memcpy(sdata->rc_rateidx_mcs_mask[i],
|
memcpy(sdata->rc_rateidx_mcs_mask[i],
|
||||||
sband->ht_cap.mcs.rx_mask,
|
sband->ht_cap.mcs.rx_mask,
|
||||||
sizeof(sdata->rc_rateidx_mcs_mask[i]));
|
sizeof(sdata->rc_rateidx_mcs_mask[i]));
|
||||||
else
|
|
||||||
|
cap = sband->vht_cap.vht_mcs.rx_mcs_map;
|
||||||
|
vht_rate_mask = sdata->rc_rateidx_vht_mcs_mask[i];
|
||||||
|
ieee80211_get_vht_mask_from_cap(cap, vht_rate_mask);
|
||||||
|
} else {
|
||||||
memset(sdata->rc_rateidx_mcs_mask[i], 0,
|
memset(sdata->rc_rateidx_mcs_mask[i], 0,
|
||||||
sizeof(sdata->rc_rateidx_mcs_mask[i]));
|
sizeof(sdata->rc_rateidx_mcs_mask[i]));
|
||||||
|
memset(sdata->rc_rateidx_vht_mcs_mask[i], 0,
|
||||||
|
sizeof(sdata->rc_rateidx_vht_mcs_mask[i]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ieee80211_set_default_queues(sdata);
|
ieee80211_set_default_queues(sdata);
|
||||||
|
|
|
@ -336,7 +336,6 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
|
||||||
ieee80211_check_fast_xmit(sta);
|
ieee80211_check_fast_xmit(sta);
|
||||||
} else {
|
} else {
|
||||||
rcu_assign_pointer(sta->gtk[idx], new);
|
rcu_assign_pointer(sta->gtk[idx], new);
|
||||||
sta->gtk_idx = idx;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
defunikey = old &&
|
defunikey = old &&
|
||||||
|
|
|
@ -115,9 +115,6 @@ struct ieee80211_key {
|
||||||
} gen;
|
} gen;
|
||||||
} u;
|
} u;
|
||||||
|
|
||||||
/* number of times this key has been used */
|
|
||||||
int tx_rx_count;
|
|
||||||
|
|
||||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||||
struct {
|
struct {
|
||||||
struct dentry *stalink;
|
struct dentry *stalink;
|
||||||
|
|
|
@ -629,6 +629,8 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
|
||||||
INIT_WORK(&local->sched_scan_stopped_work,
|
INIT_WORK(&local->sched_scan_stopped_work,
|
||||||
ieee80211_sched_scan_stopped_work);
|
ieee80211_sched_scan_stopped_work);
|
||||||
|
|
||||||
|
INIT_WORK(&local->tdls_chsw_work, ieee80211_tdls_chsw_work);
|
||||||
|
|
||||||
spin_lock_init(&local->ack_status_lock);
|
spin_lock_init(&local->ack_status_lock);
|
||||||
idr_init(&local->ack_status_frames);
|
idr_init(&local->ack_status_frames);
|
||||||
|
|
||||||
|
@ -645,6 +647,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
|
||||||
|
|
||||||
skb_queue_head_init(&local->skb_queue);
|
skb_queue_head_init(&local->skb_queue);
|
||||||
skb_queue_head_init(&local->skb_queue_unreliable);
|
skb_queue_head_init(&local->skb_queue_unreliable);
|
||||||
|
skb_queue_head_init(&local->skb_queue_tdls_chsw);
|
||||||
|
|
||||||
ieee80211_alloc_led_names(local);
|
ieee80211_alloc_led_names(local);
|
||||||
|
|
||||||
|
@ -1132,18 +1135,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ieee80211_register_hw);
|
EXPORT_SYMBOL(ieee80211_register_hw);
|
||||||
|
|
||||||
void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi,
|
|
||||||
struct net_device *napi_dev,
|
|
||||||
int (*poll)(struct napi_struct *, int),
|
|
||||||
int weight)
|
|
||||||
{
|
|
||||||
struct ieee80211_local *local = hw_to_local(hw);
|
|
||||||
|
|
||||||
netif_napi_add(napi_dev, napi, poll, weight);
|
|
||||||
local->napi = napi;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(ieee80211_napi_add);
|
|
||||||
|
|
||||||
void ieee80211_unregister_hw(struct ieee80211_hw *hw)
|
void ieee80211_unregister_hw(struct ieee80211_hw *hw)
|
||||||
{
|
{
|
||||||
struct ieee80211_local *local = hw_to_local(hw);
|
struct ieee80211_local *local = hw_to_local(hw);
|
||||||
|
@ -1173,6 +1164,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
|
||||||
|
|
||||||
cancel_work_sync(&local->restart_work);
|
cancel_work_sync(&local->restart_work);
|
||||||
cancel_work_sync(&local->reconfig_filter);
|
cancel_work_sync(&local->reconfig_filter);
|
||||||
|
cancel_work_sync(&local->tdls_chsw_work);
|
||||||
flush_work(&local->sched_scan_stopped_work);
|
flush_work(&local->sched_scan_stopped_work);
|
||||||
|
|
||||||
ieee80211_clear_tx_pending(local);
|
ieee80211_clear_tx_pending(local);
|
||||||
|
@ -1183,6 +1175,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
|
||||||
wiphy_warn(local->hw.wiphy, "skb_queue not empty\n");
|
wiphy_warn(local->hw.wiphy, "skb_queue not empty\n");
|
||||||
skb_queue_purge(&local->skb_queue);
|
skb_queue_purge(&local->skb_queue);
|
||||||
skb_queue_purge(&local->skb_queue_unreliable);
|
skb_queue_purge(&local->skb_queue_unreliable);
|
||||||
|
skb_queue_purge(&local->skb_queue_tdls_chsw);
|
||||||
|
|
||||||
destroy_workqueue(local->workqueue);
|
destroy_workqueue(local->workqueue);
|
||||||
wiphy_unregister(local->hw.wiphy);
|
wiphy_unregister(local->hw.wiphy);
|
||||||
|
|
|
@ -158,7 +158,7 @@ void mesh_sta_cleanup(struct sta_info *sta)
|
||||||
changed = mesh_accept_plinks_update(sdata);
|
changed = mesh_accept_plinks_update(sdata);
|
||||||
if (!sdata->u.mesh.user_mpm) {
|
if (!sdata->u.mesh.user_mpm) {
|
||||||
changed |= mesh_plink_deactivate(sta);
|
changed |= mesh_plink_deactivate(sta);
|
||||||
del_timer_sync(&sta->plink_timer);
|
del_timer_sync(&sta->mesh->plink_timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed)
|
if (changed)
|
||||||
|
|
|
@ -19,15 +19,6 @@
|
||||||
|
|
||||||
#define MAX_PREQ_QUEUE_LEN 64
|
#define MAX_PREQ_QUEUE_LEN 64
|
||||||
|
|
||||||
/* Destination only */
|
|
||||||
#define MP_F_DO 0x1
|
|
||||||
/* Reply and forward */
|
|
||||||
#define MP_F_RF 0x2
|
|
||||||
/* Unknown Sequence Number */
|
|
||||||
#define MP_F_USN 0x01
|
|
||||||
/* Reason code Present */
|
|
||||||
#define MP_F_RCODE 0x02
|
|
||||||
|
|
||||||
static void mesh_queue_preq(struct mesh_path *, u8);
|
static void mesh_queue_preq(struct mesh_path *, u8);
|
||||||
|
|
||||||
static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae)
|
static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae)
|
||||||
|
@ -79,6 +70,12 @@ static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)
|
||||||
#define MSEC_TO_TU(x) (x*1000/1024)
|
#define MSEC_TO_TU(x) (x*1000/1024)
|
||||||
#define SN_GT(x, y) ((s32)(y - x) < 0)
|
#define SN_GT(x, y) ((s32)(y - x) < 0)
|
||||||
#define SN_LT(x, y) ((s32)(x - y) < 0)
|
#define SN_LT(x, y) ((s32)(x - y) < 0)
|
||||||
|
#define MAX_SANE_SN_DELTA 32
|
||||||
|
|
||||||
|
static inline u32 SN_DELTA(u32 x, u32 y)
|
||||||
|
{
|
||||||
|
return x >= y ? x - y : y - x;
|
||||||
|
}
|
||||||
|
|
||||||
#define net_traversal_jiffies(s) \
|
#define net_traversal_jiffies(s) \
|
||||||
msecs_to_jiffies(s->u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime)
|
msecs_to_jiffies(s->u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime)
|
||||||
|
@ -279,15 +276,10 @@ int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,
|
||||||
*pos++ = ttl;
|
*pos++ = ttl;
|
||||||
/* number of destinations */
|
/* number of destinations */
|
||||||
*pos++ = 1;
|
*pos++ = 1;
|
||||||
/*
|
/* Flags field has AE bit only as defined in
|
||||||
* flags bit, bit 1 is unset if we know the sequence number and
|
* sec 8.4.2.117 IEEE802.11-2012
|
||||||
* bit 2 is set if we have a reason code
|
|
||||||
*/
|
*/
|
||||||
*pos = 0;
|
*pos = 0;
|
||||||
if (!target_sn)
|
|
||||||
*pos |= MP_F_USN;
|
|
||||||
if (target_rcode)
|
|
||||||
*pos |= MP_F_RCODE;
|
|
||||||
pos++;
|
pos++;
|
||||||
memcpy(pos, target, ETH_ALEN);
|
memcpy(pos, target, ETH_ALEN);
|
||||||
pos += ETH_ALEN;
|
pos += ETH_ALEN;
|
||||||
|
@ -316,8 +308,9 @@ void ieee80211s_update_metric(struct ieee80211_local *local,
|
||||||
failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK);
|
failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK);
|
||||||
|
|
||||||
/* moving average, scaled to 100 */
|
/* moving average, scaled to 100 */
|
||||||
sta->fail_avg = ((80 * sta->fail_avg + 5) / 100 + 20 * failed);
|
sta->mesh->fail_avg =
|
||||||
if (sta->fail_avg > 95)
|
((80 * sta->mesh->fail_avg + 5) / 100 + 20 * failed);
|
||||||
|
if (sta->mesh->fail_avg > 95)
|
||||||
mesh_plink_broken(sta);
|
mesh_plink_broken(sta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +326,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
|
||||||
u32 tx_time, estimated_retx;
|
u32 tx_time, estimated_retx;
|
||||||
u64 result;
|
u64 result;
|
||||||
|
|
||||||
if (sta->fail_avg >= 100)
|
if (sta->mesh->fail_avg >= 100)
|
||||||
return MAX_METRIC;
|
return MAX_METRIC;
|
||||||
|
|
||||||
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo);
|
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo);
|
||||||
|
@ -341,7 +334,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
|
||||||
if (WARN_ON(!rate))
|
if (WARN_ON(!rate))
|
||||||
return MAX_METRIC;
|
return MAX_METRIC;
|
||||||
|
|
||||||
err = (sta->fail_avg << ARITH_SHIFT) / 100;
|
err = (sta->mesh->fail_avg << ARITH_SHIFT) / 100;
|
||||||
|
|
||||||
/* bitrate is in units of 100 Kbps, while we need rate in units of
|
/* bitrate is in units of 100 Kbps, while we need rate in units of
|
||||||
* 1Mbps. This will be corrected on tx_time computation.
|
* 1Mbps. This will be corrected on tx_time computation.
|
||||||
|
@ -441,6 +434,26 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
|
||||||
process = false;
|
process = false;
|
||||||
fresh_info = false;
|
fresh_info = false;
|
||||||
}
|
}
|
||||||
|
} else if (!(mpath->flags & MESH_PATH_ACTIVE)) {
|
||||||
|
bool have_sn, newer_sn, bounced;
|
||||||
|
|
||||||
|
have_sn = mpath->flags & MESH_PATH_SN_VALID;
|
||||||
|
newer_sn = have_sn && SN_GT(orig_sn, mpath->sn);
|
||||||
|
bounced = have_sn &&
|
||||||
|
(SN_DELTA(orig_sn, mpath->sn) >
|
||||||
|
MAX_SANE_SN_DELTA);
|
||||||
|
|
||||||
|
if (!have_sn || newer_sn) {
|
||||||
|
/* if SN is newer than what we had
|
||||||
|
* then we can take it */;
|
||||||
|
} else if (bounced) {
|
||||||
|
/* if SN is way different than what
|
||||||
|
* we had then assume the other side
|
||||||
|
* rebooted or restarted */;
|
||||||
|
} else {
|
||||||
|
process = false;
|
||||||
|
fresh_info = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mpath = mesh_path_add(sdata, orig_addr);
|
mpath = mesh_path_add(sdata, orig_addr);
|
||||||
|
@ -570,15 +583,13 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
|
||||||
SN_LT(mpath->sn, target_sn)) {
|
SN_LT(mpath->sn, target_sn)) {
|
||||||
mpath->sn = target_sn;
|
mpath->sn = target_sn;
|
||||||
mpath->flags |= MESH_PATH_SN_VALID;
|
mpath->flags |= MESH_PATH_SN_VALID;
|
||||||
} else if ((!(target_flags & MP_F_DO)) &&
|
} else if ((!(target_flags & IEEE80211_PREQ_TO_FLAG)) &&
|
||||||
(mpath->flags & MESH_PATH_ACTIVE)) {
|
(mpath->flags & MESH_PATH_ACTIVE)) {
|
||||||
reply = true;
|
reply = true;
|
||||||
target_metric = mpath->metric;
|
target_metric = mpath->metric;
|
||||||
target_sn = mpath->sn;
|
target_sn = mpath->sn;
|
||||||
if (target_flags & MP_F_RF)
|
/* Case E2 of sec 13.10.9.3 IEEE 802.11-2012*/
|
||||||
target_flags |= MP_F_DO;
|
target_flags |= IEEE80211_PREQ_TO_FLAG;
|
||||||
else
|
|
||||||
forward = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
@ -736,9 +747,12 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
|
||||||
if (mpath->flags & MESH_PATH_ACTIVE &&
|
if (mpath->flags & MESH_PATH_ACTIVE &&
|
||||||
ether_addr_equal(ta, sta->sta.addr) &&
|
ether_addr_equal(ta, sta->sta.addr) &&
|
||||||
(!(mpath->flags & MESH_PATH_SN_VALID) ||
|
(!(mpath->flags & MESH_PATH_SN_VALID) ||
|
||||||
SN_GT(target_sn, mpath->sn))) {
|
SN_GT(target_sn, mpath->sn) || target_sn == 0)) {
|
||||||
mpath->flags &= ~MESH_PATH_ACTIVE;
|
mpath->flags &= ~MESH_PATH_ACTIVE;
|
||||||
mpath->sn = target_sn;
|
if (target_sn != 0)
|
||||||
|
mpath->sn = target_sn;
|
||||||
|
else
|
||||||
|
mpath->sn += 1;
|
||||||
spin_unlock_bh(&mpath->state_lock);
|
spin_unlock_bh(&mpath->state_lock);
|
||||||
if (!ifmsh->mshcfg.dot11MeshForwarding)
|
if (!ifmsh->mshcfg.dot11MeshForwarding)
|
||||||
goto endperr;
|
goto endperr;
|
||||||
|
@ -862,7 +876,7 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
sta = sta_info_get(sdata, mgmt->sa);
|
sta = sta_info_get(sdata, mgmt->sa);
|
||||||
if (!sta || sta->plink_state != NL80211_PLINK_ESTAB) {
|
if (!sta || sta->mesh->plink_state != NL80211_PLINK_ESTAB) {
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -974,7 +988,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
|
||||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||||
struct mesh_preq_queue *preq_node;
|
struct mesh_preq_queue *preq_node;
|
||||||
struct mesh_path *mpath;
|
struct mesh_path *mpath;
|
||||||
u8 ttl, target_flags;
|
u8 ttl, target_flags = 0;
|
||||||
const u8 *da;
|
const u8 *da;
|
||||||
u32 lifetime;
|
u32 lifetime;
|
||||||
|
|
||||||
|
@ -1033,9 +1047,9 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preq_node->flags & PREQ_Q_F_REFRESH)
|
if (preq_node->flags & PREQ_Q_F_REFRESH)
|
||||||
target_flags = MP_F_DO;
|
target_flags |= IEEE80211_PREQ_TO_FLAG;
|
||||||
else
|
else
|
||||||
target_flags = MP_F_RF;
|
target_flags &= ~IEEE80211_PREQ_TO_FLAG;
|
||||||
|
|
||||||
spin_unlock_bh(&mpath->state_lock);
|
spin_unlock_bh(&mpath->state_lock);
|
||||||
da = (mpath->is_root) ? mpath->rann_snd_addr : broadcast_addr;
|
da = (mpath->is_root) ? mpath->rann_snd_addr : broadcast_addr;
|
||||||
|
@ -1176,7 +1190,9 @@ void mesh_path_timer(unsigned long data)
|
||||||
spin_unlock_bh(&mpath->state_lock);
|
spin_unlock_bh(&mpath->state_lock);
|
||||||
mesh_queue_preq(mpath, 0);
|
mesh_queue_preq(mpath, 0);
|
||||||
} else {
|
} else {
|
||||||
mpath->flags = 0;
|
mpath->flags &= ~(MESH_PATH_RESOLVING |
|
||||||
|
MESH_PATH_RESOLVED |
|
||||||
|
MESH_PATH_REQ_QUEUED);
|
||||||
mpath->exp_time = jiffies;
|
mpath->exp_time = jiffies;
|
||||||
spin_unlock_bh(&mpath->state_lock);
|
spin_unlock_bh(&mpath->state_lock);
|
||||||
if (!mpath->is_gate && mesh_gate_num(sdata) > 0) {
|
if (!mpath->is_gate && mesh_gate_num(sdata) > 0) {
|
||||||
|
|
|
@ -13,10 +13,11 @@
|
||||||
#include "rate.h"
|
#include "rate.h"
|
||||||
#include "mesh.h"
|
#include "mesh.h"
|
||||||
|
|
||||||
|
#define PLINK_CNF_AID(mgmt) ((mgmt)->u.action.u.self_prot.variable + 2)
|
||||||
#define PLINK_GET_LLID(p) (p + 2)
|
#define PLINK_GET_LLID(p) (p + 2)
|
||||||
#define PLINK_GET_PLID(p) (p + 4)
|
#define PLINK_GET_PLID(p) (p + 4)
|
||||||
|
|
||||||
#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
|
#define mod_plink_timer(s, t) (mod_timer(&s->mesh->plink_timer, \
|
||||||
jiffies + msecs_to_jiffies(t)))
|
jiffies + msecs_to_jiffies(t)))
|
||||||
|
|
||||||
enum plink_event {
|
enum plink_event {
|
||||||
|
@ -53,18 +54,13 @@ static const char * const mplevents[] = {
|
||||||
[CLS_IGNR] = "CLS_IGNR"
|
[CLS_IGNR] = "CLS_IGNR"
|
||||||
};
|
};
|
||||||
|
|
||||||
static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
|
|
||||||
enum ieee80211_self_protected_actioncode action,
|
|
||||||
u8 *da, u16 llid, u16 plid, u16 reason);
|
|
||||||
|
|
||||||
|
|
||||||
/* We only need a valid sta if user configured a minimum rssi_threshold. */
|
/* We only need a valid sta if user configured a minimum rssi_threshold. */
|
||||||
static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata,
|
static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata,
|
||||||
struct sta_info *sta)
|
struct sta_info *sta)
|
||||||
{
|
{
|
||||||
s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold;
|
s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold;
|
||||||
return rssi_threshold == 0 ||
|
return rssi_threshold == 0 ||
|
||||||
(sta && (s8) -ewma_read(&sta->avg_signal) > rssi_threshold);
|
(sta && (s8) -ewma_signal_read(&sta->avg_signal) > rssi_threshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,14 +68,14 @@ static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata,
|
||||||
*
|
*
|
||||||
* @sta: mesh peer link to restart
|
* @sta: mesh peer link to restart
|
||||||
*
|
*
|
||||||
* Locking: this function must be called holding sta->plink_lock
|
* Locking: this function must be called holding sta->mesh->plink_lock
|
||||||
*/
|
*/
|
||||||
static inline void mesh_plink_fsm_restart(struct sta_info *sta)
|
static inline void mesh_plink_fsm_restart(struct sta_info *sta)
|
||||||
{
|
{
|
||||||
lockdep_assert_held(&sta->plink_lock);
|
lockdep_assert_held(&sta->mesh->plink_lock);
|
||||||
sta->plink_state = NL80211_PLINK_LISTEN;
|
sta->mesh->plink_state = NL80211_PLINK_LISTEN;
|
||||||
sta->llid = sta->plid = sta->reason = 0;
|
sta->mesh->llid = sta->mesh->plid = sta->mesh->reason = 0;
|
||||||
sta->plink_retries = 0;
|
sta->mesh->plink_retries = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -119,7 +115,7 @@ static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata)
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||||
if (sdata != sta->sdata ||
|
if (sdata != sta->sdata ||
|
||||||
sta->plink_state != NL80211_PLINK_ESTAB)
|
sta->mesh->plink_state != NL80211_PLINK_ESTAB)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
short_slot = false;
|
short_slot = false;
|
||||||
|
@ -169,7 +165,7 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||||
if (sdata != sta->sdata ||
|
if (sdata != sta->sdata ||
|
||||||
sta->plink_state != NL80211_PLINK_ESTAB)
|
sta->mesh->plink_state != NL80211_PLINK_ESTAB)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (sta->sta.bandwidth > IEEE80211_STA_RX_BW_20)
|
if (sta->sta.bandwidth > IEEE80211_STA_RX_BW_20)
|
||||||
|
@ -204,59 +200,8 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
|
||||||
return BSS_CHANGED_HT;
|
return BSS_CHANGED_HT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* __mesh_plink_deactivate - deactivate mesh peer link
|
|
||||||
*
|
|
||||||
* @sta: mesh peer link to deactivate
|
|
||||||
*
|
|
||||||
* All mesh paths with this peer as next hop will be flushed
|
|
||||||
* Returns beacon changed flag if the beacon content changed.
|
|
||||||
*
|
|
||||||
* Locking: the caller must hold sta->plink_lock
|
|
||||||
*/
|
|
||||||
static u32 __mesh_plink_deactivate(struct sta_info *sta)
|
|
||||||
{
|
|
||||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
|
||||||
u32 changed = 0;
|
|
||||||
|
|
||||||
lockdep_assert_held(&sta->plink_lock);
|
|
||||||
|
|
||||||
if (sta->plink_state == NL80211_PLINK_ESTAB)
|
|
||||||
changed = mesh_plink_dec_estab_count(sdata);
|
|
||||||
sta->plink_state = NL80211_PLINK_BLOCKED;
|
|
||||||
mesh_path_flush_by_nexthop(sta);
|
|
||||||
|
|
||||||
ieee80211_mps_sta_status_update(sta);
|
|
||||||
changed |= ieee80211_mps_set_sta_local_pm(sta,
|
|
||||||
NL80211_MESH_POWER_UNKNOWN);
|
|
||||||
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mesh_plink_deactivate - deactivate mesh peer link
|
|
||||||
*
|
|
||||||
* @sta: mesh peer link to deactivate
|
|
||||||
*
|
|
||||||
* All mesh paths with this peer as next hop will be flushed
|
|
||||||
*/
|
|
||||||
u32 mesh_plink_deactivate(struct sta_info *sta)
|
|
||||||
{
|
|
||||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
|
||||||
u32 changed;
|
|
||||||
|
|
||||||
spin_lock_bh(&sta->plink_lock);
|
|
||||||
changed = __mesh_plink_deactivate(sta);
|
|
||||||
sta->reason = WLAN_REASON_MESH_PEER_CANCELED;
|
|
||||||
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
|
|
||||||
sta->sta.addr, sta->llid, sta->plid,
|
|
||||||
sta->reason);
|
|
||||||
spin_unlock_bh(&sta->plink_lock);
|
|
||||||
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
|
static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct sta_info *sta,
|
||||||
enum ieee80211_self_protected_actioncode action,
|
enum ieee80211_self_protected_actioncode action,
|
||||||
u8 *da, u16 llid, u16 plid, u16 reason)
|
u8 *da, u16 llid, u16 plid, u16 reason)
|
||||||
{
|
{
|
||||||
|
@ -306,7 +251,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
|
||||||
if (action == WLAN_SP_MESH_PEERING_CONFIRM) {
|
if (action == WLAN_SP_MESH_PEERING_CONFIRM) {
|
||||||
/* AID */
|
/* AID */
|
||||||
pos = skb_put(skb, 2);
|
pos = skb_put(skb, 2);
|
||||||
put_unaligned_le16(plid, pos);
|
put_unaligned_le16(sta->sta.aid, pos);
|
||||||
}
|
}
|
||||||
if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
|
if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
|
||||||
ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
|
ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
|
||||||
|
@ -375,6 +320,58 @@ free:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __mesh_plink_deactivate - deactivate mesh peer link
|
||||||
|
*
|
||||||
|
* @sta: mesh peer link to deactivate
|
||||||
|
*
|
||||||
|
* All mesh paths with this peer as next hop will be flushed
|
||||||
|
* Returns beacon changed flag if the beacon content changed.
|
||||||
|
*
|
||||||
|
* Locking: the caller must hold sta->mesh->plink_lock
|
||||||
|
*/
|
||||||
|
static u32 __mesh_plink_deactivate(struct sta_info *sta)
|
||||||
|
{
|
||||||
|
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||||
|
u32 changed = 0;
|
||||||
|
|
||||||
|
lockdep_assert_held(&sta->mesh->plink_lock);
|
||||||
|
|
||||||
|
if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
|
||||||
|
changed = mesh_plink_dec_estab_count(sdata);
|
||||||
|
sta->mesh->plink_state = NL80211_PLINK_BLOCKED;
|
||||||
|
mesh_path_flush_by_nexthop(sta);
|
||||||
|
|
||||||
|
ieee80211_mps_sta_status_update(sta);
|
||||||
|
changed |= ieee80211_mps_set_sta_local_pm(sta,
|
||||||
|
NL80211_MESH_POWER_UNKNOWN);
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mesh_plink_deactivate - deactivate mesh peer link
|
||||||
|
*
|
||||||
|
* @sta: mesh peer link to deactivate
|
||||||
|
*
|
||||||
|
* All mesh paths with this peer as next hop will be flushed
|
||||||
|
*/
|
||||||
|
u32 mesh_plink_deactivate(struct sta_info *sta)
|
||||||
|
{
|
||||||
|
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||||
|
u32 changed;
|
||||||
|
|
||||||
|
spin_lock_bh(&sta->mesh->plink_lock);
|
||||||
|
changed = __mesh_plink_deactivate(sta);
|
||||||
|
sta->mesh->reason = WLAN_REASON_MESH_PEER_CANCELED;
|
||||||
|
mesh_plink_frame_tx(sdata, sta, WLAN_SP_MESH_PEERING_CLOSE,
|
||||||
|
sta->sta.addr, sta->mesh->llid, sta->mesh->plid,
|
||||||
|
sta->mesh->reason);
|
||||||
|
spin_unlock_bh(&sta->mesh->plink_lock);
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
|
static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
|
||||||
struct sta_info *sta,
|
struct sta_info *sta,
|
||||||
struct ieee802_11_elems *elems, bool insert)
|
struct ieee802_11_elems *elems, bool insert)
|
||||||
|
@ -388,13 +385,14 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
|
||||||
sband = local->hw.wiphy->bands[band];
|
sband = local->hw.wiphy->bands[band];
|
||||||
rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
|
rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
|
||||||
|
|
||||||
spin_lock_bh(&sta->plink_lock);
|
spin_lock_bh(&sta->mesh->plink_lock);
|
||||||
sta->last_rx = jiffies;
|
sta->last_rx = jiffies;
|
||||||
|
|
||||||
/* rates and capabilities don't change during peering */
|
/* rates and capabilities don't change during peering */
|
||||||
if (sta->plink_state == NL80211_PLINK_ESTAB && sta->processed_beacon)
|
if (sta->mesh->plink_state == NL80211_PLINK_ESTAB &&
|
||||||
|
sta->mesh->processed_beacon)
|
||||||
goto out;
|
goto out;
|
||||||
sta->processed_beacon = true;
|
sta->mesh->processed_beacon = true;
|
||||||
|
|
||||||
if (sta->sta.supp_rates[band] != rates)
|
if (sta->sta.supp_rates[band] != rates)
|
||||||
changed |= IEEE80211_RC_SUPP_RATES_CHANGED;
|
changed |= IEEE80211_RC_SUPP_RATES_CHANGED;
|
||||||
|
@ -421,23 +419,57 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
|
||||||
else
|
else
|
||||||
rate_control_rate_update(local, sband, sta, changed);
|
rate_control_rate_update(local, sband, sta, changed);
|
||||||
out:
|
out:
|
||||||
spin_unlock_bh(&sta->plink_lock);
|
spin_unlock_bh(&sta->mesh->plink_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mesh_allocate_aid(struct ieee80211_sub_if_data *sdata)
|
||||||
|
{
|
||||||
|
struct sta_info *sta;
|
||||||
|
unsigned long *aid_map;
|
||||||
|
int aid;
|
||||||
|
|
||||||
|
aid_map = kcalloc(BITS_TO_LONGS(IEEE80211_MAX_AID + 1),
|
||||||
|
sizeof(*aid_map), GFP_KERNEL);
|
||||||
|
if (!aid_map)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* reserve aid 0 for mcast indication */
|
||||||
|
__set_bit(0, aid_map);
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
list_for_each_entry_rcu(sta, &sdata->local->sta_list, list)
|
||||||
|
__set_bit(sta->sta.aid, aid_map);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
aid = find_first_zero_bit(aid_map, IEEE80211_MAX_AID + 1);
|
||||||
|
kfree(aid_map);
|
||||||
|
|
||||||
|
if (aid > IEEE80211_MAX_AID)
|
||||||
|
return -ENOBUFS;
|
||||||
|
|
||||||
|
return aid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sta_info *
|
static struct sta_info *
|
||||||
__mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
|
__mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
|
||||||
{
|
{
|
||||||
struct sta_info *sta;
|
struct sta_info *sta;
|
||||||
|
int aid;
|
||||||
|
|
||||||
if (sdata->local->num_sta >= MESH_MAX_PLINKS)
|
if (sdata->local->num_sta >= MESH_MAX_PLINKS)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
aid = mesh_allocate_aid(sdata);
|
||||||
|
if (aid < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
|
sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
|
||||||
if (!sta)
|
if (!sta)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
sta->plink_state = NL80211_PLINK_LISTEN;
|
sta->mesh->plink_state = NL80211_PLINK_LISTEN;
|
||||||
sta->sta.wme = true;
|
sta->sta.wme = true;
|
||||||
|
sta->sta.aid = aid;
|
||||||
|
|
||||||
sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
|
sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
|
||||||
sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
|
sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
|
||||||
|
@ -524,7 +556,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (mesh_peer_accepts_plinks(elems) &&
|
if (mesh_peer_accepts_plinks(elems) &&
|
||||||
sta->plink_state == NL80211_PLINK_LISTEN &&
|
sta->mesh->plink_state == NL80211_PLINK_LISTEN &&
|
||||||
sdata->u.mesh.accepting_plinks &&
|
sdata->u.mesh.accepting_plinks &&
|
||||||
sdata->u.mesh.mshcfg.auto_open_plinks &&
|
sdata->u.mesh.mshcfg.auto_open_plinks &&
|
||||||
rssi_threshold_check(sdata, sta))
|
rssi_threshold_check(sdata, sta))
|
||||||
|
@ -554,52 +586,52 @@ static void mesh_plink_timer(unsigned long data)
|
||||||
if (sta->sdata->local->quiescing)
|
if (sta->sdata->local->quiescing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
spin_lock_bh(&sta->plink_lock);
|
spin_lock_bh(&sta->mesh->plink_lock);
|
||||||
|
|
||||||
/* If a timer fires just before a state transition on another CPU,
|
/* If a timer fires just before a state transition on another CPU,
|
||||||
* we may have already extended the timeout and changed state by the
|
* we may have already extended the timeout and changed state by the
|
||||||
* time we've acquired the lock and arrived here. In that case,
|
* time we've acquired the lock and arrived here. In that case,
|
||||||
* skip this timer and wait for the new one.
|
* skip this timer and wait for the new one.
|
||||||
*/
|
*/
|
||||||
if (time_before(jiffies, sta->plink_timer.expires)) {
|
if (time_before(jiffies, sta->mesh->plink_timer.expires)) {
|
||||||
mpl_dbg(sta->sdata,
|
mpl_dbg(sta->sdata,
|
||||||
"Ignoring timer for %pM in state %s (timer adjusted)",
|
"Ignoring timer for %pM in state %s (timer adjusted)",
|
||||||
sta->sta.addr, mplstates[sta->plink_state]);
|
sta->sta.addr, mplstates[sta->mesh->plink_state]);
|
||||||
spin_unlock_bh(&sta->plink_lock);
|
spin_unlock_bh(&sta->mesh->plink_lock);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* del_timer() and handler may race when entering these states */
|
/* del_timer() and handler may race when entering these states */
|
||||||
if (sta->plink_state == NL80211_PLINK_LISTEN ||
|
if (sta->mesh->plink_state == NL80211_PLINK_LISTEN ||
|
||||||
sta->plink_state == NL80211_PLINK_ESTAB) {
|
sta->mesh->plink_state == NL80211_PLINK_ESTAB) {
|
||||||
mpl_dbg(sta->sdata,
|
mpl_dbg(sta->sdata,
|
||||||
"Ignoring timer for %pM in state %s (timer deleted)",
|
"Ignoring timer for %pM in state %s (timer deleted)",
|
||||||
sta->sta.addr, mplstates[sta->plink_state]);
|
sta->sta.addr, mplstates[sta->mesh->plink_state]);
|
||||||
spin_unlock_bh(&sta->plink_lock);
|
spin_unlock_bh(&sta->mesh->plink_lock);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mpl_dbg(sta->sdata,
|
mpl_dbg(sta->sdata,
|
||||||
"Mesh plink timer for %pM fired on state %s\n",
|
"Mesh plink timer for %pM fired on state %s\n",
|
||||||
sta->sta.addr, mplstates[sta->plink_state]);
|
sta->sta.addr, mplstates[sta->mesh->plink_state]);
|
||||||
sdata = sta->sdata;
|
sdata = sta->sdata;
|
||||||
mshcfg = &sdata->u.mesh.mshcfg;
|
mshcfg = &sdata->u.mesh.mshcfg;
|
||||||
|
|
||||||
switch (sta->plink_state) {
|
switch (sta->mesh->plink_state) {
|
||||||
case NL80211_PLINK_OPN_RCVD:
|
case NL80211_PLINK_OPN_RCVD:
|
||||||
case NL80211_PLINK_OPN_SNT:
|
case NL80211_PLINK_OPN_SNT:
|
||||||
/* retry timer */
|
/* retry timer */
|
||||||
if (sta->plink_retries < mshcfg->dot11MeshMaxRetries) {
|
if (sta->mesh->plink_retries < mshcfg->dot11MeshMaxRetries) {
|
||||||
u32 rand;
|
u32 rand;
|
||||||
mpl_dbg(sta->sdata,
|
mpl_dbg(sta->sdata,
|
||||||
"Mesh plink for %pM (retry, timeout): %d %d\n",
|
"Mesh plink for %pM (retry, timeout): %d %d\n",
|
||||||
sta->sta.addr, sta->plink_retries,
|
sta->sta.addr, sta->mesh->plink_retries,
|
||||||
sta->plink_timeout);
|
sta->mesh->plink_timeout);
|
||||||
get_random_bytes(&rand, sizeof(u32));
|
get_random_bytes(&rand, sizeof(u32));
|
||||||
sta->plink_timeout = sta->plink_timeout +
|
sta->mesh->plink_timeout = sta->mesh->plink_timeout +
|
||||||
rand % sta->plink_timeout;
|
rand % sta->mesh->plink_timeout;
|
||||||
++sta->plink_retries;
|
++sta->mesh->plink_retries;
|
||||||
mod_plink_timer(sta, sta->plink_timeout);
|
mod_plink_timer(sta, sta->mesh->plink_timeout);
|
||||||
action = WLAN_SP_MESH_PEERING_OPEN;
|
action = WLAN_SP_MESH_PEERING_OPEN;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -609,31 +641,31 @@ static void mesh_plink_timer(unsigned long data)
|
||||||
/* confirm timer */
|
/* confirm timer */
|
||||||
if (!reason)
|
if (!reason)
|
||||||
reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT;
|
reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT;
|
||||||
sta->plink_state = NL80211_PLINK_HOLDING;
|
sta->mesh->plink_state = NL80211_PLINK_HOLDING;
|
||||||
mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
|
mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
|
||||||
action = WLAN_SP_MESH_PEERING_CLOSE;
|
action = WLAN_SP_MESH_PEERING_CLOSE;
|
||||||
break;
|
break;
|
||||||
case NL80211_PLINK_HOLDING:
|
case NL80211_PLINK_HOLDING:
|
||||||
/* holding timer */
|
/* holding timer */
|
||||||
del_timer(&sta->plink_timer);
|
del_timer(&sta->mesh->plink_timer);
|
||||||
mesh_plink_fsm_restart(sta);
|
mesh_plink_fsm_restart(sta);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&sta->plink_lock);
|
spin_unlock_bh(&sta->mesh->plink_lock);
|
||||||
if (action)
|
if (action)
|
||||||
mesh_plink_frame_tx(sdata, action, sta->sta.addr,
|
mesh_plink_frame_tx(sdata, sta, action, sta->sta.addr,
|
||||||
sta->llid, sta->plid, reason);
|
sta->mesh->llid, sta->mesh->plid, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void mesh_plink_timer_set(struct sta_info *sta, u32 timeout)
|
static inline void mesh_plink_timer_set(struct sta_info *sta, u32 timeout)
|
||||||
{
|
{
|
||||||
sta->plink_timer.expires = jiffies + msecs_to_jiffies(timeout);
|
sta->mesh->plink_timer.expires = jiffies + msecs_to_jiffies(timeout);
|
||||||
sta->plink_timer.data = (unsigned long) sta;
|
sta->mesh->plink_timer.data = (unsigned long) sta;
|
||||||
sta->plink_timer.function = mesh_plink_timer;
|
sta->mesh->plink_timer.function = mesh_plink_timer;
|
||||||
sta->plink_timeout = timeout;
|
sta->mesh->plink_timeout = timeout;
|
||||||
add_timer(&sta->plink_timer);
|
add_timer(&sta->mesh->plink_timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool llid_in_use(struct ieee80211_sub_if_data *sdata,
|
static bool llid_in_use(struct ieee80211_sub_if_data *sdata,
|
||||||
|
@ -645,7 +677,7 @@ static bool llid_in_use(struct ieee80211_sub_if_data *sdata,
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||||
if (!memcmp(&sta->llid, &llid, sizeof(llid))) {
|
if (!memcmp(&sta->mesh->llid, &llid, sizeof(llid))) {
|
||||||
in_use = true;
|
in_use = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -661,8 +693,6 @@ static u16 mesh_get_new_llid(struct ieee80211_sub_if_data *sdata)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
get_random_bytes(&llid, sizeof(llid));
|
get_random_bytes(&llid, sizeof(llid));
|
||||||
/* for mesh PS we still only have the AID range for TIM bits */
|
|
||||||
llid = (llid % IEEE80211_MAX_AID) + 1;
|
|
||||||
} while (llid_in_use(sdata, llid));
|
} while (llid_in_use(sdata, llid));
|
||||||
|
|
||||||
return llid;
|
return llid;
|
||||||
|
@ -676,16 +706,16 @@ u32 mesh_plink_open(struct sta_info *sta)
|
||||||
if (!test_sta_flag(sta, WLAN_STA_AUTH))
|
if (!test_sta_flag(sta, WLAN_STA_AUTH))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
spin_lock_bh(&sta->plink_lock);
|
spin_lock_bh(&sta->mesh->plink_lock);
|
||||||
sta->llid = mesh_get_new_llid(sdata);
|
sta->mesh->llid = mesh_get_new_llid(sdata);
|
||||||
if (sta->plink_state != NL80211_PLINK_LISTEN &&
|
if (sta->mesh->plink_state != NL80211_PLINK_LISTEN &&
|
||||||
sta->plink_state != NL80211_PLINK_BLOCKED) {
|
sta->mesh->plink_state != NL80211_PLINK_BLOCKED) {
|
||||||
spin_unlock_bh(&sta->plink_lock);
|
spin_unlock_bh(&sta->mesh->plink_lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
sta->plink_state = NL80211_PLINK_OPN_SNT;
|
sta->mesh->plink_state = NL80211_PLINK_OPN_SNT;
|
||||||
mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout);
|
mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout);
|
||||||
spin_unlock_bh(&sta->plink_lock);
|
spin_unlock_bh(&sta->mesh->plink_lock);
|
||||||
mpl_dbg(sdata,
|
mpl_dbg(sdata,
|
||||||
"Mesh plink: starting establishment with %pM\n",
|
"Mesh plink: starting establishment with %pM\n",
|
||||||
sta->sta.addr);
|
sta->sta.addr);
|
||||||
|
@ -693,8 +723,8 @@ u32 mesh_plink_open(struct sta_info *sta)
|
||||||
/* set the non-peer mode to active during peering */
|
/* set the non-peer mode to active during peering */
|
||||||
changed = ieee80211_mps_local_status_update(sdata);
|
changed = ieee80211_mps_local_status_update(sdata);
|
||||||
|
|
||||||
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
|
mesh_plink_frame_tx(sdata, sta, WLAN_SP_MESH_PEERING_OPEN,
|
||||||
sta->sta.addr, sta->llid, 0, 0);
|
sta->sta.addr, sta->mesh->llid, 0, 0);
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -702,10 +732,10 @@ u32 mesh_plink_block(struct sta_info *sta)
|
||||||
{
|
{
|
||||||
u32 changed;
|
u32 changed;
|
||||||
|
|
||||||
spin_lock_bh(&sta->plink_lock);
|
spin_lock_bh(&sta->mesh->plink_lock);
|
||||||
changed = __mesh_plink_deactivate(sta);
|
changed = __mesh_plink_deactivate(sta);
|
||||||
sta->plink_state = NL80211_PLINK_BLOCKED;
|
sta->mesh->plink_state = NL80211_PLINK_BLOCKED;
|
||||||
spin_unlock_bh(&sta->plink_lock);
|
spin_unlock_bh(&sta->mesh->plink_lock);
|
||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
@ -715,12 +745,11 @@ static void mesh_plink_close(struct ieee80211_sub_if_data *sdata,
|
||||||
enum plink_event event)
|
enum plink_event event)
|
||||||
{
|
{
|
||||||
struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
|
struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
|
||||||
|
|
||||||
u16 reason = (event == CLS_ACPT) ?
|
u16 reason = (event == CLS_ACPT) ?
|
||||||
WLAN_REASON_MESH_CLOSE : WLAN_REASON_MESH_CONFIG;
|
WLAN_REASON_MESH_CLOSE : WLAN_REASON_MESH_CONFIG;
|
||||||
|
|
||||||
sta->reason = reason;
|
sta->mesh->reason = reason;
|
||||||
sta->plink_state = NL80211_PLINK_HOLDING;
|
sta->mesh->plink_state = NL80211_PLINK_HOLDING;
|
||||||
mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
|
mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -730,8 +759,8 @@ static u32 mesh_plink_establish(struct ieee80211_sub_if_data *sdata,
|
||||||
struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
|
struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
|
||||||
u32 changed = 0;
|
u32 changed = 0;
|
||||||
|
|
||||||
del_timer(&sta->plink_timer);
|
del_timer(&sta->mesh->plink_timer);
|
||||||
sta->plink_state = NL80211_PLINK_ESTAB;
|
sta->mesh->plink_state = NL80211_PLINK_ESTAB;
|
||||||
changed |= mesh_plink_inc_estab_count(sdata);
|
changed |= mesh_plink_inc_estab_count(sdata);
|
||||||
changed |= mesh_set_ht_prot_mode(sdata);
|
changed |= mesh_set_ht_prot_mode(sdata);
|
||||||
changed |= mesh_set_short_slot_time(sdata);
|
changed |= mesh_set_short_slot_time(sdata);
|
||||||
|
@ -758,18 +787,18 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
|
||||||
u32 changed = 0;
|
u32 changed = 0;
|
||||||
|
|
||||||
mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr,
|
mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr,
|
||||||
mplstates[sta->plink_state], mplevents[event]);
|
mplstates[sta->mesh->plink_state], mplevents[event]);
|
||||||
|
|
||||||
spin_lock_bh(&sta->plink_lock);
|
spin_lock_bh(&sta->mesh->plink_lock);
|
||||||
switch (sta->plink_state) {
|
switch (sta->mesh->plink_state) {
|
||||||
case NL80211_PLINK_LISTEN:
|
case NL80211_PLINK_LISTEN:
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case CLS_ACPT:
|
case CLS_ACPT:
|
||||||
mesh_plink_fsm_restart(sta);
|
mesh_plink_fsm_restart(sta);
|
||||||
break;
|
break;
|
||||||
case OPN_ACPT:
|
case OPN_ACPT:
|
||||||
sta->plink_state = NL80211_PLINK_OPN_RCVD;
|
sta->mesh->plink_state = NL80211_PLINK_OPN_RCVD;
|
||||||
sta->llid = mesh_get_new_llid(sdata);
|
sta->mesh->llid = mesh_get_new_llid(sdata);
|
||||||
mesh_plink_timer_set(sta,
|
mesh_plink_timer_set(sta,
|
||||||
mshcfg->dot11MeshRetryTimeout);
|
mshcfg->dot11MeshRetryTimeout);
|
||||||
|
|
||||||
|
@ -791,11 +820,11 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
|
||||||
break;
|
break;
|
||||||
case OPN_ACPT:
|
case OPN_ACPT:
|
||||||
/* retry timer is left untouched */
|
/* retry timer is left untouched */
|
||||||
sta->plink_state = NL80211_PLINK_OPN_RCVD;
|
sta->mesh->plink_state = NL80211_PLINK_OPN_RCVD;
|
||||||
action = WLAN_SP_MESH_PEERING_CONFIRM;
|
action = WLAN_SP_MESH_PEERING_CONFIRM;
|
||||||
break;
|
break;
|
||||||
case CNF_ACPT:
|
case CNF_ACPT:
|
||||||
sta->plink_state = NL80211_PLINK_CNF_RCVD;
|
sta->mesh->plink_state = NL80211_PLINK_CNF_RCVD;
|
||||||
mod_plink_timer(sta, mshcfg->dot11MeshConfirmTimeout);
|
mod_plink_timer(sta, mshcfg->dot11MeshConfirmTimeout);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -855,7 +884,7 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
|
||||||
case NL80211_PLINK_HOLDING:
|
case NL80211_PLINK_HOLDING:
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case CLS_ACPT:
|
case CLS_ACPT:
|
||||||
del_timer(&sta->plink_timer);
|
del_timer(&sta->mesh->plink_timer);
|
||||||
mesh_plink_fsm_restart(sta);
|
mesh_plink_fsm_restart(sta);
|
||||||
break;
|
break;
|
||||||
case OPN_ACPT:
|
case OPN_ACPT:
|
||||||
|
@ -874,17 +903,18 @@ static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
|
||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&sta->plink_lock);
|
spin_unlock_bh(&sta->mesh->plink_lock);
|
||||||
if (action) {
|
if (action) {
|
||||||
mesh_plink_frame_tx(sdata, action, sta->sta.addr,
|
mesh_plink_frame_tx(sdata, sta, action, sta->sta.addr,
|
||||||
sta->llid, sta->plid, sta->reason);
|
sta->mesh->llid, sta->mesh->plid,
|
||||||
|
sta->mesh->reason);
|
||||||
|
|
||||||
/* also send confirm in open case */
|
/* also send confirm in open case */
|
||||||
if (action == WLAN_SP_MESH_PEERING_OPEN) {
|
if (action == WLAN_SP_MESH_PEERING_OPEN) {
|
||||||
mesh_plink_frame_tx(sdata,
|
mesh_plink_frame_tx(sdata, sta,
|
||||||
WLAN_SP_MESH_PEERING_CONFIRM,
|
WLAN_SP_MESH_PEERING_CONFIRM,
|
||||||
sta->sta.addr, sta->llid,
|
sta->sta.addr, sta->mesh->llid,
|
||||||
sta->plid, 0);
|
sta->mesh->plid, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -939,7 +969,7 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
|
||||||
mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n");
|
mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (sta->plink_state == NL80211_PLINK_BLOCKED)
|
if (sta->mesh->plink_state == NL80211_PLINK_BLOCKED)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -954,7 +984,7 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
|
||||||
if (!matches_local)
|
if (!matches_local)
|
||||||
event = OPN_RJCT;
|
event = OPN_RJCT;
|
||||||
if (!mesh_plink_free_count(sdata) ||
|
if (!mesh_plink_free_count(sdata) ||
|
||||||
(sta->plid && sta->plid != plid))
|
(sta->mesh->plid && sta->mesh->plid != plid))
|
||||||
event = OPN_IGNR;
|
event = OPN_IGNR;
|
||||||
else
|
else
|
||||||
event = OPN_ACPT;
|
event = OPN_ACPT;
|
||||||
|
@ -963,14 +993,14 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
|
||||||
if (!matches_local)
|
if (!matches_local)
|
||||||
event = CNF_RJCT;
|
event = CNF_RJCT;
|
||||||
if (!mesh_plink_free_count(sdata) ||
|
if (!mesh_plink_free_count(sdata) ||
|
||||||
sta->llid != llid ||
|
sta->mesh->llid != llid ||
|
||||||
(sta->plid && sta->plid != plid))
|
(sta->mesh->plid && sta->mesh->plid != plid))
|
||||||
event = CNF_IGNR;
|
event = CNF_IGNR;
|
||||||
else
|
else
|
||||||
event = CNF_ACPT;
|
event = CNF_ACPT;
|
||||||
break;
|
break;
|
||||||
case WLAN_SP_MESH_PEERING_CLOSE:
|
case WLAN_SP_MESH_PEERING_CLOSE:
|
||||||
if (sta->plink_state == NL80211_PLINK_ESTAB)
|
if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
|
||||||
/* Do not check for llid or plid. This does not
|
/* Do not check for llid or plid. This does not
|
||||||
* follow the standard but since multiple plinks
|
* follow the standard but since multiple plinks
|
||||||
* per sta are not supported, it is necessary in
|
* per sta are not supported, it is necessary in
|
||||||
|
@ -981,9 +1011,9 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
|
||||||
* restarted.
|
* restarted.
|
||||||
*/
|
*/
|
||||||
event = CLS_ACPT;
|
event = CLS_ACPT;
|
||||||
else if (sta->plid != plid)
|
else if (sta->mesh->plid != plid)
|
||||||
event = CLS_IGNR;
|
event = CLS_IGNR;
|
||||||
else if (ie_len == 8 && sta->llid != llid)
|
else if (ie_len == 8 && sta->mesh->llid != llid)
|
||||||
event = CLS_IGNR;
|
event = CLS_IGNR;
|
||||||
else
|
else
|
||||||
event = CLS_ACPT;
|
event = CLS_ACPT;
|
||||||
|
@ -1070,9 +1100,9 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
|
||||||
mpl_dbg(sdata, "Mesh plink: failed to init peer!\n");
|
mpl_dbg(sdata, "Mesh plink: failed to init peer!\n");
|
||||||
goto unlock_rcu;
|
goto unlock_rcu;
|
||||||
}
|
}
|
||||||
sta->plid = plid;
|
sta->mesh->plid = plid;
|
||||||
} else if (!sta && event == OPN_RJCT) {
|
} else if (!sta && event == OPN_RJCT) {
|
||||||
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
|
mesh_plink_frame_tx(sdata, NULL, WLAN_SP_MESH_PEERING_CLOSE,
|
||||||
mgmt->sa, 0, plid,
|
mgmt->sa, 0, plid,
|
||||||
WLAN_REASON_MESH_CONFIG);
|
WLAN_REASON_MESH_CONFIG);
|
||||||
goto unlock_rcu;
|
goto unlock_rcu;
|
||||||
|
@ -1081,9 +1111,13 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
|
||||||
goto unlock_rcu;
|
goto unlock_rcu;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 802.11-2012 13.3.7.2 - update plid on CNF if not set */
|
if (event == CNF_ACPT) {
|
||||||
if (!sta->plid && event == CNF_ACPT)
|
/* 802.11-2012 13.3.7.2 - update plid on CNF if not set */
|
||||||
sta->plid = plid;
|
if (!sta->mesh->plid)
|
||||||
|
sta->mesh->plid = plid;
|
||||||
|
|
||||||
|
sta->mesh->aid = get_unaligned_le16(PLINK_CNF_AID(mgmt));
|
||||||
|
}
|
||||||
|
|
||||||
changed |= mesh_plink_fsm(sdata, sta, event);
|
changed |= mesh_plink_fsm(sdata, sta, event);
|
||||||
|
|
||||||
|
|
|
@ -92,16 +92,16 @@ u32 ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata)
|
||||||
if (sdata != sta->sdata)
|
if (sdata != sta->sdata)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
switch (sta->plink_state) {
|
switch (sta->mesh->plink_state) {
|
||||||
case NL80211_PLINK_OPN_SNT:
|
case NL80211_PLINK_OPN_SNT:
|
||||||
case NL80211_PLINK_OPN_RCVD:
|
case NL80211_PLINK_OPN_RCVD:
|
||||||
case NL80211_PLINK_CNF_RCVD:
|
case NL80211_PLINK_CNF_RCVD:
|
||||||
peering = true;
|
peering = true;
|
||||||
break;
|
break;
|
||||||
case NL80211_PLINK_ESTAB:
|
case NL80211_PLINK_ESTAB:
|
||||||
if (sta->local_pm == NL80211_MESH_POWER_LIGHT_SLEEP)
|
if (sta->mesh->local_pm == NL80211_MESH_POWER_LIGHT_SLEEP)
|
||||||
light_sleep_cnt++;
|
light_sleep_cnt++;
|
||||||
else if (sta->local_pm == NL80211_MESH_POWER_DEEP_SLEEP)
|
else if (sta->mesh->local_pm == NL80211_MESH_POWER_DEEP_SLEEP)
|
||||||
deep_sleep_cnt++;
|
deep_sleep_cnt++;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -153,19 +153,19 @@ u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
|
||||||
{
|
{
|
||||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||||
|
|
||||||
if (sta->local_pm == pm)
|
if (sta->mesh->local_pm == pm)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
mps_dbg(sdata, "local STA operates in mode %d with %pM\n",
|
mps_dbg(sdata, "local STA operates in mode %d with %pM\n",
|
||||||
pm, sta->sta.addr);
|
pm, sta->sta.addr);
|
||||||
|
|
||||||
sta->local_pm = pm;
|
sta->mesh->local_pm = pm;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* announce peer-specific power mode transition
|
* announce peer-specific power mode transition
|
||||||
* (see IEEE802.11-2012 13.14.3.2 and 13.14.3.3)
|
* (see IEEE802.11-2012 13.14.3.2 and 13.14.3.3)
|
||||||
*/
|
*/
|
||||||
if (sta->plink_state == NL80211_PLINK_ESTAB)
|
if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
|
||||||
mps_qos_null_tx(sta);
|
mps_qos_null_tx(sta);
|
||||||
|
|
||||||
return ieee80211_mps_local_status_update(sdata);
|
return ieee80211_mps_local_status_update(sdata);
|
||||||
|
@ -197,8 +197,8 @@ void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata,
|
||||||
|
|
||||||
if (is_unicast_ether_addr(hdr->addr1) &&
|
if (is_unicast_ether_addr(hdr->addr1) &&
|
||||||
ieee80211_is_data_qos(hdr->frame_control) &&
|
ieee80211_is_data_qos(hdr->frame_control) &&
|
||||||
sta->plink_state == NL80211_PLINK_ESTAB)
|
sta->mesh->plink_state == NL80211_PLINK_ESTAB)
|
||||||
pm = sta->local_pm;
|
pm = sta->mesh->local_pm;
|
||||||
else
|
else
|
||||||
pm = sdata->u.mesh.nonpeer_pm;
|
pm = sdata->u.mesh.nonpeer_pm;
|
||||||
|
|
||||||
|
@ -241,16 +241,16 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta)
|
||||||
* use peer-specific power mode if peering is established and the
|
* use peer-specific power mode if peering is established and the
|
||||||
* peer's power mode is known
|
* peer's power mode is known
|
||||||
*/
|
*/
|
||||||
if (sta->plink_state == NL80211_PLINK_ESTAB &&
|
if (sta->mesh->plink_state == NL80211_PLINK_ESTAB &&
|
||||||
sta->peer_pm != NL80211_MESH_POWER_UNKNOWN)
|
sta->mesh->peer_pm != NL80211_MESH_POWER_UNKNOWN)
|
||||||
pm = sta->peer_pm;
|
pm = sta->mesh->peer_pm;
|
||||||
else
|
else
|
||||||
pm = sta->nonpeer_pm;
|
pm = sta->mesh->nonpeer_pm;
|
||||||
|
|
||||||
do_buffer = (pm != NL80211_MESH_POWER_ACTIVE);
|
do_buffer = (pm != NL80211_MESH_POWER_ACTIVE);
|
||||||
|
|
||||||
/* clear the MPSP flags for non-peers or active STA */
|
/* clear the MPSP flags for non-peers or active STA */
|
||||||
if (sta->plink_state != NL80211_PLINK_ESTAB) {
|
if (sta->mesh->plink_state != NL80211_PLINK_ESTAB) {
|
||||||
clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
|
clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
|
||||||
clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
|
clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
|
||||||
} else if (!do_buffer) {
|
} else if (!do_buffer) {
|
||||||
|
@ -296,13 +296,13 @@ static void mps_set_sta_peer_pm(struct sta_info *sta,
|
||||||
pm = NL80211_MESH_POWER_ACTIVE;
|
pm = NL80211_MESH_POWER_ACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sta->peer_pm == pm)
|
if (sta->mesh->peer_pm == pm)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mps_dbg(sta->sdata, "STA %pM enters mode %d\n",
|
mps_dbg(sta->sdata, "STA %pM enters mode %d\n",
|
||||||
sta->sta.addr, pm);
|
sta->sta.addr, pm);
|
||||||
|
|
||||||
sta->peer_pm = pm;
|
sta->mesh->peer_pm = pm;
|
||||||
|
|
||||||
ieee80211_mps_sta_status_update(sta);
|
ieee80211_mps_sta_status_update(sta);
|
||||||
}
|
}
|
||||||
|
@ -317,13 +317,13 @@ static void mps_set_sta_nonpeer_pm(struct sta_info *sta,
|
||||||
else
|
else
|
||||||
pm = NL80211_MESH_POWER_ACTIVE;
|
pm = NL80211_MESH_POWER_ACTIVE;
|
||||||
|
|
||||||
if (sta->nonpeer_pm == pm)
|
if (sta->mesh->nonpeer_pm == pm)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mps_dbg(sta->sdata, "STA %pM sets non-peer mode to %d\n",
|
mps_dbg(sta->sdata, "STA %pM sets non-peer mode to %d\n",
|
||||||
sta->sta.addr, pm);
|
sta->sta.addr, pm);
|
||||||
|
|
||||||
sta->nonpeer_pm = pm;
|
sta->mesh->nonpeer_pm = pm;
|
||||||
|
|
||||||
ieee80211_mps_sta_status_update(sta);
|
ieee80211_mps_sta_status_update(sta);
|
||||||
}
|
}
|
||||||
|
@ -552,7 +552,7 @@ void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,
|
||||||
} else {
|
} else {
|
||||||
if (eosp)
|
if (eosp)
|
||||||
clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
|
clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
|
||||||
else if (sta->local_pm != NL80211_MESH_POWER_ACTIVE)
|
else if (sta->mesh->local_pm != NL80211_MESH_POWER_ACTIVE)
|
||||||
set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
|
set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
|
||||||
|
|
||||||
if (rspi && !test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER))
|
if (rspi && !test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER))
|
||||||
|
@ -577,9 +577,9 @@ void ieee80211_mps_frame_release(struct sta_info *sta,
|
||||||
int ac, buffer_local = 0;
|
int ac, buffer_local = 0;
|
||||||
bool has_buffered = false;
|
bool has_buffered = false;
|
||||||
|
|
||||||
if (sta->plink_state == NL80211_PLINK_ESTAB)
|
if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
|
||||||
has_buffered = ieee80211_check_tim(elems->tim, elems->tim_len,
|
has_buffered = ieee80211_check_tim(elems->tim, elems->tim_len,
|
||||||
sta->llid);
|
sta->mesh->aid);
|
||||||
|
|
||||||
if (has_buffered)
|
if (has_buffered)
|
||||||
mps_dbg(sta->sdata, "%pM indicates buffered frames\n",
|
mps_dbg(sta->sdata, "%pM indicates buffered frames\n",
|
||||||
|
@ -598,7 +598,7 @@ void ieee80211_mps_frame_release(struct sta_info *sta,
|
||||||
if (!has_buffered && !buffer_local)
|
if (!has_buffered && !buffer_local)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (sta->plink_state == NL80211_PLINK_ESTAB)
|
if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
|
||||||
mpsp_trigger_send(sta, has_buffered, !buffer_local);
|
mpsp_trigger_send(sta, has_buffered, !buffer_local);
|
||||||
else
|
else
|
||||||
mps_frame_deliver(sta, 1);
|
mps_frame_deliver(sta, 1);
|
||||||
|
|
|
@ -127,14 +127,14 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
|
||||||
|
|
||||||
/* Timing offset calculation (see 13.13.2.2.2) */
|
/* Timing offset calculation (see 13.13.2.2.2) */
|
||||||
t_t = le64_to_cpu(mgmt->u.beacon.timestamp);
|
t_t = le64_to_cpu(mgmt->u.beacon.timestamp);
|
||||||
sta->t_offset = t_t - t_r;
|
sta->mesh->t_offset = t_t - t_r;
|
||||||
|
|
||||||
if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
|
if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
|
||||||
s64 t_clockdrift = sta->t_offset_setpoint - sta->t_offset;
|
s64 t_clockdrift = sta->mesh->t_offset_setpoint - sta->mesh->t_offset;
|
||||||
msync_dbg(sdata,
|
msync_dbg(sdata,
|
||||||
"STA %pM : sta->t_offset=%lld, sta->t_offset_setpoint=%lld, t_clockdrift=%lld\n",
|
"STA %pM : t_offset=%lld, t_offset_setpoint=%lld, t_clockdrift=%lld\n",
|
||||||
sta->sta.addr, (long long) sta->t_offset,
|
sta->sta.addr, (long long) sta->mesh->t_offset,
|
||||||
(long long) sta->t_offset_setpoint,
|
(long long) sta->mesh->t_offset_setpoint,
|
||||||
(long long) t_clockdrift);
|
(long long) t_clockdrift);
|
||||||
|
|
||||||
if (t_clockdrift > TOFFSET_MAXIMUM_ADJUSTMENT ||
|
if (t_clockdrift > TOFFSET_MAXIMUM_ADJUSTMENT ||
|
||||||
|
@ -152,12 +152,12 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
|
||||||
ifmsh->sync_offset_clockdrift_max = t_clockdrift;
|
ifmsh->sync_offset_clockdrift_max = t_clockdrift;
|
||||||
spin_unlock_bh(&ifmsh->sync_offset_lock);
|
spin_unlock_bh(&ifmsh->sync_offset_lock);
|
||||||
} else {
|
} else {
|
||||||
sta->t_offset_setpoint = sta->t_offset - TOFFSET_SET_MARGIN;
|
sta->mesh->t_offset_setpoint = sta->mesh->t_offset - TOFFSET_SET_MARGIN;
|
||||||
set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN);
|
set_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN);
|
||||||
msync_dbg(sdata,
|
msync_dbg(sdata,
|
||||||
"STA %pM : offset was invalid, sta->t_offset=%lld\n",
|
"STA %pM : offset was invalid, t_offset=%lld\n",
|
||||||
sta->sta.addr,
|
sta->sta.addr,
|
||||||
(long long) sta->t_offset);
|
(long long) sta->mesh->t_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
no_sync:
|
no_sync:
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
|
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
|
||||||
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
|
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
|
||||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||||
|
* Copyright (C) 2015 Intel Deutschland GmbH
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
@ -538,11 +539,16 @@ static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
|
||||||
ieee80211_ie_build_ht_cap(pos, &ht_cap, cap);
|
ieee80211_ie_build_ht_cap(pos, &ht_cap, cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function determines vht capability flags for the association
|
||||||
|
* and builds the IE.
|
||||||
|
* Note - the function may set the owner of the MU-MIMO capability
|
||||||
|
*/
|
||||||
static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
|
static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
|
||||||
struct sk_buff *skb,
|
struct sk_buff *skb,
|
||||||
struct ieee80211_supported_band *sband,
|
struct ieee80211_supported_band *sband,
|
||||||
struct ieee80211_vht_cap *ap_vht_cap)
|
struct ieee80211_vht_cap *ap_vht_cap)
|
||||||
{
|
{
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
u8 *pos;
|
u8 *pos;
|
||||||
u32 cap;
|
u32 cap;
|
||||||
struct ieee80211_sta_vht_cap vht_cap;
|
struct ieee80211_sta_vht_cap vht_cap;
|
||||||
|
@ -576,7 +582,34 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
|
||||||
*/
|
*/
|
||||||
if (!(ap_vht_cap->vht_cap_info &
|
if (!(ap_vht_cap->vht_cap_info &
|
||||||
cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)))
|
cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)))
|
||||||
cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
|
cap &= ~(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
|
||||||
|
IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
|
||||||
|
else if (!(ap_vht_cap->vht_cap_info &
|
||||||
|
cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)))
|
||||||
|
cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If some other vif is using the MU-MIMO capablity we cannot associate
|
||||||
|
* using MU-MIMO - this will lead to contradictions in the group-id
|
||||||
|
* mechanism.
|
||||||
|
* Ownership is defined since association request, in order to avoid
|
||||||
|
* simultaneous associations with MU-MIMO.
|
||||||
|
*/
|
||||||
|
if (cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) {
|
||||||
|
bool disable_mu_mimo = false;
|
||||||
|
struct ieee80211_sub_if_data *other;
|
||||||
|
|
||||||
|
list_for_each_entry_rcu(other, &local->interfaces, list) {
|
||||||
|
if (other->flags & IEEE80211_SDATA_MU_MIMO_OWNER) {
|
||||||
|
disable_mu_mimo = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (disable_mu_mimo)
|
||||||
|
cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
|
||||||
|
else
|
||||||
|
sdata->flags |= IEEE80211_SDATA_MU_MIMO_OWNER;
|
||||||
|
}
|
||||||
|
|
||||||
mask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
|
mask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
|
||||||
|
|
||||||
|
@ -1096,24 +1129,6 @@ static void ieee80211_chswitch_timer(unsigned long data)
|
||||||
ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work);
|
ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata)
|
|
||||||
{
|
|
||||||
struct sta_info *sta;
|
|
||||||
u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
|
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
|
|
||||||
if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
|
|
||||||
!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
ieee80211_tdls_oper_request(&sdata->vif, sta->sta.addr,
|
|
||||||
NL80211_TDLS_TEARDOWN, reason,
|
|
||||||
GFP_ATOMIC);
|
|
||||||
}
|
|
||||||
rcu_read_unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||||
u64 timestamp, u32 device_timestamp,
|
u64 timestamp, u32 device_timestamp,
|
||||||
|
@ -2076,6 +2091,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||||
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
|
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
|
||||||
memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa));
|
memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa));
|
||||||
memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask));
|
memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask));
|
||||||
|
sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER;
|
||||||
|
|
||||||
sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
|
sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
|
||||||
|
|
||||||
|
@ -2538,6 +2554,7 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
|
||||||
eth_zero_addr(sdata->u.mgd.bssid);
|
eth_zero_addr(sdata->u.mgd.bssid);
|
||||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
|
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
|
||||||
sdata->u.mgd.flags = 0;
|
sdata->u.mgd.flags = 0;
|
||||||
|
sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER;
|
||||||
mutex_lock(&sdata->local->mtx);
|
mutex_lock(&sdata->local->mtx);
|
||||||
ieee80211_vif_release_channel(sdata);
|
ieee80211_vif_release_channel(sdata);
|
||||||
mutex_unlock(&sdata->local->mtx);
|
mutex_unlock(&sdata->local->mtx);
|
||||||
|
@ -3034,12 +3051,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
||||||
|
|
||||||
rate_control_rate_init(sta);
|
rate_control_rate_init(sta);
|
||||||
|
|
||||||
if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) {
|
if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
|
||||||
set_sta_flag(sta, WLAN_STA_MFP);
|
set_sta_flag(sta, WLAN_STA_MFP);
|
||||||
sta->sta.mfp = true;
|
|
||||||
} else {
|
|
||||||
sta->sta.mfp = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sta->sta.wme = elems.wmm_param && local->hw.queues >= IEEE80211_NUM_ACS;
|
sta->sta.wme = elems.wmm_param && local->hw.queues >= IEEE80211_NUM_ACS;
|
||||||
|
|
||||||
|
|
|
@ -179,7 +179,7 @@ int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
|
||||||
{
|
{
|
||||||
struct ieee80211_local *local = sdata->local;
|
struct ieee80211_local *local = sdata->local;
|
||||||
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
|
||||||
u32 changed = BSS_CHANGED_OCB;
|
u32 changed = BSS_CHANGED_OCB | BSS_CHANGED_BSSID;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (ifocb->joined == true)
|
if (ifocb->joined == true)
|
||||||
|
|
|
@ -29,6 +29,65 @@ module_param(ieee80211_default_rc_algo, charp, 0644);
|
||||||
MODULE_PARM_DESC(ieee80211_default_rc_algo,
|
MODULE_PARM_DESC(ieee80211_default_rc_algo,
|
||||||
"Default rate control algorithm for mac80211 to use");
|
"Default rate control algorithm for mac80211 to use");
|
||||||
|
|
||||||
|
void rate_control_rate_init(struct sta_info *sta)
|
||||||
|
{
|
||||||
|
struct ieee80211_local *local = sta->sdata->local;
|
||||||
|
struct rate_control_ref *ref = sta->rate_ctrl;
|
||||||
|
struct ieee80211_sta *ista = &sta->sta;
|
||||||
|
void *priv_sta = sta->rate_ctrl_priv;
|
||||||
|
struct ieee80211_supported_band *sband;
|
||||||
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||||
|
|
||||||
|
ieee80211_sta_set_rx_nss(sta);
|
||||||
|
|
||||||
|
if (!ref)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
|
||||||
|
if (WARN_ON(!chanctx_conf)) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
|
||||||
|
|
||||||
|
spin_lock_bh(&sta->rate_ctrl_lock);
|
||||||
|
ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
|
||||||
|
priv_sta);
|
||||||
|
spin_unlock_bh(&sta->rate_ctrl_lock);
|
||||||
|
rcu_read_unlock();
|
||||||
|
set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rate_control_rate_update(struct ieee80211_local *local,
|
||||||
|
struct ieee80211_supported_band *sband,
|
||||||
|
struct sta_info *sta, u32 changed)
|
||||||
|
{
|
||||||
|
struct rate_control_ref *ref = local->rate_ctrl;
|
||||||
|
struct ieee80211_sta *ista = &sta->sta;
|
||||||
|
void *priv_sta = sta->rate_ctrl_priv;
|
||||||
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||||
|
|
||||||
|
if (ref && ref->ops->rate_update) {
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
|
||||||
|
if (WARN_ON(!chanctx_conf)) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_bh(&sta->rate_ctrl_lock);
|
||||||
|
ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
|
||||||
|
ista, priv_sta, changed);
|
||||||
|
spin_unlock_bh(&sta->rate_ctrl_lock);
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
|
||||||
|
}
|
||||||
|
|
||||||
int ieee80211_rate_control_register(const struct rate_control_ops *ops)
|
int ieee80211_rate_control_register(const struct rate_control_ops *ops)
|
||||||
{
|
{
|
||||||
struct rate_control_alg *alg;
|
struct rate_control_alg *alg;
|
||||||
|
@ -294,39 +353,37 @@ bool rate_control_send_low(struct ieee80211_sta *pubsta,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(rate_control_send_low);
|
EXPORT_SYMBOL(rate_control_send_low);
|
||||||
|
|
||||||
static bool rate_idx_match_legacy_mask(struct ieee80211_tx_rate *rate,
|
static bool rate_idx_match_legacy_mask(s8 *rate_idx, int n_bitrates, u32 mask)
|
||||||
int n_bitrates, u32 mask)
|
|
||||||
{
|
{
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
/* See whether the selected rate or anything below it is allowed. */
|
/* See whether the selected rate or anything below it is allowed. */
|
||||||
for (j = rate->idx; j >= 0; j--) {
|
for (j = *rate_idx; j >= 0; j--) {
|
||||||
if (mask & (1 << j)) {
|
if (mask & (1 << j)) {
|
||||||
/* Okay, found a suitable rate. Use it. */
|
/* Okay, found a suitable rate. Use it. */
|
||||||
rate->idx = j;
|
*rate_idx = j;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try to find a higher rate that would be allowed */
|
/* Try to find a higher rate that would be allowed */
|
||||||
for (j = rate->idx + 1; j < n_bitrates; j++) {
|
for (j = *rate_idx + 1; j < n_bitrates; j++) {
|
||||||
if (mask & (1 << j)) {
|
if (mask & (1 << j)) {
|
||||||
/* Okay, found a suitable rate. Use it. */
|
/* Okay, found a suitable rate. Use it. */
|
||||||
rate->idx = j;
|
*rate_idx = j;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate,
|
static bool rate_idx_match_mcs_mask(s8 *rate_idx, u8 *mcs_mask)
|
||||||
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
|
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
int ridx, rbit;
|
int ridx, rbit;
|
||||||
|
|
||||||
ridx = rate->idx / 8;
|
ridx = *rate_idx / 8;
|
||||||
rbit = rate->idx % 8;
|
rbit = *rate_idx % 8;
|
||||||
|
|
||||||
/* sanity check */
|
/* sanity check */
|
||||||
if (ridx < 0 || ridx >= IEEE80211_HT_MCS_MASK_LEN)
|
if (ridx < 0 || ridx >= IEEE80211_HT_MCS_MASK_LEN)
|
||||||
|
@ -336,20 +393,20 @@ static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate,
|
||||||
for (i = ridx; i >= 0; i--) {
|
for (i = ridx; i >= 0; i--) {
|
||||||
for (j = rbit; j >= 0; j--)
|
for (j = rbit; j >= 0; j--)
|
||||||
if (mcs_mask[i] & BIT(j)) {
|
if (mcs_mask[i] & BIT(j)) {
|
||||||
rate->idx = i * 8 + j;
|
*rate_idx = i * 8 + j;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
rbit = 7;
|
rbit = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try to find a higher rate that would be allowed */
|
/* Try to find a higher rate that would be allowed */
|
||||||
ridx = (rate->idx + 1) / 8;
|
ridx = (*rate_idx + 1) / 8;
|
||||||
rbit = (rate->idx + 1) % 8;
|
rbit = (*rate_idx + 1) % 8;
|
||||||
|
|
||||||
for (i = ridx; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
|
for (i = ridx; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
|
||||||
for (j = rbit; j < 8; j++)
|
for (j = rbit; j < 8; j++)
|
||||||
if (mcs_mask[i] & BIT(j)) {
|
if (mcs_mask[i] & BIT(j)) {
|
||||||
rate->idx = i * 8 + j;
|
*rate_idx = i * 8 + j;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
rbit = 0;
|
rbit = 0;
|
||||||
|
@ -357,37 +414,93 @@ static bool rate_idx_match_mcs_mask(struct ieee80211_tx_rate *rate,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool rate_idx_match_vht_mcs_mask(s8 *rate_idx, u16 *vht_mask)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
int ridx, rbit;
|
||||||
|
|
||||||
|
ridx = *rate_idx >> 4;
|
||||||
|
rbit = *rate_idx & 0xf;
|
||||||
|
|
||||||
static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
|
if (ridx < 0 || ridx >= NL80211_VHT_NSS_MAX)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* See whether the selected rate or anything below it is allowed. */
|
||||||
|
for (i = ridx; i >= 0; i--) {
|
||||||
|
for (j = rbit; j >= 0; j--) {
|
||||||
|
if (vht_mask[i] & BIT(j)) {
|
||||||
|
*rate_idx = (i << 4) | j;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rbit = 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to find a higher rate that would be allowed */
|
||||||
|
ridx = (*rate_idx + 1) >> 4;
|
||||||
|
rbit = (*rate_idx + 1) & 0xf;
|
||||||
|
|
||||||
|
for (i = ridx; i < NL80211_VHT_NSS_MAX; i++) {
|
||||||
|
for (j = rbit; j < 16; j++) {
|
||||||
|
if (vht_mask[i] & BIT(j)) {
|
||||||
|
*rate_idx = (i << 4) | j;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rbit = 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rate_idx_match_mask(s8 *rate_idx, u16 *rate_flags,
|
||||||
struct ieee80211_supported_band *sband,
|
struct ieee80211_supported_band *sband,
|
||||||
enum nl80211_chan_width chan_width,
|
enum nl80211_chan_width chan_width,
|
||||||
u32 mask,
|
u32 mask,
|
||||||
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN])
|
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN],
|
||||||
|
u16 vht_mask[NL80211_VHT_NSS_MAX])
|
||||||
{
|
{
|
||||||
struct ieee80211_tx_rate alt_rate;
|
if (*rate_flags & IEEE80211_TX_RC_VHT_MCS) {
|
||||||
|
/* handle VHT rates */
|
||||||
|
if (rate_idx_match_vht_mcs_mask(rate_idx, vht_mask))
|
||||||
|
return;
|
||||||
|
|
||||||
/* handle HT rates */
|
*rate_idx = 0;
|
||||||
if (rate->flags & IEEE80211_TX_RC_MCS) {
|
/* keep protection flags */
|
||||||
if (rate_idx_match_mcs_mask(rate, mcs_mask))
|
*rate_flags &= (IEEE80211_TX_RC_USE_RTS_CTS |
|
||||||
|
IEEE80211_TX_RC_USE_CTS_PROTECT |
|
||||||
|
IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
|
||||||
|
|
||||||
|
*rate_flags |= IEEE80211_TX_RC_MCS;
|
||||||
|
if (chan_width == NL80211_CHAN_WIDTH_40)
|
||||||
|
*rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
|
||||||
|
|
||||||
|
if (rate_idx_match_mcs_mask(rate_idx, mcs_mask))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* also try the legacy rates. */
|
/* also try the legacy rates. */
|
||||||
alt_rate.idx = 0;
|
*rate_flags &= ~(IEEE80211_TX_RC_MCS |
|
||||||
/* keep protection flags */
|
IEEE80211_TX_RC_40_MHZ_WIDTH);
|
||||||
alt_rate.flags = rate->flags &
|
if (rate_idx_match_legacy_mask(rate_idx, sband->n_bitrates,
|
||||||
(IEEE80211_TX_RC_USE_RTS_CTS |
|
mask))
|
||||||
IEEE80211_TX_RC_USE_CTS_PROTECT |
|
|
||||||
IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
|
|
||||||
alt_rate.count = rate->count;
|
|
||||||
if (rate_idx_match_legacy_mask(&alt_rate,
|
|
||||||
sband->n_bitrates, mask)) {
|
|
||||||
*rate = alt_rate;
|
|
||||||
return;
|
return;
|
||||||
}
|
} else if (*rate_flags & IEEE80211_TX_RC_MCS) {
|
||||||
} else if (!(rate->flags & IEEE80211_TX_RC_VHT_MCS)) {
|
/* handle HT rates */
|
||||||
|
if (rate_idx_match_mcs_mask(rate_idx, mcs_mask))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* also try the legacy rates. */
|
||||||
|
*rate_idx = 0;
|
||||||
|
/* keep protection flags */
|
||||||
|
*rate_flags &= (IEEE80211_TX_RC_USE_RTS_CTS |
|
||||||
|
IEEE80211_TX_RC_USE_CTS_PROTECT |
|
||||||
|
IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
|
||||||
|
if (rate_idx_match_legacy_mask(rate_idx, sband->n_bitrates,
|
||||||
|
mask))
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
/* handle legacy rates */
|
/* handle legacy rates */
|
||||||
if (rate_idx_match_legacy_mask(rate, sband->n_bitrates, mask))
|
if (rate_idx_match_legacy_mask(rate_idx, sband->n_bitrates,
|
||||||
|
mask))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* if HT BSS, and we handle a data frame, also try HT rates */
|
/* if HT BSS, and we handle a data frame, also try HT rates */
|
||||||
|
@ -400,23 +513,19 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
alt_rate.idx = 0;
|
*rate_idx = 0;
|
||||||
/* keep protection flags */
|
/* keep protection flags */
|
||||||
alt_rate.flags = rate->flags &
|
*rate_flags &= (IEEE80211_TX_RC_USE_RTS_CTS |
|
||||||
(IEEE80211_TX_RC_USE_RTS_CTS |
|
IEEE80211_TX_RC_USE_CTS_PROTECT |
|
||||||
IEEE80211_TX_RC_USE_CTS_PROTECT |
|
IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
|
||||||
IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
|
|
||||||
alt_rate.count = rate->count;
|
|
||||||
|
|
||||||
alt_rate.flags |= IEEE80211_TX_RC_MCS;
|
*rate_flags |= IEEE80211_TX_RC_MCS;
|
||||||
|
|
||||||
if (chan_width == NL80211_CHAN_WIDTH_40)
|
if (chan_width == NL80211_CHAN_WIDTH_40)
|
||||||
alt_rate.flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
|
*rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
|
||||||
|
|
||||||
if (rate_idx_match_mcs_mask(&alt_rate, mcs_mask)) {
|
if (rate_idx_match_mcs_mask(rate_idx, mcs_mask))
|
||||||
*rate = alt_rate;
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -569,18 +678,92 @@ static void rate_control_fill_sta_table(struct ieee80211_sta *sta,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool rate_control_cap_mask(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct ieee80211_supported_band *sband,
|
||||||
|
struct ieee80211_sta *sta, u32 *mask,
|
||||||
|
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN],
|
||||||
|
u16 vht_mask[NL80211_VHT_NSS_MAX])
|
||||||
|
{
|
||||||
|
u32 i, flags;
|
||||||
|
|
||||||
|
*mask = sdata->rc_rateidx_mask[sband->band];
|
||||||
|
flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
|
||||||
|
for (i = 0; i < sband->n_bitrates; i++) {
|
||||||
|
if ((flags & sband->bitrates[i].flags) != flags)
|
||||||
|
*mask &= ~BIT(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*mask == (1 << sband->n_bitrates) - 1 &&
|
||||||
|
!sdata->rc_has_mcs_mask[sband->band] &&
|
||||||
|
!sdata->rc_has_vht_mcs_mask[sband->band])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (sdata->rc_has_mcs_mask[sband->band])
|
||||||
|
memcpy(mcs_mask, sdata->rc_rateidx_mcs_mask[sband->band],
|
||||||
|
IEEE80211_HT_MCS_MASK_LEN);
|
||||||
|
else
|
||||||
|
memset(mcs_mask, 0xff, IEEE80211_HT_MCS_MASK_LEN);
|
||||||
|
|
||||||
|
if (sdata->rc_has_vht_mcs_mask[sband->band])
|
||||||
|
memcpy(vht_mask, sdata->rc_rateidx_vht_mcs_mask[sband->band],
|
||||||
|
sizeof(u16) * NL80211_VHT_NSS_MAX);
|
||||||
|
else
|
||||||
|
memset(vht_mask, 0xff, sizeof(u16) * NL80211_VHT_NSS_MAX);
|
||||||
|
|
||||||
|
if (sta) {
|
||||||
|
__le16 sta_vht_cap;
|
||||||
|
u16 sta_vht_mask[NL80211_VHT_NSS_MAX];
|
||||||
|
|
||||||
|
/* Filter out rates that the STA does not support */
|
||||||
|
*mask &= sta->supp_rates[sband->band];
|
||||||
|
for (i = 0; i < sizeof(mcs_mask); i++)
|
||||||
|
mcs_mask[i] &= sta->ht_cap.mcs.rx_mask[i];
|
||||||
|
|
||||||
|
sta_vht_cap = sta->vht_cap.vht_mcs.rx_mcs_map;
|
||||||
|
ieee80211_get_vht_mask_from_cap(sta_vht_cap, sta_vht_mask);
|
||||||
|
for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
|
||||||
|
vht_mask[i] &= sta_vht_mask[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rate_control_apply_mask_ratetbl(struct sta_info *sta,
|
||||||
|
struct ieee80211_supported_band *sband,
|
||||||
|
struct ieee80211_sta_rates *rates)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
u32 mask;
|
||||||
|
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
|
||||||
|
u16 vht_mask[NL80211_VHT_NSS_MAX];
|
||||||
|
enum nl80211_chan_width chan_width;
|
||||||
|
|
||||||
|
if (!rate_control_cap_mask(sta->sdata, sband, &sta->sta, &mask,
|
||||||
|
mcs_mask, vht_mask))
|
||||||
|
return;
|
||||||
|
|
||||||
|
chan_width = sta->sdata->vif.bss_conf.chandef.width;
|
||||||
|
for (i = 0; i < IEEE80211_TX_RATE_TABLE_SIZE; i++) {
|
||||||
|
if (rates->rate[i].idx < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
rate_idx_match_mask(&rates->rate[i].idx, &rates->rate[i].flags,
|
||||||
|
sband, chan_width, mask, mcs_mask,
|
||||||
|
vht_mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
|
static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
|
||||||
struct ieee80211_sta *sta,
|
struct ieee80211_sta *sta,
|
||||||
struct ieee80211_supported_band *sband,
|
struct ieee80211_supported_band *sband,
|
||||||
struct ieee80211_tx_info *info,
|
|
||||||
struct ieee80211_tx_rate *rates,
|
struct ieee80211_tx_rate *rates,
|
||||||
int max_rates)
|
int max_rates)
|
||||||
{
|
{
|
||||||
enum nl80211_chan_width chan_width;
|
enum nl80211_chan_width chan_width;
|
||||||
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
|
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
|
||||||
bool has_mcs_mask;
|
|
||||||
u32 mask;
|
u32 mask;
|
||||||
u32 rate_flags;
|
u16 rate_flags, vht_mask[NL80211_VHT_NSS_MAX];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -588,30 +771,10 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
|
||||||
* default mask (allow all rates) is used to save some processing for
|
* default mask (allow all rates) is used to save some processing for
|
||||||
* the common case.
|
* the common case.
|
||||||
*/
|
*/
|
||||||
mask = sdata->rc_rateidx_mask[info->band];
|
if (!rate_control_cap_mask(sdata, sband, sta, &mask, mcs_mask,
|
||||||
has_mcs_mask = sdata->rc_has_mcs_mask[info->band];
|
vht_mask))
|
||||||
rate_flags =
|
|
||||||
ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
|
|
||||||
for (i = 0; i < sband->n_bitrates; i++)
|
|
||||||
if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
|
|
||||||
mask &= ~BIT(i);
|
|
||||||
|
|
||||||
if (mask == (1 << sband->n_bitrates) - 1 && !has_mcs_mask)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (has_mcs_mask)
|
|
||||||
memcpy(mcs_mask, sdata->rc_rateidx_mcs_mask[info->band],
|
|
||||||
sizeof(mcs_mask));
|
|
||||||
else
|
|
||||||
memset(mcs_mask, 0xff, sizeof(mcs_mask));
|
|
||||||
|
|
||||||
if (sta) {
|
|
||||||
/* Filter out rates that the STA does not support */
|
|
||||||
mask &= sta->supp_rates[info->band];
|
|
||||||
for (i = 0; i < sizeof(mcs_mask); i++)
|
|
||||||
mcs_mask[i] &= sta->ht_cap.mcs.rx_mask[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure the rate index selected for each TX rate is
|
* Make sure the rate index selected for each TX rate is
|
||||||
* included in the configured mask and change the rate indexes
|
* included in the configured mask and change the rate indexes
|
||||||
|
@ -623,8 +786,10 @@ static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
|
||||||
if (rates[i].idx < 0)
|
if (rates[i].idx < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
rate_idx_match_mask(&rates[i], sband, chan_width, mask,
|
rate_flags = rates[i].flags;
|
||||||
mcs_mask);
|
rate_idx_match_mask(&rates[i].idx, &rate_flags, sband,
|
||||||
|
chan_width, mask, mcs_mask, vht_mask);
|
||||||
|
rates[i].flags = rate_flags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,7 +813,7 @@ void ieee80211_get_tx_rates(struct ieee80211_vif *vif,
|
||||||
sband = sdata->local->hw.wiphy->bands[info->band];
|
sband = sdata->local->hw.wiphy->bands[info->band];
|
||||||
|
|
||||||
if (ieee80211_is_data(hdr->frame_control))
|
if (ieee80211_is_data(hdr->frame_control))
|
||||||
rate_control_apply_mask(sdata, sta, sband, info, dest, max_rates);
|
rate_control_apply_mask(sdata, sta, sband, dest, max_rates);
|
||||||
|
|
||||||
if (dest[0].idx < 0)
|
if (dest[0].idx < 0)
|
||||||
__rate_control_send_low(&sdata->local->hw, sband, sta, info,
|
__rate_control_send_low(&sdata->local->hw, sband, sta, info,
|
||||||
|
@ -705,7 +870,10 @@ int rate_control_set_rates(struct ieee80211_hw *hw,
|
||||||
{
|
{
|
||||||
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
|
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
|
||||||
struct ieee80211_sta_rates *old;
|
struct ieee80211_sta_rates *old;
|
||||||
|
struct ieee80211_supported_band *sband;
|
||||||
|
|
||||||
|
sband = hw->wiphy->bands[ieee80211_get_sdata_band(sta->sdata)];
|
||||||
|
rate_control_apply_mask_ratetbl(sta, sband, rates);
|
||||||
/*
|
/*
|
||||||
* mac80211 guarantees that this function will not be called
|
* mac80211 guarantees that this function will not be called
|
||||||
* concurrently, so the following RCU access is safe, even without
|
* concurrently, so the following RCU access is safe, even without
|
||||||
|
|
|
@ -71,64 +71,10 @@ rate_control_tx_status_noskb(struct ieee80211_local *local,
|
||||||
spin_unlock_bh(&sta->rate_ctrl_lock);
|
spin_unlock_bh(&sta->rate_ctrl_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void rate_control_rate_init(struct sta_info *sta)
|
void rate_control_rate_init(struct sta_info *sta);
|
||||||
{
|
void rate_control_rate_update(struct ieee80211_local *local,
|
||||||
struct ieee80211_local *local = sta->sdata->local;
|
|
||||||
struct rate_control_ref *ref = sta->rate_ctrl;
|
|
||||||
struct ieee80211_sta *ista = &sta->sta;
|
|
||||||
void *priv_sta = sta->rate_ctrl_priv;
|
|
||||||
struct ieee80211_supported_band *sband;
|
|
||||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
|
||||||
|
|
||||||
ieee80211_sta_set_rx_nss(sta);
|
|
||||||
|
|
||||||
if (!ref)
|
|
||||||
return;
|
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
|
|
||||||
chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
|
|
||||||
if (WARN_ON(!chanctx_conf)) {
|
|
||||||
rcu_read_unlock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
|
|
||||||
|
|
||||||
spin_lock_bh(&sta->rate_ctrl_lock);
|
|
||||||
ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
|
|
||||||
priv_sta);
|
|
||||||
spin_unlock_bh(&sta->rate_ctrl_lock);
|
|
||||||
rcu_read_unlock();
|
|
||||||
set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void rate_control_rate_update(struct ieee80211_local *local,
|
|
||||||
struct ieee80211_supported_band *sband,
|
struct ieee80211_supported_band *sband,
|
||||||
struct sta_info *sta, u32 changed)
|
struct sta_info *sta, u32 changed);
|
||||||
{
|
|
||||||
struct rate_control_ref *ref = local->rate_ctrl;
|
|
||||||
struct ieee80211_sta *ista = &sta->sta;
|
|
||||||
void *priv_sta = sta->rate_ctrl_priv;
|
|
||||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
|
||||||
|
|
||||||
if (ref && ref->ops->rate_update) {
|
|
||||||
rcu_read_lock();
|
|
||||||
|
|
||||||
chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
|
|
||||||
if (WARN_ON(!chanctx_conf)) {
|
|
||||||
rcu_read_unlock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock_bh(&sta->rate_ctrl_lock);
|
|
||||||
ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
|
|
||||||
ista, priv_sta, changed);
|
|
||||||
spin_unlock_bh(&sta->rate_ctrl_lock);
|
|
||||||
rcu_read_unlock();
|
|
||||||
}
|
|
||||||
drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void *rate_control_alloc_sta(struct rate_control_ref *ref,
|
static inline void *rate_control_alloc_sta(struct rate_control_ref *ref,
|
||||||
struct sta_info *sta, gfp_t gfp)
|
struct sta_info *sta, gfp_t gfp)
|
||||||
|
|
|
@ -867,7 +867,13 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||||
else
|
else
|
||||||
idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8;
|
idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8;
|
||||||
|
|
||||||
if (offset > 0) {
|
/* enable RTS/CTS if needed:
|
||||||
|
* - if station is in dynamic SMPS (and streams > 1)
|
||||||
|
* - for fallback rates, to increase chances of getting through
|
||||||
|
*/
|
||||||
|
if (offset > 0 &&
|
||||||
|
(mi->sta->smps_mode == IEEE80211_SMPS_DYNAMIC &&
|
||||||
|
group->streams > 1)) {
|
||||||
ratetbl->rate[offset].count = ratetbl->rate[offset].count_rts;
|
ratetbl->rate[offset].count = ratetbl->rate[offset].count_rts;
|
||||||
flags |= IEEE80211_TX_RC_USE_RTS_CTS;
|
flags |= IEEE80211_TX_RC_USE_RTS_CTS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,51 @@ static inline void ieee80211_rx_stats(struct net_device *dev, u32 len)
|
||||||
u64_stats_update_end(&tstats->syncp);
|
u64_stats_update_end(&tstats->syncp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
|
||||||
|
enum nl80211_iftype type)
|
||||||
|
{
|
||||||
|
__le16 fc = hdr->frame_control;
|
||||||
|
|
||||||
|
if (ieee80211_is_data(fc)) {
|
||||||
|
if (len < 24) /* drop incorrect hdr len (data) */
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (ieee80211_has_a4(fc))
|
||||||
|
return NULL;
|
||||||
|
if (ieee80211_has_tods(fc))
|
||||||
|
return hdr->addr1;
|
||||||
|
if (ieee80211_has_fromds(fc))
|
||||||
|
return hdr->addr2;
|
||||||
|
|
||||||
|
return hdr->addr3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ieee80211_is_mgmt(fc)) {
|
||||||
|
if (len < 24) /* drop incorrect hdr len (mgmt) */
|
||||||
|
return NULL;
|
||||||
|
return hdr->addr3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ieee80211_is_ctl(fc)) {
|
||||||
|
if (ieee80211_is_pspoll(fc))
|
||||||
|
return hdr->addr1;
|
||||||
|
|
||||||
|
if (ieee80211_is_back_req(fc)) {
|
||||||
|
switch (type) {
|
||||||
|
case NL80211_IFTYPE_STATION:
|
||||||
|
return hdr->addr2;
|
||||||
|
case NL80211_IFTYPE_AP:
|
||||||
|
case NL80211_IFTYPE_AP_VLAN:
|
||||||
|
return hdr->addr1;
|
||||||
|
default:
|
||||||
|
break; /* fall through to the return */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* monitor mode reception
|
* monitor mode reception
|
||||||
*
|
*
|
||||||
|
@ -77,8 +122,7 @@ static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len,
|
||||||
hdr = (void *)(skb->data + rtap_vendor_space);
|
hdr = (void *)(skb->data + rtap_vendor_space);
|
||||||
|
|
||||||
if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
|
if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
|
||||||
RX_FLAG_FAILED_PLCP_CRC |
|
RX_FLAG_FAILED_PLCP_CRC))
|
||||||
RX_FLAG_AMPDU_IS_ZEROLEN))
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space))
|
if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space))
|
||||||
|
@ -346,10 +390,6 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
|
||||||
cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS);
|
cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS);
|
||||||
put_unaligned_le32(status->ampdu_reference, pos);
|
put_unaligned_le32(status->ampdu_reference, pos);
|
||||||
pos += 4;
|
pos += 4;
|
||||||
if (status->flag & RX_FLAG_AMPDU_REPORT_ZEROLEN)
|
|
||||||
flags |= IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN;
|
|
||||||
if (status->flag & RX_FLAG_AMPDU_IS_ZEROLEN)
|
|
||||||
flags |= IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN;
|
|
||||||
if (status->flag & RX_FLAG_AMPDU_LAST_KNOWN)
|
if (status->flag & RX_FLAG_AMPDU_LAST_KNOWN)
|
||||||
flags |= IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN;
|
flags |= IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN;
|
||||||
if (status->flag & RX_FLAG_AMPDU_IS_LAST)
|
if (status->flag & RX_FLAG_AMPDU_IS_LAST)
|
||||||
|
@ -1093,11 +1133,6 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
|
||||||
{
|
{
|
||||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
|
||||||
|
|
||||||
if (unlikely(rx->skb->len < 16)) {
|
|
||||||
I802_DEBUG_INC(rx->local->rx_handlers_drop_short);
|
|
||||||
return RX_DROP_MONITOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Drop disallowed frame classes based on STA auth/assoc state;
|
/* Drop disallowed frame classes based on STA auth/assoc state;
|
||||||
* IEEE 802.11, Chap 5.5.
|
* IEEE 802.11, Chap 5.5.
|
||||||
*
|
*
|
||||||
|
@ -1240,22 +1275,22 @@ static void sta_ps_end(struct sta_info *sta)
|
||||||
ieee80211_sta_ps_deliver_wakeup(sta);
|
ieee80211_sta_ps_deliver_wakeup(sta);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start)
|
int ieee80211_sta_ps_transition(struct ieee80211_sta *pubsta, bool start)
|
||||||
{
|
{
|
||||||
struct sta_info *sta_inf = container_of(sta, struct sta_info, sta);
|
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
|
||||||
bool in_ps;
|
bool in_ps;
|
||||||
|
|
||||||
WARN_ON(!ieee80211_hw_check(&sta_inf->local->hw, AP_LINK_PS));
|
WARN_ON(!ieee80211_hw_check(&sta->local->hw, AP_LINK_PS));
|
||||||
|
|
||||||
/* Don't let the same PS state be set twice */
|
/* Don't let the same PS state be set twice */
|
||||||
in_ps = test_sta_flag(sta_inf, WLAN_STA_PS_STA);
|
in_ps = test_sta_flag(sta, WLAN_STA_PS_STA);
|
||||||
if ((start && in_ps) || (!start && !in_ps))
|
if ((start && in_ps) || (!start && !in_ps))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (start)
|
if (start)
|
||||||
sta_ps_start(sta_inf);
|
sta_ps_start(sta);
|
||||||
else
|
else
|
||||||
sta_ps_end(sta_inf);
|
sta_ps_end(sta);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1393,7 +1428,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
|
||||||
sta->rx_bytes += rx->skb->len;
|
sta->rx_bytes += rx->skb->len;
|
||||||
if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
|
if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
|
||||||
sta->last_signal = status->signal;
|
sta->last_signal = status->signal;
|
||||||
ewma_add(&sta->avg_signal, -status->signal);
|
ewma_signal_add(&sta->avg_signal, -status->signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status->chains) {
|
if (status->chains) {
|
||||||
|
@ -1405,7 +1440,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
sta->chain_signal_last[i] = signal;
|
sta->chain_signal_last[i] = signal;
|
||||||
ewma_add(&sta->chain_signal_avg[i], -signal);
|
ewma_signal_add(&sta->chain_signal_avg[i], -signal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1647,7 +1682,6 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
|
||||||
if (unlikely(rx->key->flags & KEY_FLAG_TAINTED))
|
if (unlikely(rx->key->flags & KEY_FLAG_TAINTED))
|
||||||
return RX_DROP_MONITOR;
|
return RX_DROP_MONITOR;
|
||||||
|
|
||||||
rx->key->tx_rx_count++;
|
|
||||||
/* TODO: add threshold stuff again */
|
/* TODO: add threshold stuff again */
|
||||||
} else {
|
} else {
|
||||||
return RX_DROP_MONITOR;
|
return RX_DROP_MONITOR;
|
||||||
|
@ -1883,7 +1917,6 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
|
||||||
|
|
||||||
/* Complete frame has been reassembled - process it now */
|
/* Complete frame has been reassembled - process it now */
|
||||||
status = IEEE80211_SKB_RXCB(rx->skb);
|
status = IEEE80211_SKB_RXCB(rx->skb);
|
||||||
status->rx_flags |= IEEE80211_RX_FRAGMENTED;
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
ieee80211_led_rx(rx->local);
|
ieee80211_led_rx(rx->local);
|
||||||
|
@ -2108,9 +2141,8 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
|
||||||
/* deliver to local stack */
|
/* deliver to local stack */
|
||||||
skb->protocol = eth_type_trans(skb, dev);
|
skb->protocol = eth_type_trans(skb, dev);
|
||||||
memset(skb->cb, 0, sizeof(skb->cb));
|
memset(skb->cb, 0, sizeof(skb->cb));
|
||||||
if (!(rx->flags & IEEE80211_RX_REORDER_TIMER) &&
|
if (rx->napi)
|
||||||
rx->local->napi)
|
napi_gro_receive(rx->napi, skb);
|
||||||
napi_gro_receive(rx->local->napi, skb);
|
|
||||||
else
|
else
|
||||||
netif_receive_skb(skb);
|
netif_receive_skb(skb);
|
||||||
}
|
}
|
||||||
|
@ -2378,9 +2410,8 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
|
||||||
tf->category == WLAN_CATEGORY_TDLS &&
|
tf->category == WLAN_CATEGORY_TDLS &&
|
||||||
(tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
|
(tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
|
||||||
tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
|
tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
|
||||||
rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TDLS_CHSW;
|
skb_queue_tail(&local->skb_queue_tdls_chsw, rx->skb);
|
||||||
skb_queue_tail(&sdata->skb_queue, rx->skb);
|
schedule_work(&local->tdls_chsw_work);
|
||||||
ieee80211_queue_work(&rx->local->hw, &sdata->work);
|
|
||||||
if (rx->sta)
|
if (rx->sta)
|
||||||
rx->sta->rx_packets++;
|
rx->sta->rx_packets++;
|
||||||
|
|
||||||
|
@ -3004,7 +3035,6 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
|
||||||
return RX_QUEUED;
|
return RX_QUEUED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: use IEEE80211_RX_FRAGMENTED */
|
|
||||||
static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
|
static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
|
||||||
struct ieee80211_rate *rate)
|
struct ieee80211_rate *rate)
|
||||||
{
|
{
|
||||||
|
@ -3216,7 +3246,7 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
|
||||||
/* This is OK -- must be QoS data frame */
|
/* This is OK -- must be QoS data frame */
|
||||||
.security_idx = tid,
|
.security_idx = tid,
|
||||||
.seqno_idx = tid,
|
.seqno_idx = tid,
|
||||||
.flags = IEEE80211_RX_REORDER_TIMER,
|
.napi = NULL, /* must be NULL to not have races */
|
||||||
};
|
};
|
||||||
struct tid_ampdu_rx *tid_agg_rx;
|
struct tid_ampdu_rx *tid_agg_rx;
|
||||||
|
|
||||||
|
@ -3286,7 +3316,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
|
||||||
case NL80211_IFTYPE_OCB:
|
case NL80211_IFTYPE_OCB:
|
||||||
if (!bssid)
|
if (!bssid)
|
||||||
return false;
|
return false;
|
||||||
if (ieee80211_is_beacon(hdr->frame_control))
|
if (!ieee80211_is_data_present(hdr->frame_control))
|
||||||
return false;
|
return false;
|
||||||
if (!is_broadcast_ether_addr(bssid))
|
if (!is_broadcast_ether_addr(bssid))
|
||||||
return false;
|
return false;
|
||||||
|
@ -3393,7 +3423,8 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
|
||||||
* be called with rcu_read_lock protection.
|
* be called with rcu_read_lock protection.
|
||||||
*/
|
*/
|
||||||
static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
|
static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb,
|
||||||
|
struct napi_struct *napi)
|
||||||
{
|
{
|
||||||
struct ieee80211_local *local = hw_to_local(hw);
|
struct ieee80211_local *local = hw_to_local(hw);
|
||||||
struct ieee80211_sub_if_data *sdata;
|
struct ieee80211_sub_if_data *sdata;
|
||||||
|
@ -3409,6 +3440,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
|
||||||
memset(&rx, 0, sizeof(rx));
|
memset(&rx, 0, sizeof(rx));
|
||||||
rx.skb = skb;
|
rx.skb = skb;
|
||||||
rx.local = local;
|
rx.local = local;
|
||||||
|
rx.napi = napi;
|
||||||
|
|
||||||
if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))
|
if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))
|
||||||
I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
|
I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
|
||||||
|
@ -3510,7 +3542,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
|
||||||
* This is the receive path handler. It is called by a low level driver when an
|
* This is the receive path handler. It is called by a low level driver when an
|
||||||
* 802.11 MPDU is received from the hardware.
|
* 802.11 MPDU is received from the hardware.
|
||||||
*/
|
*/
|
||||||
void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
|
void ieee80211_rx_napi(struct ieee80211_hw *hw, struct sk_buff *skb,
|
||||||
|
struct napi_struct *napi)
|
||||||
{
|
{
|
||||||
struct ieee80211_local *local = hw_to_local(hw);
|
struct ieee80211_local *local = hw_to_local(hw);
|
||||||
struct ieee80211_rate *rate = NULL;
|
struct ieee80211_rate *rate = NULL;
|
||||||
|
@ -3609,7 +3642,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
|
||||||
ieee80211_tpt_led_trig_rx(local,
|
ieee80211_tpt_led_trig_rx(local,
|
||||||
((struct ieee80211_hdr *)skb->data)->frame_control,
|
((struct ieee80211_hdr *)skb->data)->frame_control,
|
||||||
skb->len);
|
skb->len);
|
||||||
__ieee80211_rx_handle_packet(hw, skb);
|
__ieee80211_rx_handle_packet(hw, skb, napi);
|
||||||
|
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
@ -3617,7 +3650,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
|
||||||
drop:
|
drop:
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ieee80211_rx);
|
EXPORT_SYMBOL(ieee80211_rx_napi);
|
||||||
|
|
||||||
/* This is a version of the rx handler that can be called from hard irq
|
/* This is a version of the rx handler that can be called from hard irq
|
||||||
* context. Post the skb on the queue and schedule the tasklet */
|
* context. Post the skb on the queue and schedule the tasklet */
|
||||||
|
|
|
@ -68,7 +68,7 @@ static const struct rhashtable_params sta_rht_params = {
|
||||||
.nelem_hint = 3, /* start small */
|
.nelem_hint = 3, /* start small */
|
||||||
.automatic_shrinking = true,
|
.automatic_shrinking = true,
|
||||||
.head_offset = offsetof(struct sta_info, hash_node),
|
.head_offset = offsetof(struct sta_info, hash_node),
|
||||||
.key_offset = offsetof(struct sta_info, sta.addr),
|
.key_offset = offsetof(struct sta_info, addr),
|
||||||
.key_len = ETH_ALEN,
|
.key_len = ETH_ALEN,
|
||||||
.hashfn = sta_addr_hash,
|
.hashfn = sta_addr_hash,
|
||||||
.max_size = CONFIG_MAC80211_STA_HASH_MAX_SIZE,
|
.max_size = CONFIG_MAC80211_STA_HASH_MAX_SIZE,
|
||||||
|
@ -249,6 +249,9 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
|
||||||
if (sta->sta.txq[0])
|
if (sta->sta.txq[0])
|
||||||
kfree(to_txq_info(sta->sta.txq[0]));
|
kfree(to_txq_info(sta->sta.txq[0]));
|
||||||
kfree(rcu_dereference_raw(sta->sta.rates));
|
kfree(rcu_dereference_raw(sta->sta.rates));
|
||||||
|
#ifdef CONFIG_MAC80211_MESH
|
||||||
|
kfree(sta->mesh);
|
||||||
|
#endif
|
||||||
kfree(sta);
|
kfree(sta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,13 +316,19 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
||||||
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
|
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
|
||||||
mutex_init(&sta->ampdu_mlme.mtx);
|
mutex_init(&sta->ampdu_mlme.mtx);
|
||||||
#ifdef CONFIG_MAC80211_MESH
|
#ifdef CONFIG_MAC80211_MESH
|
||||||
spin_lock_init(&sta->plink_lock);
|
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||||
if (ieee80211_vif_is_mesh(&sdata->vif) &&
|
sta->mesh = kzalloc(sizeof(*sta->mesh), gfp);
|
||||||
!sdata->u.mesh.user_mpm)
|
if (!sta->mesh)
|
||||||
init_timer(&sta->plink_timer);
|
goto free;
|
||||||
sta->nonpeer_pm = NL80211_MESH_POWER_ACTIVE;
|
spin_lock_init(&sta->mesh->plink_lock);
|
||||||
|
if (ieee80211_vif_is_mesh(&sdata->vif) &&
|
||||||
|
!sdata->u.mesh.user_mpm)
|
||||||
|
init_timer(&sta->mesh->plink_timer);
|
||||||
|
sta->mesh->nonpeer_pm = NL80211_MESH_POWER_ACTIVE;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
memcpy(sta->addr, addr, ETH_ALEN);
|
||||||
memcpy(sta->sta.addr, addr, ETH_ALEN);
|
memcpy(sta->sta.addr, addr, ETH_ALEN);
|
||||||
sta->local = local;
|
sta->local = local;
|
||||||
sta->sdata = sdata;
|
sta->sdata = sdata;
|
||||||
|
@ -332,9 +341,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
||||||
|
|
||||||
ktime_get_ts(&uptime);
|
ktime_get_ts(&uptime);
|
||||||
sta->last_connected = uptime.tv_sec;
|
sta->last_connected = uptime.tv_sec;
|
||||||
ewma_init(&sta->avg_signal, 1024, 8);
|
ewma_signal_init(&sta->avg_signal);
|
||||||
for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
|
for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
|
||||||
ewma_init(&sta->chain_signal_avg[i], 1024, 8);
|
ewma_signal_init(&sta->chain_signal_avg[i]);
|
||||||
|
|
||||||
if (local->ops->wake_tx_queue) {
|
if (local->ops->wake_tx_queue) {
|
||||||
void *txq_data;
|
void *txq_data;
|
||||||
|
@ -405,6 +414,9 @@ free_txq:
|
||||||
if (sta->sta.txq[0])
|
if (sta->sta.txq[0])
|
||||||
kfree(to_txq_info(sta->sta.txq[0]));
|
kfree(to_txq_info(sta->sta.txq[0]));
|
||||||
free:
|
free:
|
||||||
|
#ifdef CONFIG_MAC80211_MESH
|
||||||
|
kfree(sta->mesh);
|
||||||
|
#endif
|
||||||
kfree(sta);
|
kfree(sta);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -623,7 +635,7 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending)
|
||||||
bool indicate_tim = false;
|
bool indicate_tim = false;
|
||||||
u8 ignore_for_tim = sta->sta.uapsd_queues;
|
u8 ignore_for_tim = sta->sta.uapsd_queues;
|
||||||
int ac;
|
int ac;
|
||||||
u16 id;
|
u16 id = sta->sta.aid;
|
||||||
|
|
||||||
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
|
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
|
||||||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
|
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
|
||||||
|
@ -631,12 +643,9 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ps = &sta->sdata->bss->ps;
|
ps = &sta->sdata->bss->ps;
|
||||||
id = sta->sta.aid;
|
|
||||||
#ifdef CONFIG_MAC80211_MESH
|
#ifdef CONFIG_MAC80211_MESH
|
||||||
} else if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
|
} else if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
|
||||||
ps = &sta->sdata->u.mesh.ps;
|
ps = &sta->sdata->u.mesh.ps;
|
||||||
/* TIM map only for 1 <= PLID <= IEEE80211_MAX_AID */
|
|
||||||
id = sta->plid % (IEEE80211_MAX_AID + 1);
|
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
|
@ -1887,7 +1896,8 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))) {
|
if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))) {
|
||||||
sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
|
sinfo->signal_avg =
|
||||||
|
(s8) -ewma_signal_read(&sta->avg_signal);
|
||||||
sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG);
|
sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1902,7 +1912,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
||||||
for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
|
for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
|
||||||
sinfo->chain_signal[i] = sta->chain_signal_last[i];
|
sinfo->chain_signal[i] = sta->chain_signal_last[i];
|
||||||
sinfo->chain_signal_avg[i] =
|
sinfo->chain_signal_avg[i] =
|
||||||
(s8) -ewma_read(&sta->chain_signal_avg[i]);
|
(s8) -ewma_signal_read(&sta->chain_signal_avg[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1956,16 +1966,16 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
||||||
BIT(NL80211_STA_INFO_PEER_PM) |
|
BIT(NL80211_STA_INFO_PEER_PM) |
|
||||||
BIT(NL80211_STA_INFO_NONPEER_PM);
|
BIT(NL80211_STA_INFO_NONPEER_PM);
|
||||||
|
|
||||||
sinfo->llid = sta->llid;
|
sinfo->llid = sta->mesh->llid;
|
||||||
sinfo->plid = sta->plid;
|
sinfo->plid = sta->mesh->plid;
|
||||||
sinfo->plink_state = sta->plink_state;
|
sinfo->plink_state = sta->mesh->plink_state;
|
||||||
if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
|
if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
|
||||||
sinfo->filled |= BIT(NL80211_STA_INFO_T_OFFSET);
|
sinfo->filled |= BIT(NL80211_STA_INFO_T_OFFSET);
|
||||||
sinfo->t_offset = sta->t_offset;
|
sinfo->t_offset = sta->mesh->t_offset;
|
||||||
}
|
}
|
||||||
sinfo->local_pm = sta->local_pm;
|
sinfo->local_pm = sta->mesh->local_pm;
|
||||||
sinfo->peer_pm = sta->peer_pm;
|
sinfo->peer_pm = sta->mesh->peer_pm;
|
||||||
sinfo->nonpeer_pm = sta->nonpeer_pm;
|
sinfo->nonpeer_pm = sta->mesh->nonpeer_pm;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,8 @@
|
||||||
* @WLAN_STA_TDLS_CHAN_SWITCH: This TDLS peer supports TDLS channel-switching
|
* @WLAN_STA_TDLS_CHAN_SWITCH: This TDLS peer supports TDLS channel-switching
|
||||||
* @WLAN_STA_TDLS_OFF_CHANNEL: The local STA is currently off-channel with this
|
* @WLAN_STA_TDLS_OFF_CHANNEL: The local STA is currently off-channel with this
|
||||||
* TDLS peer
|
* TDLS peer
|
||||||
|
* @WLAN_STA_TDLS_WIDER_BW: This TDLS peer supports working on a wider bw on
|
||||||
|
* the BSS base channel.
|
||||||
* @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
|
* @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
|
||||||
* keeping station in power-save mode, reply when the driver
|
* keeping station in power-save mode, reply when the driver
|
||||||
* unblocks the station.
|
* unblocks the station.
|
||||||
|
@ -84,6 +86,7 @@ enum ieee80211_sta_info_flags {
|
||||||
WLAN_STA_TDLS_INITIATOR,
|
WLAN_STA_TDLS_INITIATOR,
|
||||||
WLAN_STA_TDLS_CHAN_SWITCH,
|
WLAN_STA_TDLS_CHAN_SWITCH,
|
||||||
WLAN_STA_TDLS_OFF_CHANNEL,
|
WLAN_STA_TDLS_OFF_CHANNEL,
|
||||||
|
WLAN_STA_TDLS_WIDER_BW,
|
||||||
WLAN_STA_UAPSD,
|
WLAN_STA_UAPSD,
|
||||||
WLAN_STA_SP,
|
WLAN_STA_SP,
|
||||||
WLAN_STA_4ADDR_EVENT,
|
WLAN_STA_4ADDR_EVENT,
|
||||||
|
@ -269,6 +272,56 @@ struct ieee80211_fast_tx {
|
||||||
struct rcu_head rcu_head;
|
struct rcu_head rcu_head;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct mesh_sta - mesh STA information
|
||||||
|
* @plink_lock: serialize access to plink fields
|
||||||
|
* @llid: Local link ID
|
||||||
|
* @plid: Peer link ID
|
||||||
|
* @aid: local aid supplied by peer
|
||||||
|
* @reason: Cancel reason on PLINK_HOLDING state
|
||||||
|
* @plink_retries: Retries in establishment
|
||||||
|
* @plink_state: peer link state
|
||||||
|
* @plink_timeout: timeout of peer link
|
||||||
|
* @plink_timer: peer link watch timer
|
||||||
|
* @t_offset: timing offset relative to this host
|
||||||
|
* @t_offset_setpoint: reference timing offset of this sta to be used when
|
||||||
|
* calculating clockdrift
|
||||||
|
* @local_pm: local link-specific power save mode
|
||||||
|
* @peer_pm: peer-specific power save mode towards local STA
|
||||||
|
* @nonpeer_pm: STA power save mode towards non-peer neighbors
|
||||||
|
* @processed_beacon: set to true after peer rates and capabilities are
|
||||||
|
* processed
|
||||||
|
* @fail_avg: moving percentage of failed MSDUs
|
||||||
|
*/
|
||||||
|
struct mesh_sta {
|
||||||
|
struct timer_list plink_timer;
|
||||||
|
|
||||||
|
s64 t_offset;
|
||||||
|
s64 t_offset_setpoint;
|
||||||
|
|
||||||
|
spinlock_t plink_lock;
|
||||||
|
u16 llid;
|
||||||
|
u16 plid;
|
||||||
|
u16 aid;
|
||||||
|
u16 reason;
|
||||||
|
u8 plink_retries;
|
||||||
|
|
||||||
|
bool processed_beacon;
|
||||||
|
|
||||||
|
enum nl80211_plink_state plink_state;
|
||||||
|
u32 plink_timeout;
|
||||||
|
|
||||||
|
/* mesh power save */
|
||||||
|
enum nl80211_mesh_power_mode local_pm;
|
||||||
|
enum nl80211_mesh_power_mode peer_pm;
|
||||||
|
enum nl80211_mesh_power_mode nonpeer_pm;
|
||||||
|
|
||||||
|
/* moving percentage of failed MSDUs */
|
||||||
|
unsigned int fail_avg;
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_EWMA(signal, 1024, 8)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct sta_info - STA information
|
* struct sta_info - STA information
|
||||||
*
|
*
|
||||||
|
@ -278,12 +331,13 @@ struct ieee80211_fast_tx {
|
||||||
* @list: global linked list entry
|
* @list: global linked list entry
|
||||||
* @free_list: list entry for keeping track of stations to free
|
* @free_list: list entry for keeping track of stations to free
|
||||||
* @hash_node: hash node for rhashtable
|
* @hash_node: hash node for rhashtable
|
||||||
|
* @addr: station's MAC address - duplicated from public part to
|
||||||
|
* let the hash table work with just a single cacheline
|
||||||
* @local: pointer to the global information
|
* @local: pointer to the global information
|
||||||
* @sdata: virtual interface this station belongs to
|
* @sdata: virtual interface this station belongs to
|
||||||
* @ptk: peer keys negotiated with this station, if any
|
* @ptk: peer keys negotiated with this station, if any
|
||||||
* @ptk_idx: last installed peer key index
|
* @ptk_idx: last installed peer key index
|
||||||
* @gtk: group keys negotiated with this station, if any
|
* @gtk: group keys negotiated with this station, if any
|
||||||
* @gtk_idx: last installed group key index
|
|
||||||
* @rate_ctrl: rate control algorithm reference
|
* @rate_ctrl: rate control algorithm reference
|
||||||
* @rate_ctrl_lock: spinlock used to protect rate control data
|
* @rate_ctrl_lock: spinlock used to protect rate control data
|
||||||
* (data inside the algorithm, so serializes calls there)
|
* (data inside the algorithm, so serializes calls there)
|
||||||
|
@ -318,30 +372,17 @@ struct ieee80211_fast_tx {
|
||||||
* @last_signal: signal of last received frame from this STA
|
* @last_signal: signal of last received frame from this STA
|
||||||
* @avg_signal: moving average of signal of received frames from this STA
|
* @avg_signal: moving average of signal of received frames from this STA
|
||||||
* @last_ack_signal: signal of last received Ack frame from this STA
|
* @last_ack_signal: signal of last received Ack frame from this STA
|
||||||
* @last_seq_ctrl: last received seq/frag number from this STA (per RX queue)
|
* @last_seq_ctrl: last received seq/frag number from this STA (per TID
|
||||||
|
* plus one for non-QoS frames)
|
||||||
* @tx_filtered_count: number of frames the hardware filtered for this STA
|
* @tx_filtered_count: number of frames the hardware filtered for this STA
|
||||||
* @tx_retry_failed: number of frames that failed retry
|
* @tx_retry_failed: number of frames that failed retry
|
||||||
* @tx_retry_count: total number of retries for frames to this STA
|
* @tx_retry_count: total number of retries for frames to this STA
|
||||||
* @fail_avg: moving percentage of failed MSDUs
|
|
||||||
* @tx_packets: number of RX/TX MSDUs
|
* @tx_packets: number of RX/TX MSDUs
|
||||||
* @tx_bytes: number of bytes transmitted to this STA
|
* @tx_bytes: number of bytes transmitted to this STA
|
||||||
* @tid_seq: per-TID sequence numbers for sending to this STA
|
* @tid_seq: per-TID sequence numbers for sending to this STA
|
||||||
* @ampdu_mlme: A-MPDU state machine state
|
* @ampdu_mlme: A-MPDU state machine state
|
||||||
* @timer_to_tid: identity mapping to ID timers
|
* @timer_to_tid: identity mapping to ID timers
|
||||||
* @plink_lock: serialize access to plink fields
|
* @mesh: mesh STA information
|
||||||
* @llid: Local link ID
|
|
||||||
* @plid: Peer link ID
|
|
||||||
* @reason: Cancel reason on PLINK_HOLDING state
|
|
||||||
* @plink_retries: Retries in establishment
|
|
||||||
* @plink_state: peer link state
|
|
||||||
* @plink_timeout: timeout of peer link
|
|
||||||
* @plink_timer: peer link watch timer
|
|
||||||
* @t_offset: timing offset relative to this host
|
|
||||||
* @t_offset_setpoint: reference timing offset of this sta to be used when
|
|
||||||
* calculating clockdrift
|
|
||||||
* @local_pm: local link-specific power save mode
|
|
||||||
* @peer_pm: peer-specific power save mode towards local STA
|
|
||||||
* @nonpeer_pm: STA power save mode towards non-peer neighbors
|
|
||||||
* @debugfs: debug filesystem info
|
* @debugfs: debug filesystem info
|
||||||
* @dead: set to true when sta is unlinked
|
* @dead: set to true when sta is unlinked
|
||||||
* @uploaded: set to true when sta is uploaded to the driver
|
* @uploaded: set to true when sta is uploaded to the driver
|
||||||
|
@ -369,19 +410,19 @@ struct ieee80211_fast_tx {
|
||||||
* @rx_msdu: MSDUs received from this station, using IEEE80211_NUM_TID
|
* @rx_msdu: MSDUs received from this station, using IEEE80211_NUM_TID
|
||||||
* entry for non-QoS frames
|
* entry for non-QoS frames
|
||||||
* @fast_tx: TX fastpath information
|
* @fast_tx: TX fastpath information
|
||||||
* @processed_beacon: set to true after peer rates and capabilities are
|
* @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to
|
||||||
* processed
|
* the BSS one.
|
||||||
*/
|
*/
|
||||||
struct sta_info {
|
struct sta_info {
|
||||||
/* General information, mostly static */
|
/* General information, mostly static */
|
||||||
struct list_head list, free_list;
|
struct list_head list, free_list;
|
||||||
struct rcu_head rcu_head;
|
struct rcu_head rcu_head;
|
||||||
struct rhash_head hash_node;
|
struct rhash_head hash_node;
|
||||||
|
u8 addr[ETH_ALEN];
|
||||||
struct ieee80211_local *local;
|
struct ieee80211_local *local;
|
||||||
struct ieee80211_sub_if_data *sdata;
|
struct ieee80211_sub_if_data *sdata;
|
||||||
struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
|
struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
|
||||||
struct ieee80211_key __rcu *ptk[NUM_DEFAULT_KEYS];
|
struct ieee80211_key __rcu *ptk[NUM_DEFAULT_KEYS];
|
||||||
u8 gtk_idx;
|
|
||||||
u8 ptk_idx;
|
u8 ptk_idx;
|
||||||
struct rate_control_ref *rate_ctrl;
|
struct rate_control_ref *rate_ctrl;
|
||||||
void *rate_ctrl_priv;
|
void *rate_ctrl_priv;
|
||||||
|
@ -390,6 +431,10 @@ struct sta_info {
|
||||||
|
|
||||||
struct ieee80211_fast_tx __rcu *fast_tx;
|
struct ieee80211_fast_tx __rcu *fast_tx;
|
||||||
|
|
||||||
|
#ifdef CONFIG_MAC80211_MESH
|
||||||
|
struct mesh_sta *mesh;
|
||||||
|
#endif
|
||||||
|
|
||||||
struct work_struct drv_deliver_wk;
|
struct work_struct drv_deliver_wk;
|
||||||
|
|
||||||
u16 listen_interval;
|
u16 listen_interval;
|
||||||
|
@ -419,12 +464,12 @@ struct sta_info {
|
||||||
unsigned long rx_fragments;
|
unsigned long rx_fragments;
|
||||||
unsigned long rx_dropped;
|
unsigned long rx_dropped;
|
||||||
int last_signal;
|
int last_signal;
|
||||||
struct ewma avg_signal;
|
struct ewma_signal avg_signal;
|
||||||
int last_ack_signal;
|
int last_ack_signal;
|
||||||
|
|
||||||
u8 chains;
|
u8 chains;
|
||||||
s8 chain_signal_last[IEEE80211_MAX_CHAINS];
|
s8 chain_signal_last[IEEE80211_MAX_CHAINS];
|
||||||
struct ewma chain_signal_avg[IEEE80211_MAX_CHAINS];
|
struct ewma_signal chain_signal_avg[IEEE80211_MAX_CHAINS];
|
||||||
|
|
||||||
/* Plus 1 for non-QoS frames */
|
/* Plus 1 for non-QoS frames */
|
||||||
__le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];
|
__le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];
|
||||||
|
@ -432,8 +477,6 @@ struct sta_info {
|
||||||
/* Updated from TX status path only, no locking requirements */
|
/* Updated from TX status path only, no locking requirements */
|
||||||
unsigned long tx_filtered_count;
|
unsigned long tx_filtered_count;
|
||||||
unsigned long tx_retry_failed, tx_retry_count;
|
unsigned long tx_retry_failed, tx_retry_count;
|
||||||
/* moving percentage of failed MSDUs */
|
|
||||||
unsigned int fail_avg;
|
|
||||||
|
|
||||||
/* Updated from TX path only, no locking requirements */
|
/* Updated from TX path only, no locking requirements */
|
||||||
u64 tx_packets[IEEE80211_NUM_ACS];
|
u64 tx_packets[IEEE80211_NUM_ACS];
|
||||||
|
@ -455,29 +498,6 @@ struct sta_info {
|
||||||
struct sta_ampdu_mlme ampdu_mlme;
|
struct sta_ampdu_mlme ampdu_mlme;
|
||||||
u8 timer_to_tid[IEEE80211_NUM_TIDS];
|
u8 timer_to_tid[IEEE80211_NUM_TIDS];
|
||||||
|
|
||||||
#ifdef CONFIG_MAC80211_MESH
|
|
||||||
/*
|
|
||||||
* Mesh peer link attributes, protected by plink_lock.
|
|
||||||
* TODO: move to a sub-structure that is referenced with pointer?
|
|
||||||
*/
|
|
||||||
spinlock_t plink_lock;
|
|
||||||
u16 llid;
|
|
||||||
u16 plid;
|
|
||||||
u16 reason;
|
|
||||||
u8 plink_retries;
|
|
||||||
enum nl80211_plink_state plink_state;
|
|
||||||
u32 plink_timeout;
|
|
||||||
struct timer_list plink_timer;
|
|
||||||
|
|
||||||
s64 t_offset;
|
|
||||||
s64 t_offset_setpoint;
|
|
||||||
/* mesh power save */
|
|
||||||
enum nl80211_mesh_power_mode local_pm;
|
|
||||||
enum nl80211_mesh_power_mode peer_pm;
|
|
||||||
enum nl80211_mesh_power_mode nonpeer_pm;
|
|
||||||
bool processed_beacon;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||||
struct sta_info_debugfsdentries {
|
struct sta_info_debugfsdentries {
|
||||||
struct dentry *dir;
|
struct dentry *dir;
|
||||||
|
@ -498,6 +518,8 @@ struct sta_info {
|
||||||
|
|
||||||
u8 reserved_tid;
|
u8 reserved_tid;
|
||||||
|
|
||||||
|
struct cfg80211_chan_def tdls_chandef;
|
||||||
|
|
||||||
/* keep last! */
|
/* keep last! */
|
||||||
struct ieee80211_sta sta;
|
struct ieee80211_sta sta;
|
||||||
};
|
};
|
||||||
|
@ -505,7 +527,7 @@ struct sta_info {
|
||||||
static inline enum nl80211_plink_state sta_plink_state(struct sta_info *sta)
|
static inline enum nl80211_plink_state sta_plink_state(struct sta_info *sta)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_MAC80211_MESH
|
#ifdef CONFIG_MAC80211_MESH
|
||||||
return sta->plink_state;
|
return sta->mesh->plink_state;
|
||||||
#endif
|
#endif
|
||||||
return NL80211_PLINK_LISTEN;
|
return NL80211_PLINK_LISTEN;
|
||||||
}
|
}
|
||||||
|
@ -608,7 +630,7 @@ u32 sta_addr_hash(const void *key, u32 length, u32 seed);
|
||||||
_sta_bucket_idx(tbl, _addr), \
|
_sta_bucket_idx(tbl, _addr), \
|
||||||
hash_node) \
|
hash_node) \
|
||||||
/* compare address and run code only if it matches */ \
|
/* compare address and run code only if it matches */ \
|
||||||
if (ether_addr_equal(_sta->sta.addr, (_addr)))
|
if (ether_addr_equal(_sta->addr, (_addr)))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get STA info by index, BROKEN!
|
* Get STA info by index, BROKEN!
|
||||||
|
|
|
@ -515,7 +515,7 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
|
||||||
|
|
||||||
if (!sdata) {
|
if (!sdata) {
|
||||||
skb->dev = NULL;
|
skb->dev = NULL;
|
||||||
} else if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) {
|
} else {
|
||||||
unsigned int hdr_size =
|
unsigned int hdr_size =
|
||||||
ieee80211_hdrlen(hdr->frame_control);
|
ieee80211_hdrlen(hdr->frame_control);
|
||||||
|
|
||||||
|
@ -529,9 +529,6 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
|
||||||
ieee80211_mgd_conn_tx_status(sdata,
|
ieee80211_mgd_conn_tx_status(sdata,
|
||||||
hdr->frame_control,
|
hdr->frame_control,
|
||||||
acked);
|
acked);
|
||||||
} else {
|
|
||||||
/* we assign ack frame ID for the others */
|
|
||||||
WARN_ON(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
|
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
|
||||||
* Copyright 2014, Intel Corporation
|
* Copyright 2014, Intel Corporation
|
||||||
* Copyright 2014 Intel Mobile Communications GmbH
|
* Copyright 2014 Intel Mobile Communications GmbH
|
||||||
|
* Copyright 2015 Intel Deutschland GmbH
|
||||||
*
|
*
|
||||||
* This file is GPLv2 as found in COPYING.
|
* This file is GPLv2 as found in COPYING.
|
||||||
*/
|
*/
|
||||||
|
@ -11,6 +12,7 @@
|
||||||
#include <linux/ieee80211.h>
|
#include <linux/ieee80211.h>
|
||||||
#include <linux/log2.h>
|
#include <linux/log2.h>
|
||||||
#include <net/cfg80211.h>
|
#include <net/cfg80211.h>
|
||||||
|
#include <linux/rtnetlink.h>
|
||||||
#include "ieee80211_i.h"
|
#include "ieee80211_i.h"
|
||||||
#include "driver-ops.h"
|
#include "driver-ops.h"
|
||||||
|
|
||||||
|
@ -35,20 +37,28 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk)
|
||||||
mutex_unlock(&local->mtx);
|
mutex_unlock(&local->mtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ieee80211_tdls_add_ext_capab(struct ieee80211_local *local,
|
static void ieee80211_tdls_add_ext_capab(struct ieee80211_sub_if_data *sdata,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
u8 *pos = (void *)skb_put(skb, 7);
|
struct ieee80211_local *local = sdata->local;
|
||||||
bool chan_switch = local->hw.wiphy->features &
|
bool chan_switch = local->hw.wiphy->features &
|
||||||
NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
|
NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
|
||||||
|
bool wider_band = ieee80211_hw_check(&local->hw, TDLS_WIDER_BW);
|
||||||
|
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
|
||||||
|
struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
|
||||||
|
bool vht = sband && sband->vht_cap.vht_supported;
|
||||||
|
u8 *pos = (void *)skb_put(skb, 10);
|
||||||
|
|
||||||
*pos++ = WLAN_EID_EXT_CAPABILITY;
|
*pos++ = WLAN_EID_EXT_CAPABILITY;
|
||||||
*pos++ = 5; /* len */
|
*pos++ = 8; /* len */
|
||||||
*pos++ = 0x0;
|
*pos++ = 0x0;
|
||||||
*pos++ = 0x0;
|
*pos++ = 0x0;
|
||||||
*pos++ = 0x0;
|
*pos++ = 0x0;
|
||||||
*pos++ = chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0;
|
*pos++ = chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0;
|
||||||
*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
|
*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
|
||||||
|
*pos++ = 0;
|
||||||
|
*pos++ = 0;
|
||||||
|
*pos++ = (vht && wider_band) ? WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8
|
static u8
|
||||||
|
@ -283,6 +293,60 @@ static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct sta_info *sta)
|
||||||
|
{
|
||||||
|
/* IEEE802.11ac-2013 Table E-4 */
|
||||||
|
u16 centers_80mhz[] = { 5210, 5290, 5530, 5610, 5690, 5775 };
|
||||||
|
struct cfg80211_chan_def uc = sta->tdls_chandef;
|
||||||
|
enum nl80211_chan_width max_width = ieee80211_get_sta_bw(&sta->sta);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* only support upgrading non-narrow channels up to 80Mhz */
|
||||||
|
if (max_width == NL80211_CHAN_WIDTH_5 ||
|
||||||
|
max_width == NL80211_CHAN_WIDTH_10)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (max_width > NL80211_CHAN_WIDTH_80)
|
||||||
|
max_width = NL80211_CHAN_WIDTH_80;
|
||||||
|
|
||||||
|
if (uc.width == max_width)
|
||||||
|
return;
|
||||||
|
/*
|
||||||
|
* Channel usage constrains in the IEEE802.11ac-2013 specification only
|
||||||
|
* allow expanding a 20MHz channel to 80MHz in a single way. In
|
||||||
|
* addition, there are no 40MHz allowed channels that are not part of
|
||||||
|
* the allowed 80MHz range in the 5GHz spectrum (the relevant one here).
|
||||||
|
*/
|
||||||
|
for (i = 0; i < ARRAY_SIZE(centers_80mhz); i++)
|
||||||
|
if (abs(uc.chan->center_freq - centers_80mhz[i]) <= 30) {
|
||||||
|
uc.center_freq1 = centers_80mhz[i];
|
||||||
|
uc.width = NL80211_CHAN_WIDTH_80;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!uc.center_freq1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* proceed to downgrade the chandef until usable or the same */
|
||||||
|
while (uc.width > max_width &&
|
||||||
|
!cfg80211_reg_can_beacon(sdata->local->hw.wiphy,
|
||||||
|
&uc, sdata->wdev.iftype))
|
||||||
|
ieee80211_chandef_downgrade(&uc);
|
||||||
|
|
||||||
|
if (!cfg80211_chandef_identical(&uc, &sta->tdls_chandef)) {
|
||||||
|
tdls_dbg(sdata, "TDLS ch width upgraded %d -> %d\n",
|
||||||
|
sta->tdls_chandef.width, uc.width);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the station is not yet authorized when BW upgrade is done,
|
||||||
|
* locking is not required
|
||||||
|
*/
|
||||||
|
sta->tdls_chandef = uc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
|
ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
|
||||||
struct sk_buff *skb, const u8 *peer,
|
struct sk_buff *skb, const u8 *peer,
|
||||||
|
@ -320,7 +384,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
|
||||||
offset = noffset;
|
offset = noffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
ieee80211_tdls_add_ext_capab(local, skb);
|
ieee80211_tdls_add_ext_capab(sdata, skb);
|
||||||
|
|
||||||
/* add the QoS element if we support it */
|
/* add the QoS element if we support it */
|
||||||
if (local->hw.queues >= IEEE80211_NUM_ACS &&
|
if (local->hw.queues >= IEEE80211_NUM_ACS &&
|
||||||
|
@ -350,15 +414,17 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
|
||||||
offset = noffset;
|
offset = noffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
rcu_read_lock();
|
mutex_lock(&local->sta_mtx);
|
||||||
|
|
||||||
/* we should have the peer STA if we're already responding */
|
/* we should have the peer STA if we're already responding */
|
||||||
if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
|
if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
|
||||||
sta = sta_info_get(sdata, peer);
|
sta = sta_info_get(sdata, peer);
|
||||||
if (WARN_ON_ONCE(!sta)) {
|
if (WARN_ON_ONCE(!sta)) {
|
||||||
rcu_read_unlock();
|
mutex_unlock(&local->sta_mtx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sta->tdls_chandef = sdata->vif.bss_conf.chandef;
|
||||||
}
|
}
|
||||||
|
|
||||||
ieee80211_tdls_add_oper_classes(sdata, skb);
|
ieee80211_tdls_add_oper_classes(sdata, skb);
|
||||||
|
@ -384,10 +450,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
|
||||||
ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
|
ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
|
||||||
} else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
|
} else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
|
||||||
ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
|
ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
|
||||||
/* disable SMPS in TDLS responder */
|
|
||||||
sta->sta.ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED
|
|
||||||
<< IEEE80211_HT_CAP_SM_PS_SHIFT;
|
|
||||||
|
|
||||||
/* the peer caps are already intersected with our own */
|
/* the peer caps are already intersected with our own */
|
||||||
memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
|
memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
|
||||||
|
|
||||||
|
@ -448,9 +510,16 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
|
||||||
|
|
||||||
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
|
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
|
||||||
ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
|
ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if both peers support WIDER_BW, we can expand the chandef to
|
||||||
|
* a wider compatible one, up to 80MHz
|
||||||
|
*/
|
||||||
|
if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
|
||||||
|
ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
|
||||||
}
|
}
|
||||||
|
|
||||||
rcu_read_unlock();
|
mutex_unlock(&local->sta_mtx);
|
||||||
|
|
||||||
/* add any remaining IEs */
|
/* add any remaining IEs */
|
||||||
if (extra_ies_len) {
|
if (extra_ies_len) {
|
||||||
|
@ -474,15 +543,17 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
|
||||||
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
|
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
|
||||||
u8 *pos;
|
u8 *pos;
|
||||||
|
|
||||||
rcu_read_lock();
|
mutex_lock(&local->sta_mtx);
|
||||||
|
|
||||||
sta = sta_info_get(sdata, peer);
|
sta = sta_info_get(sdata, peer);
|
||||||
ap_sta = sta_info_get(sdata, ifmgd->bssid);
|
ap_sta = sta_info_get(sdata, ifmgd->bssid);
|
||||||
if (WARN_ON_ONCE(!sta || !ap_sta)) {
|
if (WARN_ON_ONCE(!sta || !ap_sta)) {
|
||||||
rcu_read_unlock();
|
mutex_unlock(&local->sta_mtx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sta->tdls_chandef = sdata->vif.bss_conf.chandef;
|
||||||
|
|
||||||
/* add any custom IEs that go before the QoS IE */
|
/* add any custom IEs that go before the QoS IE */
|
||||||
if (extra_ies_len) {
|
if (extra_ies_len) {
|
||||||
static const u8 before_qos[] = {
|
static const u8 before_qos[] = {
|
||||||
|
@ -530,12 +601,19 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
|
||||||
|
|
||||||
/* only include VHT-operation if not on the 2.4GHz band */
|
/* only include VHT-operation if not on the 2.4GHz band */
|
||||||
if (band != IEEE80211_BAND_2GHZ && sta->sta.vht_cap.vht_supported) {
|
if (band != IEEE80211_BAND_2GHZ && sta->sta.vht_cap.vht_supported) {
|
||||||
|
/*
|
||||||
|
* if both peers support WIDER_BW, we can expand the chandef to
|
||||||
|
* a wider compatible one, up to 80MHz
|
||||||
|
*/
|
||||||
|
if (test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
|
||||||
|
ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
|
||||||
|
|
||||||
pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation));
|
pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation));
|
||||||
ieee80211_ie_build_vht_oper(pos, &sta->sta.vht_cap,
|
ieee80211_ie_build_vht_oper(pos, &sta->sta.vht_cap,
|
||||||
&sdata->vif.bss_conf.chandef);
|
&sta->tdls_chandef);
|
||||||
}
|
}
|
||||||
|
|
||||||
rcu_read_unlock();
|
mutex_unlock(&local->sta_mtx);
|
||||||
|
|
||||||
/* add any remaining IEs */
|
/* add any remaining IEs */
|
||||||
if (extra_ies_len) {
|
if (extra_ies_len) {
|
||||||
|
@ -784,7 +862,7 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
|
||||||
max(sizeof(struct ieee80211_mgmt),
|
max(sizeof(struct ieee80211_mgmt),
|
||||||
sizeof(struct ieee80211_tdls_data)) +
|
sizeof(struct ieee80211_tdls_data)) +
|
||||||
50 + /* supported rates */
|
50 + /* supported rates */
|
||||||
7 + /* ext capab */
|
10 + /* ext capab */
|
||||||
26 + /* max(WMM-info, WMM-param) */
|
26 + /* max(WMM-info, WMM-param) */
|
||||||
2 + max(sizeof(struct ieee80211_ht_cap),
|
2 + max(sizeof(struct ieee80211_ht_cap),
|
||||||
sizeof(struct ieee80211_ht_operation)) +
|
sizeof(struct ieee80211_ht_operation)) +
|
||||||
|
@ -983,8 +1061,17 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
|
||||||
{
|
{
|
||||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||||
struct ieee80211_local *local = sdata->local;
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
enum ieee80211_smps_mode smps_mode = sdata->u.mgd.driver_smps_mode;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* don't support setup with forced SMPS mode that's not off */
|
||||||
|
if (smps_mode != IEEE80211_SMPS_AUTOMATIC &&
|
||||||
|
smps_mode != IEEE80211_SMPS_OFF) {
|
||||||
|
tdls_dbg(sdata, "Aborting TDLS setup due to SMPS mode %d\n",
|
||||||
|
smps_mode);
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
mutex_lock(&local->mtx);
|
mutex_lock(&local->mtx);
|
||||||
|
|
||||||
/* we don't support concurrent TDLS peer setups */
|
/* we don't support concurrent TDLS peer setups */
|
||||||
|
@ -1146,6 +1233,22 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void iee80211_tdls_recalc_chanctx(struct ieee80211_sub_if_data *sdata)
|
||||||
|
{
|
||||||
|
struct ieee80211_local *local = sdata->local;
|
||||||
|
struct ieee80211_chanctx_conf *conf;
|
||||||
|
struct ieee80211_chanctx *ctx;
|
||||||
|
|
||||||
|
mutex_lock(&local->chanctx_mtx);
|
||||||
|
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
||||||
|
lockdep_is_held(&local->chanctx_mtx));
|
||||||
|
if (conf) {
|
||||||
|
ctx = container_of(conf, struct ieee80211_chanctx, conf);
|
||||||
|
ieee80211_recalc_chanctx_chantype(local, ctx);
|
||||||
|
}
|
||||||
|
mutex_unlock(&local->chanctx_mtx);
|
||||||
|
}
|
||||||
|
|
||||||
int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
||||||
const u8 *peer, enum nl80211_tdls_operation oper)
|
const u8 *peer, enum nl80211_tdls_operation oper)
|
||||||
{
|
{
|
||||||
|
@ -1182,6 +1285,8 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iee80211_tdls_recalc_chanctx(sdata);
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
sta = sta_info_get(sdata, peer);
|
sta = sta_info_get(sdata, peer);
|
||||||
if (!sta) {
|
if (!sta) {
|
||||||
|
@ -1213,6 +1318,7 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
||||||
ieee80211_flush_queues(local, sdata, false);
|
ieee80211_flush_queues(local, sdata, false);
|
||||||
|
|
||||||
ret = sta_info_destroy_addr(sdata, peer);
|
ret = sta_info_destroy_addr(sdata, peer);
|
||||||
|
iee80211_tdls_recalc_chanctx(sdata);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -ENOTSUPP;
|
ret = -ENOTSUPP;
|
||||||
|
@ -1224,6 +1330,10 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
||||||
eth_zero_addr(sdata->u.mgd.tdls_peer);
|
eth_zero_addr(sdata->u.mgd.tdls_peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
ieee80211_queue_work(&sdata->local->hw,
|
||||||
|
&sdata->u.mgd.request_smps_work);
|
||||||
|
|
||||||
mutex_unlock(&local->mtx);
|
mutex_unlock(&local->mtx);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1627,6 +1737,31 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!elems.sec_chan_offs) {
|
||||||
|
chan_type = NL80211_CHAN_HT20;
|
||||||
|
} else {
|
||||||
|
switch (elems.sec_chan_offs->sec_chan_offs) {
|
||||||
|
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||||
|
chan_type = NL80211_CHAN_HT40PLUS;
|
||||||
|
break;
|
||||||
|
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||||
|
chan_type = NL80211_CHAN_HT40MINUS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
chan_type = NL80211_CHAN_HT20;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg80211_chandef_create(&chandef, chan, chan_type);
|
||||||
|
|
||||||
|
/* we will be active on the TDLS link */
|
||||||
|
if (!cfg80211_reg_can_beacon_relax(sdata->local->hw.wiphy, &chandef,
|
||||||
|
sdata->wdev.iftype)) {
|
||||||
|
tdls_dbg(sdata, "TDLS chan switch to forbidden channel\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
mutex_lock(&local->sta_mtx);
|
mutex_lock(&local->sta_mtx);
|
||||||
sta = sta_info_get(sdata, tf->sa);
|
sta = sta_info_get(sdata, tf->sa);
|
||||||
if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) {
|
if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) {
|
||||||
|
@ -1647,27 +1782,15 @@ ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sta->sta.ht_cap.ht_supported) {
|
/* peer should have known better */
|
||||||
chan_type = NL80211_CHAN_NO_HT;
|
if (!sta->sta.ht_cap.ht_supported && elems.sec_chan_offs &&
|
||||||
} else if (!elems.sec_chan_offs) {
|
elems.sec_chan_offs->sec_chan_offs) {
|
||||||
chan_type = NL80211_CHAN_HT20;
|
tdls_dbg(sdata, "TDLS chan switch - wide chan unsupported\n");
|
||||||
} else {
|
ret = -ENOTSUPP;
|
||||||
switch (elems.sec_chan_offs->sec_chan_offs) {
|
goto out;
|
||||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
|
||||||
chan_type = NL80211_CHAN_HT40PLUS;
|
|
||||||
break;
|
|
||||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
|
||||||
chan_type = NL80211_CHAN_HT40MINUS;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
chan_type = NL80211_CHAN_HT20;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg80211_chandef_create(&chandef, chan, chan_type);
|
|
||||||
params.chandef = &chandef;
|
params.chandef = &chandef;
|
||||||
|
|
||||||
params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time);
|
params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time);
|
||||||
params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout);
|
params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout);
|
||||||
|
|
||||||
|
@ -1691,12 +1814,15 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
|
static void
|
||||||
struct sk_buff *skb)
|
ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
|
||||||
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct ieee80211_tdls_data *tf = (void *)skb->data;
|
struct ieee80211_tdls_data *tf = (void *)skb->data;
|
||||||
struct wiphy *wiphy = sdata->local->hw.wiphy;
|
struct wiphy *wiphy = sdata->local->hw.wiphy;
|
||||||
|
|
||||||
|
ASSERT_RTNL();
|
||||||
|
|
||||||
/* make sure the driver supports it */
|
/* make sure the driver supports it */
|
||||||
if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
|
if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
|
||||||
return;
|
return;
|
||||||
|
@ -1720,3 +1846,47 @@ void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ieee80211_teardown_tdls_peers(struct ieee80211_sub_if_data *sdata)
|
||||||
|
{
|
||||||
|
struct sta_info *sta;
|
||||||
|
u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
|
||||||
|
if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
|
||||||
|
!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ieee80211_tdls_oper_request(&sdata->vif, sta->sta.addr,
|
||||||
|
NL80211_TDLS_TEARDOWN, reason,
|
||||||
|
GFP_ATOMIC);
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ieee80211_tdls_chsw_work(struct work_struct *wk)
|
||||||
|
{
|
||||||
|
struct ieee80211_local *local =
|
||||||
|
container_of(wk, struct ieee80211_local, tdls_chsw_work);
|
||||||
|
struct ieee80211_sub_if_data *sdata;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
struct ieee80211_tdls_data *tf;
|
||||||
|
|
||||||
|
rtnl_lock();
|
||||||
|
while ((skb = skb_dequeue(&local->skb_queue_tdls_chsw))) {
|
||||||
|
tf = (struct ieee80211_tdls_data *)skb->data;
|
||||||
|
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||||
|
if (!ieee80211_sdata_running(sdata) ||
|
||||||
|
sdata->vif.type != NL80211_IFTYPE_STATION ||
|
||||||
|
!ether_addr_equal(tf->da, sdata->vif.addr))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ieee80211_process_tdls_channel_switch(sdata, skb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree_skb(skb);
|
||||||
|
}
|
||||||
|
rtnl_unlock();
|
||||||
|
}
|
||||||
|
|
|
@ -311,9 +311,6 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
|
||||||
if (tx->sdata->vif.type == NL80211_IFTYPE_WDS)
|
if (tx->sdata->vif.type == NL80211_IFTYPE_WDS)
|
||||||
return TX_CONTINUE;
|
return TX_CONTINUE;
|
||||||
|
|
||||||
if (tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
|
|
||||||
return TX_CONTINUE;
|
|
||||||
|
|
||||||
if (tx->flags & IEEE80211_TX_PS_BUFFERED)
|
if (tx->flags & IEEE80211_TX_PS_BUFFERED)
|
||||||
return TX_CONTINUE;
|
return TX_CONTINUE;
|
||||||
|
|
||||||
|
@ -610,7 +607,6 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
|
||||||
if (tx->key) {
|
if (tx->key) {
|
||||||
bool skip_hw = false;
|
bool skip_hw = false;
|
||||||
|
|
||||||
tx->key->tx_rx_count++;
|
|
||||||
/* TODO: add threshold stuff again */
|
/* TODO: add threshold stuff again */
|
||||||
|
|
||||||
switch (tx->key->conf.cipher) {
|
switch (tx->key->conf.cipher) {
|
||||||
|
@ -690,7 +686,8 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
|
||||||
|
|
||||||
txrc.bss = (tx->sdata->vif.type == NL80211_IFTYPE_AP ||
|
txrc.bss = (tx->sdata->vif.type == NL80211_IFTYPE_AP ||
|
||||||
tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
|
tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
|
||||||
tx->sdata->vif.type == NL80211_IFTYPE_ADHOC);
|
tx->sdata->vif.type == NL80211_IFTYPE_ADHOC ||
|
||||||
|
tx->sdata->vif.type == NL80211_IFTYPE_OCB);
|
||||||
|
|
||||||
/* set up RTS protection if desired */
|
/* set up RTS protection if desired */
|
||||||
if (len > tx->local->hw.wiphy->rts_threshold) {
|
if (len > tx->local->hw.wiphy->rts_threshold) {
|
||||||
|
@ -2777,7 +2774,11 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
|
||||||
sdata->sequence_number += 0x10;
|
sdata->sequence_number += 0x10;
|
||||||
}
|
}
|
||||||
|
|
||||||
sta->tx_msdu[tid]++;
|
if (skb_shinfo(skb)->gso_size)
|
||||||
|
sta->tx_msdu[tid] +=
|
||||||
|
DIV_ROUND_UP(skb->len, skb_shinfo(skb)->gso_size);
|
||||||
|
else
|
||||||
|
sta->tx_msdu[tid]++;
|
||||||
|
|
||||||
info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
|
info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
|
||||||
|
|
||||||
|
@ -3213,6 +3214,16 @@ static void ieee80211_set_csa(struct ieee80211_sub_if_data *sdata,
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u8 __ieee80211_csa_update_counter(struct beacon_data *beacon)
|
||||||
|
{
|
||||||
|
beacon->csa_current_counter--;
|
||||||
|
|
||||||
|
/* the counter should never reach 0 */
|
||||||
|
WARN_ON_ONCE(!beacon->csa_current_counter);
|
||||||
|
|
||||||
|
return beacon->csa_current_counter;
|
||||||
|
}
|
||||||
|
|
||||||
u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif)
|
u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif)
|
||||||
{
|
{
|
||||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||||
|
@ -3231,11 +3242,7 @@ u8 ieee80211_csa_update_counter(struct ieee80211_vif *vif)
|
||||||
if (!beacon)
|
if (!beacon)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
|
|
||||||
beacon->csa_current_counter--;
|
count = __ieee80211_csa_update_counter(beacon);
|
||||||
|
|
||||||
/* the counter should never reach 0 */
|
|
||||||
WARN_ON_ONCE(!beacon->csa_current_counter);
|
|
||||||
count = beacon->csa_current_counter;
|
|
||||||
|
|
||||||
unlock:
|
unlock:
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
@ -3335,7 +3342,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
|
||||||
if (beacon) {
|
if (beacon) {
|
||||||
if (beacon->csa_counter_offsets[0]) {
|
if (beacon->csa_counter_offsets[0]) {
|
||||||
if (!is_template)
|
if (!is_template)
|
||||||
ieee80211_csa_update_counter(vif);
|
__ieee80211_csa_update_counter(beacon);
|
||||||
|
|
||||||
ieee80211_set_csa(sdata, beacon);
|
ieee80211_set_csa(sdata, beacon);
|
||||||
}
|
}
|
||||||
|
@ -3381,7 +3388,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
|
||||||
|
|
||||||
if (beacon->csa_counter_offsets[0]) {
|
if (beacon->csa_counter_offsets[0]) {
|
||||||
if (!is_template)
|
if (!is_template)
|
||||||
ieee80211_csa_update_counter(vif);
|
__ieee80211_csa_update_counter(beacon);
|
||||||
|
|
||||||
ieee80211_set_csa(sdata, beacon);
|
ieee80211_set_csa(sdata, beacon);
|
||||||
}
|
}
|
||||||
|
@ -3411,7 +3418,7 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw,
|
||||||
* for now we leave it consistent with overall
|
* for now we leave it consistent with overall
|
||||||
* mac80211's behavior.
|
* mac80211's behavior.
|
||||||
*/
|
*/
|
||||||
ieee80211_csa_update_counter(vif);
|
__ieee80211_csa_update_counter(beacon);
|
||||||
|
|
||||||
ieee80211_set_csa(sdata, beacon);
|
ieee80211_set_csa(sdata, beacon);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,55 +47,6 @@ struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(wiphy_to_ieee80211_hw);
|
EXPORT_SYMBOL(wiphy_to_ieee80211_hw);
|
||||||
|
|
||||||
u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
|
|
||||||
enum nl80211_iftype type)
|
|
||||||
{
|
|
||||||
__le16 fc = hdr->frame_control;
|
|
||||||
|
|
||||||
/* drop ACK/CTS frames and incorrect hdr len (ctrl) */
|
|
||||||
if (len < 16)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (ieee80211_is_data(fc)) {
|
|
||||||
if (len < 24) /* drop incorrect hdr len (data) */
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (ieee80211_has_a4(fc))
|
|
||||||
return NULL;
|
|
||||||
if (ieee80211_has_tods(fc))
|
|
||||||
return hdr->addr1;
|
|
||||||
if (ieee80211_has_fromds(fc))
|
|
||||||
return hdr->addr2;
|
|
||||||
|
|
||||||
return hdr->addr3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ieee80211_is_mgmt(fc)) {
|
|
||||||
if (len < 24) /* drop incorrect hdr len (mgmt) */
|
|
||||||
return NULL;
|
|
||||||
return hdr->addr3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ieee80211_is_ctl(fc)) {
|
|
||||||
if (ieee80211_is_pspoll(fc))
|
|
||||||
return hdr->addr1;
|
|
||||||
|
|
||||||
if (ieee80211_is_back_req(fc)) {
|
|
||||||
switch (type) {
|
|
||||||
case NL80211_IFTYPE_STATION:
|
|
||||||
return hdr->addr2;
|
|
||||||
case NL80211_IFTYPE_AP:
|
|
||||||
case NL80211_IFTYPE_AP_VLAN:
|
|
||||||
return hdr->addr1;
|
|
||||||
default:
|
|
||||||
break; /* fall through to the return */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
|
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
|
@ -752,7 +703,12 @@ EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);
|
||||||
|
|
||||||
struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
|
struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
|
||||||
{
|
{
|
||||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
struct ieee80211_sub_if_data *sdata;
|
||||||
|
|
||||||
|
if (!vif)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
sdata = vif_to_sdata(vif);
|
||||||
|
|
||||||
if (!ieee80211_sdata_running(sdata) ||
|
if (!ieee80211_sdata_running(sdata) ||
|
||||||
!(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
!(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
||||||
|
@ -1709,6 +1665,7 @@ static void ieee80211_handle_reconfig_failure(struct ieee80211_local *local)
|
||||||
local->resuming = false;
|
local->resuming = false;
|
||||||
local->suspended = false;
|
local->suspended = false;
|
||||||
local->started = false;
|
local->started = false;
|
||||||
|
local->in_reconfig = false;
|
||||||
|
|
||||||
/* scheduled scan clearly can't be running any more, but tell
|
/* scheduled scan clearly can't be running any more, but tell
|
||||||
* cfg80211 and clear local state
|
* cfg80211 and clear local state
|
||||||
|
@ -1759,16 +1716,24 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||||
struct ieee80211_sub_if_data *sched_scan_sdata;
|
struct ieee80211_sub_if_data *sched_scan_sdata;
|
||||||
struct cfg80211_sched_scan_request *sched_scan_req;
|
struct cfg80211_sched_scan_request *sched_scan_req;
|
||||||
bool sched_scan_stopped = false;
|
bool sched_scan_stopped = false;
|
||||||
|
bool suspended = local->suspended;
|
||||||
|
|
||||||
/* nothing to do if HW shouldn't run */
|
/* nothing to do if HW shouldn't run */
|
||||||
if (!local->open_count)
|
if (!local->open_count)
|
||||||
goto wake_up;
|
goto wake_up;
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
if (local->suspended)
|
if (suspended)
|
||||||
local->resuming = true;
|
local->resuming = true;
|
||||||
|
|
||||||
if (local->wowlan) {
|
if (local->wowlan) {
|
||||||
|
/*
|
||||||
|
* In the wowlan case, both mac80211 and the device
|
||||||
|
* are functional when the resume op is called, so
|
||||||
|
* clear local->suspended so the device could operate
|
||||||
|
* normally (e.g. pass rx frames).
|
||||||
|
*/
|
||||||
|
local->suspended = false;
|
||||||
res = drv_resume(local);
|
res = drv_resume(local);
|
||||||
local->wowlan = false;
|
local->wowlan = false;
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
@ -1781,8 +1746,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||||
/*
|
/*
|
||||||
* res is 1, which means the driver requested
|
* res is 1, which means the driver requested
|
||||||
* to go through a regular reset on wakeup.
|
* to go through a regular reset on wakeup.
|
||||||
|
* restore local->suspended in this case.
|
||||||
*/
|
*/
|
||||||
reconfig_due_to_wowlan = true;
|
reconfig_due_to_wowlan = true;
|
||||||
|
local->suspended = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1794,7 +1761,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||||
*/
|
*/
|
||||||
res = drv_start(local);
|
res = drv_start(local);
|
||||||
if (res) {
|
if (res) {
|
||||||
if (local->suspended)
|
if (suspended)
|
||||||
WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n");
|
WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n");
|
||||||
else
|
else
|
||||||
WARN(1, "Hardware became unavailable during restart.\n");
|
WARN(1, "Hardware became unavailable during restart.\n");
|
||||||
|
@ -2088,10 +2055,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||||
* If this is for hw restart things are still running.
|
* If this is for hw restart things are still running.
|
||||||
* We may want to change that later, however.
|
* We may want to change that later, however.
|
||||||
*/
|
*/
|
||||||
if (local->open_count && (!local->suspended || reconfig_due_to_wowlan))
|
if (local->open_count && (!suspended || reconfig_due_to_wowlan))
|
||||||
drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART);
|
drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART);
|
||||||
|
|
||||||
if (!local->suspended)
|
if (!suspended)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
|
|
|
@ -308,11 +308,15 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
|
||||||
{
|
{
|
||||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||||
enum ieee80211_sta_rx_bandwidth bw;
|
enum ieee80211_sta_rx_bandwidth bw;
|
||||||
|
enum nl80211_chan_width bss_width = sdata->vif.bss_conf.chandef.width;
|
||||||
|
|
||||||
bw = ieee80211_chan_width_to_rx_bw(sdata->vif.bss_conf.chandef.width);
|
bw = ieee80211_sta_cap_rx_bw(sta);
|
||||||
bw = min(bw, ieee80211_sta_cap_rx_bw(sta));
|
|
||||||
bw = min(bw, sta->cur_max_bandwidth);
|
bw = min(bw, sta->cur_max_bandwidth);
|
||||||
|
|
||||||
|
/* do not cap the BW of TDLS WIDER_BW peers by the bss */
|
||||||
|
if (!test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW))
|
||||||
|
bw = min(bw, ieee80211_chan_width_to_rx_bw(bss_width));
|
||||||
|
|
||||||
return bw;
|
return bw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,3 +426,29 @@ void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
|
||||||
if (changed > 0)
|
if (changed > 0)
|
||||||
rate_control_rate_update(local, sband, sta, changed);
|
rate_control_rate_update(local, sband, sta, changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ieee80211_get_vht_mask_from_cap(__le16 vht_cap,
|
||||||
|
u16 vht_mask[NL80211_VHT_NSS_MAX])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
u16 mask, cap = le16_to_cpu(vht_cap);
|
||||||
|
|
||||||
|
for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
|
||||||
|
mask = (cap >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
|
||||||
|
switch (mask) {
|
||||||
|
case IEEE80211_VHT_MCS_SUPPORT_0_7:
|
||||||
|
vht_mask[i] = 0x00FF;
|
||||||
|
break;
|
||||||
|
case IEEE80211_VHT_MCS_SUPPORT_0_8:
|
||||||
|
vht_mask[i] = 0x01FF;
|
||||||
|
break;
|
||||||
|
case IEEE80211_VHT_MCS_SUPPORT_0_9:
|
||||||
|
vht_mask[i] = 0x03FF;
|
||||||
|
break;
|
||||||
|
case IEEE80211_VHT_MCS_NOT_SUPPORTED:
|
||||||
|
default:
|
||||||
|
vht_mask[i] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -516,30 +516,33 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
|
||||||
return RX_DROP_UNUSABLE;
|
return RX_DROP_UNUSABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ccmp_hdr2pn(pn, skb->data + hdrlen);
|
if (!(status->flag & RX_FLAG_PN_VALIDATED)) {
|
||||||
|
ccmp_hdr2pn(pn, skb->data + hdrlen);
|
||||||
|
|
||||||
queue = rx->security_idx;
|
queue = rx->security_idx;
|
||||||
|
|
||||||
if (memcmp(pn, key->u.ccmp.rx_pn[queue], IEEE80211_CCMP_PN_LEN) <= 0) {
|
if (memcmp(pn, key->u.ccmp.rx_pn[queue],
|
||||||
key->u.ccmp.replays++;
|
IEEE80211_CCMP_PN_LEN) <= 0) {
|
||||||
return RX_DROP_UNUSABLE;
|
key->u.ccmp.replays++;
|
||||||
}
|
|
||||||
|
|
||||||
if (!(status->flag & RX_FLAG_DECRYPTED)) {
|
|
||||||
u8 aad[2 * AES_BLOCK_SIZE];
|
|
||||||
u8 b_0[AES_BLOCK_SIZE];
|
|
||||||
/* hardware didn't decrypt/verify MIC */
|
|
||||||
ccmp_special_blocks(skb, pn, b_0, aad);
|
|
||||||
|
|
||||||
if (ieee80211_aes_ccm_decrypt(
|
|
||||||
key->u.ccmp.tfm, b_0, aad,
|
|
||||||
skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
|
|
||||||
data_len,
|
|
||||||
skb->data + skb->len - mic_len, mic_len))
|
|
||||||
return RX_DROP_UNUSABLE;
|
return RX_DROP_UNUSABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN);
|
if (!(status->flag & RX_FLAG_DECRYPTED)) {
|
||||||
|
u8 aad[2 * AES_BLOCK_SIZE];
|
||||||
|
u8 b_0[AES_BLOCK_SIZE];
|
||||||
|
/* hardware didn't decrypt/verify MIC */
|
||||||
|
ccmp_special_blocks(skb, pn, b_0, aad);
|
||||||
|
|
||||||
|
if (ieee80211_aes_ccm_decrypt(
|
||||||
|
key->u.ccmp.tfm, b_0, aad,
|
||||||
|
skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
|
||||||
|
data_len,
|
||||||
|
skb->data + skb->len - mic_len, mic_len))
|
||||||
|
return RX_DROP_UNUSABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
/* Remove CCMP header and MIC */
|
/* Remove CCMP header and MIC */
|
||||||
if (pskb_trim(skb, skb->len - mic_len))
|
if (pskb_trim(skb, skb->len - mic_len))
|
||||||
|
@ -739,30 +742,34 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
|
||||||
return RX_DROP_UNUSABLE;
|
return RX_DROP_UNUSABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gcmp_hdr2pn(pn, skb->data + hdrlen);
|
if (!(status->flag & RX_FLAG_PN_VALIDATED)) {
|
||||||
|
gcmp_hdr2pn(pn, skb->data + hdrlen);
|
||||||
|
|
||||||
queue = rx->security_idx;
|
queue = rx->security_idx;
|
||||||
|
|
||||||
if (memcmp(pn, key->u.gcmp.rx_pn[queue], IEEE80211_GCMP_PN_LEN) <= 0) {
|
if (memcmp(pn, key->u.gcmp.rx_pn[queue],
|
||||||
key->u.gcmp.replays++;
|
IEEE80211_GCMP_PN_LEN) <= 0) {
|
||||||
return RX_DROP_UNUSABLE;
|
key->u.gcmp.replays++;
|
||||||
}
|
|
||||||
|
|
||||||
if (!(status->flag & RX_FLAG_DECRYPTED)) {
|
|
||||||
u8 aad[2 * AES_BLOCK_SIZE];
|
|
||||||
u8 j_0[AES_BLOCK_SIZE];
|
|
||||||
/* hardware didn't decrypt/verify MIC */
|
|
||||||
gcmp_special_blocks(skb, pn, j_0, aad);
|
|
||||||
|
|
||||||
if (ieee80211_aes_gcm_decrypt(
|
|
||||||
key->u.gcmp.tfm, j_0, aad,
|
|
||||||
skb->data + hdrlen + IEEE80211_GCMP_HDR_LEN,
|
|
||||||
data_len,
|
|
||||||
skb->data + skb->len - IEEE80211_GCMP_MIC_LEN))
|
|
||||||
return RX_DROP_UNUSABLE;
|
return RX_DROP_UNUSABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(key->u.gcmp.rx_pn[queue], pn, IEEE80211_GCMP_PN_LEN);
|
if (!(status->flag & RX_FLAG_DECRYPTED)) {
|
||||||
|
u8 aad[2 * AES_BLOCK_SIZE];
|
||||||
|
u8 j_0[AES_BLOCK_SIZE];
|
||||||
|
/* hardware didn't decrypt/verify MIC */
|
||||||
|
gcmp_special_blocks(skb, pn, j_0, aad);
|
||||||
|
|
||||||
|
if (ieee80211_aes_gcm_decrypt(
|
||||||
|
key->u.gcmp.tfm, j_0, aad,
|
||||||
|
skb->data + hdrlen + IEEE80211_GCMP_HDR_LEN,
|
||||||
|
data_len,
|
||||||
|
skb->data + skb->len -
|
||||||
|
IEEE80211_GCMP_MIC_LEN))
|
||||||
|
return RX_DROP_UNUSABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(key->u.gcmp.rx_pn[queue], pn, IEEE80211_GCMP_PN_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
/* Remove GCMP header and MIC */
|
/* Remove GCMP header and MIC */
|
||||||
if (pskb_trim(skb, skb->len - IEEE80211_GCMP_MIC_LEN))
|
if (pskb_trim(skb, skb->len - IEEE80211_GCMP_MIC_LEN))
|
||||||
|
|
|
@ -36,7 +36,8 @@ config RFKILL_REGULATOR
|
||||||
|
|
||||||
config RFKILL_GPIO
|
config RFKILL_GPIO
|
||||||
tristate "GPIO RFKILL driver"
|
tristate "GPIO RFKILL driver"
|
||||||
depends on RFKILL && GPIOLIB
|
depends on RFKILL
|
||||||
|
depends on GPIOLIB || COMPILE_TEST
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
If you say yes here you get support of a generic gpio RFKILL
|
If you say yes here you get support of a generic gpio RFKILL
|
||||||
|
|
|
@ -407,6 +407,9 @@ use_default_name:
|
||||||
INIT_LIST_HEAD(&rdev->bss_list);
|
INIT_LIST_HEAD(&rdev->bss_list);
|
||||||
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
|
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
|
||||||
INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results);
|
INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results);
|
||||||
|
INIT_LIST_HEAD(&rdev->mlme_unreg);
|
||||||
|
spin_lock_init(&rdev->mlme_unreg_lock);
|
||||||
|
INIT_WORK(&rdev->mlme_unreg_wk, cfg80211_mlme_unreg_wk);
|
||||||
INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk,
|
INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk,
|
||||||
cfg80211_dfs_channels_update_work);
|
cfg80211_dfs_channels_update_work);
|
||||||
#ifdef CONFIG_CFG80211_WEXT
|
#ifdef CONFIG_CFG80211_WEXT
|
||||||
|
@ -802,6 +805,7 @@ void wiphy_unregister(struct wiphy *wiphy)
|
||||||
cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
|
cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
|
||||||
flush_work(&rdev->destroy_work);
|
flush_work(&rdev->destroy_work);
|
||||||
flush_work(&rdev->sched_scan_stop_wk);
|
flush_work(&rdev->sched_scan_stop_wk);
|
||||||
|
flush_work(&rdev->mlme_unreg_wk);
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
|
if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
|
||||||
|
@ -855,6 +859,7 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
|
||||||
|
|
||||||
switch (wdev->iftype) {
|
switch (wdev->iftype) {
|
||||||
case NL80211_IFTYPE_P2P_DEVICE:
|
case NL80211_IFTYPE_P2P_DEVICE:
|
||||||
|
cfg80211_mlme_purge_registrations(wdev);
|
||||||
cfg80211_stop_p2p_device(rdev, wdev);
|
cfg80211_stop_p2p_device(rdev, wdev);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -59,6 +59,10 @@ struct cfg80211_registered_device {
|
||||||
struct list_head beacon_registrations;
|
struct list_head beacon_registrations;
|
||||||
spinlock_t beacon_registrations_lock;
|
spinlock_t beacon_registrations_lock;
|
||||||
|
|
||||||
|
struct list_head mlme_unreg;
|
||||||
|
spinlock_t mlme_unreg_lock;
|
||||||
|
struct work_struct mlme_unreg_wk;
|
||||||
|
|
||||||
/* protected by RTNL only */
|
/* protected by RTNL only */
|
||||||
int num_running_ifaces;
|
int num_running_ifaces;
|
||||||
int num_running_monitor_ifaces;
|
int num_running_monitor_ifaces;
|
||||||
|
@ -348,6 +352,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
|
||||||
int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
|
int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
|
||||||
u16 frame_type, const u8 *match_data,
|
u16 frame_type, const u8 *match_data,
|
||||||
int match_len);
|
int match_len);
|
||||||
|
void cfg80211_mlme_unreg_wk(struct work_struct *wk);
|
||||||
void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid);
|
void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid);
|
||||||
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
|
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
|
||||||
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
|
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* cfg80211 MLME SAP interface
|
* cfg80211 MLME SAP interface
|
||||||
*
|
*
|
||||||
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
|
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
|
||||||
|
* Copyright (c) 2015 Intel Deutschland GmbH
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
@ -389,6 +390,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
|
||||||
|
|
||||||
struct cfg80211_mgmt_registration {
|
struct cfg80211_mgmt_registration {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
struct wireless_dev *wdev;
|
||||||
|
|
||||||
u32 nlportid;
|
u32 nlportid;
|
||||||
|
|
||||||
|
@ -399,6 +401,46 @@ struct cfg80211_mgmt_registration {
|
||||||
u8 match[];
|
u8 match[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
cfg80211_process_mlme_unregistrations(struct cfg80211_registered_device *rdev)
|
||||||
|
{
|
||||||
|
struct cfg80211_mgmt_registration *reg;
|
||||||
|
|
||||||
|
ASSERT_RTNL();
|
||||||
|
|
||||||
|
spin_lock_bh(&rdev->mlme_unreg_lock);
|
||||||
|
while ((reg = list_first_entry_or_null(&rdev->mlme_unreg,
|
||||||
|
struct cfg80211_mgmt_registration,
|
||||||
|
list))) {
|
||||||
|
list_del(®->list);
|
||||||
|
spin_unlock_bh(&rdev->mlme_unreg_lock);
|
||||||
|
|
||||||
|
if (rdev->ops->mgmt_frame_register) {
|
||||||
|
u16 frame_type = le16_to_cpu(reg->frame_type);
|
||||||
|
|
||||||
|
rdev_mgmt_frame_register(rdev, reg->wdev,
|
||||||
|
frame_type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(reg);
|
||||||
|
|
||||||
|
spin_lock_bh(&rdev->mlme_unreg_lock);
|
||||||
|
}
|
||||||
|
spin_unlock_bh(&rdev->mlme_unreg_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cfg80211_mlme_unreg_wk(struct work_struct *wk)
|
||||||
|
{
|
||||||
|
struct cfg80211_registered_device *rdev;
|
||||||
|
|
||||||
|
rdev = container_of(wk, struct cfg80211_registered_device,
|
||||||
|
mlme_unreg_wk);
|
||||||
|
|
||||||
|
rtnl_lock();
|
||||||
|
cfg80211_process_mlme_unregistrations(rdev);
|
||||||
|
rtnl_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
|
int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
|
||||||
u16 frame_type, const u8 *match_data,
|
u16 frame_type, const u8 *match_data,
|
||||||
int match_len)
|
int match_len)
|
||||||
|
@ -449,11 +491,18 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
|
||||||
nreg->match_len = match_len;
|
nreg->match_len = match_len;
|
||||||
nreg->nlportid = snd_portid;
|
nreg->nlportid = snd_portid;
|
||||||
nreg->frame_type = cpu_to_le16(frame_type);
|
nreg->frame_type = cpu_to_le16(frame_type);
|
||||||
|
nreg->wdev = wdev;
|
||||||
list_add(&nreg->list, &wdev->mgmt_registrations);
|
list_add(&nreg->list, &wdev->mgmt_registrations);
|
||||||
|
spin_unlock_bh(&wdev->mgmt_registrations_lock);
|
||||||
|
|
||||||
|
/* process all unregistrations to avoid driver confusion */
|
||||||
|
cfg80211_process_mlme_unregistrations(rdev);
|
||||||
|
|
||||||
if (rdev->ops->mgmt_frame_register)
|
if (rdev->ops->mgmt_frame_register)
|
||||||
rdev_mgmt_frame_register(rdev, wdev, frame_type, true);
|
rdev_mgmt_frame_register(rdev, wdev, frame_type, true);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
spin_unlock_bh(&wdev->mgmt_registrations_lock);
|
spin_unlock_bh(&wdev->mgmt_registrations_lock);
|
||||||
|
|
||||||
|
@ -472,15 +521,12 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
|
||||||
if (reg->nlportid != nlportid)
|
if (reg->nlportid != nlportid)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (rdev->ops->mgmt_frame_register) {
|
|
||||||
u16 frame_type = le16_to_cpu(reg->frame_type);
|
|
||||||
|
|
||||||
rdev_mgmt_frame_register(rdev, wdev,
|
|
||||||
frame_type, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
list_del(®->list);
|
list_del(®->list);
|
||||||
kfree(reg);
|
spin_lock(&rdev->mlme_unreg_lock);
|
||||||
|
list_add_tail(®->list, &rdev->mlme_unreg);
|
||||||
|
spin_unlock(&rdev->mlme_unreg_lock);
|
||||||
|
|
||||||
|
schedule_work(&rdev->mlme_unreg_wk);
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_bh(&wdev->mgmt_registrations_lock);
|
spin_unlock_bh(&wdev->mgmt_registrations_lock);
|
||||||
|
@ -496,16 +542,15 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
|
||||||
|
|
||||||
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
|
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
|
||||||
{
|
{
|
||||||
struct cfg80211_mgmt_registration *reg, *tmp;
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||||
|
|
||||||
spin_lock_bh(&wdev->mgmt_registrations_lock);
|
spin_lock_bh(&wdev->mgmt_registrations_lock);
|
||||||
|
spin_lock(&rdev->mlme_unreg_lock);
|
||||||
list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
|
list_splice_tail_init(&wdev->mgmt_registrations, &rdev->mlme_unreg);
|
||||||
list_del(®->list);
|
spin_unlock(&rdev->mlme_unreg_lock);
|
||||||
kfree(reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_bh(&wdev->mgmt_registrations_lock);
|
spin_unlock_bh(&wdev->mgmt_registrations_lock);
|
||||||
|
|
||||||
|
cfg80211_process_mlme_unregistrations(rdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
|
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
|
||||||
|
|
|
@ -2321,6 +2321,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
|
||||||
rdev->wiphy.frag_threshold = old_frag_threshold;
|
rdev->wiphy.frag_threshold = old_frag_threshold;
|
||||||
rdev->wiphy.rts_threshold = old_rts_threshold;
|
rdev->wiphy.rts_threshold = old_rts_threshold;
|
||||||
rdev->wiphy.coverage_class = old_coverage_class;
|
rdev->wiphy.coverage_class = old_coverage_class;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -7390,7 +7391,8 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
|
||||||
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
|
||||||
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (!rdev->ops->set_mcast_rate)
|
if (!rdev->ops->set_mcast_rate)
|
||||||
|
|
|
@ -733,6 +733,8 @@ static inline void
|
||||||
rdev_mgmt_frame_register(struct cfg80211_registered_device *rdev,
|
rdev_mgmt_frame_register(struct cfg80211_registered_device *rdev,
|
||||||
struct wireless_dev *wdev, u16 frame_type, bool reg)
|
struct wireless_dev *wdev, u16 frame_type, bool reg)
|
||||||
{
|
{
|
||||||
|
might_sleep();
|
||||||
|
|
||||||
trace_rdev_mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
|
trace_rdev_mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
|
||||||
rdev->ops->mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
|
rdev->ops->mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
|
||||||
trace_rdev_return_void(&rdev->wiphy);
|
trace_rdev_return_void(&rdev->wiphy);
|
||||||
|
|
|
@ -1004,7 +1004,7 @@ static u32 map_regdom_flags(u32 rd_flags)
|
||||||
|
|
||||||
static const struct ieee80211_reg_rule *
|
static const struct ieee80211_reg_rule *
|
||||||
freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
|
freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
|
||||||
const struct ieee80211_regdomain *regd)
|
const struct ieee80211_regdomain *regd, u32 bw)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
bool band_rule_found = false;
|
bool band_rule_found = false;
|
||||||
|
@ -1028,7 +1028,7 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
|
||||||
if (!band_rule_found)
|
if (!band_rule_found)
|
||||||
band_rule_found = freq_in_rule_band(fr, center_freq);
|
band_rule_found = freq_in_rule_band(fr, center_freq);
|
||||||
|
|
||||||
bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20));
|
bw_fits = reg_does_bw_fit(fr, center_freq, bw);
|
||||||
|
|
||||||
if (band_rule_found && bw_fits)
|
if (band_rule_found && bw_fits)
|
||||||
return rr;
|
return rr;
|
||||||
|
@ -1040,14 +1040,26 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq,
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const struct ieee80211_reg_rule *__freq_reg_info(struct wiphy *wiphy,
|
||||||
|
u32 center_freq, u32 min_bw)
|
||||||
|
{
|
||||||
|
const struct ieee80211_regdomain *regd = reg_get_regdomain(wiphy);
|
||||||
|
const struct ieee80211_reg_rule *reg_rule = NULL;
|
||||||
|
u32 bw;
|
||||||
|
|
||||||
|
for (bw = MHZ_TO_KHZ(20); bw >= min_bw; bw = bw / 2) {
|
||||||
|
reg_rule = freq_reg_info_regd(wiphy, center_freq, regd, bw);
|
||||||
|
if (!IS_ERR(reg_rule))
|
||||||
|
return reg_rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reg_rule;
|
||||||
|
}
|
||||||
|
|
||||||
const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
|
const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy,
|
||||||
u32 center_freq)
|
u32 center_freq)
|
||||||
{
|
{
|
||||||
const struct ieee80211_regdomain *regd;
|
return __freq_reg_info(wiphy, center_freq, MHZ_TO_KHZ(20));
|
||||||
|
|
||||||
regd = reg_get_regdomain(wiphy);
|
|
||||||
|
|
||||||
return freq_reg_info_regd(wiphy, center_freq, regd);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(freq_reg_info);
|
EXPORT_SYMBOL(freq_reg_info);
|
||||||
|
|
||||||
|
@ -1176,8 +1188,20 @@ static void handle_channel(struct wiphy *wiphy,
|
||||||
if (reg_rule->flags & NL80211_RRF_AUTO_BW)
|
if (reg_rule->flags & NL80211_RRF_AUTO_BW)
|
||||||
max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
|
max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
|
||||||
|
|
||||||
|
/* If we get a reg_rule we can assume that at least 5Mhz fit */
|
||||||
|
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
|
||||||
|
MHZ_TO_KHZ(10)))
|
||||||
|
bw_flags |= IEEE80211_CHAN_NO_10MHZ;
|
||||||
|
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
|
||||||
|
MHZ_TO_KHZ(20)))
|
||||||
|
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
|
||||||
|
|
||||||
|
if (max_bandwidth_khz < MHZ_TO_KHZ(10))
|
||||||
|
bw_flags |= IEEE80211_CHAN_NO_10MHZ;
|
||||||
|
if (max_bandwidth_khz < MHZ_TO_KHZ(20))
|
||||||
|
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
|
||||||
if (max_bandwidth_khz < MHZ_TO_KHZ(40))
|
if (max_bandwidth_khz < MHZ_TO_KHZ(40))
|
||||||
bw_flags = IEEE80211_CHAN_NO_HT40;
|
bw_flags |= IEEE80211_CHAN_NO_HT40;
|
||||||
if (max_bandwidth_khz < MHZ_TO_KHZ(80))
|
if (max_bandwidth_khz < MHZ_TO_KHZ(80))
|
||||||
bw_flags |= IEEE80211_CHAN_NO_80MHZ;
|
bw_flags |= IEEE80211_CHAN_NO_80MHZ;
|
||||||
if (max_bandwidth_khz < MHZ_TO_KHZ(160))
|
if (max_bandwidth_khz < MHZ_TO_KHZ(160))
|
||||||
|
@ -1695,9 +1719,15 @@ static void handle_channel_custom(struct wiphy *wiphy,
|
||||||
const struct ieee80211_power_rule *power_rule = NULL;
|
const struct ieee80211_power_rule *power_rule = NULL;
|
||||||
const struct ieee80211_freq_range *freq_range = NULL;
|
const struct ieee80211_freq_range *freq_range = NULL;
|
||||||
u32 max_bandwidth_khz;
|
u32 max_bandwidth_khz;
|
||||||
|
u32 bw;
|
||||||
|
|
||||||
reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq),
|
for (bw = MHZ_TO_KHZ(20); bw >= MHZ_TO_KHZ(5); bw = bw / 2) {
|
||||||
regd);
|
reg_rule = freq_reg_info_regd(wiphy,
|
||||||
|
MHZ_TO_KHZ(chan->center_freq),
|
||||||
|
regd, bw);
|
||||||
|
if (!IS_ERR(reg_rule))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (IS_ERR(reg_rule)) {
|
if (IS_ERR(reg_rule)) {
|
||||||
REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
|
REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
|
||||||
|
@ -1721,8 +1751,20 @@ static void handle_channel_custom(struct wiphy *wiphy,
|
||||||
if (reg_rule->flags & NL80211_RRF_AUTO_BW)
|
if (reg_rule->flags & NL80211_RRF_AUTO_BW)
|
||||||
max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
|
max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
|
||||||
|
|
||||||
|
/* If we get a reg_rule we can assume that at least 5Mhz fit */
|
||||||
|
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
|
||||||
|
MHZ_TO_KHZ(10)))
|
||||||
|
bw_flags |= IEEE80211_CHAN_NO_10MHZ;
|
||||||
|
if (!reg_does_bw_fit(freq_range, MHZ_TO_KHZ(chan->center_freq),
|
||||||
|
MHZ_TO_KHZ(20)))
|
||||||
|
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
|
||||||
|
|
||||||
|
if (max_bandwidth_khz < MHZ_TO_KHZ(10))
|
||||||
|
bw_flags |= IEEE80211_CHAN_NO_10MHZ;
|
||||||
|
if (max_bandwidth_khz < MHZ_TO_KHZ(20))
|
||||||
|
bw_flags |= IEEE80211_CHAN_NO_20MHZ;
|
||||||
if (max_bandwidth_khz < MHZ_TO_KHZ(40))
|
if (max_bandwidth_khz < MHZ_TO_KHZ(40))
|
||||||
bw_flags = IEEE80211_CHAN_NO_HT40;
|
bw_flags |= IEEE80211_CHAN_NO_HT40;
|
||||||
if (max_bandwidth_khz < MHZ_TO_KHZ(80))
|
if (max_bandwidth_khz < MHZ_TO_KHZ(80))
|
||||||
bw_flags |= IEEE80211_CHAN_NO_80MHZ;
|
bw_flags |= IEEE80211_CHAN_NO_80MHZ;
|
||||||
if (max_bandwidth_khz < MHZ_TO_KHZ(160))
|
if (max_bandwidth_khz < MHZ_TO_KHZ(160))
|
||||||
|
@ -2079,10 +2121,7 @@ static void reg_process_hint(struct regulatory_request *reg_request)
|
||||||
reg_process_hint_core(reg_request);
|
reg_process_hint_core(reg_request);
|
||||||
return;
|
return;
|
||||||
case NL80211_REGDOM_SET_BY_USER:
|
case NL80211_REGDOM_SET_BY_USER:
|
||||||
treatment = reg_process_hint_user(reg_request);
|
reg_process_hint_user(reg_request);
|
||||||
if (treatment == REG_REQ_IGNORE ||
|
|
||||||
treatment == REG_REQ_ALREADY_SET)
|
|
||||||
return;
|
|
||||||
return;
|
return;
|
||||||
case NL80211_REGDOM_SET_BY_DRIVER:
|
case NL80211_REGDOM_SET_BY_DRIVER:
|
||||||
if (!wiphy)
|
if (!wiphy)
|
||||||
|
@ -2099,7 +2138,9 @@ static void reg_process_hint(struct regulatory_request *reg_request)
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is required so that the orig_* parameters are saved */
|
/* This is required so that the orig_* parameters are saved.
|
||||||
|
* NOTE: treatment must be set for any case that reaches here!
|
||||||
|
*/
|
||||||
if (treatment == REG_REQ_ALREADY_SET && wiphy &&
|
if (treatment == REG_REQ_ALREADY_SET && wiphy &&
|
||||||
wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
|
wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
|
||||||
wiphy_update_regulatory(wiphy, reg_request->initiator);
|
wiphy_update_regulatory(wiphy, reg_request->initiator);
|
||||||
|
|
Loading…
Reference in a new issue