• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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