1// SPDX-License-Identifier: ISC
2/*
3 * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
4 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
5 */
6
7#include "wil6210.h"
8#include "wmi.h"
9
10#define P2P_WILDCARD_SSID "DIRECT-"
11#define P2P_DMG_SOCIAL_CHANNEL 2
12#define P2P_SEARCH_DURATION_MS 500
13#define P2P_DEFAULT_BI 100
14
15static int wil_p2p_start_listen(struct wil6210_vif *vif)
16{
17	struct wil6210_priv *wil = vif_to_wil(vif);
18	struct wil_p2p_info *p2p = &vif->p2p;
19	u8 channel = p2p->listen_chan.hw_value;
20	int rc;
21
22	lockdep_assert_held(&wil->mutex);
23
24	rc = wmi_p2p_cfg(vif, channel, P2P_DEFAULT_BI);
25	if (rc) {
26		wil_err(wil, "wmi_p2p_cfg failed\n");
27		goto out;
28	}
29
30	rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
31	if (rc) {
32		wil_err(wil, "wmi_set_ssid failed\n");
33		goto out_stop;
34	}
35
36	rc = wmi_start_listen(vif);
37	if (rc) {
38		wil_err(wil, "wmi_start_listen failed\n");
39		goto out_stop;
40	}
41
42	INIT_WORK(&p2p->discovery_expired_work, wil_p2p_listen_expired);
43	mod_timer(&p2p->discovery_timer,
44		  jiffies + msecs_to_jiffies(p2p->listen_duration));
45out_stop:
46	if (rc)
47		wmi_stop_discovery(vif);
48
49out:
50	return rc;
51}
52
53bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request)
54{
55	return (request->n_channels == 1) &&
56	       (request->channels[0]->hw_value == P2P_DMG_SOCIAL_CHANNEL);
57}
58
59int wil_p2p_search(struct wil6210_vif *vif,
60		   struct cfg80211_scan_request *request)
61{
62	struct wil6210_priv *wil = vif_to_wil(vif);
63	int rc;
64	struct wil_p2p_info *p2p = &vif->p2p;
65
66	wil_dbg_misc(wil, "p2p_search: channel %d\n", P2P_DMG_SOCIAL_CHANNEL);
67
68	lockdep_assert_held(&wil->mutex);
69
70	if (p2p->discovery_started) {
71		wil_err(wil, "search failed. discovery already ongoing\n");
72		rc = -EBUSY;
73		goto out;
74	}
75
76	rc = wmi_p2p_cfg(vif, P2P_DMG_SOCIAL_CHANNEL, P2P_DEFAULT_BI);
77	if (rc) {
78		wil_err(wil, "wmi_p2p_cfg failed\n");
79		goto out;
80	}
81
82	rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
83	if (rc) {
84		wil_err(wil, "wmi_set_ssid failed\n");
85		goto out_stop;
86	}
87
88	/* Set application IE to probe request and probe response */
89	rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ,
90			request->ie_len, request->ie);
91	if (rc) {
92		wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_REQ) failed\n");
93		goto out_stop;
94	}
95
96	/* supplicant doesn't provide Probe Response IEs. As a workaround -
97	 * re-use Probe Request IEs
98	 */
99	rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP,
100			request->ie_len, request->ie);
101	if (rc) {
102		wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_RESP) failed\n");
103		goto out_stop;
104	}
105
106	rc = wmi_start_search(vif);
107	if (rc) {
108		wil_err(wil, "wmi_start_search failed\n");
109		goto out_stop;
110	}
111
112	p2p->discovery_started = 1;
113	INIT_WORK(&p2p->discovery_expired_work, wil_p2p_search_expired);
114	mod_timer(&p2p->discovery_timer,
115		  jiffies + msecs_to_jiffies(P2P_SEARCH_DURATION_MS));
116
117out_stop:
118	if (rc)
119		wmi_stop_discovery(vif);
120
121out:
122	return rc;
123}
124
125int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev,
126		   unsigned int duration, struct ieee80211_channel *chan,
127		   u64 *cookie)
128{
129	struct wil6210_vif *vif = wdev_to_vif(wil, wdev);
130	struct wil_p2p_info *p2p = &vif->p2p;
131	int rc;
132
133	if (!chan)
134		return -EINVAL;
135
136	wil_dbg_misc(wil, "p2p_listen: duration %d\n", duration);
137
138	mutex_lock(&wil->mutex);
139
140	if (p2p->discovery_started) {
141		wil_err(wil, "discovery already ongoing\n");
142		rc = -EBUSY;
143		goto out;
144	}
145
146	memcpy(&p2p->listen_chan, chan, sizeof(*chan));
147	*cookie = ++p2p->cookie;
148	p2p->listen_duration = duration;
149
150	mutex_lock(&wil->vif_mutex);
151	if (vif->scan_request) {
152		wil_dbg_misc(wil, "Delaying p2p listen until scan done\n");
153		p2p->pending_listen_wdev = wdev;
154		p2p->discovery_started = 1;
155		rc = 0;
156		mutex_unlock(&wil->vif_mutex);
157		goto out;
158	}
159	mutex_unlock(&wil->vif_mutex);
160
161	rc = wil_p2p_start_listen(vif);
162	if (rc)
163		goto out;
164
165	p2p->discovery_started = 1;
166	if (vif->mid == 0)
167		wil->radio_wdev = wdev;
168
169	cfg80211_ready_on_channel(wdev, *cookie, chan, duration,
170				  GFP_KERNEL);
171
172out:
173	mutex_unlock(&wil->mutex);
174	return rc;
175}
176
177u8 wil_p2p_stop_discovery(struct wil6210_vif *vif)
178{
179	struct wil_p2p_info *p2p = &vif->p2p;
180	u8 started = p2p->discovery_started;
181
182	if (p2p->discovery_started) {
183		if (p2p->pending_listen_wdev) {
184			/* discovery not really started, only pending */
185			p2p->pending_listen_wdev = NULL;
186		} else {
187			del_timer_sync(&p2p->discovery_timer);
188			wmi_stop_discovery(vif);
189		}
190		p2p->discovery_started = 0;
191	}
192
193	return started;
194}
195
196int wil_p2p_cancel_listen(struct wil6210_vif *vif, u64 cookie)
197{
198	struct wil6210_priv *wil = vif_to_wil(vif);
199	struct wil_p2p_info *p2p = &vif->p2p;
200	u8 started;
201
202	mutex_lock(&wil->mutex);
203
204	if (cookie != p2p->cookie) {
205		wil_info(wil, "Cookie mismatch: 0x%016llx vs. 0x%016llx\n",
206			 p2p->cookie, cookie);
207		mutex_unlock(&wil->mutex);
208		return -ENOENT;
209	}
210
211	started = wil_p2p_stop_discovery(vif);
212
213	mutex_unlock(&wil->mutex);
214
215	if (!started) {
216		wil_err(wil, "listen not started\n");
217		return -ENOENT;
218	}
219
220	mutex_lock(&wil->vif_mutex);
221	cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif),
222					   p2p->cookie,
223					   &p2p->listen_chan,
224					   GFP_KERNEL);
225	if (vif->mid == 0)
226		wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
227	mutex_unlock(&wil->vif_mutex);
228	return 0;
229}
230
231void wil_p2p_listen_expired(struct work_struct *work)
232{
233	struct wil_p2p_info *p2p = container_of(work,
234			struct wil_p2p_info, discovery_expired_work);
235	struct wil6210_vif *vif = container_of(p2p,
236			struct wil6210_vif, p2p);
237	struct wil6210_priv *wil = vif_to_wil(vif);
238	u8 started;
239
240	wil_dbg_misc(wil, "p2p_listen_expired\n");
241
242	mutex_lock(&wil->mutex);
243	started = wil_p2p_stop_discovery(vif);
244	mutex_unlock(&wil->mutex);
245
246	if (!started)
247		return;
248
249	mutex_lock(&wil->vif_mutex);
250	cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif),
251					   p2p->cookie,
252					   &p2p->listen_chan,
253					   GFP_KERNEL);
254	if (vif->mid == 0)
255		wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
256	mutex_unlock(&wil->vif_mutex);
257}
258
259void wil_p2p_search_expired(struct work_struct *work)
260{
261	struct wil_p2p_info *p2p = container_of(work,
262			struct wil_p2p_info, discovery_expired_work);
263	struct wil6210_vif *vif = container_of(p2p,
264			struct wil6210_vif, p2p);
265	struct wil6210_priv *wil = vif_to_wil(vif);
266	u8 started;
267
268	wil_dbg_misc(wil, "p2p_search_expired\n");
269
270	mutex_lock(&wil->mutex);
271	started = wil_p2p_stop_discovery(vif);
272	mutex_unlock(&wil->mutex);
273
274	if (started) {
275		struct cfg80211_scan_info info = {
276			.aborted = false,
277		};
278
279		mutex_lock(&wil->vif_mutex);
280		if (vif->scan_request) {
281			cfg80211_scan_done(vif->scan_request, &info);
282			vif->scan_request = NULL;
283			if (vif->mid == 0)
284				wil->radio_wdev =
285					wil->main_ndev->ieee80211_ptr;
286		}
287		mutex_unlock(&wil->vif_mutex);
288	}
289}
290
291void wil_p2p_delayed_listen_work(struct work_struct *work)
292{
293	struct wil_p2p_info *p2p = container_of(work,
294			struct wil_p2p_info, delayed_listen_work);
295	struct wil6210_vif *vif = container_of(p2p,
296			struct wil6210_vif, p2p);
297	struct wil6210_priv *wil = vif_to_wil(vif);
298	int rc;
299
300	mutex_lock(&wil->mutex);
301
302	wil_dbg_misc(wil, "Checking delayed p2p listen\n");
303	if (!p2p->discovery_started || !p2p->pending_listen_wdev)
304		goto out;
305
306	mutex_lock(&wil->vif_mutex);
307	if (vif->scan_request) {
308		/* another scan started, wait again... */
309		mutex_unlock(&wil->vif_mutex);
310		goto out;
311	}
312	mutex_unlock(&wil->vif_mutex);
313
314	rc = wil_p2p_start_listen(vif);
315
316	mutex_lock(&wil->vif_mutex);
317	if (rc) {
318		cfg80211_remain_on_channel_expired(p2p->pending_listen_wdev,
319						   p2p->cookie,
320						   &p2p->listen_chan,
321						   GFP_KERNEL);
322		if (vif->mid == 0)
323			wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
324	} else {
325		cfg80211_ready_on_channel(p2p->pending_listen_wdev, p2p->cookie,
326					  &p2p->listen_chan,
327					  p2p->listen_duration, GFP_KERNEL);
328		if (vif->mid == 0)
329			wil->radio_wdev = p2p->pending_listen_wdev;
330	}
331	p2p->pending_listen_wdev = NULL;
332	mutex_unlock(&wil->vif_mutex);
333
334out:
335	mutex_unlock(&wil->mutex);
336}
337
338void wil_p2p_stop_radio_operations(struct wil6210_priv *wil)
339{
340	struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
341	struct wil_p2p_info *p2p = &vif->p2p;
342	struct cfg80211_scan_info info = {
343		.aborted = true,
344	};
345
346	lockdep_assert_held(&wil->mutex);
347	lockdep_assert_held(&wil->vif_mutex);
348
349	if (wil->radio_wdev != wil->p2p_wdev)
350		goto out;
351
352	if (!p2p->discovery_started) {
353		/* Regular scan on the p2p device */
354		if (vif->scan_request &&
355		    vif->scan_request->wdev == wil->p2p_wdev)
356			wil_abort_scan(vif, true);
357		goto out;
358	}
359
360	/* Search or listen on p2p device */
361	mutex_unlock(&wil->vif_mutex);
362	wil_p2p_stop_discovery(vif);
363	mutex_lock(&wil->vif_mutex);
364
365	if (vif->scan_request) {
366		/* search */
367		cfg80211_scan_done(vif->scan_request, &info);
368		vif->scan_request = NULL;
369	} else {
370		/* listen */
371		cfg80211_remain_on_channel_expired(wil->radio_wdev,
372						   p2p->cookie,
373						   &p2p->listen_chan,
374						   GFP_KERNEL);
375	}
376
377out:
378	wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
379}
380