Merge tag 'hwmon-for-linus-v4.8-2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[cascardo/linux.git] / drivers / net / wireless / ath / wil6210 / p2p.c
1 /*
2  * Copyright (c) 2014-2016 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 bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request)
26 {
27         return (request->n_channels == 1) &&
28                (request->channels[0]->hw_value == P2P_DMG_SOCIAL_CHANNEL);
29 }
30
31 void wil_p2p_discovery_timer_fn(ulong x)
32 {
33         struct wil6210_priv *wil = (void *)x;
34
35         wil_dbg_misc(wil, "%s\n", __func__);
36
37         schedule_work(&wil->p2p.discovery_expired_work);
38 }
39
40 int wil_p2p_search(struct wil6210_priv *wil,
41                    struct cfg80211_scan_request *request)
42 {
43         int rc;
44         struct wil_p2p_info *p2p = &wil->p2p;
45
46         wil_dbg_misc(wil, "%s: channel %d\n",
47                      __func__, P2P_DMG_SOCIAL_CHANNEL);
48
49         mutex_lock(&wil->mutex);
50
51         if (p2p->discovery_started) {
52                 wil_err(wil, "%s: search failed. discovery already ongoing\n",
53                         __func__);
54                 rc = -EBUSY;
55                 goto out;
56         }
57
58         rc = wmi_p2p_cfg(wil, P2P_DMG_SOCIAL_CHANNEL, P2P_DEFAULT_BI);
59         if (rc) {
60                 wil_err(wil, "%s: wmi_p2p_cfg failed\n", __func__);
61                 goto out;
62         }
63
64         rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
65         if (rc) {
66                 wil_err(wil, "%s: wmi_set_ssid failed\n", __func__);
67                 goto out_stop;
68         }
69
70         /* Set application IE to probe request and probe response */
71         rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ,
72                         request->ie_len, request->ie);
73         if (rc) {
74                 wil_err(wil, "%s: wmi_set_ie(WMI_FRAME_PROBE_REQ) failed\n",
75                         __func__);
76                 goto out_stop;
77         }
78
79         /* supplicant doesn't provide Probe Response IEs. As a workaround -
80          * re-use Probe Request IEs
81          */
82         rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP,
83                         request->ie_len, request->ie);
84         if (rc) {
85                 wil_err(wil, "%s: wmi_set_ie(WMI_FRAME_PROBE_RESP) failed\n",
86                         __func__);
87                 goto out_stop;
88         }
89
90         rc = wmi_start_search(wil);
91         if (rc) {
92                 wil_err(wil, "%s: wmi_start_search failed\n", __func__);
93                 goto out_stop;
94         }
95
96         p2p->discovery_started = 1;
97         INIT_WORK(&p2p->discovery_expired_work, wil_p2p_search_expired);
98         mod_timer(&p2p->discovery_timer,
99                   jiffies + msecs_to_jiffies(P2P_SEARCH_DURATION_MS));
100
101 out_stop:
102         if (rc)
103                 wmi_stop_discovery(wil);
104
105 out:
106         mutex_unlock(&wil->mutex);
107         return rc;
108 }
109
110 int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration,
111                    struct ieee80211_channel *chan, u64 *cookie)
112 {
113         struct wil_p2p_info *p2p = &wil->p2p;
114         u8 channel = P2P_DMG_SOCIAL_CHANNEL;
115         int rc;
116
117         if (!chan)
118                 return -EINVAL;
119
120         channel = chan->hw_value;
121
122         wil_dbg_misc(wil, "%s: duration %d\n", __func__, duration);
123
124         mutex_lock(&wil->mutex);
125
126         if (p2p->discovery_started) {
127                 wil_err(wil, "%s: discovery already ongoing\n", __func__);
128                 rc = -EBUSY;
129                 goto out;
130         }
131
132         rc = wmi_p2p_cfg(wil, channel, P2P_DEFAULT_BI);
133         if (rc) {
134                 wil_err(wil, "%s: wmi_p2p_cfg failed\n", __func__);
135                 goto out;
136         }
137
138         rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
139         if (rc) {
140                 wil_err(wil, "%s: wmi_set_ssid failed\n", __func__);
141                 goto out_stop;
142         }
143
144         rc = wmi_start_listen(wil);
145         if (rc) {
146                 wil_err(wil, "%s: wmi_start_listen failed\n", __func__);
147                 goto out_stop;
148         }
149
150         memcpy(&p2p->listen_chan, chan, sizeof(*chan));
151         *cookie = ++p2p->cookie;
152
153         p2p->discovery_started = 1;
154         INIT_WORK(&p2p->discovery_expired_work, wil_p2p_listen_expired);
155         mod_timer(&p2p->discovery_timer,
156                   jiffies + msecs_to_jiffies(duration));
157
158 out_stop:
159         if (rc)
160                 wmi_stop_discovery(wil);
161
162 out:
163         mutex_unlock(&wil->mutex);
164         return rc;
165 }
166
167 u8 wil_p2p_stop_discovery(struct wil6210_priv *wil)
168 {
169         struct wil_p2p_info *p2p = &wil->p2p;
170         u8 started = p2p->discovery_started;
171
172         if (p2p->discovery_started) {
173                 del_timer_sync(&p2p->discovery_timer);
174                 p2p->discovery_started = 0;
175                 wmi_stop_discovery(wil);
176         }
177
178         return started;
179 }
180
181 int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie)
182 {
183         struct wil_p2p_info *p2p = &wil->p2p;
184         u8 started;
185
186         mutex_lock(&wil->mutex);
187
188         if (cookie != p2p->cookie) {
189                 wil_info(wil, "%s: Cookie mismatch: 0x%016llx vs. 0x%016llx\n",
190                          __func__, p2p->cookie, cookie);
191                 mutex_unlock(&wil->mutex);
192                 return -ENOENT;
193         }
194
195         started = wil_p2p_stop_discovery(wil);
196
197         mutex_unlock(&wil->mutex);
198
199         if (!started) {
200                 wil_err(wil, "%s: listen not started\n", __func__);
201                 return -ENOENT;
202         }
203
204         mutex_lock(&wil->p2p_wdev_mutex);
205         cfg80211_remain_on_channel_expired(wil->radio_wdev,
206                                            p2p->cookie,
207                                            &p2p->listen_chan,
208                                            GFP_KERNEL);
209         wil->radio_wdev = wil->wdev;
210         mutex_unlock(&wil->p2p_wdev_mutex);
211         return 0;
212 }
213
214 void wil_p2p_listen_expired(struct work_struct *work)
215 {
216         struct wil_p2p_info *p2p = container_of(work,
217                         struct wil_p2p_info, discovery_expired_work);
218         struct wil6210_priv *wil = container_of(p2p,
219                         struct wil6210_priv, p2p);
220         u8 started;
221
222         wil_dbg_misc(wil, "%s()\n", __func__);
223
224         mutex_lock(&wil->mutex);
225         started = wil_p2p_stop_discovery(wil);
226         mutex_unlock(&wil->mutex);
227
228         if (started) {
229                 mutex_lock(&wil->p2p_wdev_mutex);
230                 cfg80211_remain_on_channel_expired(wil->radio_wdev,
231                                                    p2p->cookie,
232                                                    &p2p->listen_chan,
233                                                    GFP_KERNEL);
234                 wil->radio_wdev = wil->wdev;
235                 mutex_unlock(&wil->p2p_wdev_mutex);
236         }
237
238 }
239
240 void wil_p2p_search_expired(struct work_struct *work)
241 {
242         struct wil_p2p_info *p2p = container_of(work,
243                         struct wil_p2p_info, discovery_expired_work);
244         struct wil6210_priv *wil = container_of(p2p,
245                         struct wil6210_priv, p2p);
246         u8 started;
247
248         wil_dbg_misc(wil, "%s()\n", __func__);
249
250         mutex_lock(&wil->mutex);
251         started = wil_p2p_stop_discovery(wil);
252         mutex_unlock(&wil->mutex);
253
254         if (started) {
255                 struct cfg80211_scan_info info = {
256                         .aborted = false,
257                 };
258
259                 mutex_lock(&wil->p2p_wdev_mutex);
260                 cfg80211_scan_done(wil->scan_request, &info);
261                 wil->scan_request = NULL;
262                 wil->radio_wdev = wil->wdev;
263                 mutex_unlock(&wil->p2p_wdev_mutex);
264         }
265 }