Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next

Fixed-up drivers/net/wireless/iwlwifi/mvm/mac80211.c to change change
IEEE80211_HW_NEED_DTIM_PERIOD to IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC
as requested by Johannes Berg. -- JWL

Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
John W. Linville 2013-02-08 14:23:51 -05:00
commit 3549c6b195
38 changed files with 1442 additions and 455 deletions

View file

@ -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) {

View file

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

View file

@ -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 |

View file

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

View file

@ -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,
};
/**

View file

@ -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 */

View file

@ -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 */

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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__)

View file

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

View file

@ -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

View file

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

View file

@ -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,

View file

@ -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,

View file

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

View file

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

View file

@ -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;
}

View file

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

View file

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

585
net/mac80211/mesh_ps.c Normal file
View file

@ -0,0 +1,585 @@
/*
* Copyright 2012-2013, Marco Porsch <marco.porsch@s2005.tu-chemnitz.de>
* Copyright 2012-2013, cozybit Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "mesh.h"
#include "wme.h"
/* mesh PS management */
/**
* mps_qos_null_get - create pre-addressed QoS Null frame for mesh powersave
*/
static struct sk_buff *mps_qos_null_get(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
struct ieee80211_hdr *nullfunc; /* use 4addr header */
struct sk_buff *skb;
int size = sizeof(*nullfunc);
__le16 fc;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + size + 2);
if (!skb)
return NULL;
skb_reserve(skb, local->hw.extra_tx_headroom);
nullfunc = (struct ieee80211_hdr *) skb_put(skb, size);
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC);
ieee80211_fill_mesh_addresses(nullfunc, &fc, sta->sta.addr,
sdata->vif.addr);
nullfunc->frame_control = fc;
nullfunc->duration_id = 0;
/* no address resolution for this frame -> set addr 1 immediately */
memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);
memset(skb_put(skb, 2), 0, 2); /* append QoS control field */
ieee80211_mps_set_frame_flags(sdata, sta, nullfunc);
return skb;
}
/**
* mps_qos_null_tx - send a QoS Null to indicate link-specific power mode
*/
static void mps_qos_null_tx(struct sta_info *sta)
{
struct sk_buff *skb;
skb = mps_qos_null_get(sta);
if (!skb)
return;
mps_dbg(sta->sdata, "announcing peer-specific power mode to %pM\n",
sta->sta.addr);
/* don't unintentionally start a MPSP */
if (!test_sta_flag(sta, WLAN_STA_PS_STA)) {
u8 *qc = ieee80211_get_qos_ctl((void *) skb->data);
qc[0] |= IEEE80211_QOS_CTL_EOSP;
}
ieee80211_tx_skb(sta->sdata, skb);
}
/**
* ieee80211_mps_local_status_update - track status of local link-specific PMs
*
* @sdata: local mesh subif
*
* sets the non-peer power mode and triggers the driver PS (re-)configuration
*/
void ieee80211_mps_local_status_update(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct sta_info *sta;
bool peering = false;
int light_sleep_cnt = 0;
int deep_sleep_cnt = 0;
rcu_read_lock();
list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
if (sdata != sta->sdata)
continue;
switch (sta->plink_state) {
case NL80211_PLINK_OPN_SNT:
case NL80211_PLINK_OPN_RCVD:
case NL80211_PLINK_CNF_RCVD:
peering = true;
break;
case NL80211_PLINK_ESTAB:
if (sta->local_pm == NL80211_MESH_POWER_LIGHT_SLEEP)
light_sleep_cnt++;
else if (sta->local_pm == NL80211_MESH_POWER_DEEP_SLEEP)
deep_sleep_cnt++;
break;
default:
break;
}
}
rcu_read_unlock();
/*
* Set non-peer mode to active during peering/scanning/authentication
* (see IEEE802.11-2012 13.14.8.3). The non-peer mesh power mode is
* deep sleep if the local STA is in light or deep sleep towards at
* least one mesh peer (see 13.14.3.1). Otherwise, set it to the
* user-configured default value.
*/
if (peering) {
mps_dbg(sdata, "setting non-peer PM to active for peering\n");
ifmsh->nonpeer_pm = NL80211_MESH_POWER_ACTIVE;
} else if (light_sleep_cnt || deep_sleep_cnt) {
mps_dbg(sdata, "setting non-peer PM to deep sleep\n");
ifmsh->nonpeer_pm = NL80211_MESH_POWER_DEEP_SLEEP;
} else {
mps_dbg(sdata, "setting non-peer PM to user value\n");
ifmsh->nonpeer_pm = ifmsh->mshcfg.power_mode;
}
ifmsh->ps_peers_light_sleep = light_sleep_cnt;
ifmsh->ps_peers_deep_sleep = deep_sleep_cnt;
}
/**
* ieee80211_mps_set_sta_local_pm - set local PM towards a mesh STA
*
* @sta: mesh STA
* @pm: the power mode to set
*/
void ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
enum nl80211_mesh_power_mode pm)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
mps_dbg(sdata, "local STA operates in mode %d with %pM\n",
pm, sta->sta.addr);
sta->local_pm = pm;
/*
* announce peer-specific power mode transition
* (see IEEE802.11-2012 13.14.3.2 and 13.14.3.3)
*/
if (sta->plink_state == NL80211_PLINK_ESTAB)
mps_qos_null_tx(sta);
ieee80211_mps_local_status_update(sdata);
}
/**
* ieee80211_mps_set_frame_flags - set mesh PS flags in FC (and QoS Control)
*
* @sdata: local mesh subif
* @sta: mesh STA
* @hdr: 802.11 frame header
*
* see IEEE802.11-2012 8.2.4.1.7 and 8.2.4.5.11
*
* NOTE: sta must be given when an individually-addressed QoS frame header
* is handled, for group-addressed and management frames it is not used
*/
void ieee80211_mps_set_frame_flags(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee80211_hdr *hdr)
{
enum nl80211_mesh_power_mode pm;
u8 *qc;
if (WARN_ON(is_unicast_ether_addr(hdr->addr1) &&
ieee80211_is_data_qos(hdr->frame_control) &&
!sta))
return;
if (is_unicast_ether_addr(hdr->addr1) &&
ieee80211_is_data_qos(hdr->frame_control) &&
sta->plink_state == NL80211_PLINK_ESTAB)
pm = sta->local_pm;
else
pm = sdata->u.mesh.nonpeer_pm;
if (pm == NL80211_MESH_POWER_ACTIVE)
hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_PM);
else
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
if (!ieee80211_is_data_qos(hdr->frame_control))
return;
qc = ieee80211_get_qos_ctl(hdr);
if ((is_unicast_ether_addr(hdr->addr1) &&
pm == NL80211_MESH_POWER_DEEP_SLEEP) ||
(is_multicast_ether_addr(hdr->addr1) &&
sdata->u.mesh.ps_peers_deep_sleep > 0))
qc[1] |= (IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8);
else
qc[1] &= ~(IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8);
}
/**
* ieee80211_mps_sta_status_update - update buffering status of neighbor STA
*
* @sta: mesh STA
*
* called after change of peering status or non-peer/peer-specific power mode
*/
void ieee80211_mps_sta_status_update(struct sta_info *sta)
{
enum nl80211_mesh_power_mode pm;
bool do_buffer;
/*
* use peer-specific power mode if peering is established and the
* peer's power mode is known
*/
if (sta->plink_state == NL80211_PLINK_ESTAB &&
sta->peer_pm != NL80211_MESH_POWER_UNKNOWN)
pm = sta->peer_pm;
else
pm = sta->nonpeer_pm;
do_buffer = (pm != NL80211_MESH_POWER_ACTIVE);
/* Don't let the same PS state be set twice */
if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer)
return;
if (do_buffer) {
set_sta_flag(sta, WLAN_STA_PS_STA);
atomic_inc(&sta->sdata->u.mesh.ps.num_sta_ps);
mps_dbg(sta->sdata, "start PS buffering frames towards %pM\n",
sta->sta.addr);
} else {
ieee80211_sta_ps_deliver_wakeup(sta);
}
/* clear the MPSP flags for non-peers or active STA */
if (sta->plink_state != NL80211_PLINK_ESTAB) {
clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
} else if (!do_buffer) {
clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
}
}
static void mps_set_sta_peer_pm(struct sta_info *sta,
struct ieee80211_hdr *hdr)
{
enum nl80211_mesh_power_mode pm;
u8 *qc = ieee80211_get_qos_ctl(hdr);
/*
* Test Power Management field of frame control (PW) and
* mesh power save level subfield of QoS control field (PSL)
*
* | PM | PSL| Mesh PM |
* +----+----+---------+
* | 0 |Rsrv| Active |
* | 1 | 0 | Light |
* | 1 | 1 | Deep |
*/
if (ieee80211_has_pm(hdr->frame_control)) {
if (qc[1] & (IEEE80211_QOS_CTL_MESH_PS_LEVEL >> 8))
pm = NL80211_MESH_POWER_DEEP_SLEEP;
else
pm = NL80211_MESH_POWER_LIGHT_SLEEP;
} else {
pm = NL80211_MESH_POWER_ACTIVE;
}
if (sta->peer_pm == pm)
return;
mps_dbg(sta->sdata, "STA %pM enters mode %d\n",
sta->sta.addr, pm);
sta->peer_pm = pm;
ieee80211_mps_sta_status_update(sta);
}
static void mps_set_sta_nonpeer_pm(struct sta_info *sta,
struct ieee80211_hdr *hdr)
{
enum nl80211_mesh_power_mode pm;
if (ieee80211_has_pm(hdr->frame_control))
pm = NL80211_MESH_POWER_DEEP_SLEEP;
else
pm = NL80211_MESH_POWER_ACTIVE;
if (sta->nonpeer_pm == pm)
return;
mps_dbg(sta->sdata, "STA %pM sets non-peer mode to %d\n",
sta->sta.addr, pm);
sta->nonpeer_pm = pm;
ieee80211_mps_sta_status_update(sta);
}
/**
* ieee80211_mps_rx_h_sta_process - frame receive handler for mesh powersave
*
* @sta: STA info that transmitted the frame
* @hdr: IEEE 802.11 (QoS) Header
*/
void ieee80211_mps_rx_h_sta_process(struct sta_info *sta,
struct ieee80211_hdr *hdr)
{
if (is_unicast_ether_addr(hdr->addr1) &&
ieee80211_is_data_qos(hdr->frame_control)) {
/*
* individually addressed QoS Data/Null frames contain
* peer link-specific PS mode towards the local STA
*/
mps_set_sta_peer_pm(sta, hdr);
/* check for mesh Peer Service Period trigger frames */
ieee80211_mpsp_trigger_process(ieee80211_get_qos_ctl(hdr),
sta, false, false);
} else {
/*
* can only determine non-peer PS mode
* (see IEEE802.11-2012 8.2.4.1.7)
*/
mps_set_sta_nonpeer_pm(sta, hdr);
}
}
/* mesh PS frame release */
static void mpsp_trigger_send(struct sta_info *sta, bool rspi, bool eosp)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct sk_buff *skb;
struct ieee80211_hdr *nullfunc;
struct ieee80211_tx_info *info;
u8 *qc;
skb = mps_qos_null_get(sta);
if (!skb)
return;
nullfunc = (struct ieee80211_hdr *) skb->data;
if (!eosp)
nullfunc->frame_control |=
cpu_to_le16(IEEE80211_FCTL_MOREDATA);
/*
* | RSPI | EOSP | MPSP triggering |
* +------+------+--------------------+
* | 0 | 0 | local STA is owner |
* | 0 | 1 | no MPSP (MPSP end) |
* | 1 | 0 | both STA are owner |
* | 1 | 1 | peer STA is owner | see IEEE802.11-2012 13.14.9.2
*/
qc = ieee80211_get_qos_ctl(nullfunc);
if (rspi)
qc[1] |= (IEEE80211_QOS_CTL_RSPI >> 8);
if (eosp)
qc[0] |= IEEE80211_QOS_CTL_EOSP;
info = IEEE80211_SKB_CB(skb);
info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER |
IEEE80211_TX_CTL_REQ_TX_STATUS;
mps_dbg(sdata, "sending MPSP trigger%s%s to %pM\n",
rspi ? " RSPI" : "", eosp ? " EOSP" : "", sta->sta.addr);
ieee80211_tx_skb(sdata, skb);
}
/**
* mpsp_qos_null_append - append QoS Null frame to MPSP skb queue if needed
*
* To properly end a mesh MPSP the last transmitted frame has to set the EOSP
* flag in the QoS Control field. In case the current tailing frame is not a
* QoS Data frame, append a QoS Null to carry the flag.
*/
static void mpsp_qos_null_append(struct sta_info *sta,
struct sk_buff_head *frames)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct sk_buff *new_skb, *skb = skb_peek_tail(frames);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *info;
if (ieee80211_is_data_qos(hdr->frame_control))
return;
new_skb = mps_qos_null_get(sta);
if (!new_skb)
return;
mps_dbg(sdata, "appending QoS Null in MPSP towards %pM\n",
sta->sta.addr);
/*
* This frame has to be transmitted last. Assign lowest priority to
* make sure it cannot pass other frames when releasing multiple ACs.
*/
new_skb->priority = 1;
skb_set_queue_mapping(new_skb, IEEE80211_AC_BK);
ieee80211_set_qos_hdr(sdata, new_skb);
info = IEEE80211_SKB_CB(new_skb);
info->control.vif = &sdata->vif;
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
__skb_queue_tail(frames, new_skb);
}
/**
* mps_frame_deliver - transmit frames during mesh powersave
*
* @sta: STA info to transmit to
* @n_frames: number of frames to transmit. -1 for all
*/
static void mps_frame_deliver(struct sta_info *sta, int n_frames)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
int ac;
struct sk_buff_head frames;
struct sk_buff *skb;
bool more_data = false;
skb_queue_head_init(&frames);
/* collect frame(s) from buffers */
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
while (n_frames != 0) {
skb = skb_dequeue(&sta->tx_filtered[ac]);
if (!skb) {
skb = skb_dequeue(
&sta->ps_tx_buf[ac]);
if (skb)
local->total_ps_buffered--;
}
if (!skb)
break;
n_frames--;
__skb_queue_tail(&frames, skb);
}
if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
!skb_queue_empty(&sta->ps_tx_buf[ac]))
more_data = true;
}
/* nothing to send? -> EOSP */
if (skb_queue_empty(&frames)) {
mpsp_trigger_send(sta, false, true);
return;
}
/* in a MPSP make sure the last skb is a QoS Data frame */
if (test_sta_flag(sta, WLAN_STA_MPSP_OWNER))
mpsp_qos_null_append(sta, &frames);
mps_dbg(sta->sdata, "sending %d frames to PS STA %pM\n",
skb_queue_len(&frames), sta->sta.addr);
/* prepare collected frames for transmission */
skb_queue_walk(&frames, skb) {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (void *) skb->data;
/*
* Tell TX path to send this frame even though the
* STA may still remain is PS mode after this frame
* exchange.
*/
info->flags |= IEEE80211_TX_CTL_NO_PS_BUFFER;
if (more_data || !skb_queue_is_last(&frames, skb))
hdr->frame_control |=
cpu_to_le16(IEEE80211_FCTL_MOREDATA);
else
hdr->frame_control &=
cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
if (skb_queue_is_last(&frames, skb) &&
ieee80211_is_data_qos(hdr->frame_control)) {
u8 *qoshdr = ieee80211_get_qos_ctl(hdr);
/* MPSP trigger frame ends service period */
*qoshdr |= IEEE80211_QOS_CTL_EOSP;
info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
}
}
ieee80211_add_pending_skbs(local, &frames);
sta_info_recalc_tim(sta);
}
/**
* ieee80211_mpsp_trigger_process - track status of mesh Peer Service Periods
*
* @qc: QoS Control field
* @sta: peer to start a MPSP with
* @tx: frame was transmitted by the local STA
* @acked: frame has been transmitted successfully
*
* NOTE: active mode STA may only serve as MPSP owner
*/
void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,
bool tx, bool acked)
{
u8 rspi = qc[1] & (IEEE80211_QOS_CTL_RSPI >> 8);
u8 eosp = qc[0] & IEEE80211_QOS_CTL_EOSP;
if (tx) {
if (rspi && acked)
set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
if (eosp)
clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
else if (acked &&
test_sta_flag(sta, WLAN_STA_PS_STA) &&
!test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER))
mps_frame_deliver(sta, -1);
} else {
if (eosp)
clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
else if (sta->local_pm != NL80211_MESH_POWER_ACTIVE)
set_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
if (rspi && !test_and_set_sta_flag(sta, WLAN_STA_MPSP_OWNER))
mps_frame_deliver(sta, -1);
}
}
/**
* ieee80211_mps_frame_release - release buffered frames in response to beacon
*
* @sta: mesh STA
* @elems: beacon IEs
*
* For peers if we have individually-addressed frames buffered or the peer
* indicates buffered frames, send a corresponding MPSP trigger frame. Since
* we do not evaluate the awake window duration, QoS Nulls are used as MPSP
* trigger frames. If the neighbour STA is not a peer, only send single frames.
*/
void ieee80211_mps_frame_release(struct sta_info *sta,
struct ieee802_11_elems *elems)
{
int ac, buffer_local = 0;
bool has_buffered = false;
/* TIM map only for LLID <= IEEE80211_MAX_AID */
if (sta->plink_state == NL80211_PLINK_ESTAB)
has_buffered = ieee80211_check_tim(elems->tim, elems->tim_len,
le16_to_cpu(sta->llid) % IEEE80211_MAX_AID);
if (has_buffered)
mps_dbg(sta->sdata, "%pM indicates buffered frames\n",
sta->sta.addr);
/* only transmit to PS STA with announced, non-zero awake window */
if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
(!elems->awake_window || !le16_to_cpu(*elems->awake_window)))
return;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) +
skb_queue_len(&sta->tx_filtered[ac]);
if (!has_buffered && !buffer_local)
return;
if (sta->plink_state == NL80211_PLINK_ESTAB)
mpsp_trigger_send(sta, has_buffered, !buffer_local);
else
mps_frame_deliver(sta, 1);
}

View file

@ -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) {

View file

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

View file

@ -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 */

View file

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

View file

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

View file

@ -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

View file

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

View file

@ -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;
}

View file

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

View file

@ -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;
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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