Commit | Line | Data |
---|---|---|
85630469 | 1 | // SPDX-License-Identifier: ISC |
e6d68341 | 2 | /* |
af3db60a | 3 | * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. |
e00243fa | 4 | * Copyright (c) 2018, The Linux Foundation. All rights reserved. |
e6d68341 DL |
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 | ||
e00243fa | 15 | static int wil_p2p_start_listen(struct wil6210_vif *vif) |
bb6743f7 | 16 | { |
e00243fa LD |
17 | struct wil6210_priv *wil = vif_to_wil(vif); |
18 | struct wil_p2p_info *p2p = &vif->p2p; | |
bb6743f7 LD |
19 | u8 channel = p2p->listen_chan.hw_value; |
20 | int rc; | |
21 | ||
22 | lockdep_assert_held(&wil->mutex); | |
23 | ||
e00243fa | 24 | rc = wmi_p2p_cfg(vif, channel, P2P_DEFAULT_BI); |
bb6743f7 LD |
25 | if (rc) { |
26 | wil_err(wil, "wmi_p2p_cfg failed\n"); | |
27 | goto out; | |
28 | } | |
29 | ||
e00243fa | 30 | rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID); |
bb6743f7 LD |
31 | if (rc) { |
32 | wil_err(wil, "wmi_set_ssid failed\n"); | |
33 | goto out_stop; | |
34 | } | |
35 | ||
e00243fa | 36 | rc = wmi_start_listen(vif); |
bb6743f7 LD |
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) | |
e00243fa | 47 | wmi_stop_discovery(vif); |
bb6743f7 LD |
48 | |
49 | out: | |
50 | return rc; | |
51 | } | |
52 | ||
321a000b LD |
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 | ||
e00243fa | 59 | int wil_p2p_search(struct wil6210_vif *vif, |
e6d68341 DL |
60 | struct cfg80211_scan_request *request) |
61 | { | |
e00243fa | 62 | struct wil6210_priv *wil = vif_to_wil(vif); |
e6d68341 | 63 | int rc; |
e00243fa | 64 | struct wil_p2p_info *p2p = &vif->p2p; |
e6d68341 | 65 | |
af3db60a | 66 | wil_dbg_misc(wil, "p2p_search: channel %d\n", P2P_DMG_SOCIAL_CHANNEL); |
e6d68341 | 67 | |
bb6743f7 | 68 | lockdep_assert_held(&wil->mutex); |
e6d68341 DL |
69 | |
70 | if (p2p->discovery_started) { | |
af3db60a | 71 | wil_err(wil, "search failed. discovery already ongoing\n"); |
e6d68341 DL |
72 | rc = -EBUSY; |
73 | goto out; | |
74 | } | |
75 | ||
e00243fa | 76 | rc = wmi_p2p_cfg(vif, P2P_DMG_SOCIAL_CHANNEL, P2P_DEFAULT_BI); |
e6d68341 | 77 | if (rc) { |
af3db60a | 78 | wil_err(wil, "wmi_p2p_cfg failed\n"); |
e6d68341 DL |
79 | goto out; |
80 | } | |
81 | ||
e00243fa | 82 | rc = wmi_set_ssid(vif, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID); |
e6d68341 | 83 | if (rc) { |
af3db60a | 84 | wil_err(wil, "wmi_set_ssid failed\n"); |
e6d68341 DL |
85 | goto out_stop; |
86 | } | |
87 | ||
88 | /* Set application IE to probe request and probe response */ | |
e00243fa | 89 | rc = wmi_set_ie(vif, WMI_FRAME_PROBE_REQ, |
e6d68341 DL |
90 | request->ie_len, request->ie); |
91 | if (rc) { | |
af3db60a | 92 | wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_REQ) failed\n"); |
e6d68341 DL |
93 | goto out_stop; |
94 | } | |
95 | ||
96 | /* supplicant doesn't provide Probe Response IEs. As a workaround - | |
97 | * re-use Probe Request IEs | |
98 | */ | |
e00243fa | 99 | rc = wmi_set_ie(vif, WMI_FRAME_PROBE_RESP, |
e6d68341 DL |
100 | request->ie_len, request->ie); |
101 | if (rc) { | |
af3db60a | 102 | wil_err(wil, "wmi_set_ie(WMI_FRAME_PROBE_RESP) failed\n"); |
e6d68341 DL |
103 | goto out_stop; |
104 | } | |
105 | ||
e00243fa | 106 | rc = wmi_start_search(vif); |
e6d68341 | 107 | if (rc) { |
af3db60a | 108 | wil_err(wil, "wmi_start_search failed\n"); |
e6d68341 DL |
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) | |
e00243fa | 119 | wmi_stop_discovery(vif); |
e6d68341 DL |
120 | |
121 | out: | |
e6d68341 DL |
122 | return rc; |
123 | } | |
124 | ||
bb6743f7 LD |
125 | int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev, |
126 | unsigned int duration, struct ieee80211_channel *chan, | |
127 | u64 *cookie) | |
e6d68341 | 128 | { |
e00243fa LD |
129 | struct wil6210_vif *vif = wdev_to_vif(wil, wdev); |
130 | struct wil_p2p_info *p2p = &vif->p2p; | |
e6d68341 DL |
131 | int rc; |
132 | ||
9c830abe ME |
133 | if (!chan) |
134 | return -EINVAL; | |
135 | ||
af3db60a | 136 | wil_dbg_misc(wil, "p2p_listen: duration %d\n", duration); |
e6d68341 DL |
137 | |
138 | mutex_lock(&wil->mutex); | |
139 | ||
140 | if (p2p->discovery_started) { | |
af3db60a | 141 | wil_err(wil, "discovery already ongoing\n"); |
e6d68341 DL |
142 | rc = -EBUSY; |
143 | goto out; | |
144 | } | |
145 | ||
bb6743f7 LD |
146 | memcpy(&p2p->listen_chan, chan, sizeof(*chan)); |
147 | *cookie = ++p2p->cookie; | |
148 | p2p->listen_duration = duration; | |
e6d68341 | 149 | |
404bbb3c | 150 | mutex_lock(&wil->vif_mutex); |
e00243fa | 151 | if (vif->scan_request) { |
bb6743f7 LD |
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; | |
404bbb3c | 156 | mutex_unlock(&wil->vif_mutex); |
bb6743f7 | 157 | goto out; |
e6d68341 | 158 | } |
404bbb3c | 159 | mutex_unlock(&wil->vif_mutex); |
e6d68341 | 160 | |
e00243fa | 161 | rc = wil_p2p_start_listen(vif); |
bb6743f7 LD |
162 | if (rc) |
163 | goto out; | |
e6d68341 DL |
164 | |
165 | p2p->discovery_started = 1; | |
e00243fa LD |
166 | if (vif->mid == 0) |
167 | wil->radio_wdev = wdev; | |
e6d68341 | 168 | |
bb6743f7 LD |
169 | cfg80211_ready_on_channel(wdev, *cookie, chan, duration, |
170 | GFP_KERNEL); | |
e6d68341 DL |
171 | |
172 | out: | |
173 | mutex_unlock(&wil->mutex); | |
174 | return rc; | |
175 | } | |
176 | ||
e00243fa | 177 | u8 wil_p2p_stop_discovery(struct wil6210_vif *vif) |
e6d68341 | 178 | { |
e00243fa | 179 | struct wil_p2p_info *p2p = &vif->p2p; |
280ab987 | 180 | u8 started = p2p->discovery_started; |
e6d68341 DL |
181 | |
182 | if (p2p->discovery_started) { | |
bb6743f7 LD |
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); | |
e00243fa | 188 | wmi_stop_discovery(vif); |
bb6743f7 | 189 | } |
e6d68341 | 190 | p2p->discovery_started = 0; |
e6d68341 | 191 | } |
280ab987 LD |
192 | |
193 | return started; | |
e6d68341 DL |
194 | } |
195 | ||
e00243fa | 196 | int wil_p2p_cancel_listen(struct wil6210_vif *vif, u64 cookie) |
e6d68341 | 197 | { |
e00243fa LD |
198 | struct wil6210_priv *wil = vif_to_wil(vif); |
199 | struct wil_p2p_info *p2p = &vif->p2p; | |
280ab987 LD |
200 | u8 started; |
201 | ||
202 | mutex_lock(&wil->mutex); | |
e6d68341 | 203 | |
280ab987 | 204 | if (cookie != p2p->cookie) { |
af3db60a LA |
205 | wil_info(wil, "Cookie mismatch: 0x%016llx vs. 0x%016llx\n", |
206 | p2p->cookie, cookie); | |
280ab987 LD |
207 | mutex_unlock(&wil->mutex); |
208 | return -ENOENT; | |
209 | } | |
210 | ||
e00243fa | 211 | started = wil_p2p_stop_discovery(vif); |
280ab987 LD |
212 | |
213 | mutex_unlock(&wil->mutex); | |
e6d68341 | 214 | |
280ab987 | 215 | if (!started) { |
af3db60a | 216 | wil_err(wil, "listen not started\n"); |
280ab987 LD |
217 | return -ENOENT; |
218 | } | |
4332cac1 | 219 | |
404bbb3c | 220 | mutex_lock(&wil->vif_mutex); |
e00243fa | 221 | cfg80211_remain_on_channel_expired(vif_to_radio_wdev(wil, vif), |
e6d68341 DL |
222 | p2p->cookie, |
223 | &p2p->listen_chan, | |
224 | GFP_KERNEL); | |
e00243fa LD |
225 | if (vif->mid == 0) |
226 | wil->radio_wdev = wil->main_ndev->ieee80211_ptr; | |
404bbb3c | 227 | mutex_unlock(&wil->vif_mutex); |
280ab987 | 228 | return 0; |
e6d68341 DL |
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); | |
e00243fa LD |
235 | struct wil6210_vif *vif = container_of(p2p, |
236 | struct wil6210_vif, p2p); | |
237 | struct wil6210_priv *wil = vif_to_wil(vif); | |
280ab987 | 238 | u8 started; |
e6d68341 | 239 | |
af3db60a | 240 | wil_dbg_misc(wil, "p2p_listen_expired\n"); |
e6d68341 | 241 | |
280ab987 | 242 | mutex_lock(&wil->mutex); |
e00243fa | 243 | started = wil_p2p_stop_discovery(vif); |
280ab987 | 244 | mutex_unlock(&wil->mutex); |
4332cac1 | 245 | |
e00243fa LD |
246 | if (!started) |
247 | return; | |
4332cac1 | 248 | |
404bbb3c | 249 | mutex_lock(&wil->vif_mutex); |
e00243fa LD |
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; | |
404bbb3c | 256 | mutex_unlock(&wil->vif_mutex); |
e6d68341 DL |
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); | |
e00243fa LD |
263 | struct wil6210_vif *vif = container_of(p2p, |
264 | struct wil6210_vif, p2p); | |
265 | struct wil6210_priv *wil = vif_to_wil(vif); | |
280ab987 | 266 | u8 started; |
e6d68341 | 267 | |
af3db60a | 268 | wil_dbg_misc(wil, "p2p_search_expired\n"); |
e6d68341 | 269 | |
280ab987 | 270 | mutex_lock(&wil->mutex); |
e00243fa | 271 | started = wil_p2p_stop_discovery(vif); |
280ab987 | 272 | mutex_unlock(&wil->mutex); |
4332cac1 | 273 | |
280ab987 | 274 | if (started) { |
1d76250b AS |
275 | struct cfg80211_scan_info info = { |
276 | .aborted = false, | |
277 | }; | |
278 | ||
404bbb3c | 279 | mutex_lock(&wil->vif_mutex); |
e00243fa LD |
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; | |
bb6743f7 | 286 | } |
404bbb3c | 287 | mutex_unlock(&wil->vif_mutex); |
bb6743f7 LD |
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); | |
e00243fa LD |
295 | struct wil6210_vif *vif = container_of(p2p, |
296 | struct wil6210_vif, p2p); | |
297 | struct wil6210_priv *wil = vif_to_wil(vif); | |
bb6743f7 LD |
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 | ||
404bbb3c | 306 | mutex_lock(&wil->vif_mutex); |
e00243fa | 307 | if (vif->scan_request) { |
bb6743f7 | 308 | /* another scan started, wait again... */ |
404bbb3c | 309 | mutex_unlock(&wil->vif_mutex); |
bb6743f7 | 310 | goto out; |
280ab987 | 311 | } |
404bbb3c | 312 | mutex_unlock(&wil->vif_mutex); |
bb6743f7 | 313 | |
e00243fa | 314 | rc = wil_p2p_start_listen(vif); |
bb6743f7 | 315 | |
404bbb3c | 316 | mutex_lock(&wil->vif_mutex); |
bb6743f7 LD |
317 | if (rc) { |
318 | cfg80211_remain_on_channel_expired(p2p->pending_listen_wdev, | |
319 | p2p->cookie, | |
320 | &p2p->listen_chan, | |
321 | GFP_KERNEL); | |
e00243fa LD |
322 | if (vif->mid == 0) |
323 | wil->radio_wdev = wil->main_ndev->ieee80211_ptr; | |
bb6743f7 LD |
324 | } else { |
325 | cfg80211_ready_on_channel(p2p->pending_listen_wdev, p2p->cookie, | |
326 | &p2p->listen_chan, | |
327 | p2p->listen_duration, GFP_KERNEL); | |
e00243fa LD |
328 | if (vif->mid == 0) |
329 | wil->radio_wdev = p2p->pending_listen_wdev; | |
bb6743f7 LD |
330 | } |
331 | p2p->pending_listen_wdev = NULL; | |
404bbb3c | 332 | mutex_unlock(&wil->vif_mutex); |
bb6743f7 LD |
333 | |
334 | out: | |
335 | mutex_unlock(&wil->mutex); | |
e6d68341 | 336 | } |
d35c2b6f ME |
337 | |
338 | void wil_p2p_stop_radio_operations(struct wil6210_priv *wil) | |
339 | { | |
e00243fa LD |
340 | struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); |
341 | struct wil_p2p_info *p2p = &vif->p2p; | |
d35c2b6f ME |
342 | struct cfg80211_scan_info info = { |
343 | .aborted = true, | |
344 | }; | |
345 | ||
346 | lockdep_assert_held(&wil->mutex); | |
404bbb3c | 347 | lockdep_assert_held(&wil->vif_mutex); |
d35c2b6f ME |
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 */ | |
e00243fa LD |
354 | if (vif->scan_request && |
355 | vif->scan_request->wdev == wil->p2p_wdev) | |
356 | wil_abort_scan(vif, true); | |
d35c2b6f ME |
357 | goto out; |
358 | } | |
359 | ||
360 | /* Search or listen on p2p device */ | |
404bbb3c | 361 | mutex_unlock(&wil->vif_mutex); |
e00243fa | 362 | wil_p2p_stop_discovery(vif); |
404bbb3c | 363 | mutex_lock(&wil->vif_mutex); |
d35c2b6f | 364 | |
e00243fa | 365 | if (vif->scan_request) { |
d35c2b6f | 366 | /* search */ |
e00243fa LD |
367 | cfg80211_scan_done(vif->scan_request, &info); |
368 | vif->scan_request = NULL; | |
d35c2b6f ME |
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: | |
e00243fa | 378 | wil->radio_wdev = wil->main_ndev->ieee80211_ptr; |
d35c2b6f | 379 | } |