diff options
Diffstat (limited to 'net/mac80211/cfg.c')
| -rw-r--r-- | net/mac80211/cfg.c | 1159 | 
1 files changed, 668 insertions, 491 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 2e7855a1b10..592f4b152ba 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -109,6 +109,15 @@ static int ieee80211_change_iface(struct wiphy *wiphy,  static int ieee80211_start_p2p_device(struct wiphy *wiphy,  				      struct wireless_dev *wdev)  { +	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); +	int ret; + +	mutex_lock(&sdata->local->chanctx_mtx); +	ret = ieee80211_check_combinations(sdata, NULL, 0, 0); +	mutex_unlock(&sdata->local->chanctx_mtx); +	if (ret < 0) +		return ret; +  	return ieee80211_do_open(wdev, true);  } @@ -133,7 +142,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,  			     struct key_params *params)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local;  	struct sta_info *sta = NULL; +	const struct ieee80211_cipher_scheme *cs = NULL;  	struct ieee80211_key *key;  	int err; @@ -145,22 +156,28 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,  	case WLAN_CIPHER_SUITE_WEP40:  	case WLAN_CIPHER_SUITE_TKIP:  	case WLAN_CIPHER_SUITE_WEP104: -		if (IS_ERR(sdata->local->wep_tx_tfm)) +		if (IS_ERR(local->wep_tx_tfm))  			return -EINVAL;  		break; +	case WLAN_CIPHER_SUITE_CCMP: +	case WLAN_CIPHER_SUITE_AES_CMAC: +	case WLAN_CIPHER_SUITE_GCMP: +		break;  	default: +		cs = ieee80211_cs_get(local, params->cipher, sdata->vif.type);  		break;  	}  	key = ieee80211_key_alloc(params->cipher, key_idx, params->key_len, -				  params->key, params->seq_len, params->seq); +				  params->key, params->seq_len, params->seq, +				  cs);  	if (IS_ERR(key))  		return PTR_ERR(key);  	if (pairwise)  		key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE; -	mutex_lock(&sdata->local->sta_mtx); +	mutex_lock(&local->sta_mtx);  	if (mac_addr) {  		if (ieee80211_vif_is_mesh(&sdata->vif)) @@ -216,10 +233,13 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,  		break;  	} +	if (sta) +		sta->cipher_scheme = cs; +  	err = ieee80211_key_link(key, sdata, sta);   out_unlock: -	mutex_unlock(&sdata->local->sta_mtx); +	mutex_unlock(&local->sta_mtx);  	return err;  } @@ -244,7 +264,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,  			goto out_unlock;  		if (pairwise) -			key = key_mtx_dereference(local, sta->ptk); +			key = key_mtx_dereference(local, sta->ptk[key_idx]);  		else  			key = key_mtx_dereference(local, sta->gtk[key_idx]);  	} else @@ -290,9 +310,10 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,  		if (!sta)  			goto out; -		if (pairwise) -			key = rcu_dereference(sta->ptk); -		else if (key_idx < NUM_DEFAULT_KEYS) +		if (pairwise && key_idx < NUM_DEFAULT_KEYS) +			key = rcu_dereference(sta->ptk[key_idx]); +		else if (!pairwise && +			 key_idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)  			key = rcu_dereference(sta->gtk[key_idx]);  	} else  		key = rcu_dereference(sdata->keys[key_idx]); @@ -439,11 +460,11 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)  		rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;  	if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)  		rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; -	if (sta->last_rx_rate_flag & RX_FLAG_80MHZ) +	if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80MHZ)  		rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; -	if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ) +	if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_80P80MHZ)  		rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH; -	if (sta->last_rx_rate_flag & RX_FLAG_160MHZ) +	if (sta->last_rx_rate_vht_flag & RX_VHT_FLAG_160MHZ)  		rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;  } @@ -451,10 +472,15 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)  {  	struct ieee80211_sub_if_data *sdata = sta->sdata;  	struct ieee80211_local *local = sdata->local; +	struct rate_control_ref *ref = NULL;  	struct timespec uptime;  	u64 packets = 0; +	u32 thr = 0;  	int i, ac; +	if (test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) +		ref = local->rate_ctrl; +  	sinfo->generation = sdata->local->sta_generation;  	sinfo->filled = STATION_INFO_INACTIVE_TIME | @@ -521,8 +547,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)  				 STATION_INFO_PEER_PM |  				 STATION_INFO_NONPEER_PM; -		sinfo->llid = le16_to_cpu(sta->llid); -		sinfo->plid = le16_to_cpu(sta->plid); +		sinfo->llid = sta->llid; +		sinfo->plid = sta->plid;  		sinfo->plink_state = sta->plink_state;  		if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {  			sinfo->filled |= STATION_INFO_T_OFFSET; @@ -566,6 +592,17 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)  		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);  	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))  		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); + +	/* check if the driver has a SW RC implementation */ +	if (ref && ref->ops->get_expected_throughput) +		thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv); +	else +		thr = drv_get_expected_throughput(local, &sta->sta); + +	if (thr != 0) { +		sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT; +		sinfo->expected_throughput = thr; +	}  }  static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { @@ -756,7 +793,7 @@ static void ieee80211_get_et_strings(struct wiphy *wiphy,  }  static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, -				 int idx, u8 *mac, struct station_info *sinfo) +				  int idx, u8 *mac, struct station_info *sinfo)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = sdata->local; @@ -786,7 +823,7 @@ static int ieee80211_dump_survey(struct wiphy *wiphy, struct net_device *dev,  }  static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, -				 u8 *mac, struct station_info *sinfo) +				 const u8 *mac, struct station_info *sinfo)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = sdata->local; @@ -816,6 +853,7 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,  	if (cfg80211_chandef_identical(&local->monitor_chandef, chandef))  		return 0; +	mutex_lock(&local->mtx);  	mutex_lock(&local->iflist_mtx);  	if (local->use_chanctx) {  		sdata = rcu_dereference_protected( @@ -834,6 +872,7 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,  	if (ret == 0)  		local->monitor_chandef = *chandef;  	mutex_unlock(&local->iflist_mtx); +	mutex_unlock(&local->mtx);  	return ret;  } @@ -846,7 +885,7 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,  	if (!resp || !resp_len)  		return 1; -	old = rtnl_dereference(sdata->u.ap.probe_resp); +	old = sdata_dereference(sdata->u.ap.probe_resp, sdata);  	new = kzalloc(sizeof(struct probe_resp) + resp_len, GFP_KERNEL);  	if (!new) @@ -862,15 +901,16 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,  	return 0;  } -int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, -			    struct cfg80211_beacon_data *params) +static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, +				   struct cfg80211_beacon_data *params)  {  	struct beacon_data *new, *old;  	int new_head_len, new_tail_len;  	int size, err;  	u32 changed = BSS_CHANGED_BEACON; -	old = rtnl_dereference(sdata->u.ap.beacon); +	old = sdata_dereference(sdata->u.ap.beacon, sdata); +  	/* Need to have a beacon head if we don't have one yet */  	if (!params->head && !old) @@ -938,6 +978,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,  			      struct cfg80211_ap_settings *params)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local;  	struct beacon_data *old;  	struct ieee80211_sub_if_data *vlan;  	u32 changed = BSS_CHANGED_BEACON_INT | @@ -947,20 +988,22 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,  		      BSS_CHANGED_P2P_PS;  	int err; -	old = rtnl_dereference(sdata->u.ap.beacon); +	old = sdata_dereference(sdata->u.ap.beacon, sdata);  	if (old)  		return -EALREADY;  	/* TODO: make hostapd tell us what it wants */  	sdata->smps_mode = IEEE80211_SMPS_OFF;  	sdata->needed_rx_chains = sdata->local->rx_chains; -	sdata->radar_required = params->radar_required; +	mutex_lock(&local->mtx);  	err = ieee80211_vif_use_channel(sdata, ¶ms->chandef,  					IEEE80211_CHANCTX_SHARED); +	if (!err) +		ieee80211_vif_copy_chanctx_to_vlans(sdata, false); +	mutex_unlock(&local->mtx);  	if (err)  		return err; -	ieee80211_vif_copy_chanctx_to_vlans(sdata, false);  	/*  	 * Apply control port protocol, this allows us to @@ -968,11 +1011,19 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,  	 */  	sdata->control_port_protocol = params->crypto.control_port_ethertype;  	sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt; +	sdata->encrypt_headroom = ieee80211_cs_headroom(sdata->local, +							¶ms->crypto, +							sdata->vif.type); +  	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {  		vlan->control_port_protocol =  			params->crypto.control_port_ethertype;  		vlan->control_port_no_encrypt =  			params->crypto.control_port_no_encrypt; +		vlan->encrypt_headroom = +			ieee80211_cs_headroom(sdata->local, +					      ¶ms->crypto, +					      vlan->vif.type);  	}  	sdata->vif.bss_conf.beacon_int = params->beacon_interval; @@ -995,19 +1046,24 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,  					IEEE80211_P2P_OPPPS_ENABLE_BIT;  	err = ieee80211_assign_beacon(sdata, ¶ms->beacon); -	if (err < 0) +	if (err < 0) { +		ieee80211_vif_release_channel(sdata);  		return err; +	}  	changed |= err;  	err = drv_start_ap(sdata->local, sdata);  	if (err) { -		old = rtnl_dereference(sdata->u.ap.beacon); +		old = sdata_dereference(sdata->u.ap.beacon, sdata); +  		if (old)  			kfree_rcu(old, rcu_head);  		RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); +		ieee80211_vif_release_channel(sdata);  		return err;  	} +	ieee80211_recalc_dtim(local, sdata);  	ieee80211_bss_info_change_notify(sdata, changed);  	netif_carrier_on(dev); @@ -1025,6 +1081,7 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,  	int err;  	sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	sdata_assert_lock(sdata);  	/* don't allow changing the beacon while CSA is in place - offset  	 * of channel switch counter may change @@ -1032,7 +1089,7 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,  	if (sdata->vif.csa_active)  		return -EBUSY; -	old = rtnl_dereference(sdata->u.ap.beacon); +	old = sdata_dereference(sdata->u.ap.beacon, sdata);  	if (!old)  		return -ENOENT; @@ -1043,6 +1100,31 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,  	return 0;  } +bool ieee80211_csa_needs_block_tx(struct ieee80211_local *local) +{ +	struct ieee80211_sub_if_data *sdata; + +	lockdep_assert_held(&local->mtx); + +	rcu_read_lock(); +	list_for_each_entry_rcu(sdata, &local->interfaces, list) { +		if (!ieee80211_sdata_running(sdata)) +			continue; + +		if (!sdata->vif.csa_active) +			continue; + +		if (!sdata->csa_block_tx) +			continue; + +		rcu_read_unlock(); +		return true; +	} +	rcu_read_unlock(); + +	return false; +} +  static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1050,15 +1132,26 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)  	struct ieee80211_local *local = sdata->local;  	struct beacon_data *old_beacon;  	struct probe_resp *old_probe_resp; +	struct cfg80211_chan_def chandef; -	old_beacon = rtnl_dereference(sdata->u.ap.beacon); +	sdata_assert_lock(sdata); + +	old_beacon = sdata_dereference(sdata->u.ap.beacon, sdata);  	if (!old_beacon)  		return -ENOENT; -	old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp); +	old_probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata);  	/* abort any running channel switch */ +	mutex_lock(&local->mtx);  	sdata->vif.csa_active = false; -	cancel_work_sync(&sdata->csa_finalize_work); +	if (!ieee80211_csa_needs_block_tx(local)) +		ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_CSA); +	mutex_unlock(&local->mtx); + +	kfree(sdata->u.ap.next_beacon); +	sdata->u.ap.next_beacon = NULL;  	/* turn off carrier for this interface and dependent VLANs */  	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) @@ -1071,18 +1164,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)  	kfree_rcu(old_beacon, rcu_head);  	if (old_probe_resp)  		kfree_rcu(old_probe_resp, rcu_head); +	sdata->u.ap.driver_smps_mode = IEEE80211_SMPS_OFF; -	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) -		sta_info_flush_defer(vlan); -	sta_info_flush_defer(sdata); -	synchronize_net(); -	rcu_barrier(); -	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { -		sta_info_flush_cleanup(vlan); -		ieee80211_free_keys(vlan); -	} -	sta_info_flush_cleanup(sdata); -	ieee80211_free_keys(sdata); +	__sta_info_flush(sdata, true); +	ieee80211_free_keys(sdata, true);  	sdata->vif.bss_conf.enable_beacon = false;  	sdata->vif.bss_conf.ssid_len = 0; @@ -1090,8 +1175,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)  	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);  	if (sdata->wdev.cac_started) { +		chandef = sdata->vif.bss_conf.chandef;  		cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); -		cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_ABORTED, +		cfg80211_cac_event(sdata->dev, &chandef, +				   NL80211_RADAR_CAC_ABORTED,  				   GFP_KERNEL);  	} @@ -1101,8 +1188,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)  	local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);  	skb_queue_purge(&sdata->u.ap.ps.bc_buf); +	mutex_lock(&local->mtx);  	ieee80211_vif_copy_chanctx_to_vlans(sdata, true);  	ieee80211_vif_release_channel(sdata); +	mutex_unlock(&local->mtx);  	return 0;  } @@ -1314,6 +1403,15 @@ static int sta_apply_parameters(struct ieee80211_local *local,  		ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,  						    params->vht_capa, sta); +	if (params->opmode_notif_used) { +		/* returned value is only needed for rc update, but the +		 * rc isn't initialized here yet, so ignore it +		 */ +		__ieee80211_vht_handle_opmode(sdata, sta, +					      params->opmode_notif, +					      band, false); +	} +  	if (ieee80211_vif_is_mesh(&sdata->vif)) {  #ifdef CONFIG_MAC80211_MESH  		u32 changed = 0; @@ -1342,8 +1440,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,  				sta->plink_state = params->plink_state;  				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);  				break;  			default:  				/*  nothing  */ @@ -1367,7 +1465,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,  			changed |=  			      ieee80211_mps_set_sta_local_pm(sta,  							     params->local_pm); -		ieee80211_bss_info_change_notify(sdata, changed); +		ieee80211_mbss_info_change_notify(sdata, changed);  #endif  	} @@ -1375,7 +1473,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,  }  static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, -				 u8 *mac, struct station_parameters *params) +				 const u8 *mac, +				 struct station_parameters *params)  {  	struct ieee80211_local *local = wiphy_priv(wiphy);  	struct sta_info *sta; @@ -1409,6 +1508,8 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,  	if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) {  		sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);  		sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); +	} else { +		sta->sta.tdls = true;  	}  	err = sta_apply_parameters(local, sta, params); @@ -1442,7 +1543,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,  }  static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, -				 u8 *mac) +				 const u8 *mac)  {  	struct ieee80211_sub_if_data *sdata; @@ -1456,7 +1557,7 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,  }  static int ieee80211_change_station(struct wiphy *wiphy, -				    struct net_device *dev, u8 *mac, +				    struct net_device *dev, const u8 *mac,  				    struct station_parameters *params)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1525,7 +1626,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,  		if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&  		    sta->sdata->u.vlan.sta) { -			rcu_assign_pointer(sta->sdata->u.vlan.sta, NULL); +			RCU_INIT_POINTER(sta->sdata->u.vlan.sta, NULL);  			prev_4addr = true;  		} @@ -1553,6 +1654,20 @@ static int ieee80211_change_station(struct wiphy *wiphy,  	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 &&  	    params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {  		ieee80211_recalc_ps(local, -1); @@ -1567,7 +1682,7 @@ out_err:  #ifdef CONFIG_MAC80211_MESH  static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, -				 u8 *dst, u8 *next_hop) +			       const u8 *dst, const u8 *next_hop)  {  	struct ieee80211_sub_if_data *sdata;  	struct mesh_path *mpath; @@ -1595,7 +1710,7 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,  }  static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev, -			       u8 *dst) +			       const u8 *dst)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -1606,9 +1721,8 @@ static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,  	return 0;  } -static int ieee80211_change_mpath(struct wiphy *wiphy, -				    struct net_device *dev, -				    u8 *dst, u8 *next_hop) +static int ieee80211_change_mpath(struct wiphy *wiphy, struct net_device *dev, +				  const u8 *dst, const u8 *next_hop)  {  	struct ieee80211_sub_if_data *sdata;  	struct mesh_path *mpath; @@ -1700,8 +1814,8 @@ static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,  }  static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev, -				 int idx, u8 *dst, u8 *next_hop, -				 struct mpath_info *pinfo) +				int idx, u8 *dst, u8 *next_hop, +				struct mpath_info *pinfo)  {  	struct ieee80211_sub_if_data *sdata;  	struct mesh_path *mpath; @@ -1911,8 +2025,10 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,  	sdata->smps_mode = IEEE80211_SMPS_OFF;  	sdata->needed_rx_chains = sdata->local->rx_chains; +	mutex_lock(&sdata->local->mtx);  	err = ieee80211_vif_use_channel(sdata, &setup->chandef,  					IEEE80211_CHANCTX_SHARED); +	mutex_unlock(&sdata->local->mtx);  	if (err)  		return err; @@ -1924,7 +2040,9 @@ static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	ieee80211_stop_mesh(sdata); +	mutex_lock(&sdata->local->mtx);  	ieee80211_vif_release_channel(sdata); +	mutex_unlock(&sdata->local->mtx);  	return 0;  } @@ -1938,7 +2056,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy,  	enum ieee80211_band band;  	u32 changed = 0; -	if (!rtnl_dereference(sdata->u.ap.beacon)) +	if (!sdata_dereference(sdata->u.ap.beacon, sdata))  		return -ENOENT;  	band = ieee80211_get_sdata_band(sdata); @@ -2337,8 +2455,92 @@ static int ieee80211_testmode_dump(struct wiphy *wiphy,  }  #endif -int __ieee80211_request_smps(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) +{ +	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)  {  	const u8 *ap;  	enum ieee80211_smps_mode old_req; @@ -2346,6 +2548,9 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,  	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;  	sdata->u.mgd.req_smps = smps_mode; @@ -2386,8 +2591,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); -	if (sdata->vif.type != NL80211_IFTYPE_STATION && -	    sdata->vif.type != NL80211_IFTYPE_MESH_POINT) +	if (sdata->vif.type != NL80211_IFTYPE_STATION)  		return -EOPNOTSUPP;  	if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) @@ -2402,7 +2606,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,  	/* no change, but if automatic follow powersave */  	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);  	if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) @@ -2460,8 +2664,8 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,  		int j;  		sdata->rc_rateidx_mask[i] = mask->control[i].legacy; -		memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].mcs, -		       sizeof(mask->control[i].mcs)); +		memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs, +		       sizeof(mask->control[i].ht_mcs));  		sdata->rc_has_mcs_mask[i] = false;  		if (!sband) @@ -2497,6 +2701,18 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,  	if (!roc)  		return -ENOMEM; +	/* +	 * If the duration is zero, then the driver +	 * wouldn't actually do anything. Set it to +	 * 10 for now. +	 * +	 * TODO: cancel the off-channel operation +	 *       when we get the SKB's TX status and +	 *       the wait time was zero before. +	 */ +	if (!duration) +		duration = 10; +  	roc->chan = channel;  	roc->duration = duration;  	roc->req_duration = duration; @@ -2507,6 +2723,24 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,  	INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);  	INIT_LIST_HEAD(&roc->dependents); +	/* +	 * cookie is either the roc cookie (for normal roc) +	 * or the SKB (for mgmt TX) +	 */ +	if (!txskb) { +		/* local->mtx protects this */ +		local->roc_cookie_counter++; +		roc->cookie = local->roc_cookie_counter; +		/* wow, you wrapped 64 bits ... more likely a bug */ +		if (WARN_ON(roc->cookie == 0)) { +			roc->cookie = 1; +			local->roc_cookie_counter++; +		} +		*cookie = roc->cookie; +	} else { +		*cookie = (unsigned long)txskb; +	} +  	/* if there's one pending or we're scanning, queue this one */  	if (!list_empty(&local->roc_list) ||  	    local->scanning || local->radar_detect_enabled) @@ -2520,18 +2754,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,  	/* otherwise actually kick it off here (for error handling) */ -	/* -	 * If the duration is zero, then the driver -	 * wouldn't actually do anything. Set it to -	 * 10 for now. -	 * -	 * TODO: cancel the off-channel operation -	 *       when we get the SKB's TX status and -	 *       the wait time was zero before. -	 */ -	if (!duration) -		duration = 10; -  	ret = drv_remain_on_channel(local, sdata, channel, duration, type);  	if (ret) {  		kfree(roc); @@ -2641,24 +2863,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,  	if (!queued)  		list_add_tail(&roc->list, &local->roc_list); -	/* -	 * cookie is either the roc cookie (for normal roc) -	 * or the SKB (for mgmt TX) -	 */ -	if (!txskb) { -		/* local->mtx protects this */ -		local->roc_cookie_counter++; -		roc->cookie = local->roc_cookie_counter; -		/* wow, you wrapped 64 bits ... more likely a bug */ -		if (WARN_ON(roc->cookie == 0)) { -			roc->cookie = 1; -			local->roc_cookie_counter++; -		} -		*cookie = roc->cookie; -	} else { -		*cookie = (unsigned long)txskb; -	} -  	return 0;  } @@ -2769,33 +2973,35 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,  static int ieee80211_start_radar_detection(struct wiphy *wiphy,  					   struct net_device *dev, -					   struct cfg80211_chan_def *chandef) +					   struct cfg80211_chan_def *chandef, +					   u32 cac_time_ms)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = sdata->local; -	unsigned long timeout;  	int err; -	if (!list_empty(&local->roc_list) || local->scanning) -		return -EBUSY; +	mutex_lock(&local->mtx); +	if (!list_empty(&local->roc_list) || local->scanning) { +		err = -EBUSY; +		goto out_unlock; +	}  	/* whatever, but channel contexts should not complain about that one */  	sdata->smps_mode = IEEE80211_SMPS_OFF;  	sdata->needed_rx_chains = local->rx_chains; -	sdata->radar_required = true; -	mutex_lock(&local->iflist_mtx);  	err = ieee80211_vif_use_channel(sdata, chandef,  					IEEE80211_CHANCTX_SHARED); -	mutex_unlock(&local->iflist_mtx);  	if (err) -		return err; +		goto out_unlock; -	timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);  	ieee80211_queue_delayed_work(&sdata->local->hw, -				     &sdata->dfs_cac_timer_work, timeout); +				     &sdata->dfs_cac_timer_work, +				     msecs_to_jiffies(cac_time_ms)); -	return 0; + out_unlock: +	mutex_unlock(&local->mtx); +	return err;  }  static struct cfg80211_beacon_data * @@ -2854,52 +3060,272 @@ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)  	return new_beacon;  } +void ieee80211_csa_finish(struct ieee80211_vif *vif) +{ +	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + +	ieee80211_queue_work(&sdata->local->hw, +			     &sdata->csa_finalize_work); +} +EXPORT_SYMBOL(ieee80211_csa_finish); + +static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata, +					  u32 *changed) +{ +	int err; + +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_AP: +		err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon); +		kfree(sdata->u.ap.next_beacon); +		sdata->u.ap.next_beacon = NULL; + +		if (err < 0) +			return err; +		*changed |= err; +		break; +	case NL80211_IFTYPE_ADHOC: +		err = ieee80211_ibss_finish_csa(sdata); +		if (err < 0) +			return err; +		*changed |= err; +		break; +#ifdef CONFIG_MAC80211_MESH +	case NL80211_IFTYPE_MESH_POINT: +		err = ieee80211_mesh_finish_csa(sdata); +		if (err < 0) +			return err; +		*changed |= err; +		break; +#endif +	default: +		WARN_ON(1); +		return -EINVAL; +	} + +	return 0; +} + +static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_local *local = sdata->local; +	u32 changed = 0; +	int err; + +	sdata_assert_lock(sdata); +	lockdep_assert_held(&local->mtx); + +	sdata->radar_required = sdata->csa_radar_required; +	err = ieee80211_vif_change_channel(sdata, &changed); +	if (err < 0) +		return err; + +	if (!local->use_chanctx) { +		local->_oper_chandef = sdata->csa_chandef; +		ieee80211_hw_config(local, 0); +	} + +	sdata->vif.csa_active = false; + +	err = ieee80211_set_after_csa_beacon(sdata, &changed); +	if (err) +		return err; + +	ieee80211_bss_info_change_notify(sdata, changed); +	cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef); + +	if (!ieee80211_csa_needs_block_tx(local)) +		ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_CSA); + +	return 0; +} + +static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) +{ +	if (__ieee80211_csa_finalize(sdata)) { +		sdata_info(sdata, "failed to finalize CSA, disconnecting\n"); +		cfg80211_stop_iface(sdata->local->hw.wiphy, &sdata->wdev, +				    GFP_KERNEL); +	} +} +  void ieee80211_csa_finalize_work(struct work_struct *work)  {  	struct ieee80211_sub_if_data *sdata =  		container_of(work, struct ieee80211_sub_if_data,  			     csa_finalize_work);  	struct ieee80211_local *local = sdata->local; -	int err, changed; + +	sdata_lock(sdata); +	mutex_lock(&local->mtx); + +	/* AP might have been stopped while waiting for the lock. */ +	if (!sdata->vif.csa_active) +		goto unlock;  	if (!ieee80211_sdata_running(sdata)) -		return; +		goto unlock; -	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) -		return; +	ieee80211_csa_finalize(sdata); -	sdata->radar_required = sdata->csa_radar_required; -	err = ieee80211_vif_change_channel(sdata, &local->csa_chandef, -					   &changed); -	if (WARN_ON(err < 0)) -		return; +unlock: +	mutex_unlock(&local->mtx); +	sdata_unlock(sdata); +} -	err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon); -	if (err < 0) -		return; +static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata, +				    struct cfg80211_csa_settings *params, +				    u32 *changed) +{ +	int err; -	changed |= err; -	kfree(sdata->u.ap.next_beacon); -	sdata->u.ap.next_beacon = NULL; -	sdata->vif.csa_active = false; +	switch (sdata->vif.type) { +	case NL80211_IFTYPE_AP: +		sdata->u.ap.next_beacon = +			cfg80211_beacon_dup(¶ms->beacon_after); +		if (!sdata->u.ap.next_beacon) +			return -ENOMEM; -	ieee80211_wake_queues_by_reason(&sdata->local->hw, -					IEEE80211_MAX_QUEUE_MAP, -					IEEE80211_QUEUE_STOP_REASON_CSA); +		/* +		 * With a count of 0, we don't have to wait for any +		 * TBTT before switching, so complete the CSA +		 * immediately.  In theory, with a count == 1 we +		 * should delay the switch until just before the next +		 * TBTT, but that would complicate things so we switch +		 * immediately too.  If we would delay the switch +		 * until the next TBTT, we would have to set the probe +		 * response here. +		 * +		 * TODO: A channel switch with count <= 1 without +		 * sending a CSA action frame is kind of useless, +		 * because the clients won't know we're changing +		 * channels.  The action frame must be implemented +		 * either here or in the userspace. +		 */ +		if (params->count <= 1) +			break; -	ieee80211_bss_info_change_notify(sdata, changed); +		if ((params->n_counter_offsets_beacon > +		     IEEE80211_MAX_CSA_COUNTERS_NUM) || +		    (params->n_counter_offsets_presp > +		     IEEE80211_MAX_CSA_COUNTERS_NUM)) +			return -EINVAL; + +		/* make sure we don't have garbage in other counters */ +		memset(sdata->csa_counter_offset_beacon, 0, +		       sizeof(sdata->csa_counter_offset_beacon)); +		memset(sdata->csa_counter_offset_presp, 0, +		       sizeof(sdata->csa_counter_offset_presp)); + +		memcpy(sdata->csa_counter_offset_beacon, +		       params->counter_offsets_beacon, +		       params->n_counter_offsets_beacon * sizeof(u16)); +		memcpy(sdata->csa_counter_offset_presp, +		       params->counter_offsets_presp, +		       params->n_counter_offsets_presp * sizeof(u16)); + +		err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa); +		if (err < 0) { +			kfree(sdata->u.ap.next_beacon); +			return err; +		} +		*changed |= err; + +		break; +	case NL80211_IFTYPE_ADHOC: +		if (!sdata->vif.bss_conf.ibss_joined) +			return -EINVAL; + +		if (params->chandef.width != sdata->u.ibss.chandef.width) +			return -EINVAL; + +		switch (params->chandef.width) { +		case NL80211_CHAN_WIDTH_40: +			if (cfg80211_get_chandef_type(¶ms->chandef) != +			    cfg80211_get_chandef_type(&sdata->u.ibss.chandef)) +				return -EINVAL; +		case NL80211_CHAN_WIDTH_5: +		case NL80211_CHAN_WIDTH_10: +		case NL80211_CHAN_WIDTH_20_NOHT: +		case NL80211_CHAN_WIDTH_20: +			break; +		default: +			return -EINVAL; +		} + +		/* changes into another band are not supported */ +		if (sdata->u.ibss.chandef.chan->band != +		    params->chandef.chan->band) +			return -EINVAL; + +		/* see comments in the NL80211_IFTYPE_AP block */ +		if (params->count > 1) { +			err = ieee80211_ibss_csa_beacon(sdata, params); +			if (err < 0) +				return err; +			*changed |= err; +		} + +		ieee80211_send_action_csa(sdata, params); + +		break; +#ifdef CONFIG_MAC80211_MESH +	case NL80211_IFTYPE_MESH_POINT: { +		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + +		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; + +		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_NONE) { +			ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_INIT; +			if (!ifmsh->pre_value) +				ifmsh->pre_value = 1; +			else +				ifmsh->pre_value++; +		} + +		/* see comments in the NL80211_IFTYPE_AP block */ +		if (params->count > 1) { +			err = ieee80211_mesh_csa_beacon(sdata, params); +			if (err < 0) { +				ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE; +				return err; +			} +			*changed |= err; +		} + +		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT) +			ieee80211_send_action_csa(sdata, params); + +		break; +		} +#endif +	default: +		return -EOPNOTSUPP; +	} -	cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef); +	return 0;  } -static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, -				    struct cfg80211_csa_settings *params) +static int +__ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, +			   struct cfg80211_csa_settings *params)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = sdata->local; -	struct ieee80211_chanctx_conf *chanctx_conf; +	struct ieee80211_chanctx_conf *conf;  	struct ieee80211_chanctx *chanctx; -	int err, num_chanctx; +	int err, num_chanctx, changed = 0; + +	sdata_assert_lock(sdata); +	lockdep_assert_held(&local->mtx);  	if (!list_empty(&local->roc_list) || local->scanning)  		return -EBUSY; @@ -2911,23 +3337,24 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,  				       &sdata->vif.bss_conf.chandef))  		return -EINVAL; -	rcu_read_lock(); -	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); -	if (!chanctx_conf) { -		rcu_read_unlock(); +	mutex_lock(&local->chanctx_mtx); +	conf = rcu_dereference_protected(sdata->vif.chanctx_conf, +					 lockdep_is_held(&local->chanctx_mtx)); +	if (!conf) { +		mutex_unlock(&local->chanctx_mtx);  		return -EBUSY;  	}  	/* don't handle for multi-VIF cases */ -	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); -	if (chanctx->refcount > 1) { -		rcu_read_unlock(); +	chanctx = container_of(conf, struct ieee80211_chanctx, conf); +	if (ieee80211_chanctx_refcount(local, chanctx) > 1) { +		mutex_unlock(&local->chanctx_mtx);  		return -EBUSY;  	}  	num_chanctx = 0;  	list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)  		num_chanctx++; -	rcu_read_unlock(); +	mutex_unlock(&local->chanctx_mtx);  	if (num_chanctx > 1)  		return -EBUSY; @@ -2936,61 +3363,67 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,  	if (sdata->vif.csa_active)  		return -EBUSY; -	/* only handle AP for now. */ -	switch (sdata->vif.type) { -	case NL80211_IFTYPE_AP: -		break; -	default: -		return -EOPNOTSUPP; -	} - -	sdata->u.ap.next_beacon = cfg80211_beacon_dup(¶ms->beacon_after); -	if (!sdata->u.ap.next_beacon) -		return -ENOMEM; +	err = ieee80211_set_csa_beacon(sdata, params, &changed); +	if (err) +		return err; -	sdata->csa_counter_offset_beacon = params->counter_offset_beacon; -	sdata->csa_counter_offset_presp = params->counter_offset_presp;  	sdata->csa_radar_required = params->radar_required; +	sdata->csa_chandef = params->chandef; +	sdata->csa_block_tx = params->block_tx; +	sdata->csa_current_counter = params->count; +	sdata->vif.csa_active = true; -	if (params->block_tx) +	if (sdata->csa_block_tx)  		ieee80211_stop_queues_by_reason(&local->hw, -				IEEE80211_MAX_QUEUE_MAP, -				IEEE80211_QUEUE_STOP_REASON_CSA); +					IEEE80211_MAX_QUEUE_MAP, +					IEEE80211_QUEUE_STOP_REASON_CSA); -	err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa); -	if (err < 0) -		return err; +	if (changed) { +		ieee80211_bss_info_change_notify(sdata, changed); +		drv_channel_switch_beacon(sdata, ¶ms->chandef); +	} else { +		/* if the beacon didn't change, we can finalize immediately */ +		ieee80211_csa_finalize(sdata); +	} -	local->csa_chandef = params->chandef; -	sdata->vif.csa_active = true; +	return 0; +} -	ieee80211_bss_info_change_notify(sdata, err); -	drv_channel_switch_beacon(sdata, ¶ms->chandef); +int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, +			     struct cfg80211_csa_settings *params) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct ieee80211_local *local = sdata->local; +	int err; -	return 0; +	mutex_lock(&local->mtx); +	err = __ieee80211_channel_switch(wiphy, dev, params); +	mutex_unlock(&local->mtx); + +	return err;  }  static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, -			     struct ieee80211_channel *chan, bool offchan, -			     unsigned int wait, const u8 *buf, size_t len, -			     bool no_cck, bool dont_wait_for_ack, u64 *cookie) +			     struct cfg80211_mgmt_tx_params *params, +			     u64 *cookie)  {  	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);  	struct ieee80211_local *local = sdata->local;  	struct sk_buff *skb;  	struct sta_info *sta; -	const struct ieee80211_mgmt *mgmt = (void *)buf; +	const struct ieee80211_mgmt *mgmt = (void *)params->buf;  	bool need_offchan = false;  	u32 flags;  	int ret; +	u8 *data; -	if (dont_wait_for_ack) +	if (params->dont_wait_for_ack)  		flags = IEEE80211_TX_CTL_NO_ACK;  	else  		flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |  			IEEE80211_TX_CTL_REQ_TX_STATUS; -	if (no_cck) +	if (params->no_cck)  		flags |= IEEE80211_TX_CTL_NO_CCK_RATE;  	switch (sdata->vif.type) { @@ -3014,7 +3447,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,  			need_offchan = true;  		if (!ieee80211_is_action(mgmt->frame_control) ||  		    mgmt->u.action.category == WLAN_CATEGORY_PUBLIC || -		    mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED) +		    mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED || +		    mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)  			break;  		rcu_read_lock();  		sta = sta_info_get(sdata, mgmt->da); @@ -3037,7 +3471,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,  	/* configurations requiring offchan cannot work if no channel has been  	 * specified  	 */ -	if (need_offchan && !chan) +	if (need_offchan && !params->chan)  		return -EINVAL;  	mutex_lock(&local->mtx); @@ -3050,8 +3484,10 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,  		chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);  		if (chanctx_conf) { -			need_offchan = chan && (chan != chanctx_conf->def.chan); -		} else if (!chan) { +			need_offchan = params->chan && +				       (params->chan != +					chanctx_conf->def.chan); +		} else if (!params->chan) {  			ret = -EINVAL;  			rcu_read_unlock();  			goto out_unlock; @@ -3061,19 +3497,32 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,  		rcu_read_unlock();  	} -	if (need_offchan && !offchan) { +	if (need_offchan && !params->offchan) {  		ret = -EBUSY;  		goto out_unlock;  	} -	skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); +	skb = dev_alloc_skb(local->hw.extra_tx_headroom + params->len);  	if (!skb) {  		ret = -ENOMEM;  		goto out_unlock;  	}  	skb_reserve(skb, local->hw.extra_tx_headroom); -	memcpy(skb_put(skb, len), buf, len); +	data = skb_put(skb, params->len); +	memcpy(data, params->buf, params->len); + +	/* Update CSA counters */ +	if (sdata->vif.csa_active && +	    (sdata->vif.type == NL80211_IFTYPE_AP || +	     sdata->vif.type == NL80211_IFTYPE_ADHOC) && +	    params->n_csa_offsets) { +		int i; +		u8 c = sdata->csa_current_counter; + +		for (i = 0; i < params->n_csa_offsets; i++) +			data[params->csa_offsets[i]] = c; +	}  	IEEE80211_SKB_CB(skb)->flags = flags; @@ -3093,8 +3542,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,  			local->hw.offchannel_tx_hw_queue;  	/* This will handle all kinds of coalescing and immediate TX */ -	ret = ieee80211_start_roc_work(local, sdata, chan, -				       wait, cookie, skb, +	ret = ieee80211_start_roc_work(local, sdata, params->chan, +				       params->wait, cookie, skb,  				       IEEE80211_ROC_TYPE_MGMT_TX);  	if (ret)  		kfree_skb(skb); @@ -3182,320 +3631,6 @@ static int ieee80211_set_rekey_data(struct wiphy *wiphy,  	return 0;  } -static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb) -{ -	u8 *pos = (void *)skb_put(skb, 7); - -	*pos++ = WLAN_EID_EXT_CAPABILITY; -	*pos++ = 5; /* len */ -	*pos++ = 0x0; -	*pos++ = 0x0; -	*pos++ = 0x0; -	*pos++ = 0x0; -	*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; -} - -static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_local *local = sdata->local; -	u16 capab; - -	capab = 0; -	if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ) -		return capab; - -	if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) -		capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; -	if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) -		capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; - -	return capab; -} - -static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, -				       u8 *peer, u8 *bssid) -{ -	struct ieee80211_tdls_lnkie *lnkid; - -	lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); - -	lnkid->ie_type = WLAN_EID_LINK_ID; -	lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; - -	memcpy(lnkid->bssid, bssid, ETH_ALEN); -	memcpy(lnkid->init_sta, src_addr, ETH_ALEN); -	memcpy(lnkid->resp_sta, peer, ETH_ALEN); -} - -static int -ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, -			       u8 *peer, u8 action_code, u8 dialog_token, -			       u16 status_code, struct sk_buff *skb) -{ -	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); -	enum ieee80211_band band = ieee80211_get_sdata_band(sdata); -	struct ieee80211_tdls_data *tf; - -	tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); - -	memcpy(tf->da, peer, ETH_ALEN); -	memcpy(tf->sa, sdata->vif.addr, ETH_ALEN); -	tf->ether_type = cpu_to_be16(ETH_P_TDLS); -	tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; - -	switch (action_code) { -	case WLAN_TDLS_SETUP_REQUEST: -		tf->category = WLAN_CATEGORY_TDLS; -		tf->action_code = WLAN_TDLS_SETUP_REQUEST; - -		skb_put(skb, sizeof(tf->u.setup_req)); -		tf->u.setup_req.dialog_token = dialog_token; -		tf->u.setup_req.capability = -			cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - -		ieee80211_add_srates_ie(sdata, skb, false, band); -		ieee80211_add_ext_srates_ie(sdata, skb, false, band); -		ieee80211_tdls_add_ext_capab(skb); -		break; -	case WLAN_TDLS_SETUP_RESPONSE: -		tf->category = WLAN_CATEGORY_TDLS; -		tf->action_code = WLAN_TDLS_SETUP_RESPONSE; - -		skb_put(skb, sizeof(tf->u.setup_resp)); -		tf->u.setup_resp.status_code = cpu_to_le16(status_code); -		tf->u.setup_resp.dialog_token = dialog_token; -		tf->u.setup_resp.capability = -			cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - -		ieee80211_add_srates_ie(sdata, skb, false, band); -		ieee80211_add_ext_srates_ie(sdata, skb, false, band); -		ieee80211_tdls_add_ext_capab(skb); -		break; -	case WLAN_TDLS_SETUP_CONFIRM: -		tf->category = WLAN_CATEGORY_TDLS; -		tf->action_code = WLAN_TDLS_SETUP_CONFIRM; - -		skb_put(skb, sizeof(tf->u.setup_cfm)); -		tf->u.setup_cfm.status_code = cpu_to_le16(status_code); -		tf->u.setup_cfm.dialog_token = dialog_token; -		break; -	case WLAN_TDLS_TEARDOWN: -		tf->category = WLAN_CATEGORY_TDLS; -		tf->action_code = WLAN_TDLS_TEARDOWN; - -		skb_put(skb, sizeof(tf->u.teardown)); -		tf->u.teardown.reason_code = cpu_to_le16(status_code); -		break; -	case WLAN_TDLS_DISCOVERY_REQUEST: -		tf->category = WLAN_CATEGORY_TDLS; -		tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; - -		skb_put(skb, sizeof(tf->u.discover_req)); -		tf->u.discover_req.dialog_token = dialog_token; -		break; -	default: -		return -EINVAL; -	} - -	return 0; -} - -static int -ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, -			   u8 *peer, u8 action_code, u8 dialog_token, -			   u16 status_code, struct sk_buff *skb) -{ -	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); -	enum ieee80211_band band = ieee80211_get_sdata_band(sdata); -	struct ieee80211_mgmt *mgmt; - -	mgmt = (void *)skb_put(skb, 24); -	memset(mgmt, 0, 24); -	memcpy(mgmt->da, peer, ETH_ALEN); -	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); -	memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); - -	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | -					  IEEE80211_STYPE_ACTION); - -	switch (action_code) { -	case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: -		skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); -		mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; -		mgmt->u.action.u.tdls_discover_resp.action_code = -			WLAN_PUB_ACTION_TDLS_DISCOVER_RES; -		mgmt->u.action.u.tdls_discover_resp.dialog_token = -			dialog_token; -		mgmt->u.action.u.tdls_discover_resp.capability = -			cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - -		ieee80211_add_srates_ie(sdata, skb, false, band); -		ieee80211_add_ext_srates_ie(sdata, skb, false, band); -		ieee80211_tdls_add_ext_capab(skb); -		break; -	default: -		return -EINVAL; -	} - -	return 0; -} - -static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, -			       u8 *peer, u8 action_code, u8 dialog_token, -			       u16 status_code, const u8 *extra_ies, -			       size_t extra_ies_len) -{ -	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); -	struct ieee80211_local *local = sdata->local; -	struct sk_buff *skb = NULL; -	bool send_direct; -	int ret; - -	if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) -		return -ENOTSUPP; - -	/* make sure we are in managed mode, and associated */ -	if (sdata->vif.type != NL80211_IFTYPE_STATION || -	    !sdata->u.mgd.associated) -		return -EINVAL; - -	tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n", -		 action_code, peer); - -	skb = dev_alloc_skb(local->hw.extra_tx_headroom + -			    max(sizeof(struct ieee80211_mgmt), -				sizeof(struct ieee80211_tdls_data)) + -			    50 + /* supported rates */ -			    7 + /* ext capab */ -			    extra_ies_len + -			    sizeof(struct ieee80211_tdls_lnkie)); -	if (!skb) -		return -ENOMEM; - -	skb_reserve(skb, local->hw.extra_tx_headroom); - -	switch (action_code) { -	case WLAN_TDLS_SETUP_REQUEST: -	case WLAN_TDLS_SETUP_RESPONSE: -	case WLAN_TDLS_SETUP_CONFIRM: -	case WLAN_TDLS_TEARDOWN: -	case WLAN_TDLS_DISCOVERY_REQUEST: -		ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer, -						     action_code, dialog_token, -						     status_code, skb); -		send_direct = false; -		break; -	case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: -		ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code, -						 dialog_token, status_code, -						 skb); -		send_direct = true; -		break; -	default: -		ret = -ENOTSUPP; -		break; -	} - -	if (ret < 0) -		goto fail; - -	if (extra_ies_len) -		memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); - -	/* the TDLS link IE is always added last */ -	switch (action_code) { -	case WLAN_TDLS_SETUP_REQUEST: -	case WLAN_TDLS_SETUP_CONFIRM: -	case WLAN_TDLS_TEARDOWN: -	case WLAN_TDLS_DISCOVERY_REQUEST: -		/* we are the initiator */ -		ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer, -					   sdata->u.mgd.bssid); -		break; -	case WLAN_TDLS_SETUP_RESPONSE: -	case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: -		/* we are the responder */ -		ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr, -					   sdata->u.mgd.bssid); -		break; -	default: -		ret = -ENOTSUPP; -		goto fail; -	} - -	if (send_direct) { -		ieee80211_tx_skb(sdata, skb); -		return 0; -	} - -	/* -	 * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise -	 * we should default to AC_VI. -	 */ -	switch (action_code) { -	case WLAN_TDLS_SETUP_REQUEST: -	case WLAN_TDLS_SETUP_RESPONSE: -		skb_set_queue_mapping(skb, IEEE80211_AC_BK); -		skb->priority = 2; -		break; -	default: -		skb_set_queue_mapping(skb, IEEE80211_AC_VI); -		skb->priority = 5; -		break; -	} - -	/* disable bottom halves when entering the Tx path */ -	local_bh_disable(); -	ret = ieee80211_subif_start_xmit(skb, dev); -	local_bh_enable(); - -	return ret; - -fail: -	dev_kfree_skb(skb); -	return ret; -} - -static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, -			       u8 *peer, enum nl80211_tdls_operation oper) -{ -	struct sta_info *sta; -	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - -	if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) -		return -ENOTSUPP; - -	if (sdata->vif.type != NL80211_IFTYPE_STATION) -		return -EINVAL; - -	tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); - -	switch (oper) { -	case NL80211_TDLS_ENABLE_LINK: -		rcu_read_lock(); -		sta = sta_info_get(sdata, peer); -		if (!sta) { -			rcu_read_unlock(); -			return -ENOLINK; -		} - -		set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); -		rcu_read_unlock(); -		break; -	case NL80211_TDLS_DISABLE_LINK: -		return sta_info_destroy_addr(sdata, peer); -	case NL80211_TDLS_TEARDOWN: -	case NL80211_TDLS_SETUP: -	case NL80211_TDLS_DISCOVERY_REQ: -		/* We don't support in-driver setup/teardown/discovery */ -		return -ENOTSUPP; -	default: -		return -ENOTSUPP; -	} - -	return 0; -} -  static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,  				  const u8 *peer, u64 *cookie)  { @@ -3518,7 +3653,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,  		return -EINVAL;  	}  	band = chanctx_conf->def.chan->band; -	sta = sta_info_get(sdata, peer); +	sta = sta_info_get_bss(sdata, peer);  	if (sta) {  		qos = test_sta_flag(sta, WLAN_STA_WME);  	} else { @@ -3609,7 +3744,47 @@ static void ieee80211_set_wakeup(struct wiphy *wiphy, bool enabled)  }  #endif -struct cfg80211_ops mac80211_config_ops = { +static int ieee80211_set_qos_map(struct wiphy *wiphy, +				 struct net_device *dev, +				 struct cfg80211_qos_map *qos_map) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	struct mac80211_qos_map *new_qos_map, *old_qos_map; + +	if (qos_map) { +		new_qos_map = kzalloc(sizeof(*new_qos_map), GFP_KERNEL); +		if (!new_qos_map) +			return -ENOMEM; +		memcpy(&new_qos_map->qos_map, qos_map, sizeof(*qos_map)); +	} else { +		/* A NULL qos_map was passed to disable QoS mapping */ +		new_qos_map = NULL; +	} + +	old_qos_map = sdata_dereference(sdata->qos_map, sdata); +	rcu_assign_pointer(sdata->qos_map, new_qos_map); +	if (old_qos_map) +		kfree_rcu(old_qos_map, rcu_head); + +	return 0; +} + +static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy, +				      struct net_device *dev, +				      struct cfg80211_chan_def *chandef) +{ +	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); +	int ret; +	u32 changed = 0; + +	ret = ieee80211_vif_change_bandwidth(sdata, chandef, &changed); +	if (ret == 0) +		ieee80211_bss_info_change_notify(sdata, changed); + +	return ret; +} + +const struct cfg80211_ops mac80211_config_ops = {  	.add_virtual_intf = ieee80211_add_iface,  	.del_virtual_intf = ieee80211_del_iface,  	.change_virtual_intf = ieee80211_change_iface, @@ -3688,4 +3863,6 @@ struct cfg80211_ops mac80211_config_ops = {  	.get_channel = ieee80211_cfg_get_channel,  	.start_radar_detection = ieee80211_start_radar_detection,  	.channel_switch = ieee80211_channel_switch, +	.set_qos_map = ieee80211_set_qos_map, +	.set_ap_chanwidth = ieee80211_set_ap_chanwidth,  };  | 
