blob: d2cefb4c7d2a8921dae0f2e780b077a0abd4d82c [file] [log] [blame]
Arend van Spriel9f440b72013-02-08 15:53:36 +01001/*
2 * Copyright (c) 2012 Broadcom Corporation
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 ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16#include <linux/slab.h>
17#include <linux/netdevice.h>
18#include <net/cfg80211.h>
19
20#include <brcmu_wifi.h>
21#include <brcmu_utils.h>
22#include <defs.h>
23#include <dhd.h>
24#include <dhd_dbg.h>
25#include "fwil.h"
Arend van Sprield3c0b632013-02-08 15:53:37 +010026#include "fwil_types.h"
Arend van Spriel9f440b72013-02-08 15:53:36 +010027#include "p2p.h"
28#include "wl_cfg80211.h"
29
30/* parameters used for p2p escan */
31#define P2PAPI_SCAN_NPROBES 1
32#define P2PAPI_SCAN_DWELL_TIME_MS 80
33#define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 40
34#define P2PAPI_SCAN_HOME_TIME_MS 60
35#define P2PAPI_SCAN_NPROBS_TIME_MS 30
36#define P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS 100
37#define WL_SCAN_CONNECT_DWELL_TIME_MS 200
38#define WL_SCAN_JOIN_PROBE_INTERVAL_MS 20
39
40#define BRCMF_P2P_WILDCARD_SSID "DIRECT-"
41#define BRCMF_P2P_WILDCARD_SSID_LEN (sizeof(BRCMF_P2P_WILDCARD_SSID) - 1)
42
43#define SOCIAL_CHAN_1 1
44#define SOCIAL_CHAN_2 6
45#define SOCIAL_CHAN_3 11
46#define SOCIAL_CHAN_CNT 3
47#define AF_PEER_SEARCH_CNT 2
48
Arend van Sprield3c0b632013-02-08 15:53:37 +010049#define BRCMF_SCB_TIMEOUT_VALUE 20
50
Arend van Spriel9f440b72013-02-08 15:53:36 +010051/**
52 * struct brcmf_p2p_disc_st_le - set discovery state in firmware.
53 *
54 * @state: requested discovery state (see enum brcmf_p2p_disc_state).
55 * @chspec: channel parameter for %WL_P2P_DISC_ST_LISTEN state.
56 * @dwell: dwell time in ms for %WL_P2P_DISC_ST_LISTEN state.
57 */
58struct brcmf_p2p_disc_st_le {
59 u8 state;
60 __le16 chspec;
61 __le16 dwell;
62};
63
64/**
65 * enum brcmf_p2p_disc_state - P2P discovery state values
66 *
67 * @WL_P2P_DISC_ST_SCAN: P2P discovery with wildcard SSID and P2P IE.
68 * @WL_P2P_DISC_ST_LISTEN: P2P discovery off-channel for specified time.
69 * @WL_P2P_DISC_ST_SEARCH: P2P discovery with P2P wildcard SSID and P2P IE.
70 */
71enum brcmf_p2p_disc_state {
72 WL_P2P_DISC_ST_SCAN,
73 WL_P2P_DISC_ST_LISTEN,
74 WL_P2P_DISC_ST_SEARCH
75};
76
77/**
78 * struct brcmf_p2p_scan_le - P2P specific scan request.
79 *
80 * @type: type of scan method requested (values: 'E' or 'S').
81 * @reserved: reserved (ignored).
82 * @eparams: parameters used for type 'E'.
83 * @sparams: parameters used for type 'S'.
84 */
85struct brcmf_p2p_scan_le {
86 u8 type;
87 u8 reserved[3];
88 union {
89 struct brcmf_escan_params_le eparams;
90 struct brcmf_scan_params_le sparams;
91 };
92};
93
Arend van Spriel9f440b72013-02-08 15:53:36 +010094/**
95 * brcmf_p2p_set_firmware() - prepare firmware for peer-to-peer operation.
96 *
97 * @p2p: P2P specific data.
98 */
99static int brcmf_p2p_set_firmware(struct brcmf_p2p_info *p2p)
100{
101 struct net_device *ndev = cfg_to_ndev(p2p->cfg);
102 u8 null_eth_addr[] = { 0, 0, 0, 0, 0, 0 };
103 s32 ret = 0;
104
105 brcmf_fil_iovar_int_set(netdev_priv(ndev), "apsta", 1);
106
107 /* In case of COB type, firmware has default mac address
108 * After Initializing firmware, we have to set current mac address to
109 * firmware for P2P device address
110 */
111 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "p2p_da_override",
112 null_eth_addr, sizeof(null_eth_addr));
113 if (ret)
114 brcmf_err("failed to update device address ret %d\n", ret);
115
116 return ret;
117}
118
119/**
120 * brcmf_p2p_generate_bss_mac() - derive mac addresses for P2P.
121 *
122 * @p2p: P2P specific data.
123 *
124 * P2P needs mac addresses for P2P device and interface. These are
125 * derived from the primary net device, ie. the permanent ethernet
126 * address of the device.
127 */
128static void brcmf_p2p_generate_bss_mac(struct brcmf_p2p_info *p2p)
129{
Arend van Spriel9f440b72013-02-08 15:53:36 +0100130 /* Generate the P2P Device Address. This consists of the device's
131 * primary MAC address with the locally administered bit set.
132 */
Arend van Sprielde66efc2013-02-08 15:53:40 +0100133 memcpy(p2p->dev_addr, p2p->cfg->pub->mac, ETH_ALEN);
Arend van Spriel9f440b72013-02-08 15:53:36 +0100134 p2p->dev_addr[0] |= 0x02;
135
136 /* Generate the P2P Interface Address. If the discovery and connection
137 * BSSCFGs need to simultaneously co-exist, then this address must be
138 * different from the P2P Device Address, but also locally administered.
139 */
140 memcpy(p2p->int_addr, p2p->dev_addr, ETH_ALEN);
141 p2p->int_addr[4] ^= 0x80;
142}
143
144/**
145 * brcmf_p2p_scan_is_p2p_request() - is cfg80211 scan request a P2P scan.
146 *
147 * @request: the scan request as received from cfg80211.
148 *
149 * returns true if one of the ssids in the request matches the
150 * P2P wildcard ssid; otherwise returns false.
151 */
152static bool brcmf_p2p_scan_is_p2p_request(struct cfg80211_scan_request *request)
153{
154 struct cfg80211_ssid *ssids = request->ssids;
155 int i;
156
157 for (i = 0; i < request->n_ssids; i++) {
158 if (ssids[i].ssid_len != BRCMF_P2P_WILDCARD_SSID_LEN)
159 continue;
160
161 brcmf_dbg(INFO, "comparing ssid \"%s\"", ssids[i].ssid);
162 if (!memcmp(BRCMF_P2P_WILDCARD_SSID, ssids[i].ssid,
163 BRCMF_P2P_WILDCARD_SSID_LEN))
164 return true;
165 }
166 return false;
167}
168
169/**
170 * brcmf_p2p_set_discover_state - set discover state in firmware.
171 *
172 * @ifp: low-level interface object.
173 * @state: discover state to set.
174 * @chanspec: channel parameters (for state @WL_P2P_DISC_ST_LISTEN only).
175 * @listen_ms: duration to listen (for state @WL_P2P_DISC_ST_LISTEN only).
176 */
177static s32 brcmf_p2p_set_discover_state(struct brcmf_if *ifp, u8 state,
178 u16 chanspec, u16 listen_ms)
179{
180 struct brcmf_p2p_disc_st_le discover_state;
181 s32 ret = 0;
182 brcmf_dbg(TRACE, "enter\n");
183
184 discover_state.state = state;
185 discover_state.chspec = cpu_to_le16(chanspec);
186 discover_state.dwell = cpu_to_le16(listen_ms);
187 ret = brcmf_fil_bsscfg_data_set(ifp, "p2p_state", &discover_state,
188 sizeof(discover_state));
189 return ret;
190}
191
192/**
Arend van Spriel9f440b72013-02-08 15:53:36 +0100193 * brcmf_p2p_init_discovery() - enable discovery in the firmware.
194 *
195 * @p2p: P2P specific data.
196 *
197 * Configures the firmware to allow P2P peer discovery. Creates the
198 * virtual interface and consequently the P2P device for it.
199 */
200static s32 brcmf_p2p_init_discovery(struct brcmf_p2p_info *p2p)
201{
202 struct net_device *ndev = cfg_to_ndev(p2p->cfg);
203 struct brcmf_cfg80211_vif *vif;
204 struct brcmf_if *ifp;
205 struct p2p_bss *bss_dev;
206 s32 index;
207 s32 ret;
208
209 brcmf_dbg(TRACE, "enter\n");
210
211 bss_dev = &p2p->bss_idx[P2PAPI_BSSCFG_DEVICE];
212 if (bss_dev->vif != NULL) {
213 brcmf_dbg(INFO, "do nothing, already initialized\n");
214 return 0;
215 }
216
217 /* Enable P2P Discovery in the firmware */
218 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "p2p_disc", 1);
219 if (ret < 0) {
220 brcmf_err("set discover error\n");
221 return ret;
222 }
223
224 /* obtain bsscfg index for P2P discovery */
225 ret = brcmf_fil_iovar_int_get(netdev_priv(ndev), "p2p_dev", &index);
226 if (ret < 0) {
227 brcmf_err("retrieving discover bsscfg index failed\n");
228 return ret;
229 }
230
231 /*
232 * need brcmf_if for setting the discovery state.
233 */
234 ifp = kzalloc(sizeof(*vif->ifp), GFP_KERNEL);
235 if (!ifp) {
236 brcmf_err("could not create discovery if\n");
237 return -ENOMEM;
238 }
239
240 /* set required fields */
241 ifp->drvr = p2p->cfg->pub;
242 ifp->ifidx = 0;
243 ifp->bssidx = index;
244
245 /* Set the initial discovery state to SCAN */
246 ret = brcmf_p2p_set_discover_state(ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
247
248 if (ret != 0) {
249 brcmf_err("unable to set WL_P2P_DISC_ST_SCAN\n");
250 (void)brcmf_fil_iovar_int_set(netdev_priv(ndev), "p2p_disc", 0);
251 kfree(ifp);
252 return ret;
253 }
254
255 /* create a vif for it */
Arend van Sprield3c0b632013-02-08 15:53:37 +0100256 vif = brcmf_alloc_vif(p2p->cfg, NL80211_IFTYPE_P2P_DEVICE, false);
Arend van Spriel9f440b72013-02-08 15:53:36 +0100257 if (IS_ERR(vif)) {
258 brcmf_err("could not create discovery vif\n");
259 kfree(ifp);
260 return PTR_ERR(vif);
261 }
262
263 vif->ifp = ifp;
264 ifp->vif = vif;
265 bss_dev->vif = vif;
266
267 return 0;
268}
269
270/**
271 * brcmf_p2p_deinit_discovery() - disable P2P device discovery.
272 *
273 * @p2p: P2P specific data.
274 *
275 * Resets the discovery state and disables it in firmware. The virtual
276 * interface and P2P device are freed.
277 */
278static s32 brcmf_p2p_deinit_discovery(struct brcmf_p2p_info *p2p)
279{
280 struct net_device *ndev = cfg_to_ndev(p2p->cfg);
281 struct brcmf_if *ifp;
282 struct p2p_bss *bss_dev;
283 brcmf_dbg(TRACE, "enter\n");
284
285 bss_dev = &p2p->bss_idx[P2PAPI_BSSCFG_DEVICE];
Arend van Spriel9f440b72013-02-08 15:53:36 +0100286 ifp = bss_dev->vif->ifp;
287
288 /* Set the discovery state to SCAN */
289 (void)brcmf_p2p_set_discover_state(ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
290
291 /* Disable P2P discovery in the firmware */
292 (void)brcmf_fil_iovar_int_set(netdev_priv(ndev), "p2p_disc", 0);
293
294 /* remove discovery interface */
295 brcmf_free_vif(bss_dev->vif);
296 bss_dev->vif = NULL;
297 kfree(ifp);
298
299 return 0;
300}
301
302/**
303 * brcmf_p2p_enable_discovery() - initialize and configure discovery.
304 *
305 * @p2p: P2P specific data.
306 * @ie: buffer containing information elements.
307 * @ie_len: length of @ie buffer.
308 *
309 * Initializes the discovery device and configure the virtual interface.
310 */
Hante Meuleman0de8aac2013-02-08 15:53:38 +0100311static int brcmf_p2p_enable_discovery(struct brcmf_p2p_info *p2p)
Arend van Spriel9f440b72013-02-08 15:53:36 +0100312{
313 struct brcmf_cfg80211_vif *vif;
314 s32 ret = 0;
315
316 brcmf_dbg(TRACE, "enter\n");
317 vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
318 if (vif) {
Hante Meuleman0de8aac2013-02-08 15:53:38 +0100319 brcmf_dbg(INFO, "DISCOVERY init already done\n");
320 goto exit;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100321 }
322
323 ret = brcmf_p2p_init_discovery(p2p);
324 if (ret < 0) {
325 brcmf_err("init discovery error %d\n", ret);
326 goto exit;
327 }
328
329 vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
330
331 /*
332 * Set wsec to any non-zero value in the discovery bsscfg
333 * to ensure our P2P probe responses have the privacy bit
334 * set in the 802.11 WPA IE. Some peer devices may not
335 * initiate WPS with us if this bit is not set.
336 */
337 ret = brcmf_fil_bsscfg_int_set(vif->ifp, "wsec", AES_ENABLED);
338 if (ret < 0)
339 brcmf_err("wsec error %d\n", ret);
340
Arend van Spriel9f440b72013-02-08 15:53:36 +0100341exit:
342 return ret;
343}
344
Hante Meuleman0de8aac2013-02-08 15:53:38 +0100345/**
Arend van Spriel9f440b72013-02-08 15:53:36 +0100346 * brcmf_p2p_escan() - initiate a P2P scan.
347 *
348 * @p2p: P2P specific data.
349 * @num_chans: number of channels to scan.
350 * @chanspecs: channel parameters for @num_chans channels.
351 * @search_state: P2P discover state to use.
352 * @action: scan action to pass to firmware.
353 * @bss_type: type of P2P bss.
354 */
355static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
356 u16 chanspecs[], s32 search_state, u16 action,
357 enum p2p_bss_type bss_type)
358{
359 s32 ret = 0;
360 s32 memsize = offsetof(struct brcmf_p2p_scan_le,
361 eparams.params_le.channel_list);
362 s32 nprobes;
363 s32 active;
364 u32 i;
365 u8 *memblk;
366 struct brcmf_cfg80211_vif *vif;
367 struct brcmf_p2p_scan_le *p2p_params;
368 struct brcmf_scan_params_le *sparams;
369 struct brcmf_ssid ssid;
370
Arend van Spriel9f440b72013-02-08 15:53:36 +0100371 memsize += num_chans * sizeof(__le16);
372 memblk = kzalloc(memsize, GFP_KERNEL);
373 if (!memblk)
374 return -ENOMEM;
375
376 vif = p2p->bss_idx[bss_type].vif;
377 if (vif == NULL) {
378 brcmf_err("no vif for bss type %d\n", bss_type);
379 ret = -EINVAL;
380 goto exit;
381 }
382
383 switch (search_state) {
384 case WL_P2P_DISC_ST_SEARCH:
385 /*
386 * If we in SEARCH STATE, we don't need to set SSID explictly
387 * because dongle use P2P WILDCARD internally by default
388 */
389 /* use null ssid */
390 ssid.SSID_len = 0;
391 memset(ssid.SSID, 0, sizeof(ssid.SSID));
392 break;
393 case WL_P2P_DISC_ST_SCAN:
394 /*
395 * wpa_supplicant has p2p_find command with type social or
396 * progressive. For progressive, we need to set the ssid to
397 * P2P WILDCARD because we just do broadcast scan unless
398 * setting SSID.
399 */
400 ssid.SSID_len = BRCMF_P2P_WILDCARD_SSID_LEN;
401 memcpy(ssid.SSID, BRCMF_P2P_WILDCARD_SSID, ssid.SSID_len);
402 break;
403 default:
404 brcmf_err(" invalid search state %d\n", search_state);
405 ret = -EINVAL;
406 goto exit;
407 }
408
409 brcmf_p2p_set_discover_state(vif->ifp, search_state, 0, 0);
410
411 /*
412 * set p2p scan parameters.
413 */
414 p2p_params = (struct brcmf_p2p_scan_le *)memblk;
415 p2p_params->type = 'E';
416
417 /* determine the scan engine parameters */
418 sparams = &p2p_params->eparams.params_le;
419 sparams->bss_type = DOT11_BSSTYPE_ANY;
420 if (p2p->cfg->active_scan)
421 sparams->scan_type = 0;
422 else
423 sparams->scan_type = 1;
424
425 memset(&sparams->bssid, 0xFF, ETH_ALEN);
426 if (ssid.SSID_len)
427 memcpy(sparams->ssid_le.SSID, ssid.SSID, ssid.SSID_len);
428 sparams->ssid_le.SSID_len = cpu_to_le32(ssid.SSID_len);
429 sparams->home_time = cpu_to_le32(P2PAPI_SCAN_HOME_TIME_MS);
430
431 /*
432 * SOCIAL_CHAN_CNT + 1 takes care of the Progressive scan
433 * supported by the supplicant.
434 */
435 if (num_chans == SOCIAL_CHAN_CNT || num_chans == (SOCIAL_CHAN_CNT + 1))
436 active = P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS;
437 else if (num_chans == AF_PEER_SEARCH_CNT)
438 active = P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS;
439 else if (wl_get_vif_state_all(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED))
440 active = -1;
441 else
442 active = P2PAPI_SCAN_DWELL_TIME_MS;
443
444 /* Override scan params to find a peer for a connection */
445 if (num_chans == 1) {
446 active = WL_SCAN_CONNECT_DWELL_TIME_MS;
447 /* XXX WAR to sync with presence period of VSDB GO.
448 * send probe request more frequently
449 */
450 nprobes = active / WL_SCAN_JOIN_PROBE_INTERVAL_MS;
451 } else {
452 nprobes = active / P2PAPI_SCAN_NPROBS_TIME_MS;
453 }
454
455 if (nprobes <= 0)
456 nprobes = 1;
457
458 brcmf_dbg(INFO, "nprobes # %d, active_time %d\n", nprobes, active);
459 sparams->active_time = cpu_to_le32(active);
460 sparams->nprobes = cpu_to_le32(nprobes);
461 sparams->passive_time = cpu_to_le32(-1);
462 sparams->channel_num = cpu_to_le32(num_chans &
463 BRCMF_SCAN_PARAMS_COUNT_MASK);
464 for (i = 0; i < num_chans; i++)
465 sparams->channel_list[i] = cpu_to_le16(chanspecs[i]);
466
467 /* set the escan specific parameters */
468 p2p_params->eparams.version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
469 p2p_params->eparams.action = cpu_to_le16(action);
470 p2p_params->eparams.sync_id = cpu_to_le16(0x1234);
471 /* perform p2p scan on primary device */
472 ret = brcmf_fil_bsscfg_data_set(vif->ifp, "p2p_scan", memblk, memsize);
473 if (!ret)
474 set_bit(BRCMF_SCAN_STATUS_BUSY, &p2p->cfg->scan_status);
475exit:
476 kfree(memblk);
477 return ret;
478}
479
480/**
481 * brcmf_p2p_run_escan() - escan callback for peer-to-peer.
482 *
483 * @cfg: driver private data for cfg80211 interface.
484 * @ndev: net device for which scan is requested.
485 * @request: scan request from cfg80211.
486 * @action: scan action.
487 *
488 * Determines the P2P discovery state based to scan request parameters and
489 * validates the channels in the request.
490 */
491static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg,
492 struct net_device *ndev,
493 struct cfg80211_scan_request *request,
494 u16 action)
495{
496 struct brcmf_p2p_info *p2p = &cfg->p2p;
497 s32 err = 0;
498 s32 search_state = WL_P2P_DISC_ST_SCAN;
499 struct brcmf_cfg80211_vif *vif;
500 struct net_device *dev = NULL;
501 int i, num_nodfs = 0;
502 u16 *chanspecs;
503
504 brcmf_dbg(TRACE, "enter\n");
505
506 if (!request) {
507 err = -EINVAL;
508 goto exit;
509 }
510
511 if (request->n_channels) {
512 chanspecs = kcalloc(request->n_channels, sizeof(*chanspecs),
513 GFP_KERNEL);
514 if (!chanspecs) {
515 err = -ENOMEM;
516 goto exit;
517 }
518 vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif;
519 if (vif)
520 dev = vif->wdev.netdev;
521 if (request->n_channels == 3 &&
522 request->channels[0]->hw_value == SOCIAL_CHAN_1 &&
523 request->channels[1]->hw_value == SOCIAL_CHAN_2 &&
524 request->channels[2]->hw_value == SOCIAL_CHAN_3) {
525 /* SOCIAL CHANNELS 1, 6, 11 */
526 search_state = WL_P2P_DISC_ST_SEARCH;
527 brcmf_dbg(INFO, "P2P SEARCH PHASE START\n");
528 } else if (dev != NULL && vif->mode == WL_MODE_AP) {
529 /* If you are already a GO, then do SEARCH only */
530 brcmf_dbg(INFO, "Already a GO. Do SEARCH Only\n");
531 search_state = WL_P2P_DISC_ST_SEARCH;
532 } else {
533 brcmf_dbg(INFO, "P2P SCAN STATE START\n");
534 }
535
536 /*
537 * no P2P scanning on passive or DFS channels.
538 */
539 for (i = 0; i < request->n_channels; i++) {
540 struct ieee80211_channel *chan = request->channels[i];
541
542 if (chan->flags & (IEEE80211_CHAN_RADAR |
543 IEEE80211_CHAN_PASSIVE_SCAN))
544 continue;
545
546 chanspecs[i] = channel_to_chanspec(chan);
547 brcmf_dbg(INFO, "%d: chan=%d, channel spec=%x\n",
548 num_nodfs, chan->hw_value, chanspecs[i]);
549 num_nodfs++;
550 }
551 err = brcmf_p2p_escan(p2p, num_nodfs, chanspecs, search_state,
552 action, P2PAPI_BSSCFG_DEVICE);
553 }
554exit:
555 if (err)
556 brcmf_err("error (%d)\n", err);
557 return err;
558}
559
560/**
561 * brcmf_p2p_scan_prep() - prepare scan based on request.
562 *
563 * @wiphy: wiphy device.
564 * @request: scan request from cfg80211.
Hante Meuleman0f8ffe12013-02-08 15:53:42 +0100565 * @vif: vif on which scan request is to be executed.
Arend van Spriel9f440b72013-02-08 15:53:36 +0100566 *
567 * Prepare the scan appropriately for type of scan requested. Overrides the
568 * escan .run() callback for peer-to-peer scanning.
569 */
570int brcmf_p2p_scan_prep(struct wiphy *wiphy,
Hante Meuleman0f8ffe12013-02-08 15:53:42 +0100571 struct cfg80211_scan_request *request,
572 struct brcmf_cfg80211_vif *vif)
Arend van Spriel9f440b72013-02-08 15:53:36 +0100573{
574 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
575 struct brcmf_p2p_info *p2p = &cfg->p2p;
576 int err = 0;
577
578 if (brcmf_p2p_scan_is_p2p_request(request)) {
579 /* find my listen channel */
580 err = cfg80211_get_p2p_attr(request->ie, request->ie_len,
581 IEEE80211_P2P_ATTR_LISTEN_CHANNEL,
582 &p2p->listen_channel, 1);
583 if (err < 0)
584 return err;
585
586 clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status);
587 brcmf_dbg(INFO, "P2P: GO_NEG_PHASE status cleared\n");
588
Hante Meuleman0de8aac2013-02-08 15:53:38 +0100589 err = brcmf_p2p_enable_discovery(p2p);
Hante Meuleman0f8ffe12013-02-08 15:53:42 +0100590 if (err)
591 return err;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100592
Hante Meuleman0f8ffe12013-02-08 15:53:42 +0100593 vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
594
595 /* override .run_escan() callback. */
Arend van Spriel9f440b72013-02-08 15:53:36 +0100596 cfg->escan_info.run = brcmf_p2p_run_escan;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100597 }
Hante Meuleman0f8ffe12013-02-08 15:53:42 +0100598 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG,
599 request->ie, request->ie_len);
Arend van Spriel9f440b72013-02-08 15:53:36 +0100600 return err;
601}
602
Hante Meuleman0de8aac2013-02-08 15:53:38 +0100603
604/**
605 * brcmf_p2p_remain_on_channel() - put device on channel and stay there.
606 *
607 * @wiphy: wiphy device.
608 * @channel: channel to stay on.
609 * @duration: time in ms to remain on channel.
610 *
611 */
612int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
613 struct ieee80211_channel *channel,
614 unsigned int duration, u64 *cookie)
615{
616 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
617 struct brcmf_p2p_info *p2p = &cfg->p2p;
618 struct brcmf_cfg80211_vif *vif;
619 s32 err;
620 u16 chanspec;
621
622 brcmf_dbg(TRACE, "Enter, channel: %d, duration ms (%d)\n",
623 ieee80211_frequency_to_channel(channel->center_freq),
624 duration);
625
626 *cookie = 0;
627 err = brcmf_p2p_enable_discovery(p2p);
628 if (err)
629 goto exit;
630
631 chanspec = channel_to_chanspec(channel);
632 vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
633 err = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_LISTEN,
634 chanspec, (u16)duration);
635 if (err)
636 goto exit;
637
638 memcpy(&p2p->remain_on_channel, channel,
639 sizeof(p2p->remain_on_channel));
640
641 set_bit(BRCMF_P2P_STATUS_REMAIN_ON_CHANNEL, &p2p->status);
642
643exit:
644 cfg80211_ready_on_channel(wdev, *cookie, channel, duration, GFP_KERNEL);
645 return err;
646}
647
648
649/**
650 * brcmf_p2p_notify_listen_complete() - p2p listen has completed.
651 *
652 * @ifp: interfac control.
653 * @e: event message. Not used, to make it usable for fweh event dispatcher.
654 * @data: payload of message. Not used.
655 *
656 */
657int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp,
658 const struct brcmf_event_msg *e,
659 void *data)
660{
661 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
662 struct brcmf_p2p_info *p2p = &cfg->p2p;
663
664 brcmf_dbg(TRACE, "Enter\n");
665 if (test_and_clear_bit(BRCMF_P2P_STATUS_REMAIN_ON_CHANNEL,
666 &p2p->status))
667 cfg80211_remain_on_channel_expired(&ifp->vif->wdev, 0,
668 &p2p->remain_on_channel,
669 GFP_KERNEL);
670 return 0;
671}
672
673
674/**
675 * brcmf_p2p_cancel_remain_on_channel() - cancel p2p listen state.
676 *
677 * @ifp: interfac control.
678 *
679 */
680void brcmf_p2p_cancel_remain_on_channel(struct brcmf_if *ifp)
681{
682 if (!ifp)
683 return;
684 brcmf_p2p_set_discover_state(ifp, WL_P2P_DISC_ST_SCAN, 0, 0);
685 brcmf_p2p_notify_listen_complete(ifp, NULL, NULL);
686}
687
688
Arend van Spriel9f440b72013-02-08 15:53:36 +0100689/**
690 * brcmf_p2p_attach() - attach for P2P.
691 *
692 * @cfg: driver private data for cfg80211 interface.
693 */
694void brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg)
695{
696 struct brcmf_p2p_info *p2p;
697
698 p2p = &cfg->p2p;
699
700 p2p->cfg = cfg;
701 brcmf_p2p_set_firmware(p2p);
702 brcmf_p2p_generate_bss_mac(p2p);
703}
704
705/**
706 * brcmf_p2p_detach() - detach P2P.
707 *
708 * @p2p: P2P specific data.
709 */
710void brcmf_p2p_detach(struct brcmf_p2p_info *p2p)
711{
Hante Meuleman0de8aac2013-02-08 15:53:38 +0100712 if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif != NULL) {
713 brcmf_p2p_cancel_remain_on_channel(
714 p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->ifp);
715 brcmf_p2p_deinit_discovery(p2p);
716 }
Arend van Spriel9f440b72013-02-08 15:53:36 +0100717 /* just set it all to zero */
718 memset(p2p, 0, sizeof(*p2p));
719}
720
Arend van Sprield3c0b632013-02-08 15:53:37 +0100721static int brcmf_p2p_request_p2p_if(struct brcmf_if *ifp, u8 ea[ETH_ALEN],
722 enum brcmf_fil_p2p_if_types iftype)
723{
724 struct brcmf_fil_p2p_if_le if_request;
725 struct brcmf_fil_chan_info_le ci;
726 u16 chanspec = 11 & WL_CHANSPEC_CHAN_MASK;
727 int err;
728
729 /* we need a default channel */
730 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_CHANNEL, &ci, sizeof(ci));
731 if (!err) {
732 chanspec = le32_to_cpu(ci.hw_channel) & WL_CHANSPEC_CHAN_MASK;
733 if (chanspec < CH_MAX_2G_CHANNEL)
734 chanspec |= WL_CHANSPEC_BAND_2G;
735 else
736 chanspec |= WL_CHANSPEC_BAND_5G;
737 }
738 chanspec |= WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE;
739
740 /* fill the firmware request */
741 memcpy(if_request.addr, ea, ETH_ALEN);
Hante Meuleman7ee2d922013-02-08 15:53:43 +0100742 if_request.type = cpu_to_le16((u16)iftype);
Arend van Sprield3c0b632013-02-08 15:53:37 +0100743 if_request.chspec = cpu_to_le16(chanspec);
744
745 err = brcmf_fil_iovar_data_set(ifp, "p2p_ifadd", &if_request,
746 sizeof(if_request));
747 if (err)
748 return err;
749
Arend van Sprield3c0b632013-02-08 15:53:37 +0100750 return err;
751}
752
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100753static int brcmf_p2p_disable_p2p_if(struct brcmf_cfg80211_vif *vif)
754{
755 struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev);
756 struct net_device *pri_ndev = cfg_to_ndev(cfg);
757 struct brcmf_if *ifp = netdev_priv(pri_ndev);
758 u8 *addr = vif->wdev.netdev->dev_addr;
759
760 return brcmf_fil_iovar_data_set(ifp, "p2p_ifdis", addr, ETH_ALEN);
761}
762
763static int brcmf_p2p_release_p2p_if(struct brcmf_cfg80211_vif *vif)
764{
765 struct brcmf_cfg80211_info *cfg = wdev_to_cfg(&vif->wdev);
766 struct net_device *pri_ndev = cfg_to_ndev(cfg);
767 struct brcmf_if *ifp = netdev_priv(pri_ndev);
768 u8 *addr = vif->wdev.netdev->dev_addr;
769
770 return brcmf_fil_iovar_data_set(ifp, "p2p_ifdel", addr, ETH_ALEN);
771}
772
Arend van Spriel9f440b72013-02-08 15:53:36 +0100773/**
774 * brcmf_p2p_add_vif() - create a new P2P virtual interface.
775 *
776 * @wiphy: wiphy device of new interface.
777 * @name: name of the new interface.
778 * @type: nl80211 interface type.
779 * @flags: TBD
780 * @params: TBD
Arend van Spriel9f440b72013-02-08 15:53:36 +0100781 */
782struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
783 enum nl80211_iftype type, u32 *flags,
784 struct vif_params *params)
785{
Arend van Sprield3c0b632013-02-08 15:53:37 +0100786 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
787 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
788 struct brcmf_cfg80211_vif *vif;
789 enum brcmf_fil_p2p_if_types iftype;
790 enum wl_mode mode;
791 int err;
792
793 if (brcmf_cfg80211_vif_event_armed(cfg))
794 return ERR_PTR(-EBUSY);
795
Arend van Spriel9f440b72013-02-08 15:53:36 +0100796 brcmf_dbg(INFO, "adding vif \"%s\" (type=%d)\n", name, type);
Arend van Sprield3c0b632013-02-08 15:53:37 +0100797
798 switch (type) {
799 case NL80211_IFTYPE_P2P_CLIENT:
800 iftype = BRCMF_FIL_P2P_IF_CLIENT;
801 mode = WL_MODE_BSS;
802 break;
803 case NL80211_IFTYPE_P2P_GO:
804 iftype = BRCMF_FIL_P2P_IF_GO;
805 mode = WL_MODE_AP;
806 break;
807 default:
808 return ERR_PTR(-EOPNOTSUPP);
809 }
810
811 vif = brcmf_alloc_vif(cfg, type, false);
Hante Meuleman7ee2d922013-02-08 15:53:43 +0100812 if (IS_ERR(vif))
813 return (struct wireless_dev *)vif;
Arend van Sprield3c0b632013-02-08 15:53:37 +0100814 brcmf_cfg80211_arm_vif_event(cfg, vif);
815
816 err = brcmf_p2p_request_p2p_if(ifp, cfg->p2p.int_addr, iftype);
Hante Meuleman7ee2d922013-02-08 15:53:43 +0100817 if (err) {
818 brcmf_cfg80211_arm_vif_event(cfg, NULL);
Arend van Sprield3c0b632013-02-08 15:53:37 +0100819 goto fail;
Hante Meuleman7ee2d922013-02-08 15:53:43 +0100820 }
Arend van Sprield3c0b632013-02-08 15:53:37 +0100821
822 /* wait for firmware event */
823 err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
824 msecs_to_jiffies(1500));
825 brcmf_cfg80211_arm_vif_event(cfg, NULL);
826 if (!err) {
827 brcmf_err("timeout occurred\n");
828 err = -EIO;
829 goto fail;
830 }
831
832 /* interface created in firmware */
833 ifp = vif->ifp;
834 if (!ifp) {
835 brcmf_err("no if pointer provided\n");
836 err = -ENOENT;
Hante Meuleman7ee2d922013-02-08 15:53:43 +0100837 goto fail;
Arend van Sprield3c0b632013-02-08 15:53:37 +0100838 }
839
840 strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
841 brcmf_cfg80211_vif_complete(cfg);
Hante Meuleman7ee2d922013-02-08 15:53:43 +0100842 cfg->p2p.bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = vif;
843 /* Disable firmware roaming for P2P interface */
844 brcmf_fil_iovar_int_set(ifp, "roam_off", 1);
845 if (iftype == BRCMF_FIL_P2P_IF_GO) {
846 /* set station timeout for p2p */
847 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCB_TIMEOUT,
848 BRCMF_SCB_TIMEOUT_VALUE);
849 }
Arend van Sprield3c0b632013-02-08 15:53:37 +0100850 return &ifp->vif->wdev;
851
852fail:
853 brcmf_free_vif(vif);
854 return ERR_PTR(err);
Arend van Spriel9f440b72013-02-08 15:53:36 +0100855}
856
857/**
858 * brcmf_p2p_del_vif() - delete a P2P virtual interface.
859 *
860 * @wiphy: wiphy device of interface.
861 * @wdev: wireless device of interface.
862 *
863 * TODO: not yet supported.
864 */
865int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
866{
Arend van Sprield3c0b632013-02-08 15:53:37 +0100867 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriel9f440b72013-02-08 15:53:36 +0100868 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100869 unsigned long jiffie_timeout = msecs_to_jiffies(1500);
870 bool wait_for_disable = false;
Arend van Sprield3c0b632013-02-08 15:53:37 +0100871 int err;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100872
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100873 brcmf_dbg(TRACE, "delete P2P vif\n");
Arend van Spriel9f440b72013-02-08 15:53:36 +0100874 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
Arend van Sprield3c0b632013-02-08 15:53:37 +0100875
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100876 switch (vif->wdev.iftype) {
877 case NL80211_IFTYPE_P2P_CLIENT:
878 if (test_bit(BRCMF_VIF_STATUS_DISCONNECTING, &vif->sme_state))
879 wait_for_disable = true;
880 break;
881
882 case NL80211_IFTYPE_P2P_GO:
883 if (!brcmf_p2p_disable_p2p_if(vif))
884 wait_for_disable = true;
885 break;
886
887 case NL80211_IFTYPE_P2P_DEVICE:
888 default:
889 return -ENOTSUPP;
890 break;
891 }
892
893 if (wait_for_disable)
Hante Meuleman7ee2d922013-02-08 15:53:43 +0100894 wait_for_completion_timeout(&cfg->vif_disabled,
895 msecs_to_jiffies(500));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100896
897 brcmf_vif_clear_mgmt_ies(vif);
Arend van Sprield3c0b632013-02-08 15:53:37 +0100898
899 brcmf_cfg80211_arm_vif_event(cfg, vif);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100900 err = brcmf_p2p_release_p2p_if(vif);
Hante Meuleman7ee2d922013-02-08 15:53:43 +0100901 if (!err) {
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100902 /* wait for firmware event */
903 err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL,
904 jiffie_timeout);
Hante Meuleman7ee2d922013-02-08 15:53:43 +0100905 if (!err)
906 err = -EIO;
907 else
908 err = 0;
909 }
Arend van Sprield3c0b632013-02-08 15:53:37 +0100910 brcmf_cfg80211_arm_vif_event(cfg, NULL);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100911 brcmf_free_vif(vif);
Hante Meuleman7ee2d922013-02-08 15:53:43 +0100912 cfg->p2p.bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL;
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100913
914 return err;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100915}