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