mirror of https://gitee.com/openkylin/linux.git
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Fixed-up drivers/net/wireless/iwlwifi/mvm/mac80211.c to change change IEEE80211_HW_NEED_DTIM_PERIOD to IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC as requested by Johannes Berg. -- JWL Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
commit
3549c6b195
|
@ -1853,7 +1853,7 @@ void *carl9170_alloc(size_t priv_size)
|
|||
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
|
||||
IEEE80211_HW_SUPPORTS_PS |
|
||||
IEEE80211_HW_PS_NULLFUNC_STACK |
|
||||
IEEE80211_HW_NEED_DTIM_PERIOD |
|
||||
IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC |
|
||||
IEEE80211_HW_SIGNAL_DBM;
|
||||
|
||||
if (!modparam_noht) {
|
||||
|
|
|
@ -5733,7 +5733,7 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length)
|
|||
/* Tell mac80211 our characteristics */
|
||||
hw->flags =
|
||||
IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_AMPDU_AGGREGATION |
|
||||
IEEE80211_HW_NEED_DTIM_PERIOD | IEEE80211_HW_SPECTRUM_MGMT |
|
||||
IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC | IEEE80211_HW_SPECTRUM_MGMT |
|
||||
IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_PS |
|
||||
IEEE80211_HW_SUPPORTS_DYNAMIC_PS;
|
||||
if (il->cfg->sku & IL_SKU_N)
|
||||
|
|
|
@ -145,7 +145,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv,
|
|||
/* Tell mac80211 our characteristics */
|
||||
hw->flags = IEEE80211_HW_SIGNAL_DBM |
|
||||
IEEE80211_HW_AMPDU_AGGREGATION |
|
||||
IEEE80211_HW_NEED_DTIM_PERIOD |
|
||||
IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC |
|
||||
IEEE80211_HW_SPECTRUM_MGMT |
|
||||
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
|
||||
IEEE80211_HW_QUEUE_CONTROL |
|
||||
|
|
|
@ -114,7 +114,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
|
|||
IEEE80211_HW_QUEUE_CONTROL |
|
||||
IEEE80211_HW_WANT_MONITOR_VIF |
|
||||
IEEE80211_HW_SCAN_WHILE_IDLE |
|
||||
IEEE80211_HW_NEED_DTIM_PERIOD |
|
||||
IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC |
|
||||
IEEE80211_HW_SUPPORTS_PS |
|
||||
IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
|
||||
IEEE80211_HW_AMPDU_AGGREGATION;
|
||||
|
|
|
@ -151,6 +151,11 @@
|
|||
/* Mesh Control 802.11s */
|
||||
#define IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT 0x0100
|
||||
|
||||
/* Mesh Power Save Level */
|
||||
#define IEEE80211_QOS_CTL_MESH_PS_LEVEL 0x0200
|
||||
/* Mesh Receiver Service Period Initiated */
|
||||
#define IEEE80211_QOS_CTL_RSPI 0x0400
|
||||
|
||||
/* U-APSD queue for WMM IEs sent by AP */
|
||||
#define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7)
|
||||
#define IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK 0x0f
|
||||
|
@ -675,11 +680,14 @@ struct ieee80211_meshconf_ie {
|
|||
* @IEEE80211_MESHCONF_CAPAB_FORWARDING: the STA forwards MSDUs
|
||||
* @IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING: TBTT adjustment procedure
|
||||
* is ongoing
|
||||
* @IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL: STA is in deep sleep mode or has
|
||||
* neighbors in deep sleep mode
|
||||
*/
|
||||
enum mesh_config_capab_flags {
|
||||
IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS = 0x01,
|
||||
IEEE80211_MESHCONF_CAPAB_FORWARDING = 0x08,
|
||||
IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING = 0x20,
|
||||
IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL = 0x40,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -672,8 +672,10 @@ struct station_parameters {
|
|||
* @STATION_INFO_SIGNAL: @signal filled
|
||||
* @STATION_INFO_TX_BITRATE: @txrate fields are filled
|
||||
* (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs)
|
||||
* @STATION_INFO_RX_PACKETS: @rx_packets filled
|
||||
* @STATION_INFO_TX_PACKETS: @tx_packets filled
|
||||
* @STATION_INFO_RX_PACKETS: @rx_packets filled with 32-bit value
|
||||
* @STATION_INFO_TX_PACKETS: @tx_packets filled with 32-bit value
|
||||
* @STATION_INFO_RX_PACKETS64: @rx_packets filled with 64-bit value
|
||||
* @STATION_INFO_TX_PACKETS64: @tx_packets filled with 64-bit value
|
||||
* @STATION_INFO_TX_RETRIES: @tx_retries filled
|
||||
* @STATION_INFO_TX_FAILED: @tx_failed filled
|
||||
* @STATION_INFO_RX_DROP_MISC: @rx_dropped_misc filled
|
||||
|
@ -714,6 +716,8 @@ enum station_info_flags {
|
|||
STATION_INFO_LOCAL_PM = 1<<21,
|
||||
STATION_INFO_PEER_PM = 1<<22,
|
||||
STATION_INFO_NONPEER_PM = 1<<23,
|
||||
STATION_INFO_RX_BYTES64 = 1<<24,
|
||||
STATION_INFO_TX_BYTES64 = 1<<25,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -835,8 +839,8 @@ struct station_info {
|
|||
u32 filled;
|
||||
u32 connected_time;
|
||||
u32 inactive_time;
|
||||
u32 rx_bytes;
|
||||
u32 tx_bytes;
|
||||
u64 rx_bytes;
|
||||
u64 tx_bytes;
|
||||
u16 llid;
|
||||
u16 plid;
|
||||
u8 plink_state;
|
||||
|
@ -1289,7 +1293,6 @@ struct cfg80211_bss_ies {
|
|||
* @beacon_ies: the information elements from the last Beacon frame
|
||||
* @proberesp_ies: the information elements from the last Probe Response frame
|
||||
* @signal: signal strength value (type depends on the wiphy's signal_type)
|
||||
* @free_priv: function pointer to free private data
|
||||
* @priv: private area for driver use, has at least wiphy->bss_priv_size bytes
|
||||
*/
|
||||
struct cfg80211_bss {
|
||||
|
@ -1301,8 +1304,6 @@ struct cfg80211_bss {
|
|||
const struct cfg80211_bss_ies __rcu *beacon_ies;
|
||||
const struct cfg80211_bss_ies __rcu *proberesp_ies;
|
||||
|
||||
void (*free_priv)(struct cfg80211_bss *bss);
|
||||
|
||||
s32 signal;
|
||||
|
||||
u16 beacon_interval;
|
||||
|
@ -1596,6 +1597,32 @@ struct cfg80211_wowlan {
|
|||
int n_patterns;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_wowlan_wakeup - wakeup report
|
||||
* @disconnect: woke up by getting disconnected
|
||||
* @magic_pkt: woke up by receiving magic packet
|
||||
* @gtk_rekey_failure: woke up by GTK rekey failure
|
||||
* @eap_identity_req: woke up by EAP identity request packet
|
||||
* @four_way_handshake: woke up by 4-way handshake
|
||||
* @rfkill_release: woke up by rfkill being released
|
||||
* @pattern_idx: pattern that caused wakeup, -1 if not due to pattern
|
||||
* @packet_present_len: copied wakeup packet data
|
||||
* @packet_len: original wakeup packet length
|
||||
* @packet: The packet causing the wakeup, if any.
|
||||
* @packet_80211: For pattern match, magic packet and other data
|
||||
* frame triggers an 802.3 frame should be reported, for
|
||||
* disconnect due to deauth 802.11 frame. This indicates which
|
||||
* it is.
|
||||
*/
|
||||
struct cfg80211_wowlan_wakeup {
|
||||
bool disconnect, magic_pkt, gtk_rekey_failure,
|
||||
eap_identity_req, four_way_handshake,
|
||||
rfkill_release, packet_80211;
|
||||
s32 pattern_idx;
|
||||
u32 packet_present_len, packet_len;
|
||||
const void *packet;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_gtk_rekey_data - rekey data
|
||||
* @kek: key encryption key
|
||||
|
@ -3137,10 +3164,6 @@ cfg80211_get_ibss(struct wiphy *wiphy,
|
|||
WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS);
|
||||
}
|
||||
|
||||
struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy,
|
||||
struct ieee80211_channel *channel,
|
||||
const u8 *meshid, size_t meshidlen,
|
||||
const u8 *meshcfg);
|
||||
/**
|
||||
* cfg80211_ref_bss - reference BSS struct
|
||||
* @bss: the BSS struct to reference
|
||||
|
@ -3852,6 +3875,21 @@ int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len,
|
|||
enum ieee80211_p2p_attr_id attr,
|
||||
u8 *buf, unsigned int bufsize);
|
||||
|
||||
/**
|
||||
* cfg80211_report_wowlan_wakeup - report wakeup from WoWLAN
|
||||
* @wdev: the wireless device reporting the wakeup
|
||||
* @wakeup: the wakeup report
|
||||
* @gfp: allocation flags
|
||||
*
|
||||
* This function reports that the given device woke up. If it
|
||||
* caused the wakeup, report the reason(s), otherwise you may
|
||||
* pass %NULL as the @wakeup parameter to advertise that something
|
||||
* else caused the wakeup.
|
||||
*/
|
||||
void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
|
||||
struct cfg80211_wowlan_wakeup *wakeup,
|
||||
gfp_t gfp);
|
||||
|
||||
/* Logging, debugging and troubleshooting/diagnostic helpers. */
|
||||
|
||||
/* wiphy_printk helpers, similar to dev_printk */
|
||||
|
|
|
@ -208,6 +208,8 @@ struct ieee80211_chanctx_conf {
|
|||
* @BSS_CHANGED_TXPOWER: TX power setting changed for this interface
|
||||
* @BSS_CHANGED_P2P_PS: P2P powersave settings (CTWindow, opportunistic PS)
|
||||
* changed (currently only in P2P client mode, GO mode will be later)
|
||||
* @BSS_CHANGED_DTIM_PERIOD: the DTIM period value was changed (set when
|
||||
* it becomes valid, managed mode only)
|
||||
*/
|
||||
enum ieee80211_bss_change {
|
||||
BSS_CHANGED_ASSOC = 1<<0,
|
||||
|
@ -230,6 +232,7 @@ enum ieee80211_bss_change {
|
|||
BSS_CHANGED_PS = 1<<17,
|
||||
BSS_CHANGED_TXPOWER = 1<<18,
|
||||
BSS_CHANGED_P2P_PS = 1<<19,
|
||||
BSS_CHANGED_DTIM_PERIOD = 1<<20,
|
||||
|
||||
/* when adding here, make sure to change ieee80211_reconfig */
|
||||
};
|
||||
|
@ -271,9 +274,8 @@ enum ieee80211_rssi_event {
|
|||
* if the hardware cannot handle this it must set the
|
||||
* IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE hardware flag
|
||||
* @dtim_period: num of beacons before the next DTIM, for beaconing,
|
||||
* valid in station mode only while @assoc is true and if also
|
||||
* requested by %IEEE80211_HW_NEED_DTIM_PERIOD (cf. also hw conf
|
||||
* @ps_dtim_period)
|
||||
* valid in station mode only if after the driver was notified
|
||||
* with the %BSS_CHANGED_DTIM_PERIOD flag, will be non-zero then.
|
||||
* @sync_tsf: last beacon's/probe response's TSF timestamp (could be old
|
||||
* as it may have been received during scanning long ago)
|
||||
* @sync_device_ts: the device timestamp corresponding to the sync_tsf,
|
||||
|
@ -406,6 +408,9 @@ struct ieee80211_bss_conf {
|
|||
* @IEEE80211_TX_INTFL_RETRANSMISSION: This frame is being retransmitted
|
||||
* after TX status because the destination was asleep, it must not
|
||||
* be modified again (no seqno assignment, crypto, etc.)
|
||||
* @IEEE80211_TX_INTFL_MLME_CONN_TX: This frame was transmitted by the MLME
|
||||
* code for connection establishment, this indicates that its status
|
||||
* should kick the MLME state machine.
|
||||
* @IEEE80211_TX_INTFL_NL80211_FRAME_TX: Frame was requested through nl80211
|
||||
* MLME command (internal to mac80211 to figure out whether to send TX
|
||||
* status to user space)
|
||||
|
@ -457,7 +462,7 @@ enum mac80211_tx_control_flags {
|
|||
IEEE80211_TX_CTL_NO_PS_BUFFER = BIT(17),
|
||||
IEEE80211_TX_CTL_MORE_FRAMES = BIT(18),
|
||||
IEEE80211_TX_INTFL_RETRANSMISSION = BIT(19),
|
||||
/* hole at 20, use later */
|
||||
IEEE80211_TX_INTFL_MLME_CONN_TX = BIT(20),
|
||||
IEEE80211_TX_INTFL_NL80211_FRAME_TX = BIT(21),
|
||||
IEEE80211_TX_CTL_LDPC = BIT(22),
|
||||
IEEE80211_TX_CTL_STBC = BIT(23) | BIT(24),
|
||||
|
@ -1328,9 +1333,9 @@ struct ieee80211_tx_control {
|
|||
* When this flag is set, signaling beacon-loss will cause an immediate
|
||||
* change to disassociated state.
|
||||
*
|
||||
* @IEEE80211_HW_NEED_DTIM_PERIOD:
|
||||
* This device needs to know the DTIM period for the BSS before
|
||||
* associating.
|
||||
* @IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC:
|
||||
* This device needs to get data from beacon before association (i.e.
|
||||
* dtim_period).
|
||||
*
|
||||
* @IEEE80211_HW_SUPPORTS_PER_STA_GTK: The device's crypto engine supports
|
||||
* per-station GTKs as used by IBSS RSN or during fast transition. If
|
||||
|
@ -1366,10 +1371,6 @@ struct ieee80211_tx_control {
|
|||
* @IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF: Use the P2P Device address for any
|
||||
* P2P Interface. This will be honoured even if more than one interface
|
||||
* is supported.
|
||||
*
|
||||
* @IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL: On this hardware TX BA session
|
||||
* should be tear down once BAR frame will not be acked.
|
||||
*
|
||||
*/
|
||||
enum ieee80211_hw_flags {
|
||||
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
|
||||
|
@ -1379,7 +1380,7 @@ enum ieee80211_hw_flags {
|
|||
IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE = 1<<4,
|
||||
IEEE80211_HW_SIGNAL_UNSPEC = 1<<5,
|
||||
IEEE80211_HW_SIGNAL_DBM = 1<<6,
|
||||
IEEE80211_HW_NEED_DTIM_PERIOD = 1<<7,
|
||||
IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC = 1<<7,
|
||||
IEEE80211_HW_SPECTRUM_MGMT = 1<<8,
|
||||
IEEE80211_HW_AMPDU_AGGREGATION = 1<<9,
|
||||
IEEE80211_HW_SUPPORTS_PS = 1<<10,
|
||||
|
@ -1398,7 +1399,6 @@ enum ieee80211_hw_flags {
|
|||
IEEE80211_HW_TX_AMPDU_SETUP_IN_HW = 1<<23,
|
||||
IEEE80211_HW_SCAN_WHILE_IDLE = 1<<24,
|
||||
IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF = 1<<25,
|
||||
IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL = 1<<26,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -3877,6 +3877,8 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif);
|
|||
* When beacon filtering is enabled with %IEEE80211_VIF_BEACON_FILTER, and
|
||||
* %IEEE80211_CONF_PS and %IEEE80211_HW_CONNECTION_MONITOR are set, the driver
|
||||
* needs to inform if the connection to the AP has been lost.
|
||||
* The function may also be called if the connection needs to be terminated
|
||||
* for some other reason, even if %IEEE80211_HW_CONNECTION_MONITOR isn't set.
|
||||
*
|
||||
* This function will cause immediate change to disassociated state,
|
||||
* without connection recovery attempts.
|
||||
|
@ -4211,4 +4213,16 @@ void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif);
|
|||
*/
|
||||
int ieee80211_ave_rssi(struct ieee80211_vif *vif);
|
||||
|
||||
/**
|
||||
* ieee80211_report_wowlan_wakeup - report WoWLAN wakeup
|
||||
* @vif: virtual interface
|
||||
* @wakeup: wakeup reason(s)
|
||||
* @gfp: allocation flags
|
||||
*
|
||||
* See cfg80211_report_wowlan_wakeup().
|
||||
*/
|
||||
void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
|
||||
struct cfg80211_wowlan_wakeup *wakeup,
|
||||
gfp_t gfp);
|
||||
|
||||
#endif /* MAC80211_H */
|
||||
|
|
|
@ -513,6 +513,12 @@
|
|||
* command with the %NL80211_ATTR_WOWLAN_TRIGGERS attribute. For
|
||||
* more background information, see
|
||||
* http://wireless.kernel.org/en/users/Documentation/WoWLAN.
|
||||
* The @NL80211_CMD_SET_WOWLAN command can also be used as a notification
|
||||
* from the driver reporting the wakeup reason. In this case, the
|
||||
* @NL80211_ATTR_WOWLAN_TRIGGERS attribute will contain the reason
|
||||
* for the wakeup, if it was caused by wireless. If it is not present
|
||||
* in the wakeup notification, the wireless device didn't cause the
|
||||
* wakeup but reports that it was woken up.
|
||||
*
|
||||
* @NL80211_CMD_SET_REKEY_OFFLOAD: This command is used give the driver
|
||||
* the necessary information for supporting GTK rekey offload. This
|
||||
|
@ -1851,6 +1857,8 @@ enum nl80211_sta_bss_param {
|
|||
* @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
|
||||
* @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station)
|
||||
* @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
|
||||
* @NL80211_STA_INFO_RX_BYTES64: total received bytes (u64, from this station)
|
||||
* @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (u64, to this station)
|
||||
* @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
|
||||
* @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
|
||||
* containing info as possible, see &enum nl80211_rate_info
|
||||
|
@ -1903,6 +1911,8 @@ enum nl80211_sta_info {
|
|||
NL80211_STA_INFO_LOCAL_PM,
|
||||
NL80211_STA_INFO_PEER_PM,
|
||||
NL80211_STA_INFO_NONPEER_PM,
|
||||
NL80211_STA_INFO_RX_BYTES64,
|
||||
NL80211_STA_INFO_TX_BYTES64,
|
||||
|
||||
/* keep last */
|
||||
__NL80211_STA_INFO_AFTER_LAST,
|
||||
|
@ -2947,6 +2957,10 @@ struct nl80211_wowlan_pattern_support {
|
|||
*
|
||||
* In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute
|
||||
* carrying a &struct nl80211_wowlan_pattern_support.
|
||||
*
|
||||
* When reporting wakeup. it is a u32 attribute containing the 0-based
|
||||
* index of the pattern that caused the wakeup, in the patterns passed
|
||||
* to the kernel when configuring.
|
||||
* @NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: Not a real trigger, and cannot be
|
||||
* used when setting, used only to indicate that GTK rekeying is supported
|
||||
* by the device (flag)
|
||||
|
@ -2957,8 +2971,25 @@ struct nl80211_wowlan_pattern_support {
|
|||
* @NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: wake up on 4-way handshake (flag)
|
||||
* @NL80211_WOWLAN_TRIG_RFKILL_RELEASE: wake up when rfkill is released
|
||||
* (on devices that have rfkill in the device) (flag)
|
||||
* @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211: For wakeup reporting only, contains
|
||||
* the 802.11 packet that caused the wakeup, e.g. a deauth frame. The frame
|
||||
* may be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN
|
||||
* attribute contains the original length.
|
||||
* @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN: Original length of the 802.11
|
||||
* packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211
|
||||
* attribute if the packet was truncated somewhere.
|
||||
* @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023: For wakeup reporting only, contains the
|
||||
* 802.11 packet that caused the wakeup, e.g. a magic packet. The frame may
|
||||
* be truncated, the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN attribute
|
||||
* contains the original length.
|
||||
* @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: Original length of the 802.3
|
||||
* packet, may be bigger than the @NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023
|
||||
* attribute if the packet was truncated somewhere.
|
||||
* @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
|
||||
* @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
|
||||
*
|
||||
* These nested attributes are used to configure the wakeup triggers and
|
||||
* to report the wakeup reason(s).
|
||||
*/
|
||||
enum nl80211_wowlan_triggers {
|
||||
__NL80211_WOWLAN_TRIG_INVALID,
|
||||
|
@ -2971,6 +3002,10 @@ enum nl80211_wowlan_triggers {
|
|||
NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST,
|
||||
NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE,
|
||||
NL80211_WOWLAN_TRIG_RFKILL_RELEASE,
|
||||
NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211,
|
||||
NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN,
|
||||
NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023,
|
||||
NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN,
|
||||
|
||||
/* keep last */
|
||||
NUM_NL80211_WOWLAN_TRIG,
|
||||
|
|
|
@ -258,6 +258,17 @@ config MAC80211_MESH_SYNC_DEBUG
|
|||
|
||||
Do not select this option.
|
||||
|
||||
config MAC80211_MESH_PS_DEBUG
|
||||
bool "Verbose mesh powersave debugging"
|
||||
depends on MAC80211_DEBUG_MENU
|
||||
depends on MAC80211_MESH
|
||||
---help---
|
||||
Selecting this option causes mac80211 to print out very verbose mesh
|
||||
powersave debugging messages (when mac80211 is taking part in a
|
||||
mesh network).
|
||||
|
||||
Do not select this option.
|
||||
|
||||
config MAC80211_TDLS_DEBUG
|
||||
bool "Verbose TDLS debugging"
|
||||
depends on MAC80211_DEBUG_MENU
|
||||
|
|
|
@ -39,7 +39,8 @@ mac80211-$(CONFIG_MAC80211_MESH) += \
|
|||
mesh_pathtbl.o \
|
||||
mesh_plink.o \
|
||||
mesh_hwmp.o \
|
||||
mesh_sync.o
|
||||
mesh_sync.o \
|
||||
mesh_ps.o
|
||||
|
||||
mac80211-$(CONFIG_PM) += pm.o
|
||||
|
||||
|
|
|
@ -492,7 +492,10 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
|||
#ifdef CONFIG_MAC80211_MESH
|
||||
sinfo->filled |= STATION_INFO_LLID |
|
||||
STATION_INFO_PLID |
|
||||
STATION_INFO_PLINK_STATE;
|
||||
STATION_INFO_PLINK_STATE |
|
||||
STATION_INFO_LOCAL_PM |
|
||||
STATION_INFO_PEER_PM |
|
||||
STATION_INFO_NONPEER_PM;
|
||||
|
||||
sinfo->llid = le16_to_cpu(sta->llid);
|
||||
sinfo->plid = le16_to_cpu(sta->plid);
|
||||
|
@ -501,6 +504,9 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
|||
sinfo->filled |= STATION_INFO_T_OFFSET;
|
||||
sinfo->t_offset = sta->t_offset;
|
||||
}
|
||||
sinfo->local_pm = sta->local_pm;
|
||||
sinfo->peer_pm = sta->peer_pm;
|
||||
sinfo->nonpeer_pm = sta->nonpeer_pm;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1262,6 +1268,10 @@ static int sta_apply_parameters(struct ieee80211_local *local,
|
|||
changed = mesh_plink_inc_estab_count(
|
||||
sdata);
|
||||
sta->plink_state = params->plink_state;
|
||||
|
||||
ieee80211_mps_sta_status_update(sta);
|
||||
ieee80211_mps_set_sta_local_pm(sta,
|
||||
sdata->u.mesh.mshcfg.power_mode);
|
||||
break;
|
||||
case NL80211_PLINK_LISTEN:
|
||||
case NL80211_PLINK_BLOCKED:
|
||||
|
@ -1273,6 +1283,9 @@ static int sta_apply_parameters(struct ieee80211_local *local,
|
|||
changed = mesh_plink_dec_estab_count(
|
||||
sdata);
|
||||
sta->plink_state = params->plink_state;
|
||||
|
||||
ieee80211_mps_sta_status_update(sta);
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
break;
|
||||
default:
|
||||
/* nothing */
|
||||
|
@ -1289,6 +1302,9 @@ static int sta_apply_parameters(struct ieee80211_local *local,
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (params->local_pm)
|
||||
ieee80211_mps_set_sta_local_pm(sta, params->local_pm);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1777,6 +1793,15 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
|
|||
if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, mask))
|
||||
conf->dot11MeshHWMPconfirmationInterval =
|
||||
nconf->dot11MeshHWMPconfirmationInterval;
|
||||
if (_chg_mesh_attr(NL80211_MESHCONF_POWER_MODE, mask)) {
|
||||
conf->power_mode = nconf->power_mode;
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
}
|
||||
if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask)) {
|
||||
conf->dot11MeshAwakeWindowDuration =
|
||||
nconf->dot11MeshAwakeWindowDuration;
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,12 @@
|
|||
#define MAC80211_MESH_SYNC_DEBUG 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH_PS_DEBUG
|
||||
#define MAC80211_MESH_PS_DEBUG 1
|
||||
#else
|
||||
#define MAC80211_MESH_PS_DEBUG 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MAC80211_TDLS_DEBUG
|
||||
#define MAC80211_TDLS_DEBUG 1
|
||||
#else
|
||||
|
@ -151,6 +157,10 @@ do { \
|
|||
_sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \
|
||||
sdata, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define mps_dbg(sdata, fmt, ...) \
|
||||
_sdata_dbg(MAC80211_MESH_PS_DEBUG, \
|
||||
sdata, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define tdls_dbg(sdata, fmt, ...) \
|
||||
_sdata_dbg(MAC80211_TDLS_DEBUG, \
|
||||
sdata, fmt, ##__VA_ARGS__)
|
||||
|
|
|
@ -121,8 +121,8 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf,
|
|||
sf += snprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n");
|
||||
if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD)
|
||||
sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_PERIOD\n");
|
||||
if (local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC)
|
||||
sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_BEFORE_ASSOC\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n");
|
||||
if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)
|
||||
|
|
|
@ -515,6 +515,9 @@ IEEE80211_IF_FILE(dot11MeshHWMProotInterval,
|
|||
u.mesh.mshcfg.dot11MeshHWMProotInterval, DEC);
|
||||
IEEE80211_IF_FILE(dot11MeshHWMPconfirmationInterval,
|
||||
u.mesh.mshcfg.dot11MeshHWMPconfirmationInterval, DEC);
|
||||
IEEE80211_IF_FILE(power_mode, u.mesh.mshcfg.power_mode, DEC);
|
||||
IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration,
|
||||
u.mesh.mshcfg.dot11MeshAwakeWindowDuration, DEC);
|
||||
#endif
|
||||
|
||||
#define DEBUGFS_ADD_MODE(name, mode) \
|
||||
|
@ -620,6 +623,8 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
|
|||
MESHPARAMS_ADD(dot11MeshHWMPactivePathToRootTimeout);
|
||||
MESHPARAMS_ADD(dot11MeshHWMProotInterval);
|
||||
MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval);
|
||||
MESHPARAMS_ADD(power_mode);
|
||||
MESHPARAMS_ADD(dot11MeshAwakeWindowDuration);
|
||||
#undef MESHPARAMS_ADD
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -65,7 +65,7 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
|
|||
test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : ""
|
||||
|
||||
int res = scnprintf(buf, sizeof(buf),
|
||||
"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
|
||||
"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
|
||||
TEST(AUTH), TEST(ASSOC), TEST(PS_STA),
|
||||
TEST(PS_DRIVER), TEST(AUTHORIZED),
|
||||
TEST(SHORT_PREAMBLE),
|
||||
|
@ -74,7 +74,8 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
|
|||
TEST(UAPSD), TEST(SP), TEST(TDLS_PEER),
|
||||
TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT),
|
||||
TEST(INSERTED), TEST(RATE_CONTROL),
|
||||
TEST(TOFFSET_KNOWN));
|
||||
TEST(TOFFSET_KNOWN), TEST(MPSP_OWNER),
|
||||
TEST(MPSP_RECIPIENT));
|
||||
#undef TEST
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
|
||||
}
|
||||
|
|
|
@ -302,7 +302,7 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta,
|
|||
"TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n",
|
||||
sdata->vif.addr, addr, sdata->u.ibss.bssid);
|
||||
ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, 0, NULL, 0,
|
||||
addr, sdata->u.ibss.bssid, NULL, 0, 0);
|
||||
addr, sdata->u.ibss.bssid, NULL, 0, 0, 0);
|
||||
}
|
||||
return sta;
|
||||
}
|
||||
|
@ -422,7 +422,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
|
|||
* has actually implemented this.
|
||||
*/
|
||||
ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, 0, NULL, 0,
|
||||
mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0);
|
||||
mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0, 0);
|
||||
}
|
||||
|
||||
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
|
||||
|
|
|
@ -86,23 +86,11 @@ struct ieee80211_fragment_entry {
|
|||
|
||||
|
||||
struct ieee80211_bss {
|
||||
/* don't want to look up all the time */
|
||||
size_t ssid_len;
|
||||
u8 ssid[IEEE80211_MAX_SSID_LEN];
|
||||
|
||||
u32 device_ts;
|
||||
|
||||
bool wmm_used;
|
||||
bool uapsd_supported;
|
||||
|
||||
unsigned long last_probe_resp;
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
u8 *mesh_id;
|
||||
size_t mesh_id_len;
|
||||
u8 *mesh_cfg;
|
||||
#endif
|
||||
|
||||
#define IEEE80211_MAX_SUPP_RATES 32
|
||||
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
|
||||
size_t supp_rates_len;
|
||||
|
@ -153,31 +141,6 @@ enum ieee80211_bss_valid_data_flags {
|
|||
IEEE80211_BSS_VALID_ERP = BIT(3)
|
||||
};
|
||||
|
||||
static inline u8 *bss_mesh_cfg(struct ieee80211_bss *bss)
|
||||
{
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
return bss->mesh_cfg;
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline u8 *bss_mesh_id(struct ieee80211_bss *bss)
|
||||
{
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
return bss->mesh_id;
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline u8 bss_mesh_id_len(struct ieee80211_bss *bss)
|
||||
{
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
return bss->mesh_id_len;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
typedef unsigned __bitwise__ ieee80211_tx_result;
|
||||
#define TX_CONTINUE ((__force ieee80211_tx_result) 0u)
|
||||
#define TX_DROP ((__force ieee80211_tx_result) 1u)
|
||||
|
@ -399,8 +362,7 @@ struct ieee80211_mgd_assoc_data {
|
|||
u8 ssid_len;
|
||||
u8 supp_rates_len;
|
||||
bool wmm, uapsd;
|
||||
bool have_beacon;
|
||||
bool sent_assoc;
|
||||
bool have_beacon, need_beacon;
|
||||
bool synced;
|
||||
|
||||
u8 ap_ht_param;
|
||||
|
@ -425,6 +387,7 @@ struct ieee80211_if_managed {
|
|||
unsigned long probe_timeout;
|
||||
int probe_send_count;
|
||||
bool nullfunc_failed;
|
||||
bool connection_loss;
|
||||
|
||||
struct mutex mtx;
|
||||
struct cfg80211_bss *associated;
|
||||
|
@ -449,6 +412,10 @@ struct ieee80211_if_managed {
|
|||
bool beacon_crc_valid;
|
||||
u32 beacon_crc;
|
||||
|
||||
bool status_acked;
|
||||
bool status_received;
|
||||
__le16 status_fc;
|
||||
|
||||
enum {
|
||||
IEEE80211_MFP_DISABLED,
|
||||
IEEE80211_MFP_OPTIONAL,
|
||||
|
@ -623,6 +590,11 @@ struct ieee80211_if_mesh {
|
|||
s64 sync_offset_clockdrift_max;
|
||||
spinlock_t sync_offset_lock;
|
||||
bool adjusting_tbtt;
|
||||
/* mesh power save */
|
||||
enum nl80211_mesh_power_mode nonpeer_pm;
|
||||
int ps_peers_light_sleep;
|
||||
int ps_peers_deep_sleep;
|
||||
struct ps_data ps;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
|
@ -1218,6 +1190,7 @@ struct ieee802_11_elems {
|
|||
struct ieee80211_meshconf_ie *mesh_config;
|
||||
u8 *mesh_id;
|
||||
u8 *peering;
|
||||
__le16 *awake_window;
|
||||
u8 *preq;
|
||||
u8 *prep;
|
||||
u8 *perr;
|
||||
|
@ -1318,6 +1291,8 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|||
void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
|
||||
__le16 fc, bool acked);
|
||||
|
||||
/* IBSS code */
|
||||
void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
|
||||
|
@ -1578,7 +1553,8 @@ static inline void ieee80211_add_pending_skbs(struct ieee80211_local *local,
|
|||
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
|
||||
u16 transaction, u16 auth_alg, u16 status,
|
||||
u8 *extra, size_t extra_len, const u8 *bssid,
|
||||
const u8 *da, const u8 *key, u8 key_len, u8 key_idx);
|
||||
const u8 *da, const u8 *key, u8 key_len, u8 key_idx,
|
||||
u32 tx_flags);
|
||||
void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *bssid, u16 stype, u16 reason,
|
||||
bool send_frame, u8 *frame_buf);
|
||||
|
@ -1595,7 +1571,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
|
|||
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len,
|
||||
u32 ratemask, bool directed, bool no_cck,
|
||||
u32 ratemask, bool directed, u32 tx_flags,
|
||||
struct ieee80211_channel *channel, bool scan);
|
||||
|
||||
void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
|
||||
|
|
|
@ -261,6 +261,9 @@ mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
|
|||
*pos = IEEE80211_MESHCONF_CAPAB_FORWARDING;
|
||||
*pos |= ifmsh->accepting_plinks ?
|
||||
IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
|
||||
/* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */
|
||||
*pos |= ifmsh->ps_peers_deep_sleep ?
|
||||
IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL : 0x00;
|
||||
*pos++ |= ifmsh->adjusting_tbtt ?
|
||||
IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
|
||||
*pos++ = 0x00;
|
||||
|
@ -286,6 +289,29 @@ mesh_add_meshid_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int mesh_add_awake_window_ie(struct sk_buff *skb,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
u8 *pos;
|
||||
|
||||
/* see IEEE802.11-2012 13.14.6 */
|
||||
if (ifmsh->ps_peers_light_sleep == 0 &&
|
||||
ifmsh->ps_peers_deep_sleep == 0 &&
|
||||
ifmsh->nonpeer_pm == NL80211_MESH_POWER_ACTIVE)
|
||||
return 0;
|
||||
|
||||
if (skb_tailroom(skb) < 4)
|
||||
return -ENOMEM;
|
||||
|
||||
pos = skb_put(skb, 2 + 2);
|
||||
*pos++ = WLAN_EID_MESH_AWAKE_WINDOW;
|
||||
*pos++ = 2;
|
||||
put_unaligned_le16(ifmsh->mshcfg.dot11MeshAwakeWindowDuration, pos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
|
@ -629,10 +655,7 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
|
|||
sdata->vif.bss_conf.basic_rates =
|
||||
ieee80211_mandatory_rates(local, band);
|
||||
|
||||
if (band == IEEE80211_BAND_5GHZ) {
|
||||
sdata->vif.bss_conf.use_short_slot = true;
|
||||
changed |= BSS_CHANGED_ERP_SLOT;
|
||||
}
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
|
||||
|
@ -656,6 +679,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
|
|||
sta_info_flush(sdata);
|
||||
mesh_path_flush_by_iface(sdata);
|
||||
|
||||
/* free all potentially still buffered group-addressed frames */
|
||||
local->total_ps_buffered -= skb_queue_len(&ifmsh->ps.bc_buf);
|
||||
skb_queue_purge(&ifmsh->ps.bc_buf);
|
||||
|
||||
del_timer_sync(&sdata->u.mesh.housekeeping_timer);
|
||||
del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
|
||||
del_timer_sync(&sdata->u.mesh.mesh_path_timer);
|
||||
|
@ -833,6 +860,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
|
|||
ieee80211_mesh_path_root_timer,
|
||||
(unsigned long) sdata);
|
||||
INIT_LIST_HEAD(&ifmsh->preq_queue.list);
|
||||
skb_queue_head_init(&ifmsh->ps.bc_buf);
|
||||
spin_lock_init(&ifmsh->mesh_preq_queue_lock);
|
||||
spin_lock_init(&ifmsh->sync_offset_lock);
|
||||
|
||||
|
|
|
@ -222,6 +222,8 @@ int mesh_add_meshid_ie(struct sk_buff *skb,
|
|||
struct ieee80211_sub_if_data *sdata);
|
||||
int mesh_add_rsn_ie(struct sk_buff *skb,
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
int mesh_add_awake_window_ie(struct sk_buff *skb,
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
int mesh_add_vendor_ies(struct sk_buff *skb,
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
int mesh_add_ds_params_ie(struct sk_buff *skb,
|
||||
|
@ -242,6 +244,21 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata);
|
|||
void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh);
|
||||
const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method);
|
||||
|
||||
/* mesh power save */
|
||||
void ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
|
||||
enum nl80211_mesh_power_mode pm);
|
||||
void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata,
|
||||
struct sta_info *sta,
|
||||
struct ieee80211_hdr *hdr);
|
||||
void ieee80211_mps_sta_status_update(struct sta_info *sta);
|
||||
void ieee80211_mps_rx_h_sta_process(struct sta_info *sta,
|
||||
struct ieee80211_hdr *hdr);
|
||||
void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,
|
||||
bool tx, bool acked);
|
||||
void ieee80211_mps_frame_release(struct sta_info *sta,
|
||||
struct ieee802_11_elems *elems);
|
||||
|
||||
/* Mesh paths */
|
||||
int mesh_nexthop_lookup(struct sk_buff *skb,
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
|
|
|
@ -205,6 +205,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata,
|
|||
struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
|
||||
skb_set_mac_header(skb, 0);
|
||||
skb_set_network_header(skb, 0);
|
||||
|
@ -217,6 +218,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata,
|
|||
info->control.vif = &sdata->vif;
|
||||
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
|
||||
ieee80211_set_qos_hdr(sdata, skb);
|
||||
ieee80211_mps_set_frame_flags(sdata, NULL, hdr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1080,6 +1082,10 @@ int mesh_nexthop_resolve(struct sk_buff *skb,
|
|||
u8 *target_addr = hdr->addr3;
|
||||
int err = 0;
|
||||
|
||||
/* Nulls are only sent to peers for PS and should be pre-addressed */
|
||||
if (ieee80211_is_qos_nullfunc(hdr->frame_control))
|
||||
return 0;
|
||||
|
||||
rcu_read_lock();
|
||||
err = mesh_nexthop_lookup(skb, sdata);
|
||||
if (!err)
|
||||
|
@ -1151,6 +1157,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
|
|||
if (next_hop) {
|
||||
memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN);
|
||||
memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN);
|
||||
ieee80211_mps_set_frame_flags(sdata, next_hop, hdr);
|
||||
err = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -212,6 +212,7 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta)
|
|||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN);
|
||||
memcpy(hdr->addr2, mpath->sdata->vif.addr, ETH_ALEN);
|
||||
ieee80211_mps_set_frame_flags(sta->sdata, sta, hdr);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&mpath->frame_queue.lock, flags);
|
||||
|
|
|
@ -55,6 +55,66 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta)
|
|||
sta->plink_retries = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* mesh_set_short_slot_time - enable / disable ERP short slot time.
|
||||
*
|
||||
* The standard indirectly mandates mesh STAs to turn off short slot time by
|
||||
* disallowing advertising this (802.11-2012 8.4.1.4), but that doesn't mean we
|
||||
* can't be sneaky about it. Enable short slot time if all mesh STAs in the
|
||||
* MBSS support ERP rates.
|
||||
*
|
||||
* Returns BSS_CHANGED_ERP_SLOT or 0 for no change.
|
||||
*/
|
||||
static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
|
||||
struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
|
||||
struct sta_info *sta;
|
||||
u32 erp_rates = 0, changed = 0;
|
||||
int i;
|
||||
bool short_slot = false;
|
||||
|
||||
if (band == IEEE80211_BAND_5GHZ) {
|
||||
/* (IEEE 802.11-2012 19.4.5) */
|
||||
short_slot = true;
|
||||
goto out;
|
||||
} else if (band != IEEE80211_BAND_2GHZ ||
|
||||
(band == IEEE80211_BAND_2GHZ &&
|
||||
local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < sband->n_bitrates; i++)
|
||||
if (sband->bitrates[i].flags & IEEE80211_RATE_ERP_G)
|
||||
erp_rates |= BIT(i);
|
||||
|
||||
if (!erp_rates)
|
||||
goto out;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||
if (sdata != sta->sdata ||
|
||||
sta->plink_state != NL80211_PLINK_ESTAB)
|
||||
continue;
|
||||
|
||||
short_slot = false;
|
||||
if (erp_rates & sta->sta.supp_rates[band])
|
||||
short_slot = true;
|
||||
else
|
||||
break;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
out:
|
||||
if (sdata->vif.bss_conf.use_short_slot != short_slot) {
|
||||
sdata->vif.bss_conf.use_short_slot = short_slot;
|
||||
changed = BSS_CHANGED_ERP_SLOT;
|
||||
mpl_dbg(sdata, "mesh_plink %pM: ERP short slot time %d\n",
|
||||
sdata->vif.addr, short_slot);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* mesh_set_ht_prot_mode - set correct HT protection mode
|
||||
*
|
||||
|
@ -141,6 +201,9 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)
|
|||
sta->plink_state = NL80211_PLINK_BLOCKED;
|
||||
mesh_path_flush_by_nexthop(sta);
|
||||
|
||||
ieee80211_mps_sta_status_update(sta);
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
@ -443,6 +506,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
|
|||
rssi_threshold_check(sta, sdata))
|
||||
mesh_plink_open(sta);
|
||||
|
||||
ieee80211_mps_frame_release(sta, elems);
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
@ -573,6 +637,9 @@ int mesh_plink_open(struct sta_info *sta)
|
|||
"Mesh plink: starting establishment with %pM\n",
|
||||
sta->sta.addr);
|
||||
|
||||
/* set the non-peer mode to active during peering */
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
|
||||
return mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
|
||||
sta->sta.addr, llid, 0, 0);
|
||||
}
|
||||
|
@ -806,6 +873,10 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
|
|||
sta->llid = llid;
|
||||
mesh_plink_timer_set(sta,
|
||||
mshcfg->dot11MeshRetryTimeout);
|
||||
|
||||
/* set the non-peer mode to active during peering */
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
|
||||
spin_unlock_bh(&sta->lock);
|
||||
mesh_plink_frame_tx(sdata,
|
||||
WLAN_SP_MESH_PEERING_OPEN,
|
||||
|
@ -896,8 +967,12 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
|
|||
spin_unlock_bh(&sta->lock);
|
||||
changed |= mesh_plink_inc_estab_count(sdata);
|
||||
changed |= mesh_set_ht_prot_mode(sdata);
|
||||
changed |= mesh_set_short_slot_time(sdata);
|
||||
mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n",
|
||||
sta->sta.addr);
|
||||
ieee80211_mps_sta_status_update(sta);
|
||||
ieee80211_mps_set_sta_local_pm(sta,
|
||||
mshcfg->power_mode);
|
||||
break;
|
||||
default:
|
||||
spin_unlock_bh(&sta->lock);
|
||||
|
@ -931,11 +1006,15 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
|
|||
spin_unlock_bh(&sta->lock);
|
||||
changed |= mesh_plink_inc_estab_count(sdata);
|
||||
changed |= mesh_set_ht_prot_mode(sdata);
|
||||
changed |= mesh_set_short_slot_time(sdata);
|
||||
mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n",
|
||||
sta->sta.addr);
|
||||
mesh_plink_frame_tx(sdata,
|
||||
WLAN_SP_MESH_PEERING_CONFIRM,
|
||||
sta->sta.addr, llid, plid, 0);
|
||||
ieee80211_mps_sta_status_update(sta);
|
||||
ieee80211_mps_set_sta_local_pm(sta,
|
||||
mshcfg->power_mode);
|
||||
break;
|
||||
default:
|
||||
spin_unlock_bh(&sta->lock);
|
||||
|
@ -954,6 +1033,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
|
|||
mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
|
||||
spin_unlock_bh(&sta->lock);
|
||||
changed |= mesh_set_ht_prot_mode(sdata);
|
||||
changed |= mesh_set_short_slot_time(sdata);
|
||||
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
|
||||
sta->sta.addr, llid, plid, reason);
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,585 @@
|
|||
/*
|
||||
* Copyright 2012-2013, Marco Porsch <marco.porsch@s2005.tu-chemnitz.de>
|
||||
* Copyright 2012-2013, cozybit Inc.
|
||||
*
|
||||
* 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 "mesh.h"
|
||||
#include "wme.h"
|
||||
|
||||
|
||||
/* mesh PS management */
|
||||
|
||||
/**
|
||||
* mps_qos_null_get - create pre-addressed QoS Null frame for mesh powersave
|
||||
*/
|
||||
static struct sk_buff *mps_qos_null_get(struct sta_info *sta)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_hdr *nullfunc; /* use 4addr header */
|
||||
struct sk_buff *skb;
|
||||
int size = sizeof(*nullfunc);
|
||||
__le16 fc;
|
||||
|
||||
skb = dev_alloc_skb(local->hw.extra_tx_headroom + size + 2);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
skb_reserve(skb, local->hw.extra_tx_headroom);
|
||||
|
||||
nullfunc = (struct ieee80211_hdr *) skb_put(skb, size);
|
||||
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC);
|
||||
ieee80211_fill_mesh_addresses(nullfunc, &fc, sta->sta.addr,
|
||||
sdata->vif.addr);
|
||||
nullfunc->frame_control = fc;
|
||||
nullfunc->duration_id = 0;
|
||||
/* no address resolution for this frame -> set addr 1 immediately */
|
||||
memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);
|
||||
memset(skb_put(skb, 2), 0, 2); /* append QoS control field */
|
||||
ieee80211_mps_set_frame_flags(sdata, sta, nullfunc);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
/**
|
||||
* mps_qos_null_tx - send a QoS Null to indicate link-specific power mode
|
||||
*/
|
||||
static void mps_qos_null_tx(struct sta_info *sta)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = mps_qos_null_get(sta);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
mps_dbg(sta->sdata, "announcing peer-specific power mode to %pM\n",
|
||||
sta->sta.addr);
|
||||
|
||||
/* don't unintentionally start a MPSP */
|
||||
if (!test_sta_flag(sta, WLAN_STA_PS_STA)) {
|
||||
u8 *qc = ieee80211_get_qos_ctl((void *) skb->data);
|
||||
|
||||
qc[0] |= IEEE80211_QOS_CTL_EOSP;
|
||||
}
|
||||
|
||||
ieee80211_tx_skb(sta->sdata, skb);
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_mps_local_status_update - track status of local link-specific PMs
|
||||
*
|
||||
* @sdata: local mesh subif
|
||||
*
|
||||
* sets the non-peer power mode and triggers the driver PS (re-)configuration
|
||||
*/
|
||||
void ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct sta_info *sta;
|
||||
bool peering = false;
|
||||
int light_sleep_cnt = 0;
|
||||
int deep_sleep_cnt = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
|
||||
if (sdata != sta->sdata)
|
||||
continue;
|
||||
|
||||
switch (sta->plink_state) {
|
||||
case NL80211_PLINK_OPN_SNT:
|
||||
case NL80211_PLINK_OPN_RCVD:
|
||||
case NL80211_PLINK_CNF_RCVD:
|
||||
peering = true;
|
||||
break;
|
||||
case NL80211_PLINK_ESTAB:
|
||||
if (sta->local_pm == NL80211_MESH_POWER_LIGHT_SLEEP)
|
||||
light_sleep_cnt++;
|
||||
else if (sta->local_pm == NL80211_MESH_POWER_DEEP_SLEEP)
|
||||
deep_sleep_cnt++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
/*
|
||||
* Set non-peer mode to active during peering/scanning/authentication
|
||||
* (see IEEE802.11-2012 13.14.8.3). The non-peer mesh power mode is
|
||||
* deep sleep if the local STA is in light or deep sleep towards at
|
||||
* least one mesh peer (see 13.14.3.1). Otherwise, set it to the
|
||||
* user-configured default value.
|
||||
*/
|
||||
if (peering) {
|
||||
mps_dbg(sdata, "setting non-peer PM to active for peering\n");
|
||||
ifmsh->nonpeer_pm = NL80211_MESH_POWER_ACTIVE;
|
||||
} else if (light_sleep_cnt || deep_sleep_cnt) {
|
||||
mps_dbg(sdata, "setting non-peer PM to deep sleep\n");
|
||||
ifmsh->nonpeer_pm = NL80211_MESH_POWER_DEEP_SLEEP;
|
||||
} else {
|
||||
mps_dbg(sdata, "setting non-peer PM to user value\n");
|
||||
ifmsh->nonpeer_pm = ifmsh->mshcfg.power_mode;
|
||||
}
|
||||
|
||||
ifmsh->ps_peers_light_sleep = light_sleep_cnt;
|
||||
ifmsh->ps_peers_deep_sleep = deep_sleep_cnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_mps_set_sta_local_pm - set local PM towards a mesh STA
|
||||
*
|
||||
* @sta: mesh STA
|
||||
* @pm: the power mode to set
|
||||
*/
|
||||
void ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
|
||||
enum nl80211_mesh_power_mode pm)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
|
||||
mps_dbg(sdata, "local STA operates in mode %d with %pM\n",
|
||||
pm, sta->sta.addr);
|
||||
|
||||
sta->local_pm = pm;
|
||||
|
||||
/*
|
||||
* announce peer-specific power mode transition
|
||||
* (see IEEE802.11-2012 13.14.3.2 and 13.14.3.3)
|
||||
*/
|
||||
if (sta->plink_state == NL80211_PLINK_ESTAB)
|
||||
mps_qos_null_tx(sta);
|
||||
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_mps_set_frame_flags - set mesh PS flags in FC (and QoS Control)
|
||||
*
|
||||
* @sdata: local mesh subif
|
||||
* @sta: mesh STA
|
||||
* @hdr: 802.11 frame header
|
||||
*
|
||||
* see IEEE802.11-2012 8.2.4.1.7 and 8.2.4.5.11
|
||||
*
|
||||
* NOTE: sta must be given when an individually-addressed QoS frame header
|
||||
* is handled, for group-addressed and management frames it is not used
|
||||
*/
|
||||
void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata,
|
||||
struct sta_info *sta,
|
||||
struct ieee80211_hdr *hdr)
|
||||
{
|
||||
enum nl80211_mesh_power_mode pm;
|
||||
u8 *qc;
|
||||
|
||||
if (WARN_ON(is_unicast_ether_addr(hdr->addr1) &&
|
||||
ieee80211_is_data_qos(hdr->frame_control) &&
|
||||
!sta))
|
||||
return;
|
||||
|
||||
if (is_unicast_ether_addr(hdr->addr1) &&
|
||||
ieee80211_is_data_qos(hdr->frame_control) &&
|
||||
sta->plink_state == NL80211_PLINK_ESTAB)
|
||||
pm = sta->local_pm;
|
||||
else
|
||||
pm = sdata->u.mesh.nonpeer_pm;
|
||||
|
||||
if (pm == NL80211_MESH_POWER_ACTIVE)
|
||||
hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_PM);
|
||||
else
|
||||
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
|
||||
|
||||
if (!ieee80211_is_data_qos(hdr->frame_control))
|
||||
return;
|
||||
|
||||
qc = ieee80211_get_qos_ctl(hdr);
|
||||
|
||||
if ((is_unicast_ether_addr(hdr->addr1) &&
|
||||
pm == NL80211_MESH_POWER_DEEP_SLEEP) ||
|
||||
(is_multicast_ether_addr(hdr->addr1) &&
|
||||
sdata->u.mesh.ps_peers_deep_sleep > 0))
|
||||
qc[1] |= (IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8);
|
||||
else
|
||||
qc[1] &= ~(IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_mps_sta_status_update - update buffering status of neighbor STA
|
||||
*
|
||||
* @sta: mesh STA
|
||||
*
|
||||
* called after change of peering status or non-peer/peer-specific power mode
|
||||
*/
|
||||
void ieee80211_mps_sta_status_update(struct sta_info *sta)
|
||||
{
|
||||
enum nl80211_mesh_power_mode pm;
|
||||
bool do_buffer;
|
||||
|
||||
/*
|
||||
* use peer-specific power mode if peering is established and the
|
||||
* peer's power mode is known
|
||||
*/
|
||||
if (sta->plink_state == NL80211_PLINK_ESTAB &&
|
||||
sta->peer_pm != NL80211_MESH_POWER_UNKNOWN)
|
||||
pm = sta->peer_pm;
|
||||
else
|
||||
pm = sta->nonpeer_pm;
|
||||
|
||||
do_buffer = (pm != NL80211_MESH_POWER_ACTIVE);
|
||||
|
||||
/* Don't let the same PS state be set twice */
|
||||
if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer)
|
||||
return;
|
||||
|
||||
if (do_buffer) {
|
||||
set_sta_flag(sta, WLAN_STA_PS_STA);
|
||||
atomic_inc(&sta->sdata->u.mesh.ps.num_sta_ps);
|
||||
mps_dbg(sta->sdata, "start PS buffering frames towards %pM\n",
|
||||
sta->sta.addr);
|
||||
} else {
|
||||
ieee80211_sta_ps_deliver_wakeup(sta);
|
||||
}
|
||||
|
||||
/* clear the MPSP flags for non-peers or active STA */
|
||||
if (sta->plink_state != NL80211_PLINK_ESTAB) {
|
||||
clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
|
||||
clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
|
||||
} else if (!do_buffer) {
|
||||
clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
|
||||
}
|
||||
}
|
||||
|
||||
static void mps_set_sta_peer_pm(struct sta_info *sta,
|
||||
struct ieee80211_hdr *hdr)
|
||||
{
|
||||
enum nl80211_mesh_power_mode pm;
|
||||
u8 *qc = ieee80211_get_qos_ctl(hdr);
|
||||
|
||||
/*
|
||||
* Test Power Management field of frame control (PW) and
|
||||
* mesh power save level subfield of QoS control field (PSL)
|
||||
*
|
||||
* | PM | PSL| Mesh PM |
|
||||
* +----+----+---------+
|
||||
* | 0 |Rsrv| Active |
|
||||
* | 1 | 0 | Light |
|
||||
* | 1 | 1 | Deep |
|
||||
*/
|
||||
if (ieee80211_has_pm(hdr->frame_control)) {
|
||||
if (qc[1] & (IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8))
|
||||
pm = NL80211_MESH_POWER_DEEP_SLEEP;
|
||||
else
|
||||
pm = NL80211_MESH_POWER_LIGHT_SLEEP;
|
||||
} else {
|
||||
pm = NL80211_MESH_POWER_ACTIVE;
|
||||
}
|
||||
|
||||
if (sta->peer_pm == pm)
|
||||
return;
|
||||
|
||||
mps_dbg(sta->sdata, "STA %pM enters mode %d\n",
|
||||
sta->sta.addr, pm);
|
||||
|
||||
sta->peer_pm = pm;
|
||||
|
||||
ieee80211_mps_sta_status_update(sta);
|
||||
}
|
||||
|
||||
static void mps_set_sta_nonpeer_pm(struct sta_info *sta,
|
||||
struct ieee80211_hdr *hdr)
|
||||
{
|
||||
enum nl80211_mesh_power_mode pm;
|
||||
|
||||
if (ieee80211_has_pm(hdr->frame_control))
|
||||
pm = NL80211_MESH_POWER_DEEP_SLEEP;
|
||||
else
|
||||
pm = NL80211_MESH_POWER_ACTIVE;
|
||||
|
||||
if (sta->nonpeer_pm == pm)
|
||||
return;
|
||||
|
||||
mps_dbg(sta->sdata, "STA %pM sets non-peer mode to %d\n",
|
||||
sta->sta.addr, pm);
|
||||
|
||||
sta->nonpeer_pm = pm;
|
||||
|
||||
ieee80211_mps_sta_status_update(sta);
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_mps_rx_h_sta_process - frame receive handler for mesh powersave
|
||||
*
|
||||
* @sta: STA info that transmitted the frame
|
||||
* @hdr: IEEE 802.11 (QoS) Header
|
||||
*/
|
||||
void ieee80211_mps_rx_h_sta_process(struct sta_info *sta,
|
||||
struct ieee80211_hdr *hdr)
|
||||
{
|
||||
if (is_unicast_ether_addr(hdr->addr1) &&
|
||||
ieee80211_is_data_qos(hdr->frame_control)) {
|
||||
/*
|
||||
* individually addressed QoS Data/Null frames contain
|
||||
* peer link-specific PS mode towards the local STA
|
||||
*/
|
||||
mps_set_sta_peer_pm(sta, hdr);
|
||||
|
||||
/* check for mesh Peer Service Period trigger frames */
|
||||
ieee80211_mpsp_trigger_process(ieee80211_get_qos_ctl(hdr),
|
||||
sta, false, false);
|
||||
} else {
|
||||
/*
|
||||
* can only determine non-peer PS mode
|
||||
* (see IEEE802.11-2012 8.2.4.1.7)
|
||||
*/
|
||||
mps_set_sta_nonpeer_pm(sta, hdr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* mesh PS frame release */
|
||||
|
||||
static void mpsp_trigger_send(struct sta_info *sta, bool rspi, bool eosp)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_hdr *nullfunc;
|
||||
struct ieee80211_tx_info *info;
|
||||
u8 *qc;
|
||||
|
||||
skb = mps_qos_null_get(sta);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
nullfunc = (struct ieee80211_hdr *) skb->data;
|
||||
if (!eosp)
|
||||
nullfunc->frame_control |=
|
||||
cpu_to_le16(IEEE80211_FCTL_MOREDATA);
|
||||
/*
|
||||
* | RSPI | EOSP | MPSP triggering |
|
||||
* +------+------+--------------------+
|
||||
* | 0 | 0 | local STA is owner |
|
||||
* | 0 | 1 | no MPSP (MPSP end) |
|
||||
* | 1 | 0 | both STA are owner |
|
||||
* | 1 | 1 | peer STA is owner | see IEEE802.11-2012 13.14.9.2
|
||||
*/
|
||||
qc = ieee80211_get_qos_ctl(nullfunc);
|
||||
if (rspi)
|
||||
qc[1] |= (IEEE80211_QOS_CTL_RSPI >> 8);
|
||||
if (eosp)
|
||||
qc[0] |= IEEE80211_QOS_CTL_EOSP;
|
||||
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER |
|
||||
IEEE80211_TX_CTL_REQ_TX_STATUS;
|
||||
|
||||
mps_dbg(sdata, "sending MPSP trigger%s%s to %pM\n",
|
||||
rspi ? " RSPI" : "", eosp ? " EOSP" : "", sta->sta.addr);
|
||||
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
}
|
||||
|
||||
/**
|
||||
* mpsp_qos_null_append - append QoS Null frame to MPSP skb queue if needed
|
||||
*
|
||||
* To properly end a mesh MPSP the last transmitted frame has to set the EOSP
|
||||
* flag in the QoS Control field. In case the current tailing frame is not a
|
||||
* QoS Data frame, append a QoS Null to carry the flag.
|
||||
*/
|
||||
static void mpsp_qos_null_append(struct sta_info *sta,
|
||||
struct sk_buff_head *frames)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
struct sk_buff *new_skb, *skb = skb_peek_tail(frames);
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
struct ieee80211_tx_info *info;
|
||||
|
||||
if (ieee80211_is_data_qos(hdr->frame_control))
|
||||
return;
|
||||
|
||||
new_skb = mps_qos_null_get(sta);
|
||||
if (!new_skb)
|
||||
return;
|
||||
|
||||
mps_dbg(sdata, "appending QoS Null in MPSP towards %pM\n",
|
||||
sta->sta.addr);
|
||||
/*
|
||||
* This frame has to be transmitted last. Assign lowest priority to
|
||||
* make sure it cannot pass other frames when releasing multiple ACs.
|
||||
*/
|
||||
new_skb->priority = 1;
|
||||
skb_set_queue_mapping(new_skb, IEEE80211_AC_BK);
|
||||
ieee80211_set_qos_hdr(sdata, new_skb);
|
||||
|
||||
info = IEEE80211_SKB_CB(new_skb);
|
||||
info->control.vif = &sdata->vif;
|
||||
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
|
||||
|
||||
__skb_queue_tail(frames, new_skb);
|
||||
}
|
||||
|
||||
/**
|
||||
* mps_frame_deliver - transmit frames during mesh powersave
|
||||
*
|
||||
* @sta: STA info to transmit to
|
||||
* @n_frames: number of frames to transmit. -1 for all
|
||||
*/
|
||||
static void mps_frame_deliver(struct sta_info *sta, int n_frames)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int ac;
|
||||
struct sk_buff_head frames;
|
||||
struct sk_buff *skb;
|
||||
bool more_data = false;
|
||||
|
||||
skb_queue_head_init(&frames);
|
||||
|
||||
/* collect frame(s) from buffers */
|
||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||
while (n_frames != 0) {
|
||||
skb = skb_dequeue(&sta->tx_filtered[ac]);
|
||||
if (!skb) {
|
||||
skb = skb_dequeue(
|
||||
&sta->ps_tx_buf[ac]);
|
||||
if (skb)
|
||||
local->total_ps_buffered--;
|
||||
}
|
||||
if (!skb)
|
||||
break;
|
||||
n_frames--;
|
||||
__skb_queue_tail(&frames, skb);
|
||||
}
|
||||
|
||||
if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
|
||||
!skb_queue_empty(&sta->ps_tx_buf[ac]))
|
||||
more_data = true;
|
||||
}
|
||||
|
||||
/* nothing to send? -> EOSP */
|
||||
if (skb_queue_empty(&frames)) {
|
||||
mpsp_trigger_send(sta, false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
/* in a MPSP make sure the last skb is a QoS Data frame */
|
||||
if (test_sta_flag(sta, WLAN_STA_MPSP_OWNER))
|
||||
mpsp_qos_null_append(sta, &frames);
|
||||
|
||||
mps_dbg(sta->sdata, "sending %d frames to PS STA %pM\n",
|
||||
skb_queue_len(&frames), sta->sta.addr);
|
||||
|
||||
/* prepare collected frames for transmission */
|
||||
skb_queue_walk(&frames, skb) {
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_hdr *hdr = (void *) skb->data;
|
||||
|
||||
/*
|
||||
* Tell TX path to send this frame even though the
|
||||
* STA may still remain is PS mode after this frame
|
||||
* exchange.
|
||||
*/
|
||||
info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
|
||||
|
||||
if (more_data || !skb_queue_is_last(&frames, skb))
|
||||
hdr->frame_control |=
|
||||
cpu_to_le16(IEEE80211_FCTL_MOREDATA);
|
||||
else
|
||||
hdr->frame_control &=
|
||||
cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
|
||||
|
||||
if (skb_queue_is_last(&frames, skb) &&
|
||||
ieee80211_is_data_qos(hdr->frame_control)) {
|
||||
u8 *qoshdr = ieee80211_get_qos_ctl(hdr);
|
||||
|
||||
/* MPSP trigger frame ends service period */
|
||||
*qoshdr |= IEEE80211_QOS_CTL_EOSP;
|
||||
info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
|
||||
}
|
||||
}
|
||||
|
||||
ieee80211_add_pending_skbs(local, &frames);
|
||||
sta_info_recalc_tim(sta);
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_mpsp_trigger_process - track status of mesh Peer Service Periods
|
||||
*
|
||||
* @qc: QoS Control field
|
||||
* @sta: peer to start a MPSP with
|
||||
* @tx: frame was transmitted by the local STA
|
||||
* @acked: frame has been transmitted successfully
|
||||
*
|
||||
* NOTE: active mode STA may only serve as MPSP owner
|
||||
*/
|
||||
void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,
|
||||
bool tx, bool acked)
|
||||
{
|
||||
u8 rspi = qc[1] & (IEEE80211_QOS_CTL_RSPI >> 8);
|
||||
u8 eosp = qc[0] & IEEE80211_QOS_CTL_EOSP;
|
||||
|
||||
if (tx) {
|
||||
if (rspi && acked)
|
||||
set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
|
||||
|
||||
if (eosp)
|
||||
clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
|
||||
else if (acked &&
|
||||
test_sta_flag(sta, WLAN_STA_PS_STA) &&
|
||||
!test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER))
|
||||
mps_frame_deliver(sta, -1);
|
||||
} else {
|
||||
if (eosp)
|
||||
clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
|
||||
else if (sta->local_pm != NL80211_MESH_POWER_ACTIVE)
|
||||
set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
|
||||
|
||||
if (rspi && !test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER))
|
||||
mps_frame_deliver(sta, -1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_mps_frame_release - release buffered frames in response to beacon
|
||||
*
|
||||
* @sta: mesh STA
|
||||
* @elems: beacon IEs
|
||||
*
|
||||
* For peers if we have individually-addressed frames buffered or the peer
|
||||
* indicates buffered frames, send a corresponding MPSP trigger frame. Since
|
||||
* we do not evaluate the awake window duration, QoS Nulls are used as MPSP
|
||||
* trigger frames. If the neighbour STA is not a peer, only send single frames.
|
||||
*/
|
||||
void ieee80211_mps_frame_release(struct sta_info *sta,
|
||||
struct ieee802_11_elems *elems)
|
||||
{
|
||||
int ac, buffer_local = 0;
|
||||
bool has_buffered = false;
|
||||
|
||||
/* TIM map only for LLID <= IEEE80211_MAX_AID */
|
||||
if (sta->plink_state == NL80211_PLINK_ESTAB)
|
||||
has_buffered = ieee80211_check_tim(elems->tim, elems->tim_len,
|
||||
le16_to_cpu(sta->llid) % IEEE80211_MAX_AID);
|
||||
|
||||
if (has_buffered)
|
||||
mps_dbg(sta->sdata, "%pM indicates buffered frames\n",
|
||||
sta->sta.addr);
|
||||
|
||||
/* only transmit to PS STA with announced, non-zero awake window */
|
||||
if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
|
||||
(!elems->awake_window || !le16_to_cpu(*elems->awake_window)))
|
||||
return;
|
||||
|
||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
|
||||
buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) +
|
||||
skb_queue_len(&sta->tx_filtered[ac]);
|
||||
|
||||
if (!has_buffered && !buffer_local)
|
||||
return;
|
||||
|
||||
if (sta->plink_state == NL80211_PLINK_ESTAB)
|
||||
mpsp_trigger_send(sta, has_buffered, !buffer_local);
|
||||
else
|
||||
mps_frame_deliver(sta, 1);
|
||||
}
|
|
@ -30,11 +30,13 @@
|
|||
#include "rate.h"
|
||||
#include "led.h"
|
||||
|
||||
#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
|
||||
#define IEEE80211_AUTH_MAX_TRIES 3
|
||||
#define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5)
|
||||
#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
|
||||
#define IEEE80211_ASSOC_MAX_TRIES 3
|
||||
#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
|
||||
#define IEEE80211_AUTH_TIMEOUT_SHORT (HZ / 10)
|
||||
#define IEEE80211_AUTH_MAX_TRIES 3
|
||||
#define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5)
|
||||
#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
|
||||
#define IEEE80211_ASSOC_TIMEOUT_SHORT (HZ / 10)
|
||||
#define IEEE80211_ASSOC_MAX_TRIES 3
|
||||
|
||||
static int max_nullfunc_tries = 2;
|
||||
module_param(max_nullfunc_tries, int, 0644);
|
||||
|
@ -644,6 +646,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|
|||
drv_mgd_prepare_tx(local, sdata);
|
||||
|
||||
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
||||
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
|
||||
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
|
||||
IEEE80211_TX_INTFL_MLME_CONN_TX;
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
}
|
||||
|
||||
|
@ -1445,7 +1450,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
ieee80211_led_assoc(local, 1);
|
||||
|
||||
if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
|
||||
if (sdata->u.mgd.assoc_data->have_beacon) {
|
||||
/*
|
||||
* If the AP is buggy we may get here with no DTIM period
|
||||
* known, so assume it's 1 which is the only safe assumption
|
||||
|
@ -1453,6 +1458,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
|
|||
* probably just won't work at all.
|
||||
*/
|
||||
bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1;
|
||||
bss_info_changed |= BSS_CHANGED_DTIM_PERIOD;
|
||||
} else {
|
||||
bss_conf->dtim_period = 0;
|
||||
}
|
||||
|
@ -1706,7 +1712,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
|
|||
ssid_len = ssid[1];
|
||||
|
||||
ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
|
||||
0, (u32) -1, true, false,
|
||||
0, (u32) -1, true, 0,
|
||||
ifmgd->associated->channel, false);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
@ -1821,8 +1827,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
|
|||
}
|
||||
EXPORT_SYMBOL(ieee80211_ap_probereq_get);
|
||||
|
||||
static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata,
|
||||
bool transmit_frame)
|
||||
static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
|
@ -1836,7 +1841,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
||||
transmit_frame, frame_buf);
|
||||
true, frame_buf);
|
||||
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
|
||||
mutex_unlock(&ifmgd->mtx);
|
||||
|
||||
|
@ -1867,10 +1872,10 @@ static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
|
|||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) {
|
||||
if (ifmgd->connection_loss) {
|
||||
sdata_info(sdata, "Connection to AP %pM lost\n",
|
||||
ifmgd->bssid);
|
||||
__ieee80211_disconnect(sdata, false);
|
||||
__ieee80211_disconnect(sdata);
|
||||
} else {
|
||||
ieee80211_mgd_probe_ap(sdata, true);
|
||||
}
|
||||
|
@ -1884,7 +1889,7 @@ static void ieee80211_csa_connection_drop_work(struct work_struct *work)
|
|||
|
||||
ieee80211_wake_queues_by_reason(&sdata->local->hw,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
__ieee80211_disconnect(sdata, true);
|
||||
__ieee80211_disconnect(sdata);
|
||||
}
|
||||
|
||||
void ieee80211_beacon_loss(struct ieee80211_vif *vif)
|
||||
|
@ -1895,6 +1900,7 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif)
|
|||
trace_api_beacon_loss(sdata);
|
||||
|
||||
WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR);
|
||||
sdata->u.mgd.connection_loss = false;
|
||||
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_beacon_loss);
|
||||
|
@ -1906,7 +1912,7 @@ void ieee80211_connection_loss(struct ieee80211_vif *vif)
|
|||
|
||||
trace_api_connection_loss(sdata);
|
||||
|
||||
WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR));
|
||||
sdata->u.mgd.connection_loss = true;
|
||||
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_connection_loss);
|
||||
|
@ -1936,9 +1942,11 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
|
|||
static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt, size_t len)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data;
|
||||
u8 *pos;
|
||||
struct ieee802_11_elems elems;
|
||||
u32 tx_flags = 0;
|
||||
|
||||
pos = mgmt->u.auth.variable;
|
||||
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
|
||||
|
@ -1946,11 +1954,14 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
|
|||
return;
|
||||
auth_data->expected_transaction = 4;
|
||||
drv_mgd_prepare_tx(sdata->local, sdata);
|
||||
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
|
||||
tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
|
||||
IEEE80211_TX_INTFL_MLME_CONN_TX;
|
||||
ieee80211_send_auth(sdata, 3, auth_data->algorithm, 0,
|
||||
elems.challenge - 2, elems.challenge_len + 2,
|
||||
auth_data->bss->bssid, auth_data->bss->bssid,
|
||||
auth_data->key, auth_data->key_len,
|
||||
auth_data->key_idx);
|
||||
auth_data->key_idx, tx_flags);
|
||||
}
|
||||
|
||||
static enum rx_mgmt_action __must_check
|
||||
|
@ -2548,14 +2559,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|||
chan = chanctx_conf->def.chan;
|
||||
rcu_read_unlock();
|
||||
|
||||
if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon &&
|
||||
if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
|
||||
ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) {
|
||||
ieee802_11_parse_elems(mgmt->u.beacon.variable,
|
||||
len - baselen, &elems);
|
||||
|
||||
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
|
||||
ifmgd->assoc_data->have_beacon = true;
|
||||
ifmgd->assoc_data->sent_assoc = false;
|
||||
ifmgd->assoc_data->need_beacon = false;
|
||||
/* continue assoc process */
|
||||
ifmgd->assoc_data->timeout = jiffies;
|
||||
run_again(ifmgd, ifmgd->assoc_data->timeout);
|
||||
|
@ -2712,6 +2723,19 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|||
elems.wmm_param_len))
|
||||
changed |= BSS_CHANGED_QOS;
|
||||
|
||||
/*
|
||||
* If we haven't had a beacon before, tell the driver about the
|
||||
* DTIM period now.
|
||||
*/
|
||||
if (!bss_conf->dtim_period) {
|
||||
/* a few bogus AP send dtim_period = 0 or no TIM IE */
|
||||
if (elems.tim)
|
||||
bss_conf->dtim_period = elems.tim->dtim_period ?: 1;
|
||||
else
|
||||
bss_conf->dtim_period = 1;
|
||||
changed |= BSS_CHANGED_DTIM_PERIOD;
|
||||
}
|
||||
|
||||
if (elems.erp_info && elems.erp_info_len >= 1) {
|
||||
erp_valid = true;
|
||||
erp_value = elems.erp_info[0];
|
||||
|
@ -2827,14 +2851,14 @@ static void ieee80211_sta_timer(unsigned long data)
|
|||
}
|
||||
|
||||
static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
|
||||
u8 *bssid, u8 reason)
|
||||
u8 *bssid, u8 reason, bool tx)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
||||
|
||||
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
|
||||
false, frame_buf);
|
||||
tx, frame_buf);
|
||||
mutex_unlock(&ifmgd->mtx);
|
||||
|
||||
/*
|
||||
|
@ -2855,12 +2879,17 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
|
|||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data;
|
||||
u32 tx_flags = 0;
|
||||
|
||||
lockdep_assert_held(&ifmgd->mtx);
|
||||
|
||||
if (WARN_ON_ONCE(!auth_data))
|
||||
return -EINVAL;
|
||||
|
||||
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
|
||||
tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
|
||||
IEEE80211_TX_INTFL_MLME_CONN_TX;
|
||||
|
||||
auth_data->tries++;
|
||||
|
||||
if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) {
|
||||
|
@ -2897,7 +2926,8 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
|
|||
ieee80211_send_auth(sdata, trans, auth_data->algorithm, status,
|
||||
auth_data->data, auth_data->data_len,
|
||||
auth_data->bss->bssid,
|
||||
auth_data->bss->bssid, NULL, 0, 0);
|
||||
auth_data->bss->bssid, NULL, 0, 0,
|
||||
tx_flags);
|
||||
} else {
|
||||
const u8 *ssidie;
|
||||
|
||||
|
@ -2916,13 +2946,15 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
|
|||
* will not answer to direct packet in unassociated state.
|
||||
*/
|
||||
ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1],
|
||||
NULL, 0, (u32) -1, true, false,
|
||||
NULL, 0, (u32) -1, true, tx_flags,
|
||||
auth_data->bss->channel, false);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
|
||||
run_again(ifmgd, auth_data->timeout);
|
||||
if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
|
||||
auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
|
||||
run_again(ifmgd, auth_data->timeout);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2953,12 +2985,26 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
|
|||
IEEE80211_ASSOC_MAX_TRIES);
|
||||
ieee80211_send_assoc(sdata);
|
||||
|
||||
assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
|
||||
run_again(&sdata->u.mgd, assoc_data->timeout);
|
||||
if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
|
||||
assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
|
||||
run_again(&sdata->u.mgd, assoc_data->timeout);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
|
||||
__le16 fc, bool acked)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
|
||||
sdata->u.mgd.status_fc = fc;
|
||||
sdata->u.mgd.status_acked = acked;
|
||||
sdata->u.mgd.status_received = true;
|
||||
|
||||
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||
}
|
||||
|
||||
void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
|
@ -2966,6 +3012,33 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
|||
|
||||
mutex_lock(&ifmgd->mtx);
|
||||
|
||||
if (ifmgd->status_received) {
|
||||
__le16 fc = ifmgd->status_fc;
|
||||
bool status_acked = ifmgd->status_acked;
|
||||
|
||||
ifmgd->status_received = false;
|
||||
if (ifmgd->auth_data &&
|
||||
(ieee80211_is_probe_req(fc) || ieee80211_is_auth(fc))) {
|
||||
if (status_acked) {
|
||||
ifmgd->auth_data->timeout =
|
||||
jiffies + IEEE80211_AUTH_TIMEOUT_SHORT;
|
||||
run_again(ifmgd, ifmgd->auth_data->timeout);
|
||||
} else {
|
||||
ifmgd->auth_data->timeout = jiffies - 1;
|
||||
}
|
||||
} else if (ifmgd->assoc_data &&
|
||||
(ieee80211_is_assoc_req(fc) ||
|
||||
ieee80211_is_reassoc_req(fc))) {
|
||||
if (status_acked) {
|
||||
ifmgd->assoc_data->timeout =
|
||||
jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT;
|
||||
run_again(ifmgd, ifmgd->assoc_data->timeout);
|
||||
} else {
|
||||
ifmgd->assoc_data->timeout = jiffies - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ifmgd->auth_data &&
|
||||
time_after(jiffies, ifmgd->auth_data->timeout)) {
|
||||
if (ifmgd->auth_data->done) {
|
||||
|
@ -2990,7 +3063,8 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
|||
|
||||
if (ifmgd->assoc_data &&
|
||||
time_after(jiffies, ifmgd->assoc_data->timeout)) {
|
||||
if (!ifmgd->assoc_data->have_beacon ||
|
||||
if ((ifmgd->assoc_data->need_beacon &&
|
||||
!ifmgd->assoc_data->have_beacon) ||
|
||||
ieee80211_do_assoc(sdata)) {
|
||||
u8 bssid[ETH_ALEN];
|
||||
|
||||
|
@ -3033,7 +3107,8 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
|||
"No ack for nullfunc frame to AP %pM, disconnecting.\n",
|
||||
bssid);
|
||||
ieee80211_sta_connection_lost(sdata, bssid,
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
||||
false);
|
||||
}
|
||||
} else if (time_is_after_jiffies(ifmgd->probe_timeout))
|
||||
run_again(ifmgd, ifmgd->probe_timeout);
|
||||
|
@ -3042,7 +3117,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
|||
"Failed to send nullfunc to AP %pM after %dms, disconnecting\n",
|
||||
bssid, probe_wait_ms);
|
||||
ieee80211_sta_connection_lost(sdata, bssid,
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);
|
||||
} else if (ifmgd->probe_send_count < max_tries) {
|
||||
mlme_dbg(sdata,
|
||||
"No probe response from AP %pM after %dms, try %d/%i\n",
|
||||
|
@ -3061,7 +3136,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
|||
bssid, probe_wait_ms);
|
||||
|
||||
ieee80211_sta_connection_lost(sdata, bssid,
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3081,6 +3156,7 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
|
|||
if (local->quiescing)
|
||||
return;
|
||||
|
||||
sdata->u.mgd.connection_loss = false;
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&sdata->u.mgd.beacon_connection_loss_work);
|
||||
}
|
||||
|
@ -3167,7 +3243,8 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
|
|||
mlme_dbg(sdata, "driver requested disconnect after resume\n");
|
||||
ieee80211_sta_connection_lost(sdata,
|
||||
ifmgd->associated->bssid,
|
||||
WLAN_REASON_UNSPECIFIED);
|
||||
WLAN_REASON_UNSPECIFIED,
|
||||
true);
|
||||
mutex_unlock(&ifmgd->mtx);
|
||||
return;
|
||||
}
|
||||
|
@ -3777,6 +3854,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
|||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
struct ieee80211_bss *bss = (void *)req->bss->priv;
|
||||
struct ieee80211_mgd_assoc_data *assoc_data;
|
||||
const struct cfg80211_bss_ies *beacon_ies;
|
||||
struct ieee80211_supported_band *sband;
|
||||
const u8 *ssidie, *ht_ie, *vht_ie;
|
||||
int i, err;
|
||||
|
@ -3942,40 +4020,35 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
|||
if (err)
|
||||
goto err_clear;
|
||||
|
||||
if (sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
|
||||
const struct cfg80211_bss_ies *beacon_ies;
|
||||
rcu_read_lock();
|
||||
beacon_ies = rcu_dereference(req->bss->beacon_ies);
|
||||
|
||||
rcu_read_lock();
|
||||
beacon_ies = rcu_dereference(req->bss->beacon_ies);
|
||||
if (!beacon_ies) {
|
||||
/*
|
||||
* Wait up to one beacon interval ...
|
||||
* should this be more if we miss one?
|
||||
*/
|
||||
sdata_info(sdata, "waiting for beacon from %pM\n",
|
||||
ifmgd->bssid);
|
||||
assoc_data->timeout =
|
||||
TU_TO_EXP_TIME(req->bss->beacon_interval);
|
||||
} else {
|
||||
const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
|
||||
beacon_ies->data,
|
||||
beacon_ies->len);
|
||||
if (tim_ie && tim_ie[1] >=
|
||||
sizeof(struct ieee80211_tim_ie)) {
|
||||
const struct ieee80211_tim_ie *tim;
|
||||
tim = (void *)(tim_ie + 2);
|
||||
ifmgd->dtim_period = tim->dtim_period;
|
||||
}
|
||||
assoc_data->have_beacon = true;
|
||||
assoc_data->sent_assoc = false;
|
||||
assoc_data->timeout = jiffies;
|
||||
if (sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC &&
|
||||
!beacon_ies) {
|
||||
/*
|
||||
* Wait up to one beacon interval ...
|
||||
* should this be more if we miss one?
|
||||
*/
|
||||
sdata_info(sdata, "waiting for beacon from %pM\n",
|
||||
ifmgd->bssid);
|
||||
assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval);
|
||||
assoc_data->need_beacon = true;
|
||||
} else if (beacon_ies) {
|
||||
const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
|
||||
beacon_ies->data,
|
||||
beacon_ies->len);
|
||||
if (tim_ie && tim_ie[1] >= sizeof(struct ieee80211_tim_ie)) {
|
||||
const struct ieee80211_tim_ie *tim;
|
||||
tim = (void *)(tim_ie + 2);
|
||||
ifmgd->dtim_period = tim->dtim_period;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
} else {
|
||||
assoc_data->have_beacon = true;
|
||||
assoc_data->sent_assoc = false;
|
||||
assoc_data->timeout = jiffies;
|
||||
} else {
|
||||
assoc_data->timeout = jiffies;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
run_again(ifmgd, assoc_data->timeout);
|
||||
|
||||
if (bss->corrupt_data) {
|
||||
|
|
|
@ -228,3 +228,13 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
|
|||
* ieee80211_reconfig(), which is also needed for hardware
|
||||
* hang/firmware failure/etc. recovery.
|
||||
*/
|
||||
|
||||
void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
|
||||
struct cfg80211_wowlan_wakeup *wakeup,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||
|
||||
cfg80211_report_wowlan_wakeup(&sdata->wdev, wakeup, gfp);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_report_wowlan_wakeup);
|
||||
|
|
|
@ -1452,6 +1452,10 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
|
|||
}
|
||||
}
|
||||
|
||||
/* mesh power save support */
|
||||
if (ieee80211_vif_is_mesh(&rx->sdata->vif))
|
||||
ieee80211_mps_rx_h_sta_process(sta, hdr);
|
||||
|
||||
/*
|
||||
* Drop (qos-)data::nullfunc frames silently, since they
|
||||
* are used only to control station power saving mode.
|
||||
|
@ -2090,7 +2094,10 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
|
|||
if (is_multicast_ether_addr(fwd_hdr->addr1)) {
|
||||
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_mcast);
|
||||
memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN);
|
||||
/* update power mode indication when forwarding */
|
||||
ieee80211_mps_set_frame_flags(sdata, NULL, fwd_hdr);
|
||||
} else if (!mesh_nexthop_lookup(fwd_skb, sdata)) {
|
||||
/* mesh power mode flags updated in mesh_nexthop_lookup */
|
||||
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_unicast);
|
||||
} else {
|
||||
/* unable to resolve next hop */
|
||||
|
|
|
@ -27,15 +27,7 @@
|
|||
|
||||
#define IEEE80211_PROBE_DELAY (HZ / 33)
|
||||
#define IEEE80211_CHANNEL_TIME (HZ / 33)
|
||||
#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 8)
|
||||
|
||||
static void ieee80211_rx_bss_free(struct cfg80211_bss *cbss)
|
||||
{
|
||||
struct ieee80211_bss *bss = (void *)cbss->priv;
|
||||
|
||||
kfree(bss_mesh_id(bss));
|
||||
kfree(bss_mesh_cfg(bss));
|
||||
}
|
||||
#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 9)
|
||||
|
||||
void ieee80211_rx_bss_put(struct ieee80211_local *local,
|
||||
struct ieee80211_bss *bss)
|
||||
|
@ -85,7 +77,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
|
|||
if (!cbss)
|
||||
return NULL;
|
||||
|
||||
cbss->free_priv = ieee80211_rx_bss_free;
|
||||
bss = (void *)cbss->priv;
|
||||
|
||||
bss->device_ts = rx_status->device_timestamp;
|
||||
|
@ -146,9 +137,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
|
|||
bss->valid_data |= IEEE80211_BSS_VALID_WMM;
|
||||
}
|
||||
|
||||
if (!beacon)
|
||||
bss->last_probe_resp = jiffies;
|
||||
|
||||
return bss;
|
||||
}
|
||||
|
||||
|
@ -401,7 +389,8 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
|
|||
local->scan_req->ssids[i].ssid_len,
|
||||
local->scan_req->ie, local->scan_req->ie_len,
|
||||
local->scan_req->rates[band], false,
|
||||
local->scan_req->no_cck,
|
||||
local->scan_req->no_cck ?
|
||||
IEEE80211_TX_CTL_NO_CCK_RATE : 0,
|
||||
local->hw.conf.channel, true);
|
||||
|
||||
/*
|
||||
|
@ -546,8 +535,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
|
|||
bool associated = false;
|
||||
bool tx_empty = true;
|
||||
bool bad_latency;
|
||||
bool listen_int_exceeded;
|
||||
unsigned long min_beacon_int = 0;
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct ieee80211_channel *next_chan;
|
||||
enum mac80211_scan_state next_scan_state;
|
||||
|
@ -566,11 +553,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
|
|||
if (sdata->u.mgd.associated) {
|
||||
associated = true;
|
||||
|
||||
if (sdata->vif.bss_conf.beacon_int <
|
||||
min_beacon_int || min_beacon_int == 0)
|
||||
min_beacon_int =
|
||||
sdata->vif.bss_conf.beacon_int;
|
||||
|
||||
if (!qdisc_all_tx_empty(sdata->dev)) {
|
||||
tx_empty = false;
|
||||
break;
|
||||
|
@ -587,34 +569,19 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
|
|||
* see if we can scan another channel without interfering
|
||||
* with the current traffic situation.
|
||||
*
|
||||
* Since we don't know if the AP has pending frames for us
|
||||
* we can only check for our tx queues and use the current
|
||||
* pm_qos requirements for rx. Hence, if no tx traffic occurs
|
||||
* at all we will scan as many channels in a row as the pm_qos
|
||||
* latency allows us to. Additionally we also check for the
|
||||
* currently negotiated listen interval to prevent losing
|
||||
* frames unnecessarily.
|
||||
*
|
||||
* Otherwise switch back to the operating channel.
|
||||
* Keep good latency, do not stay off-channel more than 125 ms.
|
||||
*/
|
||||
|
||||
bad_latency = time_after(jiffies +
|
||||
ieee80211_scan_get_channel_time(next_chan),
|
||||
local->leave_oper_channel_time +
|
||||
usecs_to_jiffies(pm_qos_request(PM_QOS_NETWORK_LATENCY)));
|
||||
|
||||
listen_int_exceeded = time_after(jiffies +
|
||||
ieee80211_scan_get_channel_time(next_chan),
|
||||
local->leave_oper_channel_time +
|
||||
usecs_to_jiffies(min_beacon_int * 1024) *
|
||||
local->hw.conf.listen_interval);
|
||||
ieee80211_scan_get_channel_time(next_chan),
|
||||
local->leave_oper_channel_time + HZ / 8);
|
||||
|
||||
if (associated && !tx_empty) {
|
||||
if (local->scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
|
||||
next_scan_state = SCAN_ABORT;
|
||||
else
|
||||
next_scan_state = SCAN_SUSPEND;
|
||||
} else if (associated && (bad_latency || listen_int_exceeded)) {
|
||||
} else if (associated && bad_latency) {
|
||||
next_scan_state = SCAN_SUSPEND;
|
||||
} else {
|
||||
next_scan_state = SCAN_SET_CHANNEL;
|
||||
|
|
|
@ -120,6 +120,8 @@ static void cleanup_single_sta(struct sta_info *sta)
|
|||
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
|
||||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
|
||||
ps = &sdata->bss->ps;
|
||||
else if (ieee80211_vif_is_mesh(&sdata->vif))
|
||||
ps = &sdata->u.mesh.ps;
|
||||
else
|
||||
return;
|
||||
|
||||
|
@ -587,6 +589,12 @@ void sta_info_recalc_tim(struct sta_info *sta)
|
|||
|
||||
ps = &sta->sdata->bss->ps;
|
||||
id = sta->sta.aid;
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
} else if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
|
||||
ps = &sta->sdata->u.mesh.ps;
|
||||
/* TIM map only for PLID <= IEEE80211_MAX_AID */
|
||||
id = le16_to_cpu(sta->plid) % IEEE80211_MAX_AID;
|
||||
#endif
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
@ -745,8 +753,9 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local,
|
|||
bool have_buffered = false;
|
||||
int ac;
|
||||
|
||||
/* This is only necessary for stations on BSS interfaces */
|
||||
if (!sta->sdata->bss)
|
||||
/* This is only necessary for stations on BSS/MBSS interfaces */
|
||||
if (!sta->sdata->bss &&
|
||||
!ieee80211_vif_is_mesh(&sta->sdata->vif))
|
||||
return false;
|
||||
|
||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
|
||||
|
@ -934,6 +943,11 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
|
|||
if (time_after(jiffies, sta->last_rx + exp_time)) {
|
||||
sta_dbg(sta->sdata, "expiring inactive STA %pM\n",
|
||||
sta->sta.addr);
|
||||
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif) &&
|
||||
test_sta_flag(sta, WLAN_STA_PS_STA))
|
||||
atomic_dec(&sdata->u.mesh.ps.num_sta_ps);
|
||||
|
||||
WARN_ON(__sta_info_destroy(sta));
|
||||
}
|
||||
}
|
||||
|
@ -992,6 +1006,8 @@ static void clear_sta_ps_flags(void *_sta)
|
|||
if (sdata->vif.type == NL80211_IFTYPE_AP ||
|
||||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
|
||||
ps = &sdata->bss->ps;
|
||||
else if (ieee80211_vif_is_mesh(&sdata->vif))
|
||||
ps = &sdata->u.mesh.ps;
|
||||
else
|
||||
return;
|
||||
|
||||
|
|
|
@ -56,6 +56,8 @@
|
|||
* @WLAN_STA_INSERTED: This station is inserted into the hash table.
|
||||
* @WLAN_STA_RATE_CONTROL: rate control was initialized for this station.
|
||||
* @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid.
|
||||
* @WLAN_STA_MPSP_OWNER: local STA is owner of a mesh Peer Service Period.
|
||||
* @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP.
|
||||
*/
|
||||
enum ieee80211_sta_info_flags {
|
||||
WLAN_STA_AUTH,
|
||||
|
@ -78,6 +80,8 @@ enum ieee80211_sta_info_flags {
|
|||
WLAN_STA_INSERTED,
|
||||
WLAN_STA_RATE_CONTROL,
|
||||
WLAN_STA_TOFFSET_KNOWN,
|
||||
WLAN_STA_MPSP_OWNER,
|
||||
WLAN_STA_MPSP_RECIPIENT,
|
||||
};
|
||||
|
||||
#define ADDBA_RESP_INTERVAL HZ
|
||||
|
@ -282,6 +286,9 @@ struct sta_ampdu_mlme {
|
|||
* @t_offset_setpoint: reference timing offset of this sta to be used when
|
||||
* calculating clockdrift
|
||||
* @ch_width: peer's channel width
|
||||
* @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
|
||||
* @dead: set to true when sta is unlinked
|
||||
* @uploaded: set to true when sta is uploaded to the driver
|
||||
|
@ -379,6 +386,10 @@ struct sta_info {
|
|||
s64 t_offset;
|
||||
s64 t_offset_setpoint;
|
||||
enum nl80211_chan_width ch_width;
|
||||
/* mesh power save */
|
||||
enum nl80211_mesh_power_mode local_pm;
|
||||
enum nl80211_mesh_power_mode peer_pm;
|
||||
enum nl80211_mesh_power_mode nonpeer_pm;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
|
|
|
@ -335,7 +335,8 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
|
|||
if (dropped)
|
||||
acked = false;
|
||||
|
||||
if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
|
||||
if (info->flags & (IEEE80211_TX_INTFL_NL80211_FRAME_TX |
|
||||
IEEE80211_TX_INTFL_MLME_CONN_TX)) {
|
||||
struct ieee80211_sub_if_data *sdata = NULL;
|
||||
struct ieee80211_sub_if_data *iter_sdata;
|
||||
u64 cookie = (unsigned long)skb;
|
||||
|
@ -357,10 +358,13 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
|
|||
sdata = rcu_dereference(local->p2p_sdata);
|
||||
}
|
||||
|
||||
if (!sdata)
|
||||
if (!sdata) {
|
||||
skb->dev = NULL;
|
||||
else if (ieee80211_is_nullfunc(hdr->frame_control) ||
|
||||
ieee80211_is_qos_nullfunc(hdr->frame_control)) {
|
||||
} else if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) {
|
||||
ieee80211_mgd_conn_tx_status(sdata, hdr->frame_control,
|
||||
acked);
|
||||
} else if (ieee80211_is_nullfunc(hdr->frame_control) ||
|
||||
ieee80211_is_qos_nullfunc(hdr->frame_control)) {
|
||||
cfg80211_probe_status(sdata->dev, hdr->addr1,
|
||||
cookie, acked, GFP_ATOMIC);
|
||||
} else {
|
||||
|
@ -468,6 +472,13 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
|||
return;
|
||||
}
|
||||
|
||||
/* mesh Peer Service Period support */
|
||||
if (ieee80211_vif_is_mesh(&sta->sdata->vif) &&
|
||||
ieee80211_is_data_qos(fc))
|
||||
ieee80211_mpsp_trigger_process(
|
||||
ieee80211_get_qos_ctl(hdr),
|
||||
sta, true, acked);
|
||||
|
||||
if ((local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) &&
|
||||
(rates_idx != -1))
|
||||
sta->last_tx_rate = info->status.rates[rates_idx];
|
||||
|
@ -502,11 +513,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
|||
IEEE80211_BAR_CTRL_TID_INFO_MASK) >>
|
||||
IEEE80211_BAR_CTRL_TID_INFO_SHIFT;
|
||||
|
||||
if (local->hw.flags &
|
||||
IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL)
|
||||
ieee80211_stop_tx_ba_session(&sta->sta, tid);
|
||||
else
|
||||
ieee80211_set_bar_pending(sta, tid, ssn);
|
||||
ieee80211_set_bar_pending(sta, tid, ssn);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -329,6 +329,8 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
|
|||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP)
|
||||
ps = &sdata->u.ap.ps;
|
||||
else if (ieee80211_vif_is_mesh(&sdata->vif))
|
||||
ps = &sdata->u.mesh.ps;
|
||||
else
|
||||
continue;
|
||||
|
||||
|
@ -372,18 +374,20 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
|
|||
/*
|
||||
* broadcast/multicast frame
|
||||
*
|
||||
* If any of the associated stations is in power save mode,
|
||||
* If any of the associated/peer stations is in power save mode,
|
||||
* the frame is buffered to be sent after DTIM beacon frame.
|
||||
* This is done either by the hardware or us.
|
||||
*/
|
||||
|
||||
/* powersaving STAs currently only in AP/VLAN mode */
|
||||
/* powersaving STAs currently only in AP/VLAN/mesh mode */
|
||||
if (tx->sdata->vif.type == NL80211_IFTYPE_AP ||
|
||||
tx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
|
||||
if (!tx->sdata->bss)
|
||||
return TX_CONTINUE;
|
||||
|
||||
ps = &tx->sdata->bss->ps;
|
||||
} else if (ieee80211_vif_is_mesh(&tx->sdata->vif)) {
|
||||
ps = &tx->sdata->u.mesh.ps;
|
||||
} else {
|
||||
return TX_CONTINUE;
|
||||
}
|
||||
|
@ -594,7 +598,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
|
|||
break;
|
||||
}
|
||||
|
||||
if (unlikely(tx->key && tx->key->flags & KEY_FLAG_TAINTED))
|
||||
if (unlikely(tx->key && tx->key->flags & KEY_FLAG_TAINTED &&
|
||||
!ieee80211_is_deauth(hdr->frame_control)))
|
||||
return TX_DROP;
|
||||
|
||||
if (!skip_hw && tx->key &&
|
||||
|
@ -1472,12 +1477,14 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
|
|||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
info->control.vif = &sdata->vif;
|
||||
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif) &&
|
||||
ieee80211_is_data(hdr->frame_control) &&
|
||||
!is_multicast_ether_addr(hdr->addr1) &&
|
||||
mesh_nexthop_resolve(skb, sdata)) {
|
||||
/* skb queued: don't free */
|
||||
return;
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
if (ieee80211_is_data(hdr->frame_control) &&
|
||||
is_unicast_ether_addr(hdr->addr1)) {
|
||||
if (mesh_nexthop_resolve(skb, sdata))
|
||||
return; /* skb queued: don't free */
|
||||
} else {
|
||||
ieee80211_mps_set_frame_flags(sdata, NULL, hdr);
|
||||
}
|
||||
}
|
||||
|
||||
ieee80211_set_qos_hdr(sdata, skb);
|
||||
|
@ -2444,12 +2451,14 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
|
|||
2 + /* NULL SSID */
|
||||
2 + 8 + /* supported rates */
|
||||
2 + 3 + /* DS params */
|
||||
256 + /* TIM IE */
|
||||
2 + (IEEE80211_MAX_SUPP_RATES - 8) +
|
||||
2 + sizeof(struct ieee80211_ht_cap) +
|
||||
2 + sizeof(struct ieee80211_ht_operation) +
|
||||
2 + sdata->u.mesh.mesh_id_len +
|
||||
2 + sizeof(struct ieee80211_meshconf_ie) +
|
||||
sdata->u.mesh.ie_len);
|
||||
sdata->u.mesh.ie_len +
|
||||
2 + sizeof(__le16)); /* awake window */
|
||||
if (!skb)
|
||||
goto out;
|
||||
|
||||
|
@ -2461,6 +2470,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
|
|||
eth_broadcast_addr(mgmt->da);
|
||||
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
||||
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
|
||||
ieee80211_mps_set_frame_flags(sdata, NULL, (void *) mgmt);
|
||||
mgmt->u.beacon.beacon_int =
|
||||
cpu_to_le16(sdata->vif.bss_conf.beacon_int);
|
||||
mgmt->u.beacon.capab_info |= cpu_to_le16(
|
||||
|
@ -2474,12 +2484,14 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
|
|||
|
||||
if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
|
||||
mesh_add_ds_params_ie(skb, sdata) ||
|
||||
ieee80211_beacon_add_tim(sdata, &ifmsh->ps, skb) ||
|
||||
ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
|
||||
mesh_add_rsn_ie(skb, sdata) ||
|
||||
mesh_add_ht_cap_ie(skb, sdata) ||
|
||||
mesh_add_ht_oper_ie(skb, sdata) ||
|
||||
mesh_add_meshid_ie(skb, sdata) ||
|
||||
mesh_add_meshconf_ie(skb, sdata) ||
|
||||
mesh_add_awake_window_ie(skb, sdata) ||
|
||||
mesh_add_vendor_ies(skb, sdata)) {
|
||||
pr_err("o11s: couldn't add ies!\n");
|
||||
goto out;
|
||||
|
@ -2733,6 +2745,8 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
|
|||
goto out;
|
||||
|
||||
ps = &sdata->u.ap.ps;
|
||||
} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
ps = &sdata->u.mesh.ps;
|
||||
} else {
|
||||
goto out;
|
||||
}
|
||||
|
|
|
@ -805,6 +805,10 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
|
|||
elems->peering = pos;
|
||||
elems->peering_len = elen;
|
||||
break;
|
||||
case WLAN_EID_MESH_AWAKE_WINDOW:
|
||||
if (elen >= 2)
|
||||
elems->awake_window = (void *)pos;
|
||||
break;
|
||||
case WLAN_EID_PREQ:
|
||||
elems->preq = pos;
|
||||
elems->preq_len = elen;
|
||||
|
@ -1030,7 +1034,8 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
|
|||
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
|
||||
u16 transaction, u16 auth_alg, u16 status,
|
||||
u8 *extra, size_t extra_len, const u8 *da,
|
||||
const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx)
|
||||
const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx,
|
||||
u32 tx_flags)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sk_buff *skb;
|
||||
|
@ -1063,7 +1068,8 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
|
|||
WARN_ON(err);
|
||||
}
|
||||
|
||||
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
||||
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
|
||||
tx_flags;
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
}
|
||||
|
||||
|
@ -1277,7 +1283,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
|
|||
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len,
|
||||
u32 ratemask, bool directed, bool no_cck,
|
||||
u32 ratemask, bool directed, u32 tx_flags,
|
||||
struct ieee80211_channel *channel, bool scan)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
@ -1286,9 +1292,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
|
|||
ssid, ssid_len,
|
||||
ie, ie_len, directed);
|
||||
if (skb) {
|
||||
if (no_cck)
|
||||
IEEE80211_SKB_CB(skb)->flags |=
|
||||
IEEE80211_TX_CTL_NO_CCK_RATE;
|
||||
IEEE80211_SKB_CB(skb)->flags |= tx_flags;
|
||||
if (scan)
|
||||
ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band);
|
||||
else
|
||||
|
@ -1538,6 +1542,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
|||
changed |= BSS_CHANGED_ASSOC |
|
||||
BSS_CHANGED_ARP_FILTER |
|
||||
BSS_CHANGED_PS;
|
||||
|
||||
if (sdata->u.mgd.dtim_period)
|
||||
changed |= BSS_CHANGED_DTIM_PERIOD;
|
||||
|
||||
mutex_lock(&sdata->u.mgd.mtx);
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
mutex_unlock(&sdata->u.mgd.mtx);
|
||||
|
|
|
@ -191,6 +191,15 @@ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
/* qos header is 2 bytes */
|
||||
*p++ = ack_policy | tid;
|
||||
*p = ieee80211_vif_is_mesh(&sdata->vif) ?
|
||||
(IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8) : 0;
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
/* preserve RSPI and Mesh PS Level bit */
|
||||
*p &= ((IEEE80211_QOS_CTL_RSPI |
|
||||
IEEE80211_QOS_CTL_MESH_PS_LEVEL) >> 8);
|
||||
|
||||
/* Nulls don't have a mesh header (frame body) */
|
||||
if (!ieee80211_is_qos_nullfunc(hdr->frame_control))
|
||||
*p |= (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8);
|
||||
} else {
|
||||
*p = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -514,7 +514,7 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
|
|||
if (wdev->sme_state != CFG80211_SME_CONNECTED)
|
||||
return -ENOTCONN;
|
||||
|
||||
if (WARN_ON(!wdev->current_bss))
|
||||
if (WARN(!wdev->current_bss, "sme_state=%d\n", wdev->sme_state))
|
||||
return -ENOTCONN;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
|
|
@ -3057,12 +3057,22 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
|
|||
nla_put_u32(msg, NL80211_STA_INFO_INACTIVE_TIME,
|
||||
sinfo->inactive_time))
|
||||
goto nla_put_failure;
|
||||
if ((sinfo->filled & STATION_INFO_RX_BYTES) &&
|
||||
if ((sinfo->filled & (STATION_INFO_RX_BYTES |
|
||||
STATION_INFO_RX_BYTES64)) &&
|
||||
nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES,
|
||||
(u32)sinfo->rx_bytes))
|
||||
goto nla_put_failure;
|
||||
if ((sinfo->filled & (STATION_INFO_TX_BYTES |
|
||||
NL80211_STA_INFO_TX_BYTES64)) &&
|
||||
nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,
|
||||
(u32)sinfo->tx_bytes))
|
||||
goto nla_put_failure;
|
||||
if ((sinfo->filled & STATION_INFO_RX_BYTES64) &&
|
||||
nla_put_u64(msg, NL80211_STA_INFO_RX_BYTES64,
|
||||
sinfo->rx_bytes))
|
||||
goto nla_put_failure;
|
||||
if ((sinfo->filled & STATION_INFO_TX_BYTES) &&
|
||||
nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,
|
||||
if ((sinfo->filled & STATION_INFO_TX_BYTES64) &&
|
||||
nla_put_u64(msg, NL80211_STA_INFO_TX_BYTES64,
|
||||
sinfo->tx_bytes))
|
||||
goto nla_put_failure;
|
||||
if ((sinfo->filled & STATION_INFO_LLID) &&
|
||||
|
@ -9323,6 +9333,103 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy,
|
|||
}
|
||||
EXPORT_SYMBOL(cfg80211_report_obss_beacon);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
|
||||
struct cfg80211_wowlan_wakeup *wakeup,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
|
||||
struct sk_buff *msg;
|
||||
void *hdr;
|
||||
int err, size = 200;
|
||||
|
||||
trace_cfg80211_report_wowlan_wakeup(wdev->wiphy, wdev, wakeup);
|
||||
|
||||
if (wakeup)
|
||||
size += wakeup->packet_present_len;
|
||||
|
||||
msg = nlmsg_new(size, gfp);
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_WOWLAN);
|
||||
if (!hdr)
|
||||
goto free_msg;
|
||||
|
||||
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
||||
nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
|
||||
goto free_msg;
|
||||
|
||||
if (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
|
||||
wdev->netdev->ifindex))
|
||||
goto free_msg;
|
||||
|
||||
if (wakeup) {
|
||||
struct nlattr *reasons;
|
||||
|
||||
reasons = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
|
||||
|
||||
if (wakeup->disconnect &&
|
||||
nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT))
|
||||
goto free_msg;
|
||||
if (wakeup->magic_pkt &&
|
||||
nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT))
|
||||
goto free_msg;
|
||||
if (wakeup->gtk_rekey_failure &&
|
||||
nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE))
|
||||
goto free_msg;
|
||||
if (wakeup->eap_identity_req &&
|
||||
nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST))
|
||||
goto free_msg;
|
||||
if (wakeup->four_way_handshake &&
|
||||
nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE))
|
||||
goto free_msg;
|
||||
if (wakeup->rfkill_release &&
|
||||
nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))
|
||||
goto free_msg;
|
||||
|
||||
if (wakeup->pattern_idx >= 0 &&
|
||||
nla_put_u32(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
|
||||
wakeup->pattern_idx))
|
||||
goto free_msg;
|
||||
|
||||
if (wakeup->packet) {
|
||||
u32 pkt_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211;
|
||||
u32 len_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN;
|
||||
|
||||
if (!wakeup->packet_80211) {
|
||||
pkt_attr =
|
||||
NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023;
|
||||
len_attr =
|
||||
NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN;
|
||||
}
|
||||
|
||||
if (wakeup->packet_len &&
|
||||
nla_put_u32(msg, len_attr, wakeup->packet_len))
|
||||
goto free_msg;
|
||||
|
||||
if (nla_put(msg, pkt_attr, wakeup->packet_present_len,
|
||||
wakeup->packet))
|
||||
goto free_msg;
|
||||
}
|
||||
|
||||
nla_nest_end(msg, reasons);
|
||||
}
|
||||
|
||||
err = genlmsg_end(msg, hdr);
|
||||
if (err < 0)
|
||||
goto free_msg;
|
||||
|
||||
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
||||
nl80211_mlme_mcgrp.id, gfp);
|
||||
return;
|
||||
|
||||
free_msg:
|
||||
nlmsg_free(msg);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_report_wowlan_wakeup);
|
||||
#endif
|
||||
|
||||
void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
|
||||
enum nl80211_tdls_operation oper,
|
||||
u16 reason_code, gfp_t gfp)
|
||||
|
|
|
@ -31,9 +31,6 @@ static void bss_release(struct kref *ref)
|
|||
if (WARN_ON(atomic_read(&bss->hold)))
|
||||
return;
|
||||
|
||||
if (bss->pub.free_priv)
|
||||
bss->pub.free_priv(&bss->pub);
|
||||
|
||||
ies = (void *)rcu_access_pointer(bss->pub.beacon_ies);
|
||||
if (ies)
|
||||
kfree_rcu(ies, rcu_head);
|
||||
|
@ -44,22 +41,34 @@ static void bss_release(struct kref *ref)
|
|||
kfree(bss);
|
||||
}
|
||||
|
||||
/* must hold dev->bss_lock! */
|
||||
static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
|
||||
struct cfg80211_internal_bss *bss)
|
||||
static inline void bss_ref_get(struct cfg80211_internal_bss *bss)
|
||||
{
|
||||
kref_get(&bss->ref);
|
||||
}
|
||||
|
||||
static inline void bss_ref_put(struct cfg80211_internal_bss *bss)
|
||||
{
|
||||
list_del_init(&bss->list);
|
||||
rb_erase(&bss->rbn, &dev->bss_tree);
|
||||
kref_put(&bss->ref, bss_release);
|
||||
}
|
||||
|
||||
/* must hold dev->bss_lock! */
|
||||
static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
|
||||
struct cfg80211_internal_bss *bss)
|
||||
{
|
||||
lockdep_assert_held(&dev->bss_lock);
|
||||
|
||||
list_del_init(&bss->list);
|
||||
rb_erase(&bss->rbn, &dev->bss_tree);
|
||||
bss_ref_put(bss);
|
||||
}
|
||||
|
||||
static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev,
|
||||
unsigned long expire_time)
|
||||
{
|
||||
struct cfg80211_internal_bss *bss, *tmp;
|
||||
bool expired = false;
|
||||
|
||||
lockdep_assert_held(&dev->bss_lock);
|
||||
|
||||
list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
|
||||
if (atomic_read(&bss->hold))
|
||||
continue;
|
||||
|
@ -234,15 +243,16 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* must hold dev->bss_lock! */
|
||||
void cfg80211_bss_age(struct cfg80211_registered_device *dev,
|
||||
unsigned long age_secs)
|
||||
{
|
||||
struct cfg80211_internal_bss *bss;
|
||||
unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
|
||||
|
||||
spin_lock_bh(&dev->bss_lock);
|
||||
list_for_each_entry(bss, &dev->bss_list, list)
|
||||
bss->ts -= age_jiffies;
|
||||
spin_unlock_bh(&dev->bss_lock);
|
||||
}
|
||||
|
||||
void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
|
||||
|
@ -291,26 +301,6 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type,
|
|||
}
|
||||
EXPORT_SYMBOL(cfg80211_find_vendor_ie);
|
||||
|
||||
static int cmp_ies(u8 num, const u8 *ies1, int len1, const u8 *ies2, int len2)
|
||||
{
|
||||
const u8 *ie1 = cfg80211_find_ie(num, ies1, len1);
|
||||
const u8 *ie2 = cfg80211_find_ie(num, ies2, len2);
|
||||
|
||||
/* equal if both missing */
|
||||
if (!ie1 && !ie2)
|
||||
return 0;
|
||||
/* sort missing IE before (left of) present IE */
|
||||
if (!ie1)
|
||||
return -1;
|
||||
if (!ie2)
|
||||
return 1;
|
||||
|
||||
/* sort by length first, then by contents */
|
||||
if (ie1[1] != ie2[1])
|
||||
return ie2[1] - ie1[1];
|
||||
return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
|
||||
}
|
||||
|
||||
static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
|
||||
const u8 *ssid, size_t ssid_len)
|
||||
{
|
||||
|
@ -334,152 +324,82 @@ static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
|
|||
return memcmp(ssidie + 2, ssid, ssid_len) == 0;
|
||||
}
|
||||
|
||||
static bool is_mesh_bss(struct cfg80211_bss *a)
|
||||
{
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
const u8 *ie;
|
||||
/**
|
||||
* enum bss_compare_mode - BSS compare mode
|
||||
* @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find)
|
||||
* @BSS_CMP_HIDE_ZLEN: find hidden SSID with zero-length mode
|
||||
* @BSS_CMP_HIDE_NUL: find hidden SSID with NUL-ed out mode
|
||||
*/
|
||||
enum bss_compare_mode {
|
||||
BSS_CMP_REGULAR,
|
||||
BSS_CMP_HIDE_ZLEN,
|
||||
BSS_CMP_HIDE_NUL,
|
||||
};
|
||||
|
||||
if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
|
||||
return false;
|
||||
|
||||
ies = rcu_access_pointer(a->ies);
|
||||
if (!ies)
|
||||
return false;
|
||||
|
||||
ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len);
|
||||
if (!ie)
|
||||
return false;
|
||||
|
||||
ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len);
|
||||
if (!ie)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_mesh(struct cfg80211_bss *a,
|
||||
const u8 *meshid, size_t meshidlen,
|
||||
const u8 *meshcfg)
|
||||
{
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
const u8 *ie;
|
||||
|
||||
if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
|
||||
return false;
|
||||
|
||||
ies = rcu_access_pointer(a->ies);
|
||||
if (!ies)
|
||||
return false;
|
||||
|
||||
ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len);
|
||||
if (!ie)
|
||||
return false;
|
||||
if (ie[1] != meshidlen)
|
||||
return false;
|
||||
if (memcmp(ie + 2, meshid, meshidlen))
|
||||
return false;
|
||||
|
||||
ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len);
|
||||
if (!ie)
|
||||
return false;
|
||||
if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Ignore mesh capability (last two bytes of the IE) when
|
||||
* comparing since that may differ between stations taking
|
||||
* part in the same mesh.
|
||||
*/
|
||||
return memcmp(ie + 2, meshcfg,
|
||||
sizeof(struct ieee80211_meshconf_ie) - 2) == 0;
|
||||
}
|
||||
|
||||
static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b)
|
||||
static int cmp_bss(struct cfg80211_bss *a,
|
||||
struct cfg80211_bss *b,
|
||||
enum bss_compare_mode mode)
|
||||
{
|
||||
const struct cfg80211_bss_ies *a_ies, *b_ies;
|
||||
int r;
|
||||
const u8 *ie1 = NULL;
|
||||
const u8 *ie2 = NULL;
|
||||
int i, r;
|
||||
|
||||
if (a->channel != b->channel)
|
||||
return b->channel->center_freq - a->channel->center_freq;
|
||||
|
||||
if (is_mesh_bss(a) && is_mesh_bss(b)) {
|
||||
a_ies = rcu_access_pointer(a->ies);
|
||||
if (!a_ies)
|
||||
return -1;
|
||||
b_ies = rcu_access_pointer(b->ies);
|
||||
if (!b_ies)
|
||||
return 1;
|
||||
a_ies = rcu_access_pointer(a->ies);
|
||||
if (!a_ies)
|
||||
return -1;
|
||||
b_ies = rcu_access_pointer(b->ies);
|
||||
if (!b_ies)
|
||||
return 1;
|
||||
|
||||
r = cmp_ies(WLAN_EID_MESH_ID,
|
||||
a_ies->data, a_ies->len,
|
||||
b_ies->data, b_ies->len);
|
||||
if (r)
|
||||
return r;
|
||||
return cmp_ies(WLAN_EID_MESH_CONFIG,
|
||||
a_ies->data, a_ies->len,
|
||||
b_ies->data, b_ies->len);
|
||||
if (WLAN_CAPABILITY_IS_STA_BSS(a->capability))
|
||||
ie1 = cfg80211_find_ie(WLAN_EID_MESH_ID,
|
||||
a_ies->data, a_ies->len);
|
||||
if (WLAN_CAPABILITY_IS_STA_BSS(b->capability))
|
||||
ie2 = cfg80211_find_ie(WLAN_EID_MESH_ID,
|
||||
b_ies->data, b_ies->len);
|
||||
if (ie1 && ie2) {
|
||||
int mesh_id_cmp;
|
||||
|
||||
if (ie1[1] == ie2[1])
|
||||
mesh_id_cmp = memcmp(ie1 + 2, ie2 + 2, ie1[1]);
|
||||
else
|
||||
mesh_id_cmp = ie2[1] - ie1[1];
|
||||
|
||||
ie1 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
|
||||
a_ies->data, a_ies->len);
|
||||
ie2 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
|
||||
b_ies->data, b_ies->len);
|
||||
if (ie1 && ie2) {
|
||||
if (mesh_id_cmp)
|
||||
return mesh_id_cmp;
|
||||
if (ie1[1] != ie2[1])
|
||||
return ie2[1] - ie1[1];
|
||||
return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* we can't use compare_ether_addr here since we need a < > operator.
|
||||
* The binary return value of compare_ether_addr isn't enough
|
||||
*/
|
||||
return memcmp(a->bssid, b->bssid, sizeof(a->bssid));
|
||||
}
|
||||
|
||||
static int cmp_bss(struct cfg80211_bss *a,
|
||||
struct cfg80211_bss *b)
|
||||
{
|
||||
const struct cfg80211_bss_ies *a_ies, *b_ies;
|
||||
int r;
|
||||
|
||||
r = cmp_bss_core(a, b);
|
||||
r = memcmp(a->bssid, b->bssid, sizeof(a->bssid));
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
a_ies = rcu_access_pointer(a->ies);
|
||||
if (!a_ies)
|
||||
return -1;
|
||||
b_ies = rcu_access_pointer(b->ies);
|
||||
if (!b_ies)
|
||||
return 1;
|
||||
|
||||
return cmp_ies(WLAN_EID_SSID,
|
||||
a_ies->data, a_ies->len,
|
||||
b_ies->data, b_ies->len);
|
||||
}
|
||||
|
||||
static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b)
|
||||
{
|
||||
const struct cfg80211_bss_ies *a_ies, *b_ies;
|
||||
const u8 *ie1;
|
||||
const u8 *ie2;
|
||||
int i;
|
||||
int r;
|
||||
|
||||
r = cmp_bss_core(a, b);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
a_ies = rcu_access_pointer(a->ies);
|
||||
if (!a_ies)
|
||||
return -1;
|
||||
b_ies = rcu_access_pointer(b->ies);
|
||||
if (!b_ies)
|
||||
return 1;
|
||||
|
||||
ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len);
|
||||
ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len);
|
||||
|
||||
if (!ie1 && !ie2)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Key comparator must use same algorithm in any rb-tree
|
||||
* search function (order is important), otherwise ordering
|
||||
* of items in the tree is broken and search gives incorrect
|
||||
* results. This code uses same order as cmp_ies() does.
|
||||
*
|
||||
* Note that due to the differring behaviour with hidden SSIDs
|
||||
* this function only works when "b" is the tree element and
|
||||
* "a" is the key we're looking for.
|
||||
* Note that with "hide_ssid", the function returns a match if
|
||||
* the already-present BSS ("b") is a hidden SSID beacon for
|
||||
* the new BSS ("a").
|
||||
*/
|
||||
|
||||
/* sort missing IE before (left of) present IE */
|
||||
|
@ -488,24 +408,36 @@ static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b)
|
|||
if (!ie2)
|
||||
return 1;
|
||||
|
||||
/* zero-size SSID is used as an indication of the hidden bss */
|
||||
if (!ie2[1])
|
||||
switch (mode) {
|
||||
case BSS_CMP_HIDE_ZLEN:
|
||||
/*
|
||||
* In ZLEN mode we assume the BSS entry we're
|
||||
* looking for has a zero-length SSID. So if
|
||||
* the one we're looking at right now has that,
|
||||
* return 0. Otherwise, return the difference
|
||||
* in length, but since we're looking for the
|
||||
* 0-length it's really equivalent to returning
|
||||
* the length of the one we're looking at.
|
||||
*
|
||||
* No content comparison is needed as we assume
|
||||
* the content length is zero.
|
||||
*/
|
||||
return ie2[1];
|
||||
case BSS_CMP_REGULAR:
|
||||
default:
|
||||
/* sort by length first, then by contents */
|
||||
if (ie1[1] != ie2[1])
|
||||
return ie2[1] - ie1[1];
|
||||
return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
|
||||
case BSS_CMP_HIDE_NUL:
|
||||
if (ie1[1] != ie2[1])
|
||||
return ie2[1] - ie1[1];
|
||||
/* this is equivalent to memcmp(zeroes, ie2 + 2, len) */
|
||||
for (i = 0; i < ie2[1]; i++)
|
||||
if (ie2[i + 2])
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
/* sort by length first, then by contents */
|
||||
if (ie1[1] != ie2[1])
|
||||
return ie2[1] - ie1[1];
|
||||
|
||||
/*
|
||||
* zeroed SSID ie is another indication of a hidden bss;
|
||||
* if it isn't zeroed just return the regular sort value
|
||||
* to find the next candidate
|
||||
*/
|
||||
for (i = 0; i < ie2[1]; i++)
|
||||
if (ie2[i + 2])
|
||||
return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
|
||||
|
@ -534,7 +466,7 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
|
|||
continue;
|
||||
if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
|
||||
res = bss;
|
||||
kref_get(&res->ref);
|
||||
bss_ref_get(res);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -547,34 +479,6 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
|
|||
}
|
||||
EXPORT_SYMBOL(cfg80211_get_bss);
|
||||
|
||||
struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy,
|
||||
struct ieee80211_channel *channel,
|
||||
const u8 *meshid, size_t meshidlen,
|
||||
const u8 *meshcfg)
|
||||
{
|
||||
struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
|
||||
struct cfg80211_internal_bss *bss, *res = NULL;
|
||||
|
||||
spin_lock_bh(&dev->bss_lock);
|
||||
|
||||
list_for_each_entry(bss, &dev->bss_list, list) {
|
||||
if (channel && bss->pub.channel != channel)
|
||||
continue;
|
||||
if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) {
|
||||
res = bss;
|
||||
kref_get(&res->ref);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_bh(&dev->bss_lock);
|
||||
if (!res)
|
||||
return NULL;
|
||||
return &res->pub;
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_get_mesh);
|
||||
|
||||
|
||||
static void rb_insert_bss(struct cfg80211_registered_device *dev,
|
||||
struct cfg80211_internal_bss *bss)
|
||||
{
|
||||
|
@ -587,7 +491,7 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev,
|
|||
parent = *p;
|
||||
tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn);
|
||||
|
||||
cmp = cmp_bss(&bss->pub, &tbss->pub);
|
||||
cmp = cmp_bss(&bss->pub, &tbss->pub, BSS_CMP_REGULAR);
|
||||
|
||||
if (WARN_ON(!cmp)) {
|
||||
/* will sort of leak this BSS */
|
||||
|
@ -606,7 +510,8 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev,
|
|||
|
||||
static struct cfg80211_internal_bss *
|
||||
rb_find_bss(struct cfg80211_registered_device *dev,
|
||||
struct cfg80211_internal_bss *res)
|
||||
struct cfg80211_internal_bss *res,
|
||||
enum bss_compare_mode mode)
|
||||
{
|
||||
struct rb_node *n = dev->bss_tree.rb_node;
|
||||
struct cfg80211_internal_bss *bss;
|
||||
|
@ -614,30 +519,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,
|
|||
|
||||
while (n) {
|
||||
bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
|
||||
r = cmp_bss(&res->pub, &bss->pub);
|
||||
|
||||
if (r == 0)
|
||||
return bss;
|
||||
else if (r < 0)
|
||||
n = n->rb_left;
|
||||
else
|
||||
n = n->rb_right;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct cfg80211_internal_bss *
|
||||
rb_find_hidden_bss(struct cfg80211_registered_device *dev,
|
||||
struct cfg80211_internal_bss *res)
|
||||
{
|
||||
struct rb_node *n = dev->bss_tree.rb_node;
|
||||
struct cfg80211_internal_bss *bss;
|
||||
int r;
|
||||
|
||||
while (n) {
|
||||
bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
|
||||
r = cmp_hidden_bss(&res->pub, &bss->pub);
|
||||
r = cmp_bss(&res->pub, &bss->pub, mode);
|
||||
|
||||
if (r == 0)
|
||||
return bss;
|
||||
|
@ -687,7 +569,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
found = rb_find_bss(dev, tmp);
|
||||
found = rb_find_bss(dev, tmp, BSS_CMP_REGULAR);
|
||||
|
||||
if (found) {
|
||||
found->pub.beacon_interval = tmp->pub.beacon_interval;
|
||||
|
@ -711,16 +593,15 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
|
|||
kfree_rcu((struct cfg80211_bss_ies *)old,
|
||||
rcu_head);
|
||||
} else if (rcu_access_pointer(tmp->pub.beacon_ies)) {
|
||||
const struct cfg80211_bss_ies *old, *ies;
|
||||
const struct cfg80211_bss_ies *old;
|
||||
|
||||
old = rcu_access_pointer(found->pub.beacon_ies);
|
||||
ies = rcu_access_pointer(found->pub.ies);
|
||||
|
||||
rcu_assign_pointer(found->pub.beacon_ies,
|
||||
tmp->pub.beacon_ies);
|
||||
|
||||
/* Override IEs if they were from a beacon before */
|
||||
if (old == ies)
|
||||
if (old == rcu_access_pointer(found->pub.ies))
|
||||
rcu_assign_pointer(found->pub.ies,
|
||||
tmp->pub.beacon_ies);
|
||||
|
||||
|
@ -742,9 +623,14 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
|
|||
/* TODO: The code is not trying to update existing probe
|
||||
* response bss entries when beacon ies are
|
||||
* getting changed. */
|
||||
hidden = rb_find_hidden_bss(dev, tmp);
|
||||
if (hidden)
|
||||
hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN);
|
||||
if (hidden) {
|
||||
copy_hidden_ies(tmp, hidden);
|
||||
} else {
|
||||
hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_NUL);
|
||||
if (hidden)
|
||||
copy_hidden_ies(tmp, hidden);
|
||||
}
|
||||
|
||||
/*
|
||||
* create a copy -- the "res" variable that is passed in
|
||||
|
@ -773,7 +659,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
|
|||
dev->bss_generation++;
|
||||
spin_unlock_bh(&dev->bss_lock);
|
||||
|
||||
kref_get(&found->ref);
|
||||
bss_ref_get(found);
|
||||
return found;
|
||||
}
|
||||
|
||||
|
@ -841,11 +727,8 @@ cfg80211_inform_bss(struct wiphy *wiphy,
|
|||
* Response frame, we need to pick one of the options and only use it
|
||||
* with the driver that does not provide the full Beacon/Probe Response
|
||||
* frame. Use Beacon frame pointer to avoid indicating that this should
|
||||
* override the iies pointer should we have received an earlier
|
||||
* override the IEs pointer should we have received an earlier
|
||||
* indication of Probe Response data.
|
||||
*
|
||||
* The initial buffer for the IEs is allocated with the BSS entry and
|
||||
* is located after the private area.
|
||||
*/
|
||||
ies = kmalloc(sizeof(*ies) + ielen, gfp);
|
||||
if (!ies)
|
||||
|
@ -943,7 +826,7 @@ void cfg80211_ref_bss(struct cfg80211_bss *pub)
|
|||
return;
|
||||
|
||||
bss = container_of(pub, struct cfg80211_internal_bss, pub);
|
||||
kref_get(&bss->ref);
|
||||
bss_ref_get(bss);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_ref_bss);
|
||||
|
||||
|
@ -955,7 +838,7 @@ void cfg80211_put_bss(struct cfg80211_bss *pub)
|
|||
return;
|
||||
|
||||
bss = container_of(pub, struct cfg80211_internal_bss, pub);
|
||||
kref_put(&bss->ref, bss_release);
|
||||
bss_ref_put(bss);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_put_bss);
|
||||
|
||||
|
|
|
@ -108,9 +108,7 @@ static int wiphy_resume(struct device *dev)
|
|||
int ret = 0;
|
||||
|
||||
/* Age scan results with time spent in suspend */
|
||||
spin_lock_bh(&rdev->bss_lock);
|
||||
cfg80211_bss_age(rdev, get_seconds() - rdev->suspend_at);
|
||||
spin_unlock_bh(&rdev->bss_lock);
|
||||
|
||||
if (rdev->ops->resume) {
|
||||
rtnl_lock();
|
||||
|
|
|
@ -2333,6 +2333,41 @@ TRACE_EVENT(cfg80211_return_u32,
|
|||
TP_printk("ret: %u", __entry->ret)
|
||||
);
|
||||
|
||||
TRACE_EVENT(cfg80211_report_wowlan_wakeup,
|
||||
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
struct cfg80211_wowlan_wakeup *wakeup),
|
||||
TP_ARGS(wiphy, wdev, wakeup),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
WDEV_ENTRY
|
||||
__field(bool, disconnect)
|
||||
__field(bool, magic_pkt)
|
||||
__field(bool, gtk_rekey_failure)
|
||||
__field(bool, eap_identity_req)
|
||||
__field(bool, four_way_handshake)
|
||||
__field(bool, rfkill_release)
|
||||
__field(s32, pattern_idx)
|
||||
__field(u32, packet_len)
|
||||
__dynamic_array(u8, packet, wakeup->packet_present_len)
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
WDEV_ASSIGN;
|
||||
__entry->disconnect = wakeup->disconnect;
|
||||
__entry->magic_pkt = wakeup->magic_pkt;
|
||||
__entry->gtk_rekey_failure = wakeup->gtk_rekey_failure;
|
||||
__entry->eap_identity_req = wakeup->eap_identity_req;
|
||||
__entry->four_way_handshake = wakeup->four_way_handshake;
|
||||
__entry->rfkill_release = wakeup->rfkill_release;
|
||||
__entry->pattern_idx = wakeup->pattern_idx;
|
||||
__entry->packet_len = wakeup->packet_len;
|
||||
if (wakeup->packet && wakeup->packet_present_len)
|
||||
memcpy(__get_dynamic_array(packet), wakeup->packet,
|
||||
wakeup->packet_present_len);
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
|
||||
);
|
||||
|
||||
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
|
|
Loading…
Reference in New Issue