diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index ef82751722e0..f293b3ff4756 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -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) { diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 9741ac10a334..835662a449da 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -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) diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index c2f03ecd4bf8..75f6f6cfdd47 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -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 | diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index a6b05a02cfd4..ce6127caabdf 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -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; diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 11c8bc87fdcb..7e8a498efe6d 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -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, }; /** diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 36e076e374d2..3ec70e1681d3 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -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 */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 5c98d654fc75..7da11211825d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -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 */ diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 5b7dbc1ea966..9a2ecdc4136c 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.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, diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index b4ecf267a34b..0ecf947ad378 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -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 diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 4911202334d9..9d7d840aac6d 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -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 diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 661b878bd19c..f4f7e7691077 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -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; } diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h index 8f383a576016..4ccc5ed6237d 100644 --- a/net/mac80211/debug.h +++ b/net/mac80211/debug.h @@ -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__) diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 466f4b45dd94..8e6040998ba6 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -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) diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index cbde5cc49a40..059bbb82e84f 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -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 diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 6fb1168b9f16..c7591f73dbc3 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -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); } diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index b4b866f41919..a54c8248e0e0 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -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, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 5fba867d9e2e..5fe9db707880 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -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, diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 694e27376afa..35ac38871420 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -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); diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index aff301544c7f..eb336253b6b3 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -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); diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 6b4603a90031..f0dd8742ed42 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -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; } diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index aa749818860e..d5786c3eaee2 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -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); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 81e612682bc3..fe7c3334d6fe 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -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; diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c new file mode 100644 index 000000000000..b677962525ed --- /dev/null +++ b/net/mac80211/mesh_ps.c @@ -0,0 +1,585 @@ +/* + * Copyright 2012-2013, Marco Porsch + * 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); +} diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 5913fb924b12..353b690900e9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -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) { diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index e45b83610e85..53801d20176d 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -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); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index a19089565c4b..c98be0593756 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -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 */ diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 607684c47d55..7f80f0a5026e 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -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; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 227233c3ff7f..47a0f0601768 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -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; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index af7d78aa5523..5a1deba2c645 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -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 diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 07d99578a2b1..43439203f4e4 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -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); } } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index a2cb6a302cc7..2ef0e19b06bb 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -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; } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 7519018ff71a..6cb71a350edd 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -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); diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 906f00cd6d2f..afba19cb6f87 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -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; + } } diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 461e692cdfec..fee9bf70efcf 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -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)); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b5978ab4ad7a..807d448e702e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -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) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 01592d7d4789..36daacb31788 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -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); diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 9bf6d5e32166..73bf39f11314 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -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(); diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 8bc553199686..c9cafb0ea95f 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -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