185630469SLior David // SPDX-License-Identifier: ISC
2e6d68341SDedy Lansky /*
3af3db60aSLazar Alexei  * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
4e00243faSLior David  * Copyright (c) 2018, The Linux Foundation. All rights reserved.
5e6d68341SDedy Lansky  */
6e6d68341SDedy Lansky 
7e6d68341SDedy Lansky #include "wil6210.h"
8e6d68341SDedy Lansky #include "wmi.h"
9e6d68341SDedy Lansky 
10e6d68341SDedy Lansky #define P2P_WILDCARD_SSID "DIRECT-"
11e6d68341SDedy Lansky #define P2P_DMG_SOCIAL_CHANNEL 2
12e6d68341SDedy Lansky #define P2P_SEARCH_DURATION_MS 500
13e6d68341SDedy Lansky #define P2P_DEFAULT_BI 100
14e6d68341SDedy Lansky 
wil_p2p_start_listen(struct wil6210_vif * vif)15e00243faSLior David static int wil_p2p_start_listen(struct wil6210_vif *vif)
16bb6743f7SLior David {
17e00243faSLior David 	struct wil6210_priv *wil = vif_to_wil(vif);
18e00243faSLior David 	struct wil_p2p_info *p2p = &vif->p2p;
19bb6743f7SLior David 	u8 channel = p2p->listen_chan.hw_value;
20bb6743f7SLior David 	int rc;
21bb6743f7SLior David 
22bb6743f7SLior David 	lockdep_assert_held(&wil->mutex);
23bb6743f7SLior David 
24e00243faSLior David 	rc = wmi_p2p_cfg(vif, channel, P2P_DEFAULT_BI);
25bb6743f7SLior David 	if (rc) {
26bb6743f7SLior David 		wil_err(wil, "wmi_p2p_cfg failed\n");
27bb6743f7SLior David 		goto out;
28bb6743f7SLior David 	}
29bb6743f7SLior David 
30e00243faSLior David 	rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
31bb6743f7SLior David 	if (rc) {
32bb6743f7SLior David 		wil_err(wil, "wmi_set_ssid failed\n");
33bb6743f7SLior David 		goto out_stop;
34bb6743f7SLior David 	}
35bb6743f7SLior David 
36e00243faSLior David 	rc = wmi_start_listen(vif);
37bb6743f7SLior David 	if (rc) {
38bb6743f7SLior David 		wil_err(wil, "wmi_start_listen failed\n");
39bb6743f7SLior David 		goto out_stop;
40bb6743f7SLior David 	}
41bb6743f7SLior David 
42bb6743f7SLior David 	INIT_WORK(&p2p->discovery_expired_work, wil_p2p_listen_expired);
43bb6743f7SLior David 	mod_timer(&p2p->discovery_timer,
44bb6743f7SLior David 		  jiffies + msecs_to_jiffies(p2p->listen_duration));
45bb6743f7SLior David out_stop:
46bb6743f7SLior David 	if (rc)
47e00243faSLior David 		wmi_stop_discovery(vif);
48bb6743f7SLior David 
49bb6743f7SLior David out:
50bb6743f7SLior David 	return rc;
51bb6743f7SLior David }
52bb6743f7SLior David 
wil_p2p_is_social_scan(struct cfg80211_scan_request * request)53321a000bSLior David bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request)
54321a000bSLior David {
55321a000bSLior David 	return (request->n_channels == 1) &&
56321a000bSLior David 	       (request->channels[0]->hw_value == P2P_DMG_SOCIAL_CHANNEL);
57321a000bSLior David }
58321a000bSLior David 
wil_p2p_search(struct wil6210_vif * vif,struct cfg80211_scan_request * request)59e00243faSLior David int wil_p2p_search(struct wil6210_vif *vif,
60e6d68341SDedy Lansky 		   struct cfg80211_scan_request *request)
61e6d68341SDedy Lansky {
62e00243faSLior David 	struct wil6210_priv *wil = vif_to_wil(vif);
63e6d68341SDedy Lansky 	int rc;
64e00243faSLior David 	struct wil_p2p_info *p2p = &vif->p2p;
65e6d68341SDedy Lansky 
66af3db60aSLazar Alexei 	wil_dbg_misc(wil, "p2p_search: channel %d\n", P2P_DMG_SOCIAL_CHANNEL);
67e6d68341SDedy Lansky 
68bb6743f7SLior David 	lockdep_assert_held(&wil->mutex);
69e6d68341SDedy Lansky 
70e6d68341SDedy Lansky 	if (p2p->discovery_started) {
71af3db60aSLazar Alexei 		wil_err(wil, "search failed. discovery already ongoing\n");
72e6d68341SDedy Lansky 		rc = -EBUSY;
73e6d68341SDedy Lansky 		goto out;
74e6d68341SDedy Lansky 	}
75e6d68341SDedy Lansky 
76e00243faSLior David 	rc = wmi_p2p_cfg(vif, P2P_DMG_SOCIAL_CHANNEL, P2P_DEFAULT_BI);
77e6d68341SDedy Lansky 	if (rc) {
78af3db60aSLazar Alexei 		wil_err(wil, "wmi_p2p_cfg failed\n");
79e6d68341SDedy Lansky 		goto out;
80e6d68341SDedy Lansky 	}
81e6d68341SDedy Lansky 
82e00243faSLior David 	rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
83e6d68341SDedy Lansky 	if (rc) {
84af3db60aSLazar Alexei 		wil_err(wil, "wmi_set_ssid failed\n");
85e6d68341SDedy Lansky 		goto out_stop;
86e6d68341SDedy Lansky 	}
87e6d68341SDedy Lansky 
88e6d68341SDedy Lansky 	/* Set application IE to probe request and probe response */
89e00243faSLior David 	rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
90e6d68341SDedy Lansky 			request->ie_len, request->ie);
91e6d68341SDedy Lansky 	if (rc) {
92af3db60aSLazar Alexei 		wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_REQ) failed\n");
93e6d68341SDedy Lansky 		goto out_stop;
94e6d68341SDedy Lansky 	}
95e6d68341SDedy Lansky 
96e6d68341SDedy Lansky 	/* supplicant doesn't provide Probe Response IEs. As a workaround -
97e6d68341SDedy Lansky 	 * re-use Probe Request IEs
98e6d68341SDedy Lansky 	 */
99e00243faSLior David 	rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP,
100e6d68341SDedy Lansky 			request->ie_len, request->ie);
101e6d68341SDedy Lansky 	if (rc) {
102af3db60aSLazar Alexei 		wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_RESP) failed\n");
103e6d68341SDedy Lansky 		goto out_stop;
104e6d68341SDedy Lansky 	}
105e6d68341SDedy Lansky 
106e00243faSLior David 	rc = wmi_start_search(vif);
107e6d68341SDedy Lansky 	if (rc) {
108af3db60aSLazar Alexei 		wil_err(wil, "wmi_start_search failed\n");
109e6d68341SDedy Lansky 		goto out_stop;
110e6d68341SDedy Lansky 	}
111e6d68341SDedy Lansky 
112e6d68341SDedy Lansky 	p2p->discovery_started = 1;
113e6d68341SDedy Lansky 	INIT_WORK(&p2p->discovery_expired_work, wil_p2p_search_expired);
114e6d68341SDedy Lansky 	mod_timer(&p2p->discovery_timer,
115e6d68341SDedy Lansky 		  jiffies + msecs_to_jiffies(P2P_SEARCH_DURATION_MS));
116e6d68341SDedy Lansky 
117e6d68341SDedy Lansky out_stop:
118e6d68341SDedy Lansky 	if (rc)
119e00243faSLior David 		wmi_stop_discovery(vif);
120e6d68341SDedy Lansky 
121e6d68341SDedy Lansky out:
122e6d68341SDedy Lansky 	return rc;
123e6d68341SDedy Lansky }
124e6d68341SDedy Lansky 
wil_p2p_listen(struct wil6210_priv * wil,struct wireless_dev * wdev,unsigned int duration,struct ieee80211_channel * chan,u64 * cookie)125bb6743f7SLior David int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev,
126bb6743f7SLior David 		   unsigned int duration, struct ieee80211_channel *chan,
127bb6743f7SLior David 		   u64 *cookie)
128e6d68341SDedy Lansky {
129e00243faSLior David 	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
130e00243faSLior David 	struct wil_p2p_info *p2p = &vif->p2p;
131e6d68341SDedy Lansky 	int rc;
132e6d68341SDedy Lansky 
1339c830abeSMaya Erez 	if (!chan)
1349c830abeSMaya Erez 		return -EINVAL;
1359c830abeSMaya Erez 
136af3db60aSLazar Alexei 	wil_dbg_misc(wil, "p2p_listen: duration %d\n", duration);
137e6d68341SDedy Lansky 
138e6d68341SDedy Lansky 	mutex_lock(&wil->mutex);
139e6d68341SDedy Lansky 
140e6d68341SDedy Lansky 	if (p2p->discovery_started) {
141af3db60aSLazar Alexei 		wil_err(wil, "discovery already ongoing\n");
142e6d68341SDedy Lansky 		rc = -EBUSY;
143e6d68341SDedy Lansky 		goto out;
144e6d68341SDedy Lansky 	}
145e6d68341SDedy Lansky 
146e6d68341SDedy Lansky 	memcpy(&p2p->listen_chan, chan, sizeof(*chan));
147e6d68341SDedy Lansky 	*cookie = ++p2p->cookie;
148bb6743f7SLior David 	p2p->listen_duration = duration;
149bb6743f7SLior David 
150404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
151e00243faSLior David 	if (vif->scan_request) {
152bb6743f7SLior David 		wil_dbg_misc(wil, "Delaying p2p listen until scan done\n");
153bb6743f7SLior David 		p2p->pending_listen_wdev = wdev;
154bb6743f7SLior David 		p2p->discovery_started = 1;
155bb6743f7SLior David 		rc = 0;
156404bbb3cSLior David 		mutex_unlock(&wil->vif_mutex);
157bb6743f7SLior David 		goto out;
158bb6743f7SLior David 	}
159404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
160bb6743f7SLior David 
161e00243faSLior David 	rc = wil_p2p_start_listen(vif);
162bb6743f7SLior David 	if (rc)
163bb6743f7SLior David 		goto out;
164e6d68341SDedy Lansky 
165e6d68341SDedy Lansky 	p2p->discovery_started = 1;
166e00243faSLior David 	if (vif->mid == 0)
167bb6743f7SLior David 		wil->radio_wdev = wdev;
168e6d68341SDedy Lansky 
169bb6743f7SLior David 	cfg80211_ready_on_channel(wdev, *cookie, chan, duration,
170bb6743f7SLior David 				  GFP_KERNEL);
171e6d68341SDedy Lansky 
172e6d68341SDedy Lansky out:
173e6d68341SDedy Lansky 	mutex_unlock(&wil->mutex);
174e6d68341SDedy Lansky 	return rc;
175e6d68341SDedy Lansky }
176e6d68341SDedy Lansky 
wil_p2p_stop_discovery(struct wil6210_vif * vif)177e00243faSLior David u8 wil_p2p_stop_discovery(struct wil6210_vif *vif)
178e6d68341SDedy Lansky {
179e00243faSLior David 	struct wil_p2p_info *p2p = &vif->p2p;
180280ab987SLior David 	u8 started = p2p->discovery_started;
181e6d68341SDedy Lansky 
182e6d68341SDedy Lansky 	if (p2p->discovery_started) {
183bb6743f7SLior David 		if (p2p->pending_listen_wdev) {
184bb6743f7SLior David 			/* discovery not really started, only pending */
185bb6743f7SLior David 			p2p->pending_listen_wdev = NULL;
186bb6743f7SLior David 		} else {
187e6d68341SDedy Lansky 			del_timer_sync(&p2p->discovery_timer);
188e00243faSLior David 			wmi_stop_discovery(vif);
189e6d68341SDedy Lansky 		}
190bb6743f7SLior David 		p2p->discovery_started = 0;
191bb6743f7SLior David 	}
192280ab987SLior David 
193280ab987SLior David 	return started;
194e6d68341SDedy Lansky }
195e6d68341SDedy Lansky 
wil_p2p_cancel_listen(struct wil6210_vif * vif,u64 cookie)196e00243faSLior David int wil_p2p_cancel_listen(struct wil6210_vif *vif, u64 cookie)
197e6d68341SDedy Lansky {
198e00243faSLior David 	struct wil6210_priv *wil = vif_to_wil(vif);
199e00243faSLior David 	struct wil_p2p_info *p2p = &vif->p2p;
200280ab987SLior David 	u8 started;
201e6d68341SDedy Lansky 
202280ab987SLior David 	mutex_lock(&wil->mutex);
203280ab987SLior David 
204280ab987SLior David 	if (cookie != p2p->cookie) {
205af3db60aSLazar Alexei 		wil_info(wil, "Cookie mismatch: 0x%016llx vs. 0x%016llx\n",
206af3db60aSLazar Alexei 			 p2p->cookie, cookie);
207280ab987SLior David 		mutex_unlock(&wil->mutex);
208280ab987SLior David 		return -ENOENT;
209280ab987SLior David 	}
210e6d68341SDedy Lansky 
211e00243faSLior David 	started = wil_p2p_stop_discovery(vif);
212280ab987SLior David 
213280ab987SLior David 	mutex_unlock(&wil->mutex);
214280ab987SLior David 
215280ab987SLior David 	if (!started) {
216af3db60aSLazar Alexei 		wil_err(wil, "listen not started\n");
217280ab987SLior David 		return -ENOENT;
218280ab987SLior David 	}
2194332cac1SLior David 
220404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
221e00243faSLior David 	cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif),
222e6d68341SDedy Lansky 					   p2p->cookie,
223e6d68341SDedy Lansky 					   &p2p->listen_chan,
224e6d68341SDedy Lansky 					   GFP_KERNEL);
225e00243faSLior David 	if (vif->mid == 0)
226e00243faSLior David 		wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
227404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
228280ab987SLior David 	return 0;
229e6d68341SDedy Lansky }
230e6d68341SDedy Lansky 
wil_p2p_listen_expired(struct work_struct * work)231e6d68341SDedy Lansky void wil_p2p_listen_expired(struct work_struct *work)
232e6d68341SDedy Lansky {
233e6d68341SDedy Lansky 	struct wil_p2p_info *p2p = container_of(work,
234e6d68341SDedy Lansky 			struct wil_p2p_info, discovery_expired_work);
235e00243faSLior David 	struct wil6210_vif *vif = container_of(p2p,
236e00243faSLior David 			struct wil6210_vif, p2p);
237e00243faSLior David 	struct wil6210_priv *wil = vif_to_wil(vif);
238280ab987SLior David 	u8 started;
239e6d68341SDedy Lansky 
240af3db60aSLazar Alexei 	wil_dbg_misc(wil, "p2p_listen_expired\n");
241e6d68341SDedy Lansky 
242280ab987SLior David 	mutex_lock(&wil->mutex);
243e00243faSLior David 	started = wil_p2p_stop_discovery(vif);
244280ab987SLior David 	mutex_unlock(&wil->mutex);
2454332cac1SLior David 
246e00243faSLior David 	if (!started)
247e00243faSLior David 		return;
248e00243faSLior David 
249404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
250e00243faSLior David 	cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif),
251e6d68341SDedy Lansky 					   p2p->cookie,
252e6d68341SDedy Lansky 					   &p2p->listen_chan,
253e6d68341SDedy Lansky 					   GFP_KERNEL);
254e00243faSLior David 	if (vif->mid == 0)
255e00243faSLior David 		wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
256404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
257280ab987SLior David }
2584332cac1SLior David 
wil_p2p_search_expired(struct work_struct * work)259e6d68341SDedy Lansky void wil_p2p_search_expired(struct work_struct *work)
260e6d68341SDedy Lansky {
261e6d68341SDedy Lansky 	struct wil_p2p_info *p2p = container_of(work,
262e6d68341SDedy Lansky 			struct wil_p2p_info, discovery_expired_work);
263e00243faSLior David 	struct wil6210_vif *vif = container_of(p2p,
264e00243faSLior David 			struct wil6210_vif, p2p);
265e00243faSLior David 	struct wil6210_priv *wil = vif_to_wil(vif);
266280ab987SLior David 	u8 started;
267e6d68341SDedy Lansky 
268af3db60aSLazar Alexei 	wil_dbg_misc(wil, "p2p_search_expired\n");
269e6d68341SDedy Lansky 
270280ab987SLior David 	mutex_lock(&wil->mutex);
271e00243faSLior David 	started = wil_p2p_stop_discovery(vif);
272280ab987SLior David 	mutex_unlock(&wil->mutex);
2734332cac1SLior David 
274280ab987SLior David 	if (started) {
2751d76250bSAvraham Stern 		struct cfg80211_scan_info info = {
2761d76250bSAvraham Stern 			.aborted = false,
2771d76250bSAvraham Stern 		};
2781d76250bSAvraham Stern 
279404bbb3cSLior David 		mutex_lock(&wil->vif_mutex);
280e00243faSLior David 		if (vif->scan_request) {
281e00243faSLior David 			cfg80211_scan_done(vif->scan_request, &info);
282e00243faSLior David 			vif->scan_request = NULL;
283e00243faSLior David 			if (vif->mid == 0)
284e00243faSLior David 				wil->radio_wdev =
285e00243faSLior David 					wil->main_ndev->ieee80211_ptr;
286bb6743f7SLior David 		}
287404bbb3cSLior David 		mutex_unlock(&wil->vif_mutex);
288e6d68341SDedy Lansky 	}
289280ab987SLior David }
290d35c2b6fSMaya Erez 
wil_p2p_delayed_listen_work(struct work_struct * work)291bb6743f7SLior David void wil_p2p_delayed_listen_work(struct work_struct *work)
292bb6743f7SLior David {
293bb6743f7SLior David 	struct wil_p2p_info *p2p = container_of(work,
294bb6743f7SLior David 			struct wil_p2p_info, delayed_listen_work);
295e00243faSLior David 	struct wil6210_vif *vif = container_of(p2p,
296e00243faSLior David 			struct wil6210_vif, p2p);
297e00243faSLior David 	struct wil6210_priv *wil = vif_to_wil(vif);
298bb6743f7SLior David 	int rc;
299bb6743f7SLior David 
300bb6743f7SLior David 	mutex_lock(&wil->mutex);
301bb6743f7SLior David 
302bb6743f7SLior David 	wil_dbg_misc(wil, "Checking delayed p2p listen\n");
303bb6743f7SLior David 	if (!p2p->discovery_started || !p2p->pending_listen_wdev)
304bb6743f7SLior David 		goto out;
305bb6743f7SLior David 
306404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
307e00243faSLior David 	if (vif->scan_request) {
308bb6743f7SLior David 		/* another scan started, wait again... */
309404bbb3cSLior David 		mutex_unlock(&wil->vif_mutex);
310bb6743f7SLior David 		goto out;
311bb6743f7SLior David 	}
312404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
313bb6743f7SLior David 
314e00243faSLior David 	rc = wil_p2p_start_listen(vif);
315bb6743f7SLior David 
316404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
317bb6743f7SLior David 	if (rc) {
318bb6743f7SLior David 		cfg80211_remain_on_channel_expired(p2p->pending_listen_wdev,
319bb6743f7SLior David 						   p2p->cookie,
320bb6743f7SLior David 						   &p2p->listen_chan,
321bb6743f7SLior David 						   GFP_KERNEL);
322e00243faSLior David 		if (vif->mid == 0)
323e00243faSLior David 			wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
324bb6743f7SLior David 	} else {
325bb6743f7SLior David 		cfg80211_ready_on_channel(p2p->pending_listen_wdev, p2p->cookie,
326bb6743f7SLior David 					  &p2p->listen_chan,
327bb6743f7SLior David 					  p2p->listen_duration, GFP_KERNEL);
328e00243faSLior David 		if (vif->mid == 0)
329bb6743f7SLior David 			wil->radio_wdev = p2p->pending_listen_wdev;
330bb6743f7SLior David 	}
331bb6743f7SLior David 	p2p->pending_listen_wdev = NULL;
332404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
333bb6743f7SLior David 
334bb6743f7SLior David out:
335bb6743f7SLior David 	mutex_unlock(&wil->mutex);
336bb6743f7SLior David }
337bb6743f7SLior David 
wil_p2p_stop_radio_operations(struct wil6210_priv * wil)338d35c2b6fSMaya Erez void wil_p2p_stop_radio_operations(struct wil6210_priv *wil)
339d35c2b6fSMaya Erez {
340e00243faSLior David 	struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
341e00243faSLior David 	struct wil_p2p_info *p2p = &vif->p2p;
342d35c2b6fSMaya Erez 	struct cfg80211_scan_info info = {
343d35c2b6fSMaya Erez 		.aborted = true,
344d35c2b6fSMaya Erez 	};
345d35c2b6fSMaya Erez 
346d35c2b6fSMaya Erez 	lockdep_assert_held(&wil->mutex);
347404bbb3cSLior David 	lockdep_assert_held(&wil->vif_mutex);
348d35c2b6fSMaya Erez 
349d35c2b6fSMaya Erez 	if (wil->radio_wdev != wil->p2p_wdev)
350d35c2b6fSMaya Erez 		goto out;
351d35c2b6fSMaya Erez 
352d35c2b6fSMaya Erez 	if (!p2p->discovery_started) {
353d35c2b6fSMaya Erez 		/* Regular scan on the p2p device */
354e00243faSLior David 		if (vif->scan_request &&
355e00243faSLior David 		    vif->scan_request->wdev == wil->p2p_wdev)
356e00243faSLior David 			wil_abort_scan(vif, true);
357d35c2b6fSMaya Erez 		goto out;
358d35c2b6fSMaya Erez 	}
359d35c2b6fSMaya Erez 
360d35c2b6fSMaya Erez 	/* Search or listen on p2p device */
361404bbb3cSLior David 	mutex_unlock(&wil->vif_mutex);
362e00243faSLior David 	wil_p2p_stop_discovery(vif);
363404bbb3cSLior David 	mutex_lock(&wil->vif_mutex);
364d35c2b6fSMaya Erez 
365e00243faSLior David 	if (vif->scan_request) {
366d35c2b6fSMaya Erez 		/* search */
367e00243faSLior David 		cfg80211_scan_done(vif->scan_request, &info);
368e00243faSLior David 		vif->scan_request = NULL;
369d35c2b6fSMaya Erez 	} else {
370d35c2b6fSMaya Erez 		/* listen */
371d35c2b6fSMaya Erez 		cfg80211_remain_on_channel_expired(wil->radio_wdev,
372d35c2b6fSMaya Erez 						   p2p->cookie,
373d35c2b6fSMaya Erez 						   &p2p->listen_chan,
374d35c2b6fSMaya Erez 						   GFP_KERNEL);
375d35c2b6fSMaya Erez 	}
376d35c2b6fSMaya Erez 
377d35c2b6fSMaya Erez out:
378e00243faSLior David 	wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
379d35c2b6fSMaya Erez }
380