]> git.karo-electronics.de Git - mv-sheeva.git/blob - drivers/staging/brcm80211/brcmfmac/dhd_common.c
3dbf72eebd4ab3e2992deb4ec3c9db5fef372a4d
[mv-sheeva.git] / drivers / staging / brcm80211 / brcmfmac / dhd_common.c
1 /*
2  * Copyright (c) 2010 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/kernel.h>
17 #include <linux/string.h>
18 #include <bcmdefs.h>
19 #include <linux/netdevice.h>
20 #include <osl.h>
21 #include <bcmutils.h>
22 #include <bcmendian.h>
23 #include <dngl_stats.h>
24 #include <dhd.h>
25 #include <dhd_bus.h>
26 #include <dhd_proto.h>
27 #include <dhd_dbg.h>
28 #include <msgtrace.h>
29 #include <wlioctl.h>
30
31 int dhd_msg_level;
32 char fw_path[MOD_PARAM_PATHLEN];
33 char nv_path[MOD_PARAM_PATHLEN];
34
35 /* Last connection success/failure status */
36 u32 dhd_conn_event;
37 u32 dhd_conn_status;
38 u32 dhd_conn_reason;
39
40 #define htod32(i) i
41 #define htod16(i) i
42 #define dtoh32(i) i
43 #define dtoh16(i) i
44
45 extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf,
46                             uint len);
47 extern void dhd_ind_scan_confirm(void *h, bool status);
48 extern int dhd_wl_ioctl(dhd_pub_t *dhd, uint cmd, char *buf, uint buflen);
49 void dhd_iscan_lock(void);
50 void dhd_iscan_unlock(void);
51
52 /* Packet alignment for most efficient SDIO (can change based on platform) */
53 #ifndef DHD_SDALIGN
54 #define DHD_SDALIGN     32
55 #endif
56 #if !ISPOWEROF2(DHD_SDALIGN)
57 #error DHD_SDALIGN is not a power of 2!
58 #endif
59
60 #ifdef DHD_DEBUG
61 #define EPI_VERSION_STR         "4.218.248.5"
62 const char dhd_version[] =
63 "Dongle Host Driver, version " EPI_VERSION_STR "\nCompiled on " __DATE__
64 " at " __TIME__;
65 #else
66 const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR;
67 #endif
68
69 void dhd_set_timer(void *bus, uint wdtick);
70
71 /* IOVar table */
72 enum {
73         IOV_VERSION = 1,
74         IOV_MSGLEVEL,
75         IOV_BCMERRORSTR,
76         IOV_BCMERROR,
77         IOV_WDTICK,
78         IOV_DUMP,
79 #ifdef DHD_DEBUG
80         IOV_CONS,
81         IOV_DCONSOLE_POLL,
82 #endif
83         IOV_CLEARCOUNTS,
84         IOV_LOGDUMP,
85         IOV_LOGCAL,
86         IOV_LOGSTAMP,
87         IOV_GPIOOB,
88         IOV_IOCTLTIMEOUT,
89         IOV_LAST
90 };
91
92 const bcm_iovar_t dhd_iovars[] = {
93         {"version", IOV_VERSION, 0, IOVT_BUFFER, sizeof(dhd_version)}
94         ,
95 #ifdef DHD_DEBUG
96         {"msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0}
97         ,
98 #endif                          /* DHD_DEBUG */
99         {"bcmerrorstr", IOV_BCMERRORSTR, 0, IOVT_BUFFER, BCME_STRLEN}
100         ,
101         {"bcmerror", IOV_BCMERROR, 0, IOVT_INT8, 0}
102         ,
103         {"wdtick", IOV_WDTICK, 0, IOVT_UINT32, 0}
104         ,
105         {"dump", IOV_DUMP, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN}
106         ,
107 #ifdef DHD_DEBUG
108         {"dconpoll", IOV_DCONSOLE_POLL, 0, IOVT_UINT32, 0}
109         ,
110         {"cons", IOV_CONS, 0, IOVT_BUFFER, 0}
111         ,
112 #endif
113         {"clearcounts", IOV_CLEARCOUNTS, 0, IOVT_VOID, 0}
114         ,
115         {"gpioob", IOV_GPIOOB, 0, IOVT_UINT32, 0}
116         ,
117         {"ioctl_timeout", IOV_IOCTLTIMEOUT, 0, IOVT_UINT32, 0}
118         ,
119         {NULL, 0, 0, 0, 0}
120 };
121
122 void dhd_common_init(void)
123 {
124         /* Init global variables at run-time, not as part of the declaration.
125          * This is required to support init/de-init of the driver.
126          * Initialization
127          * of globals as part of the declaration results in non-deterministic
128          * behaviour since the value of the globals may be different on the
129          * first time that the driver is initialized vs subsequent
130          * initializations.
131          */
132         dhd_msg_level = DHD_ERROR_VAL;
133 #ifdef CONFIG_BCM4329_FW_PATH
134         strncpy(fw_path, CONFIG_BCM4329_FW_PATH, MOD_PARAM_PATHLEN - 1);
135 #else
136         fw_path[0] = '\0';
137 #endif
138 #ifdef CONFIG_BCM4329_NVRAM_PATH
139         strncpy(nv_path, CONFIG_BCM4329_NVRAM_PATH, MOD_PARAM_PATHLEN - 1);
140 #else
141         nv_path[0] = '\0';
142 #endif
143 }
144
145 static int dhd_dump(dhd_pub_t *dhdp, char *buf, int buflen)
146 {
147         struct bcmstrbuf b;
148         struct bcmstrbuf *strbuf = &b;
149
150         bcm_binit(strbuf, buf, buflen);
151
152         /* Base DHD info */
153         bcm_bprintf(strbuf, "%s\n", dhd_version);
154         bcm_bprintf(strbuf, "\n");
155         bcm_bprintf(strbuf, "pub.up %d pub.txoff %d pub.busstate %d\n",
156                     dhdp->up, dhdp->txoff, dhdp->busstate);
157         bcm_bprintf(strbuf, "pub.hdrlen %d pub.maxctl %d pub.rxsz %d\n",
158                     dhdp->hdrlen, dhdp->maxctl, dhdp->rxsz);
159         bcm_bprintf(strbuf, "pub.iswl %d pub.drv_version %ld pub.mac %pM\n",
160                     dhdp->iswl, dhdp->drv_version, &dhdp->mac);
161         bcm_bprintf(strbuf, "pub.bcmerror %d tickcnt %d\n", dhdp->bcmerror,
162                     dhdp->tickcnt);
163
164         bcm_bprintf(strbuf, "dongle stats:\n");
165         bcm_bprintf(strbuf,
166                     "tx_packets %ld tx_bytes %ld tx_errors %ld tx_dropped %ld\n",
167                     dhdp->dstats.tx_packets, dhdp->dstats.tx_bytes,
168                     dhdp->dstats.tx_errors, dhdp->dstats.tx_dropped);
169         bcm_bprintf(strbuf,
170                     "rx_packets %ld rx_bytes %ld rx_errors %ld rx_dropped %ld\n",
171                     dhdp->dstats.rx_packets, dhdp->dstats.rx_bytes,
172                     dhdp->dstats.rx_errors, dhdp->dstats.rx_dropped);
173         bcm_bprintf(strbuf, "multicast %ld\n", dhdp->dstats.multicast);
174
175         bcm_bprintf(strbuf, "bus stats:\n");
176         bcm_bprintf(strbuf, "tx_packets %ld tx_multicast %ld tx_errors %ld\n",
177                     dhdp->tx_packets, dhdp->tx_multicast, dhdp->tx_errors);
178         bcm_bprintf(strbuf, "tx_ctlpkts %ld tx_ctlerrs %ld\n",
179                     dhdp->tx_ctlpkts, dhdp->tx_ctlerrs);
180         bcm_bprintf(strbuf, "rx_packets %ld rx_multicast %ld rx_errors %ld\n",
181                     dhdp->rx_packets, dhdp->rx_multicast, dhdp->rx_errors);
182         bcm_bprintf(strbuf,
183                     "rx_ctlpkts %ld rx_ctlerrs %ld rx_dropped %ld rx_flushed %ld\n",
184                     dhdp->rx_ctlpkts, dhdp->rx_ctlerrs, dhdp->rx_dropped,
185                     dhdp->rx_flushed);
186         bcm_bprintf(strbuf,
187                     "rx_readahead_cnt %ld tx_realloc %ld fc_packets %ld\n",
188                     dhdp->rx_readahead_cnt, dhdp->tx_realloc, dhdp->fc_packets);
189         bcm_bprintf(strbuf, "wd_dpc_sched %ld\n", dhdp->wd_dpc_sched);
190         bcm_bprintf(strbuf, "\n");
191
192         /* Add any prot info */
193         dhd_prot_dump(dhdp, strbuf);
194         bcm_bprintf(strbuf, "\n");
195
196         /* Add any bus info */
197         dhd_bus_dump(dhdp, strbuf);
198
199         return !strbuf->size ? BCME_BUFTOOSHORT : 0;
200 }
201
202 static int
203 dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, u32 actionid,
204             const char *name, void *params, int plen, void *arg, int len,
205             int val_size)
206 {
207         int bcmerror = 0;
208         s32 int_val = 0;
209
210         DHD_TRACE(("%s: Enter\n", __func__));
211
212         bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid));
213         if (bcmerror != 0)
214                 goto exit;
215
216         if (plen >= (int)sizeof(int_val))
217                 bcopy(params, &int_val, sizeof(int_val));
218
219         switch (actionid) {
220         case IOV_GVAL(IOV_VERSION):
221                 /* Need to have checked buffer length */
222                 strncpy((char *)arg, dhd_version, len);
223                 break;
224
225         case IOV_GVAL(IOV_MSGLEVEL):
226                 int_val = (s32) dhd_msg_level;
227                 bcopy(&int_val, arg, val_size);
228                 break;
229
230         case IOV_SVAL(IOV_MSGLEVEL):
231                 dhd_msg_level = int_val;
232                 break;
233
234         case IOV_GVAL(IOV_BCMERRORSTR):
235                 strncpy((char *)arg, bcmerrorstr(dhd_pub->bcmerror),
236                         BCME_STRLEN);
237                 ((char *)arg)[BCME_STRLEN - 1] = 0x00;
238                 break;
239
240         case IOV_GVAL(IOV_BCMERROR):
241                 int_val = (s32) dhd_pub->bcmerror;
242                 bcopy(&int_val, arg, val_size);
243                 break;
244
245         case IOV_GVAL(IOV_WDTICK):
246                 int_val = (s32) dhd_watchdog_ms;
247                 bcopy(&int_val, arg, val_size);
248                 break;
249
250         case IOV_SVAL(IOV_WDTICK):
251                 if (!dhd_pub->up) {
252                         bcmerror = BCME_NOTUP;
253                         break;
254                 }
255                 dhd_os_wd_timer(dhd_pub, (uint) int_val);
256                 break;
257
258         case IOV_GVAL(IOV_DUMP):
259                 bcmerror = dhd_dump(dhd_pub, arg, len);
260                 break;
261
262 #ifdef DHD_DEBUG
263         case IOV_GVAL(IOV_DCONSOLE_POLL):
264                 int_val = (s32) dhd_console_ms;
265                 bcopy(&int_val, arg, val_size);
266                 break;
267
268         case IOV_SVAL(IOV_DCONSOLE_POLL):
269                 dhd_console_ms = (uint) int_val;
270                 break;
271
272         case IOV_SVAL(IOV_CONS):
273                 if (len > 0)
274                         bcmerror = dhd_bus_console_in(dhd_pub, arg, len - 1);
275                 break;
276 #endif
277
278         case IOV_SVAL(IOV_CLEARCOUNTS):
279                 dhd_pub->tx_packets = dhd_pub->rx_packets = 0;
280                 dhd_pub->tx_errors = dhd_pub->rx_errors = 0;
281                 dhd_pub->tx_ctlpkts = dhd_pub->rx_ctlpkts = 0;
282                 dhd_pub->tx_ctlerrs = dhd_pub->rx_ctlerrs = 0;
283                 dhd_pub->rx_dropped = 0;
284                 dhd_pub->rx_readahead_cnt = 0;
285                 dhd_pub->tx_realloc = 0;
286                 dhd_pub->wd_dpc_sched = 0;
287                 memset(&dhd_pub->dstats, 0, sizeof(dhd_pub->dstats));
288                 dhd_bus_clearcounts(dhd_pub);
289                 break;
290
291         case IOV_GVAL(IOV_IOCTLTIMEOUT):{
292                         int_val = (s32) dhd_os_get_ioctl_resp_timeout();
293                         bcopy(&int_val, arg, sizeof(int_val));
294                         break;
295                 }
296
297         case IOV_SVAL(IOV_IOCTLTIMEOUT):{
298                         if (int_val <= 0)
299                                 bcmerror = BCME_BADARG;
300                         else
301                                 dhd_os_set_ioctl_resp_timeout((unsigned int)
302                                                               int_val);
303                         break;
304                 }
305
306         default:
307                 bcmerror = BCME_UNSUPPORTED;
308                 break;
309         }
310
311 exit:
312         return bcmerror;
313 }
314
315 /* Store the status of a connection attempt for later retrieval by an iovar */
316 void dhd_store_conn_status(u32 event, u32 status, u32 reason)
317 {
318         /* Do not overwrite a WLC_E_PRUNE with a WLC_E_SET_SSID
319          * because an encryption/rsn mismatch results in both events, and
320          * the important information is in the WLC_E_PRUNE.
321          */
322         if (!(event == WLC_E_SET_SSID && status == WLC_E_STATUS_FAIL &&
323               dhd_conn_event == WLC_E_PRUNE)) {
324                 dhd_conn_event = event;
325                 dhd_conn_status = status;
326                 dhd_conn_reason = reason;
327         }
328 }
329
330 bool dhd_prec_enq(dhd_pub_t *dhdp, struct pktq *q, struct sk_buff *pkt,
331                   int prec)
332 {
333         struct sk_buff *p;
334         int eprec = -1;         /* precedence to evict from */
335         bool discard_oldest;
336
337         /* Fast case, precedence queue is not full and we are also not
338          * exceeding total queue length
339          */
340         if (!pktq_pfull(q, prec) && !pktq_full(q)) {
341                 pktq_penq(q, prec, pkt);
342                 return true;
343         }
344
345         /* Determine precedence from which to evict packet, if any */
346         if (pktq_pfull(q, prec))
347                 eprec = prec;
348         else if (pktq_full(q)) {
349                 p = pktq_peek_tail(q, &eprec);
350                 ASSERT(p);
351                 if (eprec > prec)
352                         return false;
353         }
354
355         /* Evict if needed */
356         if (eprec >= 0) {
357                 /* Detect queueing to unconfigured precedence */
358                 ASSERT(!pktq_pempty(q, eprec));
359                 discard_oldest = AC_BITMAP_TST(dhdp->wme_dp, eprec);
360                 if (eprec == prec && !discard_oldest)
361                         return false;   /* refuse newer (incoming) packet */
362                 /* Evict packet according to discard policy */
363                 p = discard_oldest ? pktq_pdeq(q, eprec) : pktq_pdeq_tail(q,
364                                                   eprec);
365                 if (p == NULL) {
366                         DHD_ERROR(("%s: pktq_penq() failed, oldest %d.",
367                                    __func__, discard_oldest));
368                         ASSERT(p);
369                 }
370
371                 pkt_buf_free_skb(dhdp->osh, p, true);
372         }
373
374         /* Enqueue */
375         p = pktq_penq(q, prec, pkt);
376         if (p == NULL) {
377                 DHD_ERROR(("%s: pktq_penq() failed.", __func__));
378                 ASSERT(p);
379         }
380
381         return true;
382 }
383
384 static int
385 dhd_iovar_op(dhd_pub_t *dhd_pub, const char *name,
386              void *params, int plen, void *arg, int len, bool set)
387 {
388         int bcmerror = 0;
389         int val_size;
390         const bcm_iovar_t *vi = NULL;
391         u32 actionid;
392
393         DHD_TRACE(("%s: Enter\n", __func__));
394
395         ASSERT(name);
396         ASSERT(len >= 0);
397
398         /* Get MUST have return space */
399         ASSERT(set || (arg && len));
400
401         /* Set does NOT take qualifiers */
402         ASSERT(!set || (!params && !plen));
403
404         vi = bcm_iovar_lookup(dhd_iovars, name);
405         if (vi == NULL) {
406                 bcmerror = BCME_UNSUPPORTED;
407                 goto exit;
408         }
409
410         DHD_CTL(("%s: %s %s, len %d plen %d\n", __func__,
411                  name, (set ? "set" : "get"), len, plen));
412
413         /* set up 'params' pointer in case this is a set command so that
414          * the convenience int and bool code can be common to set and get
415          */
416         if (params == NULL) {
417                 params = arg;
418                 plen = len;
419         }
420
421         if (vi->type == IOVT_VOID)
422                 val_size = 0;
423         else if (vi->type == IOVT_BUFFER)
424                 val_size = len;
425         else
426                 /* all other types are integer sized */
427                 val_size = sizeof(int);
428
429         actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
430         bcmerror =
431             dhd_doiovar(dhd_pub, vi, actionid, name, params, plen, arg, len,
432                         val_size);
433
434 exit:
435         return bcmerror;
436 }
437
438 int dhd_ioctl(dhd_pub_t *dhd_pub, dhd_ioctl_t *ioc, void *buf, uint buflen)
439 {
440         int bcmerror = 0;
441
442         DHD_TRACE(("%s: Enter\n", __func__));
443
444         if (!buf)
445                 return BCME_BADARG;
446
447         switch (ioc->cmd) {
448         case DHD_GET_MAGIC:
449                 if (buflen < sizeof(int))
450                         bcmerror = BCME_BUFTOOSHORT;
451                 else
452                         *(int *)buf = DHD_IOCTL_MAGIC;
453                 break;
454
455         case DHD_GET_VERSION:
456                 if (buflen < sizeof(int))
457                         bcmerror = -BCME_BUFTOOSHORT;
458                 else
459                         *(int *)buf = DHD_IOCTL_VERSION;
460                 break;
461
462         case DHD_GET_VAR:
463         case DHD_SET_VAR:{
464                         char *arg;
465                         uint arglen;
466
467                         /* scan past the name to any arguments */
468                         for (arg = buf, arglen = buflen; *arg && arglen;
469                              arg++, arglen--)
470                                 ;
471
472                         if (*arg) {
473                                 bcmerror = BCME_BUFTOOSHORT;
474                                 break;
475                         }
476
477                         /* account for the NUL terminator */
478                         arg++, arglen--;
479
480                         /* call with the appropriate arguments */
481                         if (ioc->cmd == DHD_GET_VAR)
482                                 bcmerror =
483                                     dhd_iovar_op(dhd_pub, buf, arg, arglen, buf,
484                                                  buflen, IOV_GET);
485                         else
486                                 bcmerror =
487                                     dhd_iovar_op(dhd_pub, buf, NULL, 0, arg,
488                                                  arglen, IOV_SET);
489                         if (bcmerror != BCME_UNSUPPORTED)
490                                 break;
491
492                         /* not in generic table, try protocol module */
493                         if (ioc->cmd == DHD_GET_VAR)
494                                 bcmerror = dhd_prot_iovar_op(dhd_pub, buf, arg,
495                                                              arglen, buf,
496                                                              buflen, IOV_GET);
497                         else
498                                 bcmerror = dhd_prot_iovar_op(dhd_pub, buf,
499                                                              NULL, 0, arg,
500                                                              arglen, IOV_SET);
501                         if (bcmerror != BCME_UNSUPPORTED)
502                                 break;
503
504                         /* if still not found, try bus module */
505                         if (ioc->cmd == DHD_GET_VAR)
506                                 bcmerror = dhd_bus_iovar_op(dhd_pub, buf,
507                                                             arg, arglen, buf,
508                                                             buflen, IOV_GET);
509                         else
510                                 bcmerror = dhd_bus_iovar_op(dhd_pub, buf,
511                                                             NULL, 0, arg,
512                                                             arglen, IOV_SET);
513
514                         break;
515                 }
516
517         default:
518                 bcmerror = BCME_UNSUPPORTED;
519         }
520
521         return bcmerror;
522 }
523
524 #ifdef SHOW_EVENTS
525 static void wl_show_host_event(wl_event_msg_t *event, void *event_data)
526 {
527         uint i, status, reason;
528         bool group = false, flush_txq = false, link = false;
529         char *auth_str, *event_name;
530         unsigned char *buf;
531         char err_msg[256], eabuf[ETHER_ADDR_STR_LEN];
532         static struct {
533                 uint event;
534                 char *event_name;
535         } event_names[] = {
536                 {
537                 WLC_E_SET_SSID, "SET_SSID"}, {
538                 WLC_E_JOIN, "JOIN"}, {
539                 WLC_E_START, "START"}, {
540                 WLC_E_AUTH, "AUTH"}, {
541                 WLC_E_AUTH_IND, "AUTH_IND"}, {
542                 WLC_E_DEAUTH, "DEAUTH"}, {
543                 WLC_E_DEAUTH_IND, "DEAUTH_IND"}, {
544                 WLC_E_ASSOC, "ASSOC"}, {
545                 WLC_E_ASSOC_IND, "ASSOC_IND"}, {
546                 WLC_E_REASSOC, "REASSOC"}, {
547                 WLC_E_REASSOC_IND, "REASSOC_IND"}, {
548                 WLC_E_DISASSOC, "DISASSOC"}, {
549                 WLC_E_DISASSOC_IND, "DISASSOC_IND"}, {
550                 WLC_E_QUIET_START, "START_QUIET"}, {
551                 WLC_E_QUIET_END, "END_QUIET"}, {
552                 WLC_E_BEACON_RX, "BEACON_RX"}, {
553                 WLC_E_LINK, "LINK"}, {
554                 WLC_E_MIC_ERROR, "MIC_ERROR"}, {
555                 WLC_E_NDIS_LINK, "NDIS_LINK"}, {
556                 WLC_E_ROAM, "ROAM"}, {
557                 WLC_E_TXFAIL, "TXFAIL"}, {
558                 WLC_E_PMKID_CACHE, "PMKID_CACHE"}, {
559                 WLC_E_RETROGRADE_TSF, "RETROGRADE_TSF"}, {
560                 WLC_E_PRUNE, "PRUNE"}, {
561                 WLC_E_AUTOAUTH, "AUTOAUTH"}, {
562                 WLC_E_EAPOL_MSG, "EAPOL_MSG"}, {
563                 WLC_E_SCAN_COMPLETE, "SCAN_COMPLETE"}, {
564                 WLC_E_ADDTS_IND, "ADDTS_IND"}, {
565                 WLC_E_DELTS_IND, "DELTS_IND"}, {
566                 WLC_E_BCNSENT_IND, "BCNSENT_IND"}, {
567                 WLC_E_BCNRX_MSG, "BCNRX_MSG"}, {
568                 WLC_E_BCNLOST_MSG, "BCNLOST_MSG"}, {
569                 WLC_E_ROAM_PREP, "ROAM_PREP"}, {
570                 WLC_E_PFN_NET_FOUND, "PNO_NET_FOUND"}, {
571                 WLC_E_PFN_NET_LOST, "PNO_NET_LOST"}, {
572                 WLC_E_RESET_COMPLETE, "RESET_COMPLETE"}, {
573                 WLC_E_JOIN_START, "JOIN_START"}, {
574                 WLC_E_ROAM_START, "ROAM_START"}, {
575                 WLC_E_ASSOC_START, "ASSOC_START"}, {
576                 WLC_E_IBSS_ASSOC, "IBSS_ASSOC"}, {
577                 WLC_E_RADIO, "RADIO"}, {
578                 WLC_E_PSM_WATCHDOG, "PSM_WATCHDOG"}, {
579                 WLC_E_PROBREQ_MSG, "PROBREQ_MSG"}, {
580                 WLC_E_SCAN_CONFIRM_IND, "SCAN_CONFIRM_IND"}, {
581                 WLC_E_PSK_SUP, "PSK_SUP"}, {
582                 WLC_E_COUNTRY_CODE_CHANGED, "COUNTRY_CODE_CHANGED"}, {
583                 WLC_E_EXCEEDED_MEDIUM_TIME, "EXCEEDED_MEDIUM_TIME"}, {
584                 WLC_E_ICV_ERROR, "ICV_ERROR"}, {
585                 WLC_E_UNICAST_DECODE_ERROR, "UNICAST_DECODE_ERROR"}, {
586                 WLC_E_MULTICAST_DECODE_ERROR, "MULTICAST_DECODE_ERROR"}, {
587                 WLC_E_TRACE, "TRACE"}, {
588                 WLC_E_ACTION_FRAME, "ACTION FRAME"}, {
589                 WLC_E_ACTION_FRAME_COMPLETE, "ACTION FRAME TX COMPLETE"}, {
590                 WLC_E_IF, "IF"}, {
591                 WLC_E_RSSI, "RSSI"}, {
592                 WLC_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE"}
593         };
594         uint event_type, flags, auth_type, datalen;
595         event_type = ntoh32(event->event_type);
596         flags = ntoh16(event->flags);
597         status = ntoh32(event->status);
598         reason = ntoh32(event->reason);
599         auth_type = ntoh32(event->auth_type);
600         datalen = ntoh32(event->datalen);
601         /* debug dump of event messages */
602         sprintf(eabuf, "%pM", event->addr.octet);
603
604         event_name = "UNKNOWN";
605         for (i = 0; i < ARRAY_SIZE(event_names); i++) {
606                 if (event_names[i].event == event_type)
607                         event_name = event_names[i].event_name;
608         }
609
610         DHD_EVENT(("EVENT: %s, event ID = %d\n", event_name, event_type));
611
612         if (flags & WLC_EVENT_MSG_LINK)
613                 link = true;
614         if (flags & WLC_EVENT_MSG_GROUP)
615                 group = true;
616         if (flags & WLC_EVENT_MSG_FLUSHTXQ)
617                 flush_txq = true;
618
619         switch (event_type) {
620         case WLC_E_START:
621         case WLC_E_DEAUTH:
622         case WLC_E_DISASSOC:
623                 DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
624                 break;
625
626         case WLC_E_ASSOC_IND:
627         case WLC_E_REASSOC_IND:
628                 DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
629                 break;
630
631         case WLC_E_ASSOC:
632         case WLC_E_REASSOC:
633                 if (status == WLC_E_STATUS_SUCCESS) {
634                         DHD_EVENT(("MACEVENT: %s, MAC %s, SUCCESS\n",
635                                    event_name, eabuf));
636                 } else if (status == WLC_E_STATUS_TIMEOUT) {
637                         DHD_EVENT(("MACEVENT: %s, MAC %s, TIMEOUT\n",
638                                    event_name, eabuf));
639                 } else if (status == WLC_E_STATUS_FAIL) {
640                         DHD_EVENT(("MACEVENT: %s, MAC %s, FAILURE, reason %d\n",
641                                    event_name, eabuf, (int)reason));
642                 } else {
643                         DHD_EVENT(("MACEVENT: %s, MAC %s, unexpected status "
644                                 "%d\n", event_name, eabuf, (int)status));
645                 }
646                 break;
647
648         case WLC_E_DEAUTH_IND:
649         case WLC_E_DISASSOC_IND:
650                 DHD_EVENT(("MACEVENT: %s, MAC %s, reason %d\n", event_name,
651                            eabuf, (int)reason));
652                 break;
653
654         case WLC_E_AUTH:
655         case WLC_E_AUTH_IND:
656                 if (auth_type == DOT11_OPEN_SYSTEM)
657                         auth_str = "Open System";
658                 else if (auth_type == DOT11_SHARED_KEY)
659                         auth_str = "Shared Key";
660                 else {
661                         sprintf(err_msg, "AUTH unknown: %d", (int)auth_type);
662                         auth_str = err_msg;
663                 }
664                 if (event_type == WLC_E_AUTH_IND) {
665                         DHD_EVENT(("MACEVENT: %s, MAC %s, %s\n", event_name,
666                                    eabuf, auth_str));
667                 } else if (status == WLC_E_STATUS_SUCCESS) {
668                         DHD_EVENT(("MACEVENT: %s, MAC %s, %s, SUCCESS\n",
669                                    event_name, eabuf, auth_str));
670                 } else if (status == WLC_E_STATUS_TIMEOUT) {
671                         DHD_EVENT(("MACEVENT: %s, MAC %s, %s, TIMEOUT\n",
672                                    event_name, eabuf, auth_str));
673                 } else if (status == WLC_E_STATUS_FAIL) {
674                         DHD_EVENT(("MACEVENT: %s, MAC %s, %s, FAILURE, "
675                                 "reason %d\n",
676                                 event_name, eabuf, auth_str, (int)reason));
677                 }
678
679                 break;
680
681         case WLC_E_JOIN:
682         case WLC_E_ROAM:
683         case WLC_E_SET_SSID:
684                 if (status == WLC_E_STATUS_SUCCESS) {
685                         DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name,
686                                    eabuf));
687                 } else if (status == WLC_E_STATUS_FAIL) {
688                         DHD_EVENT(("MACEVENT: %s, failed\n", event_name));
689                 } else if (status == WLC_E_STATUS_NO_NETWORKS) {
690                         DHD_EVENT(("MACEVENT: %s, no networks found\n",
691                                    event_name));
692                 } else {
693                         DHD_EVENT(("MACEVENT: %s, unexpected status %d\n",
694                                    event_name, (int)status));
695                 }
696                 break;
697
698         case WLC_E_BEACON_RX:
699                 if (status == WLC_E_STATUS_SUCCESS) {
700                         DHD_EVENT(("MACEVENT: %s, SUCCESS\n", event_name));
701                 } else if (status == WLC_E_STATUS_FAIL) {
702                         DHD_EVENT(("MACEVENT: %s, FAIL\n", event_name));
703                 } else {
704                         DHD_EVENT(("MACEVENT: %s, status %d\n", event_name,
705                                    status));
706                 }
707                 break;
708
709         case WLC_E_LINK:
710                 DHD_EVENT(("MACEVENT: %s %s\n", event_name,
711                            link ? "UP" : "DOWN"));
712                 break;
713
714         case WLC_E_MIC_ERROR:
715                 DHD_EVENT(("MACEVENT: %s, MAC %s, Group %d, Flush %d\n",
716                            event_name, eabuf, group, flush_txq));
717                 break;
718
719         case WLC_E_ICV_ERROR:
720         case WLC_E_UNICAST_DECODE_ERROR:
721         case WLC_E_MULTICAST_DECODE_ERROR:
722                 DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
723                 break;
724
725         case WLC_E_TXFAIL:
726                 DHD_EVENT(("MACEVENT: %s, RA %s\n", event_name, eabuf));
727                 break;
728
729         case WLC_E_SCAN_COMPLETE:
730         case WLC_E_PMKID_CACHE:
731                 DHD_EVENT(("MACEVENT: %s\n", event_name));
732                 break;
733
734         case WLC_E_PFN_NET_FOUND:
735         case WLC_E_PFN_NET_LOST:
736         case WLC_E_PFN_SCAN_COMPLETE:
737                 DHD_EVENT(("PNOEVENT: %s\n", event_name));
738                 break;
739
740         case WLC_E_PSK_SUP:
741         case WLC_E_PRUNE:
742                 DHD_EVENT(("MACEVENT: %s, status %d, reason %d\n",
743                            event_name, (int)status, (int)reason));
744                 break;
745
746         case WLC_E_TRACE:
747                 {
748                         static u32 seqnum_prev;
749                         msgtrace_hdr_t hdr;
750                         u32 nblost;
751                         char *s, *p;
752
753                         buf = (unsigned char *) event_data;
754                         memcpy(&hdr, buf, MSGTRACE_HDRLEN);
755
756                         if (hdr.version != MSGTRACE_VERSION) {
757                                 printf
758                                     ("\nMACEVENT: %s [unsupported version --> "
759                                      "dhd version:%d dongle version:%d]\n",
760                                      event_name, MSGTRACE_VERSION, hdr.version);
761                                 /* Reset datalen to avoid display below */
762                                 datalen = 0;
763                                 break;
764                         }
765
766                         /* There are 2 bytes available at the end of data */
767                         buf[MSGTRACE_HDRLEN + ntoh16(hdr.len)] = '\0';
768
769                         if (ntoh32(hdr.discarded_bytes)
770                             || ntoh32(hdr.discarded_printf)) {
771                                 printf
772                                     ("\nWLC_E_TRACE: [Discarded traces in dongle -->"
773                                      "discarded_bytes %d discarded_printf %d]\n",
774                                      ntoh32(hdr.discarded_bytes),
775                                      ntoh32(hdr.discarded_printf));
776                         }
777
778                         nblost = ntoh32(hdr.seqnum) - seqnum_prev - 1;
779                         if (nblost > 0) {
780                                 printf
781                                     ("\nWLC_E_TRACE: [Event lost --> seqnum %d nblost %d\n",
782                                      ntoh32(hdr.seqnum), nblost);
783                         }
784                         seqnum_prev = ntoh32(hdr.seqnum);
785
786                         /* Display the trace buffer. Advance from \n to \n to
787                          * avoid display big
788                          * printf (issue with Linux printk )
789                          */
790                         p = (char *)&buf[MSGTRACE_HDRLEN];
791                         while ((s = strstr(p, "\n")) != NULL) {
792                                 *s = '\0';
793                                 printf("%s\n", p);
794                                 p = s + 1;
795                         }
796                         printf("%s\n", p);
797
798                         /* Reset datalen to avoid display below */
799                         datalen = 0;
800                 }
801                 break;
802
803         case WLC_E_RSSI:
804                 DHD_EVENT(("MACEVENT: %s %d\n", event_name,
805                            ntoh32(*((int *)event_data))));
806                 break;
807
808         default:
809                 DHD_EVENT(("MACEVENT: %s %d, MAC %s, status %d, reason %d, "
810                         "auth %d\n", event_name, event_type, eabuf,
811                         (int)status, (int)reason, (int)auth_type));
812                 break;
813         }
814
815         /* show any appended data */
816         if (datalen) {
817                 buf = (unsigned char *) event_data;
818                 DHD_EVENT((" data (%d) : ", datalen));
819                 for (i = 0; i < datalen; i++)
820                         DHD_EVENT((" 0x%02x ", *buf++));
821                 DHD_EVENT(("\n"));
822         }
823 }
824 #endif                          /* SHOW_EVENTS */
825
826 int
827 wl_host_event(struct dhd_info *dhd, int *ifidx, void *pktdata,
828               wl_event_msg_t *event, void **data_ptr)
829 {
830         /* check whether packet is a BRCM event pkt */
831         bcm_event_t *pvt_data = (bcm_event_t *) pktdata;
832         char *event_data;
833         u32 type, status;
834         u16 flags;
835         int evlen;
836
837         if (memcmp(BRCM_OUI, &pvt_data->bcm_hdr.oui[0], DOT11_OUI_LEN)) {
838                 DHD_ERROR(("%s: mismatched OUI, bailing\n", __func__));
839                 return BCME_ERROR;
840         }
841
842         /* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */
843         if (ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype) !=
844             BCMILCP_BCM_SUBTYPE_EVENT) {
845                 DHD_ERROR(("%s: mismatched subtype, bailing\n", __func__));
846                 return BCME_ERROR;
847         }
848
849         *data_ptr = &pvt_data[1];
850         event_data = *data_ptr;
851
852         /* memcpy since BRCM event pkt may be unaligned. */
853         memcpy(event, &pvt_data->event, sizeof(wl_event_msg_t));
854
855         type = ntoh32_ua((void *)&event->event_type);
856         flags = ntoh16_ua((void *)&event->flags);
857         status = ntoh32_ua((void *)&event->status);
858         evlen = ntoh32_ua((void *)&event->datalen) + sizeof(bcm_event_t);
859
860         switch (type) {
861         case WLC_E_IF:
862                 {
863                         dhd_if_event_t *ifevent = (dhd_if_event_t *) event_data;
864                         DHD_TRACE(("%s: if event\n", __func__));
865
866                         if (ifevent->ifidx > 0 &&
867                                  ifevent->ifidx < DHD_MAX_IFS) {
868                                 if (ifevent->action == WLC_E_IF_ADD)
869                                         dhd_add_if(dhd, ifevent->ifidx,
870                                                    NULL, event->ifname,
871                                                    pvt_data->eth.ether_dhost,
872                                                    ifevent->flags,
873                                                    ifevent->bssidx);
874                                 else
875                                         dhd_del_if(dhd, ifevent->ifidx);
876                         } else {
877                                 DHD_ERROR(("%s: Invalid ifidx %d for %s\n",
878                                            __func__, ifevent->ifidx,
879                                            event->ifname));
880                         }
881                 }
882                 /* send up the if event: btamp user needs it */
883                 *ifidx = dhd_ifname2idx(dhd, event->ifname);
884                 /* push up to external supp/auth */
885                 dhd_event(dhd, (char *)pvt_data, evlen, *ifidx);
886                 break;
887
888 #ifdef P2P
889         case WLC_E_NDIS_LINK:
890                 break;
891 #endif
892                 /* fall through */
893                 /* These are what external supplicant/authenticator wants */
894         case WLC_E_LINK:
895         case WLC_E_ASSOC_IND:
896         case WLC_E_REASSOC_IND:
897         case WLC_E_DISASSOC_IND:
898         case WLC_E_MIC_ERROR:
899         default:
900                 /* Fall through: this should get _everything_  */
901
902                 *ifidx = dhd_ifname2idx(dhd, event->ifname);
903                 /* push up to external supp/auth */
904                 dhd_event(dhd, (char *)pvt_data, evlen, *ifidx);
905                 DHD_TRACE(("%s: MAC event %d, flags %x, status %x\n",
906                            __func__, type, flags, status));
907
908                 /* put it back to WLC_E_NDIS_LINK */
909                 if (type == WLC_E_NDIS_LINK) {
910                         u32 temp;
911
912                         temp = ntoh32_ua((void *)&event->event_type);
913                         DHD_TRACE(("Converted to WLC_E_LINK type %d\n", temp));
914
915                         temp = ntoh32(WLC_E_NDIS_LINK);
916                         memcpy((void *)(&pvt_data->event.event_type), &temp,
917                                sizeof(pvt_data->event.event_type));
918                 }
919                 break;
920         }
921
922 #ifdef SHOW_EVENTS
923         wl_show_host_event(event, event_data);
924 #endif                          /* SHOW_EVENTS */
925
926         return BCME_OK;
927 }
928
929 void wl_event_to_host_order(wl_event_msg_t *evt)
930 {
931         /* Event struct members passed from dongle to host are stored
932          * in network
933          * byte order. Convert all members to host-order.
934          */
935         evt->event_type = ntoh32(evt->event_type);
936         evt->flags = ntoh16(evt->flags);
937         evt->status = ntoh32(evt->status);
938         evt->reason = ntoh32(evt->reason);
939         evt->auth_type = ntoh32(evt->auth_type);
940         evt->datalen = ntoh32(evt->datalen);
941         evt->version = ntoh16(evt->version);
942 }
943
944 void print_buf(void *pbuf, int len, int bytes_per_line)
945 {
946         int i, j = 0;
947         unsigned char *buf = pbuf;
948
949         if (bytes_per_line == 0)
950                 bytes_per_line = len;
951
952         for (i = 0; i < len; i++) {
953                 printf("%2.2x", *buf++);
954                 j++;
955                 if (j == bytes_per_line) {
956                         printf("\n");
957                         j = 0;
958                 } else {
959                         printf(":");
960                 }
961         }
962         printf("\n");
963 }
964
965 /* Convert user's input in hex pattern to byte-size mask */
966 static int wl_pattern_atoh(char *src, char *dst)
967 {
968         int i;
969         if (strncmp(src, "0x", 2) != 0 && strncmp(src, "0X", 2) != 0) {
970                 DHD_ERROR(("Mask invalid format. Needs to start with 0x\n"));
971                 return -1;
972         }
973         src = src + 2;          /* Skip past 0x */
974         if (strlen(src) % 2 != 0) {
975                 DHD_ERROR(("Mask invalid format. Length must be even.\n"));
976                 return -1;
977         }
978         for (i = 0; *src != '\0'; i++) {
979                 char num[3];
980                 strncpy(num, src, 2);
981                 num[2] = '\0';
982                 dst[i] = (u8) simple_strtoul(num, NULL, 16);
983                 src += 2;
984         }
985         return i;
986 }
987
988 void
989 dhd_pktfilter_offload_enable(dhd_pub_t *dhd, char *arg, int enable,
990                              int master_mode)
991 {
992         char *argv[8];
993         int i = 0;
994         const char *str;
995         int buf_len;
996         int str_len;
997         char *arg_save = 0, *arg_org = 0;
998         int rc;
999         char buf[128];
1000         wl_pkt_filter_enable_t enable_parm;
1001         wl_pkt_filter_enable_t *pkt_filterp;
1002
1003         arg_save = kmalloc(strlen(arg) + 1, GFP_ATOMIC);
1004         if (!arg_save) {
1005                 DHD_ERROR(("%s: kmalloc failed\n", __func__));
1006                 goto fail;
1007         }
1008         arg_org = arg_save;
1009         memcpy(arg_save, arg, strlen(arg) + 1);
1010
1011         argv[i] = strsep(&arg_save, " ");
1012
1013         i = 0;
1014         if (NULL == argv[i]) {
1015                 DHD_ERROR(("No args provided\n"));
1016                 goto fail;
1017         }
1018
1019         str = "pkt_filter_enable";
1020         str_len = strlen(str);
1021         strncpy(buf, str, str_len);
1022         buf[str_len] = '\0';
1023         buf_len = str_len + 1;
1024
1025         pkt_filterp = (wl_pkt_filter_enable_t *) (buf + str_len + 1);
1026
1027         /* Parse packet filter id. */
1028         enable_parm.id = htod32(simple_strtoul(argv[i], NULL, 0));
1029
1030         /* Parse enable/disable value. */
1031         enable_parm.enable = htod32(enable);
1032
1033         buf_len += sizeof(enable_parm);
1034         memcpy((char *)pkt_filterp, &enable_parm, sizeof(enable_parm));
1035
1036         /* Enable/disable the specified filter. */
1037         rc = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, buf_len);
1038         rc = rc >= 0 ? 0 : rc;
1039         if (rc)
1040                 DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
1041                            __func__, arg, rc));
1042         else
1043                 DHD_TRACE(("%s: successfully added pktfilter %s\n",
1044                            __func__, arg));
1045
1046         /* Contorl the master mode */
1047         bcm_mkiovar("pkt_filter_mode", (char *)&master_mode, 4, buf,
1048                     sizeof(buf));
1049         rc = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, sizeof(buf));
1050         rc = rc >= 0 ? 0 : rc;
1051         if (rc)
1052                 DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
1053                            __func__, arg, rc));
1054
1055 fail:
1056         if (arg_org)
1057                 kfree(arg_org);
1058 }
1059
1060 void dhd_pktfilter_offload_set(dhd_pub_t *dhd, char *arg)
1061 {
1062         const char *str;
1063         wl_pkt_filter_t pkt_filter;
1064         wl_pkt_filter_t *pkt_filterp;
1065         int buf_len;
1066         int str_len;
1067         int rc;
1068         u32 mask_size;
1069         u32 pattern_size;
1070         char *argv[8], *buf = 0;
1071         int i = 0;
1072         char *arg_save = 0, *arg_org = 0;
1073 #define BUF_SIZE                2048
1074
1075         arg_save = kmalloc(strlen(arg) + 1, GFP_ATOMIC);
1076         if (!arg_save) {
1077                 DHD_ERROR(("%s: kmalloc failed\n", __func__));
1078                 goto fail;
1079         }
1080
1081         arg_org = arg_save;
1082
1083         buf = kmalloc(BUF_SIZE, GFP_ATOMIC);
1084         if (!buf) {
1085                 DHD_ERROR(("%s: kmalloc failed\n", __func__));
1086                 goto fail;
1087         }
1088
1089         memcpy(arg_save, arg, strlen(arg) + 1);
1090
1091         if (strlen(arg) > BUF_SIZE) {
1092                 DHD_ERROR(("Not enough buffer %d < %d\n", (int)strlen(arg),
1093                            (int)sizeof(buf)));
1094                 goto fail;
1095         }
1096
1097         argv[i] = strsep(&arg_save, " ");
1098         while (argv[i++])
1099                 argv[i] = strsep(&arg_save, " ");
1100
1101         i = 0;
1102         if (NULL == argv[i]) {
1103                 DHD_ERROR(("No args provided\n"));
1104                 goto fail;
1105         }
1106
1107         str = "pkt_filter_add";
1108         str_len = strlen(str);
1109         strncpy(buf, str, str_len);
1110         buf[str_len] = '\0';
1111         buf_len = str_len + 1;
1112
1113         pkt_filterp = (wl_pkt_filter_t *) (buf + str_len + 1);
1114
1115         /* Parse packet filter id. */
1116         pkt_filter.id = htod32(simple_strtoul(argv[i], NULL, 0));
1117
1118         if (NULL == argv[++i]) {
1119                 DHD_ERROR(("Polarity not provided\n"));
1120                 goto fail;
1121         }
1122
1123         /* Parse filter polarity. */
1124         pkt_filter.negate_match = htod32(simple_strtoul(argv[i], NULL, 0));
1125
1126         if (NULL == argv[++i]) {
1127                 DHD_ERROR(("Filter type not provided\n"));
1128                 goto fail;
1129         }
1130
1131         /* Parse filter type. */
1132         pkt_filter.type = htod32(simple_strtoul(argv[i], NULL, 0));
1133
1134         if (NULL == argv[++i]) {
1135                 DHD_ERROR(("Offset not provided\n"));
1136                 goto fail;
1137         }
1138
1139         /* Parse pattern filter offset. */
1140         pkt_filter.u.pattern.offset = htod32(simple_strtoul(argv[i], NULL, 0));
1141
1142         if (NULL == argv[++i]) {
1143                 DHD_ERROR(("Bitmask not provided\n"));
1144                 goto fail;
1145         }
1146
1147         /* Parse pattern filter mask. */
1148         mask_size =
1149             htod32(wl_pattern_atoh
1150                    (argv[i], (char *)pkt_filterp->u.pattern.mask_and_pattern));
1151
1152         if (NULL == argv[++i]) {
1153                 DHD_ERROR(("Pattern not provided\n"));
1154                 goto fail;
1155         }
1156
1157         /* Parse pattern filter pattern. */
1158         pattern_size =
1159             htod32(wl_pattern_atoh(argv[i],
1160                                    (char *)&pkt_filterp->u.pattern.
1161                                    mask_and_pattern[mask_size]));
1162
1163         if (mask_size != pattern_size) {
1164                 DHD_ERROR(("Mask and pattern not the same size\n"));
1165                 goto fail;
1166         }
1167
1168         pkt_filter.u.pattern.size_bytes = mask_size;
1169         buf_len += WL_PKT_FILTER_FIXED_LEN;
1170         buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size);
1171
1172         /* Keep-alive attributes are set in local
1173          * variable (keep_alive_pkt), and
1174          ** then memcpy'ed into buffer (keep_alive_pktp) since there is no
1175          ** guarantee that the buffer is properly aligned.
1176          */
1177         memcpy((char *)pkt_filterp,
1178                &pkt_filter,
1179                WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN);
1180
1181         rc = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, buf_len);
1182         rc = rc >= 0 ? 0 : rc;
1183
1184         if (rc)
1185                 DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
1186                            __func__, arg, rc));
1187         else
1188                 DHD_TRACE(("%s: successfully added pktfilter %s\n",
1189                            __func__, arg));
1190
1191 fail:
1192         if (arg_org)
1193                 kfree(arg_org);
1194
1195         if (buf)
1196                 kfree(buf);
1197 }
1198
1199 void dhd_arp_offload_set(dhd_pub_t *dhd, int arp_mode)
1200 {
1201         char iovbuf[32];
1202         int retcode;
1203
1204         bcm_mkiovar("arp_ol", (char *)&arp_mode, 4, iovbuf, sizeof(iovbuf));
1205         retcode = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
1206         retcode = retcode >= 0 ? 0 : retcode;
1207         if (retcode)
1208                 DHD_TRACE(("%s: failed to set ARP offload mode to 0x%x, "
1209                         "retcode = %d\n", __func__, arp_mode, retcode));
1210         else
1211                 DHD_TRACE(("%s: successfully set ARP offload mode to 0x%x\n",
1212                            __func__, arp_mode));
1213 }
1214
1215 void dhd_arp_offload_enable(dhd_pub_t *dhd, int arp_enable)
1216 {
1217         char iovbuf[32];
1218         int retcode;
1219
1220         bcm_mkiovar("arpoe", (char *)&arp_enable, 4, iovbuf, sizeof(iovbuf));
1221         retcode = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
1222         retcode = retcode >= 0 ? 0 : retcode;
1223         if (retcode)
1224                 DHD_TRACE(("%s: failed to enabe ARP offload to %d, "
1225                         "retcode = %d\n", __func__, arp_enable, retcode));
1226         else
1227                 DHD_TRACE(("%s: successfully enabed ARP offload to %d\n",
1228                            __func__, arp_enable));
1229 }
1230
1231 int dhd_preinit_ioctls(dhd_pub_t *dhd)
1232 {
1233         char iovbuf[WL_EVENTING_MASK_LEN + 12]; /*  Room for
1234                                  "event_msgs" + '\0' + bitvec  */
1235         uint up = 0;
1236         char buf[128], *ptr;
1237         uint power_mode = PM_FAST;
1238         u32 dongle_align = DHD_SDALIGN;
1239         u32 glom = 0;
1240         uint bcn_timeout = 3;
1241         int scan_assoc_time = 40;
1242         int scan_unassoc_time = 40;
1243 #ifdef GET_CUSTOM_MAC_ENABLE
1244         int ret = 0;
1245         struct ether_addr ea_addr;
1246 #endif                          /* GET_CUSTOM_MAC_ENABLE */
1247
1248         dhd_os_proto_block(dhd);
1249
1250 #ifdef GET_CUSTOM_MAC_ENABLE
1251         /* Read MAC address from external customer place
1252          ** NOTE that default mac address has to be present in
1253          ** otp or nvram file to bring up
1254          ** firmware but unique per board mac address maybe provided by
1255          ** customer code
1256          */
1257         ret = dhd_custom_get_mac_address(ea_addr.octet);
1258         if (!ret) {
1259                 bcm_mkiovar("cur_etheraddr", (void *)&ea_addr, ETH_ALEN,
1260                             buf, sizeof(buf));
1261                 ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, sizeof(buf));
1262                 if (ret < 0) {
1263                         DHD_ERROR(("%s: can't set MAC address , error=%d\n",
1264                                    __func__, ret));
1265                 } else
1266                         memcpy(dhd->mac.octet, (void *)&ea_addr,
1267                                ETH_ALEN);
1268         }
1269 #endif                          /* GET_CUSTOM_MAC_ENABLE */
1270
1271         /* Set Country code */
1272         if (dhd->country_code[0] != 0) {
1273                 if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_COUNTRY,
1274                                      dhd->country_code,
1275                                      sizeof(dhd->country_code)) < 0) {
1276                         DHD_ERROR(("%s: country code setting failed\n",
1277                                    __func__));
1278                 }
1279         }
1280
1281         /* query for 'ver' to get version info from firmware */
1282         memset(buf, 0, sizeof(buf));
1283         ptr = buf;
1284         bcm_mkiovar("ver", 0, 0, buf, sizeof(buf));
1285         dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf));
1286         strsep(&ptr, "\n");
1287         /* Print fw version info */
1288         DHD_ERROR(("Firmware version = %s\n", buf));
1289
1290         /* Set PowerSave mode */
1291         dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM, (char *)&power_mode,
1292                          sizeof(power_mode));
1293
1294         /* Match Host and Dongle rx alignment */
1295         bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf,
1296                     sizeof(iovbuf));
1297         dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
1298
1299         /* disable glom option per default */
1300         bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf));
1301         dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
1302
1303         /* Setup timeout if Beacons are lost and roam is off to report
1304                  link down */
1305         bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf,
1306                     sizeof(iovbuf));
1307         dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
1308
1309         /* Enable/Disable build-in roaming to allowed ext supplicant to take
1310                  of romaing */
1311         bcm_mkiovar("roam_off", (char *)&dhd_roam, 4, iovbuf, sizeof(iovbuf));
1312         dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
1313
1314         /* Force STA UP */
1315         if (dhd_radio_up)
1316                 dhdcdc_set_ioctl(dhd, 0, WLC_UP, (char *)&up, sizeof(up));
1317
1318         /* Setup event_msgs */
1319         bcm_mkiovar("event_msgs", dhd->eventmask, WL_EVENTING_MASK_LEN, iovbuf,
1320                     sizeof(iovbuf));
1321         dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
1322
1323         dhdcdc_set_ioctl(dhd, 0, WLC_SET_SCAN_CHANNEL_TIME,
1324                          (char *)&scan_assoc_time, sizeof(scan_assoc_time));
1325         dhdcdc_set_ioctl(dhd, 0, WLC_SET_SCAN_UNASSOC_TIME,
1326                          (char *)&scan_unassoc_time, sizeof(scan_unassoc_time));
1327
1328 #ifdef ARP_OFFLOAD_SUPPORT
1329         /* Set and enable ARP offload feature */
1330         if (dhd_arp_enable)
1331                 dhd_arp_offload_set(dhd, dhd_arp_mode);
1332         dhd_arp_offload_enable(dhd, dhd_arp_enable);
1333 #endif                          /* ARP_OFFLOAD_SUPPORT */
1334
1335 #ifdef PKT_FILTER_SUPPORT
1336         {
1337                 int i;
1338                 /* Set up pkt filter */
1339                 if (dhd_pkt_filter_enable) {
1340                         for (i = 0; i < dhd->pktfilter_count; i++) {
1341                                 dhd_pktfilter_offload_set(dhd,
1342                                                           dhd->pktfilter[i]);
1343                                 dhd_pktfilter_offload_enable(dhd,
1344                                      dhd->pktfilter[i],
1345                                      dhd_pkt_filter_init,
1346                                      dhd_master_mode);
1347                         }
1348                 }
1349         }
1350 #endif                          /* PKT_FILTER_SUPPORT */
1351
1352         dhd_os_proto_unblock(dhd);
1353
1354         return 0;
1355 }
1356
1357 #ifdef SIMPLE_ISCAN
1358 uint iscan_thread_id;
1359 iscan_buf_t *iscan_chain;
1360
1361 iscan_buf_t *dhd_iscan_allocate_buf(dhd_pub_t *dhd, iscan_buf_t **iscanbuf)
1362 {
1363         iscan_buf_t *iscanbuf_alloc = 0;
1364         iscan_buf_t *iscanbuf_head;
1365
1366         dhd_iscan_lock();
1367
1368         iscanbuf_alloc = kmalloc(sizeof(iscan_buf_t), GFP_ATOMIC);
1369         if (iscanbuf_alloc == NULL)
1370                 goto fail;
1371
1372         iscanbuf_alloc->next = NULL;
1373         iscanbuf_head = *iscanbuf;
1374
1375         DHD_ISCAN(("%s: addr of allocated node = 0x%X"
1376                    "addr of iscanbuf_head = 0x%X dhd = 0x%X\n",
1377                    __func__, iscanbuf_alloc, iscanbuf_head, dhd));
1378
1379         if (iscanbuf_head == NULL) {
1380                 *iscanbuf = iscanbuf_alloc;
1381                 DHD_ISCAN(("%s: Head is allocated\n", __func__));
1382                 goto fail;
1383         }
1384
1385         while (iscanbuf_head->next)
1386                 iscanbuf_head = iscanbuf_head->next;
1387
1388         iscanbuf_head->next = iscanbuf_alloc;
1389
1390 fail:
1391         dhd_iscan_unlock();
1392         return iscanbuf_alloc;
1393 }
1394
1395 void dhd_iscan_free_buf(void *dhdp, iscan_buf_t *iscan_delete)
1396 {
1397         iscan_buf_t *iscanbuf_free = 0;
1398         iscan_buf_t *iscanbuf_prv = 0;
1399         iscan_buf_t *iscanbuf_cur = iscan_chain;
1400         dhd_pub_t *dhd = dhd_bus_pub(dhdp);
1401
1402         dhd_iscan_lock();
1403         /* If iscan_delete is null then delete the entire
1404          * chain or else delete specific one provided
1405          */
1406         if (!iscan_delete) {
1407                 while (iscanbuf_cur) {
1408                         iscanbuf_free = iscanbuf_cur;
1409                         iscanbuf_cur = iscanbuf_cur->next;
1410                         iscanbuf_free->next = 0;
1411                         kfree(iscanbuf_free);
1412                 }
1413                 iscan_chain = 0;
1414         } else {
1415                 while (iscanbuf_cur) {
1416                         if (iscanbuf_cur == iscan_delete)
1417                                 break;
1418                         iscanbuf_prv = iscanbuf_cur;
1419                         iscanbuf_cur = iscanbuf_cur->next;
1420                 }
1421                 if (iscanbuf_prv)
1422                         iscanbuf_prv->next = iscan_delete->next;
1423
1424                 iscan_delete->next = 0;
1425                 kfree(iscan_delete);
1426
1427                 if (!iscanbuf_prv)
1428                         iscan_chain = 0;
1429         }
1430         dhd_iscan_unlock();
1431 }
1432
1433 iscan_buf_t *dhd_iscan_result_buf(void)
1434 {
1435         return iscan_chain;
1436 }
1437
1438 /*
1439 * print scan cache
1440 * print partial iscan_skip list differently
1441 */
1442 int dhd_iscan_print_cache(iscan_buf_t *iscan_skip)
1443 {
1444         int i = 0, l = 0;
1445         iscan_buf_t *iscan_cur;
1446         wl_iscan_results_t *list;
1447         wl_scan_results_t *results;
1448         wl_bss_info_t UNALIGNED *bi;
1449
1450         dhd_iscan_lock();
1451
1452         iscan_cur = dhd_iscan_result_buf();
1453
1454         while (iscan_cur) {
1455                 list = (wl_iscan_results_t *)iscan_cur->iscan_buf;
1456                 if (!list)
1457                         break;
1458
1459                 results = (wl_scan_results_t *)&list->results;
1460                 if (!results)
1461                         break;
1462
1463                 if (results->version != WL_BSS_INFO_VERSION) {
1464                         DHD_ISCAN(("%s: results->version %d != "
1465                                 "WL_BSS_INFO_VERSION\n",
1466                                 __func__, results->version));
1467                         goto done;
1468                 }
1469
1470                 bi = results->bss_info;
1471                 for (i = 0; i < results->count; i++) {
1472                         if (!bi)
1473                                 break;
1474
1475                         DHD_ISCAN(("%s[%2.2d:%2.2d] %X:%X:%X:%X:%X:%X\n",
1476                                    iscan_cur != iscan_skip ? "BSS" : "bss", l,
1477                                    i, bi->BSSID.octet[0], bi->BSSID.octet[1],
1478                                    bi->BSSID.octet[2], bi->BSSID.octet[3],
1479                                    bi->BSSID.octet[4], bi->BSSID.octet[5]));
1480
1481                         bi = (wl_bss_info_t *)((unsigned long)bi +
1482                                                 dtoh32(bi->length));
1483                 }
1484                 iscan_cur = iscan_cur->next;
1485                 l++;
1486         }
1487
1488 done:
1489         dhd_iscan_unlock();
1490         return 0;
1491 }
1492
1493 /*
1494 * delete disappeared AP from specific scan cache but skip partial
1495 * list in iscan_skip
1496 */
1497 int dhd_iscan_delete_bss(void *dhdp, void *addr, iscan_buf_t *iscan_skip)
1498 {
1499         int i = 0, j = 0, l = 0;
1500         iscan_buf_t *iscan_cur;
1501         wl_iscan_results_t *list;
1502         wl_scan_results_t *results;
1503         wl_bss_info_t UNALIGNED *bi, *bi_new, *bi_next;
1504
1505         unsigned char *s_addr = addr;
1506
1507         dhd_iscan_lock();
1508         DHD_ISCAN(("%s: BSS to remove %X:%X:%X:%X:%X:%X\n",
1509                    __func__, s_addr[0], s_addr[1], s_addr[2],
1510                    s_addr[3], s_addr[4], s_addr[5]));
1511
1512         iscan_cur = dhd_iscan_result_buf();
1513
1514         while (iscan_cur) {
1515                 if (iscan_cur != iscan_skip) {
1516                         list = (wl_iscan_results_t *)iscan_cur->iscan_buf;
1517                         if (!list)
1518                                 break;
1519
1520                         results = (wl_scan_results_t *)&list->results;
1521                         if (!results)
1522                                 break;
1523
1524                         if (results->version != WL_BSS_INFO_VERSION) {
1525                                 DHD_ERROR(("%s: results->version %d != "
1526                                         "WL_BSS_INFO_VERSION\n",
1527                                         __func__, results->version));
1528                                 goto done;
1529                         }
1530
1531                         bi = results->bss_info;
1532                         for (i = 0; i < results->count; i++) {
1533                                 if (!bi)
1534                                         break;
1535
1536                                 if (!memcmp
1537                                     (bi->BSSID.octet, addr, ETH_ALEN)) {
1538                                         DHD_ISCAN(("%s: Del BSS[%2.2d:%2.2d] "
1539                                         "%X:%X:%X:%X:%X:%X\n",
1540                                         __func__, l, i, bi->BSSID.octet[0],
1541                                         bi->BSSID.octet[1], bi->BSSID.octet[2],
1542                                         bi->BSSID.octet[3], bi->BSSID.octet[4],
1543                                         bi->BSSID.octet[5]));
1544
1545                                         bi_new = bi;
1546                                         bi = (wl_bss_info_t *)((unsigned long)bi +
1547                                                                 dtoh32
1548                                                                 (bi->length));
1549 /*
1550                         if(bi && bi_new) {
1551                                 bcopy(bi, bi_new, results->buflen -
1552                                 dtoh32(bi_new->length));
1553                                 results->buflen -= dtoh32(bi_new->length);
1554                         }
1555 */
1556                                         results->buflen -=
1557                                             dtoh32(bi_new->length);
1558                                         results->count--;
1559
1560                                         for (j = i; j < results->count; j++) {
1561                                                 if (bi && bi_new) {
1562                                                         DHD_ISCAN(("%s: Moved up BSS[%2.2d:%2.2d]" "%X:%X:%X:%X:%X:%X\n",
1563                                                         __func__, l, j,
1564                                                         bi->BSSID.octet[0],
1565                                                         bi->BSSID.octet[1],
1566                                                         bi->BSSID.octet[2],
1567                                                         bi->BSSID.octet[3],
1568                                                         bi->BSSID.octet[4],
1569                                                         bi->BSSID.octet[5]));
1570
1571                                                         bi_next =
1572                                                             (wl_bss_info_t *)((unsigned long)bi +
1573                                                                  dtoh32
1574                                                                  (bi->length));
1575                                                         bcopy(bi, bi_new,
1576                                                               dtoh32
1577                                                               (bi->length));
1578                                                         bi_new =
1579                                                             (wl_bss_info_t *)((unsigned long)bi_new +
1580                                                                  dtoh32
1581                                                                  (bi_new->
1582                                                                   length));
1583                                                         bi = bi_next;
1584                                                 }
1585                                         }
1586
1587                                         if (results->count == 0) {
1588                                                 /* Prune now empty partial
1589                                                 scan list */
1590                                                 dhd_iscan_free_buf(dhdp,
1591                                                                    iscan_cur);
1592                                                 goto done;
1593                                         }
1594                                         break;
1595                                 }
1596                                 bi = (wl_bss_info_t *)((unsigned long)bi +
1597                                                         dtoh32(bi->length));
1598                         }
1599                 }
1600                 iscan_cur = iscan_cur->next;
1601                 l++;
1602         }
1603
1604 done:
1605         dhd_iscan_unlock();
1606         return 0;
1607 }
1608
1609 int dhd_iscan_remove_duplicates(void *dhdp, iscan_buf_t *iscan_cur)
1610 {
1611         int i = 0;
1612         wl_iscan_results_t *list;
1613         wl_scan_results_t *results;
1614         wl_bss_info_t UNALIGNED *bi, *bi_new, *bi_next;
1615
1616         dhd_iscan_lock();
1617
1618         DHD_ISCAN(("%s: Scan cache before delete\n", __func__));
1619         dhd_iscan_print_cache(iscan_cur);
1620
1621         if (!iscan_cur)
1622                 goto done;
1623
1624         list = (wl_iscan_results_t *)iscan_cur->iscan_buf;
1625         if (!list)
1626                 goto done;
1627
1628         results = (wl_scan_results_t *)&list->results;
1629         if (!results)
1630                 goto done;
1631
1632         if (results->version != WL_BSS_INFO_VERSION) {
1633                 DHD_ERROR(("%s: results->version %d != WL_BSS_INFO_VERSION\n",
1634                            __func__, results->version));
1635                 goto done;
1636         }
1637
1638         bi = results->bss_info;
1639         for (i = 0; i < results->count; i++) {
1640                 if (!bi)
1641                         break;
1642
1643                 DHD_ISCAN(("%s: Find dups for BSS[%2.2d] %X:%X:%X:%X:%X:%X\n",
1644                            __func__, i, bi->BSSID.octet[0],
1645                            bi->BSSID.octet[1], bi->BSSID.octet[2],
1646                            bi->BSSID.octet[3], bi->BSSID.octet[4],
1647                            bi->BSSID.octet[5]));
1648
1649                 dhd_iscan_delete_bss(dhdp, bi->BSSID.octet, iscan_cur);
1650
1651                 bi = (wl_bss_info_t *)((unsigned long)bi + dtoh32(bi->length));
1652         }
1653
1654 done:
1655         DHD_ISCAN(("%s: Scan cache after delete\n", __func__));
1656         dhd_iscan_print_cache(iscan_cur);
1657         dhd_iscan_unlock();
1658         return 0;
1659 }
1660
1661 void dhd_iscan_ind_scan_confirm(void *dhdp, bool status)
1662 {
1663
1664         dhd_ind_scan_confirm(dhdp, status);
1665 }
1666
1667 int dhd_iscan_request(void *dhdp, u16 action)
1668 {
1669         int rc;
1670         wl_iscan_params_t params;
1671         dhd_pub_t *dhd = dhd_bus_pub(dhdp);
1672         char buf[WLC_IOCTL_SMLEN];
1673
1674         memset(&params, 0, sizeof(wl_iscan_params_t));
1675         memcpy(&params.params.bssid, &ether_bcast, ETH_ALEN);
1676
1677         params.params.bss_type = DOT11_BSSTYPE_ANY;
1678         params.params.scan_type = DOT11_SCANTYPE_ACTIVE;
1679
1680         params.params.nprobes = htod32(-1);
1681         params.params.active_time = htod32(-1);
1682         params.params.passive_time = htod32(-1);
1683         params.params.home_time = htod32(-1);
1684         params.params.channel_num = htod32(0);
1685
1686         params.version = htod32(ISCAN_REQ_VERSION);
1687         params.action = htod16(action);
1688         params.scan_duration = htod16(0);
1689
1690         bcm_mkiovar("iscan", (char *)&params, sizeof(wl_iscan_params_t), buf,
1691                     WLC_IOCTL_SMLEN);
1692         rc = dhd_wl_ioctl(dhdp, WLC_SET_VAR, buf, WLC_IOCTL_SMLEN);
1693
1694         return rc;
1695 }
1696
1697 static int dhd_iscan_get_partial_result(void *dhdp, uint *scan_count)
1698 {
1699         wl_iscan_results_t *list_buf;
1700         wl_iscan_results_t list;
1701         wl_scan_results_t *results;
1702         iscan_buf_t *iscan_cur;
1703         int status = -1;
1704         dhd_pub_t *dhd = dhd_bus_pub(dhdp);
1705         int rc;
1706
1707         iscan_cur = dhd_iscan_allocate_buf(dhd, &iscan_chain);
1708         if (!iscan_cur) {
1709                 DHD_ERROR(("%s: Failed to allocate node\n", __func__));
1710                 dhd_iscan_free_buf(dhdp, 0);
1711                 dhd_iscan_request(dhdp, WL_SCAN_ACTION_ABORT);
1712                 goto fail;
1713         }
1714
1715         dhd_iscan_lock();
1716
1717         memset(iscan_cur->iscan_buf, 0, WLC_IW_ISCAN_MAXLEN);
1718         list_buf = (wl_iscan_results_t *) iscan_cur->iscan_buf;
1719         results = &list_buf->results;
1720         results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE;
1721         results->version = 0;
1722         results->count = 0;
1723
1724         memset(&list, 0, sizeof(list));
1725         list.results.buflen = htod32(WLC_IW_ISCAN_MAXLEN);
1726         bcm_mkiovar("iscanresults", (char *)&list, WL_ISCAN_RESULTS_FIXED_SIZE,
1727                     iscan_cur->iscan_buf, WLC_IW_ISCAN_MAXLEN);
1728         rc = dhd_wl_ioctl(dhdp, WLC_GET_VAR, iscan_cur->iscan_buf,
1729                           WLC_IW_ISCAN_MAXLEN);
1730
1731         results->buflen = dtoh32(results->buflen);
1732         results->version = dtoh32(results->version);
1733         *scan_count = results->count = dtoh32(results->count);
1734         status = dtoh32(list_buf->status);
1735
1736         dhd_iscan_unlock();
1737
1738         if (!(*scan_count))
1739                 dhd_iscan_free_buf(dhdp, iscan_cur);
1740         else
1741                 dhd_iscan_remove_duplicates(dhdp, iscan_cur);
1742
1743 fail:
1744         return status;
1745 }
1746 #endif                          /* SIMPLE_ISCAN */
1747
1748 #ifdef PNO_SUPPORT
1749 int dhd_pno_clean(dhd_pub_t *dhd)
1750 {
1751         char iovbuf[128];
1752         int pfn_enabled = 0;
1753         int iov_len = 0;
1754         int ret;
1755
1756         /* Disable pfn */
1757         iov_len =
1758             bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf));
1759         ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
1760         if (ret >= 0) {
1761                 /* clear pfn */
1762                 iov_len = bcm_mkiovar("pfnclear", 0, 0, iovbuf, sizeof(iovbuf));
1763                 if (iov_len) {
1764                         ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf,
1765                                         iov_len);
1766                         if (ret < 0) {
1767                                 DHD_ERROR(("%s failed code %d\n", __func__,
1768                                            ret));
1769                         }
1770                 } else {
1771                         ret = -1;
1772                         DHD_ERROR(("%s failed code %d\n", __func__, iov_len));
1773                 }
1774         } else
1775                 DHD_ERROR(("%s failed code %d\n", __func__, ret));
1776
1777         return ret;
1778 }
1779
1780 int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled)
1781 {
1782         char iovbuf[128];
1783         int ret = -1;
1784
1785         if ((!dhd) && ((pfn_enabled != 0) || (pfn_enabled != 1))) {
1786                 DHD_ERROR(("%s error exit\n", __func__));
1787                 return ret;
1788         }
1789
1790         /* Enable/disable PNO */
1791         ret = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf,
1792                         sizeof(iovbuf));
1793         if (ret > 0) {
1794                 ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf,
1795                                 sizeof(iovbuf));
1796                 if (ret < 0) {
1797                         DHD_ERROR(("%s failed for error=%d\n", __func__, ret));
1798                         return ret;
1799                 } else {
1800                         dhd->pno_enable = pfn_enabled;
1801                         DHD_TRACE(("%s set pno as %d\n", __func__,
1802                                    dhd->pno_enable));
1803                 }
1804         } else
1805                 DHD_ERROR(("%s failed err=%d\n", __func__, ret));
1806
1807         return ret;
1808 }
1809
1810 /* Function to execute combined scan */
1811 int
1812 dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t *ssids_local, int nssid, unsigned char scan_fr)
1813 {
1814         int err = -1;
1815         char iovbuf[128];
1816         int k, i;
1817         wl_pfn_param_t pfn_param;
1818         wl_pfn_t pfn_element;
1819
1820         DHD_TRACE(("%s nssid=%d nchan=%d\n", __func__, nssid, scan_fr));
1821
1822         if ((!dhd) && (!ssids_local)) {
1823                 DHD_ERROR(("%s error exit\n", __func__));
1824                 err = -1;
1825         }
1826
1827         /* Check for broadcast ssid */
1828         for (k = 0; k < nssid; k++) {
1829                 if (!ssids_local[k].SSID_len) {
1830                         DHD_ERROR(("%d: Broadcast SSID is ilegal for PNO "
1831                                 "setting\n", k));
1832                         return err;
1833                 }
1834         }
1835 /* #define  PNO_DUMP 1 */
1836 #ifdef PNO_DUMP
1837         {
1838                 int j;
1839                 for (j = 0; j < nssid; j++) {
1840                         DHD_ERROR(("%d: scan  for  %s size =%d\n", j,
1841                                    ssids_local[j].SSID,
1842                                    ssids_local[j].SSID_len));
1843                 }
1844         }
1845 #endif                          /* PNO_DUMP */
1846
1847         /* clean up everything */
1848         err = dhd_pno_clean(dhd);
1849         if (err < 0) {
1850                 DHD_ERROR(("%s failed error=%d\n", __func__, err));
1851                 return err;
1852         }
1853         memset(&pfn_param, 0, sizeof(pfn_param));
1854         memset(&pfn_element, 0, sizeof(pfn_element));
1855
1856         /* set pfn parameters */
1857         pfn_param.version = htod32(PFN_VERSION);
1858         pfn_param.flags = htod16((PFN_LIST_ORDER << SORT_CRITERIA_BIT));
1859
1860         /* set up pno scan fr */
1861         if (scan_fr != 0)
1862                 pfn_param.scan_freq = htod32(scan_fr);
1863
1864         bcm_mkiovar("pfn_set", (char *)&pfn_param, sizeof(pfn_param), iovbuf,
1865                     sizeof(iovbuf));
1866         dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
1867
1868         /* set all pfn ssid */
1869         for (i = 0; i < nssid; i++) {
1870
1871                 pfn_element.bss_type = htod32(DOT11_BSSTYPE_INFRASTRUCTURE);
1872                 pfn_element.auth = (DOT11_OPEN_SYSTEM);
1873                 pfn_element.wpa_auth = htod32(WPA_AUTH_PFN_ANY);
1874                 pfn_element.wsec = htod32(0);
1875                 pfn_element.infra = htod32(1);
1876
1877                 memcpy((char *)pfn_element.ssid.SSID, ssids_local[i].SSID,
1878                        ssids_local[i].SSID_len);
1879                 pfn_element.ssid.SSID_len = ssids_local[i].SSID_len;
1880
1881                 err = bcm_mkiovar("pfn_add", (char *)&pfn_element,
1882                                 sizeof(pfn_element), iovbuf, sizeof(iovbuf));
1883                 if (err > 0) {
1884                         err = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf,
1885                                         sizeof(iovbuf));
1886                         if (err < 0) {
1887                                 DHD_ERROR(("%s failed for i=%d error=%d\n",
1888                                            __func__, i, err));
1889                                 return err;
1890                         }
1891                 } else
1892                         DHD_ERROR(("%s failed err=%d\n", __func__, err));
1893         }
1894
1895         /* Enable PNO */
1896         /* dhd_pno_enable(dhd, 1); */
1897         return err;
1898 }
1899
1900 int dhd_pno_get_status(dhd_pub_t *dhd)
1901 {
1902         int ret = -1;
1903
1904         if (!dhd)
1905                 return ret;
1906         else
1907                 return dhd->pno_enable;
1908 }
1909
1910 #endif                          /* PNO_SUPPORT */
1911
1912 /* Androd ComboSCAN support */