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(timer: &p2p->discovery_timer, |
44 | expires: jiffies + msecs_to_jiffies(m: 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, type: WMI_FRAME_PROBE_REQ, |
90 | ie_len: request->ie_len, ie: 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, type: WMI_FRAME_PROBE_RESP, |
100 | ie_len: request->ie_len, ie: 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(timer: &p2p->discovery_timer, |
115 | expires: 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(lock: &wil->vif_mutex); |
157 | goto out; |
158 | } |
159 | mutex_unlock(lock: &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: *cookie, chan, duration, |
170 | GFP_KERNEL); |
171 | |
172 | out: |
173 | mutex_unlock(lock: &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(timer: &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(lock: &wil->mutex); |
208 | return -ENOENT; |
209 | } |
210 | |
211 | started = wil_p2p_stop_discovery(vif); |
212 | |
213 | mutex_unlock(lock: &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(wdev: vif_to_radio_wdev(wil, vif), |
222 | cookie: p2p->cookie, |
223 | chan: &p2p->listen_chan, |
224 | GFP_KERNEL); |
225 | if (vif->mid == 0) |
226 | wil->radio_wdev = wil->main_ndev->ieee80211_ptr; |
227 | mutex_unlock(lock: &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(lock: &wil->mutex); |
245 | |
246 | if (!started) |
247 | return; |
248 | |
249 | mutex_lock(&wil->vif_mutex); |
250 | cfg80211_remain_on_channel_expired(wdev: vif_to_radio_wdev(wil, vif), |
251 | cookie: p2p->cookie, |
252 | chan: &p2p->listen_chan, |
253 | GFP_KERNEL); |
254 | if (vif->mid == 0) |
255 | wil->radio_wdev = wil->main_ndev->ieee80211_ptr; |
256 | mutex_unlock(lock: &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(lock: &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(request: vif->scan_request, info: &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(lock: &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(lock: &wil->vif_mutex); |
310 | goto out; |
311 | } |
312 | mutex_unlock(lock: &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(wdev: p2p->pending_listen_wdev, |
319 | cookie: p2p->cookie, |
320 | chan: &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(wdev: p2p->pending_listen_wdev, cookie: p2p->cookie, |
326 | chan: &p2p->listen_chan, |
327 | duration: 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(lock: &wil->vif_mutex); |
333 | |
334 | out: |
335 | mutex_unlock(lock: &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, sync: true); |
357 | goto out; |
358 | } |
359 | |
360 | /* Search or listen on p2p device */ |
361 | mutex_unlock(lock: &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(request: vif->scan_request, info: &info); |
368 | vif->scan_request = NULL; |
369 | } else { |
370 | /* listen */ |
371 | cfg80211_remain_on_channel_expired(wdev: wil->radio_wdev, |
372 | cookie: p2p->cookie, |
373 | chan: &p2p->listen_chan, |
374 | GFP_KERNEL); |
375 | } |
376 | |
377 | out: |
378 | wil->radio_wdev = wil->main_ndev->ieee80211_ptr; |
379 | } |
380 | |