diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/net/wireless/bcmdhd/wl_cfgp2p.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/net/wireless/bcmdhd/wl_cfgp2p.c')
-rw-r--r-- | drivers/net/wireless/bcmdhd/wl_cfgp2p.c | 1469 |
1 files changed, 1469 insertions, 0 deletions
diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c new file mode 100644 index 00000000000..4ee6557e17d --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c | |||
@@ -0,0 +1,1469 @@ | |||
1 | /* | ||
2 | * Linux cfgp2p driver | ||
3 | * | ||
4 | * Copyright (C) 1999-2011, Broadcom Corporation | ||
5 | * | ||
6 | * Unless you and Broadcom execute a separate written software license | ||
7 | * agreement governing use of this software, this software is licensed to you | ||
8 | * under the terms of the GNU General Public License version 2 (the "GPL"), | ||
9 | * available at http://www.broadcom.com/licenses/GPLv2.php, with the | ||
10 | * following added to such license: | ||
11 | * | ||
12 | * As a special exception, the copyright holders of this software give you | ||
13 | * permission to link this software with independent modules, and to copy and | ||
14 | * distribute the resulting executable under terms of your choice, provided that | ||
15 | * you also meet, for each linked independent module, the terms and conditions of | ||
16 | * the license of that module. An independent module is a module which is not | ||
17 | * derived from this software. The special exception does not apply to any | ||
18 | * modifications of the software. | ||
19 | * | ||
20 | * Notwithstanding the above, under no circumstances may you combine this | ||
21 | * software in any way with any other Broadcom software provided under a license | ||
22 | * other than the GPL, without Broadcom's express prior written consent. | ||
23 | * | ||
24 | * $Id: wl_cfgp2p.c,v 1.1.4.1.2.14 2011-02-09 01:40:07 $ | ||
25 | * | ||
26 | */ | ||
27 | #include <typedefs.h> | ||
28 | #include <linuxver.h> | ||
29 | #include <osl.h> | ||
30 | #include <linux/kernel.h> | ||
31 | #include <linux/kthread.h> | ||
32 | #include <linux/netdevice.h> | ||
33 | #include <linux/types.h> | ||
34 | #include <linux/string.h> | ||
35 | #include <linux/timer.h> | ||
36 | #include <linux/if_arp.h> | ||
37 | #include <asm/uaccess.h> | ||
38 | |||
39 | #include <bcmutils.h> | ||
40 | #include <bcmendian.h> | ||
41 | #include <proto/ethernet.h> | ||
42 | #include <dngl_stats.h> | ||
43 | #include <dhd.h> | ||
44 | #include <dhdioctl.h> | ||
45 | #include <wlioctl.h> | ||
46 | |||
47 | #include <wl_cfg80211.h> | ||
48 | #include <wl_cfgp2p.h> | ||
49 | #include <wldev_common.h> | ||
50 | |||
51 | |||
52 | static s8 ioctlbuf[WLC_IOCTL_MAXLEN]; | ||
53 | static s8 scanparambuf[WLC_IOCTL_SMLEN]; | ||
54 | static s8 *smbuf = ioctlbuf; | ||
55 | |||
56 | static bool | ||
57 | wl_cfgp2p_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, const u8 *oui, u32 oui_len, u8 type); | ||
58 | |||
59 | static s32 | ||
60 | wl_cfgp2p_vndr_ie(struct net_device *ndev, s32 bssidx, s32 pktflag, | ||
61 | s8 *oui, s32 ie_id, s8 *data, s32 data_len, s32 delete); | ||
62 | /* | ||
63 | * Initialize variables related to P2P | ||
64 | * | ||
65 | */ | ||
66 | s32 | ||
67 | wl_cfgp2p_init_priv(struct wl_priv *wl) | ||
68 | { | ||
69 | if (!(wl->p2p = kzalloc(sizeof(struct p2p_info), GFP_KERNEL))) { | ||
70 | CFGP2P_ERR(("struct p2p_info allocation failed\n")); | ||
71 | return -ENOMEM; | ||
72 | } | ||
73 | #define INIT_IE(IE_TYPE, BSS_TYPE) \ | ||
74 | do { \ | ||
75 | memset(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie, 0, \ | ||
76 | sizeof(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie)); \ | ||
77 | wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie_len = 0; \ | ||
78 | } while (0); | ||
79 | |||
80 | INIT_IE(probe_req, P2PAPI_BSSCFG_PRIMARY); | ||
81 | INIT_IE(probe_res, P2PAPI_BSSCFG_PRIMARY); | ||
82 | INIT_IE(assoc_req, P2PAPI_BSSCFG_PRIMARY); | ||
83 | INIT_IE(assoc_res, P2PAPI_BSSCFG_PRIMARY); | ||
84 | INIT_IE(beacon, P2PAPI_BSSCFG_PRIMARY); | ||
85 | INIT_IE(probe_req, P2PAPI_BSSCFG_DEVICE); | ||
86 | INIT_IE(probe_res, P2PAPI_BSSCFG_DEVICE); | ||
87 | INIT_IE(assoc_req, P2PAPI_BSSCFG_DEVICE); | ||
88 | INIT_IE(assoc_res, P2PAPI_BSSCFG_DEVICE); | ||
89 | INIT_IE(beacon, P2PAPI_BSSCFG_DEVICE); | ||
90 | INIT_IE(probe_req, P2PAPI_BSSCFG_CONNECTION); | ||
91 | INIT_IE(probe_res, P2PAPI_BSSCFG_CONNECTION); | ||
92 | INIT_IE(assoc_req, P2PAPI_BSSCFG_CONNECTION); | ||
93 | INIT_IE(assoc_res, P2PAPI_BSSCFG_CONNECTION); | ||
94 | INIT_IE(beacon, P2PAPI_BSSCFG_CONNECTION); | ||
95 | #undef INIT_IE | ||
96 | wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY) = wl_to_prmry_ndev(wl); | ||
97 | wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_PRIMARY) = 0; | ||
98 | wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = NULL; | ||
99 | wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = 0; | ||
100 | wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION) = NULL; | ||
101 | wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION) = 0; | ||
102 | spin_lock_init(&wl->p2p->timer_lock); | ||
103 | return BCME_OK; | ||
104 | |||
105 | } | ||
106 | /* | ||
107 | * Deinitialize variables related to P2P | ||
108 | * | ||
109 | */ | ||
110 | void | ||
111 | wl_cfgp2p_deinit_priv(struct wl_priv *wl) | ||
112 | { | ||
113 | if (wl->p2p) { | ||
114 | kfree(wl->p2p); | ||
115 | wl->p2p = NULL; | ||
116 | } | ||
117 | wl->p2p_supported = 0; | ||
118 | } | ||
119 | /* | ||
120 | * Set P2P functions into firmware | ||
121 | */ | ||
122 | s32 | ||
123 | wl_cfgp2p_set_firm_p2p(struct wl_priv *wl) | ||
124 | { | ||
125 | struct net_device *ndev = wl_to_prmry_ndev(wl); | ||
126 | struct ether_addr null_eth_addr = { { 0, 0, 0, 0, 0, 0 } }; | ||
127 | s32 ret = BCME_OK; | ||
128 | s32 val = 0; | ||
129 | /* Do we have to check whether APSTA is enabled or not ? */ | ||
130 | wldev_iovar_getint(ndev, "apsta", &val); | ||
131 | if (val == 0) { | ||
132 | val = 1; | ||
133 | wldev_ioctl(ndev, WLC_DOWN, &val, sizeof(s32), true); | ||
134 | wldev_iovar_setint(ndev, "apsta", val); | ||
135 | wldev_ioctl(ndev, WLC_UP, &val, sizeof(s32), true); | ||
136 | } | ||
137 | val = 1; | ||
138 | /* Disable firmware roaming for P2P */ | ||
139 | wldev_iovar_setint(ndev, "roam_off", val); | ||
140 | /* In case of COB type, firmware has default mac address | ||
141 | * After Initializing firmware, we have to set current mac address to | ||
142 | * firmware for P2P device address | ||
143 | */ | ||
144 | ret = wldev_iovar_setbuf_bsscfg(ndev, "p2p_da_override", &null_eth_addr, | ||
145 | sizeof(null_eth_addr), ioctlbuf, sizeof(ioctlbuf), 0); | ||
146 | if (ret && ret != BCME_UNSUPPORTED) { | ||
147 | CFGP2P_ERR(("failed to update device address\n")); | ||
148 | } | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | /* Create a new P2P BSS. | ||
153 | * Parameters: | ||
154 | * @mac : MAC address of the BSS to create | ||
155 | * @if_type : interface type: WL_P2P_IF_GO or WL_P2P_IF_CLIENT | ||
156 | * @chspec : chspec to use if creating a GO BSS. | ||
157 | * Returns 0 if success. | ||
158 | */ | ||
159 | s32 | ||
160 | wl_cfgp2p_ifadd(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, | ||
161 | chanspec_t chspec) | ||
162 | { | ||
163 | wl_p2p_if_t ifreq; | ||
164 | s32 err; | ||
165 | struct net_device *ndev = wl_to_prmry_ndev(wl); | ||
166 | |||
167 | ifreq.type = if_type; | ||
168 | ifreq.chspec = chspec; | ||
169 | memcpy(ifreq.addr.octet, mac->octet, sizeof(ifreq.addr.octet)); | ||
170 | |||
171 | CFGP2P_INFO(("---wl p2p_ifadd %02x:%02x:%02x:%02x:%02x:%02x %s %u\n", | ||
172 | ifreq.addr.octet[0], ifreq.addr.octet[1], ifreq.addr.octet[2], | ||
173 | ifreq.addr.octet[3], ifreq.addr.octet[4], ifreq.addr.octet[5], | ||
174 | (if_type == WL_P2P_IF_GO) ? "go" : "client", | ||
175 | (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT)); | ||
176 | |||
177 | err = wldev_iovar_setbuf(ndev, "p2p_ifadd", &ifreq, sizeof(ifreq), | ||
178 | ioctlbuf, sizeof(ioctlbuf)); | ||
179 | return err; | ||
180 | } | ||
181 | |||
182 | /* Delete a P2P BSS. | ||
183 | * Parameters: | ||
184 | * @mac : MAC address of the BSS to create | ||
185 | * Returns 0 if success. | ||
186 | */ | ||
187 | s32 | ||
188 | wl_cfgp2p_ifdel(struct wl_priv *wl, struct ether_addr *mac) | ||
189 | { | ||
190 | s32 ret; | ||
191 | struct net_device *netdev = wl_to_prmry_ndev(wl); | ||
192 | |||
193 | CFGP2P_INFO(("------primary idx %d : wl p2p_ifdel %02x:%02x:%02x:%02x:%02x:%02x\n", | ||
194 | netdev->ifindex, mac->octet[0], mac->octet[1], mac->octet[2], | ||
195 | mac->octet[3], mac->octet[4], mac->octet[5])); | ||
196 | ret = wldev_iovar_setbuf(netdev, "p2p_ifdel", mac, sizeof(*mac), | ||
197 | ioctlbuf, sizeof(ioctlbuf)); | ||
198 | if (unlikely(ret < 0)) { | ||
199 | printk("'wl p2p_ifdel' error %d\n", ret); | ||
200 | } | ||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | /* Change a P2P Role. | ||
205 | * Parameters: | ||
206 | * @mac : MAC address of the BSS to change a role | ||
207 | * Returns 0 if success. | ||
208 | */ | ||
209 | s32 | ||
210 | wl_cfgp2p_ifchange(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, | ||
211 | chanspec_t chspec) | ||
212 | { | ||
213 | wl_p2p_if_t ifreq; | ||
214 | s32 err; | ||
215 | struct net_device *netdev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); | ||
216 | |||
217 | ifreq.type = if_type; | ||
218 | ifreq.chspec = chspec; | ||
219 | memcpy(ifreq.addr.octet, mac->octet, sizeof(ifreq.addr.octet)); | ||
220 | |||
221 | CFGP2P_INFO(("---wl p2p_ifchange %02x:%02x:%02x:%02x:%02x:%02x %s %u\n", | ||
222 | ifreq.addr.octet[0], ifreq.addr.octet[1], ifreq.addr.octet[2], | ||
223 | ifreq.addr.octet[3], ifreq.addr.octet[4], ifreq.addr.octet[5], | ||
224 | (if_type == WL_P2P_IF_GO) ? "go" : "client", | ||
225 | (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT)); | ||
226 | |||
227 | err = wldev_iovar_setbuf(netdev, "p2p_ifupd", &ifreq, sizeof(ifreq), | ||
228 | ioctlbuf, sizeof(ioctlbuf)); | ||
229 | |||
230 | if (unlikely(err < 0)) { | ||
231 | printk("'wl p2p_ifupd' error %d\n", err); | ||
232 | } | ||
233 | return err; | ||
234 | } | ||
235 | |||
236 | |||
237 | /* Get the index of a created P2P BSS. | ||
238 | * Parameters: | ||
239 | * @mac : MAC address of the created BSS | ||
240 | * @index : output: index of created BSS | ||
241 | * Returns 0 if success. | ||
242 | */ | ||
243 | s32 | ||
244 | wl_cfgp2p_ifidx(struct wl_priv *wl, struct ether_addr *mac, s32 *index) | ||
245 | { | ||
246 | s32 ret; | ||
247 | u8 getbuf[64]; | ||
248 | struct net_device *dev = wl_to_prmry_ndev(wl); | ||
249 | |||
250 | CFGP2P_INFO(("---wl p2p_if %02x:%02x:%02x:%02x:%02x:%02x\n", | ||
251 | mac->octet[0], mac->octet[1], mac->octet[2], | ||
252 | mac->octet[3], mac->octet[4], mac->octet[5])); | ||
253 | |||
254 | ret = wldev_iovar_getbuf_bsscfg(dev, "p2p_if", mac, sizeof(*mac), | ||
255 | getbuf, sizeof(getbuf), wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_PRIMARY)); | ||
256 | |||
257 | if (ret == 0) { | ||
258 | memcpy(index, getbuf, sizeof(index)); | ||
259 | CFGP2P_INFO(("---wl p2p_if ==> %d\n", *index)); | ||
260 | } | ||
261 | |||
262 | return ret; | ||
263 | } | ||
264 | |||
265 | s32 | ||
266 | wl_cfgp2p_set_discovery(struct wl_priv *wl, s32 on) | ||
267 | { | ||
268 | s32 ret = BCME_OK; | ||
269 | struct net_device *ndev = wl_to_prmry_ndev(wl); | ||
270 | CFGP2P_DBG(("enter\n")); | ||
271 | |||
272 | ret = wldev_iovar_setint(ndev, "p2p_disc", on); | ||
273 | |||
274 | if (unlikely(ret < 0)) { | ||
275 | CFGP2P_ERR(("p2p_disc %d error %d\n", on, ret)); | ||
276 | } | ||
277 | |||
278 | return ret; | ||
279 | } | ||
280 | |||
281 | /* Set the WL driver's P2P mode. | ||
282 | * Parameters : | ||
283 | * @mode : is one of WL_P2P_DISC_ST_{SCAN,LISTEN,SEARCH}. | ||
284 | * @channel : the channel to listen | ||
285 | * @listen_ms : the time (milli seconds) to wait | ||
286 | * @bssidx : bss index for BSSCFG | ||
287 | * Returns 0 if success | ||
288 | */ | ||
289 | |||
290 | s32 | ||
291 | wl_cfgp2p_set_p2p_mode(struct wl_priv *wl, u8 mode, u32 channel, u16 listen_ms, int bssidx) | ||
292 | { | ||
293 | wl_p2p_disc_st_t discovery_mode; | ||
294 | s32 ret; | ||
295 | struct net_device *dev; | ||
296 | CFGP2P_DBG(("enter\n")); | ||
297 | |||
298 | if (unlikely(bssidx >= P2PAPI_BSSCFG_MAX)) { | ||
299 | CFGP2P_ERR((" %d index out of range\n", bssidx)); | ||
300 | return -1; | ||
301 | } | ||
302 | |||
303 | dev = wl_to_p2p_bss_ndev(wl, bssidx); | ||
304 | if (unlikely(dev == NULL)) { | ||
305 | CFGP2P_ERR(("bssidx %d is not assigned\n", bssidx)); | ||
306 | return BCME_NOTFOUND; | ||
307 | } | ||
308 | |||
309 | /* Put the WL driver into P2P Listen Mode to respond to P2P probe reqs */ | ||
310 | discovery_mode.state = mode; | ||
311 | discovery_mode.chspec = CH20MHZ_CHSPEC(channel); | ||
312 | discovery_mode.dwell = listen_ms; | ||
313 | ret = wldev_iovar_setbuf_bsscfg(dev, "p2p_state", &discovery_mode, | ||
314 | sizeof(discovery_mode), ioctlbuf, sizeof(ioctlbuf), bssidx); | ||
315 | |||
316 | return ret; | ||
317 | } | ||
318 | |||
319 | /* Get the index of the P2P Discovery BSS */ | ||
320 | s32 | ||
321 | wl_cfgp2p_get_disc_idx(struct wl_priv *wl, s32 *index) | ||
322 | { | ||
323 | s32 ret; | ||
324 | struct net_device *dev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); | ||
325 | |||
326 | ret = wldev_iovar_getint(dev, "p2p_dev", index); | ||
327 | CFGP2P_INFO(("p2p_dev bsscfg_idx=%d ret=%d\n", *index, ret)); | ||
328 | |||
329 | if (unlikely(ret < 0)) { | ||
330 | CFGP2P_ERR(("'p2p_dev' error %d\n", ret)); | ||
331 | return ret; | ||
332 | } | ||
333 | return ret; | ||
334 | } | ||
335 | |||
336 | s32 | ||
337 | wl_cfgp2p_init_discovery(struct wl_priv *wl) | ||
338 | { | ||
339 | |||
340 | s32 index = 0; | ||
341 | s32 ret = BCME_OK; | ||
342 | |||
343 | CFGP2P_DBG(("enter\n")); | ||
344 | |||
345 | if (wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) != 0) { | ||
346 | CFGP2P_ERR(("do nothing, already initialized\n")); | ||
347 | return ret; | ||
348 | } | ||
349 | |||
350 | ret = wl_cfgp2p_set_discovery(wl, 1); | ||
351 | if (ret < 0) { | ||
352 | CFGP2P_ERR(("set discover error\n")); | ||
353 | return ret; | ||
354 | } | ||
355 | /* Enable P2P Discovery in the WL Driver */ | ||
356 | ret = wl_cfgp2p_get_disc_idx(wl, &index); | ||
357 | |||
358 | if (ret < 0) { | ||
359 | return ret; | ||
360 | } | ||
361 | wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = | ||
362 | wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); | ||
363 | wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = index; | ||
364 | |||
365 | /* Set the initial discovery state to SCAN */ | ||
366 | ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, | ||
367 | wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); | ||
368 | |||
369 | if (unlikely(ret != 0)) { | ||
370 | CFGP2P_ERR(("unable to set WL_P2P_DISC_ST_SCAN\n")); | ||
371 | wl_cfgp2p_set_discovery(wl, 0); | ||
372 | wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = 0; | ||
373 | wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = NULL; | ||
374 | return 0; | ||
375 | } | ||
376 | return ret; | ||
377 | } | ||
378 | |||
379 | /* Deinitialize P2P Discovery | ||
380 | * Parameters : | ||
381 | * @wl : wl_private data | ||
382 | * Returns 0 if succes | ||
383 | */ | ||
384 | s32 | ||
385 | wl_cfgp2p_deinit_discovery(struct wl_priv *wl) | ||
386 | { | ||
387 | s32 ret = BCME_OK; | ||
388 | CFGP2P_DBG(("enter\n")); | ||
389 | |||
390 | if (wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) == 0) { | ||
391 | CFGP2P_ERR(("do nothing, not initialized\n")); | ||
392 | return -1; | ||
393 | } | ||
394 | /* Set the discovery state to SCAN */ | ||
395 | ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, | ||
396 | wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); | ||
397 | /* Disable P2P discovery in the WL driver (deletes the discovery BSSCFG) */ | ||
398 | ret = wl_cfgp2p_set_discovery(wl, 0); | ||
399 | |||
400 | /* Clear our saved WPS and P2P IEs for the discovery BSS. The driver | ||
401 | * deleted these IEs when wl_cfgp2p_set_discovery() deleted the discovery | ||
402 | * BSS. | ||
403 | */ | ||
404 | |||
405 | /* Clear the saved bsscfg index of the discovery BSSCFG to indicate we | ||
406 | * have no discovery BSS. | ||
407 | */ | ||
408 | wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = 0; | ||
409 | wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = NULL; | ||
410 | |||
411 | return ret; | ||
412 | |||
413 | } | ||
414 | /* Enable P2P Discovery | ||
415 | * Parameters: | ||
416 | * @wl : wl_private data | ||
417 | * @ie : probe request ie (WPS IE + P2P IE) | ||
418 | * @ie_len : probe request ie length | ||
419 | * Returns 0 if success. | ||
420 | */ | ||
421 | s32 | ||
422 | wl_cfgp2p_enable_discovery(struct wl_priv *wl, struct net_device *dev, const u8 *ie, u32 ie_len) | ||
423 | { | ||
424 | s32 ret = BCME_OK; | ||
425 | if (wl_get_p2p_status(wl, DISCOVERY_ON)) { | ||
426 | CFGP2P_INFO((" DISCOVERY is already initialized, we have nothing to do\n")); | ||
427 | goto set_ie; | ||
428 | } | ||
429 | |||
430 | wl_set_p2p_status(wl, DISCOVERY_ON); | ||
431 | |||
432 | CFGP2P_DBG(("enter\n")); | ||
433 | |||
434 | ret = wl_cfgp2p_init_discovery(wl); | ||
435 | if (unlikely(ret < 0)) { | ||
436 | CFGP2P_ERR((" init discovery error %d\n", ret)); | ||
437 | goto exit; | ||
438 | } | ||
439 | /* Set wsec to any non-zero value in the discovery bsscfg to ensure our | ||
440 | * P2P probe responses have the privacy bit set in the 802.11 WPA IE. | ||
441 | * Some peer devices may not initiate WPS with us if this bit is not set. | ||
442 | */ | ||
443 | ret = wldev_iovar_setint_bsscfg(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE), | ||
444 | "wsec", AES_ENABLED, wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); | ||
445 | if (unlikely(ret < 0)) { | ||
446 | CFGP2P_ERR((" wsec error %d\n", ret)); | ||
447 | } | ||
448 | set_ie: | ||
449 | ret = wl_cfgp2p_set_management_ie(wl, dev, | ||
450 | wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE), | ||
451 | VNDR_IE_PRBREQ_FLAG, ie, ie_len); | ||
452 | |||
453 | if (unlikely(ret < 0)) { | ||
454 | CFGP2P_ERR(("set probreq ie occurs error %d\n", ret)); | ||
455 | goto exit; | ||
456 | } | ||
457 | exit: | ||
458 | return ret; | ||
459 | } | ||
460 | |||
461 | /* Disable P2P Discovery | ||
462 | * Parameters: | ||
463 | * @wl : wl_private_data | ||
464 | * Returns 0 if success. | ||
465 | */ | ||
466 | s32 | ||
467 | wl_cfgp2p_disable_discovery(struct wl_priv *wl) | ||
468 | { | ||
469 | s32 ret = BCME_OK; | ||
470 | CFGP2P_DBG((" enter\n")); | ||
471 | wl_clr_p2p_status(wl, DISCOVERY_ON); | ||
472 | |||
473 | if (wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) == 0) { | ||
474 | CFGP2P_ERR((" do nothing, not initialized\n")); | ||
475 | goto exit; | ||
476 | } | ||
477 | |||
478 | ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, | ||
479 | wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); | ||
480 | |||
481 | if (unlikely(ret < 0)) { | ||
482 | |||
483 | CFGP2P_ERR(("unable to set WL_P2P_DISC_ST_SCAN\n")); | ||
484 | } | ||
485 | /* Do a scan abort to stop the driver's scan engine in case it is still | ||
486 | * waiting out an action frame tx dwell time. | ||
487 | */ | ||
488 | #ifdef NOT_YET | ||
489 | if (wl_get_p2p_status(wl, SCANNING)) { | ||
490 | p2pwlu_scan_abort(hdl, FALSE); | ||
491 | } | ||
492 | #endif | ||
493 | wl_clr_p2p_status(wl, DISCOVERY_ON); | ||
494 | ret = wl_cfgp2p_deinit_discovery(wl); | ||
495 | |||
496 | exit: | ||
497 | return ret; | ||
498 | } | ||
499 | |||
500 | s32 | ||
501 | wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, | ||
502 | u32 num_chans, u16 *channels, | ||
503 | s32 search_state, u16 action, u32 bssidx) | ||
504 | { | ||
505 | s32 ret = BCME_OK; | ||
506 | s32 memsize; | ||
507 | s32 eparams_size; | ||
508 | u32 i; | ||
509 | s8 *memblk; | ||
510 | wl_p2p_scan_t *p2p_params; | ||
511 | wl_escan_params_t *eparams; | ||
512 | wlc_ssid_t ssid; | ||
513 | /* Scan parameters */ | ||
514 | #define P2PAPI_SCAN_NPROBES 4 | ||
515 | #define P2PAPI_SCAN_DWELL_TIME_MS 80 | ||
516 | #define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 100 | ||
517 | #define P2PAPI_SCAN_HOME_TIME_MS 10 | ||
518 | struct net_device *pri_dev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); | ||
519 | wl_set_p2p_status(wl, SCANNING); | ||
520 | /* Allocate scan params which need space for 3 channels and 0 ssids */ | ||
521 | eparams_size = (WL_SCAN_PARAMS_FIXED_SIZE + | ||
522 | OFFSETOF(wl_escan_params_t, params)) + | ||
523 | num_chans * sizeof(eparams->params.channel_list[0]); | ||
524 | |||
525 | memsize = sizeof(wl_p2p_scan_t) + eparams_size; | ||
526 | memblk = scanparambuf; | ||
527 | if (memsize > sizeof(scanparambuf)) { | ||
528 | CFGP2P_ERR((" scanpar buf too small (%u > %u)\n", | ||
529 | memsize, sizeof(scanparambuf))); | ||
530 | return -1; | ||
531 | } | ||
532 | memset(memblk, 0, memsize); | ||
533 | memset(ioctlbuf, 0, sizeof(ioctlbuf)); | ||
534 | if (search_state == WL_P2P_DISC_ST_SEARCH) { | ||
535 | /* | ||
536 | * If we in SEARCH STATE, we don't need to set SSID explictly | ||
537 | * because dongle use P2P WILDCARD internally by default | ||
538 | */ | ||
539 | wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SEARCH, 0, 0, bssidx); | ||
540 | ssid.SSID_len = htod32(0); | ||
541 | |||
542 | } else if (search_state == WL_P2P_DISC_ST_SCAN) { | ||
543 | /* SCAN STATE 802.11 SCAN | ||
544 | * WFD Supplicant has p2p_find command with (type=progressive, type= full) | ||
545 | * So if P2P_find command with type=progressive, | ||
546 | * we have to set ssid to P2P WILDCARD because | ||
547 | * we just do broadcast scan unless setting SSID | ||
548 | */ | ||
549 | strcpy(ssid.SSID, WL_P2P_WILDCARD_SSID); | ||
550 | ssid.SSID_len = htod32(WL_P2P_WILDCARD_SSID_LEN); | ||
551 | wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, bssidx); | ||
552 | } | ||
553 | |||
554 | |||
555 | /* Fill in the P2P scan structure at the start of the iovar param block */ | ||
556 | p2p_params = (wl_p2p_scan_t*) memblk; | ||
557 | p2p_params->type = 'E'; | ||
558 | /* Fill in the Scan structure that follows the P2P scan structure */ | ||
559 | eparams = (wl_escan_params_t*) (p2p_params + 1); | ||
560 | eparams->params.bss_type = DOT11_BSSTYPE_ANY; | ||
561 | if (active) | ||
562 | eparams->params.scan_type = DOT11_SCANTYPE_ACTIVE; | ||
563 | else | ||
564 | eparams->params.scan_type = DOT11_SCANTYPE_PASSIVE; | ||
565 | |||
566 | memcpy(&eparams->params.bssid, ðer_bcast, ETHER_ADDR_LEN); | ||
567 | if (ssid.SSID_len) | ||
568 | memcpy(&eparams->params.ssid, &ssid, sizeof(wlc_ssid_t)); | ||
569 | |||
570 | eparams->params.nprobes = htod32(P2PAPI_SCAN_NPROBES); | ||
571 | eparams->params.home_time = htod32(P2PAPI_SCAN_HOME_TIME_MS); | ||
572 | if (wl_get_drv_status(wl, CONNECTED)) | ||
573 | eparams->params.active_time = htod32(-1); | ||
574 | else if (num_chans == 3) | ||
575 | eparams->params.active_time = htod32(P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS); | ||
576 | else | ||
577 | eparams->params.active_time = htod32(P2PAPI_SCAN_DWELL_TIME_MS); | ||
578 | eparams->params.passive_time = htod32(-1); | ||
579 | eparams->params.channel_num = htod32((0 << WL_SCAN_PARAMS_NSSID_SHIFT) | | ||
580 | (num_chans & WL_SCAN_PARAMS_COUNT_MASK)); | ||
581 | |||
582 | for (i = 0; i < num_chans; i++) { | ||
583 | eparams->params.channel_list[i] = htodchanspec(channels[i]); | ||
584 | } | ||
585 | eparams->version = htod32(ESCAN_REQ_VERSION); | ||
586 | eparams->action = htod16(action); | ||
587 | eparams->sync_id = htod16(0x1234); | ||
588 | CFGP2P_INFO(("SCAN CHANNELS : ")); | ||
589 | |||
590 | for (i = 0; i < num_chans; i++) { | ||
591 | if (i == 0) CFGP2P_INFO(("%d", channels[i])); | ||
592 | else CFGP2P_INFO((",%d", channels[i])); | ||
593 | } | ||
594 | |||
595 | CFGP2P_INFO(("\n")); | ||
596 | |||
597 | ret = wldev_iovar_setbuf_bsscfg(pri_dev, "p2p_scan", | ||
598 | memblk, memsize, smbuf, sizeof(ioctlbuf), bssidx); | ||
599 | return ret; | ||
600 | } | ||
601 | /* Check whether pointed-to IE looks like WPA. */ | ||
602 | #define wl_cfgp2p_is_wpa_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ | ||
603 | (const uint8 *)WPS_OUI, WPS_OUI_LEN, WPA_OUI_TYPE) | ||
604 | /* Check whether pointed-to IE looks like WPS. */ | ||
605 | #define wl_cfgp2p_is_wps_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ | ||
606 | (const uint8 *)WPS_OUI, WPS_OUI_LEN, WPS_OUI_TYPE) | ||
607 | /* Check whether the given IE looks like WFA P2P IE. */ | ||
608 | #define wl_cfgp2p_is_p2p_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ | ||
609 | (const uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_P2P) | ||
610 | /* Delete and Set a management vndr ie to firmware | ||
611 | * Parameters: | ||
612 | * @wl : wl_private data | ||
613 | * @ndev : net device for bssidx | ||
614 | * @bssidx : bssidx for BSS | ||
615 | * @pktflag : packet flag for IE (VNDR_IE_PRBREQ_FLAG,VNDR_IE_PRBRSP_FLAG, VNDR_IE_ASSOCRSP_FLAG, | ||
616 | * VNDR_IE_ASSOCREQ_FLAG) | ||
617 | * @ie : VNDR IE (such as P2P IE , WPS IE) | ||
618 | * @ie_len : VNDR IE Length | ||
619 | * Returns 0 if success. | ||
620 | */ | ||
621 | |||
622 | s32 | ||
623 | wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, | ||
624 | s32 pktflag, const u8 *vndr_ie, u32 vndr_ie_len) | ||
625 | { | ||
626 | /* Vendor-specific Information Element ID */ | ||
627 | #define VNDR_SPEC_ELEMENT_ID 0xdd | ||
628 | s32 ret = BCME_OK; | ||
629 | u32 pos; | ||
630 | u8 *ie_buf; | ||
631 | u8 *mgmt_ie_buf = NULL; | ||
632 | u32 mgmt_ie_buf_len = 0; | ||
633 | u32 *mgmt_ie_len = 0; | ||
634 | u8 ie_id, ie_len; | ||
635 | u8 delete = 0; | ||
636 | #define IE_TYPE(type, bsstype) (wl_to_p2p_bss_saved_ie(wl, bsstype).p2p_ ## type ## _ie) | ||
637 | #define IE_TYPE_LEN(type, bsstype) (wl_to_p2p_bss_saved_ie(wl, bsstype).p2p_ ## type ## _ie_len) | ||
638 | if (wl->p2p_supported && p2p_on(wl) && bssidx != -1) { | ||
639 | if (bssidx == P2PAPI_BSSCFG_PRIMARY) | ||
640 | bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); | ||
641 | switch (pktflag) { | ||
642 | case VNDR_IE_PRBREQ_FLAG : | ||
643 | mgmt_ie_buf = IE_TYPE(probe_req, bssidx); | ||
644 | mgmt_ie_len = &IE_TYPE_LEN(probe_req, bssidx); | ||
645 | mgmt_ie_buf_len = sizeof(IE_TYPE(probe_req, bssidx)); | ||
646 | break; | ||
647 | case VNDR_IE_PRBRSP_FLAG : | ||
648 | mgmt_ie_buf = IE_TYPE(probe_res, bssidx); | ||
649 | mgmt_ie_len = &IE_TYPE_LEN(probe_res, bssidx); | ||
650 | mgmt_ie_buf_len = sizeof(IE_TYPE(probe_res, bssidx)); | ||
651 | break; | ||
652 | case VNDR_IE_ASSOCREQ_FLAG : | ||
653 | mgmt_ie_buf = IE_TYPE(assoc_req, bssidx); | ||
654 | mgmt_ie_len = &IE_TYPE_LEN(assoc_req, bssidx); | ||
655 | mgmt_ie_buf_len = sizeof(IE_TYPE(assoc_req, bssidx)); | ||
656 | break; | ||
657 | case VNDR_IE_ASSOCRSP_FLAG : | ||
658 | mgmt_ie_buf = IE_TYPE(assoc_res, bssidx); | ||
659 | mgmt_ie_len = &IE_TYPE_LEN(assoc_res, bssidx); | ||
660 | mgmt_ie_buf_len = sizeof(IE_TYPE(assoc_res, bssidx)); | ||
661 | break; | ||
662 | case VNDR_IE_BEACON_FLAG : | ||
663 | mgmt_ie_buf = IE_TYPE(beacon, bssidx); | ||
664 | mgmt_ie_len = &IE_TYPE_LEN(beacon, bssidx); | ||
665 | mgmt_ie_buf_len = sizeof(IE_TYPE(beacon, bssidx)); | ||
666 | break; | ||
667 | default: | ||
668 | mgmt_ie_buf = NULL; | ||
669 | mgmt_ie_len = NULL; | ||
670 | CFGP2P_ERR(("not suitable type\n")); | ||
671 | return -1; | ||
672 | } | ||
673 | } else if (get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { | ||
674 | switch (pktflag) { | ||
675 | case VNDR_IE_PRBRSP_FLAG : | ||
676 | mgmt_ie_buf = wl->ap_info->probe_res_ie; | ||
677 | mgmt_ie_len = &wl->ap_info->probe_res_ie_len; | ||
678 | mgmt_ie_buf_len = sizeof(wl->ap_info->probe_res_ie); | ||
679 | break; | ||
680 | case VNDR_IE_BEACON_FLAG : | ||
681 | mgmt_ie_buf = wl->ap_info->beacon_ie; | ||
682 | mgmt_ie_len = &wl->ap_info->beacon_ie_len; | ||
683 | mgmt_ie_buf_len = sizeof(wl->ap_info->beacon_ie); | ||
684 | break; | ||
685 | default: | ||
686 | mgmt_ie_buf = NULL; | ||
687 | mgmt_ie_len = NULL; | ||
688 | CFGP2P_ERR(("not suitable type\n")); | ||
689 | return -1; | ||
690 | } | ||
691 | bssidx = 0; | ||
692 | } else if (bssidx == -1 && get_mode_by_netdev(wl, ndev) == WL_MODE_BSS) { | ||
693 | switch (pktflag) { | ||
694 | case VNDR_IE_PRBREQ_FLAG : | ||
695 | mgmt_ie_buf = wl->sta_info->probe_req_ie; | ||
696 | mgmt_ie_len = &wl->sta_info->probe_req_ie_len; | ||
697 | mgmt_ie_buf_len = sizeof(wl->sta_info->probe_req_ie); | ||
698 | break; | ||
699 | case VNDR_IE_ASSOCREQ_FLAG : | ||
700 | mgmt_ie_buf = wl->sta_info->assoc_req_ie; | ||
701 | mgmt_ie_len = &wl->sta_info->assoc_req_ie_len; | ||
702 | mgmt_ie_buf_len = sizeof(wl->sta_info->assoc_req_ie); | ||
703 | break; | ||
704 | default: | ||
705 | mgmt_ie_buf = NULL; | ||
706 | mgmt_ie_len = NULL; | ||
707 | CFGP2P_ERR(("not suitable type\n")); | ||
708 | return -1; | ||
709 | } | ||
710 | bssidx = 0; | ||
711 | } else { | ||
712 | CFGP2P_ERR(("not suitable type\n")); | ||
713 | return -1; | ||
714 | } | ||
715 | |||
716 | if (vndr_ie_len > mgmt_ie_buf_len) { | ||
717 | CFGP2P_ERR(("extra IE size too big\n")); | ||
718 | ret = -ENOMEM; | ||
719 | } else { | ||
720 | if (mgmt_ie_buf != NULL) { | ||
721 | if (vndr_ie_len && (vndr_ie_len == *mgmt_ie_len) && | ||
722 | (memcmp(mgmt_ie_buf, vndr_ie, vndr_ie_len) == 0)) { | ||
723 | CFGP2P_INFO(("Previous mgmt IE is equals to current IE")); | ||
724 | goto exit; | ||
725 | } | ||
726 | pos = 0; | ||
727 | delete = 1; | ||
728 | ie_buf = (u8 *) mgmt_ie_buf; | ||
729 | while (pos < *mgmt_ie_len) { | ||
730 | ie_id = ie_buf[pos++]; | ||
731 | ie_len = ie_buf[pos++]; | ||
732 | if ((ie_id == DOT11_MNG_VS_ID) && | ||
733 | (wl_cfgp2p_is_wps_ie(&ie_buf[pos-2], NULL, 0) || | ||
734 | wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0))) { | ||
735 | CFGP2P_INFO(("DELELED ID : %d, Len : %d , OUI :" | ||
736 | "%02x:%02x:%02x\n", ie_id, ie_len, ie_buf[pos], | ||
737 | ie_buf[pos+1], ie_buf[pos+2])); | ||
738 | ret = wl_cfgp2p_vndr_ie(ndev, bssidx, pktflag, ie_buf+pos, | ||
739 | VNDR_SPEC_ELEMENT_ID, ie_buf+pos+3, ie_len-3, delete); | ||
740 | } | ||
741 | pos += ie_len; | ||
742 | } | ||
743 | |||
744 | } | ||
745 | *mgmt_ie_len = 0; | ||
746 | /* Add if there is any extra IE */ | ||
747 | if (vndr_ie && vndr_ie_len) { | ||
748 | /* save the current IE in wl struct */ | ||
749 | memcpy(mgmt_ie_buf, vndr_ie, vndr_ie_len); | ||
750 | *mgmt_ie_len = vndr_ie_len; | ||
751 | pos = 0; | ||
752 | ie_buf = (u8 *) vndr_ie; | ||
753 | delete = 0; | ||
754 | while (pos < vndr_ie_len) { | ||
755 | ie_id = ie_buf[pos++]; | ||
756 | ie_len = ie_buf[pos++]; | ||
757 | if ((ie_id == DOT11_MNG_VS_ID) && | ||
758 | (wl_cfgp2p_is_wps_ie(&ie_buf[pos-2], NULL, 0) || | ||
759 | wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0))) { | ||
760 | CFGP2P_INFO(("ADDED ID : %d, Len : %d , OUI :" | ||
761 | "%02x:%02x:%02x\n", ie_id, ie_len, ie_buf[pos], | ||
762 | ie_buf[pos+1], ie_buf[pos+2])); | ||
763 | ret = wl_cfgp2p_vndr_ie(ndev, bssidx, pktflag, ie_buf+pos, | ||
764 | VNDR_SPEC_ELEMENT_ID, ie_buf+pos+3, ie_len-3, delete); | ||
765 | } | ||
766 | pos += ie_len; | ||
767 | } | ||
768 | } | ||
769 | } | ||
770 | #undef IE_TYPE | ||
771 | #undef IE_TYPE_LEN | ||
772 | exit: | ||
773 | return ret; | ||
774 | } | ||
775 | |||
776 | /* Clear the manament IE buffer of BSSCFG | ||
777 | * Parameters: | ||
778 | * @wl : wl_private data | ||
779 | * @bssidx : bssidx for BSS | ||
780 | * | ||
781 | * Returns 0 if success. | ||
782 | */ | ||
783 | s32 | ||
784 | wl_cfgp2p_clear_management_ie(struct wl_priv *wl, s32 bssidx) | ||
785 | { | ||
786 | #define INIT_IE(IE_TYPE, BSS_TYPE) \ | ||
787 | do { \ | ||
788 | memset(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie, 0, \ | ||
789 | sizeof(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie)); \ | ||
790 | wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie_len = 0; \ | ||
791 | } while (0); | ||
792 | if (bssidx < 0) { | ||
793 | CFGP2P_ERR(("invalid bssidx\n")); | ||
794 | return BCME_BADARG; | ||
795 | } | ||
796 | INIT_IE(probe_req, bssidx); | ||
797 | INIT_IE(probe_res, bssidx); | ||
798 | INIT_IE(assoc_req, bssidx); | ||
799 | INIT_IE(assoc_res, bssidx); | ||
800 | INIT_IE(beacon, bssidx); | ||
801 | return BCME_OK; | ||
802 | } | ||
803 | |||
804 | |||
805 | /* Is any of the tlvs the expected entry? If | ||
806 | * not update the tlvs buffer pointer/length. | ||
807 | */ | ||
808 | static bool | ||
809 | wl_cfgp2p_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, const u8 *oui, u32 oui_len, u8 type) | ||
810 | { | ||
811 | /* If the contents match the OUI and the type */ | ||
812 | if (ie[TLV_LEN_OFF] >= oui_len + 1 && | ||
813 | !bcmp(&ie[TLV_BODY_OFF], oui, oui_len) && | ||
814 | type == ie[TLV_BODY_OFF + oui_len]) { | ||
815 | return TRUE; | ||
816 | } | ||
817 | |||
818 | if (tlvs == NULL) | ||
819 | return FALSE; | ||
820 | /* point to the next ie */ | ||
821 | ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN; | ||
822 | /* calculate the length of the rest of the buffer */ | ||
823 | *tlvs_len -= (int)(ie - *tlvs); | ||
824 | /* update the pointer to the start of the buffer */ | ||
825 | *tlvs = ie; | ||
826 | |||
827 | return FALSE; | ||
828 | } | ||
829 | |||
830 | wpa_ie_fixed_t * | ||
831 | wl_cfgp2p_find_wpaie(u8 *parse, u32 len) | ||
832 | { | ||
833 | bcm_tlv_t *ie; | ||
834 | |||
835 | while ((ie = bcm_parse_tlvs(parse, (u32)len, DOT11_MNG_VS_ID))) { | ||
836 | if (wl_cfgp2p_is_wpa_ie((u8*)ie, &parse, &len)) { | ||
837 | return (wpa_ie_fixed_t *)ie; | ||
838 | } | ||
839 | } | ||
840 | return NULL; | ||
841 | } | ||
842 | |||
843 | wpa_ie_fixed_t * | ||
844 | wl_cfgp2p_find_wpsie(u8 *parse, u32 len) | ||
845 | { | ||
846 | bcm_tlv_t *ie; | ||
847 | |||
848 | while ((ie = bcm_parse_tlvs(parse, (u32)len, DOT11_MNG_VS_ID))) { | ||
849 | if (wl_cfgp2p_is_wps_ie((u8*)ie, &parse, &len)) { | ||
850 | return (wpa_ie_fixed_t *)ie; | ||
851 | } | ||
852 | } | ||
853 | return NULL; | ||
854 | } | ||
855 | |||
856 | wifi_p2p_ie_t * | ||
857 | wl_cfgp2p_find_p2pie(u8 *parse, u32 len) | ||
858 | { | ||
859 | bcm_tlv_t *ie; | ||
860 | |||
861 | while ((ie = bcm_parse_tlvs(parse, (int)len, DOT11_MNG_VS_ID))) { | ||
862 | if (wl_cfgp2p_is_p2p_ie((uint8*)ie, &parse, &len)) { | ||
863 | return (wifi_p2p_ie_t *)ie; | ||
864 | } | ||
865 | } | ||
866 | return NULL; | ||
867 | } | ||
868 | |||
869 | static s32 | ||
870 | wl_cfgp2p_vndr_ie(struct net_device *ndev, s32 bssidx, s32 pktflag, | ||
871 | s8 *oui, s32 ie_id, s8 *data, s32 data_len, s32 delete) | ||
872 | { | ||
873 | s32 err = BCME_OK; | ||
874 | s32 buf_len; | ||
875 | s32 iecount; | ||
876 | |||
877 | vndr_ie_setbuf_t *ie_setbuf; | ||
878 | |||
879 | /* Validate the pktflag parameter */ | ||
880 | if ((pktflag & ~(VNDR_IE_BEACON_FLAG | VNDR_IE_PRBRSP_FLAG | | ||
881 | VNDR_IE_ASSOCRSP_FLAG | VNDR_IE_AUTHRSP_FLAG | | ||
882 | VNDR_IE_PRBREQ_FLAG | VNDR_IE_ASSOCREQ_FLAG))) { | ||
883 | CFGP2P_ERR(("p2pwl_vndr_ie: Invalid packet flag 0x%x\n", pktflag)); | ||
884 | return -1; | ||
885 | } | ||
886 | |||
887 | buf_len = sizeof(vndr_ie_setbuf_t) + data_len - 1; | ||
888 | ie_setbuf = (vndr_ie_setbuf_t *) kzalloc(buf_len, GFP_KERNEL); | ||
889 | |||
890 | CFGP2P_INFO((" ie_id : %02x, data length : %d\n", ie_id, data_len)); | ||
891 | if (!ie_setbuf) { | ||
892 | |||
893 | CFGP2P_ERR(("Error allocating buffer for IE\n")); | ||
894 | return -ENOMEM; | ||
895 | } | ||
896 | if (delete) | ||
897 | strcpy(ie_setbuf->cmd, "del"); | ||
898 | else | ||
899 | strcpy(ie_setbuf->cmd, "add"); | ||
900 | /* Buffer contains only 1 IE */ | ||
901 | iecount = htod32(1); | ||
902 | memcpy((void *)&ie_setbuf->vndr_ie_buffer.iecount, &iecount, sizeof(int)); | ||
903 | pktflag = htod32(pktflag); | ||
904 | memcpy((void *)&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].pktflag, | ||
905 | &pktflag, sizeof(uint32)); | ||
906 | ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.id = ie_id; | ||
907 | ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len | ||
908 | = (uchar)(data_len + VNDR_IE_MIN_LEN); | ||
909 | memcpy(ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui, oui, 3); | ||
910 | memcpy(ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.data, data, data_len); | ||
911 | err = wldev_iovar_setbuf_bsscfg(ndev, "vndr_ie", ie_setbuf, buf_len, | ||
912 | ioctlbuf, sizeof(ioctlbuf), bssidx); | ||
913 | |||
914 | CFGP2P_INFO(("vndr_ie iovar returns %d\n", err)); | ||
915 | kfree(ie_setbuf); | ||
916 | return err; | ||
917 | } | ||
918 | |||
919 | /* | ||
920 | * Search the bssidx based on dev argument | ||
921 | * Parameters: | ||
922 | * @wl : wl_private data | ||
923 | * @ndev : net device to search bssidx | ||
924 | * Returns bssidx for ndev | ||
925 | */ | ||
926 | s32 | ||
927 | wl_cfgp2p_find_idx(struct wl_priv *wl, struct net_device *ndev) | ||
928 | { | ||
929 | u32 i; | ||
930 | s32 index = -1; | ||
931 | |||
932 | if (ndev == NULL) { | ||
933 | CFGP2P_ERR((" ndev is NULL\n")); | ||
934 | goto exit; | ||
935 | } | ||
936 | if (!wl->p2p_supported) { | ||
937 | return P2PAPI_BSSCFG_PRIMARY; | ||
938 | } | ||
939 | for (i = 0; i < P2PAPI_BSSCFG_MAX; i++) { | ||
940 | if (ndev == wl_to_p2p_bss_ndev(wl, i)) { | ||
941 | index = wl_to_p2p_bss_bssidx(wl, i); | ||
942 | break; | ||
943 | } | ||
944 | } | ||
945 | if (index == -1) | ||
946 | return P2PAPI_BSSCFG_PRIMARY; | ||
947 | exit: | ||
948 | return index; | ||
949 | } | ||
950 | /* | ||
951 | * Callback function for WLC_E_P2P_DISC_LISTEN_COMPLETE | ||
952 | */ | ||
953 | s32 | ||
954 | wl_cfgp2p_listen_complete(struct wl_priv *wl, struct net_device *ndev, | ||
955 | const wl_event_msg_t *e, void *data) | ||
956 | { | ||
957 | s32 ret = BCME_OK; | ||
958 | |||
959 | CFGP2P_DBG((" Enter\n")); | ||
960 | if (wl_get_p2p_status(wl, LISTEN_EXPIRED) == 0) { | ||
961 | wl_set_p2p_status(wl, LISTEN_EXPIRED); | ||
962 | if (timer_pending(&wl->p2p->listen_timer)) { | ||
963 | spin_lock_bh(&wl->p2p->timer_lock); | ||
964 | del_timer_sync(&wl->p2p->listen_timer); | ||
965 | spin_unlock_bh(&wl->p2p->timer_lock); | ||
966 | } | ||
967 | cfg80211_remain_on_channel_expired(ndev, wl->cache_cookie, &wl->remain_on_chan, | ||
968 | wl->remain_on_chan_type, GFP_KERNEL); | ||
969 | } else | ||
970 | wl_clr_p2p_status(wl, LISTEN_EXPIRED); | ||
971 | |||
972 | return ret; | ||
973 | |||
974 | } | ||
975 | |||
976 | /* | ||
977 | * Timer expire callback function for LISTEN | ||
978 | * We can't report cfg80211_remain_on_channel_expired from Timer ISR context, | ||
979 | * so lets do it from thread context. | ||
980 | */ | ||
981 | static void | ||
982 | wl_cfgp2p_listen_expired(unsigned long data) | ||
983 | { | ||
984 | wl_event_msg_t msg; | ||
985 | struct wl_priv *wl = (struct wl_priv *) data; | ||
986 | |||
987 | CFGP2P_DBG((" Enter\n")); | ||
988 | msg.event_type = hton32(WLC_E_P2P_DISC_LISTEN_COMPLETE); | ||
989 | wl_cfg80211_event(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE), &msg, NULL); | ||
990 | } | ||
991 | |||
992 | /* | ||
993 | * Do a P2P Listen on the given channel for the given duration. | ||
994 | * A listen consists of sitting idle and responding to P2P probe requests | ||
995 | * with a P2P probe response. | ||
996 | * | ||
997 | * This fn assumes dongle p2p device discovery is already enabled. | ||
998 | * Parameters : | ||
999 | * @wl : wl_private data | ||
1000 | * @channel : channel to listen | ||
1001 | * @duration_ms : the time (milli seconds) to wait | ||
1002 | */ | ||
1003 | s32 | ||
1004 | wl_cfgp2p_discover_listen(struct wl_priv *wl, s32 channel, u32 duration_ms) | ||
1005 | { | ||
1006 | #define INIT_TIMER(timer, func, duration, extra_delay) \ | ||
1007 | do { \ | ||
1008 | init_timer(timer); \ | ||
1009 | timer->function = func; \ | ||
1010 | timer->expires = jiffies + msecs_to_jiffies(duration + extra_delay); \ | ||
1011 | timer->data = (unsigned long) wl; \ | ||
1012 | add_timer(timer); \ | ||
1013 | } while (0); | ||
1014 | |||
1015 | s32 ret = BCME_OK; | ||
1016 | struct timer_list *_timer; | ||
1017 | CFGP2P_DBG((" Enter Channel : %d, Duration : %d\n", channel, duration_ms)); | ||
1018 | if (unlikely(wl_get_p2p_status(wl, DISCOVERY_ON) == 0)) { | ||
1019 | |||
1020 | CFGP2P_ERR((" Discovery is not set, so we have noting to do\n")); | ||
1021 | |||
1022 | ret = BCME_NOTREADY; | ||
1023 | goto exit; | ||
1024 | } | ||
1025 | if (timer_pending(&wl->p2p->listen_timer)) { | ||
1026 | CFGP2P_DBG(("previous LISTEN is not completed yet\n")); | ||
1027 | goto exit; | ||
1028 | |||
1029 | } else | ||
1030 | wl_clr_p2p_status(wl, LISTEN_EXPIRED); | ||
1031 | |||
1032 | wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_LISTEN, channel, (u16) duration_ms, | ||
1033 | wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); | ||
1034 | _timer = &wl->p2p->listen_timer; | ||
1035 | |||
1036 | /* We will wait to receive WLC_E_P2P_DISC_LISTEN_COMPLETE from dongle , | ||
1037 | * otherwise we will wait up to duration_ms + 200ms | ||
1038 | */ | ||
1039 | INIT_TIMER(_timer, wl_cfgp2p_listen_expired, duration_ms, 200); | ||
1040 | |||
1041 | #undef INIT_TIMER | ||
1042 | exit: | ||
1043 | return ret; | ||
1044 | } | ||
1045 | |||
1046 | |||
1047 | s32 | ||
1048 | wl_cfgp2p_discover_enable_search(struct wl_priv *wl, u8 enable) | ||
1049 | { | ||
1050 | s32 ret = BCME_OK; | ||
1051 | CFGP2P_DBG((" Enter\n")); | ||
1052 | if (!wl_get_p2p_status(wl, DISCOVERY_ON)) { | ||
1053 | |||
1054 | CFGP2P_DBG((" do nothing, discovery is off\n")); | ||
1055 | return ret; | ||
1056 | } | ||
1057 | if (wl_get_p2p_status(wl, SEARCH_ENABLED) == enable) { | ||
1058 | CFGP2P_DBG(("already : %d\n", enable)); | ||
1059 | return ret; | ||
1060 | } | ||
1061 | |||
1062 | wl_chg_p2p_status(wl, SEARCH_ENABLED); | ||
1063 | /* When disabling Search, reset the WL driver's p2p discovery state to | ||
1064 | * WL_P2P_DISC_ST_SCAN. | ||
1065 | */ | ||
1066 | if (!enable) { | ||
1067 | wl_clr_p2p_status(wl, SCANNING); | ||
1068 | ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, | ||
1069 | wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); | ||
1070 | } | ||
1071 | |||
1072 | return ret; | ||
1073 | } | ||
1074 | |||
1075 | /* | ||
1076 | * Callback function for WLC_E_ACTION_FRAME_COMPLETE, WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE | ||
1077 | */ | ||
1078 | s32 | ||
1079 | wl_cfgp2p_action_tx_complete(struct wl_priv *wl, struct net_device *ndev, | ||
1080 | const wl_event_msg_t *e, void *data) | ||
1081 | { | ||
1082 | s32 ret = BCME_OK; | ||
1083 | u32 event_type = ntoh32(e->event_type); | ||
1084 | u32 status = ntoh32(e->status); | ||
1085 | CFGP2P_DBG((" Enter\n")); | ||
1086 | if (event_type == WLC_E_ACTION_FRAME_COMPLETE) { | ||
1087 | |||
1088 | CFGP2P_INFO((" WLC_E_ACTION_FRAME_COMPLETE is received : %d\n", status)); | ||
1089 | if (status == WLC_E_STATUS_SUCCESS) { | ||
1090 | wl_set_p2p_status(wl, ACTION_TX_COMPLETED); | ||
1091 | } | ||
1092 | else { | ||
1093 | wl_set_p2p_status(wl, ACTION_TX_NOACK); | ||
1094 | CFGP2P_ERR(("WLC_E_ACTION_FRAME_COMPLETE : NO ACK\n")); | ||
1095 | } | ||
1096 | wake_up_interruptible(&wl->dongle_event_wait); | ||
1097 | } else { | ||
1098 | CFGP2P_INFO((" WLC_E_ACTION_FRAME_OFFCHAN_COMPLETE is received," | ||
1099 | "status : %d\n", status)); | ||
1100 | } | ||
1101 | return ret; | ||
1102 | } | ||
1103 | /* Send an action frame immediately without doing channel synchronization. | ||
1104 | * | ||
1105 | * This function does not wait for a completion event before returning. | ||
1106 | * The WLC_E_ACTION_FRAME_COMPLETE event will be received when the action | ||
1107 | * frame is transmitted. | ||
1108 | * The WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE event will be received when an | ||
1109 | * 802.11 ack has been received for the sent action frame. | ||
1110 | */ | ||
1111 | s32 | ||
1112 | wl_cfgp2p_tx_action_frame(struct wl_priv *wl, struct net_device *dev, | ||
1113 | wl_af_params_t *af_params, s32 bssidx) | ||
1114 | { | ||
1115 | s32 ret = BCME_OK; | ||
1116 | s32 timeout = 0; | ||
1117 | |||
1118 | |||
1119 | CFGP2P_INFO(("\n")); | ||
1120 | CFGP2P_INFO(("channel : %u , dwell time : %u\n", | ||
1121 | af_params->channel, af_params->dwell_time)); | ||
1122 | |||
1123 | wl_clr_p2p_status(wl, ACTION_TX_COMPLETED); | ||
1124 | wl_clr_p2p_status(wl, ACTION_TX_NOACK); | ||
1125 | #define MAX_WAIT_TIME 2000 | ||
1126 | if (bssidx == P2PAPI_BSSCFG_PRIMARY) | ||
1127 | bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); | ||
1128 | |||
1129 | ret = wldev_iovar_setbuf_bsscfg(dev, "actframe", | ||
1130 | af_params, sizeof(*af_params), ioctlbuf, sizeof(ioctlbuf), bssidx); | ||
1131 | |||
1132 | if (ret < 0) { | ||
1133 | |||
1134 | CFGP2P_ERR((" sending action frame is failed\n")); | ||
1135 | goto exit; | ||
1136 | } | ||
1137 | timeout = wait_event_interruptible_timeout(wl->dongle_event_wait, | ||
1138 | (wl_get_p2p_status(wl, ACTION_TX_COMPLETED) || wl_get_p2p_status(wl, ACTION_TX_NOACK)), | ||
1139 | msecs_to_jiffies(MAX_WAIT_TIME)); | ||
1140 | |||
1141 | if (timeout > 0 && wl_get_p2p_status(wl, ACTION_TX_COMPLETED)) { | ||
1142 | CFGP2P_INFO(("tx action frame operation is completed\n")); | ||
1143 | ret = BCME_OK; | ||
1144 | } else { | ||
1145 | ret = BCME_ERROR; | ||
1146 | CFGP2P_INFO(("tx action frame operation is failed\n")); | ||
1147 | } | ||
1148 | exit: | ||
1149 | CFGP2P_INFO((" via act frame iovar : status = %d\n", ret)); | ||
1150 | #undef MAX_WAIT_TIME | ||
1151 | return ret; | ||
1152 | } | ||
1153 | |||
1154 | /* Generate our P2P Device Address and P2P Interface Address from our primary | ||
1155 | * MAC address. | ||
1156 | */ | ||
1157 | void | ||
1158 | wl_cfgp2p_generate_bss_mac(struct ether_addr *primary_addr, | ||
1159 | struct ether_addr *out_dev_addr, struct ether_addr *out_int_addr) | ||
1160 | { | ||
1161 | memset(out_dev_addr, 0, sizeof(*out_dev_addr)); | ||
1162 | memset(out_int_addr, 0, sizeof(*out_int_addr)); | ||
1163 | |||
1164 | /* Generate the P2P Device Address. This consists of the device's | ||
1165 | * primary MAC address with the locally administered bit set. | ||
1166 | */ | ||
1167 | memcpy(out_dev_addr, primary_addr, sizeof(*out_dev_addr)); | ||
1168 | out_dev_addr->octet[0] |= 0x02; | ||
1169 | |||
1170 | /* Generate the P2P Interface Address. If the discovery and connection | ||
1171 | * BSSCFGs need to simultaneously co-exist, then this address must be | ||
1172 | * different from the P2P Device Address. | ||
1173 | */ | ||
1174 | memcpy(out_int_addr, out_dev_addr, sizeof(*out_int_addr)); | ||
1175 | out_int_addr->octet[4] ^= 0x80; | ||
1176 | |||
1177 | } | ||
1178 | |||
1179 | /* P2P IF Address change to Virtual Interface MAC Address */ | ||
1180 | void | ||
1181 | wl_cfg80211_change_ifaddr(u8* buf, struct ether_addr *p2p_int_addr, u8 element_id) | ||
1182 | { | ||
1183 | wifi_p2p_ie_t *ie = (wifi_p2p_ie_t*) buf; | ||
1184 | u16 len = ie->len; | ||
1185 | u8 *subel; | ||
1186 | u8 subelt_id; | ||
1187 | u16 subelt_len; | ||
1188 | CFGP2P_DBG((" Enter\n")); | ||
1189 | |||
1190 | /* Point subel to the P2P IE's subelt field. | ||
1191 | * Subtract the preceding fields (id, len, OUI, oui_type) from the length. | ||
1192 | */ | ||
1193 | subel = ie->subelts; | ||
1194 | len -= 4; /* exclude OUI + OUI_TYPE */ | ||
1195 | |||
1196 | while (len >= 3) { | ||
1197 | /* attribute id */ | ||
1198 | subelt_id = *subel; | ||
1199 | subel += 1; | ||
1200 | len -= 1; | ||
1201 | |||
1202 | /* 2-byte little endian */ | ||
1203 | subelt_len = *subel++; | ||
1204 | subelt_len |= *subel++ << 8; | ||
1205 | |||
1206 | len -= 2; | ||
1207 | len -= subelt_len; /* for the remaining subelt fields */ | ||
1208 | |||
1209 | if (subelt_id == element_id) { | ||
1210 | if (subelt_id == P2P_SEID_INTINTADDR) { | ||
1211 | memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); | ||
1212 | CFGP2P_INFO(("Intended P2P Interface Address ATTR FOUND\n")); | ||
1213 | } else if (subelt_id == P2P_SEID_DEV_ID) { | ||
1214 | memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); | ||
1215 | CFGP2P_INFO(("Device ID ATTR FOUND\n")); | ||
1216 | } else if (subelt_id == P2P_SEID_DEV_INFO) { | ||
1217 | memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); | ||
1218 | CFGP2P_INFO(("Device INFO ATTR FOUND\n")); | ||
1219 | } else if (subelt_id == P2P_SEID_GROUP_ID) { | ||
1220 | memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); | ||
1221 | CFGP2P_INFO(("GROUP ID ATTR FOUND\n")); | ||
1222 | } return; | ||
1223 | } else { | ||
1224 | CFGP2P_DBG(("OTHER id : %d\n", subelt_id)); | ||
1225 | } | ||
1226 | subel += subelt_len; | ||
1227 | } | ||
1228 | } | ||
1229 | /* | ||
1230 | * Check if a BSS is up. | ||
1231 | * This is a common implementation called by most OSL implementations of | ||
1232 | * p2posl_bss_isup(). DO NOT call this function directly from the | ||
1233 | * common code -- call p2posl_bss_isup() instead to allow the OSL to | ||
1234 | * override the common implementation if necessary. | ||
1235 | */ | ||
1236 | bool | ||
1237 | wl_cfgp2p_bss_isup(struct net_device *ndev, int bsscfg_idx) | ||
1238 | { | ||
1239 | s32 result, val; | ||
1240 | bool isup = false; | ||
1241 | s8 getbuf[64]; | ||
1242 | |||
1243 | /* Check if the BSS is up */ | ||
1244 | *(int*)getbuf = -1; | ||
1245 | result = wldev_iovar_getbuf_bsscfg(ndev, "bss", &bsscfg_idx, | ||
1246 | sizeof(bsscfg_idx), getbuf, sizeof(getbuf), 0); | ||
1247 | if (result != 0) { | ||
1248 | CFGP2P_ERR(("'wl bss -C %d' failed: %d\n", bsscfg_idx, result)); | ||
1249 | CFGP2P_ERR(("NOTE: this ioctl error is normal " | ||
1250 | "when the BSS has not been created yet.\n")); | ||
1251 | } else { | ||
1252 | val = *(int*)getbuf; | ||
1253 | val = dtoh32(val); | ||
1254 | CFGP2P_INFO(("---wl bss -C %d ==> %d\n", bsscfg_idx, val)); | ||
1255 | isup = (val ? TRUE : FALSE); | ||
1256 | } | ||
1257 | return isup; | ||
1258 | } | ||
1259 | |||
1260 | |||
1261 | /* Bring up or down a BSS */ | ||
1262 | s32 | ||
1263 | wl_cfgp2p_bss(struct net_device *ndev, s32 bsscfg_idx, s32 up) | ||
1264 | { | ||
1265 | s32 ret = BCME_OK; | ||
1266 | s32 val = up ? 1 : 0; | ||
1267 | |||
1268 | struct { | ||
1269 | s32 cfg; | ||
1270 | s32 val; | ||
1271 | } bss_setbuf; | ||
1272 | |||
1273 | bss_setbuf.cfg = htod32(bsscfg_idx); | ||
1274 | bss_setbuf.val = htod32(val); | ||
1275 | CFGP2P_INFO(("---wl bss -C %d %s\n", bsscfg_idx, up ? "up" : "down")); | ||
1276 | ret = wldev_iovar_setbuf(ndev, "bss", &bss_setbuf, sizeof(bss_setbuf), | ||
1277 | ioctlbuf, sizeof(ioctlbuf)); | ||
1278 | |||
1279 | if (ret != 0) { | ||
1280 | CFGP2P_ERR(("'bss %d' failed with %d\n", up, ret)); | ||
1281 | } | ||
1282 | |||
1283 | return ret; | ||
1284 | } | ||
1285 | |||
1286 | /* Check if 'p2p' is supported in the driver */ | ||
1287 | s32 | ||
1288 | wl_cfgp2p_supported(struct wl_priv *wl, struct net_device *ndev) | ||
1289 | { | ||
1290 | s32 ret = BCME_OK; | ||
1291 | s32 p2p_supported = 0; | ||
1292 | ret = wldev_iovar_getint(ndev, "p2p", | ||
1293 | &p2p_supported); | ||
1294 | if (ret < 0) { | ||
1295 | CFGP2P_ERR(("wl p2p error %d\n", ret)); | ||
1296 | return 0; | ||
1297 | } | ||
1298 | if (p2p_supported == 1) { | ||
1299 | CFGP2P_INFO(("p2p is supported\n")); | ||
1300 | } else { | ||
1301 | CFGP2P_INFO(("p2p is unsupported\n")); | ||
1302 | p2p_supported = 0; | ||
1303 | } | ||
1304 | return p2p_supported; | ||
1305 | } | ||
1306 | |||
1307 | /* Cleanup P2P resources */ | ||
1308 | s32 | ||
1309 | wl_cfgp2p_down(struct wl_priv *wl) | ||
1310 | { | ||
1311 | if (timer_pending(&wl->p2p->listen_timer)) | ||
1312 | del_timer_sync(&wl->p2p->listen_timer); | ||
1313 | wl_cfgp2p_deinit_priv(wl); | ||
1314 | return 0; | ||
1315 | } | ||
1316 | |||
1317 | s32 wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) | ||
1318 | { | ||
1319 | s32 ret = -1; | ||
1320 | int count, start, duration; | ||
1321 | wl_p2p_sched_t dongle_noa; | ||
1322 | |||
1323 | CFGP2P_DBG((" Enter\n")); | ||
1324 | |||
1325 | memset(&dongle_noa, 0, sizeof(dongle_noa)); | ||
1326 | |||
1327 | if (wl->p2p && wl->p2p->vif_created) { | ||
1328 | |||
1329 | wl->p2p->noa.desc[0].start = 0; | ||
1330 | |||
1331 | sscanf(buf, "%d %d %d", &count, &start, &duration); | ||
1332 | CFGP2P_DBG(("set_p2p_noa count %d start %d duration %d\n", | ||
1333 | count, start, duration)); | ||
1334 | if (count != -1) | ||
1335 | wl->p2p->noa.desc[0].count = count; | ||
1336 | |||
1337 | /* supplicant gives interval as start */ | ||
1338 | if (start != -1) | ||
1339 | wl->p2p->noa.desc[0].interval = start; | ||
1340 | |||
1341 | if (duration != -1) | ||
1342 | wl->p2p->noa.desc[0].duration = duration; | ||
1343 | |||
1344 | if (wl->p2p->noa.desc[0].count != 255) { | ||
1345 | wl->p2p->noa.desc[0].start = 200; | ||
1346 | dongle_noa.type = WL_P2P_SCHED_TYPE_REQ_ABS; | ||
1347 | dongle_noa.action = WL_P2P_SCHED_ACTION_GOOFF; | ||
1348 | dongle_noa.option = WL_P2P_SCHED_OPTION_TSFOFS; | ||
1349 | } | ||
1350 | else { | ||
1351 | /* Continuous NoA interval. */ | ||
1352 | dongle_noa.action = WL_P2P_SCHED_ACTION_NONE; | ||
1353 | dongle_noa.type = WL_P2P_SCHED_TYPE_ABS; | ||
1354 | if ((wl->p2p->noa.desc[0].interval == 102) || | ||
1355 | (wl->p2p->noa.desc[0].interval == 100)) { | ||
1356 | wl->p2p->noa.desc[0].start = 100 - | ||
1357 | wl->p2p->noa.desc[0].duration; | ||
1358 | dongle_noa.option = WL_P2P_SCHED_OPTION_BCNPCT; | ||
1359 | } | ||
1360 | else { | ||
1361 | dongle_noa.option = WL_P2P_SCHED_OPTION_NORMAL; | ||
1362 | } | ||
1363 | } | ||
1364 | /* Put the noa descriptor in dongle format for dongle */ | ||
1365 | dongle_noa.desc[0].count = htod32(wl->p2p->noa.desc[0].count); | ||
1366 | if (dongle_noa.option == WL_P2P_SCHED_OPTION_BCNPCT) { | ||
1367 | dongle_noa.desc[0].start = htod32(wl->p2p->noa.desc[0].start); | ||
1368 | dongle_noa.desc[0].duration = htod32(wl->p2p->noa.desc[0].duration); | ||
1369 | } | ||
1370 | else { | ||
1371 | dongle_noa.desc[0].start = htod32(wl->p2p->noa.desc[0].start*1000); | ||
1372 | dongle_noa.desc[0].duration = htod32(wl->p2p->noa.desc[0].duration*1000); | ||
1373 | } | ||
1374 | dongle_noa.desc[0].interval = htod32(wl->p2p->noa.desc[0].interval*1000); | ||
1375 | |||
1376 | ret = wldev_iovar_setbuf(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), | ||
1377 | "p2p_noa", &dongle_noa, sizeof(dongle_noa), ioctlbuf, sizeof(ioctlbuf)); | ||
1378 | |||
1379 | if (ret < 0) { | ||
1380 | CFGP2P_ERR(("fw set p2p_noa failed %d\n", ret)); | ||
1381 | } | ||
1382 | } | ||
1383 | else { | ||
1384 | CFGP2P_ERR(("ERROR: set_noa in non-p2p mode\n")); | ||
1385 | } | ||
1386 | return ret; | ||
1387 | } | ||
1388 | |||
1389 | s32 wl_cfgp2p_get_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int buf_len) | ||
1390 | { | ||
1391 | wifi_p2p_noa_desc_t *noa_desc; | ||
1392 | int len = 0, i; | ||
1393 | char _buf[200]; | ||
1394 | |||
1395 | CFGP2P_DBG((" Enter\n")); | ||
1396 | buf[0] = '\0'; | ||
1397 | if (wl->p2p && wl->p2p->vif_created) { | ||
1398 | if (wl->p2p->noa.desc[0].count || wl->p2p->ops.ops) { | ||
1399 | _buf[0] = 1; /* noa index */ | ||
1400 | _buf[1] = (wl->p2p->ops.ops ? 0x80: 0) | | ||
1401 | (wl->p2p->ops.ctw & 0x7f); /* ops + ctw */ | ||
1402 | len += 2; | ||
1403 | if (wl->p2p->noa.desc[0].count) { | ||
1404 | noa_desc = (wifi_p2p_noa_desc_t*)&_buf[len]; | ||
1405 | noa_desc->cnt_type = wl->p2p->noa.desc[0].count; | ||
1406 | noa_desc->duration = wl->p2p->noa.desc[0].duration; | ||
1407 | noa_desc->interval = wl->p2p->noa.desc[0].interval; | ||
1408 | noa_desc->start = wl->p2p->noa.desc[0].start; | ||
1409 | len += sizeof(wifi_p2p_noa_desc_t); | ||
1410 | } | ||
1411 | if (buf_len <= len * 2) { | ||
1412 | CFGP2P_ERR(("ERROR: buf_len %d in not enough for" | ||
1413 | "returning noa in string format\n", buf_len)); | ||
1414 | return -1; | ||
1415 | } | ||
1416 | /* We have to convert the buffer data into ASCII strings */ | ||
1417 | for (i = 0; i < len; i++) { | ||
1418 | sprintf(buf, "%02x", _buf[i]); | ||
1419 | buf += 2; | ||
1420 | } | ||
1421 | buf[i*2] = '\0'; | ||
1422 | } | ||
1423 | } | ||
1424 | else { | ||
1425 | CFGP2P_ERR(("ERROR: get_noa in non-p2p mode\n")); | ||
1426 | return -1; | ||
1427 | } | ||
1428 | return len * 2; | ||
1429 | } | ||
1430 | |||
1431 | s32 wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) | ||
1432 | { | ||
1433 | int ps, ctw; | ||
1434 | int ret = -1; | ||
1435 | s32 legacy_ps; | ||
1436 | |||
1437 | CFGP2P_DBG((" Enter\n")); | ||
1438 | if (wl->p2p && wl->p2p->vif_created) { | ||
1439 | sscanf(buf, "%d %d %d", &legacy_ps, &ps, &ctw); | ||
1440 | CFGP2P_DBG((" Enter legacy_ps %d ps %d ctw %d\n", legacy_ps, ps, ctw)); | ||
1441 | if (ctw != -1) { | ||
1442 | wl->p2p->ops.ctw = ctw; | ||
1443 | ret = 0; | ||
1444 | } | ||
1445 | if (ps != -1) { | ||
1446 | wl->p2p->ops.ops = ps; | ||
1447 | ret = wldev_iovar_setbuf(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), | ||
1448 | "p2p_ops", &wl->p2p->ops, sizeof(wl->p2p->ops), | ||
1449 | ioctlbuf, sizeof(ioctlbuf)); | ||
1450 | if (ret < 0) { | ||
1451 | CFGP2P_ERR(("fw set p2p_ops failed %d\n", ret)); | ||
1452 | } | ||
1453 | } | ||
1454 | |||
1455 | if (legacy_ps != -1) { | ||
1456 | s32 pm = legacy_ps ? PM_MAX : PM_OFF; | ||
1457 | ret = wldev_ioctl(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), | ||
1458 | WLC_SET_PM, &pm, sizeof(pm), true); | ||
1459 | if (unlikely(ret)) { | ||
1460 | CFGP2P_ERR(("error (%d)\n", ret)); | ||
1461 | } | ||
1462 | } | ||
1463 | } | ||
1464 | else { | ||
1465 | CFGP2P_ERR(("ERROR: set_p2p_ps in non-p2p mode\n")); | ||
1466 | ret = -1; | ||
1467 | } | ||
1468 | return ret; | ||
1469 | } | ||