xref: /linux/drivers/net/wireless/ath/wil6210/p2p.c (revision 0883c2c06fb5bcf5b9e008270827e63c09a88c1e)
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 		channel = chan->hw_value;
119 
120 	wil_dbg_misc(wil, "%s: duration %d\n", __func__, duration);
121 
122 	mutex_lock(&wil->mutex);
123 
124 	if (p2p->discovery_started) {
125 		wil_err(wil, "%s: discovery already ongoing\n", __func__);
126 		rc = -EBUSY;
127 		goto out;
128 	}
129 
130 	rc = wmi_p2p_cfg(wil, channel, P2P_DEFAULT_BI);
131 	if (rc) {
132 		wil_err(wil, "%s: wmi_p2p_cfg failed\n", __func__);
133 		goto out;
134 	}
135 
136 	rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID);
137 	if (rc) {
138 		wil_err(wil, "%s: wmi_set_ssid failed\n", __func__);
139 		goto out_stop;
140 	}
141 
142 	rc = wmi_start_listen(wil);
143 	if (rc) {
144 		wil_err(wil, "%s: wmi_start_listen failed\n", __func__);
145 		goto out_stop;
146 	}
147 
148 	memcpy(&p2p->listen_chan, chan, sizeof(*chan));
149 	*cookie = ++p2p->cookie;
150 
151 	p2p->discovery_started = 1;
152 	INIT_WORK(&p2p->discovery_expired_work, wil_p2p_listen_expired);
153 	mod_timer(&p2p->discovery_timer,
154 		  jiffies + msecs_to_jiffies(duration));
155 
156 out_stop:
157 	if (rc)
158 		wmi_stop_discovery(wil);
159 
160 out:
161 	mutex_unlock(&wil->mutex);
162 	return rc;
163 }
164 
165 u8 wil_p2p_stop_discovery(struct wil6210_priv *wil)
166 {
167 	struct wil_p2p_info *p2p = &wil->p2p;
168 	u8 started = p2p->discovery_started;
169 
170 	if (p2p->discovery_started) {
171 		del_timer_sync(&p2p->discovery_timer);
172 		p2p->discovery_started = 0;
173 		wmi_stop_discovery(wil);
174 	}
175 
176 	return started;
177 }
178 
179 int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie)
180 {
181 	struct wil_p2p_info *p2p = &wil->p2p;
182 	u8 started;
183 
184 	mutex_lock(&wil->mutex);
185 
186 	if (cookie != p2p->cookie) {
187 		wil_info(wil, "%s: Cookie mismatch: 0x%016llx vs. 0x%016llx\n",
188 			 __func__, p2p->cookie, cookie);
189 		mutex_unlock(&wil->mutex);
190 		return -ENOENT;
191 	}
192 
193 	started = wil_p2p_stop_discovery(wil);
194 
195 	mutex_unlock(&wil->mutex);
196 
197 	if (!started) {
198 		wil_err(wil, "%s: listen not started\n", __func__);
199 		return -ENOENT;
200 	}
201 
202 	mutex_lock(&wil->p2p_wdev_mutex);
203 	cfg80211_remain_on_channel_expired(wil->radio_wdev,
204 					   p2p->cookie,
205 					   &p2p->listen_chan,
206 					   GFP_KERNEL);
207 	wil->radio_wdev = wil->wdev;
208 	mutex_unlock(&wil->p2p_wdev_mutex);
209 	return 0;
210 }
211 
212 void wil_p2p_listen_expired(struct work_struct *work)
213 {
214 	struct wil_p2p_info *p2p = container_of(work,
215 			struct wil_p2p_info, discovery_expired_work);
216 	struct wil6210_priv *wil = container_of(p2p,
217 			struct wil6210_priv, p2p);
218 	u8 started;
219 
220 	wil_dbg_misc(wil, "%s()\n", __func__);
221 
222 	mutex_lock(&wil->mutex);
223 	started = wil_p2p_stop_discovery(wil);
224 	mutex_unlock(&wil->mutex);
225 
226 	if (started) {
227 		mutex_lock(&wil->p2p_wdev_mutex);
228 		cfg80211_remain_on_channel_expired(wil->radio_wdev,
229 						   p2p->cookie,
230 						   &p2p->listen_chan,
231 						   GFP_KERNEL);
232 		wil->radio_wdev = wil->wdev;
233 		mutex_unlock(&wil->p2p_wdev_mutex);
234 	}
235 
236 }
237 
238 void wil_p2p_search_expired(struct work_struct *work)
239 {
240 	struct wil_p2p_info *p2p = container_of(work,
241 			struct wil_p2p_info, discovery_expired_work);
242 	struct wil6210_priv *wil = container_of(p2p,
243 			struct wil6210_priv, p2p);
244 	u8 started;
245 
246 	wil_dbg_misc(wil, "%s()\n", __func__);
247 
248 	mutex_lock(&wil->mutex);
249 	started = wil_p2p_stop_discovery(wil);
250 	mutex_unlock(&wil->mutex);
251 
252 	if (started) {
253 		mutex_lock(&wil->p2p_wdev_mutex);
254 		cfg80211_scan_done(wil->scan_request, 0);
255 		wil->scan_request = NULL;
256 		wil->radio_wdev = wil->wdev;
257 		mutex_unlock(&wil->p2p_wdev_mutex);
258 	}
259 }
260