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
wil_p2p_is_social_scan(struct cfg80211_scan_request * request)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
wil_p2p_discovery_timer_fn(ulong x)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
wil_p2p_search(struct wil6210_priv * wil,struct cfg80211_scan_request * request)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
wil_p2p_listen(struct wil6210_priv * wil,unsigned int duration,struct ieee80211_channel * chan,u64 * cookie)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
wil_p2p_stop_discovery(struct wil6210_priv * wil)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
wil_p2p_cancel_listen(struct wil6210_priv * wil,u64 cookie)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
wil_p2p_listen_expired(struct work_struct * work)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
wil_p2p_search_expired(struct work_struct * work)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 }
266
wil_p2p_stop_radio_operations(struct wil6210_priv * wil)267 void wil_p2p_stop_radio_operations(struct wil6210_priv *wil)
268 {
269 struct wil_p2p_info *p2p = &wil->p2p;
270 struct cfg80211_scan_info info = {
271 .aborted = true,
272 };
273
274 lockdep_assert_held(&wil->mutex);
275
276 mutex_lock(&wil->p2p_wdev_mutex);
277
278 if (wil->radio_wdev != wil->p2p_wdev)
279 goto out;
280
281 if (!p2p->discovery_started) {
282 /* Regular scan on the p2p device */
283 if (wil->scan_request &&
284 wil->scan_request->wdev == wil->p2p_wdev) {
285 cfg80211_scan_done(wil->scan_request, &info);
286 wil->scan_request = NULL;
287 }
288 goto out;
289 }
290
291 /* Search or listen on p2p device */
292 mutex_unlock(&wil->p2p_wdev_mutex);
293 wil_p2p_stop_discovery(wil);
294 mutex_lock(&wil->p2p_wdev_mutex);
295
296 if (wil->scan_request) {
297 /* search */
298 cfg80211_scan_done(wil->scan_request, &info);
299 wil->scan_request = NULL;
300 } else {
301 /* listen */
302 cfg80211_remain_on_channel_expired(wil->radio_wdev,
303 p2p->cookie,
304 &p2p->listen_chan,
305 GFP_KERNEL);
306 }
307
308 out:
309 wil->radio_wdev = wil->wdev;
310 mutex_unlock(&wil->p2p_wdev_mutex);
311 }
312