diff options
Diffstat (limited to 'net/mac80211/cfg.c')
| -rw-r--r-- | net/mac80211/cfg.c | 531 | 
1 files changed, 28 insertions, 503 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c2bd1b6a6922..166a29fe6c35 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1169,8 +1169,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,  		 * rc isn't initialized here yet, so ignore it  		 */  		__ieee80211_vht_handle_opmode(sdata, sta, -					      params->opmode_notif, -					      band, false); +					      params->opmode_notif, band);  	}  	if (ieee80211_vif_is_mesh(&sdata->vif)) @@ -1216,16 +1215,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,  	if (!sta)  		return -ENOMEM; -	/* -	 * defaults -- if userspace wants something else we'll -	 * change it accordingly in sta_apply_parameters() -	 */ -	if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && -	    !(params->sta_flags_set & (BIT(NL80211_STA_FLAG_AUTHENTICATED) | -					BIT(NL80211_STA_FLAG_ASSOCIATED)))) { -		sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); -		sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); -	}  	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))  		sta->sta.tdls = true; @@ -1994,6 +1983,11 @@ static int ieee80211_scan(struct wiphy *wiphy,  	return ieee80211_request_scan(sdata, req);  } +static void ieee80211_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev) +{ +	ieee80211_scan_cancel(wiphy_priv(wiphy)); +} +  static int  ieee80211_sched_scan_start(struct wiphy *wiphy,  			   struct net_device *dev, @@ -2509,294 +2503,6 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,  	return 0;  } -static bool ieee80211_coalesce_started_roc(struct ieee80211_local *local, -					   struct ieee80211_roc_work *new_roc, -					   struct ieee80211_roc_work *cur_roc) -{ -	unsigned long now = jiffies; -	unsigned long remaining = cur_roc->hw_start_time + -				  msecs_to_jiffies(cur_roc->duration) - -				  now; - -	if (WARN_ON(!cur_roc->started || !cur_roc->hw_begun)) -		return false; - -	/* if it doesn't fit entirely, schedule a new one */ -	if (new_roc->duration > jiffies_to_msecs(remaining)) -		return false; - -	ieee80211_handle_roc_started(new_roc); - -	/* add to dependents so we send the expired event properly */ -	list_add_tail(&new_roc->list, &cur_roc->dependents); -	return true; -} - -static u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local) -{ -	lockdep_assert_held(&local->mtx); - -	local->roc_cookie_counter++; - -	/* wow, you wrapped 64 bits ... more likely a bug */ -	if (WARN_ON(local->roc_cookie_counter == 0)) -		local->roc_cookie_counter++; - -	return local->roc_cookie_counter; -} - -static int ieee80211_start_roc_work(struct ieee80211_local *local, -				    struct ieee80211_sub_if_data *sdata, -				    struct ieee80211_channel *channel, -				    unsigned int duration, u64 *cookie, -				    struct sk_buff *txskb, -				    enum ieee80211_roc_type type) -{ -	struct ieee80211_roc_work *roc, *tmp; -	bool queued = false; -	int ret; - -	lockdep_assert_held(&local->mtx); - -	if (local->use_chanctx && !local->ops->remain_on_channel) -		return -EOPNOTSUPP; - -	roc = kzalloc(sizeof(*roc), GFP_KERNEL); -	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; -	roc->frame = txskb; -	roc->type = type; -	roc->sdata = sdata; -	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) { -		roc->cookie = ieee80211_mgmt_tx_cookie(local); -		*cookie = roc->cookie; -	} else { -		roc->mgmt_tx_cookie = *cookie; -	} - -	/* if there's one pending or we're scanning, queue this one */ -	if (!list_empty(&local->roc_list) || -	    local->scanning || ieee80211_is_radar_required(local)) -		goto out_check_combine; - -	/* if not HW assist, just queue & schedule work */ -	if (!local->ops->remain_on_channel) { -		ieee80211_queue_delayed_work(&local->hw, &roc->work, 0); -		goto out_queue; -	} - -	/* otherwise actually kick it off here (for error handling) */ - -	ret = drv_remain_on_channel(local, sdata, channel, duration, type); -	if (ret) { -		kfree(roc); -		return ret; -	} - -	roc->started = true; -	goto out_queue; - - out_check_combine: -	list_for_each_entry(tmp, &local->roc_list, list) { -		if (tmp->chan != channel || tmp->sdata != sdata) -			continue; - -		/* -		 * Extend this ROC if possible: -		 * -		 * If it hasn't started yet, just increase the duration -		 * and add the new one to the list of dependents. -		 * If the type of the new ROC has higher priority, modify the -		 * type of the previous one to match that of the new one. -		 */ -		if (!tmp->started) { -			list_add_tail(&roc->list, &tmp->dependents); -			tmp->duration = max(tmp->duration, roc->duration); -			tmp->type = max(tmp->type, roc->type); -			queued = true; -			break; -		} - -		/* If it has already started, it's more difficult ... */ -		if (local->ops->remain_on_channel) { -			/* -			 * In the offloaded ROC case, if it hasn't begun, add -			 * this new one to the dependent list to be handled -			 * when the master one begins. If it has begun, -			 * check if it fits entirely within the existing one, -			 * in which case it will just be dependent as well. -			 * Otherwise, schedule it by itself. -			 */ -			if (!tmp->hw_begun) { -				list_add_tail(&roc->list, &tmp->dependents); -				queued = true; -				break; -			} - -			if (ieee80211_coalesce_started_roc(local, roc, tmp)) -				queued = true; -		} else if (del_timer_sync(&tmp->work.timer)) { -			unsigned long new_end; - -			/* -			 * In the software ROC case, cancel the timer, if -			 * that fails then the finish work is already -			 * queued/pending and thus we queue the new ROC -			 * normally, if that succeeds then we can extend -			 * the timer duration and TX the frame (if any.) -			 */ - -			list_add_tail(&roc->list, &tmp->dependents); -			queued = true; - -			new_end = jiffies + msecs_to_jiffies(roc->duration); - -			/* ok, it was started & we canceled timer */ -			if (time_after(new_end, tmp->work.timer.expires)) -				mod_timer(&tmp->work.timer, new_end); -			else -				add_timer(&tmp->work.timer); - -			ieee80211_handle_roc_started(roc); -		} -		break; -	} - - out_queue: -	if (!queued) -		list_add_tail(&roc->list, &local->roc_list); - -	return 0; -} - -static int ieee80211_remain_on_channel(struct wiphy *wiphy, -				       struct wireless_dev *wdev, -				       struct ieee80211_channel *chan, -				       unsigned int duration, -				       u64 *cookie) -{ -	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); -	struct ieee80211_local *local = sdata->local; -	int ret; - -	mutex_lock(&local->mtx); -	ret = ieee80211_start_roc_work(local, sdata, chan, -				       duration, cookie, NULL, -				       IEEE80211_ROC_TYPE_NORMAL); -	mutex_unlock(&local->mtx); - -	return ret; -} - -static int ieee80211_cancel_roc(struct ieee80211_local *local, -				u64 cookie, bool mgmt_tx) -{ -	struct ieee80211_roc_work *roc, *tmp, *found = NULL; -	int ret; - -	mutex_lock(&local->mtx); -	list_for_each_entry_safe(roc, tmp, &local->roc_list, list) { -		struct ieee80211_roc_work *dep, *tmp2; - -		list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) { -			if (!mgmt_tx && dep->cookie != cookie) -				continue; -			else if (mgmt_tx && dep->mgmt_tx_cookie != cookie) -				continue; -			/* found dependent item -- just remove it */ -			list_del(&dep->list); -			mutex_unlock(&local->mtx); - -			ieee80211_roc_notify_destroy(dep, true); -			return 0; -		} - -		if (!mgmt_tx && roc->cookie != cookie) -			continue; -		else if (mgmt_tx && roc->mgmt_tx_cookie != cookie) -			continue; - -		found = roc; -		break; -	} - -	if (!found) { -		mutex_unlock(&local->mtx); -		return -ENOENT; -	} - -	/* -	 * We found the item to cancel, so do that. Note that it -	 * may have dependents, which we also cancel (and send -	 * the expired signal for.) Not doing so would be quite -	 * tricky here, but we may need to fix it later. -	 */ - -	if (local->ops->remain_on_channel) { -		if (found->started) { -			ret = drv_cancel_remain_on_channel(local); -			if (WARN_ON_ONCE(ret)) { -				mutex_unlock(&local->mtx); -				return ret; -			} -		} - -		list_del(&found->list); - -		if (found->started) -			ieee80211_start_next_roc(local); -		mutex_unlock(&local->mtx); - -		ieee80211_roc_notify_destroy(found, true); -	} else { -		/* work may be pending so use it all the time */ -		found->abort = true; -		ieee80211_queue_delayed_work(&local->hw, &found->work, 0); - -		mutex_unlock(&local->mtx); - -		/* work will clean up etc */ -		flush_delayed_work(&found->work); -		WARN_ON(!found->to_be_freed); -		kfree(found); -	} - -	return 0; -} - -static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, -					      struct wireless_dev *wdev, -					      u64 cookie) -{ -	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); -	struct ieee80211_local *local = sdata->local; - -	return ieee80211_cancel_roc(local, cookie, false); -} -  static int ieee80211_start_radar_detection(struct wiphy *wiphy,  					   struct net_device *dev,  					   struct cfg80211_chan_def *chandef, @@ -3267,9 +2973,21 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,  	return err;  } -static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local, -					      struct sk_buff *skb, u64 *cookie, -					      gfp_t gfp) +u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local) +{ +	lockdep_assert_held(&local->mtx); + +	local->roc_cookie_counter++; + +	/* wow, you wrapped 64 bits ... more likely a bug */ +	if (WARN_ON(local->roc_cookie_counter == 0)) +		local->roc_cookie_counter++; + +	return local->roc_cookie_counter; +} + +int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb, +			     u64 *cookie, gfp_t gfp)  {  	unsigned long spin_flags;  	struct sk_buff *ack_skb; @@ -3277,7 +2995,7 @@ static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,  	ack_skb = skb_copy(skb, gfp);  	if (!ack_skb) -		return ERR_PTR(-ENOMEM); +		return -ENOMEM;  	spin_lock_irqsave(&local->ack_status_lock, spin_flags);  	id = idr_alloc(&local->ack_status_frames, ack_skb, @@ -3286,7 +3004,7 @@ static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,  	if (id < 0) {  		kfree_skb(ack_skb); -		return ERR_PTR(-ENOMEM); +		return -ENOMEM;  	}  	IEEE80211_SKB_CB(skb)->ack_frame_id = id; @@ -3294,200 +3012,7 @@ static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,  	*cookie = ieee80211_mgmt_tx_cookie(local);  	IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie; -	return ack_skb; -} - -static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, -			     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, *ack_skb; -	struct sta_info *sta; -	const struct ieee80211_mgmt *mgmt = (void *)params->buf; -	bool need_offchan = false; -	u32 flags; -	int ret; -	u8 *data; - -	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 (params->no_cck) -		flags |= IEEE80211_TX_CTL_NO_CCK_RATE; - -	switch (sdata->vif.type) { -	case NL80211_IFTYPE_ADHOC: -		if (!sdata->vif.bss_conf.ibss_joined) -			need_offchan = true; -		/* fall through */ -#ifdef CONFIG_MAC80211_MESH -	case NL80211_IFTYPE_MESH_POINT: -		if (ieee80211_vif_is_mesh(&sdata->vif) && -		    !sdata->u.mesh.mesh_id_len) -			need_offchan = true; -		/* fall through */ -#endif -	case NL80211_IFTYPE_AP: -	case NL80211_IFTYPE_AP_VLAN: -	case NL80211_IFTYPE_P2P_GO: -		if (sdata->vif.type != NL80211_IFTYPE_ADHOC && -		    !ieee80211_vif_is_mesh(&sdata->vif) && -		    !rcu_access_pointer(sdata->bss->beacon)) -			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_SPECTRUM_MGMT) -			break; -		rcu_read_lock(); -		sta = sta_info_get(sdata, mgmt->da); -		rcu_read_unlock(); -		if (!sta) -			return -ENOLINK; -		break; -	case NL80211_IFTYPE_STATION: -	case NL80211_IFTYPE_P2P_CLIENT: -		sdata_lock(sdata); -		if (!sdata->u.mgd.associated || -		    (params->offchan && params->wait && -		     local->ops->remain_on_channel && -		     memcmp(sdata->u.mgd.associated->bssid, -			    mgmt->bssid, ETH_ALEN))) -			need_offchan = true; -		sdata_unlock(sdata); -		break; -	case NL80211_IFTYPE_P2P_DEVICE: -		need_offchan = true; -		break; -	default: -		return -EOPNOTSUPP; -	} - -	/* configurations requiring offchan cannot work if no channel has been -	 * specified -	 */ -	if (need_offchan && !params->chan) -		return -EINVAL; - -	mutex_lock(&local->mtx); - -	/* Check if the operating channel is the requested channel */ -	if (!need_offchan) { -		struct ieee80211_chanctx_conf *chanctx_conf; - -		rcu_read_lock(); -		chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - -		if (chanctx_conf) { -			need_offchan = params->chan && -				       (params->chan != -					chanctx_conf->def.chan); -		} else if (!params->chan) { -			ret = -EINVAL; -			rcu_read_unlock(); -			goto out_unlock; -		} else { -			need_offchan = true; -		} -		rcu_read_unlock(); -	} - -	if (need_offchan && !params->offchan) { -		ret = -EBUSY; -		goto out_unlock; -	} - -	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); - -	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_MESH_POINT || -	     sdata->vif.type == NL80211_IFTYPE_ADHOC) && -	    params->n_csa_offsets) { -		int i; -		struct beacon_data *beacon = NULL; - -		rcu_read_lock(); - -		if (sdata->vif.type == NL80211_IFTYPE_AP) -			beacon = rcu_dereference(sdata->u.ap.beacon); -		else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) -			beacon = rcu_dereference(sdata->u.ibss.presp); -		else if (ieee80211_vif_is_mesh(&sdata->vif)) -			beacon = rcu_dereference(sdata->u.mesh.beacon); - -		if (beacon) -			for (i = 0; i < params->n_csa_offsets; i++) -				data[params->csa_offsets[i]] = -					beacon->csa_current_counter; - -		rcu_read_unlock(); -	} - -	IEEE80211_SKB_CB(skb)->flags = flags; - -	skb->dev = sdata->dev; - -	if (!params->dont_wait_for_ack) { -		/* make a copy to preserve the frame contents -		 * in case of encryption. -		 */ -		ack_skb = ieee80211_make_ack_skb(local, skb, cookie, -						 GFP_KERNEL); -		if (IS_ERR(ack_skb)) { -			ret = PTR_ERR(ack_skb); -			kfree_skb(skb); -			goto out_unlock; -		} -	} else { -		/* for cookie below */ -		ack_skb = skb; -	} - -	if (!need_offchan) { -		ieee80211_tx_skb(sdata, skb); -		ret = 0; -		goto out_unlock; -	} - -	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN | -					IEEE80211_TX_INTFL_OFFCHAN_TX_OK; -	if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) -		IEEE80211_SKB_CB(skb)->hw_queue = -			local->hw.offchannel_tx_hw_queue; - -	/* This will handle all kinds of coalescing and immediate TX */ -	ret = ieee80211_start_roc_work(local, sdata, params->chan, -				       params->wait, cookie, skb, -				       IEEE80211_ROC_TYPE_MGMT_TX); -	if (ret) -		kfree_skb(skb); - out_unlock: -	mutex_unlock(&local->mtx); -	return ret; -} - -static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, -					 struct wireless_dev *wdev, -					 u64 cookie) -{ -	struct ieee80211_local *local = wiphy_priv(wiphy); - -	return ieee80211_cancel_roc(local, cookie, true); +	return 0;  }  static void ieee80211_mgmt_frame_register(struct wiphy *wiphy, @@ -3565,7 +3090,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);  	struct ieee80211_local *local = sdata->local;  	struct ieee80211_qos_hdr *nullfunc; -	struct sk_buff *skb, *ack_skb; +	struct sk_buff *skb;  	int size = sizeof(*nullfunc);  	__le16 fc;  	bool qos; @@ -3633,10 +3158,9 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,  	if (qos)  		nullfunc->qos_ctrl = cpu_to_le16(7); -	ack_skb = ieee80211_make_ack_skb(local, skb, cookie, GFP_ATOMIC); -	if (IS_ERR(ack_skb)) { +	ret = ieee80211_attach_ack_skb(local, skb, cookie, GFP_ATOMIC); +	if (ret) {  		kfree_skb(skb); -		ret = PTR_ERR(ack_skb);  		goto unlock;  	} @@ -3838,6 +3362,7 @@ const struct cfg80211_ops mac80211_config_ops = {  	.suspend = ieee80211_suspend,  	.resume = ieee80211_resume,  	.scan = ieee80211_scan, +	.abort_scan = ieee80211_abort_scan,  	.sched_scan_start = ieee80211_sched_scan_start,  	.sched_scan_stop = ieee80211_sched_scan_stop,  	.auth = ieee80211_auth,  | 
