diff options
Diffstat (limited to 'net/mac80211/mlme.c')
| -rw-r--r-- | net/mac80211/mlme.c | 554 | 
1 files changed, 310 insertions, 244 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 346ad4cfb01..29620bfc7a6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -56,7 +56,10 @@ MODULE_PARM_DESC(max_probe_tries,   * probe on beacon miss before declaring the connection lost   * default to what we want.   */ -#define IEEE80211_BEACON_LOSS_COUNT	7 +static int beacon_loss_count = 7; +module_param(beacon_loss_count, int, 0644); +MODULE_PARM_DESC(beacon_loss_count, +		 "Number of beacon intervals before we decide beacon was lost.");  /*   * Time the connection can be idle before we probe @@ -87,9 +90,6 @@ MODULE_PARM_DESC(probe_wait_ms,   */  #define IEEE80211_SIGNAL_AVE_MIN_COUNT	4 -#define TMR_RUNNING_TIMER	0 -#define TMR_RUNNING_CHANSW	1 -  /*   * All cfg80211 functions have to be called outside a locked   * section so that they can acquire a lock themselves... This @@ -289,6 +289,8 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,  	} else {  		/* 40 MHz (and 80 MHz) must be supported for VHT */  		ret = IEEE80211_STA_DISABLE_VHT; +		/* also mark 40 MHz disabled */ +		ret |= IEEE80211_STA_DISABLE_40MHZ;  		goto out;  	} @@ -303,12 +305,6 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,  					       channel->band);  	vht_chandef.center_freq2 = 0; -	if (vht_oper->center_freq_seg2_idx) -		vht_chandef.center_freq2 = -			ieee80211_channel_to_frequency( -				vht_oper->center_freq_seg2_idx, -				channel->band); -  	switch (vht_oper->chan_width) {  	case IEEE80211_VHT_CHANWIDTH_USE_HT:  		vht_chandef.width = chandef->width; @@ -321,6 +317,10 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,  		break;  	case IEEE80211_VHT_CHANWIDTH_80P80MHZ:  		vht_chandef.width = NL80211_CHAN_WIDTH_80P80; +		vht_chandef.center_freq2 = +			ieee80211_channel_to_frequency( +				vht_oper->center_freq_seg2_idx, +				channel->band);  		break;  	default:  		if (verbose) @@ -604,11 +604,11 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,  	u8 *pos;  	u32 cap;  	struct ieee80211_sta_vht_cap vht_cap; -	int i;  	BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));  	memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap)); +	ieee80211_apply_vhtcap_overrides(sdata, &vht_cap);  	/* determine capability flags */  	cap = vht_cap.cap; @@ -631,37 +631,6 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,  			cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)))  		cap &= ~IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; -	if (!(ap_vht_cap->vht_cap_info & -			cpu_to_le32(IEEE80211_VHT_CAP_TXSTBC))) -		cap &= ~(IEEE80211_VHT_CAP_RXSTBC_1 | -			 IEEE80211_VHT_CAP_RXSTBC_3 | -			 IEEE80211_VHT_CAP_RXSTBC_4); - -	for (i = 0; i < 8; i++) { -		int shift = i * 2; -		u16 mask = IEEE80211_VHT_MCS_NOT_SUPPORTED << shift; -		u16 ap_mcs, our_mcs; - -		ap_mcs = (le16_to_cpu(ap_vht_cap->supp_mcs.tx_mcs_map) & -								mask) >> shift; -		our_mcs = (le16_to_cpu(vht_cap.vht_mcs.rx_mcs_map) & -								mask) >> shift; - -		if (our_mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED) -			continue; - -		switch (ap_mcs) { -		default: -			if (our_mcs <= ap_mcs) -				break; -			/* fall through */ -		case IEEE80211_VHT_MCS_NOT_SUPPORTED: -			vht_cap.vht_mcs.rx_mcs_map &= cpu_to_le16(~mask); -			vht_cap.vht_mcs.rx_mcs_map |= -				cpu_to_le16(ap_mcs << shift); -		} -	} -  	/* reserve and fill IE */  	pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);  	ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); @@ -987,6 +956,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)  {  	struct ieee80211_sub_if_data *sdata =  		container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work); +	struct ieee80211_local *local = sdata->local;  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;  	if (!ieee80211_sdata_running(sdata)) @@ -996,21 +966,22 @@ static void ieee80211_chswitch_work(struct work_struct *work)  	if (!ifmgd->associated)  		goto out; -	sdata->local->_oper_channel = sdata->local->csa_channel; -	if (!sdata->local->ops->channel_switch) { +	local->_oper_chandef = local->csa_chandef; + +	if (!local->ops->channel_switch) {  		/* call "hw_config" only if doing sw channel switch */ -		ieee80211_hw_config(sdata->local, -			IEEE80211_CONF_CHANGE_CHANNEL); +		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);  	} else {  		/* update the device channel directly */ -		sdata->local->hw.conf.channel = sdata->local->_oper_channel; +		local->hw.conf.chandef = local->_oper_chandef;  	}  	/* XXX: shouldn't really modify cfg80211-owned data! */ -	ifmgd->associated->channel = sdata->local->_oper_channel; +	ifmgd->associated->channel = local->_oper_chandef.chan;  	/* XXX: wait for a beacon first? */ -	ieee80211_wake_queues_by_reason(&sdata->local->hw, +	ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_CSA);   out:  	ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; @@ -1038,66 +1009,197 @@ static void ieee80211_chswitch_timer(unsigned long data)  {  	struct ieee80211_sub_if_data *sdata =  		(struct ieee80211_sub_if_data *) data; -	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; -	if (sdata->local->quiescing) { -		set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running); -		return; -	} - -	ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); +	ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work);  } -void +static void  ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, -				 const struct ieee80211_channel_sw_ie *sw_elem, -				 struct ieee80211_bss *bss, u64 timestamp) +				 u64 timestamp, struct ieee802_11_elems *elems)  { -	struct cfg80211_bss *cbss = -		container_of((void *)bss, struct cfg80211_bss, priv); -	struct ieee80211_channel *new_ch; +	struct ieee80211_local *local = sdata->local;  	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; -	int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num, -						      cbss->channel->band); +	struct cfg80211_bss *cbss = ifmgd->associated; +	struct ieee80211_bss *bss;  	struct ieee80211_chanctx *chanctx; +	enum ieee80211_band new_band; +	int new_freq; +	u8 new_chan_no; +	u8 count; +	u8 mode; +	struct ieee80211_channel *new_chan; +	struct cfg80211_chan_def new_chandef = {}; +	struct cfg80211_chan_def new_vht_chandef = {}; +	const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; +	const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; +	int secondary_channel_offset = -1;  	ASSERT_MGD_MTX(ifmgd); -	if (!ifmgd->associated) +	if (!cbss)  		return; -	if (sdata->local->scanning) +	if (local->scanning)  		return; -	/* Disregard subsequent beacons if we are already running a timer -	   processing a CSA */ - +	/* disregard subsequent announcements if we are already processing */  	if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)  		return; -	new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); -	if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) { +	sec_chan_offs = elems->sec_chan_offs; +	wide_bw_chansw_ie = elems->wide_bw_chansw_ie; + +	if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT | +			    IEEE80211_STA_DISABLE_40MHZ)) { +		sec_chan_offs = NULL; +		wide_bw_chansw_ie = NULL; +	} + +	if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) +		wide_bw_chansw_ie = NULL; + +	if (elems->ext_chansw_ie) { +		if (!ieee80211_operating_class_to_band( +				elems->ext_chansw_ie->new_operating_class, +				&new_band)) { +			sdata_info(sdata, +				   "cannot understand ECSA IE operating class %d, disconnecting\n", +				   elems->ext_chansw_ie->new_operating_class); +			ieee80211_queue_work(&local->hw, +					     &ifmgd->csa_connection_drop_work); +		} +		new_chan_no = elems->ext_chansw_ie->new_ch_num; +		count = elems->ext_chansw_ie->count; +		mode = elems->ext_chansw_ie->mode; +	} else if (elems->ch_switch_ie) { +		new_band = cbss->channel->band; +		new_chan_no = elems->ch_switch_ie->new_ch_num; +		count = elems->ch_switch_ie->count; +		mode = elems->ch_switch_ie->mode; +	} else { +		/* nothing here we understand */ +		return; +	} + +	bss = (void *)cbss->priv; + +	new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); +	new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); +	if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {  		sdata_info(sdata,  			   "AP %pM switches to unsupported channel (%d MHz), disconnecting\n",  			   ifmgd->associated->bssid, new_freq); -		ieee80211_queue_work(&sdata->local->hw, +		ieee80211_queue_work(&local->hw, +				     &ifmgd->csa_connection_drop_work); +		return; +	} + +	if (sec_chan_offs) { +		secondary_channel_offset = sec_chan_offs->sec_chan_offs; +	} else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) { +		/* if HT is enabled and the IE not present, it's still HT */ +		secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; +	} + +	switch (secondary_channel_offset) { +	default: +		/* secondary_channel_offset was present but is invalid */ +	case IEEE80211_HT_PARAM_CHA_SEC_NONE: +		cfg80211_chandef_create(&new_chandef, new_chan, +					NL80211_CHAN_HT20); +		break; +	case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: +		cfg80211_chandef_create(&new_chandef, new_chan, +					NL80211_CHAN_HT40PLUS); +		break; +	case IEEE80211_HT_PARAM_CHA_SEC_BELOW: +		cfg80211_chandef_create(&new_chandef, new_chan, +					NL80211_CHAN_HT40MINUS); +		break; +	case -1: +		cfg80211_chandef_create(&new_chandef, new_chan, +					NL80211_CHAN_NO_HT); +		break; +	} + +	if (wide_bw_chansw_ie) { +		new_vht_chandef.chan = new_chan; +		new_vht_chandef.center_freq1 = +			ieee80211_channel_to_frequency( +				wide_bw_chansw_ie->new_center_freq_seg0, +				new_band); + +		switch (wide_bw_chansw_ie->new_channel_width) { +		default: +			/* hmmm, ignore VHT and use HT if present */ +		case IEEE80211_VHT_CHANWIDTH_USE_HT: +			new_vht_chandef.chan = NULL; +			break; +		case IEEE80211_VHT_CHANWIDTH_80MHZ: +			new_vht_chandef.width = NL80211_CHAN_WIDTH_80; +			break; +		case IEEE80211_VHT_CHANWIDTH_160MHZ: +			new_vht_chandef.width = NL80211_CHAN_WIDTH_160; +			break; +		case IEEE80211_VHT_CHANWIDTH_80P80MHZ: +			/* field is otherwise reserved */ +			new_vht_chandef.center_freq2 = +				ieee80211_channel_to_frequency( +					wide_bw_chansw_ie->new_center_freq_seg1, +					new_band); +			new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80; +			break; +		} +		if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ && +		    new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80) +			chandef_downgrade(&new_vht_chandef); +		if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ && +		    new_vht_chandef.width == NL80211_CHAN_WIDTH_160) +			chandef_downgrade(&new_vht_chandef); +		if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ && +		    new_vht_chandef.width > NL80211_CHAN_WIDTH_20) +			chandef_downgrade(&new_vht_chandef); +	} + +	/* if VHT data is there validate & use it */ +	if (new_vht_chandef.chan) { +		if (!cfg80211_chandef_compatible(&new_vht_chandef, +						 &new_chandef)) { +			sdata_info(sdata, +				   "AP %pM CSA has inconsistent channel data, disconnecting\n", +				   ifmgd->associated->bssid); +			ieee80211_queue_work(&local->hw, +					     &ifmgd->csa_connection_drop_work); +			return; +		} +		new_chandef = new_vht_chandef; +	} + +	if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef, +				     IEEE80211_CHAN_DISABLED)) { +		sdata_info(sdata, +			   "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", +			   ifmgd->associated->bssid, new_freq, +			   new_chandef.width, new_chandef.center_freq1, +			   new_chandef.center_freq2); +		ieee80211_queue_work(&local->hw,  				     &ifmgd->csa_connection_drop_work);  		return;  	}  	ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; -	if (sdata->local->use_chanctx) { +	if (local->use_chanctx) {  		sdata_info(sdata,  			   "not handling channel switch with channel contexts\n"); -		ieee80211_queue_work(&sdata->local->hw, +		ieee80211_queue_work(&local->hw,  				     &ifmgd->csa_connection_drop_work);  		return;  	} -	mutex_lock(&sdata->local->chanctx_mtx); +	mutex_lock(&local->chanctx_mtx);  	if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) { -		mutex_unlock(&sdata->local->chanctx_mtx); +		mutex_unlock(&local->chanctx_mtx);  		return;  	}  	chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf), @@ -1105,39 +1207,39 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,  	if (chanctx->refcount > 1) {  		sdata_info(sdata,  			   "channel switch with multiple interfaces on the same channel, disconnecting\n"); -		ieee80211_queue_work(&sdata->local->hw, +		ieee80211_queue_work(&local->hw,  				     &ifmgd->csa_connection_drop_work); -		mutex_unlock(&sdata->local->chanctx_mtx); +		mutex_unlock(&local->chanctx_mtx);  		return;  	} -	mutex_unlock(&sdata->local->chanctx_mtx); +	mutex_unlock(&local->chanctx_mtx); -	sdata->local->csa_channel = new_ch; +	local->csa_chandef = new_chandef; -	if (sw_elem->mode) -		ieee80211_stop_queues_by_reason(&sdata->local->hw, +	if (mode) +		ieee80211_stop_queues_by_reason(&local->hw, +				IEEE80211_MAX_QUEUE_MAP,  				IEEE80211_QUEUE_STOP_REASON_CSA); -	if (sdata->local->ops->channel_switch) { +	if (local->ops->channel_switch) {  		/* use driver's channel switch callback */  		struct ieee80211_channel_switch ch_switch = {  			.timestamp = timestamp, -			.block_tx = sw_elem->mode, -			.channel = new_ch, -			.count = sw_elem->count, +			.block_tx = mode, +			.chandef = new_chandef, +			.count = count,  		}; -		drv_channel_switch(sdata->local, &ch_switch); +		drv_channel_switch(local, &ch_switch);  		return;  	}  	/* channel switch handled in software */ -	if (sw_elem->count <= 1) -		ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); +	if (count <= 1) +		ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);  	else  		mod_timer(&ifmgd->chswitch_timer, -			  TU_TO_EXP_TIME(sw_elem->count * -					 cbss->beacon_interval)); +			  TU_TO_EXP_TIME(count * cbss->beacon_interval));  }  static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, @@ -1383,6 +1485,7 @@ void ieee80211_dynamic_ps_disable_work(struct work_struct *work)  	}  	ieee80211_wake_queues_by_reason(&local->hw, +					IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_PS);  } @@ -1435,16 +1538,14 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)  	if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&  	    !(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) { -		netif_tx_stop_all_queues(sdata->dev); - -		if (drv_tx_frames_pending(local)) +		if (drv_tx_frames_pending(local)) {  			mod_timer(&local->dynamic_ps_timer, jiffies +  				  msecs_to_jiffies(  				  local->hw.conf.dynamic_ps_timeout)); -		else { +		} else {  			ieee80211_send_nullfunc(local, sdata, 1);  			/* Flush to get the tx status of nullfunc frame */ -			drv_flush(local, false); +			ieee80211_flush_queues(local, sdata);  		}  	} @@ -1455,9 +1556,6 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)  		local->hw.conf.flags |= IEEE80211_CONF_PS;  		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);  	} - -	if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) -		netif_tx_wake_all_queues(sdata->dev);  }  void ieee80211_dynamic_ps_timer(unsigned long data) @@ -1563,6 +1661,7 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,  		params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4);  		params.cw_min = ecw2cw(pos[1] & 0x0f);  		params.txop = get_unaligned_le16(pos + 2); +		params.acm = acm;  		params.uapsd = uapsd;  		mlme_dbg(sdata, @@ -1650,7 +1749,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,  		bss_conf->assoc_capability, bss->has_erp_value, bss->erp_value);  	sdata->u.mgd.beacon_timeout = usecs_to_jiffies(ieee80211_tu_to_usec( -		IEEE80211_BEACON_LOSS_COUNT * bss_conf->beacon_int)); +		beacon_loss_count * bss_conf->beacon_int));  	sdata->u.mgd.associated = cbss;  	memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN); @@ -1663,18 +1762,17 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,  		rcu_read_lock();  		ies = rcu_dereference(cbss->ies);  		if (ies) { -			u8 noa[2];  			int ret;  			ret = cfg80211_get_p2p_attr(  					ies->data, ies->len,  					IEEE80211_P2P_ATTR_ABSENCE_NOTICE, -					noa, sizeof(noa)); +					(u8 *) &bss_conf->p2p_noa_attr, +					sizeof(bss_conf->p2p_noa_attr));  			if (ret >= 2) { -				bss_conf->p2p_oppps = noa[1] & 0x80; -				bss_conf->p2p_ctwindow = noa[1] & 0x7f; +				sdata->u.mgd.p2p_noa_index = +					bss_conf->p2p_noa_attr.index;  				bss_info_changed |= BSS_CHANGED_P2P_PS; -				sdata->u.mgd.p2p_noa_index = noa[0];  			}  		}  		rcu_read_unlock(); @@ -1718,7 +1816,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,  	ieee80211_recalc_smps(sdata);  	ieee80211_recalc_ps_vif(sdata); -	netif_tx_start_all_queues(sdata->dev);  	netif_carrier_on(sdata->dev);  } @@ -1741,22 +1838,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,  	ieee80211_stop_poll(sdata);  	ifmgd->associated = NULL; - -	/* -	 * we need to commit the associated = NULL change because the -	 * scan code uses that to determine whether this iface should -	 * go to/wake up from powersave or not -- and could otherwise -	 * wake the queues erroneously. -	 */ -	smp_mb(); - -	/* -	 * Thus, we can only afterwards stop the queues -- to account -	 * for the case where another CPU is finishing a scan at this -	 * time -- we don't want the scan code to enable queues. -	 */ - -	netif_tx_stop_all_queues(sdata->dev);  	netif_carrier_off(sdata->dev);  	/* @@ -1775,7 +1856,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,  	/* flush out any pending frame (e.g. DELBA) before deauth/disassoc */  	if (tx) -		drv_flush(local, false); +		ieee80211_flush_queues(local, sdata);  	/* deauthenticate/disassociate now */  	if (tx || frame_buf) @@ -1784,7 +1865,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,  	/* flush out frame */  	if (tx) -		drv_flush(local, false); +		ieee80211_flush_queues(local, sdata);  	/* clear bssid only after building the needed mgmt frames */  	memset(ifmgd->bssid, 0, ETH_ALEN); @@ -1799,12 +1880,15 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,  	changed |= BSS_CHANGED_ASSOC;  	sdata->vif.bss_conf.assoc = false; -	sdata->vif.bss_conf.p2p_ctwindow = 0; -	sdata->vif.bss_conf.p2p_oppps = false; +	ifmgd->p2p_noa_index = -1; +	memset(&sdata->vif.bss_conf.p2p_noa_attr, 0, +	       sizeof(sdata->vif.bss_conf.p2p_noa_attr)); -	/* on the next assoc, re-program HT parameters */ +	/* on the next assoc, re-program HT/VHT parameters */  	memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));  	memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); +	memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa)); +	memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask));  	sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL; @@ -1830,8 +1914,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,  	del_timer_sync(&sdata->u.mgd.timer);  	del_timer_sync(&sdata->u.mgd.chswitch_timer); -	sdata->u.mgd.timers_running = 0; -  	sdata->vif.bss_conf.dtim_period = 0;  	ifmgd->flags = 0; @@ -1956,7 +2038,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)  	ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);  	run_again(ifmgd, ifmgd->probe_timeout);  	if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) -		drv_flush(sdata->local, false); +		ieee80211_flush_queues(sdata->local, sdata);  }  static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, @@ -1980,12 +2062,15 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,  		goto out;  	} -	if (beacon) +	if (beacon) {  		mlme_dbg_ratelimited(sdata, -				     "detected beacon loss from AP - probing\n"); +				     "detected beacon loss from AP (missed %d beacons) - probing\n", +				     beacon_loss_count); -	ieee80211_cqm_rssi_notify(&sdata->vif, -		NL80211_CQM_RSSI_BEACON_LOSS_EVENT, GFP_KERNEL); +		ieee80211_cqm_rssi_notify(&sdata->vif, +					  NL80211_CQM_RSSI_BEACON_LOSS_EVENT, +					  GFP_KERNEL); +	}  	/*  	 * The driver/our work has already reported this event or the @@ -2079,6 +2164,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)  			       true, frame_buf);  	ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;  	ieee80211_wake_queues_by_reason(&sdata->local->hw, +					IEEE80211_MAX_QUEUE_MAP,  					IEEE80211_QUEUE_STOP_REASON_CSA);  	mutex_unlock(&ifmgd->mtx); @@ -2130,7 +2216,6 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif)  	trace_api_beacon_loss(sdata); -	WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR);  	sdata->u.mgd.connection_loss = false;  	ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);  } @@ -2180,7 +2265,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,  	u32 tx_flags = 0;  	pos = mgmt->u.auth.variable; -	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); +	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);  	if (!elems.challenge)  		return;  	auth_data->expected_transaction = 4; @@ -2445,7 +2530,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,  	}  	pos = mgmt->u.assoc_resp.variable; -	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); +	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);  	if (!elems.supp_rates) {  		sdata_info(sdata, "no SuppRates element in AssocResp\n"); @@ -2614,13 +2699,13 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,  		   capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));  	pos = mgmt->u.assoc_resp.variable; -	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems); +	ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);  	if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && -	    elems.timeout_int && elems.timeout_int_len == 5 && -	    elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) { +	    elems.timeout_int && +	    elems.timeout_int->type == WLAN_TIMEOUT_ASSOC_COMEBACK) {  		u32 tu, ms; -		tu = get_unaligned_le32(elems.timeout_int + 1); +		tu = le32_to_cpu(elems.timeout_int->value);  		ms = tu * 1024 / 1000;  		sdata_info(sdata,  			   "%pM rejected association temporarily; comeback duration %u TU (%u ms)\n", @@ -2669,6 +2754,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_channel *channel;  	bool need_ps = false; +	lockdep_assert_held(&sdata->u.mgd.mtx); +  	if ((sdata->u.mgd.associated &&  	     ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) ||  	    (sdata->u.mgd.assoc_data && @@ -2683,7 +2770,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  		}  	} -	if (elems->ds_params && elems->ds_params_len == 1) +	if (elems->ds_params)  		freq = ieee80211_channel_to_frequency(elems->ds_params[0],  						      rx_status->band);  	else @@ -2699,7 +2786,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  	if (bss)  		ieee80211_rx_bss_put(local, bss); -	if (!sdata->u.mgd.associated) +	if (!sdata->u.mgd.associated || +	    !ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid))  		return;  	if (need_ps) { @@ -2708,10 +2796,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,  		mutex_unlock(&local->iflist_mtx);  	} -	if (elems->ch_switch_ie && -	    memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid, ETH_ALEN) == 0) -		ieee80211_sta_process_chanswitch(sdata, elems->ch_switch_ie, -						 bss, rx_status->mactime); +	ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, elems); +  } @@ -2736,7 +2822,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,  		return;  	ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen, -				&elems); +			       false, &elems);  	ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); @@ -2819,7 +2905,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  	if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&  	    ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) {  		ieee802_11_parse_elems(mgmt->u.beacon.variable, -				       len - baselen, &elems); +				       len - baselen, false, &elems);  		ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);  		ifmgd->assoc_data->have_beacon = true; @@ -2929,7 +3015,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  	ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);  	ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable, -					  len - baselen, &elems, +					  len - baselen, false, &elems,  					  care_about_ies, ncrc);  	if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) { @@ -2961,22 +3047,30 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  	}  	if (sdata->vif.p2p) { -		u8 noa[2]; +		struct ieee80211_p2p_noa_attr noa = {};  		int ret;  		ret = cfg80211_get_p2p_attr(mgmt->u.beacon.variable,  					    len - baselen,  					    IEEE80211_P2P_ATTR_ABSENCE_NOTICE, -					    noa, sizeof(noa)); -		if (ret >= 2 && sdata->u.mgd.p2p_noa_index != noa[0]) { -			bss_conf->p2p_oppps = noa[1] & 0x80; -			bss_conf->p2p_ctwindow = noa[1] & 0x7f; +					    (u8 *) &noa, sizeof(noa)); +		if (ret >= 2) { +			if (sdata->u.mgd.p2p_noa_index != noa.index) { +				/* valid noa_attr and index changed */ +				sdata->u.mgd.p2p_noa_index = noa.index; +				memcpy(&bss_conf->p2p_noa_attr, &noa, sizeof(noa)); +				changed |= BSS_CHANGED_P2P_PS; +				/* +				 * make sure we update all information, the CRC +				 * mechanism doesn't look at P2P attributes. +				 */ +				ifmgd->beacon_crc_valid = false; +			} +		} else if (sdata->u.mgd.p2p_noa_index != -1) { +			/* noa_attr not found and we had valid noa_attr before */ +			sdata->u.mgd.p2p_noa_index = -1; +			memset(&bss_conf->p2p_noa_attr, 0, sizeof(bss_conf->p2p_noa_attr));  			changed |= BSS_CHANGED_P2P_PS; -			sdata->u.mgd.p2p_noa_index = noa[0]; -			/* -			 * make sure we update all information, the CRC -			 * mechanism doesn't look at P2P attributes. -			 */  			ifmgd->beacon_crc_valid = false;  		}  	} @@ -3018,7 +3112,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,  		changed |= BSS_CHANGED_DTIM_PERIOD;  	} -	if (elems.erp_info && elems.erp_info_len >= 1) { +	if (elems.erp_info) {  		erp_valid = true;  		erp_value = elems.erp_info[0];  	} else { @@ -3068,6 +3162,8 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  	enum rx_mgmt_action rma = RX_MGMT_NONE;  	u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];  	u16 fc; +	struct ieee802_11_elems elems; +	int ies_len;  	rx_status = (struct ieee80211_rx_status *) skb->cb;  	mgmt = (struct ieee80211_mgmt *) skb->data; @@ -3097,14 +3193,48 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  		rma = ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, &bss);  		break;  	case IEEE80211_STYPE_ACTION: -		switch (mgmt->u.action.category) { -		case WLAN_CATEGORY_SPECTRUM_MGMT: +		if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) { +			ies_len = skb->len - +				  offsetof(struct ieee80211_mgmt, +					   u.action.u.chan_switch.variable); + +			if (ies_len < 0) +				break; + +			ieee802_11_parse_elems( +				mgmt->u.action.u.chan_switch.variable, +				ies_len, true, &elems); + +			if (elems.parse_error) +				break; +  			ieee80211_sta_process_chanswitch(sdata, -					&mgmt->u.action.u.chan_switch.sw_elem, -					(void *)ifmgd->associated->priv, -					rx_status->mactime); -			break; +							 rx_status->mactime, +							 &elems); +		} else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { +			ies_len = skb->len - +				  offsetof(struct ieee80211_mgmt, +					   u.action.u.ext_chan_switch.variable); + +			if (ies_len < 0) +				break; + +			ieee802_11_parse_elems( +				mgmt->u.action.u.ext_chan_switch.variable, +				ies_len, true, &elems); + +			if (elems.parse_error) +				break; + +			/* for the handling code pretend this was also an IE */ +			elems.ext_chansw_ie = +				&mgmt->u.action.u.ext_chan_switch.data; + +			ieee80211_sta_process_chanswitch(sdata, +							 rx_status->mactime, +							 &elems);  		} +		break;  	}  	mutex_unlock(&ifmgd->mtx); @@ -3140,15 +3270,8 @@ static void ieee80211_sta_timer(unsigned long data)  {  	struct ieee80211_sub_if_data *sdata =  		(struct ieee80211_sub_if_data *) data; -	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; -	struct ieee80211_local *local = sdata->local; -	if (local->quiescing) { -		set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); -		return; -	} - -	ieee80211_queue_work(&local->hw, &sdata->work); +	ieee80211_queue_work(&sdata->local->hw, &sdata->work);  }  static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, @@ -3500,72 +3623,6 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)  	}  } -#ifdef CONFIG_PM -void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - -	/* -	 * Stop timers before deleting work items, as timers -	 * could race and re-add the work-items. They will be -	 * re-established on connection. -	 */ -	del_timer_sync(&ifmgd->conn_mon_timer); -	del_timer_sync(&ifmgd->bcn_mon_timer); - -	/* -	 * we need to use atomic bitops for the running bits -	 * only because both timers might fire at the same -	 * time -- the code here is properly synchronised. -	 */ - -	cancel_work_sync(&ifmgd->request_smps_work); - -	cancel_work_sync(&ifmgd->monitor_work); -	cancel_work_sync(&ifmgd->beacon_connection_loss_work); -	cancel_work_sync(&ifmgd->csa_connection_drop_work); -	if (del_timer_sync(&ifmgd->timer)) -		set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); - -	if (del_timer_sync(&ifmgd->chswitch_timer)) -		set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running); -	cancel_work_sync(&ifmgd->chswitch_work); -} - -void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) -{ -	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - -	mutex_lock(&ifmgd->mtx); -	if (!ifmgd->associated) { -		mutex_unlock(&ifmgd->mtx); -		return; -	} - -	if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) { -		sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME; -		mlme_dbg(sdata, "driver requested disconnect after resume\n"); -		ieee80211_sta_connection_lost(sdata, -					      ifmgd->associated->bssid, -					      WLAN_REASON_UNSPECIFIED, -					      true); -		mutex_unlock(&ifmgd->mtx); -		return; -	} -	mutex_unlock(&ifmgd->mtx); - -	if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running)) -		add_timer(&ifmgd->timer); -	if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running)) -		add_timer(&ifmgd->chswitch_timer); -	ieee80211_sta_reset_beacon_monitor(sdata); - -	mutex_lock(&sdata->local->mtx); -	ieee80211_restart_sta_timer(sdata); -	mutex_unlock(&sdata->local->mtx); -} -#endif -  /* interface setup */  void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)  { @@ -3590,8 +3647,9 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)  	ifmgd->flags = 0;  	ifmgd->powersave = sdata->wdev.ps; -	ifmgd->uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES; -	ifmgd->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN; +	ifmgd->uapsd_queues = sdata->local->hw.uapsd_queues; +	ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len; +	ifmgd->p2p_noa_index = -1;  	mutex_init(&ifmgd->mtx); @@ -4089,6 +4147,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,  		ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;  	} +	if (req->flags & ASSOC_REQ_DISABLE_VHT) +		ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; +  	/* Also disable HT if we don't support it or the AP doesn't use WMM */  	sband = local->hw.wiphy->bands[req->bss->channel->band];  	if (!sband->ht_cap.ht_supported || @@ -4112,6 +4173,10 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,  	memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask,  	       sizeof(ifmgd->ht_capa_mask)); +	memcpy(&ifmgd->vht_capa, &req->vht_capa, sizeof(ifmgd->vht_capa)); +	memcpy(&ifmgd->vht_capa_mask, &req->vht_capa_mask, +	       sizeof(ifmgd->vht_capa_mask)); +  	if (req->ie && req->ie_len) {  		memcpy(assoc_data->ie, req->ie, req->ie_len);  		assoc_data->ie_len = req->ie_len; @@ -4149,7 +4214,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,  	rcu_read_unlock();  	if (bss->wmm_used && bss->uapsd_supported && -	    (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { +	    (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) && +	    sdata->wmm_acm != 0xff) {  		assoc_data->uapsd = true;  		ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;  	} else {  |