Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Conflicts: net/wireless/reg.c
This commit is contained in:
		
				commit
				
					
						353c78152c
					
				
			
		
					 38 changed files with 1302 additions and 453 deletions
				
			
		| 
						 | 
					@ -152,8 +152,8 @@
 | 
				
			||||||
!Finclude/net/cfg80211.h cfg80211_scan_request
 | 
					!Finclude/net/cfg80211.h cfg80211_scan_request
 | 
				
			||||||
!Finclude/net/cfg80211.h cfg80211_scan_done
 | 
					!Finclude/net/cfg80211.h cfg80211_scan_done
 | 
				
			||||||
!Finclude/net/cfg80211.h cfg80211_bss
 | 
					!Finclude/net/cfg80211.h cfg80211_bss
 | 
				
			||||||
!Finclude/net/cfg80211.h cfg80211_inform_bss_frame
 | 
					!Finclude/net/cfg80211.h cfg80211_inform_bss_width_frame
 | 
				
			||||||
!Finclude/net/cfg80211.h cfg80211_inform_bss
 | 
					!Finclude/net/cfg80211.h cfg80211_inform_bss_width
 | 
				
			||||||
!Finclude/net/cfg80211.h cfg80211_unlink_bss
 | 
					!Finclude/net/cfg80211.h cfg80211_unlink_bss
 | 
				
			||||||
!Finclude/net/cfg80211.h cfg80211_find_ie
 | 
					!Finclude/net/cfg80211.h cfg80211_find_ie
 | 
				
			||||||
!Finclude/net/cfg80211.h ieee80211_bss_get_ie
 | 
					!Finclude/net/cfg80211.h ieee80211_bss_get_ie
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -167,6 +167,7 @@ struct hwsim_vif_priv {
 | 
				
			||||||
	u32 magic;
 | 
						u32 magic;
 | 
				
			||||||
	u8 bssid[ETH_ALEN];
 | 
						u8 bssid[ETH_ALEN];
 | 
				
			||||||
	bool assoc;
 | 
						bool assoc;
 | 
				
			||||||
 | 
						bool bcn_en;
 | 
				
			||||||
	u16 aid;
 | 
						u16 aid;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1170,6 +1171,16 @@ static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw,
 | 
				
			||||||
	*total_flags = data->rx_filter;
 | 
						*total_flags = data->rx_filter;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void mac80211_hwsim_bcn_en_iter(void *data, u8 *mac,
 | 
				
			||||||
 | 
									       struct ieee80211_vif *vif)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int *count = data;
 | 
				
			||||||
 | 
						struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (vp->bcn_en)
 | 
				
			||||||
 | 
							(*count)++;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
 | 
					static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
 | 
				
			||||||
					    struct ieee80211_vif *vif,
 | 
										    struct ieee80211_vif *vif,
 | 
				
			||||||
					    struct ieee80211_bss_conf *info,
 | 
										    struct ieee80211_bss_conf *info,
 | 
				
			||||||
| 
						 | 
					@ -1180,7 +1191,8 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hwsim_check_magic(vif);
 | 
						hwsim_check_magic(vif);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wiphy_debug(hw->wiphy, "%s(changed=0x%x)\n", __func__, changed);
 | 
						wiphy_debug(hw->wiphy, "%s(changed=0x%x vif->addr=%pM)\n",
 | 
				
			||||||
 | 
							    __func__, changed, vif->addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (changed & BSS_CHANGED_BSSID) {
 | 
						if (changed & BSS_CHANGED_BSSID) {
 | 
				
			||||||
		wiphy_debug(hw->wiphy, "%s: BSSID changed: %pM\n",
 | 
							wiphy_debug(hw->wiphy, "%s: BSSID changed: %pM\n",
 | 
				
			||||||
| 
						 | 
					@ -1202,6 +1214,7 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (changed & BSS_CHANGED_BEACON_ENABLED) {
 | 
						if (changed & BSS_CHANGED_BEACON_ENABLED) {
 | 
				
			||||||
		wiphy_debug(hw->wiphy, "  BCN EN: %d\n", info->enable_beacon);
 | 
							wiphy_debug(hw->wiphy, "  BCN EN: %d\n", info->enable_beacon);
 | 
				
			||||||
 | 
							vp->bcn_en = info->enable_beacon;
 | 
				
			||||||
		if (data->started &&
 | 
							if (data->started &&
 | 
				
			||||||
		    !hrtimer_is_queued(&data->beacon_timer.timer) &&
 | 
							    !hrtimer_is_queued(&data->beacon_timer.timer) &&
 | 
				
			||||||
		    info->enable_beacon) {
 | 
							    info->enable_beacon) {
 | 
				
			||||||
| 
						 | 
					@ -1215,9 +1228,17 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
 | 
				
			||||||
			tasklet_hrtimer_start(&data->beacon_timer,
 | 
								tasklet_hrtimer_start(&data->beacon_timer,
 | 
				
			||||||
					      ns_to_ktime(until_tbtt * 1000),
 | 
										      ns_to_ktime(until_tbtt * 1000),
 | 
				
			||||||
					      HRTIMER_MODE_REL);
 | 
										      HRTIMER_MODE_REL);
 | 
				
			||||||
		} else if (!info->enable_beacon)
 | 
							} else if (!info->enable_beacon) {
 | 
				
			||||||
 | 
								unsigned int count = 0;
 | 
				
			||||||
 | 
								ieee80211_iterate_active_interfaces(
 | 
				
			||||||
 | 
									data->hw, IEEE80211_IFACE_ITER_NORMAL,
 | 
				
			||||||
 | 
									mac80211_hwsim_bcn_en_iter, &count);
 | 
				
			||||||
 | 
								wiphy_debug(hw->wiphy, "  beaconing vifs remaining: %u",
 | 
				
			||||||
 | 
									    count);
 | 
				
			||||||
 | 
								if (count == 0)
 | 
				
			||||||
				tasklet_hrtimer_cancel(&data->beacon_timer);
 | 
									tasklet_hrtimer_cancel(&data->beacon_timer);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (changed & BSS_CHANGED_ERP_CTS_PROT) {
 | 
						if (changed & BSS_CHANGED_ERP_CTS_PROT) {
 | 
				
			||||||
		wiphy_debug(hw->wiphy, "  ERP_CTS_PROT: %d\n",
 | 
							wiphy_debug(hw->wiphy, "  ERP_CTS_PROT: %d\n",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -696,6 +696,18 @@ struct ieee80211_sec_chan_offs_ie {
 | 
				
			||||||
	u8 sec_chan_offs;
 | 
						u8 sec_chan_offs;
 | 
				
			||||||
} __packed;
 | 
					} __packed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct ieee80211_mesh_chansw_params_ie - mesh channel switch parameters IE
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This structure represents the "Mesh Channel Switch Paramters element"
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct ieee80211_mesh_chansw_params_ie {
 | 
				
			||||||
 | 
						u8 mesh_ttl;
 | 
				
			||||||
 | 
						u8 mesh_flags;
 | 
				
			||||||
 | 
						__le16 mesh_reason;
 | 
				
			||||||
 | 
						__le16 mesh_pre_value;
 | 
				
			||||||
 | 
					} __packed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * struct ieee80211_wide_bw_chansw_ie - wide bandwidth channel switch IE
 | 
					 * struct ieee80211_wide_bw_chansw_ie - wide bandwidth channel switch IE
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -750,6 +762,14 @@ enum mesh_config_capab_flags {
 | 
				
			||||||
	IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL	= 0x40,
 | 
						IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL	= 0x40,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * mesh channel switch parameters element's flag indicator
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT BIT(0)
 | 
				
			||||||
 | 
					#define WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR BIT(1)
 | 
				
			||||||
 | 
					#define WLAN_EID_CHAN_SWITCH_PARAM_REASON BIT(2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * struct ieee80211_rann_ie
 | 
					 * struct ieee80211_rann_ie
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -744,6 +744,10 @@ enum station_parameters_apply_mask {
 | 
				
			||||||
 * @capability: station capability
 | 
					 * @capability: station capability
 | 
				
			||||||
 * @ext_capab: extended capabilities of the station
 | 
					 * @ext_capab: extended capabilities of the station
 | 
				
			||||||
 * @ext_capab_len: number of extended capabilities
 | 
					 * @ext_capab_len: number of extended capabilities
 | 
				
			||||||
 | 
					 * @supported_channels: supported channels in IEEE 802.11 format
 | 
				
			||||||
 | 
					 * @supported_channels_len: number of supported channels
 | 
				
			||||||
 | 
					 * @supported_oper_classes: supported oper classes in IEEE 802.11 format
 | 
				
			||||||
 | 
					 * @supported_oper_classes_len: number of supported operating classes
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct station_parameters {
 | 
					struct station_parameters {
 | 
				
			||||||
	const u8 *supported_rates;
 | 
						const u8 *supported_rates;
 | 
				
			||||||
| 
						 | 
					@ -763,6 +767,10 @@ struct station_parameters {
 | 
				
			||||||
	u16 capability;
 | 
						u16 capability;
 | 
				
			||||||
	const u8 *ext_capab;
 | 
						const u8 *ext_capab;
 | 
				
			||||||
	u8 ext_capab_len;
 | 
						u8 ext_capab_len;
 | 
				
			||||||
 | 
						const u8 *supported_channels;
 | 
				
			||||||
 | 
						u8 supported_channels_len;
 | 
				
			||||||
 | 
						const u8 *supported_oper_classes;
 | 
				
			||||||
 | 
						u8 supported_oper_classes_len;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -1656,6 +1664,9 @@ struct cfg80211_disassoc_request {
 | 
				
			||||||
 *	sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is
 | 
					 *	sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is
 | 
				
			||||||
 *	required to assume that the port is unauthorized until authorized by
 | 
					 *	required to assume that the port is unauthorized until authorized by
 | 
				
			||||||
 *	user space. Otherwise, port is marked authorized by default.
 | 
					 *	user space. Otherwise, port is marked authorized by default.
 | 
				
			||||||
 | 
					 * @userspace_handles_dfs: whether user space controls DFS operation, i.e.
 | 
				
			||||||
 | 
					 *	changes the channel when a radar is detected. This is required
 | 
				
			||||||
 | 
					 *	to operate on DFS channels.
 | 
				
			||||||
 * @basic_rates: bitmap of basic rates to use when creating the IBSS
 | 
					 * @basic_rates: bitmap of basic rates to use when creating the IBSS
 | 
				
			||||||
 * @mcast_rate: per-band multicast rate index + 1 (0: disabled)
 | 
					 * @mcast_rate: per-band multicast rate index + 1 (0: disabled)
 | 
				
			||||||
 * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
 | 
					 * @ht_capa:  HT Capabilities over-rides.  Values set in ht_capa_mask
 | 
				
			||||||
| 
						 | 
					@ -1673,6 +1684,7 @@ struct cfg80211_ibss_params {
 | 
				
			||||||
	bool channel_fixed;
 | 
						bool channel_fixed;
 | 
				
			||||||
	bool privacy;
 | 
						bool privacy;
 | 
				
			||||||
	bool control_port;
 | 
						bool control_port;
 | 
				
			||||||
 | 
						bool userspace_handles_dfs;
 | 
				
			||||||
	int mcast_rate[IEEE80211_NUM_BANDS];
 | 
						int mcast_rate[IEEE80211_NUM_BANDS];
 | 
				
			||||||
	struct ieee80211_ht_cap ht_capa;
 | 
						struct ieee80211_ht_cap ht_capa;
 | 
				
			||||||
	struct ieee80211_ht_cap ht_capa_mask;
 | 
						struct ieee80211_ht_cap ht_capa_mask;
 | 
				
			||||||
| 
						 | 
					@ -3053,6 +3065,7 @@ struct cfg80211_cached_keys;
 | 
				
			||||||
 * @conn: (private) cfg80211 software SME connection state machine data
 | 
					 * @conn: (private) cfg80211 software SME connection state machine data
 | 
				
			||||||
 * @connect_keys: (private) keys to set after connection is established
 | 
					 * @connect_keys: (private) keys to set after connection is established
 | 
				
			||||||
 * @ibss_fixed: (private) IBSS is using fixed BSSID
 | 
					 * @ibss_fixed: (private) IBSS is using fixed BSSID
 | 
				
			||||||
 | 
					 * @ibss_dfs_possible: (private) IBSS may change to a DFS channel
 | 
				
			||||||
 * @event_list: (private) list for internal event processing
 | 
					 * @event_list: (private) list for internal event processing
 | 
				
			||||||
 * @event_lock: (private) lock for event list
 | 
					 * @event_lock: (private) lock for event list
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -3091,6 +3104,7 @@ struct wireless_dev {
 | 
				
			||||||
	struct ieee80211_channel *channel;
 | 
						struct ieee80211_channel *channel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool ibss_fixed;
 | 
						bool ibss_fixed;
 | 
				
			||||||
 | 
						bool ibss_dfs_possible;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool ps;
 | 
						bool ps;
 | 
				
			||||||
	int ps_timeout;
 | 
						int ps_timeout;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1503,6 +1503,10 @@ struct ieee80211_tx_control {
 | 
				
			||||||
 * @IEEE80211_HW_TIMING_BEACON_ONLY: Use sync timing from beacon frames
 | 
					 * @IEEE80211_HW_TIMING_BEACON_ONLY: Use sync timing from beacon frames
 | 
				
			||||||
 *	only, to allow getting TBTT of a DTIM beacon.
 | 
					 *	only, to allow getting TBTT of a DTIM beacon.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 * @IEEE80211_HW_SUPPORTS_HT_CCK_RATES: Hardware supports mixing HT/CCK rates
 | 
				
			||||||
 | 
					 *	and can cope with CCK rates in an aggregation session (e.g. by not
 | 
				
			||||||
 | 
					 *	using aggregation for such frames.)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 * @IEEE80211_HW_CHANCTX_STA_CSA: Support 802.11h based channel-switch (CSA)
 | 
					 * @IEEE80211_HW_CHANCTX_STA_CSA: Support 802.11h based channel-switch (CSA)
 | 
				
			||||||
 *	for a single active channel while using channel contexts. When support
 | 
					 *	for a single active channel while using channel contexts. When support
 | 
				
			||||||
 *	is not enabled the default action is to disconnect when getting the
 | 
					 *	is not enabled the default action is to disconnect when getting the
 | 
				
			||||||
| 
						 | 
					@ -4567,4 +4571,18 @@ void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
 | 
				
			||||||
				    struct cfg80211_wowlan_wakeup *wakeup,
 | 
									    struct cfg80211_wowlan_wakeup *wakeup,
 | 
				
			||||||
				    gfp_t gfp);
 | 
									    gfp_t gfp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * ieee80211_tx_prepare_skb - prepare an 802.11 skb for transmission
 | 
				
			||||||
 | 
					 * @hw: pointer as obtained from ieee80211_alloc_hw()
 | 
				
			||||||
 | 
					 * @vif: virtual interface
 | 
				
			||||||
 | 
					 * @skb: frame to be sent from within the driver
 | 
				
			||||||
 | 
					 * @band: the band to transmit on
 | 
				
			||||||
 | 
					 * @sta: optional pointer to get the station to send the frame to
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note: must be called under RCU lock
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
 | 
				
			||||||
 | 
								      struct ieee80211_vif *vif, struct sk_buff *skb,
 | 
				
			||||||
 | 
								      int band, struct ieee80211_sta **sta);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* MAC80211_H */
 | 
					#endif /* MAC80211_H */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -988,7 +988,7 @@ enum nl80211_commands {
 | 
				
			||||||
 * 	to query the CRDA to retrieve one regulatory domain. This attribute can
 | 
					 * 	to query the CRDA to retrieve one regulatory domain. This attribute can
 | 
				
			||||||
 * 	also be used by userspace to query the kernel for the currently set
 | 
					 * 	also be used by userspace to query the kernel for the currently set
 | 
				
			||||||
 * 	regulatory domain. We chose an alpha2 as that is also used by the
 | 
					 * 	regulatory domain. We chose an alpha2 as that is also used by the
 | 
				
			||||||
 * 	IEEE-802.11d country information element to identify a country.
 | 
					 * 	IEEE-802.11 country information element to identify a country.
 | 
				
			||||||
 * 	Users can also simply ask the wireless core to set regulatory domain
 | 
					 * 	Users can also simply ask the wireless core to set regulatory domain
 | 
				
			||||||
 * 	to a specific alpha2.
 | 
					 * 	to a specific alpha2.
 | 
				
			||||||
 * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
 | 
					 * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
 | 
				
			||||||
| 
						 | 
					@ -1496,6 +1496,18 @@ enum nl80211_commands {
 | 
				
			||||||
 * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
 | 
					 * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
 | 
				
			||||||
 *	As specified in the &enum nl80211_rxmgmt_flags.
 | 
					 *	As specified in the &enum nl80211_rxmgmt_flags.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported
 | 
				
			||||||
 | 
					 *      supported operating classes.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space
 | 
				
			||||||
 | 
					 *	controls DFS operation in IBSS mode. If the flag is included in
 | 
				
			||||||
 | 
					 *	%NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS
 | 
				
			||||||
 | 
					 *	channels and reports radar events to userspace. Userspace is required
 | 
				
			||||||
 | 
					 *	to react to radar events, e.g. initiate a channel switch or leave the
 | 
				
			||||||
 | 
					 *	IBSS network.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 * @NL80211_ATTR_MAX: highest attribute number currently defined
 | 
					 * @NL80211_ATTR_MAX: highest attribute number currently defined
 | 
				
			||||||
 * @__NL80211_ATTR_AFTER_LAST: internal use
 | 
					 * @__NL80211_ATTR_AFTER_LAST: internal use
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -1806,6 +1818,12 @@ enum nl80211_attrs {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	NL80211_ATTR_RXMGMT_FLAGS,
 | 
						NL80211_ATTR_RXMGMT_FLAGS,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						NL80211_ATTR_STA_SUPPORTED_CHANNELS,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						NL80211_ATTR_HANDLE_DFS,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* add attributes here, update the policy in nl80211.c */
 | 
						/* add attributes here, update the policy in nl80211.c */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	__NL80211_ATTR_AFTER_LAST,
 | 
						__NL80211_ATTR_AFTER_LAST,
 | 
				
			||||||
| 
						 | 
					@ -3860,13 +3878,12 @@ enum nl80211_radar_event {
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Channel states used by the DFS code.
 | 
					 * Channel states used by the DFS code.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability
 | 
					 * @NL80211_DFS_USABLE: The channel can be used, but channel availability
 | 
				
			||||||
 *	check (CAC) must be performed before using it for AP or IBSS.
 | 
					 *	check (CAC) must be performed before using it for AP or IBSS.
 | 
				
			||||||
 * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
 | 
					 * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
 | 
				
			||||||
 *	is therefore marked as not available.
 | 
					 *	is therefore marked as not available.
 | 
				
			||||||
 * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
 | 
					 * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					 | 
				
			||||||
enum nl80211_dfs_state {
 | 
					enum nl80211_dfs_state {
 | 
				
			||||||
	NL80211_DFS_USABLE,
 | 
						NL80211_DFS_USABLE,
 | 
				
			||||||
	NL80211_DFS_UNAVAILABLE,
 | 
						NL80211_DFS_UNAVAILABLE,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@ config MAC80211
 | 
				
			||||||
	select CRYPTO
 | 
						select CRYPTO
 | 
				
			||||||
	select CRYPTO_ARC4
 | 
						select CRYPTO_ARC4
 | 
				
			||||||
	select CRYPTO_AES
 | 
						select CRYPTO_AES
 | 
				
			||||||
 | 
						select CRYPTO_CCM
 | 
				
			||||||
	select CRC32
 | 
						select CRC32
 | 
				
			||||||
	select AVERAGE
 | 
						select AVERAGE
 | 
				
			||||||
	---help---
 | 
						---help---
 | 
				
			||||||
| 
						 | 
					@ -258,6 +259,17 @@ config MAC80211_MESH_SYNC_DEBUG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  Do not select this option.
 | 
						  Do not select this option.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config MAC80211_MESH_CSA_DEBUG
 | 
				
			||||||
 | 
						bool "Verbose mesh channel switch debugging"
 | 
				
			||||||
 | 
						depends on MAC80211_DEBUG_MENU
 | 
				
			||||||
 | 
						depends on MAC80211_MESH
 | 
				
			||||||
 | 
						---help---
 | 
				
			||||||
 | 
						  Selecting this option causes mac80211 to print out very verbose mesh
 | 
				
			||||||
 | 
						  channel switch debugging messages (when mac80211 is taking part in a
 | 
				
			||||||
 | 
						  mesh network).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  Do not select this option.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config MAC80211_MESH_PS_DEBUG
 | 
					config MAC80211_MESH_PS_DEBUG
 | 
				
			||||||
	bool "Verbose mesh powersave debugging"
 | 
						bool "Verbose mesh powersave debugging"
 | 
				
			||||||
	depends on MAC80211_DEBUG_MENU
 | 
						depends on MAC80211_DEBUG_MENU
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,8 @@
 | 
				
			||||||
 * Copyright 2003-2004, Instant802 Networks, Inc.
 | 
					 * Copyright 2003-2004, Instant802 Networks, Inc.
 | 
				
			||||||
 * Copyright 2005-2006, Devicescape Software, Inc.
 | 
					 * Copyright 2005-2006, Devicescape Software, Inc.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 * Rewrite: Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 * This program is free software; you can redistribute it and/or modify
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU General Public License version 2 as
 | 
					 * it under the terms of the GNU General Public License version 2 as
 | 
				
			||||||
 * published by the Free Software Foundation.
 | 
					 * published by the Free Software Foundation.
 | 
				
			||||||
| 
						 | 
					@ -17,134 +19,75 @@
 | 
				
			||||||
#include "key.h"
 | 
					#include "key.h"
 | 
				
			||||||
#include "aes_ccm.h"
 | 
					#include "aes_ccm.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a)
 | 
					void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
 | 
				
			||||||
 | 
								       u8 *data, size_t data_len, u8 *mic)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int i;
 | 
						struct scatterlist assoc, pt, ct[2];
 | 
				
			||||||
	u8 *b_0, *aad, *b, *s_0;
 | 
						struct {
 | 
				
			||||||
 | 
							struct aead_request	req;
 | 
				
			||||||
 | 
							u8			priv[crypto_aead_reqsize(tfm)];
 | 
				
			||||||
 | 
						} aead_req;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b_0 = scratch + 3 * AES_BLOCK_SIZE;
 | 
						memset(&aead_req, 0, sizeof(aead_req));
 | 
				
			||||||
	aad = scratch + 4 * AES_BLOCK_SIZE;
 | 
					 | 
				
			||||||
	b = scratch;
 | 
					 | 
				
			||||||
	s_0 = scratch + AES_BLOCK_SIZE;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	crypto_cipher_encrypt_one(tfm, b, b_0);
 | 
						sg_init_one(&pt, data, data_len);
 | 
				
			||||||
 | 
						sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
 | 
				
			||||||
 | 
						sg_init_table(ct, 2);
 | 
				
			||||||
 | 
						sg_set_buf(&ct[0], data, data_len);
 | 
				
			||||||
 | 
						sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Extra Authenticate-only data (always two AES blocks) */
 | 
						aead_request_set_tfm(&aead_req.req, tfm);
 | 
				
			||||||
	for (i = 0; i < AES_BLOCK_SIZE; i++)
 | 
						aead_request_set_assoc(&aead_req.req, &assoc, assoc.length);
 | 
				
			||||||
		aad[i] ^= b[i];
 | 
						aead_request_set_crypt(&aead_req.req, &pt, ct, data_len, b_0);
 | 
				
			||||||
	crypto_cipher_encrypt_one(tfm, b, aad);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	aad += AES_BLOCK_SIZE;
 | 
						crypto_aead_encrypt(&aead_req.req);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (i = 0; i < AES_BLOCK_SIZE; i++)
 | 
					 | 
				
			||||||
		aad[i] ^= b[i];
 | 
					 | 
				
			||||||
	crypto_cipher_encrypt_one(tfm, a, aad);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Mask out bits from auth-only-b_0 */
 | 
					 | 
				
			||||||
	b_0[0] &= 0x07;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* S_0 is used to encrypt T (= MIC) */
 | 
					 | 
				
			||||||
	b_0[14] = 0;
 | 
					 | 
				
			||||||
	b_0[15] = 0;
 | 
					 | 
				
			||||||
	crypto_cipher_encrypt_one(tfm, s_0, b_0);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
 | 
				
			||||||
void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
 | 
								      u8 *data, size_t data_len, u8 *mic)
 | 
				
			||||||
			       u8 *data, size_t data_len,
 | 
					 | 
				
			||||||
			       u8 *cdata, u8 *mic)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int i, j, last_len, num_blocks;
 | 
						struct scatterlist assoc, pt, ct[2];
 | 
				
			||||||
	u8 *pos, *cpos, *b, *s_0, *e, *b_0;
 | 
						struct {
 | 
				
			||||||
 | 
							struct aead_request	req;
 | 
				
			||||||
 | 
							u8			priv[crypto_aead_reqsize(tfm)];
 | 
				
			||||||
 | 
						} aead_req;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	b = scratch;
 | 
						memset(&aead_req, 0, sizeof(aead_req));
 | 
				
			||||||
	s_0 = scratch + AES_BLOCK_SIZE;
 | 
					 | 
				
			||||||
	e = scratch + 2 * AES_BLOCK_SIZE;
 | 
					 | 
				
			||||||
	b_0 = scratch + 3 * AES_BLOCK_SIZE;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
 | 
						sg_init_one(&pt, data, data_len);
 | 
				
			||||||
	last_len = data_len % AES_BLOCK_SIZE;
 | 
						sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
 | 
				
			||||||
	aes_ccm_prepare(tfm, scratch, b);
 | 
						sg_init_table(ct, 2);
 | 
				
			||||||
 | 
						sg_set_buf(&ct[0], data, data_len);
 | 
				
			||||||
 | 
						sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Process payload blocks */
 | 
						aead_request_set_tfm(&aead_req.req, tfm);
 | 
				
			||||||
	pos = data;
 | 
						aead_request_set_assoc(&aead_req.req, &assoc, assoc.length);
 | 
				
			||||||
	cpos = cdata;
 | 
						aead_request_set_crypt(&aead_req.req, ct, &pt,
 | 
				
			||||||
	for (j = 1; j <= num_blocks; j++) {
 | 
								       data_len + IEEE80211_CCMP_MIC_LEN, b_0);
 | 
				
			||||||
		int blen = (j == num_blocks && last_len) ?
 | 
					 | 
				
			||||||
			last_len : AES_BLOCK_SIZE;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Authentication followed by encryption */
 | 
						return crypto_aead_decrypt(&aead_req.req);
 | 
				
			||||||
		for (i = 0; i < blen; i++)
 | 
					 | 
				
			||||||
			b[i] ^= pos[i];
 | 
					 | 
				
			||||||
		crypto_cipher_encrypt_one(tfm, b, b);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		b_0[14] = (j >> 8) & 0xff;
 | 
					 | 
				
			||||||
		b_0[15] = j & 0xff;
 | 
					 | 
				
			||||||
		crypto_cipher_encrypt_one(tfm, e, b_0);
 | 
					 | 
				
			||||||
		for (i = 0; i < blen; i++)
 | 
					 | 
				
			||||||
			*cpos++ = *pos++ ^ e[i];
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++)
 | 
					struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[])
 | 
				
			||||||
		mic[i] = b[i] ^ s_0[i];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
 | 
					 | 
				
			||||||
			      u8 *cdata, size_t data_len, u8 *mic, u8 *data)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int i, j, last_len, num_blocks;
 | 
						struct crypto_aead *tfm;
 | 
				
			||||||
	u8 *pos, *cpos, *b, *s_0, *a, *b_0;
 | 
						int err;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	b = scratch;
 | 
					 | 
				
			||||||
	s_0 = scratch + AES_BLOCK_SIZE;
 | 
					 | 
				
			||||||
	a = scratch + 2 * AES_BLOCK_SIZE;
 | 
					 | 
				
			||||||
	b_0 = scratch + 3 * AES_BLOCK_SIZE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
 | 
					 | 
				
			||||||
	last_len = data_len % AES_BLOCK_SIZE;
 | 
					 | 
				
			||||||
	aes_ccm_prepare(tfm, scratch, a);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Process payload blocks */
 | 
					 | 
				
			||||||
	cpos = cdata;
 | 
					 | 
				
			||||||
	pos = data;
 | 
					 | 
				
			||||||
	for (j = 1; j <= num_blocks; j++) {
 | 
					 | 
				
			||||||
		int blen = (j == num_blocks && last_len) ?
 | 
					 | 
				
			||||||
			last_len : AES_BLOCK_SIZE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* Decryption followed by authentication */
 | 
					 | 
				
			||||||
		b_0[14] = (j >> 8) & 0xff;
 | 
					 | 
				
			||||||
		b_0[15] = j & 0xff;
 | 
					 | 
				
			||||||
		crypto_cipher_encrypt_one(tfm, b, b_0);
 | 
					 | 
				
			||||||
		for (i = 0; i < blen; i++) {
 | 
					 | 
				
			||||||
			*pos = *cpos++ ^ b[i];
 | 
					 | 
				
			||||||
			a[i] ^= *pos++;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		crypto_cipher_encrypt_one(tfm, a, a);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) {
 | 
					 | 
				
			||||||
		if ((mic[i] ^ s_0[i]) != a[i])
 | 
					 | 
				
			||||||
			return -1;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[])
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct crypto_cipher *tfm;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
 | 
					 | 
				
			||||||
	if (!IS_ERR(tfm))
 | 
					 | 
				
			||||||
		crypto_cipher_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC);
 | 
				
			||||||
 | 
						if (IS_ERR(tfm))
 | 
				
			||||||
		return tfm;
 | 
							return tfm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = crypto_aead_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
 | 
				
			||||||
 | 
						if (!err)
 | 
				
			||||||
 | 
							err = crypto_aead_setauthsize(tfm, IEEE80211_CCMP_MIC_LEN);
 | 
				
			||||||
 | 
						if (!err)
 | 
				
			||||||
 | 
							return tfm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						crypto_free_aead(tfm);
 | 
				
			||||||
 | 
						return ERR_PTR(err);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ieee80211_aes_key_free(struct crypto_aead *tfm)
 | 
				
			||||||
void ieee80211_aes_key_free(struct crypto_cipher *tfm)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	crypto_free_cipher(tfm);
 | 
						crypto_free_aead(tfm);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,13 +12,11 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/crypto.h>
 | 
					#include <linux/crypto.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]);
 | 
					struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]);
 | 
				
			||||||
void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
 | 
					void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
 | 
				
			||||||
			       u8 *data, size_t data_len,
 | 
								       u8 *data, size_t data_len, u8 *mic);
 | 
				
			||||||
			       u8 *cdata, u8 *mic);
 | 
					int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
 | 
				
			||||||
int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
 | 
								      u8 *data, size_t data_len, u8 *mic);
 | 
				
			||||||
			      u8 *cdata, size_t data_len,
 | 
					void ieee80211_aes_key_free(struct crypto_aead *tfm);
 | 
				
			||||||
			      u8 *mic, u8 *data);
 | 
					 | 
				
			||||||
void ieee80211_aes_key_free(struct crypto_cipher *tfm);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* AES_CCM_H */
 | 
					#endif /* AES_CCM_H */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1059,6 +1059,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
 | 
				
			||||||
	/* abort any running channel switch */
 | 
						/* abort any running channel switch */
 | 
				
			||||||
	sdata->vif.csa_active = false;
 | 
						sdata->vif.csa_active = false;
 | 
				
			||||||
	cancel_work_sync(&sdata->csa_finalize_work);
 | 
						cancel_work_sync(&sdata->csa_finalize_work);
 | 
				
			||||||
 | 
						cancel_work_sync(&sdata->u.ap.request_smps_work);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* turn off carrier for this interface and dependent VLANs */
 | 
						/* turn off carrier for this interface and dependent VLANs */
 | 
				
			||||||
	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
 | 
						list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
 | 
				
			||||||
| 
						 | 
					@ -1342,8 +1343,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
 | 
				
			||||||
				sta->plink_state = params->plink_state;
 | 
									sta->plink_state = params->plink_state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				ieee80211_mps_sta_status_update(sta);
 | 
									ieee80211_mps_sta_status_update(sta);
 | 
				
			||||||
				changed |=
 | 
									changed |= ieee80211_mps_set_sta_local_pm(sta,
 | 
				
			||||||
				      ieee80211_mps_local_status_update(sdata);
 | 
											NL80211_MESH_POWER_UNKNOWN);
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			default:
 | 
								default:
 | 
				
			||||||
				/*  nothing  */
 | 
									/*  nothing  */
 | 
				
			||||||
| 
						 | 
					@ -1553,6 +1554,20 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_unlock(&local->sta_mtx);
 | 
						mutex_unlock(&local->sta_mtx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((sdata->vif.type == NL80211_IFTYPE_AP ||
 | 
				
			||||||
 | 
						     sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
 | 
				
			||||||
 | 
						    sta->known_smps_mode != sta->sdata->bss->req_smps &&
 | 
				
			||||||
 | 
						    test_sta_flag(sta, WLAN_STA_AUTHORIZED) &&
 | 
				
			||||||
 | 
						    sta_info_tx_streams(sta) != 1) {
 | 
				
			||||||
 | 
							ht_dbg(sta->sdata,
 | 
				
			||||||
 | 
							       "%pM just authorized and MIMO capable - update SMPS\n",
 | 
				
			||||||
 | 
							       sta->sta.addr);
 | 
				
			||||||
 | 
							ieee80211_send_smps_action(sta->sdata,
 | 
				
			||||||
 | 
								sta->sdata->bss->req_smps,
 | 
				
			||||||
 | 
								sta->sta.addr,
 | 
				
			||||||
 | 
								sta->sdata->vif.bss_conf.bssid);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (sdata->vif.type == NL80211_IFTYPE_STATION &&
 | 
						if (sdata->vif.type == NL80211_IFTYPE_STATION &&
 | 
				
			||||||
	    params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
 | 
						    params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
 | 
				
			||||||
		ieee80211_recalc_ps(local, -1);
 | 
							ieee80211_recalc_ps(local, -1);
 | 
				
			||||||
| 
						 | 
					@ -2337,7 +2352,91 @@ static int ieee80211_testmode_dump(struct wiphy *wiphy,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
 | 
					int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
 | 
									enum ieee80211_smps_mode smps_mode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sta_info *sta;
 | 
				
			||||||
 | 
						enum ieee80211_smps_mode old_req;
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						old_req = sdata->u.ap.req_smps;
 | 
				
			||||||
 | 
						sdata->u.ap.req_smps = smps_mode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* AUTOMATIC doesn't mean much for AP - don't allow it */
 | 
				
			||||||
 | 
						if (old_req == smps_mode ||
 | 
				
			||||||
 | 
						    smps_mode == IEEE80211_SMPS_AUTOMATIC)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						 /* If no associated stations, there's no need to do anything */
 | 
				
			||||||
 | 
						if (!atomic_read(&sdata->u.ap.num_mcast_sta)) {
 | 
				
			||||||
 | 
							sdata->smps_mode = smps_mode;
 | 
				
			||||||
 | 
							ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ht_dbg(sdata,
 | 
				
			||||||
 | 
						       "SMSP %d requested in AP mode, sending Action frame to %d stations\n",
 | 
				
			||||||
 | 
						       smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&sdata->local->sta_mtx);
 | 
				
			||||||
 | 
						for (i = 0; i < STA_HASH_SIZE; i++) {
 | 
				
			||||||
 | 
							for (sta = rcu_dereference_protected(sdata->local->sta_hash[i],
 | 
				
			||||||
 | 
									lockdep_is_held(&sdata->local->sta_mtx));
 | 
				
			||||||
 | 
							     sta;
 | 
				
			||||||
 | 
							     sta = rcu_dereference_protected(sta->hnext,
 | 
				
			||||||
 | 
									lockdep_is_held(&sdata->local->sta_mtx))) {
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * Only stations associated to our AP and
 | 
				
			||||||
 | 
								 * associated VLANs
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								if (sta->sdata->bss != &sdata->u.ap)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* This station doesn't support MIMO - skip it */
 | 
				
			||||||
 | 
								if (sta_info_tx_streams(sta) == 1)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * Don't wake up a STA just to send the action frame
 | 
				
			||||||
 | 
								 * unless we are getting more restrictive.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
 | 
				
			||||||
 | 
								    !ieee80211_smps_is_restrictive(sta->known_smps_mode,
 | 
				
			||||||
 | 
												   smps_mode)) {
 | 
				
			||||||
 | 
									ht_dbg(sdata,
 | 
				
			||||||
 | 
									       "Won't send SMPS to sleeping STA %pM\n",
 | 
				
			||||||
 | 
									       sta->sta.addr);
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * If the STA is not authorized, wait until it gets
 | 
				
			||||||
 | 
								 * authorized and the action frame will be sent then.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr);
 | 
				
			||||||
 | 
								ieee80211_send_smps_action(sdata, smps_mode,
 | 
				
			||||||
 | 
											   sta->sta.addr,
 | 
				
			||||||
 | 
											   sdata->vif.bss_conf.bssid);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mutex_unlock(&sdata->local->sta_mtx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sdata->smps_mode = smps_mode;
 | 
				
			||||||
 | 
						ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
				 enum ieee80211_smps_mode smps_mode)
 | 
									 enum ieee80211_smps_mode smps_mode)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const u8 *ap;
 | 
						const u8 *ap;
 | 
				
			||||||
| 
						 | 
					@ -2346,6 +2445,9 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	lockdep_assert_held(&sdata->wdev.mtx);
 | 
						lockdep_assert_held(&sdata->wdev.mtx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	old_req = sdata->u.mgd.req_smps;
 | 
						old_req = sdata->u.mgd.req_smps;
 | 
				
			||||||
	sdata->u.mgd.req_smps = smps_mode;
 | 
						sdata->u.mgd.req_smps = smps_mode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2402,7 +2504,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* no change, but if automatic follow powersave */
 | 
						/* no change, but if automatic follow powersave */
 | 
				
			||||||
	sdata_lock(sdata);
 | 
						sdata_lock(sdata);
 | 
				
			||||||
	__ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
 | 
						__ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps);
 | 
				
			||||||
	sdata_unlock(sdata);
 | 
						sdata_unlock(sdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
 | 
						if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
 | 
				
			||||||
| 
						 | 
					@ -2860,7 +2962,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
 | 
				
			||||||
		container_of(work, struct ieee80211_sub_if_data,
 | 
							container_of(work, struct ieee80211_sub_if_data,
 | 
				
			||||||
			     csa_finalize_work);
 | 
								     csa_finalize_work);
 | 
				
			||||||
	struct ieee80211_local *local = sdata->local;
 | 
						struct ieee80211_local *local = sdata->local;
 | 
				
			||||||
	int err, changed;
 | 
						int err, changed = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!ieee80211_sdata_running(sdata))
 | 
						if (!ieee80211_sdata_running(sdata))
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -2892,6 +2994,13 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
 | 
				
			||||||
	case NL80211_IFTYPE_ADHOC:
 | 
						case NL80211_IFTYPE_ADHOC:
 | 
				
			||||||
		ieee80211_ibss_finish_csa(sdata);
 | 
							ieee80211_ibss_finish_csa(sdata);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					#ifdef CONFIG_MAC80211_MESH
 | 
				
			||||||
 | 
						case NL80211_IFTYPE_MESH_POINT:
 | 
				
			||||||
 | 
							err = ieee80211_mesh_finish_csa(sdata);
 | 
				
			||||||
 | 
							if (err < 0)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		WARN_ON(1);
 | 
							WARN_ON(1);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -2912,6 +3021,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
 | 
				
			||||||
	struct ieee80211_local *local = sdata->local;
 | 
						struct ieee80211_local *local = sdata->local;
 | 
				
			||||||
	struct ieee80211_chanctx_conf *chanctx_conf;
 | 
						struct ieee80211_chanctx_conf *chanctx_conf;
 | 
				
			||||||
	struct ieee80211_chanctx *chanctx;
 | 
						struct ieee80211_chanctx *chanctx;
 | 
				
			||||||
 | 
						struct ieee80211_if_mesh __maybe_unused *ifmsh;
 | 
				
			||||||
	int err, num_chanctx;
 | 
						int err, num_chanctx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!list_empty(&local->roc_list) || local->scanning)
 | 
						if (!list_empty(&local->roc_list) || local->scanning)
 | 
				
			||||||
| 
						 | 
					@ -2995,6 +3105,26 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
 | 
				
			||||||
		if (err < 0)
 | 
							if (err < 0)
 | 
				
			||||||
			return err;
 | 
								return err;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					#ifdef CONFIG_MAC80211_MESH
 | 
				
			||||||
 | 
						case NL80211_IFTYPE_MESH_POINT:
 | 
				
			||||||
 | 
							ifmsh = &sdata->u.mesh;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!ifmsh->mesh_id)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* changes into another band are not supported */
 | 
				
			||||||
 | 
							if (sdata->vif.bss_conf.chandef.chan->band !=
 | 
				
			||||||
 | 
							    params->chandef.chan->band)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = ieee80211_mesh_csa_beacon(sdata, params, true);
 | 
				
			||||||
 | 
							if (err < 0)
 | 
				
			||||||
 | 
								return err;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return -EOPNOTSUPP;
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,6 +44,12 @@
 | 
				
			||||||
#define MAC80211_MESH_SYNC_DEBUG 0
 | 
					#define MAC80211_MESH_SYNC_DEBUG 0
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_MAC80211_MESH_CSA_DEBUG
 | 
				
			||||||
 | 
					#define MAC80211_MESH_CSA_DEBUG 1
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define MAC80211_MESH_CSA_DEBUG 0
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_MAC80211_MESH_PS_DEBUG
 | 
					#ifdef CONFIG_MAC80211_MESH_PS_DEBUG
 | 
				
			||||||
#define MAC80211_MESH_PS_DEBUG 1
 | 
					#define MAC80211_MESH_PS_DEBUG 1
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
| 
						 | 
					@ -157,6 +163,10 @@ do {									\
 | 
				
			||||||
	_sdata_dbg(MAC80211_MESH_SYNC_DEBUG,				\
 | 
						_sdata_dbg(MAC80211_MESH_SYNC_DEBUG,				\
 | 
				
			||||||
		   sdata, fmt, ##__VA_ARGS__)
 | 
							   sdata, fmt, ##__VA_ARGS__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define mcsa_dbg(sdata, fmt, ...)					\
 | 
				
			||||||
 | 
						_sdata_dbg(MAC80211_MESH_CSA_DEBUG,				\
 | 
				
			||||||
 | 
							   sdata, fmt, ##__VA_ARGS__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define mps_dbg(sdata, fmt, ...)					\
 | 
					#define mps_dbg(sdata, fmt, ...)					\
 | 
				
			||||||
	_sdata_dbg(MAC80211_MESH_PS_DEBUG,				\
 | 
						_sdata_dbg(MAC80211_MESH_PS_DEBUG,				\
 | 
				
			||||||
		   sdata, fmt, ##__VA_ARGS__)
 | 
							   sdata, fmt, ##__VA_ARGS__)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -224,12 +224,15 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
	     smps_mode == IEEE80211_SMPS_AUTOMATIC))
 | 
						     smps_mode == IEEE80211_SMPS_AUTOMATIC))
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* supported only on managed interfaces for now */
 | 
						if (sdata->vif.type != NL80211_IFTYPE_STATION &&
 | 
				
			||||||
	if (sdata->vif.type != NL80211_IFTYPE_STATION)
 | 
						    sdata->vif.type != NL80211_IFTYPE_AP)
 | 
				
			||||||
		return -EOPNOTSUPP;
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sdata_lock(sdata);
 | 
						sdata_lock(sdata);
 | 
				
			||||||
	err = __ieee80211_request_smps(sdata, smps_mode);
 | 
						if (sdata->vif.type == NL80211_IFTYPE_STATION)
 | 
				
			||||||
 | 
							err = __ieee80211_request_smps_mgd(sdata, smps_mode);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							err = __ieee80211_request_smps_ap(sdata, smps_mode);
 | 
				
			||||||
	sdata_unlock(sdata);
 | 
						sdata_unlock(sdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
| 
						 | 
					@ -245,12 +248,15 @@ static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {
 | 
				
			||||||
static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata,
 | 
					static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
				     char *buf, int buflen)
 | 
									     char *buf, int buflen)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (sdata->vif.type != NL80211_IFTYPE_STATION)
 | 
						if (sdata->vif.type == NL80211_IFTYPE_STATION)
 | 
				
			||||||
		return -EOPNOTSUPP;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return snprintf(buf, buflen, "request: %s\nused: %s\n",
 | 
							return snprintf(buf, buflen, "request: %s\nused: %s\n",
 | 
				
			||||||
				smps_modes[sdata->u.mgd.req_smps],
 | 
									smps_modes[sdata->u.mgd.req_smps],
 | 
				
			||||||
				smps_modes[sdata->smps_mode]);
 | 
									smps_modes[sdata->smps_mode]);
 | 
				
			||||||
 | 
						if (sdata->vif.type == NL80211_IFTYPE_AP)
 | 
				
			||||||
 | 
							return snprintf(buf, buflen, "request: %s\nused: %s\n",
 | 
				
			||||||
 | 
									smps_modes[sdata->u.ap.req_smps],
 | 
				
			||||||
 | 
									smps_modes[sdata->smps_mode]);
 | 
				
			||||||
 | 
						return -EINVAL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata,
 | 
					static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
| 
						 | 
					@ -563,6 +569,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
 | 
				
			||||||
static void add_ap_files(struct ieee80211_sub_if_data *sdata)
 | 
					static void add_ap_files(struct ieee80211_sub_if_data *sdata)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	DEBUGFS_ADD(num_mcast_sta);
 | 
						DEBUGFS_ADD(num_mcast_sta);
 | 
				
			||||||
 | 
						DEBUGFS_ADD_MODE(smps, 0600);
 | 
				
			||||||
	DEBUGFS_ADD(num_sta_ps);
 | 
						DEBUGFS_ADD(num_sta_ps);
 | 
				
			||||||
	DEBUGFS_ADD(dtim_count);
 | 
						DEBUGFS_ADD(dtim_count);
 | 
				
			||||||
	DEBUGFS_ADD(num_buffered_multicast);
 | 
						DEBUGFS_ADD(num_buffered_multicast);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -448,14 +448,25 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ieee80211_request_smps_work(struct work_struct *work)
 | 
					void ieee80211_request_smps_mgd_work(struct work_struct *work)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ieee80211_sub_if_data *sdata =
 | 
						struct ieee80211_sub_if_data *sdata =
 | 
				
			||||||
		container_of(work, struct ieee80211_sub_if_data,
 | 
							container_of(work, struct ieee80211_sub_if_data,
 | 
				
			||||||
			     u.mgd.request_smps_work);
 | 
								     u.mgd.request_smps_work);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sdata_lock(sdata);
 | 
						sdata_lock(sdata);
 | 
				
			||||||
	__ieee80211_request_smps(sdata, sdata->u.mgd.driver_smps_mode);
 | 
						__ieee80211_request_smps_mgd(sdata, sdata->u.mgd.driver_smps_mode);
 | 
				
			||||||
 | 
						sdata_unlock(sdata);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ieee80211_request_smps_ap_work(struct work_struct *work)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ieee80211_sub_if_data *sdata =
 | 
				
			||||||
 | 
							container_of(work, struct ieee80211_sub_if_data,
 | 
				
			||||||
 | 
								     u.ap.request_smps_work);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sdata_lock(sdata);
 | 
				
			||||||
 | 
						__ieee80211_request_smps_ap(sdata, sdata->u.ap.driver_smps_mode);
 | 
				
			||||||
	sdata_unlock(sdata);
 | 
						sdata_unlock(sdata);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -464,19 +475,29 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 | 
						struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
 | 
						if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION &&
 | 
				
			||||||
 | 
								 vif->type != NL80211_IFTYPE_AP))
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
 | 
						if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
 | 
				
			||||||
		smps_mode = IEEE80211_SMPS_AUTOMATIC;
 | 
							smps_mode = IEEE80211_SMPS_AUTOMATIC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (vif->type == NL80211_IFTYPE_STATION) {
 | 
				
			||||||
		if (sdata->u.mgd.driver_smps_mode == smps_mode)
 | 
							if (sdata->u.mgd.driver_smps_mode == smps_mode)
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
 | 
					 | 
				
			||||||
		sdata->u.mgd.driver_smps_mode = smps_mode;
 | 
							sdata->u.mgd.driver_smps_mode = smps_mode;
 | 
				
			||||||
 | 
					 | 
				
			||||||
		ieee80211_queue_work(&sdata->local->hw,
 | 
							ieee80211_queue_work(&sdata->local->hw,
 | 
				
			||||||
				     &sdata->u.mgd.request_smps_work);
 | 
									     &sdata->u.mgd.request_smps_work);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							/* AUTOMATIC is meaningless in AP mode */
 | 
				
			||||||
 | 
							if (WARN_ON_ONCE(smps_mode == IEEE80211_SMPS_AUTOMATIC))
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							if (sdata->u.ap.driver_smps_mode == smps_mode)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							sdata->u.ap.driver_smps_mode = smps_mode;
 | 
				
			||||||
 | 
							ieee80211_queue_work(&sdata->local->hw,
 | 
				
			||||||
 | 
									     &sdata->u.ap.request_smps_work);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
/* this might change ... don't want non-open drivers using it */
 | 
					/* this might change ... don't want non-open drivers using it */
 | 
				
			||||||
EXPORT_SYMBOL_GPL(ieee80211_request_smps);
 | 
					EXPORT_SYMBOL_GPL(ieee80211_request_smps);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -229,6 +229,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
	struct beacon_data *presp;
 | 
						struct beacon_data *presp;
 | 
				
			||||||
	enum nl80211_bss_scan_width scan_width;
 | 
						enum nl80211_bss_scan_width scan_width;
 | 
				
			||||||
	bool have_higher_than_11mbit;
 | 
						bool have_higher_than_11mbit;
 | 
				
			||||||
 | 
						bool radar_required = false;
 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sdata_assert_lock(sdata);
 | 
						sdata_assert_lock(sdata);
 | 
				
			||||||
| 
						 | 
					@ -273,6 +274,23 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		chandef.width = NL80211_CHAN_WIDTH_20;
 | 
							chandef.width = NL80211_CHAN_WIDTH_20;
 | 
				
			||||||
		chandef.center_freq1 = chan->center_freq;
 | 
							chandef.center_freq1 = chan->center_freq;
 | 
				
			||||||
 | 
							/* check again for downgraded chandef */
 | 
				
			||||||
 | 
							if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
 | 
				
			||||||
 | 
								sdata_info(sdata,
 | 
				
			||||||
 | 
									   "Failed to join IBSS, beacons forbidden\n");
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
 | 
				
			||||||
 | 
										    &chandef);
 | 
				
			||||||
 | 
						if (err > 0) {
 | 
				
			||||||
 | 
							if (!ifibss->userspace_handles_dfs) {
 | 
				
			||||||
 | 
								sdata_info(sdata,
 | 
				
			||||||
 | 
									   "Failed to join IBSS, DFS channel without control program\n");
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							radar_required = true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ieee80211_vif_release_channel(sdata);
 | 
						ieee80211_vif_release_channel(sdata);
 | 
				
			||||||
| 
						 | 
					@ -297,6 +315,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
	rcu_assign_pointer(ifibss->presp, presp);
 | 
						rcu_assign_pointer(ifibss->presp, presp);
 | 
				
			||||||
	mgmt = (void *)presp->head;
 | 
						mgmt = (void *)presp->head;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sdata->radar_required = radar_required;
 | 
				
			||||||
	sdata->vif.bss_conf.enable_beacon = true;
 | 
						sdata->vif.bss_conf.enable_beacon = true;
 | 
				
			||||||
	sdata->vif.bss_conf.beacon_int = beacon_int;
 | 
						sdata->vif.bss_conf.beacon_int = beacon_int;
 | 
				
			||||||
	sdata->vif.bss_conf.basic_rates = basic_rates;
 | 
						sdata->vif.bss_conf.basic_rates = basic_rates;
 | 
				
			||||||
| 
						 | 
					@ -445,60 +464,6 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
				  tsf, false);
 | 
									  tsf, false);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
 | 
					 | 
				
			||||||
				     struct cfg80211_csa_settings *csa_settings)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct sk_buff *skb;
 | 
					 | 
				
			||||||
	struct ieee80211_mgmt *mgmt;
 | 
					 | 
				
			||||||
	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
 | 
					 | 
				
			||||||
	struct ieee80211_local *local = sdata->local;
 | 
					 | 
				
			||||||
	int freq;
 | 
					 | 
				
			||||||
	int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
 | 
					 | 
				
			||||||
		      sizeof(mgmt->u.action.u.chan_switch);
 | 
					 | 
				
			||||||
	u8 *pos;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	skb = dev_alloc_skb(local->tx_headroom + hdr_len +
 | 
					 | 
				
			||||||
			    5 +	/* channel switch announcement element */
 | 
					 | 
				
			||||||
			    3);	/* secondary channel offset element */
 | 
					 | 
				
			||||||
	if (!skb)
 | 
					 | 
				
			||||||
		return -1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	skb_reserve(skb, local->tx_headroom);
 | 
					 | 
				
			||||||
	mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
 | 
					 | 
				
			||||||
	memset(mgmt, 0, hdr_len);
 | 
					 | 
				
			||||||
	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
 | 
					 | 
				
			||||||
					  IEEE80211_STYPE_ACTION);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	eth_broadcast_addr(mgmt->da);
 | 
					 | 
				
			||||||
	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
 | 
					 | 
				
			||||||
	memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
 | 
					 | 
				
			||||||
	mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
 | 
					 | 
				
			||||||
	mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
 | 
					 | 
				
			||||||
	pos = skb_put(skb, 5);
 | 
					 | 
				
			||||||
	*pos++ = WLAN_EID_CHANNEL_SWITCH;			/* EID */
 | 
					 | 
				
			||||||
	*pos++ = 3;						/* IE length */
 | 
					 | 
				
			||||||
	*pos++ = csa_settings->block_tx ? 1 : 0;		/* CSA mode */
 | 
					 | 
				
			||||||
	freq = csa_settings->chandef.chan->center_freq;
 | 
					 | 
				
			||||||
	*pos++ = ieee80211_frequency_to_channel(freq);		/* channel */
 | 
					 | 
				
			||||||
	*pos++ = csa_settings->count;				/* count */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
 | 
					 | 
				
			||||||
		enum nl80211_channel_type ch_type;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		skb_put(skb, 3);
 | 
					 | 
				
			||||||
		*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;	/* EID */
 | 
					 | 
				
			||||||
		*pos++ = 1;					/* IE length */
 | 
					 | 
				
			||||||
		ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
 | 
					 | 
				
			||||||
		if (ch_type == NL80211_CHAN_HT40PLUS)
 | 
					 | 
				
			||||||
			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ieee80211_tx_skb(sdata, skb);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
 | 
					int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
			      struct cfg80211_csa_settings *csa_settings)
 | 
								      struct cfg80211_csa_settings *csa_settings)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -796,19 +761,34 @@ static void ieee80211_csa_connection_drop_work(struct work_struct *work)
 | 
				
			||||||
	ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 | 
						ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* if the current channel is a DFS channel, mark the channel as
 | 
				
			||||||
 | 
						 * unavailable.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
 | 
				
			||||||
 | 
										    &ifibss->chandef);
 | 
				
			||||||
 | 
						if (err > 0)
 | 
				
			||||||
 | 
							cfg80211_radar_event(sdata->local->hw.wiphy, &ifibss->chandef,
 | 
				
			||||||
 | 
									     GFP_ATOMIC);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool
 | 
					static bool
 | 
				
			||||||
ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 | 
					ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
				  struct ieee802_11_elems *elems,
 | 
									  struct ieee802_11_elems *elems,
 | 
				
			||||||
				  bool beacon)
 | 
									  bool beacon)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cfg80211_csa_settings params;
 | 
						struct cfg80211_csa_settings params;
 | 
				
			||||||
 | 
						struct ieee80211_csa_ie csa_ie;
 | 
				
			||||||
	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
 | 
						struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
 | 
				
			||||||
	struct ieee80211_chanctx_conf *chanctx_conf;
 | 
						struct ieee80211_chanctx_conf *chanctx_conf;
 | 
				
			||||||
	struct ieee80211_chanctx *chanctx;
 | 
						struct ieee80211_chanctx *chanctx;
 | 
				
			||||||
	enum nl80211_channel_type ch_type;
 | 
						enum nl80211_channel_type ch_type;
 | 
				
			||||||
	int err, num_chanctx;
 | 
						int err, num_chanctx;
 | 
				
			||||||
	u32 sta_flags;
 | 
						u32 sta_flags;
 | 
				
			||||||
	u8 mode;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (sdata->vif.csa_active)
 | 
						if (sdata->vif.csa_active)
 | 
				
			||||||
		return true;
 | 
							return true;
 | 
				
			||||||
| 
						 | 
					@ -831,12 +811,10 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memset(¶ms, 0, sizeof(params));
 | 
						memset(¶ms, 0, sizeof(params));
 | 
				
			||||||
 | 
						memset(&csa_ie, 0, sizeof(csa_ie));
 | 
				
			||||||
	err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
 | 
						err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
 | 
				
			||||||
					   ifibss->chandef.chan->band,
 | 
										   ifibss->chandef.chan->band,
 | 
				
			||||||
					   sta_flags, ifibss->bssid,
 | 
										   sta_flags, ifibss->bssid, &csa_ie);
 | 
				
			||||||
					   ¶ms.count, &mode,
 | 
					 | 
				
			||||||
					   ¶ms.chandef);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* can't switch to destination channel, fail */
 | 
						/* can't switch to destination channel, fail */
 | 
				
			||||||
	if (err < 0)
 | 
						if (err < 0)
 | 
				
			||||||
		goto disconnect;
 | 
							goto disconnect;
 | 
				
			||||||
| 
						 | 
					@ -845,6 +823,9 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params.count = csa_ie.count;
 | 
				
			||||||
 | 
						params.chandef = csa_ie.chandef;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ifibss->chandef.chan->band != params.chandef.chan->band)
 | 
						if (ifibss->chandef.chan->band != params.chandef.chan->band)
 | 
				
			||||||
		goto disconnect;
 | 
							goto disconnect;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -880,8 +861,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
		goto disconnect;
 | 
							goto disconnect;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, ¶ms.chandef,
 | 
						if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, ¶ms.chandef)) {
 | 
				
			||||||
				     IEEE80211_CHAN_DISABLED)) {
 | 
					 | 
				
			||||||
		sdata_info(sdata,
 | 
							sdata_info(sdata,
 | 
				
			||||||
			   "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
 | 
								   "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
 | 
				
			||||||
			   ifibss->bssid,
 | 
								   ifibss->bssid,
 | 
				
			||||||
| 
						 | 
					@ -897,10 +877,11 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
	if (err < 0)
 | 
						if (err < 0)
 | 
				
			||||||
		goto disconnect;
 | 
							goto disconnect;
 | 
				
			||||||
	if (err) {
 | 
						if (err) {
 | 
				
			||||||
		params.radar_required = true;
 | 
							/* IBSS-DFS only allowed with a control program */
 | 
				
			||||||
 | 
							if (!ifibss->userspace_handles_dfs)
 | 
				
			||||||
		/* TODO: IBSS-DFS not (yet) supported, disconnect. */
 | 
					 | 
				
			||||||
			goto disconnect;
 | 
								goto disconnect;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							params.radar_required = true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rcu_read_lock();
 | 
						rcu_read_lock();
 | 
				
			||||||
| 
						 | 
					@ -931,7 +912,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
		 "received channel switch announcement to go to channel %d MHz\n",
 | 
							 "received channel switch announcement to go to channel %d MHz\n",
 | 
				
			||||||
		 params.chandef.chan->center_freq);
 | 
							 params.chandef.chan->center_freq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	params.block_tx = !!mode;
 | 
						params.block_tx = !!csa_ie.mode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ieee80211_ibss_csa_beacon(sdata, ¶ms);
 | 
						ieee80211_ibss_csa_beacon(sdata, ¶ms);
 | 
				
			||||||
	sdata->csa_radar_required = params.radar_required;
 | 
						sdata->csa_radar_required = params.radar_required;
 | 
				
			||||||
| 
						 | 
					@ -947,12 +928,16 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
	ieee80211_bss_info_change_notify(sdata, err);
 | 
						ieee80211_bss_info_change_notify(sdata, err);
 | 
				
			||||||
	drv_channel_switch_beacon(sdata, ¶ms.chandef);
 | 
						drv_channel_switch_beacon(sdata, ¶ms.chandef);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ieee80211_ibss_csa_mark_radar(sdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
disconnect:
 | 
					disconnect:
 | 
				
			||||||
	ibss_dbg(sdata, "Can't handle channel switch, disconnect\n");
 | 
						ibss_dbg(sdata, "Can't handle channel switch, disconnect\n");
 | 
				
			||||||
	ieee80211_queue_work(&sdata->local->hw,
 | 
						ieee80211_queue_work(&sdata->local->hw,
 | 
				
			||||||
			     &ifibss->csa_connection_drop_work);
 | 
								     &ifibss->csa_connection_drop_work);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ieee80211_ibss_csa_mark_radar(sdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1688,6 +1673,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sdata->u.ibss.privacy = params->privacy;
 | 
						sdata->u.ibss.privacy = params->privacy;
 | 
				
			||||||
	sdata->u.ibss.control_port = params->control_port;
 | 
						sdata->u.ibss.control_port = params->control_port;
 | 
				
			||||||
 | 
						sdata->u.ibss.userspace_handles_dfs = params->userspace_handles_dfs;
 | 
				
			||||||
	sdata->u.ibss.basic_rates = params->basic_rates;
 | 
						sdata->u.ibss.basic_rates = params->basic_rates;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* fix basic_rates if channel does not support these rates */
 | 
						/* fix basic_rates if channel does not support these rates */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -262,6 +262,10 @@ struct ieee80211_if_ap {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct ps_data ps;
 | 
						struct ps_data ps;
 | 
				
			||||||
	atomic_t num_mcast_sta; /* number of stations receiving multicast */
 | 
						atomic_t num_mcast_sta; /* number of stations receiving multicast */
 | 
				
			||||||
 | 
						enum ieee80211_smps_mode req_smps, /* requested smps mode */
 | 
				
			||||||
 | 
								 driver_smps_mode; /* smps mode request */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct work_struct request_smps_work;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ieee80211_if_wds {
 | 
					struct ieee80211_if_wds {
 | 
				
			||||||
| 
						 | 
					@ -498,6 +502,7 @@ struct ieee80211_if_ibss {
 | 
				
			||||||
	bool privacy;
 | 
						bool privacy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool control_port;
 | 
						bool control_port;
 | 
				
			||||||
 | 
						bool userspace_handles_dfs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	u8 bssid[ETH_ALEN] __aligned(2);
 | 
						u8 bssid[ETH_ALEN] __aligned(2);
 | 
				
			||||||
	u8 ssid[IEEE80211_MAX_SSID_LEN];
 | 
						u8 ssid[IEEE80211_MAX_SSID_LEN];
 | 
				
			||||||
| 
						 | 
					@ -539,6 +544,11 @@ struct ieee80211_mesh_sync_ops {
 | 
				
			||||||
	/* add other framework functions here */
 | 
						/* add other framework functions here */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct mesh_csa_settings {
 | 
				
			||||||
 | 
						struct rcu_head rcu_head;
 | 
				
			||||||
 | 
						struct cfg80211_csa_settings settings;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ieee80211_if_mesh {
 | 
					struct ieee80211_if_mesh {
 | 
				
			||||||
	struct timer_list housekeeping_timer;
 | 
						struct timer_list housekeeping_timer;
 | 
				
			||||||
	struct timer_list mesh_path_timer;
 | 
						struct timer_list mesh_path_timer;
 | 
				
			||||||
| 
						 | 
					@ -599,6 +609,11 @@ struct ieee80211_if_mesh {
 | 
				
			||||||
	int ps_peers_light_sleep;
 | 
						int ps_peers_light_sleep;
 | 
				
			||||||
	int ps_peers_deep_sleep;
 | 
						int ps_peers_deep_sleep;
 | 
				
			||||||
	struct ps_data ps;
 | 
						struct ps_data ps;
 | 
				
			||||||
 | 
						/* Channel Switching Support */
 | 
				
			||||||
 | 
						struct mesh_csa_settings __rcu *csa;
 | 
				
			||||||
 | 
						bool chsw_init;
 | 
				
			||||||
 | 
						u8 chsw_ttl;
 | 
				
			||||||
 | 
						u16 pre_value;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_MAC80211_MESH
 | 
					#ifdef CONFIG_MAC80211_MESH
 | 
				
			||||||
| 
						 | 
					@ -1207,6 +1222,14 @@ struct ieee80211_ra_tid {
 | 
				
			||||||
	u16 tid;
 | 
						u16 tid;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* this struct holds the value parsing from channel switch IE  */
 | 
				
			||||||
 | 
					struct ieee80211_csa_ie {
 | 
				
			||||||
 | 
						struct cfg80211_chan_def chandef;
 | 
				
			||||||
 | 
						u8 mode;
 | 
				
			||||||
 | 
						u8 count;
 | 
				
			||||||
 | 
						u8 ttl;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Parsed Information Elements */
 | 
					/* Parsed Information Elements */
 | 
				
			||||||
struct ieee802_11_elems {
 | 
					struct ieee802_11_elems {
 | 
				
			||||||
	const u8 *ie_start;
 | 
						const u8 *ie_start;
 | 
				
			||||||
| 
						 | 
					@ -1243,6 +1266,7 @@ struct ieee802_11_elems {
 | 
				
			||||||
	const struct ieee80211_timeout_interval_ie *timeout_int;
 | 
						const struct ieee80211_timeout_interval_ie *timeout_int;
 | 
				
			||||||
	const u8 *opmode_notif;
 | 
						const u8 *opmode_notif;
 | 
				
			||||||
	const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
 | 
						const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
 | 
				
			||||||
 | 
						const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* length of them, respectively */
 | 
						/* length of them, respectively */
 | 
				
			||||||
	u8 ssid_len;
 | 
						u8 ssid_len;
 | 
				
			||||||
| 
						 | 
					@ -1343,6 +1367,10 @@ void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
 | 
				
			||||||
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
 | 
					void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
 | 
				
			||||||
void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
 | 
					void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
				   struct sk_buff *skb);
 | 
									   struct sk_buff *skb);
 | 
				
			||||||
 | 
					int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
 | 
								      struct cfg80211_csa_settings *csa_settings,
 | 
				
			||||||
 | 
								      bool csa_action);
 | 
				
			||||||
 | 
					int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* scan/BSS handling */
 | 
					/* scan/BSS handling */
 | 
				
			||||||
void ieee80211_scan_work(struct work_struct *work);
 | 
					void ieee80211_scan_work(struct work_struct *work);
 | 
				
			||||||
| 
						 | 
					@ -1439,7 +1467,10 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
 | 
					int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
			       enum ieee80211_smps_mode smps, const u8 *da,
 | 
								       enum ieee80211_smps_mode smps, const u8 *da,
 | 
				
			||||||
			       const u8 *bssid);
 | 
								       const u8 *bssid);
 | 
				
			||||||
void ieee80211_request_smps_work(struct work_struct *work);
 | 
					void ieee80211_request_smps_ap_work(struct work_struct *work);
 | 
				
			||||||
 | 
					void ieee80211_request_smps_mgd_work(struct work_struct *work);
 | 
				
			||||||
 | 
					bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
 | 
				
			||||||
 | 
									   enum ieee80211_smps_mode smps_mode_new);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
 | 
					void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
 | 
				
			||||||
				     u16 initiator, u16 reason, bool stop);
 | 
									     u16 initiator, u16 reason, bool stop);
 | 
				
			||||||
| 
						 | 
					@ -1501,17 +1532,16 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
 *	%IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT,
 | 
					 *	%IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT,
 | 
				
			||||||
 *	%IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ,
 | 
					 *	%IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ,
 | 
				
			||||||
 *	%IEEE80211_STA_DISABLE_160MHZ.
 | 
					 *	%IEEE80211_STA_DISABLE_160MHZ.
 | 
				
			||||||
 * @count: to be filled with the counter until the switch (on success only)
 | 
					 | 
				
			||||||
 * @bssid: the currently connected bssid (for reporting)
 | 
					 * @bssid: the currently connected bssid (for reporting)
 | 
				
			||||||
 * @mode: to be filled with CSA mode (on success only)
 | 
					 * @csa_ie: parsed 802.11 csa elements on count, mode, chandef and mesh ttl.
 | 
				
			||||||
 * @new_chandef: to be filled with destination chandef (on success only)
 | 
						All of them will be filled with if success only.
 | 
				
			||||||
 * Return: 0 on success, <0 on error and >0 if there is nothing to parse.
 | 
					 * Return: 0 on success, <0 on error and >0 if there is nothing to parse.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
 | 
					int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
				 struct ieee802_11_elems *elems, bool beacon,
 | 
									 struct ieee802_11_elems *elems, bool beacon,
 | 
				
			||||||
				 enum ieee80211_band current_band,
 | 
									 enum ieee80211_band current_band,
 | 
				
			||||||
				 u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
 | 
									 u32 sta_flags, u8 *bssid,
 | 
				
			||||||
				 struct cfg80211_chan_def *new_chandef);
 | 
									 struct ieee80211_csa_ie *csa_ie);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Suspend/resume and hw reconfiguration */
 | 
					/* Suspend/resume and hw reconfiguration */
 | 
				
			||||||
int ieee80211_reconfig(struct ieee80211_local *local);
 | 
					int ieee80211_reconfig(struct ieee80211_local *local);
 | 
				
			||||||
| 
						 | 
					@ -1657,7 +1687,9 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
 | 
				
			||||||
u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
 | 
					u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
			    struct ieee802_11_elems *elems,
 | 
								    struct ieee802_11_elems *elems,
 | 
				
			||||||
			    enum ieee80211_band band, u32 *basic_rates);
 | 
								    enum ieee80211_band band, u32 *basic_rates);
 | 
				
			||||||
int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
 | 
					int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
 | 
									 enum ieee80211_smps_mode smps_mode);
 | 
				
			||||||
 | 
					int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
				enum ieee80211_smps_mode smps_mode);
 | 
									enum ieee80211_smps_mode smps_mode);
 | 
				
			||||||
void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
 | 
					void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1714,6 +1746,8 @@ void ieee80211_dfs_cac_timer(unsigned long data);
 | 
				
			||||||
void ieee80211_dfs_cac_timer_work(struct work_struct *work);
 | 
					void ieee80211_dfs_cac_timer_work(struct work_struct *work);
 | 
				
			||||||
void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
 | 
					void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
 | 
				
			||||||
void ieee80211_dfs_radar_detected_work(struct work_struct *work);
 | 
					void ieee80211_dfs_radar_detected_work(struct work_struct *work);
 | 
				
			||||||
 | 
					int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
 | 
								      struct cfg80211_csa_settings *csa_settings);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_MAC80211_NOINLINE
 | 
					#ifdef CONFIG_MAC80211_NOINLINE
 | 
				
			||||||
#define debug_noinline noinline
 | 
					#define debug_noinline noinline
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1293,7 +1293,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
	case NL80211_IFTYPE_AP:
 | 
						case NL80211_IFTYPE_AP:
 | 
				
			||||||
		skb_queue_head_init(&sdata->u.ap.ps.bc_buf);
 | 
							skb_queue_head_init(&sdata->u.ap.ps.bc_buf);
 | 
				
			||||||
		INIT_LIST_HEAD(&sdata->u.ap.vlans);
 | 
							INIT_LIST_HEAD(&sdata->u.ap.vlans);
 | 
				
			||||||
 | 
							INIT_WORK(&sdata->u.ap.request_smps_work,
 | 
				
			||||||
 | 
								  ieee80211_request_smps_ap_work);
 | 
				
			||||||
		sdata->vif.bss_conf.bssid = sdata->vif.addr;
 | 
							sdata->vif.bss_conf.bssid = sdata->vif.addr;
 | 
				
			||||||
 | 
							sdata->u.ap.req_smps = IEEE80211_SMPS_OFF;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case NL80211_IFTYPE_P2P_CLIENT:
 | 
						case NL80211_IFTYPE_P2P_CLIENT:
 | 
				
			||||||
		type = NL80211_IFTYPE_STATION;
 | 
							type = NL80211_IFTYPE_STATION;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,7 +83,7 @@ struct ieee80211_key {
 | 
				
			||||||
			 * Management frames.
 | 
								 * Management frames.
 | 
				
			||||||
			 */
 | 
								 */
 | 
				
			||||||
			u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN];
 | 
								u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN];
 | 
				
			||||||
			struct crypto_cipher *tfm;
 | 
								struct crypto_aead *tfm;
 | 
				
			||||||
			u32 replays; /* dot11RSNAStatsCCMPReplays */
 | 
								u32 replays; /* dot11RSNAStatsCCMPReplays */
 | 
				
			||||||
		} ccmp;
 | 
							} ccmp;
 | 
				
			||||||
		struct {
 | 
							struct {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,7 @@
 | 
				
			||||||
#include <asm/unaligned.h>
 | 
					#include <asm/unaligned.h>
 | 
				
			||||||
#include "ieee80211_i.h"
 | 
					#include "ieee80211_i.h"
 | 
				
			||||||
#include "mesh.h"
 | 
					#include "mesh.h"
 | 
				
			||||||
 | 
					#include "driver-ops.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int mesh_allocated;
 | 
					static int mesh_allocated;
 | 
				
			||||||
static struct kmem_cache *rm_cache;
 | 
					static struct kmem_cache *rm_cache;
 | 
				
			||||||
| 
						 | 
					@ -610,6 +611,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
 | 
				
			||||||
	struct sk_buff *skb;
 | 
						struct sk_buff *skb;
 | 
				
			||||||
	struct ieee80211_mgmt *mgmt;
 | 
						struct ieee80211_mgmt *mgmt;
 | 
				
			||||||
	struct ieee80211_chanctx_conf *chanctx_conf;
 | 
						struct ieee80211_chanctx_conf *chanctx_conf;
 | 
				
			||||||
 | 
						struct mesh_csa_settings *csa;
 | 
				
			||||||
	enum ieee80211_band band;
 | 
						enum ieee80211_band band;
 | 
				
			||||||
	u8 *pos;
 | 
						u8 *pos;
 | 
				
			||||||
	struct ieee80211_sub_if_data *sdata;
 | 
						struct ieee80211_sub_if_data *sdata;
 | 
				
			||||||
| 
						 | 
					@ -624,6 +626,10 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	head_len = hdr_len +
 | 
						head_len = hdr_len +
 | 
				
			||||||
		   2 + /* NULL SSID */
 | 
							   2 + /* NULL SSID */
 | 
				
			||||||
 | 
							   /* Channel Switch Announcement */
 | 
				
			||||||
 | 
							   2 + sizeof(struct ieee80211_channel_sw_ie) +
 | 
				
			||||||
 | 
							   /* Mesh Channel Swith Parameters */
 | 
				
			||||||
 | 
							   2 + sizeof(struct ieee80211_mesh_chansw_params_ie) +
 | 
				
			||||||
		   2 + 8 + /* supported rates */
 | 
							   2 + 8 + /* supported rates */
 | 
				
			||||||
		   2 + 3; /* DS params */
 | 
							   2 + 3; /* DS params */
 | 
				
			||||||
	tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
 | 
						tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
 | 
				
			||||||
| 
						 | 
					@ -665,6 +671,38 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
 | 
				
			||||||
	*pos++ = WLAN_EID_SSID;
 | 
						*pos++ = WLAN_EID_SSID;
 | 
				
			||||||
	*pos++ = 0x0;
 | 
						*pos++ = 0x0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rcu_read_lock();
 | 
				
			||||||
 | 
						csa = rcu_dereference(ifmsh->csa);
 | 
				
			||||||
 | 
						if (csa) {
 | 
				
			||||||
 | 
							__le16 pre_value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pos = skb_put(skb, 13);
 | 
				
			||||||
 | 
							memset(pos, 0, 13);
 | 
				
			||||||
 | 
							*pos++ = WLAN_EID_CHANNEL_SWITCH;
 | 
				
			||||||
 | 
							*pos++ = 3;
 | 
				
			||||||
 | 
							*pos++ = 0x0;
 | 
				
			||||||
 | 
							*pos++ = ieee80211_frequency_to_channel(
 | 
				
			||||||
 | 
									csa->settings.chandef.chan->center_freq);
 | 
				
			||||||
 | 
							sdata->csa_counter_offset_beacon = hdr_len + 6;
 | 
				
			||||||
 | 
							*pos++ = csa->settings.count;
 | 
				
			||||||
 | 
							*pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
 | 
				
			||||||
 | 
							*pos++ = 6;
 | 
				
			||||||
 | 
							if (ifmsh->chsw_init) {
 | 
				
			||||||
 | 
								*pos++ = ifmsh->mshcfg.dot11MeshTTL;
 | 
				
			||||||
 | 
								*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								*pos++ = ifmsh->chsw_ttl;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							*pos++ |= csa->settings.block_tx ?
 | 
				
			||||||
 | 
								  WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
 | 
				
			||||||
 | 
							put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos);
 | 
				
			||||||
 | 
							pos += 2;
 | 
				
			||||||
 | 
							pre_value = cpu_to_le16(ifmsh->pre_value);
 | 
				
			||||||
 | 
							memcpy(pos, &pre_value, 2);
 | 
				
			||||||
 | 
							pos += 2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rcu_read_unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
 | 
						if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
 | 
				
			||||||
	    mesh_add_ds_params_ie(sdata, skb))
 | 
						    mesh_add_ds_params_ie(sdata, skb))
 | 
				
			||||||
		goto out_free;
 | 
							goto out_free;
 | 
				
			||||||
| 
						 | 
					@ -812,6 +850,127 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
 | 
				
			||||||
	ieee80211_configure_filter(local);
 | 
						ieee80211_configure_filter(local);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool
 | 
				
			||||||
 | 
					ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
 | 
									 struct ieee802_11_elems *elems, bool beacon)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct cfg80211_csa_settings params;
 | 
				
			||||||
 | 
						struct ieee80211_csa_ie csa_ie;
 | 
				
			||||||
 | 
						struct ieee80211_chanctx_conf *chanctx_conf;
 | 
				
			||||||
 | 
						struct ieee80211_chanctx *chanctx;
 | 
				
			||||||
 | 
						struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 | 
				
			||||||
 | 
						enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
 | 
				
			||||||
 | 
						int err, num_chanctx;
 | 
				
			||||||
 | 
						u32 sta_flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sdata->vif.csa_active)
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!ifmsh->mesh_id)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sta_flags = IEEE80211_STA_DISABLE_VHT;
 | 
				
			||||||
 | 
						switch (sdata->vif.bss_conf.chandef.width) {
 | 
				
			||||||
 | 
						case NL80211_CHAN_WIDTH_20_NOHT:
 | 
				
			||||||
 | 
							sta_flags |= IEEE80211_STA_DISABLE_HT;
 | 
				
			||||||
 | 
						case NL80211_CHAN_WIDTH_20:
 | 
				
			||||||
 | 
							sta_flags |= IEEE80211_STA_DISABLE_40MHZ;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memset(¶ms, 0, sizeof(params));
 | 
				
			||||||
 | 
						memset(&csa_ie, 0, sizeof(csa_ie));
 | 
				
			||||||
 | 
						err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band,
 | 
				
			||||||
 | 
										   sta_flags, sdata->vif.addr,
 | 
				
			||||||
 | 
										   &csa_ie);
 | 
				
			||||||
 | 
						if (err < 0)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params.chandef = csa_ie.chandef;
 | 
				
			||||||
 | 
						params.count = csa_ie.count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sdata->vif.bss_conf.chandef.chan->band !=
 | 
				
			||||||
 | 
						    params.chandef.chan->band)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, ¶ms.chandef,
 | 
				
			||||||
 | 
									     IEEE80211_CHAN_DISABLED)) {
 | 
				
			||||||
 | 
							sdata_info(sdata,
 | 
				
			||||||
 | 
								   "mesh STA %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), aborting\n",
 | 
				
			||||||
 | 
								   sdata->vif.addr,
 | 
				
			||||||
 | 
								   params.chandef.chan->center_freq,
 | 
				
			||||||
 | 
								   params.chandef.width,
 | 
				
			||||||
 | 
								   params.chandef.center_freq1,
 | 
				
			||||||
 | 
								   params.chandef.center_freq2);
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
 | 
				
			||||||
 | 
										    ¶ms.chandef);
 | 
				
			||||||
 | 
						if (err < 0)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						if (err) {
 | 
				
			||||||
 | 
							params.radar_required = true;
 | 
				
			||||||
 | 
							/* TODO: DFS not (yet) supported */
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rcu_read_lock();
 | 
				
			||||||
 | 
						chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
 | 
				
			||||||
 | 
						if (!chanctx_conf)
 | 
				
			||||||
 | 
							goto failed_chswitch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* don't handle for multi-VIF cases */
 | 
				
			||||||
 | 
						chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
 | 
				
			||||||
 | 
						if (chanctx->refcount > 1)
 | 
				
			||||||
 | 
							goto failed_chswitch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						num_chanctx = 0;
 | 
				
			||||||
 | 
						list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list)
 | 
				
			||||||
 | 
							num_chanctx++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (num_chanctx > 1)
 | 
				
			||||||
 | 
							goto failed_chswitch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rcu_read_unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mcsa_dbg(sdata,
 | 
				
			||||||
 | 
							 "received channel switch announcement to go to channel %d MHz\n",
 | 
				
			||||||
 | 
							 params.chandef.chan->center_freq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params.block_tx = csa_ie.mode & WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT;
 | 
				
			||||||
 | 
						if (beacon)
 | 
				
			||||||
 | 
							ifmsh->chsw_ttl = csa_ie.ttl - 1;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							ifmsh->chsw_ttl = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ifmsh->chsw_ttl > 0)
 | 
				
			||||||
 | 
							if (ieee80211_mesh_csa_beacon(sdata, ¶ms, false) < 0)
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sdata->csa_radar_required = params.radar_required;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (params.block_tx)
 | 
				
			||||||
 | 
							ieee80211_stop_queues_by_reason(&sdata->local->hw,
 | 
				
			||||||
 | 
									IEEE80211_MAX_QUEUE_MAP,
 | 
				
			||||||
 | 
									IEEE80211_QUEUE_STOP_REASON_CSA);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sdata->local->csa_chandef = params.chandef;
 | 
				
			||||||
 | 
						sdata->vif.csa_active = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ieee80211_bss_info_change_notify(sdata, err);
 | 
				
			||||||
 | 
						drv_channel_switch_beacon(sdata, ¶ms.chandef);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					failed_chswitch:
 | 
				
			||||||
 | 
						rcu_read_unlock();
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
 | 
					ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
			    struct ieee80211_mgmt *mgmt, size_t len)
 | 
								    struct ieee80211_mgmt *mgmt, size_t len)
 | 
				
			||||||
| 
						 | 
					@ -918,6 +1077,142 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
	if (ifmsh->sync_ops)
 | 
						if (ifmsh->sync_ops)
 | 
				
			||||||
		ifmsh->sync_ops->rx_bcn_presp(sdata,
 | 
							ifmsh->sync_ops->rx_bcn_presp(sdata,
 | 
				
			||||||
			stype, mgmt, &elems, rx_status);
 | 
								stype, mgmt, &elems, rx_status);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!ifmsh->chsw_init)
 | 
				
			||||||
 | 
							ieee80211_mesh_process_chnswitch(sdata, &elems, true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 | 
				
			||||||
 | 
						struct mesh_csa_settings *tmp_csa_settings;
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Reset the TTL value and Initiator flag */
 | 
				
			||||||
 | 
						ifmsh->chsw_init = false;
 | 
				
			||||||
 | 
						ifmsh->chsw_ttl = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Remove the CSA and MCSP elements from the beacon */
 | 
				
			||||||
 | 
						tmp_csa_settings = rcu_dereference(ifmsh->csa);
 | 
				
			||||||
 | 
						rcu_assign_pointer(ifmsh->csa, NULL);
 | 
				
			||||||
 | 
						kfree_rcu(tmp_csa_settings, rcu_head);
 | 
				
			||||||
 | 
						ret = ieee80211_mesh_rebuild_beacon(sdata);
 | 
				
			||||||
 | 
						if (ret)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mcsa_dbg(sdata, "complete switching to center freq %d MHz",
 | 
				
			||||||
 | 
							 sdata->vif.bss_conf.chandef.chan->center_freq);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
 | 
								      struct cfg80211_csa_settings *csa_settings,
 | 
				
			||||||
 | 
								      bool csa_action)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 | 
				
			||||||
 | 
						struct mesh_csa_settings *tmp_csa_settings;
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings),
 | 
				
			||||||
 | 
									   GFP_ATOMIC);
 | 
				
			||||||
 | 
						if (!tmp_csa_settings)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memcpy(&tmp_csa_settings->settings, csa_settings,
 | 
				
			||||||
 | 
						       sizeof(struct cfg80211_csa_settings));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rcu_assign_pointer(ifmsh->csa, tmp_csa_settings);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = ieee80211_mesh_rebuild_beacon(sdata);
 | 
				
			||||||
 | 
						if (ret) {
 | 
				
			||||||
 | 
							tmp_csa_settings = rcu_dereference(ifmsh->csa);
 | 
				
			||||||
 | 
							rcu_assign_pointer(ifmsh->csa, NULL);
 | 
				
			||||||
 | 
							kfree_rcu(tmp_csa_settings, rcu_head);
 | 
				
			||||||
 | 
							return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (csa_action)
 | 
				
			||||||
 | 
							ieee80211_send_action_csa(sdata, csa_settings);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
 | 
								       struct ieee80211_mgmt *mgmt, size_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ieee80211_mgmt *mgmt_fwd;
 | 
				
			||||||
 | 
						struct sk_buff *skb;
 | 
				
			||||||
 | 
						struct ieee80211_local *local = sdata->local;
 | 
				
			||||||
 | 
						u8 *pos = mgmt->u.action.u.chan_switch.variable;
 | 
				
			||||||
 | 
						size_t offset_ttl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						skb = dev_alloc_skb(local->tx_headroom + len);
 | 
				
			||||||
 | 
						if (!skb)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
						skb_reserve(skb, local->tx_headroom);
 | 
				
			||||||
 | 
						mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* offset_ttl is based on whether the secondary channel
 | 
				
			||||||
 | 
						 * offset is available or not. Substract 1 from the mesh TTL
 | 
				
			||||||
 | 
						 * and disable the initiator flag before forwarding.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						offset_ttl = (len < 42) ? 7 : 10;
 | 
				
			||||||
 | 
						*(pos + offset_ttl) -= 1;
 | 
				
			||||||
 | 
						*(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
 | 
				
			||||||
 | 
						sdata->u.mesh.chsw_ttl = *(pos + offset_ttl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memcpy(mgmt_fwd, mgmt, len);
 | 
				
			||||||
 | 
						eth_broadcast_addr(mgmt_fwd->da);
 | 
				
			||||||
 | 
						memcpy(mgmt_fwd->sa, sdata->vif.addr, ETH_ALEN);
 | 
				
			||||||
 | 
						memcpy(mgmt_fwd->bssid, sdata->vif.addr, ETH_ALEN);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ieee80211_tx_skb(sdata, skb);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
 | 
								      struct ieee80211_mgmt *mgmt, size_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 | 
				
			||||||
 | 
						struct ieee802_11_elems elems;
 | 
				
			||||||
 | 
						u16 pre_value;
 | 
				
			||||||
 | 
						bool fwd_csa = true;
 | 
				
			||||||
 | 
						size_t baselen;
 | 
				
			||||||
 | 
						u8 *pos, ttl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mgmt->u.action.u.measurement.action_code !=
 | 
				
			||||||
 | 
						    WLAN_ACTION_SPCT_CHL_SWITCH)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pos = mgmt->u.action.u.chan_switch.variable;
 | 
				
			||||||
 | 
						baselen = offsetof(struct ieee80211_mgmt,
 | 
				
			||||||
 | 
								   u.action.u.chan_switch.variable);
 | 
				
			||||||
 | 
						ieee802_11_parse_elems(pos, len - baselen, false, &elems);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ttl = elems.mesh_chansw_params_ie->mesh_ttl;
 | 
				
			||||||
 | 
						if (!--ttl)
 | 
				
			||||||
 | 
							fwd_csa = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value);
 | 
				
			||||||
 | 
						if (ifmsh->pre_value >= pre_value)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ifmsh->pre_value = pre_value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!ieee80211_mesh_process_chnswitch(sdata, &elems, false)) {
 | 
				
			||||||
 | 
							mcsa_dbg(sdata, "Failed to process CSA action frame");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* forward or re-broadcast the CSA frame */
 | 
				
			||||||
 | 
						if (fwd_csa) {
 | 
				
			||||||
 | 
							if (mesh_fwd_csa_frame(sdata, mgmt, len) < 0)
 | 
				
			||||||
 | 
								mcsa_dbg(sdata, "Failed to forward the CSA frame");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
 | 
					static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
| 
						 | 
					@ -939,6 +1234,9 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
		if (mesh_action_is_path_sel(mgmt))
 | 
							if (mesh_action_is_path_sel(mgmt))
 | 
				
			||||||
			mesh_rx_path_sel_frame(sdata, mgmt, len);
 | 
								mesh_rx_path_sel_frame(sdata, mgmt, len);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
						case WLAN_CATEGORY_SPECTRUM_MGMT:
 | 
				
			||||||
 | 
							mesh_rx_csa_frame(sdata, mgmt, len);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1056,13 +1354,11 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
 | 
				
			||||||
		    (unsigned long) sdata);
 | 
							    (unsigned long) sdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ifmsh->accepting_plinks = true;
 | 
						ifmsh->accepting_plinks = true;
 | 
				
			||||||
	ifmsh->preq_id = 0;
 | 
					 | 
				
			||||||
	ifmsh->sn = 0;
 | 
					 | 
				
			||||||
	ifmsh->num_gates = 0;
 | 
					 | 
				
			||||||
	atomic_set(&ifmsh->mpaths, 0);
 | 
						atomic_set(&ifmsh->mpaths, 0);
 | 
				
			||||||
	mesh_rmc_init(sdata);
 | 
						mesh_rmc_init(sdata);
 | 
				
			||||||
	ifmsh->last_preq = jiffies;
 | 
						ifmsh->last_preq = jiffies;
 | 
				
			||||||
	ifmsh->next_perr = jiffies;
 | 
						ifmsh->next_perr = jiffies;
 | 
				
			||||||
 | 
						ifmsh->chsw_init = false;
 | 
				
			||||||
	/* Allocate all mesh structures when creating the first mesh interface. */
 | 
						/* Allocate all mesh structures when creating the first mesh interface. */
 | 
				
			||||||
	if (!mesh_allocated)
 | 
						if (!mesh_allocated)
 | 
				
			||||||
		ieee80211s_init();
 | 
							ieee80211s_init();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -222,7 +222,8 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)
 | 
				
			||||||
	mesh_path_flush_by_nexthop(sta);
 | 
						mesh_path_flush_by_nexthop(sta);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ieee80211_mps_sta_status_update(sta);
 | 
						ieee80211_mps_sta_status_update(sta);
 | 
				
			||||||
	changed |= ieee80211_mps_local_status_update(sdata);
 | 
						changed |= ieee80211_mps_set_sta_local_pm(sta,
 | 
				
			||||||
 | 
								NL80211_MESH_POWER_UNKNOWN);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return changed;
 | 
						return changed;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -152,6 +152,9 @@ u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ieee80211_sub_if_data *sdata = sta->sdata;
 | 
						struct ieee80211_sub_if_data *sdata = sta->sdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sta->local_pm == pm)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mps_dbg(sdata, "local STA operates in mode %d with %pM\n",
 | 
						mps_dbg(sdata, "local STA operates in mode %d with %pM\n",
 | 
				
			||||||
		pm, sta->sta.addr);
 | 
							pm, sta->sta.addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -245,6 +248,14 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	do_buffer = (pm != NL80211_MESH_POWER_ACTIVE);
 | 
						do_buffer = (pm != NL80211_MESH_POWER_ACTIVE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* 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);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Don't let the same PS state be set twice */
 | 
						/* Don't let the same PS state be set twice */
 | 
				
			||||||
	if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer)
 | 
						if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -257,14 +268,6 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		ieee80211_sta_ps_deliver_wakeup(sta);
 | 
							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,
 | 
					static void mps_set_sta_peer_pm(struct sta_info *sta,
 | 
				
			||||||
| 
						 | 
					@ -444,8 +447,7 @@ static void mpsp_qos_null_append(struct sta_info *sta,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void mps_frame_deliver(struct sta_info *sta, int n_frames)
 | 
					static void mps_frame_deliver(struct sta_info *sta, int n_frames)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ieee80211_sub_if_data *sdata = sta->sdata;
 | 
						struct ieee80211_local *local = sta->sdata->local;
 | 
				
			||||||
	struct ieee80211_local *local = sdata->local;
 | 
					 | 
				
			||||||
	int ac;
 | 
						int ac;
 | 
				
			||||||
	struct sk_buff_head frames;
 | 
						struct sk_buff_head frames;
 | 
				
			||||||
	struct sk_buff *skb;
 | 
						struct sk_buff *skb;
 | 
				
			||||||
| 
						 | 
					@ -558,10 +560,10 @@ void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * ieee80211_mps_frame_release - release buffered frames in response to beacon
 | 
					 * ieee80211_mps_frame_release - release frames buffered due to mesh power save
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @sta: mesh STA
 | 
					 * @sta: mesh STA
 | 
				
			||||||
 * @elems: beacon IEs
 | 
					 * @elems: IEs of beacon or probe response
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * For peers if we have individually-addressed frames buffered or the peer
 | 
					 * For peers if we have individually-addressed frames buffered or the peer
 | 
				
			||||||
 * indicates buffered frames, send a corresponding MPSP trigger frame. Since
 | 
					 * indicates buffered frames, send a corresponding MPSP trigger frame. Since
 | 
				
			||||||
| 
						 | 
					@ -588,6 +590,7 @@ void ieee80211_mps_frame_release(struct sta_info *sta,
 | 
				
			||||||
	    (!elems->awake_window || !le16_to_cpu(*elems->awake_window)))
 | 
						    (!elems->awake_window || !le16_to_cpu(*elems->awake_window)))
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!test_sta_flag(sta, WLAN_STA_MPSP_OWNER))
 | 
				
			||||||
		for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
 | 
							for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
 | 
				
			||||||
			buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) +
 | 
								buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) +
 | 
				
			||||||
					skb_queue_len(&sta->tx_filtered[ac]);
 | 
										skb_queue_len(&sta->tx_filtered[ac]);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -958,9 +958,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
	struct cfg80211_bss *cbss = ifmgd->associated;
 | 
						struct cfg80211_bss *cbss = ifmgd->associated;
 | 
				
			||||||
	struct ieee80211_chanctx *chanctx;
 | 
						struct ieee80211_chanctx *chanctx;
 | 
				
			||||||
	enum ieee80211_band current_band;
 | 
						enum ieee80211_band current_band;
 | 
				
			||||||
	u8 count;
 | 
						struct ieee80211_csa_ie csa_ie;
 | 
				
			||||||
	u8 mode;
 | 
					 | 
				
			||||||
	struct cfg80211_chan_def new_chandef = {};
 | 
					 | 
				
			||||||
	int res;
 | 
						int res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sdata_assert_lock(sdata);
 | 
						sdata_assert_lock(sdata);
 | 
				
			||||||
| 
						 | 
					@ -976,24 +974,24 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	current_band = cbss->channel->band;
 | 
						current_band = cbss->channel->band;
 | 
				
			||||||
 | 
						memset(&csa_ie, 0, sizeof(csa_ie));
 | 
				
			||||||
	res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
 | 
						res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
 | 
				
			||||||
					   ifmgd->flags,
 | 
										   ifmgd->flags,
 | 
				
			||||||
					   ifmgd->associated->bssid, &count,
 | 
										   ifmgd->associated->bssid, &csa_ie);
 | 
				
			||||||
					   &mode, &new_chandef);
 | 
					 | 
				
			||||||
	if (res	< 0)
 | 
						if (res	< 0)
 | 
				
			||||||
		ieee80211_queue_work(&local->hw,
 | 
							ieee80211_queue_work(&local->hw,
 | 
				
			||||||
				     &ifmgd->csa_connection_drop_work);
 | 
									     &ifmgd->csa_connection_drop_work);
 | 
				
			||||||
	if (res)
 | 
						if (res)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
 | 
						if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef,
 | 
				
			||||||
				     IEEE80211_CHAN_DISABLED)) {
 | 
									     IEEE80211_CHAN_DISABLED)) {
 | 
				
			||||||
		sdata_info(sdata,
 | 
							sdata_info(sdata,
 | 
				
			||||||
			   "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
 | 
								   "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
 | 
				
			||||||
			   ifmgd->associated->bssid,
 | 
								   ifmgd->associated->bssid,
 | 
				
			||||||
			   new_chandef.chan->center_freq,
 | 
								   csa_ie.chandef.chan->center_freq,
 | 
				
			||||||
			   new_chandef.width, new_chandef.center_freq1,
 | 
								   csa_ie.chandef.width, csa_ie.chandef.center_freq1,
 | 
				
			||||||
			   new_chandef.center_freq2);
 | 
								   csa_ie.chandef.center_freq2);
 | 
				
			||||||
		ieee80211_queue_work(&local->hw,
 | 
							ieee80211_queue_work(&local->hw,
 | 
				
			||||||
				     &ifmgd->csa_connection_drop_work);
 | 
									     &ifmgd->csa_connection_drop_work);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -1037,9 +1035,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	mutex_unlock(&local->chanctx_mtx);
 | 
						mutex_unlock(&local->chanctx_mtx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local->csa_chandef = new_chandef;
 | 
						local->csa_chandef = csa_ie.chandef;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (mode)
 | 
						if (csa_ie.mode)
 | 
				
			||||||
		ieee80211_stop_queues_by_reason(&local->hw,
 | 
							ieee80211_stop_queues_by_reason(&local->hw,
 | 
				
			||||||
				IEEE80211_MAX_QUEUE_MAP,
 | 
									IEEE80211_MAX_QUEUE_MAP,
 | 
				
			||||||
				IEEE80211_QUEUE_STOP_REASON_CSA);
 | 
									IEEE80211_QUEUE_STOP_REASON_CSA);
 | 
				
			||||||
| 
						 | 
					@ -1048,9 +1046,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
		/* use driver's channel switch callback */
 | 
							/* use driver's channel switch callback */
 | 
				
			||||||
		struct ieee80211_channel_switch ch_switch = {
 | 
							struct ieee80211_channel_switch ch_switch = {
 | 
				
			||||||
			.timestamp = timestamp,
 | 
								.timestamp = timestamp,
 | 
				
			||||||
			.block_tx = mode,
 | 
								.block_tx = csa_ie.mode,
 | 
				
			||||||
			.chandef = new_chandef,
 | 
								.chandef = csa_ie.chandef,
 | 
				
			||||||
			.count = count,
 | 
								.count = csa_ie.count,
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		drv_channel_switch(local, &ch_switch);
 | 
							drv_channel_switch(local, &ch_switch);
 | 
				
			||||||
| 
						 | 
					@ -1058,11 +1056,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* channel switch handled in software */
 | 
						/* channel switch handled in software */
 | 
				
			||||||
	if (count <= 1)
 | 
						if (csa_ie.count <= 1)
 | 
				
			||||||
		ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
 | 
							ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		mod_timer(&ifmgd->chswitch_timer,
 | 
							mod_timer(&ifmgd->chswitch_timer,
 | 
				
			||||||
			  TU_TO_EXP_TIME(count * cbss->beacon_interval));
 | 
								  TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
 | 
					static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
| 
						 | 
					@ -3500,7 +3498,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
 | 
				
			||||||
		  ieee80211_beacon_connection_loss_work);
 | 
							  ieee80211_beacon_connection_loss_work);
 | 
				
			||||||
	INIT_WORK(&ifmgd->csa_connection_drop_work,
 | 
						INIT_WORK(&ifmgd->csa_connection_drop_work,
 | 
				
			||||||
		  ieee80211_csa_connection_drop_work);
 | 
							  ieee80211_csa_connection_drop_work);
 | 
				
			||||||
	INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work);
 | 
						INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);
 | 
				
			||||||
	setup_timer(&ifmgd->timer, ieee80211_sta_timer,
 | 
						setup_timer(&ifmgd->timer, ieee80211_sta_timer,
 | 
				
			||||||
		    (unsigned long) sdata);
 | 
							    (unsigned long) sdata);
 | 
				
			||||||
	setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
 | 
						setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2593,13 +2593,16 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (sdata->vif.type != NL80211_IFTYPE_STATION &&
 | 
								if (sdata->vif.type != NL80211_IFTYPE_STATION &&
 | 
				
			||||||
			    sdata->vif.type != NL80211_IFTYPE_ADHOC)
 | 
								    sdata->vif.type != NL80211_IFTYPE_ADHOC &&
 | 
				
			||||||
 | 
								    sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (sdata->vif.type == NL80211_IFTYPE_STATION)
 | 
								if (sdata->vif.type == NL80211_IFTYPE_STATION)
 | 
				
			||||||
				bssid = sdata->u.mgd.bssid;
 | 
									bssid = sdata->u.mgd.bssid;
 | 
				
			||||||
			else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
 | 
								else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
 | 
				
			||||||
				bssid = sdata->u.ibss.bssid;
 | 
									bssid = sdata->u.ibss.bssid;
 | 
				
			||||||
 | 
								else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
 | 
				
			||||||
 | 
									bssid = mgmt->sa;
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,8 +24,8 @@
 | 
				
			||||||
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
 | 
					int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
				 struct ieee802_11_elems *elems, bool beacon,
 | 
									 struct ieee802_11_elems *elems, bool beacon,
 | 
				
			||||||
				 enum ieee80211_band current_band,
 | 
									 enum ieee80211_band current_band,
 | 
				
			||||||
				 u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
 | 
									 u32 sta_flags, u8 *bssid,
 | 
				
			||||||
				 struct cfg80211_chan_def *new_chandef)
 | 
									 struct ieee80211_csa_ie *csa_ie)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	enum ieee80211_band new_band;
 | 
						enum ieee80211_band new_band;
 | 
				
			||||||
	int new_freq;
 | 
						int new_freq;
 | 
				
			||||||
| 
						 | 
					@ -62,18 +62,24 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
			return -EINVAL;
 | 
								return -EINVAL;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		new_chan_no = elems->ext_chansw_ie->new_ch_num;
 | 
							new_chan_no = elems->ext_chansw_ie->new_ch_num;
 | 
				
			||||||
		*count = elems->ext_chansw_ie->count;
 | 
							csa_ie->count = elems->ext_chansw_ie->count;
 | 
				
			||||||
		*mode = elems->ext_chansw_ie->mode;
 | 
							csa_ie->mode = elems->ext_chansw_ie->mode;
 | 
				
			||||||
	} else if (elems->ch_switch_ie) {
 | 
						} else if (elems->ch_switch_ie) {
 | 
				
			||||||
		new_band = current_band;
 | 
							new_band = current_band;
 | 
				
			||||||
		new_chan_no = elems->ch_switch_ie->new_ch_num;
 | 
							new_chan_no = elems->ch_switch_ie->new_ch_num;
 | 
				
			||||||
		*count = elems->ch_switch_ie->count;
 | 
							csa_ie->count = elems->ch_switch_ie->count;
 | 
				
			||||||
		*mode = elems->ch_switch_ie->mode;
 | 
							csa_ie->mode = elems->ch_switch_ie->mode;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		/* nothing here we understand */
 | 
							/* nothing here we understand */
 | 
				
			||||||
		return 1;
 | 
							return 1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Mesh Channel Switch Parameters Element */
 | 
				
			||||||
 | 
						if (elems->mesh_chansw_params_ie) {
 | 
				
			||||||
 | 
							csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl;
 | 
				
			||||||
 | 
							csa_ie->mode = elems->mesh_chansw_params_ie->mesh_flags;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
 | 
						new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
 | 
				
			||||||
	new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
 | 
						new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
 | 
				
			||||||
	if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
 | 
						if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
 | 
				
			||||||
| 
						 | 
					@ -103,25 +109,26 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		/* secondary_channel_offset was present but is invalid */
 | 
							/* secondary_channel_offset was present but is invalid */
 | 
				
			||||||
	case IEEE80211_HT_PARAM_CHA_SEC_NONE:
 | 
						case IEEE80211_HT_PARAM_CHA_SEC_NONE:
 | 
				
			||||||
		cfg80211_chandef_create(new_chandef, new_chan,
 | 
							cfg80211_chandef_create(&csa_ie->chandef, new_chan,
 | 
				
			||||||
					NL80211_CHAN_HT20);
 | 
										NL80211_CHAN_HT20);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
 | 
						case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
 | 
				
			||||||
		cfg80211_chandef_create(new_chandef, new_chan,
 | 
							cfg80211_chandef_create(&csa_ie->chandef, new_chan,
 | 
				
			||||||
					NL80211_CHAN_HT40PLUS);
 | 
										NL80211_CHAN_HT40PLUS);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
 | 
						case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
 | 
				
			||||||
		cfg80211_chandef_create(new_chandef, new_chan,
 | 
							cfg80211_chandef_create(&csa_ie->chandef, new_chan,
 | 
				
			||||||
					NL80211_CHAN_HT40MINUS);
 | 
										NL80211_CHAN_HT40MINUS);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case -1:
 | 
						case -1:
 | 
				
			||||||
		cfg80211_chandef_create(new_chandef, new_chan,
 | 
							cfg80211_chandef_create(&csa_ie->chandef, new_chan,
 | 
				
			||||||
					NL80211_CHAN_NO_HT);
 | 
										NL80211_CHAN_NO_HT);
 | 
				
			||||||
		/* keep width for 5/10 MHz channels */
 | 
							/* keep width for 5/10 MHz channels */
 | 
				
			||||||
		switch (sdata->vif.bss_conf.chandef.width) {
 | 
							switch (sdata->vif.bss_conf.chandef.width) {
 | 
				
			||||||
		case NL80211_CHAN_WIDTH_5:
 | 
							case NL80211_CHAN_WIDTH_5:
 | 
				
			||||||
		case NL80211_CHAN_WIDTH_10:
 | 
							case NL80211_CHAN_WIDTH_10:
 | 
				
			||||||
			new_chandef->width = sdata->vif.bss_conf.chandef.width;
 | 
								csa_ie->chandef.width =
 | 
				
			||||||
 | 
									sdata->vif.bss_conf.chandef.width;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
| 
						 | 
					@ -171,13 +178,13 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
	/* if VHT data is there validate & use it */
 | 
						/* if VHT data is there validate & use it */
 | 
				
			||||||
	if (new_vht_chandef.chan) {
 | 
						if (new_vht_chandef.chan) {
 | 
				
			||||||
		if (!cfg80211_chandef_compatible(&new_vht_chandef,
 | 
							if (!cfg80211_chandef_compatible(&new_vht_chandef,
 | 
				
			||||||
						 new_chandef)) {
 | 
											 &csa_ie->chandef)) {
 | 
				
			||||||
			sdata_info(sdata,
 | 
								sdata_info(sdata,
 | 
				
			||||||
				   "BSS %pM: CSA has inconsistent channel data, disconnecting\n",
 | 
									   "BSS %pM: CSA has inconsistent channel data, disconnecting\n",
 | 
				
			||||||
				   bssid);
 | 
									   bssid);
 | 
				
			||||||
			return -EINVAL;
 | 
								return -EINVAL;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		*new_chandef = new_vht_chandef;
 | 
							csa_ie->chandef = new_vht_chandef;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -385,6 +385,30 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
		sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
 | 
							sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sta->sta.smps_mode = IEEE80211_SMPS_OFF;
 | 
						sta->sta.smps_mode = IEEE80211_SMPS_OFF;
 | 
				
			||||||
 | 
						if (sdata->vif.type == NL80211_IFTYPE_AP ||
 | 
				
			||||||
 | 
						    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
 | 
				
			||||||
 | 
							struct ieee80211_supported_band *sband =
 | 
				
			||||||
 | 
								local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
 | 
				
			||||||
 | 
							u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
 | 
				
			||||||
 | 
									IEEE80211_HT_CAP_SM_PS_SHIFT;
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Assume that hostapd advertises our caps in the beacon and
 | 
				
			||||||
 | 
							 * this is the known_smps_mode for a station that just assciated
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							switch (smps) {
 | 
				
			||||||
 | 
							case WLAN_HT_SMPS_CONTROL_DISABLED:
 | 
				
			||||||
 | 
								sta->known_smps_mode = IEEE80211_SMPS_OFF;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case WLAN_HT_SMPS_CONTROL_STATIC:
 | 
				
			||||||
 | 
								sta->known_smps_mode = IEEE80211_SMPS_STATIC;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case WLAN_HT_SMPS_CONTROL_DYNAMIC:
 | 
				
			||||||
 | 
								sta->known_smps_mode = IEEE80211_SMPS_DYNAMIC;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								WARN_ON(1);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
 | 
						sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1069,6 +1093,19 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta);
 | 
						ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* This station just woke up and isn't aware of our SMPS state */
 | 
				
			||||||
 | 
						if (!ieee80211_smps_is_restrictive(sta->known_smps_mode,
 | 
				
			||||||
 | 
										   sdata->smps_mode) &&
 | 
				
			||||||
 | 
						    sta->known_smps_mode != sdata->bss->req_smps &&
 | 
				
			||||||
 | 
						    sta_info_tx_streams(sta) != 1) {
 | 
				
			||||||
 | 
							ht_dbg(sdata,
 | 
				
			||||||
 | 
							       "%pM just woke up and MIMO capable - update SMPS\n",
 | 
				
			||||||
 | 
							       sta->sta.addr);
 | 
				
			||||||
 | 
							ieee80211_send_smps_action(sdata, sdata->bss->req_smps,
 | 
				
			||||||
 | 
										   sta->sta.addr,
 | 
				
			||||||
 | 
										   sdata->vif.bss_conf.bssid);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local->total_ps_buffered -= buffered;
 | 
						local->total_ps_buffered -= buffered;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sta_info_recalc_tim(sta);
 | 
						sta_info_recalc_tim(sta);
 | 
				
			||||||
| 
						 | 
					@ -1520,3 +1557,38 @@ int sta_info_move_state(struct sta_info *sta,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u8 sta_info_tx_streams(struct sta_info *sta)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ieee80211_sta_ht_cap *ht_cap = &sta->sta.ht_cap;
 | 
				
			||||||
 | 
						u8 rx_streams;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!sta->sta.ht_cap.ht_supported)
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sta->sta.vht_cap.vht_supported) {
 | 
				
			||||||
 | 
							int i;
 | 
				
			||||||
 | 
							u16 tx_mcs_map =
 | 
				
			||||||
 | 
								le16_to_cpu(sta->sta.vht_cap.vht_mcs.tx_mcs_map);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (i = 7; i >= 0; i--)
 | 
				
			||||||
 | 
								if ((tx_mcs_map & (0x3 << (i * 2))) !=
 | 
				
			||||||
 | 
								    IEEE80211_VHT_MCS_NOT_SUPPORTED)
 | 
				
			||||||
 | 
									return i + 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ht_cap->mcs.rx_mask[3])
 | 
				
			||||||
 | 
							rx_streams = 4;
 | 
				
			||||||
 | 
						else if (ht_cap->mcs.rx_mask[2])
 | 
				
			||||||
 | 
							rx_streams = 3;
 | 
				
			||||||
 | 
						else if (ht_cap->mcs.rx_mask[1])
 | 
				
			||||||
 | 
							rx_streams = 2;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							rx_streams = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!(ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_RX_DIFF))
 | 
				
			||||||
 | 
							return rx_streams;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
 | 
				
			||||||
 | 
								>> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -301,6 +301,8 @@ struct sta_ampdu_mlme {
 | 
				
			||||||
 * @chains: chains ever used for RX from this station
 | 
					 * @chains: chains ever used for RX from this station
 | 
				
			||||||
 * @chain_signal_last: last signal (per chain)
 | 
					 * @chain_signal_last: last signal (per chain)
 | 
				
			||||||
 * @chain_signal_avg: signal average (per chain)
 | 
					 * @chain_signal_avg: signal average (per chain)
 | 
				
			||||||
 | 
					 * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for
 | 
				
			||||||
 | 
					 *	AP only.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct sta_info {
 | 
					struct sta_info {
 | 
				
			||||||
	/* General information, mostly static */
 | 
						/* General information, mostly static */
 | 
				
			||||||
| 
						 | 
					@ -411,6 +413,8 @@ struct sta_info {
 | 
				
			||||||
	unsigned int lost_packets;
 | 
						unsigned int lost_packets;
 | 
				
			||||||
	unsigned int beacon_loss_count;
 | 
						unsigned int beacon_loss_count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enum ieee80211_smps_mode known_smps_mode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* keep last! */
 | 
						/* keep last! */
 | 
				
			||||||
	struct ieee80211_sta sta;
 | 
						struct ieee80211_sta sta;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -613,6 +617,7 @@ void sta_set_rate_info_rx(struct sta_info *sta,
 | 
				
			||||||
			  struct rate_info *rinfo);
 | 
								  struct rate_info *rinfo);
 | 
				
			||||||
void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
 | 
					void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
			  unsigned long exp_time);
 | 
								  unsigned long exp_time);
 | 
				
			||||||
 | 
					u8 sta_info_tx_streams(struct sta_info *sta);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
 | 
					void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
 | 
				
			||||||
void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
 | 
					void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -194,8 +194,23 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
 | 
				
			||||||
	if (ieee80211_is_action(mgmt->frame_control) &&
 | 
						if (ieee80211_is_action(mgmt->frame_control) &&
 | 
				
			||||||
	    mgmt->u.action.category == WLAN_CATEGORY_HT &&
 | 
						    mgmt->u.action.category == WLAN_CATEGORY_HT &&
 | 
				
			||||||
	    mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS &&
 | 
						    mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS &&
 | 
				
			||||||
	    sdata->vif.type == NL80211_IFTYPE_STATION &&
 | 
					 | 
				
			||||||
	    ieee80211_sdata_running(sdata)) {
 | 
						    ieee80211_sdata_running(sdata)) {
 | 
				
			||||||
 | 
							enum ieee80211_smps_mode smps_mode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch (mgmt->u.action.u.ht_smps.smps_control) {
 | 
				
			||||||
 | 
							case WLAN_HT_SMPS_CONTROL_DYNAMIC:
 | 
				
			||||||
 | 
								smps_mode = IEEE80211_SMPS_DYNAMIC;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case WLAN_HT_SMPS_CONTROL_STATIC:
 | 
				
			||||||
 | 
								smps_mode = IEEE80211_SMPS_STATIC;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case WLAN_HT_SMPS_CONTROL_DISABLED:
 | 
				
			||||||
 | 
							default: /* shouldn't happen since we don't send that */
 | 
				
			||||||
 | 
								smps_mode = IEEE80211_SMPS_OFF;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (sdata->vif.type == NL80211_IFTYPE_STATION) {
 | 
				
			||||||
			/*
 | 
								/*
 | 
				
			||||||
			 * This update looks racy, but isn't -- if we come
 | 
								 * This update looks racy, but isn't -- if we come
 | 
				
			||||||
			 * here we've definitely got a station that we're
 | 
								 * here we've definitely got a station that we're
 | 
				
			||||||
| 
						 | 
					@ -203,20 +218,12 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
 | 
				
			||||||
			 * only be the AP. And the only other place updating
 | 
								 * only be the AP. And the only other place updating
 | 
				
			||||||
			 * this variable in managed mode is before association.
 | 
								 * this variable in managed mode is before association.
 | 
				
			||||||
			 */
 | 
								 */
 | 
				
			||||||
		switch (mgmt->u.action.u.ht_smps.smps_control) {
 | 
								sdata->smps_mode = smps_mode;
 | 
				
			||||||
		case WLAN_HT_SMPS_CONTROL_DYNAMIC:
 | 
					 | 
				
			||||||
			sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case WLAN_HT_SMPS_CONTROL_STATIC:
 | 
					 | 
				
			||||||
			sdata->smps_mode = IEEE80211_SMPS_STATIC;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case WLAN_HT_SMPS_CONTROL_DISABLED:
 | 
					 | 
				
			||||||
		default: /* shouldn't happen since we don't send that */
 | 
					 | 
				
			||||||
			sdata->smps_mode = IEEE80211_SMPS_OFF;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
 | 
								ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
 | 
				
			||||||
 | 
							} else if (sdata->vif.type == NL80211_IFTYPE_AP ||
 | 
				
			||||||
 | 
								   sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
 | 
				
			||||||
 | 
								sta->known_smps_mode = smps_mode;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1367,6 +1367,35 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
 | 
				
			||||||
 | 
								      struct ieee80211_vif *vif, struct sk_buff *skb,
 | 
				
			||||||
 | 
								      int band, struct ieee80211_sta **sta)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
 | 
				
			||||||
 | 
						struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 | 
				
			||||||
 | 
						struct ieee80211_tx_data tx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						info->band = band;
 | 
				
			||||||
 | 
						info->control.vif = vif;
 | 
				
			||||||
 | 
						info->hw_queue = vif->hw_queue[skb_get_queue_mapping(skb)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (invoke_tx_handlers(&tx))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sta) {
 | 
				
			||||||
 | 
							if (tx.sta)
 | 
				
			||||||
 | 
								*sta = &tx.sta->sta;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								*sta = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Returns false if the frame couldn't be transmitted but was queued instead.
 | 
					 * Returns false if the frame couldn't be transmitted but was queued instead.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -2370,6 +2399,10 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
		beacon_data = beacon->head;
 | 
							beacon_data = beacon->head;
 | 
				
			||||||
		beacon_data_len = beacon->head_len;
 | 
							beacon_data_len = beacon->head_len;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
						case NL80211_IFTYPE_MESH_POINT:
 | 
				
			||||||
 | 
							beacon_data = beacon->head;
 | 
				
			||||||
 | 
							beacon_data_len = beacon->head_len;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -2424,6 +2457,15 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
 | 
				
			||||||
		if (!beacon)
 | 
							if (!beacon)
 | 
				
			||||||
			goto out;
 | 
								goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							beacon_data = beacon->head;
 | 
				
			||||||
 | 
							beacon_data_len = beacon->head_len;
 | 
				
			||||||
 | 
						} else if (vif->type == NL80211_IFTYPE_MESH_POINT) {
 | 
				
			||||||
 | 
							struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							beacon = rcu_dereference(ifmsh->beacon);
 | 
				
			||||||
 | 
							if (!beacon)
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		beacon_data = beacon->head;
 | 
							beacon_data = beacon->head;
 | 
				
			||||||
		beacon_data_len = beacon->head_len;
 | 
							beacon_data_len = beacon->head_len;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
| 
						 | 
					@ -2531,6 +2573,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
 | 
				
			||||||
		if (!bcn)
 | 
							if (!bcn)
 | 
				
			||||||
			goto out;
 | 
								goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (sdata->vif.csa_active)
 | 
				
			||||||
 | 
								ieee80211_update_csa(sdata, bcn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (ifmsh->sync_ops)
 | 
							if (ifmsh->sync_ops)
 | 
				
			||||||
			ifmsh->sync_ops->adjust_tbtt(
 | 
								ifmsh->sync_ops->adjust_tbtt(
 | 
				
			||||||
						sdata);
 | 
											sdata);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -300,9 +300,6 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
 | 
				
			||||||
		if (!sdata->dev)
 | 
							if (!sdata->dev)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE &&
 | 
							if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE &&
 | 
				
			||||||
		    local->queue_stop_reasons[sdata->vif.cab_queue] != 0)
 | 
							    local->queue_stop_reasons[sdata->vif.cab_queue] != 0)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
| 
						 | 
					@ -743,6 +740,7 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
 | 
				
			||||||
		case WLAN_EID_TIMEOUT_INTERVAL:
 | 
							case WLAN_EID_TIMEOUT_INTERVAL:
 | 
				
			||||||
		case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
 | 
							case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
 | 
				
			||||||
		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
 | 
							case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
 | 
				
			||||||
 | 
							case WLAN_EID_CHAN_SWITCH_PARAM:
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
 | 
							 * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
 | 
				
			||||||
		 * that if the content gets bigger it might be needed more than once
 | 
							 * that if the content gets bigger it might be needed more than once
 | 
				
			||||||
| 
						 | 
					@ -908,6 +906,14 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			elems->sec_chan_offs = (void *)pos;
 | 
								elems->sec_chan_offs = (void *)pos;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
							case WLAN_EID_CHAN_SWITCH_PARAM:
 | 
				
			||||||
 | 
								if (elen !=
 | 
				
			||||||
 | 
								    sizeof(*elems->mesh_chansw_params_ie)) {
 | 
				
			||||||
 | 
									elem_parse_failed = true;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								elems->mesh_chansw_params_ie = (void *)pos;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
		case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
 | 
							case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
 | 
				
			||||||
			if (!action ||
 | 
								if (!action ||
 | 
				
			||||||
			    elen != sizeof(*elems->wide_bw_chansw_ie)) {
 | 
								    elen != sizeof(*elems->wide_bw_chansw_ie)) {
 | 
				
			||||||
| 
						 | 
					@ -2354,3 +2360,115 @@ u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Returns true if smps_mode_new is strictly more restrictive than
 | 
				
			||||||
 | 
					 * smps_mode_old.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
 | 
				
			||||||
 | 
									   enum ieee80211_smps_mode smps_mode_new)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC ||
 | 
				
			||||||
 | 
								 smps_mode_new == IEEE80211_SMPS_AUTOMATIC))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (smps_mode_old) {
 | 
				
			||||||
 | 
						case IEEE80211_SMPS_STATIC:
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						case IEEE80211_SMPS_DYNAMIC:
 | 
				
			||||||
 | 
							return smps_mode_new == IEEE80211_SMPS_STATIC;
 | 
				
			||||||
 | 
						case IEEE80211_SMPS_OFF:
 | 
				
			||||||
 | 
							return smps_mode_new != IEEE80211_SMPS_OFF;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							WARN_ON(1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
 | 
				
			||||||
 | 
								      struct cfg80211_csa_settings *csa_settings)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sk_buff *skb;
 | 
				
			||||||
 | 
						struct ieee80211_mgmt *mgmt;
 | 
				
			||||||
 | 
						struct ieee80211_local *local = sdata->local;
 | 
				
			||||||
 | 
						int freq;
 | 
				
			||||||
 | 
						int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
 | 
				
			||||||
 | 
								       sizeof(mgmt->u.action.u.chan_switch);
 | 
				
			||||||
 | 
						u8 *pos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
 | 
				
			||||||
 | 
						    sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
 | 
				
			||||||
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						skb = dev_alloc_skb(local->tx_headroom + hdr_len +
 | 
				
			||||||
 | 
								    5 + /* channel switch announcement element */
 | 
				
			||||||
 | 
								    3 + /* secondary channel offset element */
 | 
				
			||||||
 | 
								    8); /* mesh channel switch parameters element */
 | 
				
			||||||
 | 
						if (!skb)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						skb_reserve(skb, local->tx_headroom);
 | 
				
			||||||
 | 
						mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
 | 
				
			||||||
 | 
						memset(mgmt, 0, hdr_len);
 | 
				
			||||||
 | 
						mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
 | 
				
			||||||
 | 
										  IEEE80211_STYPE_ACTION);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						eth_broadcast_addr(mgmt->da);
 | 
				
			||||||
 | 
						memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
 | 
				
			||||||
 | 
						if (ieee80211_vif_is_mesh(&sdata->vif)) {
 | 
				
			||||||
 | 
							memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
 | 
				
			||||||
 | 
							memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
 | 
				
			||||||
 | 
						mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
 | 
				
			||||||
 | 
						pos = skb_put(skb, 5);
 | 
				
			||||||
 | 
						*pos++ = WLAN_EID_CHANNEL_SWITCH;			/* EID */
 | 
				
			||||||
 | 
						*pos++ = 3;						/* IE length */
 | 
				
			||||||
 | 
						*pos++ = csa_settings->block_tx ? 1 : 0;		/* CSA mode */
 | 
				
			||||||
 | 
						freq = csa_settings->chandef.chan->center_freq;
 | 
				
			||||||
 | 
						*pos++ = ieee80211_frequency_to_channel(freq);		/* channel */
 | 
				
			||||||
 | 
						*pos++ = csa_settings->count;				/* count */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
 | 
				
			||||||
 | 
							enum nl80211_channel_type ch_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							skb_put(skb, 3);
 | 
				
			||||||
 | 
							*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;	/* EID */
 | 
				
			||||||
 | 
							*pos++ = 1;					/* IE length */
 | 
				
			||||||
 | 
							ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
 | 
				
			||||||
 | 
							if (ch_type == NL80211_CHAN_HT40PLUS)
 | 
				
			||||||
 | 
								*pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								*pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ieee80211_vif_is_mesh(&sdata->vif)) {
 | 
				
			||||||
 | 
							struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 | 
				
			||||||
 | 
							__le16 pre_value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							skb_put(skb, 8);
 | 
				
			||||||
 | 
							*pos++ = WLAN_EID_CHAN_SWITCH_PARAM;		/* EID */
 | 
				
			||||||
 | 
							*pos++ = 6;					/* IE length */
 | 
				
			||||||
 | 
							*pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL;	/* Mesh TTL */
 | 
				
			||||||
 | 
							*pos = 0x00;	/* Mesh Flag: Tx Restrict, Initiator, Reason */
 | 
				
			||||||
 | 
							*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
 | 
				
			||||||
 | 
							*pos++ |= csa_settings->block_tx ?
 | 
				
			||||||
 | 
								  WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
 | 
				
			||||||
 | 
							put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */
 | 
				
			||||||
 | 
							pos += 2;
 | 
				
			||||||
 | 
							if (!ifmsh->pre_value)
 | 
				
			||||||
 | 
								ifmsh->pre_value = 1;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								ifmsh->pre_value++;
 | 
				
			||||||
 | 
							pre_value = cpu_to_le16(ifmsh->pre_value);
 | 
				
			||||||
 | 
							memcpy(pos, &pre_value, 2);		/* Precedence Value */
 | 
				
			||||||
 | 
							pos += 2;
 | 
				
			||||||
 | 
							ifmsh->chsw_init = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ieee80211_tx_skb(sdata, skb);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -301,22 +301,16 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
 | 
					static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad,
 | 
				
			||||||
				int encrypted)
 | 
									int encrypted)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	__le16 mask_fc;
 | 
						__le16 mask_fc;
 | 
				
			||||||
	int a4_included, mgmt;
 | 
						int a4_included, mgmt;
 | 
				
			||||||
	u8 qos_tid;
 | 
						u8 qos_tid;
 | 
				
			||||||
	u8 *b_0, *aad;
 | 
						u16 len_a;
 | 
				
			||||||
	u16 data_len, len_a;
 | 
					 | 
				
			||||||
	unsigned int hdrlen;
 | 
						unsigned int hdrlen;
 | 
				
			||||||
	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 | 
						struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	memset(scratch, 0, 6 * AES_BLOCK_SIZE);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	b_0 = scratch + 3 * AES_BLOCK_SIZE;
 | 
					 | 
				
			||||||
	aad = scratch + 4 * AES_BLOCK_SIZE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Mask FC: zero subtype b4 b5 b6 (if not mgmt)
 | 
						 * Mask FC: zero subtype b4 b5 b6 (if not mgmt)
 | 
				
			||||||
	 * Retry, PwrMgt, MoreData; set Protected
 | 
						 * Retry, PwrMgt, MoreData; set Protected
 | 
				
			||||||
| 
						 | 
					@ -338,20 +332,21 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		qos_tid = 0;
 | 
							qos_tid = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN;
 | 
						/* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
 | 
				
			||||||
	if (encrypted)
 | 
						 * mode authentication are not allowed to collide, yet both are derived
 | 
				
			||||||
		data_len -= IEEE80211_CCMP_MIC_LEN;
 | 
						 * from this vector b_0. We only set L := 1 here to indicate that the
 | 
				
			||||||
 | 
						 * data size can be represented in (L+1) bytes. The CCM layer will take
 | 
				
			||||||
 | 
						 * care of storing the data length in the top (L+1) bytes and setting
 | 
				
			||||||
 | 
						 * and clearing the other bits as is required to derive the two IVs.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						b_0[0] = 0x1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* First block, b_0 */
 | 
					 | 
				
			||||||
	b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */
 | 
					 | 
				
			||||||
	/* Nonce: Nonce Flags | A2 | PN
 | 
						/* Nonce: Nonce Flags | A2 | PN
 | 
				
			||||||
	 * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7)
 | 
						 * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7)
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	b_0[1] = qos_tid | (mgmt << 4);
 | 
						b_0[1] = qos_tid | (mgmt << 4);
 | 
				
			||||||
	memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
 | 
						memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
 | 
				
			||||||
	memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);
 | 
						memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);
 | 
				
			||||||
	/* l(m) */
 | 
					 | 
				
			||||||
	put_unaligned_be16(data_len, &b_0[14]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* AAD (extra authenticate-only data) / masked 802.11 header
 | 
						/* AAD (extra authenticate-only data) / masked 802.11 header
 | 
				
			||||||
	 * FC | A1 | A2 | A3 | SC | [A4] | [QC] */
 | 
						 * FC | A1 | A2 | A3 | SC | [A4] | [QC] */
 | 
				
			||||||
| 
						 | 
					@ -407,7 +402,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
 | 
				
			||||||
	u8 *pos;
 | 
						u8 *pos;
 | 
				
			||||||
	u8 pn[6];
 | 
						u8 pn[6];
 | 
				
			||||||
	u64 pn64;
 | 
						u64 pn64;
 | 
				
			||||||
	u8 scratch[6 * AES_BLOCK_SIZE];
 | 
						u8 aad[2 * AES_BLOCK_SIZE];
 | 
				
			||||||
 | 
						u8 b_0[AES_BLOCK_SIZE];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (info->control.hw_key &&
 | 
						if (info->control.hw_key &&
 | 
				
			||||||
	    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
 | 
						    !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
 | 
				
			||||||
| 
						 | 
					@ -460,9 +456,9 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pos += IEEE80211_CCMP_HDR_LEN;
 | 
						pos += IEEE80211_CCMP_HDR_LEN;
 | 
				
			||||||
	ccmp_special_blocks(skb, pn, scratch, 0);
 | 
						ccmp_special_blocks(skb, pn, b_0, aad, 0);
 | 
				
			||||||
	ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len,
 | 
						ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
 | 
				
			||||||
				  pos, skb_put(skb, IEEE80211_CCMP_MIC_LEN));
 | 
									  skb_put(skb, IEEE80211_CCMP_MIC_LEN));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -525,16 +521,16 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!(status->flag & RX_FLAG_DECRYPTED)) {
 | 
						if (!(status->flag & RX_FLAG_DECRYPTED)) {
 | 
				
			||||||
		u8 scratch[6 * AES_BLOCK_SIZE];
 | 
							u8 aad[2 * AES_BLOCK_SIZE];
 | 
				
			||||||
 | 
							u8 b_0[AES_BLOCK_SIZE];
 | 
				
			||||||
		/* hardware didn't decrypt/verify MIC */
 | 
							/* hardware didn't decrypt/verify MIC */
 | 
				
			||||||
		ccmp_special_blocks(skb, pn, scratch, 1);
 | 
							ccmp_special_blocks(skb, pn, b_0, aad, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (ieee80211_aes_ccm_decrypt(
 | 
							if (ieee80211_aes_ccm_decrypt(
 | 
				
			||||||
			    key->u.ccmp.tfm, scratch,
 | 
								    key->u.ccmp.tfm, b_0, aad,
 | 
				
			||||||
			    skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
 | 
								    skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
 | 
				
			||||||
			    data_len,
 | 
								    data_len,
 | 
				
			||||||
			    skb->data + skb->len - IEEE80211_CCMP_MIC_LEN,
 | 
								    skb->data + skb->len - IEEE80211_CCMP_MIC_LEN))
 | 
				
			||||||
			    skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN))
 | 
					 | 
				
			||||||
			return RX_DROP_UNUSABLE;
 | 
								return RX_DROP_UNUSABLE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,7 +36,7 @@ config RFKILL_REGULATOR
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config RFKILL_GPIO
 | 
					config RFKILL_GPIO
 | 
				
			||||||
	tristate "GPIO RFKILL driver"
 | 
						tristate "GPIO RFKILL driver"
 | 
				
			||||||
	depends on RFKILL && GPIOLIB && HAVE_CLK
 | 
						depends on RFKILL && GPIOLIB
 | 
				
			||||||
	default n
 | 
						default n
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  If you say yes here you get support of a generic gpio RFKILL
 | 
						  If you say yes here you get support of a generic gpio RFKILL
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,27 +24,23 @@
 | 
				
			||||||
#include <linux/platform_device.h>
 | 
					#include <linux/platform_device.h>
 | 
				
			||||||
#include <linux/clk.h>
 | 
					#include <linux/clk.h>
 | 
				
			||||||
#include <linux/slab.h>
 | 
					#include <linux/slab.h>
 | 
				
			||||||
 | 
					#include <linux/acpi.h>
 | 
				
			||||||
 | 
					#include <linux/acpi_gpio.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/rfkill-gpio.h>
 | 
					#include <linux/rfkill-gpio.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum rfkill_gpio_clk_state {
 | 
					 | 
				
			||||||
	UNSPECIFIED = 0,
 | 
					 | 
				
			||||||
	PWR_ENABLED,
 | 
					 | 
				
			||||||
	PWR_DISABLED
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define PWR_CLK_SET(_RF, _EN) \
 | 
					 | 
				
			||||||
	((_RF)->pwr_clk_enabled = (!(_EN) ? PWR_ENABLED : PWR_DISABLED))
 | 
					 | 
				
			||||||
#define PWR_CLK_ENABLED(_RF) ((_RF)->pwr_clk_enabled == PWR_ENABLED)
 | 
					 | 
				
			||||||
#define PWR_CLK_DISABLED(_RF) ((_RF)->pwr_clk_enabled != PWR_ENABLED)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct rfkill_gpio_data {
 | 
					struct rfkill_gpio_data {
 | 
				
			||||||
	struct rfkill_gpio_platform_data	*pdata;
 | 
						const char		*name;
 | 
				
			||||||
 | 
						enum rfkill_type	type;
 | 
				
			||||||
 | 
						int			reset_gpio;
 | 
				
			||||||
 | 
						int			shutdown_gpio;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct rfkill		*rfkill_dev;
 | 
						struct rfkill		*rfkill_dev;
 | 
				
			||||||
	char			*reset_name;
 | 
						char			*reset_name;
 | 
				
			||||||
	char			*shutdown_name;
 | 
						char			*shutdown_name;
 | 
				
			||||||
	enum rfkill_gpio_clk_state		pwr_clk_enabled;
 | 
						struct clk		*clk;
 | 
				
			||||||
	struct clk				*pwr_clk;
 | 
					
 | 
				
			||||||
 | 
						bool			clk_enabled;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int rfkill_gpio_set_power(void *data, bool blocked)
 | 
					static int rfkill_gpio_set_power(void *data, bool blocked)
 | 
				
			||||||
| 
						 | 
					@ -52,23 +48,22 @@ static int rfkill_gpio_set_power(void *data, bool blocked)
 | 
				
			||||||
	struct rfkill_gpio_data *rfkill = data;
 | 
						struct rfkill_gpio_data *rfkill = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (blocked) {
 | 
						if (blocked) {
 | 
				
			||||||
		if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
 | 
							if (gpio_is_valid(rfkill->shutdown_gpio))
 | 
				
			||||||
			gpio_direction_output(rfkill->pdata->shutdown_gpio, 0);
 | 
								gpio_set_value(rfkill->shutdown_gpio, 0);
 | 
				
			||||||
		if (gpio_is_valid(rfkill->pdata->reset_gpio))
 | 
							if (gpio_is_valid(rfkill->reset_gpio))
 | 
				
			||||||
			gpio_direction_output(rfkill->pdata->reset_gpio, 0);
 | 
								gpio_set_value(rfkill->reset_gpio, 0);
 | 
				
			||||||
		if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill))
 | 
							if (!IS_ERR(rfkill->clk) && rfkill->clk_enabled)
 | 
				
			||||||
			clk_disable(rfkill->pwr_clk);
 | 
								clk_disable(rfkill->clk);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if (rfkill->pwr_clk && PWR_CLK_DISABLED(rfkill))
 | 
							if (!IS_ERR(rfkill->clk) && !rfkill->clk_enabled)
 | 
				
			||||||
			clk_enable(rfkill->pwr_clk);
 | 
								clk_enable(rfkill->clk);
 | 
				
			||||||
		if (gpio_is_valid(rfkill->pdata->reset_gpio))
 | 
							if (gpio_is_valid(rfkill->reset_gpio))
 | 
				
			||||||
			gpio_direction_output(rfkill->pdata->reset_gpio, 1);
 | 
								gpio_set_value(rfkill->reset_gpio, 1);
 | 
				
			||||||
		if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
 | 
							if (gpio_is_valid(rfkill->shutdown_gpio))
 | 
				
			||||||
			gpio_direction_output(rfkill->pdata->shutdown_gpio, 1);
 | 
								gpio_set_value(rfkill->shutdown_gpio, 1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (rfkill->pwr_clk)
 | 
						rfkill->clk_enabled = blocked;
 | 
				
			||||||
		PWR_CLK_SET(rfkill, blocked);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -77,117 +72,112 @@ static const struct rfkill_ops rfkill_gpio_ops = {
 | 
				
			||||||
	.set_block = rfkill_gpio_set_power,
 | 
						.set_block = rfkill_gpio_set_power,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int rfkill_gpio_acpi_probe(struct device *dev,
 | 
				
			||||||
 | 
									  struct rfkill_gpio_data *rfkill)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct acpi_device_id *id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						id = acpi_match_device(dev->driver->acpi_match_table, dev);
 | 
				
			||||||
 | 
						if (!id)
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rfkill->name = dev_name(dev);
 | 
				
			||||||
 | 
						rfkill->type = (unsigned)id->driver_data;
 | 
				
			||||||
 | 
						rfkill->reset_gpio = acpi_get_gpio_by_index(dev, 0, NULL);
 | 
				
			||||||
 | 
						rfkill->shutdown_gpio = acpi_get_gpio_by_index(dev, 1, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int rfkill_gpio_probe(struct platform_device *pdev)
 | 
					static int rfkill_gpio_probe(struct platform_device *pdev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rfkill_gpio_data *rfkill;
 | 
					 | 
				
			||||||
	struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
 | 
						struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
 | 
				
			||||||
 | 
						struct rfkill_gpio_data *rfkill;
 | 
				
			||||||
 | 
						const char *clk_name = NULL;
 | 
				
			||||||
	int ret = 0;
 | 
						int ret = 0;
 | 
				
			||||||
	int len = 0;
 | 
						int len = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!pdata) {
 | 
						rfkill = devm_kzalloc(&pdev->dev, sizeof(*rfkill), GFP_KERNEL);
 | 
				
			||||||
		pr_warn("%s: No platform data specified\n", __func__);
 | 
						if (!rfkill)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ACPI_HANDLE(&pdev->dev)) {
 | 
				
			||||||
 | 
							ret = rfkill_gpio_acpi_probe(&pdev->dev, rfkill);
 | 
				
			||||||
 | 
							if (ret)
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
						} else if (pdata) {
 | 
				
			||||||
 | 
							clk_name = pdata->power_clk_name;
 | 
				
			||||||
 | 
							rfkill->name = pdata->name;
 | 
				
			||||||
 | 
							rfkill->type = pdata->type;
 | 
				
			||||||
 | 
							rfkill->reset_gpio = pdata->reset_gpio;
 | 
				
			||||||
 | 
							rfkill->shutdown_gpio = pdata->shutdown_gpio;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return -ENODEV;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* make sure at-least one of the GPIO is defined and that
 | 
						/* make sure at-least one of the GPIO is defined and that
 | 
				
			||||||
	 * a name is specified for this instance */
 | 
						 * a name is specified for this instance */
 | 
				
			||||||
	if (!pdata->name || (!gpio_is_valid(pdata->reset_gpio) &&
 | 
						if ((!gpio_is_valid(rfkill->reset_gpio) &&
 | 
				
			||||||
		!gpio_is_valid(pdata->shutdown_gpio))) {
 | 
						     !gpio_is_valid(rfkill->shutdown_gpio)) || !rfkill->name) {
 | 
				
			||||||
		pr_warn("%s: invalid platform data\n", __func__);
 | 
							pr_warn("%s: invalid platform data\n", __func__);
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL);
 | 
						if (pdata && pdata->gpio_runtime_setup) {
 | 
				
			||||||
	if (!rfkill)
 | 
					 | 
				
			||||||
		return -ENOMEM;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (pdata->gpio_runtime_setup) {
 | 
					 | 
				
			||||||
		ret = pdata->gpio_runtime_setup(pdev);
 | 
							ret = pdata->gpio_runtime_setup(pdev);
 | 
				
			||||||
		if (ret) {
 | 
							if (ret) {
 | 
				
			||||||
			pr_warn("%s: can't set up gpio\n", __func__);
 | 
								pr_warn("%s: can't set up gpio\n", __func__);
 | 
				
			||||||
			goto fail_alloc;
 | 
								return ret;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rfkill->pdata = pdata;
 | 
						len = strlen(rfkill->name);
 | 
				
			||||||
 | 
						rfkill->reset_name = devm_kzalloc(&pdev->dev, len + 7, GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!rfkill->reset_name)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	len = strlen(pdata->name);
 | 
						rfkill->shutdown_name = devm_kzalloc(&pdev->dev, len + 10, GFP_KERNEL);
 | 
				
			||||||
	rfkill->reset_name = kzalloc(len + 7, GFP_KERNEL);
 | 
						if (!rfkill->shutdown_name)
 | 
				
			||||||
	if (!rfkill->reset_name) {
 | 
							return -ENOMEM;
 | 
				
			||||||
		ret = -ENOMEM;
 | 
					 | 
				
			||||||
		goto fail_alloc;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rfkill->shutdown_name = kzalloc(len + 10, GFP_KERNEL);
 | 
						snprintf(rfkill->reset_name, len + 6 , "%s_reset", rfkill->name);
 | 
				
			||||||
	if (!rfkill->shutdown_name) {
 | 
						snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", rfkill->name);
 | 
				
			||||||
		ret = -ENOMEM;
 | 
					 | 
				
			||||||
		goto fail_reset_name;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	snprintf(rfkill->reset_name, len + 6 , "%s_reset", pdata->name);
 | 
						rfkill->clk = devm_clk_get(&pdev->dev, clk_name);
 | 
				
			||||||
	snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", pdata->name);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (pdata->power_clk_name) {
 | 
						if (gpio_is_valid(rfkill->reset_gpio)) {
 | 
				
			||||||
		rfkill->pwr_clk = clk_get(&pdev->dev, pdata->power_clk_name);
 | 
							ret = devm_gpio_request_one(&pdev->dev, rfkill->reset_gpio,
 | 
				
			||||||
		if (IS_ERR(rfkill->pwr_clk)) {
 | 
										    0, rfkill->reset_name);
 | 
				
			||||||
			pr_warn("%s: can't find pwr_clk.\n", __func__);
 | 
					 | 
				
			||||||
			ret = PTR_ERR(rfkill->pwr_clk);
 | 
					 | 
				
			||||||
			goto fail_shutdown_name;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (gpio_is_valid(pdata->reset_gpio)) {
 | 
					 | 
				
			||||||
		ret = gpio_request(pdata->reset_gpio, rfkill->reset_name);
 | 
					 | 
				
			||||||
		if (ret) {
 | 
							if (ret) {
 | 
				
			||||||
			pr_warn("%s: failed to get reset gpio.\n", __func__);
 | 
								pr_warn("%s: failed to get reset gpio.\n", __func__);
 | 
				
			||||||
			goto fail_clock;
 | 
								return ret;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (gpio_is_valid(pdata->shutdown_gpio)) {
 | 
						if (gpio_is_valid(rfkill->shutdown_gpio)) {
 | 
				
			||||||
		ret = gpio_request(pdata->shutdown_gpio, rfkill->shutdown_name);
 | 
							ret = devm_gpio_request_one(&pdev->dev, rfkill->shutdown_gpio,
 | 
				
			||||||
 | 
										    0, rfkill->shutdown_name);
 | 
				
			||||||
		if (ret) {
 | 
							if (ret) {
 | 
				
			||||||
			pr_warn("%s: failed to get shutdown gpio.\n", __func__);
 | 
								pr_warn("%s: failed to get shutdown gpio.\n", __func__);
 | 
				
			||||||
			goto fail_reset;
 | 
								return ret;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type,
 | 
						rfkill->rfkill_dev = rfkill_alloc(rfkill->name, &pdev->dev,
 | 
				
			||||||
					  &rfkill_gpio_ops, rfkill);
 | 
										  rfkill->type, &rfkill_gpio_ops,
 | 
				
			||||||
	if (!rfkill->rfkill_dev) {
 | 
										  rfkill);
 | 
				
			||||||
		ret = -ENOMEM;
 | 
						if (!rfkill->rfkill_dev)
 | 
				
			||||||
		goto fail_shutdown;
 | 
							return -ENOMEM;
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = rfkill_register(rfkill->rfkill_dev);
 | 
						ret = rfkill_register(rfkill->rfkill_dev);
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
		goto fail_rfkill;
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	platform_set_drvdata(pdev, rfkill);
 | 
						platform_set_drvdata(pdev, rfkill);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev_info(&pdev->dev, "%s device registered.\n", pdata->name);
 | 
						dev_info(&pdev->dev, "%s device registered.\n", rfkill->name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
fail_rfkill:
 | 
					 | 
				
			||||||
	rfkill_destroy(rfkill->rfkill_dev);
 | 
					 | 
				
			||||||
fail_shutdown:
 | 
					 | 
				
			||||||
	if (gpio_is_valid(pdata->shutdown_gpio))
 | 
					 | 
				
			||||||
		gpio_free(pdata->shutdown_gpio);
 | 
					 | 
				
			||||||
fail_reset:
 | 
					 | 
				
			||||||
	if (gpio_is_valid(pdata->reset_gpio))
 | 
					 | 
				
			||||||
		gpio_free(pdata->reset_gpio);
 | 
					 | 
				
			||||||
fail_clock:
 | 
					 | 
				
			||||||
	if (rfkill->pwr_clk)
 | 
					 | 
				
			||||||
		clk_put(rfkill->pwr_clk);
 | 
					 | 
				
			||||||
fail_shutdown_name:
 | 
					 | 
				
			||||||
	kfree(rfkill->shutdown_name);
 | 
					 | 
				
			||||||
fail_reset_name:
 | 
					 | 
				
			||||||
	kfree(rfkill->reset_name);
 | 
					 | 
				
			||||||
fail_alloc:
 | 
					 | 
				
			||||||
	kfree(rfkill);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return ret;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int rfkill_gpio_remove(struct platform_device *pdev)
 | 
					static int rfkill_gpio_remove(struct platform_device *pdev)
 | 
				
			||||||
| 
						 | 
					@ -195,31 +185,26 @@ static int rfkill_gpio_remove(struct platform_device *pdev)
 | 
				
			||||||
	struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev);
 | 
						struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev);
 | 
				
			||||||
	struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
 | 
						struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (pdata->gpio_runtime_close)
 | 
						if (pdata && pdata->gpio_runtime_close)
 | 
				
			||||||
		pdata->gpio_runtime_close(pdev);
 | 
							pdata->gpio_runtime_close(pdev);
 | 
				
			||||||
	rfkill_unregister(rfkill->rfkill_dev);
 | 
						rfkill_unregister(rfkill->rfkill_dev);
 | 
				
			||||||
	rfkill_destroy(rfkill->rfkill_dev);
 | 
						rfkill_destroy(rfkill->rfkill_dev);
 | 
				
			||||||
	if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
 | 
					 | 
				
			||||||
		gpio_free(rfkill->pdata->shutdown_gpio);
 | 
					 | 
				
			||||||
	if (gpio_is_valid(rfkill->pdata->reset_gpio))
 | 
					 | 
				
			||||||
		gpio_free(rfkill->pdata->reset_gpio);
 | 
					 | 
				
			||||||
	if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill))
 | 
					 | 
				
			||||||
		clk_disable(rfkill->pwr_clk);
 | 
					 | 
				
			||||||
	if (rfkill->pwr_clk)
 | 
					 | 
				
			||||||
		clk_put(rfkill->pwr_clk);
 | 
					 | 
				
			||||||
	kfree(rfkill->shutdown_name);
 | 
					 | 
				
			||||||
	kfree(rfkill->reset_name);
 | 
					 | 
				
			||||||
	kfree(rfkill);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct acpi_device_id rfkill_acpi_match[] = {
 | 
				
			||||||
 | 
						{ "BCM4752", RFKILL_TYPE_GPS },
 | 
				
			||||||
 | 
						{ },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct platform_driver rfkill_gpio_driver = {
 | 
					static struct platform_driver rfkill_gpio_driver = {
 | 
				
			||||||
	.probe = rfkill_gpio_probe,
 | 
						.probe = rfkill_gpio_probe,
 | 
				
			||||||
	.remove = rfkill_gpio_remove,
 | 
						.remove = rfkill_gpio_remove,
 | 
				
			||||||
	.driver = {
 | 
						.driver = {
 | 
				
			||||||
		.name = "rfkill_gpio",
 | 
							.name = "rfkill_gpio",
 | 
				
			||||||
		.owner = THIS_MODULE,
 | 
							.owner = THIS_MODULE,
 | 
				
			||||||
 | 
							.acpi_match_table = ACPI_PTR(rfkill_acpi_match),
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -504,7 +504,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
 | 
				
			||||||
	case NL80211_IFTYPE_ADHOC:
 | 
						case NL80211_IFTYPE_ADHOC:
 | 
				
			||||||
		if (wdev->current_bss) {
 | 
							if (wdev->current_bss) {
 | 
				
			||||||
			*chan = wdev->current_bss->pub.channel;
 | 
								*chan = wdev->current_bss->pub.channel;
 | 
				
			||||||
			*chanmode = wdev->ibss_fixed
 | 
								*chanmode = (wdev->ibss_fixed &&
 | 
				
			||||||
 | 
									     !wdev->ibss_dfs_possible)
 | 
				
			||||||
				  ? CHAN_MODE_SHARED
 | 
									  ? CHAN_MODE_SHARED
 | 
				
			||||||
				  : CHAN_MODE_EXCLUSIVE;
 | 
									  : CHAN_MODE_EXCLUSIVE;
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,6 +83,8 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 | 
				
			||||||
			 struct cfg80211_cached_keys *connkeys)
 | 
								 struct cfg80211_cached_keys *connkeys)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct wireless_dev *wdev = dev->ieee80211_ptr;
 | 
						struct wireless_dev *wdev = dev->ieee80211_ptr;
 | 
				
			||||||
 | 
						struct ieee80211_channel *check_chan;
 | 
				
			||||||
 | 
						u8 radar_detect_width = 0;
 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_WDEV_LOCK(wdev);
 | 
						ASSERT_WDEV_LOCK(wdev);
 | 
				
			||||||
| 
						 | 
					@ -114,14 +116,28 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 | 
				
			||||||
	wdev->connect_keys = connkeys;
 | 
						wdev->connect_keys = connkeys;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wdev->ibss_fixed = params->channel_fixed;
 | 
						wdev->ibss_fixed = params->channel_fixed;
 | 
				
			||||||
 | 
						wdev->ibss_dfs_possible = params->userspace_handles_dfs;
 | 
				
			||||||
#ifdef CONFIG_CFG80211_WEXT
 | 
					#ifdef CONFIG_CFG80211_WEXT
 | 
				
			||||||
	wdev->wext.ibss.chandef = params->chandef;
 | 
						wdev->wext.ibss.chandef = params->chandef;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						check_chan = params->chandef.chan;
 | 
				
			||||||
 | 
						if (params->userspace_handles_dfs) {
 | 
				
			||||||
 | 
							/* use channel NULL to check for radar even if the current
 | 
				
			||||||
 | 
							 * channel is not a radar channel - it might decide to change
 | 
				
			||||||
 | 
							 * to DFS channel later.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							radar_detect_width = BIT(params->chandef.width);
 | 
				
			||||||
 | 
							check_chan = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan,
 | 
						err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
 | 
				
			||||||
				    params->channel_fixed
 | 
										   check_chan,
 | 
				
			||||||
 | 
										   (params->channel_fixed &&
 | 
				
			||||||
 | 
										    !radar_detect_width)
 | 
				
			||||||
					   ? CHAN_MODE_SHARED
 | 
										   ? CHAN_MODE_SHARED
 | 
				
			||||||
				    : CHAN_MODE_EXCLUSIVE);
 | 
										   : CHAN_MODE_EXCLUSIVE,
 | 
				
			||||||
 | 
										   radar_detect_width);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (err) {
 | 
						if (err) {
 | 
				
			||||||
		wdev->connect_keys = NULL;
 | 
							wdev->connect_keys = NULL;
 | 
				
			||||||
		return err;
 | 
							return err;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -354,6 +354,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
 | 
				
			||||||
	[NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
 | 
						[NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
 | 
				
			||||||
	[NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
 | 
						[NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
 | 
				
			||||||
	[NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
 | 
						[NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
 | 
				
			||||||
 | 
						[NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
 | 
				
			||||||
 | 
						[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
 | 
				
			||||||
 | 
						[NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* policy for the key attributes */
 | 
					/* policy for the key attributes */
 | 
				
			||||||
| 
						 | 
					@ -3896,9 +3899,45 @@ static int nl80211_parse_sta_wme(struct genl_info *info,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int nl80211_parse_sta_channel_info(struct genl_info *info,
 | 
				
			||||||
 | 
									      struct station_parameters *params)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) {
 | 
				
			||||||
 | 
							params->supported_channels =
 | 
				
			||||||
 | 
							     nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
 | 
				
			||||||
 | 
							params->supported_channels_len =
 | 
				
			||||||
 | 
							     nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Need to include at least one (first channel, number of
 | 
				
			||||||
 | 
							 * channels) tuple for each subband, and must have proper
 | 
				
			||||||
 | 
							 * tuples for the rest of the data as well.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (params->supported_channels_len < 2)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							if (params->supported_channels_len % 2)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) {
 | 
				
			||||||
 | 
							params->supported_oper_classes =
 | 
				
			||||||
 | 
							 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
 | 
				
			||||||
 | 
							params->supported_oper_classes_len =
 | 
				
			||||||
 | 
							  nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * The value of the Length field of the Supported Operating
 | 
				
			||||||
 | 
							 * Classes element is between 2 and 253.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (params->supported_oper_classes_len < 2 ||
 | 
				
			||||||
 | 
							    params->supported_oper_classes_len > 253)
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int nl80211_set_station_tdls(struct genl_info *info,
 | 
					static int nl80211_set_station_tdls(struct genl_info *info,
 | 
				
			||||||
				    struct station_parameters *params)
 | 
									    struct station_parameters *params)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
	/* Dummy STA entry gets updated once the peer capabilities are known */
 | 
						/* Dummy STA entry gets updated once the peer capabilities are known */
 | 
				
			||||||
	if (info->attrs[NL80211_ATTR_PEER_AID])
 | 
						if (info->attrs[NL80211_ATTR_PEER_AID])
 | 
				
			||||||
		params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
 | 
							params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
 | 
				
			||||||
| 
						 | 
					@ -3909,6 +3948,10 @@ static int nl80211_set_station_tdls(struct genl_info *info,
 | 
				
			||||||
		params->vht_capa =
 | 
							params->vht_capa =
 | 
				
			||||||
			nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
 | 
								nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = nl80211_parse_sta_channel_info(info, params);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nl80211_parse_sta_wme(info, params);
 | 
						return nl80211_parse_sta_wme(info, params);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4089,6 +4132,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
 | 
				
			||||||
			return -EINVAL;
 | 
								return -EINVAL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = nl80211_parse_sta_channel_info(info, ¶ms);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = nl80211_parse_sta_wme(info, ¶ms);
 | 
						err = nl80211_parse_sta_wme(info, ¶ms);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
		return err;
 | 
							return err;
 | 
				
			||||||
| 
						 | 
					@ -5653,6 +5700,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
 | 
				
			||||||
			return -EINVAL;
 | 
								return -EINVAL;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case NL80211_IFTYPE_ADHOC:
 | 
						case NL80211_IFTYPE_ADHOC:
 | 
				
			||||||
 | 
						case NL80211_IFTYPE_MESH_POINT:
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return -EOPNOTSUPP;
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
| 
						 | 
					@ -5665,9 +5713,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* only important for AP, IBSS and mesh create IEs internally */
 | 
						/* only important for AP, IBSS and mesh create IEs internally */
 | 
				
			||||||
	if (need_new_beacon &&
 | 
						if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])
 | 
				
			||||||
	    (!info->attrs[NL80211_ATTR_CSA_IES] ||
 | 
					 | 
				
			||||||
	     !info->attrs[NL80211_ATTR_CSA_C_OFF_BEACON]))
 | 
					 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
 | 
						params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
 | 
				
			||||||
| 
						 | 
					@ -5722,9 +5768,9 @@ skip_beacons:
 | 
				
			||||||
	if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef))
 | 
						if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef))
 | 
				
			||||||
		return -EINVAL;
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* DFS channels are only supported for AP/P2P GO ... for now. */
 | 
					 | 
				
			||||||
	if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
 | 
						if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
 | 
				
			||||||
	    dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) {
 | 
						    dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO ||
 | 
				
			||||||
 | 
						    dev->ieee80211_ptr->iftype == NL80211_IFTYPE_ADHOC) {
 | 
				
			||||||
		err = cfg80211_chandef_dfs_required(wdev->wiphy,
 | 
							err = cfg80211_chandef_dfs_required(wdev->wiphy,
 | 
				
			||||||
						    ¶ms.chandef);
 | 
											    ¶ms.chandef);
 | 
				
			||||||
		if (err < 0) {
 | 
							if (err < 0) {
 | 
				
			||||||
| 
						 | 
					@ -6556,6 +6602,9 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
 | 
				
			||||||
	ibss.control_port =
 | 
						ibss.control_port =
 | 
				
			||||||
		nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
 | 
							nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ibss.userspace_handles_dfs =
 | 
				
			||||||
 | 
							nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
 | 
						err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
		kfree(connkeys);
 | 
							kfree(connkeys);
 | 
				
			||||||
| 
						 | 
					@ -10762,7 +10811,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
 | 
						if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
 | 
				
			||||||
		    wdev->iftype != NL80211_IFTYPE_P2P_GO &&
 | 
							    wdev->iftype != NL80211_IFTYPE_P2P_GO &&
 | 
				
			||||||
		    wdev->iftype != NL80211_IFTYPE_ADHOC))
 | 
							    wdev->iftype != NL80211_IFTYPE_ADHOC &&
 | 
				
			||||||
 | 
							    wdev->iftype != NL80211_IFTYPE_MESH_POINT))
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wdev->channel = chandef->chan;
 | 
						wdev->channel = chandef->chan;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -787,7 +787,6 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
 | 
				
			||||||
EXPORT_SYMBOL(reg_initiator_name);
 | 
					EXPORT_SYMBOL(reg_initiator_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_CFG80211_REG_DEBUG
 | 
					#ifdef CONFIG_CFG80211_REG_DEBUG
 | 
				
			||||||
 | 
					 | 
				
			||||||
static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
 | 
					static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
 | 
				
			||||||
				    const struct ieee80211_reg_rule *reg_rule)
 | 
									    const struct ieee80211_reg_rule *reg_rule)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -974,6 +973,13 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY &&
 | 
				
			||||||
 | 
						    !(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY))
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool ignore_reg_update(struct wiphy *wiphy,
 | 
					static bool ignore_reg_update(struct wiphy *wiphy,
 | 
				
			||||||
			      enum nl80211_reg_initiator initiator)
 | 
								      enum nl80211_reg_initiator initiator)
 | 
				
			||||||
| 
						 | 
					@ -1000,7 +1006,7 @@ static bool ignore_reg_update(struct wiphy *wiphy,
 | 
				
			||||||
	 * wiphy->regd will be set once the device has its own
 | 
						 * wiphy->regd will be set once the device has its own
 | 
				
			||||||
	 * desired regulatory domain set
 | 
						 * desired regulatory domain set
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd &&
 | 
						if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd &&
 | 
				
			||||||
	    initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
 | 
						    initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
 | 
				
			||||||
	    !is_world_regdom(lr->alpha2)) {
 | 
						    !is_world_regdom(lr->alpha2)) {
 | 
				
			||||||
		REG_DBG_PRINT("Ignoring regulatory request set by %s "
 | 
							REG_DBG_PRINT("Ignoring regulatory request set by %s "
 | 
				
			||||||
| 
						 | 
					@ -1706,7 +1712,7 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(regulatory_hint);
 | 
					EXPORT_SYMBOL(regulatory_hint);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band,
 | 
					void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band,
 | 
				
			||||||
				const u8 *country_ie, u8 country_ie_len)
 | 
									const u8 *country_ie, u8 country_ie_len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char alpha2[2];
 | 
						char alpha2[2];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,7 +58,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
 | 
				
			||||||
				 gfp_t gfp);
 | 
									 gfp_t gfp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * regulatory_hint_11d - hints a country IE as a regulatory domain
 | 
					 * regulatory_hint_country_ie - hints a country IE as a regulatory domain
 | 
				
			||||||
 * @wiphy: the wireless device giving the hint (used only for reporting
 | 
					 * @wiphy: the wireless device giving the hint (used only for reporting
 | 
				
			||||||
 *	conflicts)
 | 
					 *	conflicts)
 | 
				
			||||||
 * @band: the band on which the country IE was received on. This determines
 | 
					 * @band: the band on which the country IE was received on. This determines
 | 
				
			||||||
| 
						 | 
					@ -78,7 +78,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
 | 
				
			||||||
 * not observed. For this reason if a triplet is seen with channel
 | 
					 * not observed. For this reason if a triplet is seen with channel
 | 
				
			||||||
 * information for a band the BSS is not present in it will be ignored.
 | 
					 * information for a band the BSS is not present in it will be ignored.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void regulatory_hint_11d(struct wiphy *wiphy,
 | 
					void regulatory_hint_country_ie(struct wiphy *wiphy,
 | 
				
			||||||
			 enum ieee80211_band band,
 | 
								 enum ieee80211_band band,
 | 
				
			||||||
			 const u8 *country_ie,
 | 
								 const u8 *country_ie,
 | 
				
			||||||
			 u8 country_ie_len);
 | 
								 u8 country_ie_len);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -682,7 +682,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
 | 
				
			||||||
	 * - country_ie + 2, the start of the country ie data, and
 | 
						 * - country_ie + 2, the start of the country ie data, and
 | 
				
			||||||
	 * - and country_ie[1] which is the IE length
 | 
						 * - and country_ie[1] which is the IE length
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	regulatory_hint_11d(wdev->wiphy, bss->channel->band,
 | 
						regulatory_hint_country_ie(wdev->wiphy, bss->channel->band,
 | 
				
			||||||
				   country_ie + 2, country_ie[1]);
 | 
									   country_ie + 2, country_ie[1]);
 | 
				
			||||||
	kfree(country_ie);
 | 
						kfree(country_ie);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1249,7 +1249,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
 | 
				
			||||||
	enum cfg80211_chan_mode chmode;
 | 
						enum cfg80211_chan_mode chmode;
 | 
				
			||||||
	int num_different_channels = 0;
 | 
						int num_different_channels = 0;
 | 
				
			||||||
	int total = 1;
 | 
						int total = 1;
 | 
				
			||||||
	bool radar_required;
 | 
						bool radar_required = false;
 | 
				
			||||||
	int i, j;
 | 
						int i, j;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RTNL();
 | 
						ASSERT_RTNL();
 | 
				
			||||||
| 
						 | 
					@ -1264,14 +1264,20 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
 | 
				
			||||||
	case NL80211_IFTYPE_MESH_POINT:
 | 
						case NL80211_IFTYPE_MESH_POINT:
 | 
				
			||||||
	case NL80211_IFTYPE_P2P_GO:
 | 
						case NL80211_IFTYPE_P2P_GO:
 | 
				
			||||||
	case NL80211_IFTYPE_WDS:
 | 
						case NL80211_IFTYPE_WDS:
 | 
				
			||||||
		radar_required = !!(chan &&
 | 
							/* if the interface could potentially choose a DFS channel,
 | 
				
			||||||
				    (chan->flags & IEEE80211_CHAN_RADAR));
 | 
							 * then mark DFS as required.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (!chan) {
 | 
				
			||||||
 | 
								if (chanmode != CHAN_MODE_UNDEFINED && radar_detect)
 | 
				
			||||||
 | 
									radar_required = true;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case NL80211_IFTYPE_P2P_CLIENT:
 | 
						case NL80211_IFTYPE_P2P_CLIENT:
 | 
				
			||||||
	case NL80211_IFTYPE_STATION:
 | 
						case NL80211_IFTYPE_STATION:
 | 
				
			||||||
	case NL80211_IFTYPE_P2P_DEVICE:
 | 
						case NL80211_IFTYPE_P2P_DEVICE:
 | 
				
			||||||
	case NL80211_IFTYPE_MONITOR:
 | 
						case NL80211_IFTYPE_MONITOR:
 | 
				
			||||||
		radar_required = false;
 | 
					 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case NUM_NL80211_IFTYPES:
 | 
						case NUM_NL80211_IFTYPES:
 | 
				
			||||||
	case NL80211_IFTYPE_UNSPECIFIED:
 | 
						case NL80211_IFTYPE_UNSPECIFIED:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue