PageRenderTime 189ms CodeModel.GetById 29ms app.highlight 133ms RepoModel.GetById 1ms app.codeStats 1ms

/drivers/staging/brcm80211/brcmfmac/dhd_common.c

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