380 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			380 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: ISC
 | |
| /*
 | |
|  * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
 | |
|  * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 | |
|  */
 | |
| 
 | |
| #include "wil6210.h"
 | |
| #include "wmi.h"
 | |
| 
 | |
| #define P2P_WILDCARD_SSID "DIRECT-"
 | |
| #define P2P_DMG_SOCIAL_CHANNEL 2
 | |
| #define P2P_SEARCH_DURATION_MS 500
 | |
| #define P2P_DEFAULT_BI 100
 | |
| 
 | |
| static int wil_p2p_start_listen(struct wil6210_vif *vif)
 | |
| {
 | |
| 	struct wil6210_priv *wil = vif_to_wil(vif);
 | |
| 	struct wil_p2p_info *p2p = &vif->p2p;
 | |
| 	u8 channel = p2p->listen_chan.hw_value;
 | |
| 	int rc;
 | |
| 
 | |
| 	lockdep_assert_held(&wil->mutex);
 | |
| 
 | |
| 	rc = wmi_p2p_cfg(vif, channel, P2P_DEFAULT_BI);
 | |
| 	if (rc) {
 | |
| 		wil_err(wil, "wmi_p2p_cfg failed\n");
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
 | |
| 	if (rc) {
 | |
| 		wil_err(wil, "wmi_set_ssid failed\n");
 | |
| 		goto out_stop;
 | |
| 	}
 | |
| 
 | |
| 	rc = wmi_start_listen(vif);
 | |
| 	if (rc) {
 | |
| 		wil_err(wil, "wmi_start_listen failed\n");
 | |
| 		goto out_stop;
 | |
| 	}
 | |
| 
 | |
| 	INIT_WORK(&p2p->discovery_expired_work, wil_p2p_listen_expired);
 | |
| 	mod_timer(&p2p->discovery_timer,
 | |
| 		  jiffies + msecs_to_jiffies(p2p->listen_duration));
 | |
| out_stop:
 | |
| 	if (rc)
 | |
| 		wmi_stop_discovery(vif);
 | |
| 
 | |
| out:
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request)
 | |
| {
 | |
| 	return (request->n_channels == 1) &&
 | |
| 	       (request->channels[0]->hw_value == P2P_DMG_SOCIAL_CHANNEL);
 | |
| }
 | |
| 
 | |
| int wil_p2p_search(struct wil6210_vif *vif,
 | |
| 		   struct cfg80211_scan_request *request)
 | |
| {
 | |
| 	struct wil6210_priv *wil = vif_to_wil(vif);
 | |
| 	int rc;
 | |
| 	struct wil_p2p_info *p2p = &vif->p2p;
 | |
| 
 | |
| 	wil_dbg_misc(wil, "p2p_search: channel %d\n", P2P_DMG_SOCIAL_CHANNEL);
 | |
| 
 | |
| 	lockdep_assert_held(&wil->mutex);
 | |
| 
 | |
| 	if (p2p->discovery_started) {
 | |
| 		wil_err(wil, "search failed. discovery already ongoing\n");
 | |
| 		rc = -EBUSY;
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	rc = wmi_p2p_cfg(vif, P2P_DMG_SOCIAL_CHANNEL, P2P_DEFAULT_BI);
 | |
| 	if (rc) {
 | |
| 		wil_err(wil, "wmi_p2p_cfg failed\n");
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
 | |
| 	if (rc) {
 | |
| 		wil_err(wil, "wmi_set_ssid failed\n");
 | |
| 		goto out_stop;
 | |
| 	}
 | |
| 
 | |
| 	/* Set application IE to probe request and probe response */
 | |
| 	rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
 | |
| 			request->ie_len, request->ie);
 | |
| 	if (rc) {
 | |
| 		wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_REQ) failed\n");
 | |
| 		goto out_stop;
 | |
| 	}
 | |
| 
 | |
| 	/* supplicant doesn't provide Probe Response IEs. As a workaround -
 | |
| 	 * re-use Probe Request IEs
 | |
| 	 */
 | |
| 	rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP,
 | |
| 			request->ie_len, request->ie);
 | |
| 	if (rc) {
 | |
| 		wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_RESP) failed\n");
 | |
| 		goto out_stop;
 | |
| 	}
 | |
| 
 | |
| 	rc = wmi_start_search(vif);
 | |
| 	if (rc) {
 | |
| 		wil_err(wil, "wmi_start_search failed\n");
 | |
| 		goto out_stop;
 | |
| 	}
 | |
| 
 | |
| 	p2p->discovery_started = 1;
 | |
| 	INIT_WORK(&p2p->discovery_expired_work, wil_p2p_search_expired);
 | |
| 	mod_timer(&p2p->discovery_timer,
 | |
| 		  jiffies + msecs_to_jiffies(P2P_SEARCH_DURATION_MS));
 | |
| 
 | |
| out_stop:
 | |
| 	if (rc)
 | |
| 		wmi_stop_discovery(vif);
 | |
| 
 | |
| out:
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev,
 | |
| 		   unsigned int duration, struct ieee80211_channel *chan,
 | |
| 		   u64 *cookie)
 | |
| {
 | |
| 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
 | |
| 	struct wil_p2p_info *p2p = &vif->p2p;
 | |
| 	int rc;
 | |
| 
 | |
| 	if (!chan)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	wil_dbg_misc(wil, "p2p_listen: duration %d\n", duration);
 | |
| 
 | |
| 	mutex_lock(&wil->mutex);
 | |
| 
 | |
| 	if (p2p->discovery_started) {
 | |
| 		wil_err(wil, "discovery already ongoing\n");
 | |
| 		rc = -EBUSY;
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	memcpy(&p2p->listen_chan, chan, sizeof(*chan));
 | |
| 	*cookie = ++p2p->cookie;
 | |
| 	p2p->listen_duration = duration;
 | |
| 
 | |
| 	mutex_lock(&wil->vif_mutex);
 | |
| 	if (vif->scan_request) {
 | |
| 		wil_dbg_misc(wil, "Delaying p2p listen until scan done\n");
 | |
| 		p2p->pending_listen_wdev = wdev;
 | |
| 		p2p->discovery_started = 1;
 | |
| 		rc = 0;
 | |
| 		mutex_unlock(&wil->vif_mutex);
 | |
| 		goto out;
 | |
| 	}
 | |
| 	mutex_unlock(&wil->vif_mutex);
 | |
| 
 | |
| 	rc = wil_p2p_start_listen(vif);
 | |
| 	if (rc)
 | |
| 		goto out;
 | |
| 
 | |
| 	p2p->discovery_started = 1;
 | |
| 	if (vif->mid == 0)
 | |
| 		wil->radio_wdev = wdev;
 | |
| 
 | |
| 	cfg80211_ready_on_channel(wdev, *cookie, chan, duration,
 | |
| 				  GFP_KERNEL);
 | |
| 
 | |
| out:
 | |
| 	mutex_unlock(&wil->mutex);
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| u8 wil_p2p_stop_discovery(struct wil6210_vif *vif)
 | |
| {
 | |
| 	struct wil_p2p_info *p2p = &vif->p2p;
 | |
| 	u8 started = p2p->discovery_started;
 | |
| 
 | |
| 	if (p2p->discovery_started) {
 | |
| 		if (p2p->pending_listen_wdev) {
 | |
| 			/* discovery not really started, only pending */
 | |
| 			p2p->pending_listen_wdev = NULL;
 | |
| 		} else {
 | |
| 			del_timer_sync(&p2p->discovery_timer);
 | |
| 			wmi_stop_discovery(vif);
 | |
| 		}
 | |
| 		p2p->discovery_started = 0;
 | |
| 	}
 | |
| 
 | |
| 	return started;
 | |
| }
 | |
| 
 | |
| int wil_p2p_cancel_listen(struct wil6210_vif *vif, u64 cookie)
 | |
| {
 | |
| 	struct wil6210_priv *wil = vif_to_wil(vif);
 | |
| 	struct wil_p2p_info *p2p = &vif->p2p;
 | |
| 	u8 started;
 | |
| 
 | |
| 	mutex_lock(&wil->mutex);
 | |
| 
 | |
| 	if (cookie != p2p->cookie) {
 | |
| 		wil_info(wil, "Cookie mismatch: 0x%016llx vs. 0x%016llx\n",
 | |
| 			 p2p->cookie, cookie);
 | |
| 		mutex_unlock(&wil->mutex);
 | |
| 		return -ENOENT;
 | |
| 	}
 | |
| 
 | |
| 	started = wil_p2p_stop_discovery(vif);
 | |
| 
 | |
| 	mutex_unlock(&wil->mutex);
 | |
| 
 | |
| 	if (!started) {
 | |
| 		wil_err(wil, "listen not started\n");
 | |
| 		return -ENOENT;
 | |
| 	}
 | |
| 
 | |
| 	mutex_lock(&wil->vif_mutex);
 | |
| 	cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif),
 | |
| 					   p2p->cookie,
 | |
| 					   &p2p->listen_chan,
 | |
| 					   GFP_KERNEL);
 | |
| 	if (vif->mid == 0)
 | |
| 		wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
 | |
| 	mutex_unlock(&wil->vif_mutex);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void wil_p2p_listen_expired(struct work_struct *work)
 | |
| {
 | |
| 	struct wil_p2p_info *p2p = container_of(work,
 | |
| 			struct wil_p2p_info, discovery_expired_work);
 | |
| 	struct wil6210_vif *vif = container_of(p2p,
 | |
| 			struct wil6210_vif, p2p);
 | |
| 	struct wil6210_priv *wil = vif_to_wil(vif);
 | |
| 	u8 started;
 | |
| 
 | |
| 	wil_dbg_misc(wil, "p2p_listen_expired\n");
 | |
| 
 | |
| 	mutex_lock(&wil->mutex);
 | |
| 	started = wil_p2p_stop_discovery(vif);
 | |
| 	mutex_unlock(&wil->mutex);
 | |
| 
 | |
| 	if (!started)
 | |
| 		return;
 | |
| 
 | |
| 	mutex_lock(&wil->vif_mutex);
 | |
| 	cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif),
 | |
| 					   p2p->cookie,
 | |
| 					   &p2p->listen_chan,
 | |
| 					   GFP_KERNEL);
 | |
| 	if (vif->mid == 0)
 | |
| 		wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
 | |
| 	mutex_unlock(&wil->vif_mutex);
 | |
| }
 | |
| 
 | |
| void wil_p2p_search_expired(struct work_struct *work)
 | |
| {
 | |
| 	struct wil_p2p_info *p2p = container_of(work,
 | |
| 			struct wil_p2p_info, discovery_expired_work);
 | |
| 	struct wil6210_vif *vif = container_of(p2p,
 | |
| 			struct wil6210_vif, p2p);
 | |
| 	struct wil6210_priv *wil = vif_to_wil(vif);
 | |
| 	u8 started;
 | |
| 
 | |
| 	wil_dbg_misc(wil, "p2p_search_expired\n");
 | |
| 
 | |
| 	mutex_lock(&wil->mutex);
 | |
| 	started = wil_p2p_stop_discovery(vif);
 | |
| 	mutex_unlock(&wil->mutex);
 | |
| 
 | |
| 	if (started) {
 | |
| 		struct cfg80211_scan_info info = {
 | |
| 			.aborted = false,
 | |
| 		};
 | |
| 
 | |
| 		mutex_lock(&wil->vif_mutex);
 | |
| 		if (vif->scan_request) {
 | |
| 			cfg80211_scan_done(vif->scan_request, &info);
 | |
| 			vif->scan_request = NULL;
 | |
| 			if (vif->mid == 0)
 | |
| 				wil->radio_wdev =
 | |
| 					wil->main_ndev->ieee80211_ptr;
 | |
| 		}
 | |
| 		mutex_unlock(&wil->vif_mutex);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void wil_p2p_delayed_listen_work(struct work_struct *work)
 | |
| {
 | |
| 	struct wil_p2p_info *p2p = container_of(work,
 | |
| 			struct wil_p2p_info, delayed_listen_work);
 | |
| 	struct wil6210_vif *vif = container_of(p2p,
 | |
| 			struct wil6210_vif, p2p);
 | |
| 	struct wil6210_priv *wil = vif_to_wil(vif);
 | |
| 	int rc;
 | |
| 
 | |
| 	mutex_lock(&wil->mutex);
 | |
| 
 | |
| 	wil_dbg_misc(wil, "Checking delayed p2p listen\n");
 | |
| 	if (!p2p->discovery_started || !p2p->pending_listen_wdev)
 | |
| 		goto out;
 | |
| 
 | |
| 	mutex_lock(&wil->vif_mutex);
 | |
| 	if (vif->scan_request) {
 | |
| 		/* another scan started, wait again... */
 | |
| 		mutex_unlock(&wil->vif_mutex);
 | |
| 		goto out;
 | |
| 	}
 | |
| 	mutex_unlock(&wil->vif_mutex);
 | |
| 
 | |
| 	rc = wil_p2p_start_listen(vif);
 | |
| 
 | |
| 	mutex_lock(&wil->vif_mutex);
 | |
| 	if (rc) {
 | |
| 		cfg80211_remain_on_channel_expired(p2p->pending_listen_wdev,
 | |
| 						   p2p->cookie,
 | |
| 						   &p2p->listen_chan,
 | |
| 						   GFP_KERNEL);
 | |
| 		if (vif->mid == 0)
 | |
| 			wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
 | |
| 	} else {
 | |
| 		cfg80211_ready_on_channel(p2p->pending_listen_wdev, p2p->cookie,
 | |
| 					  &p2p->listen_chan,
 | |
| 					  p2p->listen_duration, GFP_KERNEL);
 | |
| 		if (vif->mid == 0)
 | |
| 			wil->radio_wdev = p2p->pending_listen_wdev;
 | |
| 	}
 | |
| 	p2p->pending_listen_wdev = NULL;
 | |
| 	mutex_unlock(&wil->vif_mutex);
 | |
| 
 | |
| out:
 | |
| 	mutex_unlock(&wil->mutex);
 | |
| }
 | |
| 
 | |
| void wil_p2p_stop_radio_operations(struct wil6210_priv *wil)
 | |
| {
 | |
| 	struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
 | |
| 	struct wil_p2p_info *p2p = &vif->p2p;
 | |
| 	struct cfg80211_scan_info info = {
 | |
| 		.aborted = true,
 | |
| 	};
 | |
| 
 | |
| 	lockdep_assert_held(&wil->mutex);
 | |
| 	lockdep_assert_held(&wil->vif_mutex);
 | |
| 
 | |
| 	if (wil->radio_wdev != wil->p2p_wdev)
 | |
| 		goto out;
 | |
| 
 | |
| 	if (!p2p->discovery_started) {
 | |
| 		/* Regular scan on the p2p device */
 | |
| 		if (vif->scan_request &&
 | |
| 		    vif->scan_request->wdev == wil->p2p_wdev)
 | |
| 			wil_abort_scan(vif, true);
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	/* Search or listen on p2p device */
 | |
| 	mutex_unlock(&wil->vif_mutex);
 | |
| 	wil_p2p_stop_discovery(vif);
 | |
| 	mutex_lock(&wil->vif_mutex);
 | |
| 
 | |
| 	if (vif->scan_request) {
 | |
| 		/* search */
 | |
| 		cfg80211_scan_done(vif->scan_request, &info);
 | |
| 		vif->scan_request = NULL;
 | |
| 	} else {
 | |
| 		/* listen */
 | |
| 		cfg80211_remain_on_channel_expired(wil->radio_wdev,
 | |
| 						   p2p->cookie,
 | |
| 						   &p2p->listen_chan,
 | |
| 						   GFP_KERNEL);
 | |
| 	}
 | |
| 
 | |
| out:
 | |
| 	wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
 | |
| }
 |