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 
15 static 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));
45 out_stop:
46 	if (rc)
47 		wmi_stop_discovery(vif);
48 
49 out:
50 	return rc;
51 }
52 
53 bool 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 
59 int 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 
117 out_stop:
118 	if (rc)
119 		wmi_stop_discovery(vif);
120 
121 out:
122 	return rc;
123 }
124 
125 int 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 
172 out:
173 	mutex_unlock(&wil->mutex);
174 	return rc;
175 }
176 
177 u8 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 
196 int 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 
231 void 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 
259 void 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 
291 void 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 
334 out:
335 	mutex_unlock(&wil->mutex);
336 }
337 
338 void 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 
377 out:
378 	wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
379 }
380