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