/kern_2.6.32/drivers/net/wireless/bcm4329/src/dhd/sys/dhd_common.c

http://omnia2droid.googlecode.com/ · C · 1732 lines · 1332 code · 268 blank · 132 comment · 228 complexity · e3f649e15e9d35437aca1b2d5d040329 MD5 · raw file

  1. /*
  2. * Broadcom Dongle Host Driver (DHD), common DHD core.
  3. *
  4. * Copyright (C) 1999-2010, Broadcom Corporation
  5. *
  6. * Unless you and Broadcom execute a separate written software license
  7. * agreement governing use of this software, this software is licensed to you
  8. * under the terms of the GNU General Public License version 2 (the "GPL"),
  9. * available at http://www.broadcom.com/licenses/GPLv2.php, with the
  10. * following added to such license:
  11. *
  12. * As a special exception, the copyright holders of this software give you
  13. * permission to link this software with independent modules, and to copy and
  14. * distribute the resulting executable under terms of your choice, provided that
  15. * you also meet, for each linked independent module, the terms and conditions of
  16. * the license of that module. An independent module is a module which is not
  17. * derived from this software. The special exception does not apply to any
  18. * modifications of the software.
  19. *
  20. * Notwithstanding the above, under no circumstances may you combine this
  21. * software in any way with any other Broadcom software provided under a license
  22. * other than the GPL, without Broadcom's express prior written consent.
  23. *
  24. * $Id: dhd_common.c,v 1.5.6.8.2.6.6.47 2010/04/23 17:27:41 Exp $
  25. */
  26. #include <typedefs.h>
  27. #include <osl.h>
  28. #include <epivers.h>
  29. #include <bcmutils.h>
  30. #include <bcmendian.h>
  31. #include <dngl_stats.h>
  32. #include <dhd.h>
  33. #include <dhd_bus.h>
  34. #include <dhd_proto.h>
  35. #include <dhd_dbg.h>
  36. #include <msgtrace.h>
  37. #include <wlioctl.h>
  38. int dhd_msg_level;
  39. char fw_path[MOD_PARAM_PATHLEN];
  40. char nv_path[MOD_PARAM_PATHLEN];
  41. /* Last connection success/failure status */
  42. uint32 dhd_conn_event;
  43. uint32 dhd_conn_status;
  44. uint32 dhd_conn_reason;
  45. #define htod32(i) i
  46. #define htod16(i) i
  47. #define dtoh32(i) i
  48. #define dtoh16(i) i
  49. #define SOFTAP
  50. #ifdef SOFTAP
  51. extern bool ap_fw_loaded;
  52. #endif
  53. extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
  54. extern void dhd_ind_scan_confirm(void *h, bool status);
  55. extern int dhd_wl_ioctl(dhd_pub_t *dhd, uint cmd, char *buf, uint buflen);
  56. void dhd_iscan_lock(void);
  57. void dhd_iscan_unlock(void);
  58. /* Packet alignment for most efficient SDIO (can change based on platform) */
  59. #ifndef DHD_SDALIGN
  60. #define DHD_SDALIGN 32
  61. #endif
  62. #if !ISPOWEROF2(DHD_SDALIGN)
  63. #error DHD_SDALIGN is not a power of 2!
  64. #endif
  65. #ifdef DHD_DEBUG
  66. const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR "\nCompiled on "
  67. __DATE__ " at " __TIME__;
  68. #else
  69. const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR;
  70. #endif
  71. void dhd_set_timer(void *bus, uint wdtick);
  72. /* IOVar table */
  73. enum {
  74. IOV_VERSION = 1,
  75. IOV_MSGLEVEL,
  76. IOV_BCMERRORSTR,
  77. IOV_BCMERROR,
  78. IOV_WDTICK,
  79. IOV_DUMP,
  80. #ifdef DHD_DEBUG
  81. IOV_CONS,
  82. IOV_DCONSOLE_POLL,
  83. #endif
  84. IOV_CLEARCOUNTS,
  85. IOV_LOGDUMP,
  86. IOV_LOGCAL,
  87. IOV_LOGSTAMP,
  88. IOV_GPIOOB,
  89. IOV_IOCTLTIMEOUT,
  90. IOV_LAST
  91. };
  92. const bcm_iovar_t dhd_iovars[] = {
  93. {"version", IOV_VERSION, 0, IOVT_BUFFER, sizeof(dhd_version) },
  94. #ifdef DHD_DEBUG
  95. {"msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 },
  96. #endif /* DHD_DEBUG */
  97. {"bcmerrorstr", IOV_BCMERRORSTR, 0, IOVT_BUFFER, BCME_STRLEN },
  98. {"bcmerror", IOV_BCMERROR, 0, IOVT_INT8, 0 },
  99. {"wdtick", IOV_WDTICK, 0, IOVT_UINT32, 0 },
  100. {"dump", IOV_DUMP, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN },
  101. #ifdef DHD_DEBUG
  102. {"dconpoll", IOV_DCONSOLE_POLL, 0, IOVT_UINT32, 0 },
  103. {"cons", IOV_CONS, 0, IOVT_BUFFER, 0 },
  104. #endif
  105. {"clearcounts", IOV_CLEARCOUNTS, 0, IOVT_VOID, 0 },
  106. {"gpioob", IOV_GPIOOB, 0, IOVT_UINT32, 0 },
  107. {"ioctl_timeout", IOV_IOCTLTIMEOUT, 0, IOVT_UINT32, 0 },
  108. {NULL, 0, 0, 0, 0 }
  109. };
  110. void
  111. dhd_common_init(void)
  112. {
  113. /* Init global variables at run-time, not as part of the declaration.
  114. * This is required to support init/de-init of the driver. Initialization
  115. * of globals as part of the declaration results in non-deterministic
  116. * behaviour since the value of the globals may be different on the
  117. * first time that the driver is initialized vs subsequent initializations.
  118. */
  119. dhd_msg_level = DHD_ERROR_VAL/* | DHD_TRACE_VAL*/;
  120. #ifdef CONFIG_BCM4329_FW_PATH
  121. strncpy(fw_path, CONFIG_BCM4329_FW_PATH, MOD_PARAM_PATHLEN-1);
  122. #else
  123. fw_path[0] = '\0';
  124. #endif
  125. #ifdef CONFIG_BCM4329_NVRAM_PATH
  126. strncpy(nv_path, CONFIG_BCM4329_NVRAM_PATH, MOD_PARAM_PATHLEN-1);
  127. #else
  128. nv_path[0] = '\0';
  129. #endif
  130. }
  131. static int
  132. dhd_dump(dhd_pub_t *dhdp, char *buf, int buflen)
  133. {
  134. char eabuf[ETHER_ADDR_STR_LEN];
  135. struct bcmstrbuf b;
  136. struct bcmstrbuf *strbuf = &b;
  137. bcm_binit(strbuf, buf, buflen);
  138. /* Base DHD info */
  139. bcm_bprintf(strbuf, "%s\n", dhd_version);
  140. bcm_bprintf(strbuf, "\n");
  141. bcm_bprintf(strbuf, "pub.up %d pub.txoff %d pub.busstate %d\n",
  142. dhdp->up, dhdp->txoff, dhdp->busstate);
  143. bcm_bprintf(strbuf, "pub.hdrlen %d pub.maxctl %d pub.rxsz %d\n",
  144. dhdp->hdrlen, dhdp->maxctl, dhdp->rxsz);
  145. bcm_bprintf(strbuf, "pub.iswl %d pub.drv_version %ld pub.mac %s\n",
  146. dhdp->iswl, dhdp->drv_version, bcm_ether_ntoa(&dhdp->mac, eabuf));
  147. bcm_bprintf(strbuf, "pub.bcmerror %d tickcnt %d\n", dhdp->bcmerror, dhdp->tickcnt);
  148. bcm_bprintf(strbuf, "dongle stats:\n");
  149. bcm_bprintf(strbuf, "tx_packets %ld tx_bytes %ld tx_errors %ld tx_dropped %ld\n",
  150. dhdp->dstats.tx_packets, dhdp->dstats.tx_bytes,
  151. dhdp->dstats.tx_errors, dhdp->dstats.tx_dropped);
  152. bcm_bprintf(strbuf, "rx_packets %ld rx_bytes %ld rx_errors %ld rx_dropped %ld\n",
  153. dhdp->dstats.rx_packets, dhdp->dstats.rx_bytes,
  154. dhdp->dstats.rx_errors, dhdp->dstats.rx_dropped);
  155. bcm_bprintf(strbuf, "multicast %ld\n", dhdp->dstats.multicast);
  156. bcm_bprintf(strbuf, "bus stats:\n");
  157. bcm_bprintf(strbuf, "tx_packets %ld tx_multicast %ld tx_errors %ld\n",
  158. dhdp->tx_packets, dhdp->tx_multicast, dhdp->tx_errors);
  159. bcm_bprintf(strbuf, "tx_ctlpkts %ld tx_ctlerrs %ld\n",
  160. dhdp->tx_ctlpkts, dhdp->tx_ctlerrs);
  161. bcm_bprintf(strbuf, "rx_packets %ld rx_multicast %ld rx_errors %ld \n",
  162. dhdp->rx_packets, dhdp->rx_multicast, dhdp->rx_errors);
  163. bcm_bprintf(strbuf, "rx_ctlpkts %ld rx_ctlerrs %ld rx_dropped %ld rx_flushed %ld\n",
  164. dhdp->rx_ctlpkts, dhdp->rx_ctlerrs, dhdp->rx_dropped, dhdp->rx_flushed);
  165. bcm_bprintf(strbuf, "rx_readahead_cnt %ld tx_realloc %ld fc_packets %ld\n",
  166. dhdp->rx_readahead_cnt, dhdp->tx_realloc, dhdp->fc_packets);
  167. bcm_bprintf(strbuf, "wd_dpc_sched %ld\n", dhdp->wd_dpc_sched);
  168. bcm_bprintf(strbuf, "\n");
  169. /* Add any prot info */
  170. dhd_prot_dump(dhdp, strbuf);
  171. bcm_bprintf(strbuf, "\n");
  172. /* Add any bus info */
  173. dhd_bus_dump(dhdp, strbuf);
  174. return (!strbuf->size ? BCME_BUFTOOSHORT : 0);
  175. }
  176. static int
  177. dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const char *name,
  178. void *params, int plen, void *arg, int len, int val_size)
  179. {
  180. int bcmerror = 0;
  181. int32 int_val = 0;
  182. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  183. if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0)
  184. goto exit;
  185. if (plen >= (int)sizeof(int_val))
  186. bcopy(params, &int_val, sizeof(int_val));
  187. switch (actionid) {
  188. case IOV_GVAL(IOV_VERSION):
  189. /* Need to have checked buffer length */
  190. strncpy((char*)arg, dhd_version, len);
  191. break;
  192. case IOV_GVAL(IOV_MSGLEVEL):
  193. int_val = (int32)dhd_msg_level;
  194. bcopy(&int_val, arg, val_size);
  195. break;
  196. case IOV_SVAL(IOV_MSGLEVEL):
  197. dhd_msg_level = int_val;
  198. break;
  199. case IOV_GVAL(IOV_BCMERRORSTR):
  200. strncpy((char *)arg, bcmerrorstr(dhd_pub->bcmerror), BCME_STRLEN);
  201. ((char *)arg)[BCME_STRLEN - 1] = 0x00;
  202. break;
  203. case IOV_GVAL(IOV_BCMERROR):
  204. int_val = (int32)dhd_pub->bcmerror;
  205. bcopy(&int_val, arg, val_size);
  206. break;
  207. case IOV_GVAL(IOV_WDTICK):
  208. int_val = (int32)dhd_watchdog_ms;
  209. bcopy(&int_val, arg, val_size);
  210. break;
  211. case IOV_SVAL(IOV_WDTICK):
  212. if (!dhd_pub->up) {
  213. bcmerror = BCME_NOTUP;
  214. break;
  215. }
  216. dhd_os_wd_timer(dhd_pub, (uint)int_val);
  217. break;
  218. case IOV_GVAL(IOV_DUMP):
  219. bcmerror = dhd_dump(dhd_pub, arg, len);
  220. break;
  221. #ifdef DHD_DEBUG
  222. case IOV_GVAL(IOV_DCONSOLE_POLL):
  223. int_val = (int32)dhd_console_ms;
  224. bcopy(&int_val, arg, val_size);
  225. break;
  226. case IOV_SVAL(IOV_DCONSOLE_POLL):
  227. dhd_console_ms = (uint)int_val;
  228. break;
  229. case IOV_SVAL(IOV_CONS):
  230. if (len > 0)
  231. bcmerror = dhd_bus_console_in(dhd_pub, arg, len - 1);
  232. break;
  233. #endif
  234. case IOV_SVAL(IOV_CLEARCOUNTS):
  235. dhd_pub->tx_packets = dhd_pub->rx_packets = 0;
  236. dhd_pub->tx_errors = dhd_pub->rx_errors = 0;
  237. dhd_pub->tx_ctlpkts = dhd_pub->rx_ctlpkts = 0;
  238. dhd_pub->tx_ctlerrs = dhd_pub->rx_ctlerrs = 0;
  239. dhd_pub->rx_dropped = 0;
  240. dhd_pub->rx_readahead_cnt = 0;
  241. dhd_pub->tx_realloc = 0;
  242. dhd_pub->wd_dpc_sched = 0;
  243. memset(&dhd_pub->dstats, 0, sizeof(dhd_pub->dstats));
  244. dhd_bus_clearcounts(dhd_pub);
  245. break;
  246. case IOV_GVAL(IOV_IOCTLTIMEOUT): {
  247. int_val = (int32)dhd_os_get_ioctl_resp_timeout();
  248. bcopy(&int_val, arg, sizeof(int_val));
  249. break;
  250. }
  251. case IOV_SVAL(IOV_IOCTLTIMEOUT): {
  252. if (int_val <= 0)
  253. bcmerror = BCME_BADARG;
  254. else
  255. dhd_os_set_ioctl_resp_timeout((unsigned int)int_val);
  256. break;
  257. }
  258. default:
  259. bcmerror = BCME_UNSUPPORTED;
  260. break;
  261. }
  262. exit:
  263. return bcmerror;
  264. }
  265. /* Store the status of a connection attempt for later retrieval by an iovar */
  266. void
  267. dhd_store_conn_status(uint32 event, uint32 status, uint32 reason)
  268. {
  269. /* Do not overwrite a WLC_E_PRUNE with a WLC_E_SET_SSID
  270. * because an encryption/rsn mismatch results in both events, and
  271. * the important information is in the WLC_E_PRUNE.
  272. */
  273. if (!(event == WLC_E_SET_SSID && status == WLC_E_STATUS_FAIL &&
  274. dhd_conn_event == WLC_E_PRUNE)) {
  275. dhd_conn_event = event;
  276. dhd_conn_status = status;
  277. dhd_conn_reason = reason;
  278. }
  279. }
  280. bool
  281. dhd_prec_enq(dhd_pub_t *dhdp, struct pktq *q, void *pkt, int prec)
  282. {
  283. void *p;
  284. int eprec = -1; /* precedence to evict from */
  285. bool discard_oldest;
  286. /* Fast case, precedence queue is not full and we are also not
  287. * exceeding total queue length
  288. */
  289. if (!pktq_pfull(q, prec) && !pktq_full(q)) {
  290. pktq_penq(q, prec, pkt);
  291. return TRUE;
  292. }
  293. /* Determine precedence from which to evict packet, if any */
  294. if (pktq_pfull(q, prec))
  295. eprec = prec;
  296. else if (pktq_full(q)) {
  297. p = pktq_peek_tail(q, &eprec);
  298. ASSERT(p);
  299. if (eprec > prec)
  300. return FALSE;
  301. }
  302. /* Evict if needed */
  303. if (eprec >= 0) {
  304. /* Detect queueing to unconfigured precedence */
  305. ASSERT(!pktq_pempty(q, eprec));
  306. discard_oldest = AC_BITMAP_TST(dhdp->wme_dp, eprec);
  307. if (eprec == prec && !discard_oldest)
  308. return FALSE; /* refuse newer (incoming) packet */
  309. /* Evict packet according to discard policy */
  310. p = discard_oldest ? pktq_pdeq(q, eprec) : pktq_pdeq_tail(q, eprec);
  311. if (p == NULL) {
  312. DHD_ERROR(("%s: pktq_penq() failed, oldest %d.",
  313. __FUNCTION__, discard_oldest));
  314. ASSERT(p);
  315. }
  316. PKTFREE(dhdp->osh, p, TRUE);
  317. }
  318. /* Enqueue */
  319. p = pktq_penq(q, prec, pkt);
  320. if (p == NULL) {
  321. DHD_ERROR(("%s: pktq_penq() failed.", __FUNCTION__));
  322. ASSERT(p);
  323. }
  324. return TRUE;
  325. }
  326. static int
  327. dhd_iovar_op(dhd_pub_t *dhd_pub, const char *name,
  328. void *params, int plen, void *arg, int len, bool set)
  329. {
  330. int bcmerror = 0;
  331. int val_size;
  332. const bcm_iovar_t *vi = NULL;
  333. uint32 actionid;
  334. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  335. ASSERT(name);
  336. ASSERT(len >= 0);
  337. /* Get MUST have return space */
  338. ASSERT(set || (arg && len));
  339. /* Set does NOT take qualifiers */
  340. ASSERT(!set || (!params && !plen));
  341. if ((vi = bcm_iovar_lookup(dhd_iovars, name)) == NULL) {
  342. bcmerror = BCME_UNSUPPORTED;
  343. goto exit;
  344. }
  345. DHD_CTL(("%s: %s %s, len %d plen %d\n", __FUNCTION__,
  346. name, (set ? "set" : "get"), len, plen));
  347. /* set up 'params' pointer in case this is a set command so that
  348. * the convenience int and bool code can be common to set and get
  349. */
  350. if (params == NULL) {
  351. params = arg;
  352. plen = len;
  353. }
  354. if (vi->type == IOVT_VOID)
  355. val_size = 0;
  356. else if (vi->type == IOVT_BUFFER)
  357. val_size = len;
  358. else
  359. /* all other types are integer sized */
  360. val_size = sizeof(int);
  361. actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
  362. bcmerror = dhd_doiovar(dhd_pub, vi, actionid, name, params, plen, arg, len, val_size);
  363. exit:
  364. return bcmerror;
  365. }
  366. int
  367. dhd_ioctl(dhd_pub_t *dhd_pub, dhd_ioctl_t *ioc, void *buf, uint buflen)
  368. {
  369. int bcmerror = 0;
  370. DHD_TRACE(("%s: Enter\n", __FUNCTION__));
  371. if (!buf) return BCME_BADARG;
  372. switch (ioc->cmd) {
  373. case DHD_GET_MAGIC:
  374. if (buflen < sizeof(int))
  375. bcmerror = BCME_BUFTOOSHORT;
  376. else
  377. *(int*)buf = DHD_IOCTL_MAGIC;
  378. break;
  379. case DHD_GET_VERSION:
  380. if (buflen < sizeof(int))
  381. bcmerror = -BCME_BUFTOOSHORT;
  382. else
  383. *(int*)buf = DHD_IOCTL_VERSION;
  384. break;
  385. case DHD_GET_VAR:
  386. case DHD_SET_VAR: {
  387. char *arg;
  388. uint arglen;
  389. /* scan past the name to any arguments */
  390. for (arg = buf, arglen = buflen; *arg && arglen; arg++, arglen--);
  391. if (*arg) {
  392. bcmerror = BCME_BUFTOOSHORT;
  393. break;
  394. }
  395. /* account for the NUL terminator */
  396. arg++, arglen--;
  397. /* call with the appropriate arguments */
  398. if (ioc->cmd == DHD_GET_VAR)
  399. bcmerror = dhd_iovar_op(dhd_pub, buf, arg, arglen,
  400. buf, buflen, IOV_GET);
  401. else
  402. bcmerror = dhd_iovar_op(dhd_pub, buf, NULL, 0, arg, arglen, IOV_SET);
  403. if (bcmerror != BCME_UNSUPPORTED)
  404. break;
  405. /* not in generic table, try protocol module */
  406. if (ioc->cmd == DHD_GET_VAR)
  407. bcmerror = dhd_prot_iovar_op(dhd_pub, buf, arg,
  408. arglen, buf, buflen, IOV_GET);
  409. else
  410. bcmerror = dhd_prot_iovar_op(dhd_pub, buf,
  411. NULL, 0, arg, arglen, IOV_SET);
  412. if (bcmerror != BCME_UNSUPPORTED)
  413. break;
  414. /* if still not found, try bus module */
  415. if (ioc->cmd == DHD_GET_VAR)
  416. bcmerror = dhd_bus_iovar_op(dhd_pub, buf,
  417. arg, arglen, buf, buflen, IOV_GET);
  418. else
  419. bcmerror = dhd_bus_iovar_op(dhd_pub, buf,
  420. NULL, 0, arg, arglen, IOV_SET);
  421. break;
  422. }
  423. default:
  424. bcmerror = BCME_UNSUPPORTED;
  425. }
  426. return bcmerror;
  427. }
  428. #ifdef APSTA_PINGTEST
  429. struct ether_addr guest_eas[MAX_GUEST];
  430. #endif
  431. #ifdef SHOW_EVENTS
  432. static void
  433. wl_show_host_event(wl_event_msg_t *event, void *event_data)
  434. {
  435. uint i, status, reason;
  436. bool group = FALSE, flush_txq = FALSE, link = FALSE;
  437. char *auth_str, *event_name;
  438. uchar *buf;
  439. char err_msg[256], eabuf[ETHER_ADDR_STR_LEN];
  440. static struct {uint event; char *event_name;} event_names[] = {
  441. {WLC_E_SET_SSID, "SET_SSID"},
  442. {WLC_E_JOIN, "JOIN"},
  443. {WLC_E_START, "START"},
  444. {WLC_E_AUTH, "AUTH"},
  445. {WLC_E_AUTH_IND, "AUTH_IND"},
  446. {WLC_E_DEAUTH, "DEAUTH"},
  447. {WLC_E_DEAUTH_IND, "DEAUTH_IND"},
  448. {WLC_E_ASSOC, "ASSOC"},
  449. {WLC_E_ASSOC_IND, "ASSOC_IND"},
  450. {WLC_E_REASSOC, "REASSOC"},
  451. {WLC_E_REASSOC_IND, "REASSOC_IND"},
  452. {WLC_E_DISASSOC, "DISASSOC"},
  453. {WLC_E_DISASSOC_IND, "DISASSOC_IND"},
  454. {WLC_E_QUIET_START, "START_QUIET"},
  455. {WLC_E_QUIET_END, "END_QUIET"},
  456. {WLC_E_BEACON_RX, "BEACON_RX"},
  457. {WLC_E_LINK, "LINK"},
  458. {WLC_E_MIC_ERROR, "MIC_ERROR"},
  459. {WLC_E_NDIS_LINK, "NDIS_LINK"},
  460. {WLC_E_ROAM, "ROAM"},
  461. {WLC_E_TXFAIL, "TXFAIL"},
  462. {WLC_E_PMKID_CACHE, "PMKID_CACHE"},
  463. {WLC_E_RETROGRADE_TSF, "RETROGRADE_TSF"},
  464. {WLC_E_PRUNE, "PRUNE"},
  465. {WLC_E_AUTOAUTH, "AUTOAUTH"},
  466. {WLC_E_EAPOL_MSG, "EAPOL_MSG"},
  467. {WLC_E_SCAN_COMPLETE, "SCAN_COMPLETE"},
  468. {WLC_E_ADDTS_IND, "ADDTS_IND"},
  469. {WLC_E_DELTS_IND, "DELTS_IND"},
  470. {WLC_E_BCNSENT_IND, "BCNSENT_IND"},
  471. {WLC_E_BCNRX_MSG, "BCNRX_MSG"},
  472. {WLC_E_BCNLOST_MSG, "BCNLOST_MSG"},
  473. {WLC_E_ROAM_PREP, "ROAM_PREP"},
  474. {WLC_E_PFN_NET_FOUND, "PNO_NET_FOUND"},
  475. {WLC_E_PFN_NET_LOST, "PNO_NET_LOST"},
  476. {WLC_E_RESET_COMPLETE, "RESET_COMPLETE"},
  477. {WLC_E_JOIN_START, "JOIN_START"},
  478. {WLC_E_ROAM_START, "ROAM_START"},
  479. {WLC_E_ASSOC_START, "ASSOC_START"},
  480. {WLC_E_IBSS_ASSOC, "IBSS_ASSOC"},
  481. {WLC_E_RADIO, "RADIO"},
  482. {WLC_E_PSM_WATCHDOG, "PSM_WATCHDOG"},
  483. {WLC_E_PROBREQ_MSG, "PROBREQ_MSG"},
  484. {WLC_E_SCAN_CONFIRM_IND, "SCAN_CONFIRM_IND"},
  485. {WLC_E_PSK_SUP, "PSK_SUP"},
  486. {WLC_E_COUNTRY_CODE_CHANGED, "COUNTRY_CODE_CHANGED"},
  487. {WLC_E_EXCEEDED_MEDIUM_TIME, "EXCEEDED_MEDIUM_TIME"},
  488. {WLC_E_ICV_ERROR, "ICV_ERROR"},
  489. {WLC_E_UNICAST_DECODE_ERROR, "UNICAST_DECODE_ERROR"},
  490. {WLC_E_MULTICAST_DECODE_ERROR, "MULTICAST_DECODE_ERROR"},
  491. {WLC_E_TRACE, "TRACE"},
  492. {WLC_E_ACTION_FRAME, "ACTION FRAME"},
  493. {WLC_E_ACTION_FRAME_COMPLETE, "ACTION FRAME TX COMPLETE"},
  494. {WLC_E_IF, "IF"},
  495. {WLC_E_RSSI, "RSSI"},
  496. {WLC_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE"}
  497. };
  498. uint event_type, flags, auth_type, datalen;
  499. event_type = ntoh32(event->event_type);
  500. flags = ntoh16(event->flags);
  501. status = ntoh32(event->status);
  502. reason = ntoh32(event->reason);
  503. auth_type = ntoh32(event->auth_type);
  504. datalen = ntoh32(event->datalen);
  505. /* debug dump of event messages */
  506. sprintf(eabuf, "%02x:%02x:%02x:%02x:%02x:%02x",
  507. (uchar)event->addr.octet[0]&0xff,
  508. (uchar)event->addr.octet[1]&0xff,
  509. (uchar)event->addr.octet[2]&0xff,
  510. (uchar)event->addr.octet[3]&0xff,
  511. (uchar)event->addr.octet[4]&0xff,
  512. (uchar)event->addr.octet[5]&0xff);
  513. event_name = "UNKNOWN";
  514. for (i = 0; i < ARRAYSIZE(event_names); i++) {
  515. if (event_names[i].event == event_type)
  516. event_name = event_names[i].event_name;
  517. }
  518. DHD_EVENT(("EVENT: %s, event ID = %d\n", event_name, event_type));
  519. if (flags & WLC_EVENT_MSG_LINK)
  520. link = TRUE;
  521. if (flags & WLC_EVENT_MSG_GROUP)
  522. group = TRUE;
  523. if (flags & WLC_EVENT_MSG_FLUSHTXQ)
  524. flush_txq = TRUE;
  525. switch (event_type) {
  526. case WLC_E_START:
  527. case WLC_E_DEAUTH:
  528. case WLC_E_DISASSOC:
  529. DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
  530. break;
  531. case WLC_E_ASSOC_IND:
  532. case WLC_E_REASSOC_IND:
  533. #ifdef APSTA_PINGTEST
  534. {
  535. int i;
  536. for (i = 0; i < MAX_GUEST; ++i)
  537. if (ETHER_ISNULLADDR(&guest_eas[i]))
  538. break;
  539. if (i < MAX_GUEST)
  540. bcopy(event->addr.octet, guest_eas[i].octet, ETHER_ADDR_LEN);
  541. }
  542. #endif /* APSTA_PINGTEST */
  543. DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
  544. break;
  545. case WLC_E_ASSOC:
  546. case WLC_E_REASSOC:
  547. if (status == WLC_E_STATUS_SUCCESS) {
  548. DHD_EVENT(("MACEVENT: %s, MAC %s, SUCCESS\n", event_name, eabuf));
  549. } else if (status == WLC_E_STATUS_TIMEOUT) {
  550. DHD_EVENT(("MACEVENT: %s, MAC %s, TIMEOUT\n", event_name, eabuf));
  551. } else if (status == WLC_E_STATUS_FAIL) {
  552. DHD_EVENT(("MACEVENT: %s, MAC %s, FAILURE, reason %d\n",
  553. event_name, eabuf, (int)reason));
  554. } else {
  555. DHD_EVENT(("MACEVENT: %s, MAC %s, unexpected status %d\n",
  556. event_name, eabuf, (int)status));
  557. }
  558. break;
  559. case WLC_E_DEAUTH_IND:
  560. case WLC_E_DISASSOC_IND:
  561. #ifdef APSTA_PINGTEST
  562. {
  563. int i;
  564. for (i = 0; i < MAX_GUEST; ++i) {
  565. if (bcmp(guest_eas[i].octet, event->addr.octet,
  566. ETHER_ADDR_LEN) == 0) {
  567. bzero(guest_eas[i].octet, ETHER_ADDR_LEN);
  568. break;
  569. }
  570. }
  571. }
  572. #endif /* APSTA_PINGTEST */
  573. DHD_EVENT(("MACEVENT: %s, MAC %s, reason %d\n", event_name, eabuf, (int)reason));
  574. break;
  575. case WLC_E_AUTH:
  576. case WLC_E_AUTH_IND:
  577. if (auth_type == DOT11_OPEN_SYSTEM)
  578. auth_str = "Open System";
  579. else if (auth_type == DOT11_SHARED_KEY)
  580. auth_str = "Shared Key";
  581. else {
  582. sprintf(err_msg, "AUTH unknown: %d", (int)auth_type);
  583. auth_str = err_msg;
  584. }
  585. if (event_type == WLC_E_AUTH_IND) {
  586. DHD_EVENT(("MACEVENT: %s, MAC %s, %s\n", event_name, eabuf, auth_str));
  587. } else if (status == WLC_E_STATUS_SUCCESS) {
  588. DHD_EVENT(("MACEVENT: %s, MAC %s, %s, SUCCESS\n",
  589. event_name, eabuf, auth_str));
  590. } else if (status == WLC_E_STATUS_TIMEOUT) {
  591. DHD_EVENT(("MACEVENT: %s, MAC %s, %s, TIMEOUT\n",
  592. event_name, eabuf, auth_str));
  593. } else if (status == WLC_E_STATUS_FAIL) {
  594. DHD_EVENT(("MACEVENT: %s, MAC %s, %s, FAILURE, reason %d\n",
  595. event_name, eabuf, auth_str, (int)reason));
  596. }
  597. break;
  598. case WLC_E_JOIN:
  599. case WLC_E_ROAM:
  600. case WLC_E_SET_SSID:
  601. if (status == WLC_E_STATUS_SUCCESS) {
  602. DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
  603. } else if (status == WLC_E_STATUS_FAIL) {
  604. DHD_EVENT(("MACEVENT: %s, failed\n", event_name));
  605. } else if (status == WLC_E_STATUS_NO_NETWORKS) {
  606. DHD_EVENT(("MACEVENT: %s, no networks found\n", event_name));
  607. } else {
  608. DHD_EVENT(("MACEVENT: %s, unexpected status %d\n",
  609. event_name, (int)status));
  610. }
  611. break;
  612. case WLC_E_BEACON_RX:
  613. if (status == WLC_E_STATUS_SUCCESS) {
  614. DHD_EVENT(("MACEVENT: %s, SUCCESS\n", event_name));
  615. } else if (status == WLC_E_STATUS_FAIL) {
  616. DHD_EVENT(("MACEVENT: %s, FAIL\n", event_name));
  617. } else {
  618. DHD_EVENT(("MACEVENT: %s, status %d\n", event_name, status));
  619. }
  620. break;
  621. case WLC_E_LINK:
  622. DHD_EVENT(("MACEVENT: %s %s\n", event_name, link?"UP":"DOWN"));
  623. break;
  624. case WLC_E_MIC_ERROR:
  625. DHD_EVENT(("MACEVENT: %s, MAC %s, Group %d, Flush %d\n",
  626. event_name, eabuf, group, flush_txq));
  627. break;
  628. case WLC_E_ICV_ERROR:
  629. case WLC_E_UNICAST_DECODE_ERROR:
  630. case WLC_E_MULTICAST_DECODE_ERROR:
  631. DHD_EVENT(("MACEVENT: %s, MAC %s\n",
  632. event_name, eabuf));
  633. break;
  634. case WLC_E_TXFAIL:
  635. DHD_EVENT(("MACEVENT: %s, RA %s\n", event_name, eabuf));
  636. break;
  637. case WLC_E_SCAN_COMPLETE:
  638. case WLC_E_PMKID_CACHE:
  639. DHD_EVENT(("MACEVENT: %s\n", event_name));
  640. break;
  641. case WLC_E_PFN_NET_FOUND:
  642. case WLC_E_PFN_NET_LOST:
  643. case WLC_E_PFN_SCAN_COMPLETE:
  644. DHD_EVENT(("PNOEVENT: %s\n", event_name));
  645. break;
  646. case WLC_E_PSK_SUP:
  647. case WLC_E_PRUNE:
  648. DHD_EVENT(("MACEVENT: %s, status %d, reason %d\n",
  649. event_name, (int)status, (int)reason));
  650. break;
  651. case WLC_E_TRACE:
  652. {
  653. static uint32 seqnum_prev = 0;
  654. msgtrace_hdr_t hdr;
  655. uint32 nblost;
  656. char *s, *p;
  657. buf = (uchar *) event_data;
  658. memcpy(&hdr, buf, MSGTRACE_HDRLEN);
  659. if (hdr.version != MSGTRACE_VERSION) {
  660. printf("\nMACEVENT: %s [unsupported version --> "
  661. "dhd version:%d dongle version:%d]\n",
  662. event_name, MSGTRACE_VERSION, hdr.version);
  663. /* Reset datalen to avoid display below */
  664. datalen = 0;
  665. break;
  666. }
  667. /* There are 2 bytes available at the end of data */
  668. buf[MSGTRACE_HDRLEN + ntoh16(hdr.len)] = '\0';
  669. if (ntoh32(hdr.discarded_bytes) || ntoh32(hdr.discarded_printf)) {
  670. printf("\nWLC_E_TRACE: [Discarded traces in dongle -->"
  671. "discarded_bytes %d discarded_printf %d]\n",
  672. ntoh32(hdr.discarded_bytes), ntoh32(hdr.discarded_printf));
  673. }
  674. nblost = ntoh32(hdr.seqnum) - seqnum_prev - 1;
  675. if (nblost > 0) {
  676. printf("\nWLC_E_TRACE: [Event lost --> seqnum %d nblost %d\n",
  677. ntoh32(hdr.seqnum), nblost);
  678. }
  679. seqnum_prev = ntoh32(hdr.seqnum);
  680. /* Display the trace buffer. Advance from \n to \n to avoid display big
  681. * printf (issue with Linux printk )
  682. */
  683. p = (char *)&buf[MSGTRACE_HDRLEN];
  684. while ((s = strstr(p, "\n")) != NULL) {
  685. *s = '\0';
  686. printf("%s\n", p);
  687. p = s + 1;
  688. }
  689. printf("%s\n", p);
  690. /* Reset datalen to avoid display below */
  691. datalen = 0;
  692. }
  693. break;
  694. case WLC_E_RSSI:
  695. DHD_EVENT(("MACEVENT: %s %d\n", event_name, ntoh32(*((int *)event_data))));
  696. break;
  697. default:
  698. DHD_EVENT(("MACEVENT: %s %d, MAC %s, status %d, reason %d, auth %d\n",
  699. event_name, event_type, eabuf, (int)status, (int)reason,
  700. (int)auth_type));
  701. break;
  702. }
  703. /* show any appended data */
  704. if (datalen) {
  705. buf = (uchar *) event_data;
  706. DHD_EVENT((" data (%d) : ", datalen));
  707. for (i = 0; i < datalen; i++)
  708. DHD_EVENT((" 0x%02x ", *buf++));
  709. DHD_EVENT(("\n"));
  710. }
  711. }
  712. #endif /* SHOW_EVENTS */
  713. int
  714. wl_host_event(struct dhd_info *dhd, int *ifidx, void *pktdata,
  715. wl_event_msg_t *event, void **data_ptr)
  716. {
  717. /* check whether packet is a BRCM event pkt */
  718. bcm_event_t *pvt_data = (bcm_event_t *)pktdata;
  719. char *event_data;
  720. uint32 type, status;
  721. uint16 flags;
  722. int evlen;
  723. if (bcmp(BRCM_OUI, &pvt_data->bcm_hdr.oui[0], DOT11_OUI_LEN)) {
  724. DHD_ERROR(("%s: mismatched OUI, bailing\n", __FUNCTION__));
  725. return (BCME_ERROR);
  726. }
  727. /* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */
  728. if (ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype) != BCMILCP_BCM_SUBTYPE_EVENT) {
  729. DHD_ERROR(("%s: mismatched subtype, bailing\n", __FUNCTION__));
  730. return (BCME_ERROR);
  731. }
  732. *data_ptr = &pvt_data[1];
  733. event_data = *data_ptr;
  734. /* memcpy since BRCM event pkt may be unaligned. */
  735. memcpy(event, &pvt_data->event, sizeof(wl_event_msg_t));
  736. type = ntoh32_ua((void *)&event->event_type);
  737. flags = ntoh16_ua((void *)&event->flags);
  738. status = ntoh32_ua((void *)&event->status);
  739. evlen = ntoh32_ua((void *)&event->datalen) + sizeof(bcm_event_t);
  740. switch (type) {
  741. case WLC_E_IF:
  742. {
  743. dhd_if_event_t *ifevent = (dhd_if_event_t *)event_data;
  744. DHD_TRACE(("%s: if event\n", __FUNCTION__));
  745. if (ifevent->ifidx > 0 && ifevent->ifidx < DHD_MAX_IFS)
  746. {
  747. if (ifevent->action == WLC_E_IF_ADD)
  748. dhd_add_if(dhd, ifevent->ifidx,
  749. NULL, event->ifname,
  750. pvt_data->eth.ether_dhost,
  751. ifevent->flags, ifevent->bssidx);
  752. else
  753. dhd_del_if(dhd, ifevent->ifidx);
  754. } else {
  755. DHD_ERROR(("%s: Invalid ifidx %d for %s\n",
  756. __FUNCTION__, ifevent->ifidx, event->ifname));
  757. }
  758. if (ifevent->action != WLC_E_IF_ADD) {
  759. /* send up the if event: btamp user needs it */
  760. *ifidx = dhd_ifname2idx(dhd, event->ifname);
  761. }
  762. }
  763. /* push up to external supp/auth */
  764. dhd_event(dhd, (char *)pvt_data, evlen, *ifidx);
  765. break;
  766. #ifdef P2P
  767. case WLC_E_NDIS_LINK:
  768. break;
  769. #endif
  770. /* fall through */
  771. /* These are what external supplicant/authenticator wants */
  772. case WLC_E_LINK:
  773. case WLC_E_ASSOC_IND:
  774. case WLC_E_REASSOC_IND:
  775. case WLC_E_DISASSOC_IND:
  776. case WLC_E_MIC_ERROR:
  777. default:
  778. /* Fall through: this should get _everything_ */
  779. *ifidx = dhd_ifname2idx(dhd, event->ifname);
  780. /* push up to external supp/auth */
  781. dhd_event(dhd, (char *)pvt_data, evlen, *ifidx);
  782. DHD_TRACE(("%s: MAC event %d, flags %x, status %x\n",
  783. __FUNCTION__, type, flags, status));
  784. /* put it back to WLC_E_NDIS_LINK */
  785. if (type == WLC_E_NDIS_LINK) {
  786. uint32 temp;
  787. temp = ntoh32_ua((void *)&event->event_type);
  788. DHD_TRACE(("Converted to WLC_E_LINK type %d\n", temp));
  789. temp = ntoh32(WLC_E_NDIS_LINK);
  790. memcpy((void *)(&pvt_data->event.event_type), &temp,
  791. sizeof(pvt_data->event.event_type));
  792. }
  793. break;
  794. }
  795. #ifdef SHOW_EVENTS
  796. wl_show_host_event(event, event_data);
  797. #endif /* SHOW_EVENTS */
  798. return (BCME_OK);
  799. }
  800. void
  801. wl_event_to_host_order(wl_event_msg_t *evt)
  802. {
  803. /* Event struct members passed from dongle to host are stored in network
  804. * byte order. Convert all members to host-order.
  805. */
  806. evt->event_type = ntoh32(evt->event_type);
  807. evt->flags = ntoh16(evt->flags);
  808. evt->status = ntoh32(evt->status);
  809. evt->reason = ntoh32(evt->reason);
  810. evt->auth_type = ntoh32(evt->auth_type);
  811. evt->datalen = ntoh32(evt->datalen);
  812. evt->version = ntoh16(evt->version);
  813. }
  814. void print_buf(void *pbuf, int len, int bytes_per_line)
  815. {
  816. int i, j = 0;
  817. unsigned char *buf = pbuf;
  818. if (bytes_per_line == 0) {
  819. bytes_per_line = len;
  820. }
  821. for (i = 0; i < len; i++) {
  822. printf("%2.2x", *buf++);
  823. j++;
  824. if (j == bytes_per_line) {
  825. printf("\n");
  826. j = 0;
  827. } else {
  828. printf(":");
  829. }
  830. }
  831. printf("\n");
  832. }
  833. #define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
  834. /* Convert user's input in hex pattern to byte-size mask */
  835. static int
  836. wl_pattern_atoh(char *src, char *dst)
  837. {
  838. int i;
  839. if (strncmp(src, "0x", 2) != 0 &&
  840. strncmp(src, "0X", 2) != 0) {
  841. DHD_ERROR(("Mask invalid format. Needs to start with 0x\n"));
  842. return -1;
  843. }
  844. src = src + 2; /* Skip past 0x */
  845. if (strlen(src) % 2 != 0) {
  846. DHD_ERROR(("Mask invalid format. Needs to be of even length\n"));
  847. return -1;
  848. }
  849. for (i = 0; *src != '\0'; i++) {
  850. char num[3];
  851. strncpy(num, src, 2);
  852. num[2] = '\0';
  853. dst[i] = (uint8)strtoul(num, NULL, 16);
  854. src += 2;
  855. }
  856. return i;
  857. }
  858. void
  859. dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode)
  860. {
  861. char *argv[8];
  862. int i = 0;
  863. const char *str;
  864. int buf_len;
  865. int str_len;
  866. char *arg_save = 0, *arg_org = 0;
  867. int rc;
  868. char buf[128];
  869. wl_pkt_filter_enable_t enable_parm;
  870. wl_pkt_filter_enable_t * pkt_filterp;
  871. if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) {
  872. DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
  873. goto fail;
  874. }
  875. arg_org = arg_save;
  876. memcpy(arg_save, arg, strlen(arg) + 1);
  877. argv[i] = bcmstrtok(&arg_save, " ", 0);
  878. i = 0;
  879. if (NULL == argv[i]) {
  880. DHD_ERROR(("No args provided\n"));
  881. goto fail;
  882. }
  883. str = "pkt_filter_enable";
  884. str_len = strlen(str);
  885. strncpy(buf, str, str_len);
  886. buf[str_len] = '\0';
  887. buf_len = str_len + 1;
  888. pkt_filterp = (wl_pkt_filter_enable_t *)(buf + str_len + 1);
  889. /* Parse packet filter id. */
  890. enable_parm.id = htod32(strtoul(argv[i], NULL, 0));
  891. /* Parse enable/disable value. */
  892. enable_parm.enable = htod32(enable);
  893. buf_len += sizeof(enable_parm);
  894. memcpy((char *)pkt_filterp,
  895. &enable_parm,
  896. sizeof(enable_parm));
  897. /* Enable/disable the specified filter. */
  898. rc = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, buf_len);
  899. rc = rc >= 0 ? 0 : rc;
  900. if (rc)
  901. DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
  902. __FUNCTION__, arg, rc));
  903. else
  904. DHD_TRACE(("%s: successfully added pktfilter %s\n",
  905. __FUNCTION__, arg));
  906. /* Contorl the master mode */
  907. bcm_mkiovar("pkt_filter_mode", (char *)&master_mode, 4, buf, sizeof(buf));
  908. rc = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, sizeof(buf));
  909. rc = rc >= 0 ? 0 : rc;
  910. if (rc)
  911. DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
  912. __FUNCTION__, arg, rc));
  913. fail:
  914. if (arg_org)
  915. MFREE(dhd->osh, arg_org, strlen(arg) + 1);
  916. }
  917. void
  918. dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg)
  919. {
  920. const char *str;
  921. wl_pkt_filter_t pkt_filter;
  922. wl_pkt_filter_t *pkt_filterp;
  923. int buf_len;
  924. int str_len;
  925. int rc;
  926. uint32 mask_size;
  927. uint32 pattern_size;
  928. char *argv[8], * buf = 0;
  929. int i = 0;
  930. char *arg_save = 0, *arg_org = 0;
  931. #define BUF_SIZE 2048
  932. if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) {
  933. DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
  934. goto fail;
  935. }
  936. arg_org = arg_save;
  937. if (!(buf = MALLOC(dhd->osh, BUF_SIZE))) {
  938. DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
  939. goto fail;
  940. }
  941. memcpy(arg_save, arg, strlen(arg) + 1);
  942. if (strlen(arg) > BUF_SIZE) {
  943. DHD_ERROR(("Not enough buffer %d < %d\n", (int)strlen(arg), (int)sizeof(buf)));
  944. goto fail;
  945. }
  946. argv[i] = bcmstrtok(&arg_save, " ", 0);
  947. while (argv[i++])
  948. argv[i] = bcmstrtok(&arg_save, " ", 0);
  949. i = 0;
  950. if (NULL == argv[i]) {
  951. DHD_ERROR(("No args provided\n"));
  952. goto fail;
  953. }
  954. str = "pkt_filter_add";
  955. str_len = strlen(str);
  956. strncpy(buf, str, str_len);
  957. buf[ str_len ] = '\0';
  958. buf_len = str_len + 1;
  959. pkt_filterp = (wl_pkt_filter_t *) (buf + str_len + 1);
  960. /* Parse packet filter id. */
  961. pkt_filter.id = htod32(strtoul(argv[i], NULL, 0));
  962. if (NULL == argv[++i]) {
  963. DHD_ERROR(("Polarity not provided\n"));
  964. goto fail;
  965. }
  966. /* Parse filter polarity. */
  967. pkt_filter.negate_match = htod32(strtoul(argv[i], NULL, 0));
  968. if (NULL == argv[++i]) {
  969. DHD_ERROR(("Filter type not provided\n"));
  970. goto fail;
  971. }
  972. /* Parse filter type. */
  973. pkt_filter.type = htod32(strtoul(argv[i], NULL, 0));
  974. if (NULL == argv[++i]) {
  975. DHD_ERROR(("Offset not provided\n"));
  976. goto fail;
  977. }
  978. /* Parse pattern filter offset. */
  979. pkt_filter.u.pattern.offset = htod32(strtoul(argv[i], NULL, 0));
  980. if (NULL == argv[++i]) {
  981. DHD_ERROR(("Bitmask not provided\n"));
  982. goto fail;
  983. }
  984. /* Parse pattern filter mask. */
  985. mask_size =
  986. htod32(wl_pattern_atoh(argv[i], (char *) pkt_filterp->u.pattern.mask_and_pattern));
  987. if (NULL == argv[++i]) {
  988. DHD_ERROR(("Pattern not provided\n"));
  989. goto fail;
  990. }
  991. /* Parse pattern filter pattern. */
  992. pattern_size =
  993. htod32(wl_pattern_atoh(argv[i],
  994. (char *) &pkt_filterp->u.pattern.mask_and_pattern[mask_size]));
  995. if (mask_size != pattern_size) {
  996. DHD_ERROR(("Mask and pattern not the same size\n"));
  997. goto fail;
  998. }
  999. pkt_filter.u.pattern.size_bytes = mask_size;
  1000. buf_len += WL_PKT_FILTER_FIXED_LEN;
  1001. buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size);
  1002. /* Keep-alive attributes are set in local variable (keep_alive_pkt), and
  1003. ** then memcpy'ed into buffer (keep_alive_pktp) since there is no
  1004. ** guarantee that the buffer is properly aligned.
  1005. */
  1006. memcpy((char *)pkt_filterp,
  1007. &pkt_filter,
  1008. WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN);
  1009. rc = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, buf, buf_len);
  1010. rc = rc >= 0 ? 0 : rc;
  1011. if (rc)
  1012. DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
  1013. __FUNCTION__, arg, rc));
  1014. else
  1015. DHD_TRACE(("%s: successfully added pktfilter %s\n",
  1016. __FUNCTION__, arg));
  1017. fail:
  1018. if (arg_org)
  1019. MFREE(dhd->osh, arg_org, strlen(arg) + 1);
  1020. if (buf)
  1021. MFREE(dhd->osh, buf, BUF_SIZE);
  1022. }
  1023. void
  1024. dhd_arp_offload_set(dhd_pub_t * dhd, int arp_mode)
  1025. {
  1026. char iovbuf[32];
  1027. int retcode;
  1028. bcm_mkiovar("arp_ol", (char *)&arp_mode, 4, iovbuf, sizeof(iovbuf));
  1029. retcode = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  1030. retcode = retcode >= 0 ? 0 : retcode;
  1031. if (retcode)
  1032. DHD_TRACE(("%s: failed to set ARP offload mode to 0x%x, retcode = %d\n",
  1033. __FUNCTION__, arp_mode, retcode));
  1034. else
  1035. DHD_TRACE(("%s: successfully set ARP offload mode to 0x%x\n",
  1036. __FUNCTION__, arp_mode));
  1037. }
  1038. void
  1039. dhd_arp_offload_enable(dhd_pub_t * dhd, int arp_enable)
  1040. {
  1041. char iovbuf[32];
  1042. int retcode;
  1043. bcm_mkiovar("arpoe", (char *)&arp_enable, 4, iovbuf, sizeof(iovbuf));
  1044. retcode = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  1045. retcode = retcode >= 0 ? 0 : retcode;
  1046. if (retcode)
  1047. DHD_TRACE(("%s: failed to enabe ARP offload to %d, retcode = %d\n",
  1048. __FUNCTION__, arp_enable, retcode));
  1049. else
  1050. DHD_TRACE(("%s: successfully enabed ARP offload to %d\n",
  1051. __FUNCTION__, arp_enable));
  1052. }
  1053. int
  1054. dhd_preinit_ioctls(dhd_pub_t *dhd)
  1055. {
  1056. char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */
  1057. uint up = 0;
  1058. char buf[128], *ptr;
  1059. uint power_mode = PM_FAST;
  1060. uint32 dongle_align = DHD_SDALIGN;
  1061. uint32 glom = 0;
  1062. uint bcn_timeout = 3;
  1063. int scan_assoc_time = 40;
  1064. int scan_unassoc_time = 80;
  1065. #ifdef SCAN_5G_HOMECHANNEL_TIME
  1066. int scan_home_time = 60;
  1067. #endif
  1068. #ifdef FCC_CERT
  1069. uint spect = 0;
  1070. #endif
  1071. #ifdef SOFTAP
  1072. if(!ap_fw_loaded) {
  1073. #endif /* SOFTAP */
  1074. /* Set Country code */
  1075. if (dhd->country_code[0] != 0) {
  1076. if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_COUNTRY,
  1077. dhd->country_code, sizeof(dhd->country_code)) < 0) {
  1078. DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__));
  1079. }
  1080. }
  1081. #ifdef SOFTAP
  1082. }
  1083. #endif /* SOFTAP */
  1084. /* query for 'ver' to get version info from firmware */
  1085. memset(buf, 0, sizeof(buf));
  1086. ptr = buf;
  1087. bcm_mkiovar("ver", 0, 0, buf, sizeof(buf));
  1088. dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, sizeof(buf));
  1089. bcmstrtok(&ptr, "\n", 0);
  1090. /* Print fw version info */
  1091. DHD_ERROR(("Firmware version = %s\n", buf));
  1092. #ifdef BCMDISABLE_PM
  1093. /*Disable Power save features for CERTIFICATION*/
  1094. power_mode = 0;
  1095. /* Set PowerSave mode */
  1096. dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode));
  1097. /* Disable MPC */
  1098. bcm_mkiovar("mpc", (char *)&power_mode, 4, iovbuf, sizeof(iovbuf));
  1099. dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  1100. #else
  1101. /* Set PowerSave mode */
  1102. dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode));
  1103. #endif //BCMDISABLE_PM
  1104. #ifdef SOFTAP
  1105. if(!ap_fw_loaded) {
  1106. #endif /* SOFTAP */
  1107. #ifdef FCC_CERT
  1108. dhdcdc_set_ioctl(dhd, 0, WLC_DOWN, (char *)&up, sizeof(up));
  1109. /* Disable TPC to get qualification of FCC */
  1110. dhdcdc_set_ioctl(dhd, 0, WLC_SET_SPECT_MANAGMENT, (char *)&spect, sizeof(spect));
  1111. #endif /* FCC_CERT */
  1112. #ifdef SOFTAP
  1113. }
  1114. #endif /* SOFTAP */
  1115. /* Match Host and Dongle rx alignment */
  1116. bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf));
  1117. dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  1118. /* disable glom option per default */
  1119. bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf));
  1120. dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  1121. /* Setup timeout if Beacons are lost and roam is off to report link down */
  1122. bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf));
  1123. dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  1124. /* Enable/Disable build-in roaming to allowed ext supplicant to take of romaing */
  1125. bcm_mkiovar("roam_off", (char *)&dhd_roam, 4, iovbuf, sizeof(iovbuf));
  1126. dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  1127. /* Force STA UP */
  1128. if (dhd_radio_up)
  1129. dhdcdc_set_ioctl(dhd, 0, WLC_UP, (char *)&up, sizeof(up));
  1130. /* Setup event_msgs */
  1131. bcm_mkiovar("event_msgs", dhd->eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf));
  1132. dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
  1133. dhdcdc_set_ioctl(dhd, 0, WLC_SET_SCAN_CHANNEL_TIME, (char *)&scan_assoc_time,
  1134. sizeof(scan_assoc_time));
  1135. dhdcdc_set_ioctl(dhd, 0, WLC_SET_SCAN_UNASSOC_TIME, (char *)&scan_unassoc_time,
  1136. sizeof(scan_unassoc_time));
  1137. #ifdef SCAN_5G_HOMECHANNEL_TIME
  1138. DHD_INFO(("Scan Channel Home Time Set : 80 ms \r\n"));
  1139. dhdcdc_set_ioctl(dhd, 0, WLC_SET_SCAN_HOME_TIME, (char *)&scan_home_time,
  1140. sizeof(scan_home_time));
  1141. #endif
  1142. #ifdef ARP_OFFLOAD_SUPPORT
  1143. /* Set and enable ARP offload feature */
  1144. if (dhd_arp_enable)
  1145. dhd_arp_offload_set(dhd, dhd_arp_mode);
  1146. dhd_arp_offload_enable(dhd, dhd_arp_enable);
  1147. #endif /* ARP_OFFLOAD_SUPPORT */
  1148. #ifdef PKT_FILTER_SUPPORT
  1149. {
  1150. int i;
  1151. /* Set up pkt filter */
  1152. if (dhd_pkt_filter_enable) {
  1153. for (i = 0; i < dhd->pktfilter_count; i++) {
  1154. dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]);
  1155. dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
  1156. 0, dhd_master_mode);
  1157. }
  1158. }
  1159. }
  1160. #endif /* PKT_FILTER_SUPPORT */
  1161. return 0;
  1162. }
  1163. #ifdef SIMPLE_ISCAN
  1164. uint iscan_thread_id;
  1165. iscan_buf_t * iscan_chain = 0;
  1166. iscan_buf_t *
  1167. dhd_iscan_allocate_buf(dhd_pub_t *dhd, iscan_buf_t **iscanbuf)
  1168. {
  1169. iscan_buf_t *iscanbuf_alloc = 0;
  1170. iscan_buf_t *iscanbuf_head;
  1171. dhd_iscan_lock();
  1172. iscanbuf_alloc = (iscan_buf_t*)MALLOC(dhd->osh, sizeof(iscan_buf_t));
  1173. if (iscanbuf_alloc == NULL)
  1174. goto fail;
  1175. iscanbuf_alloc->next = NULL;
  1176. iscanbuf_head = *iscanbuf;
  1177. DHD_INFO(("%s: addr of allocated node = 0x%X, addr of iscanbuf_head \
  1178. = 0x%X dhd = 0x%X\n", __FUNCTION__, iscanbuf_alloc,
  1179. iscanbuf_head, dhd));
  1180. if (iscanbuf_head == NULL) {
  1181. *iscanbuf = iscanbuf_alloc;
  1182. DHD_INFO(("%s: Head is allocated\n", __FUNCTION__));
  1183. goto fail;
  1184. }
  1185. while (iscanbuf_head->next)
  1186. iscanbuf_head = iscanbuf_head->next;
  1187. iscanbuf_head->next = iscanbuf_alloc;
  1188. fail:
  1189. dhd_iscan_unlock();
  1190. return iscanbuf_alloc;
  1191. }
  1192. void
  1193. dhd_iscan_free_buf(void *dhdp, iscan_buf_t *iscan_delete)
  1194. {
  1195. iscan_buf_t *iscanbuf_free = 0;
  1196. iscan_buf_t *iscanbuf_prv = 0;
  1197. iscan_buf_t *iscanbuf_cur = iscan_chain;
  1198. dhd_pub_t *dhd = dhd_bus_pub(dhdp);
  1199. dhd_iscan_lock();
  1200. /* If iscan_delete is null then delete the entire
  1201. * chain or else delete specific one provided
  1202. */
  1203. if (!iscan_delete) {
  1204. while (iscanbuf_cur) {
  1205. iscanbuf_free = iscanbuf_cur;
  1206. iscanbuf_cur = iscanbuf_cur->next;
  1207. iscanbuf_free->next = 0;
  1208. MFREE(dhd->osh, iscanbuf_free, sizeof(iscan_buf_t));
  1209. }
  1210. iscan_chain = 0;
  1211. } else {
  1212. while (iscanbuf_cur) {
  1213. if (iscanbuf_cur == iscan_delete)
  1214. break;
  1215. iscanbuf_prv = iscanbuf_cur;
  1216. iscanbuf_cur = iscanbuf_cur->next;
  1217. }
  1218. if (iscanbuf_prv)
  1219. iscanbuf_prv->next = iscan_delete->next;
  1220. iscan_delete->next = 0;
  1221. MFREE(dhd->osh, iscan_delete, sizeof(iscan_buf_t));
  1222. if (!iscanbuf_prv)
  1223. iscan_chain = 0;
  1224. }
  1225. dhd_iscan_unlock();
  1226. }
  1227. iscan_buf_t *
  1228. dhd_iscan_result_buf(void)
  1229. {
  1230. return iscan_chain;
  1231. }
  1232. /*
  1233. * print scan cache
  1234. * print partial iscan_skip list differently
  1235. */
  1236. int
  1237. dhd_iscan_print_cache(iscan_buf_t *iscan_skip)
  1238. {
  1239. int i = 0, l = 0;
  1240. iscan_buf_t *iscan_cur;
  1241. wl_iscan_results_t *list;
  1242. wl_scan_results_t *results;
  1243. wl_bss_info_t UNALIGNED *bi;
  1244. dhd_iscan_lock();
  1245. iscan_cur = dhd_iscan_result_buf();
  1246. while (iscan_cur) {
  1247. list = (wl_iscan_results_t *)iscan_cur->iscan_buf;
  1248. if (!list)
  1249. break;
  1250. results = (wl_scan_results_t *)&list->results;
  1251. if (!results)
  1252. break;
  1253. if (results->version != WL_BSS_INFO_VERSION) {
  1254. DHD_ERROR(("%s: results->version %d != WL_BSS_INFO_VERSION\n",
  1255. __FUNCTION__, results->version));
  1256. goto done;
  1257. }
  1258. bi = results->bss_info;
  1259. for (i = 0; i < results->count; i++) {
  1260. if (!bi)
  1261. break;
  1262. DHD_ERROR(("%s[%2.2d:%2.2d] %X:%X:%X:%X:%X:%X\n",
  1263. iscan_cur != iscan_skip?"BSS":"bss", l, i,
  1264. bi->BSSID.octet[0], bi->BSSID.octet[1], bi->BSSID.octet[2],
  1265. bi->BSSID.octet[3], bi->BSSID.octet[4], bi->BSSID.octet[5]));
  1266. bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length));
  1267. }
  1268. iscan_cur = iscan_cur->next;
  1269. l++;
  1270. }
  1271. done:
  1272. dhd_iscan_unlock();
  1273. return 0;
  1274. }
  1275. /*
  1276. * delete disappeared AP from specific scan cache but skip partial list in iscan_skip
  1277. */
  1278. int
  1279. dhd_iscan_delete_bss(void *dhdp, void *addr, iscan_buf_t *iscan_skip)
  1280. {
  1281. int i = 0, j = 0, l = 0;
  1282. iscan_buf_t *iscan_cur;
  1283. wl_iscan_results_t *list;
  1284. wl_scan_results_t *results;
  1285. wl_bss_info_t UNALIGNED *bi, *bi_new, *bi_next;
  1286. uchar *s_addr = addr;
  1287. dhd_iscan_lock();
  1288. DHD_TRACE(("%s: BSS to remove %X:%X:%X:%X:%X:%X\n",
  1289. __FUNCTION__, s_addr[0], s_addr[1], s_addr[2],
  1290. s_addr[3], s_addr[4], s_addr[5]));
  1291. iscan_cur = dhd_iscan_result_buf();
  1292. while (iscan_cur) {
  1293. if (iscan_cur != iscan_skip) {
  1294. list = (wl_iscan_results_t *)iscan_cur->iscan_buf;
  1295. if (!list)
  1296. break;
  1297. results = (wl_scan_results_t *)&list->results;
  1298. if (!results)
  1299. break;
  1300. if (results->version != WL_BSS_INFO_VERSION) {
  1301. DHD_ERROR(("%s: results->version %d != WL_BSS_INFO_VERSION\n",
  1302. __FUNCTION__, results->version));
  1303. goto done;
  1304. }
  1305. bi = results->bss_info;
  1306. for (i = 0; i < results->count; i++) {
  1307. if (!bi)
  1308. break;
  1309. if (!memcmp(bi->BSSID.octet, addr, ETHER_ADDR_LEN)) {
  1310. DHD_ERROR(("%s: Del BSS[%2.2d:%2.2d] %X:%X:%X:%X:%X:%X\n", \
  1311. __FUNCTION__, l, i, bi->BSSID.octet[0], \
  1312. bi->BSSID.octet[1], bi->BSSID.octet[2], \
  1313. bi->BSSID.octet[3], bi->BSSID.octet[4], \
  1314. bi->BSSID.octet[5]));
  1315. bi_new = bi;
  1316. bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length));
  1317. /*
  1318. if(bi && bi_new) {
  1319. bcopy(bi, bi_new, results->buflen -
  1320. dtoh32(bi_new->length));
  1321. results->buflen -= dtoh32(bi_new->length);
  1322. }
  1323. */
  1324. results->buflen -= dtoh32(bi_new->length);
  1325. results->count--;
  1326. for (j = i; j < results->count; j++) {
  1327. if (bi && bi_new) {
  1328. DHD_TRACE(("%s: Moved up BSS[%2.2d:%2.2d] \
  1329. %X:%X:%X:%X:%X:%X\n",
  1330. __FUNCTION__, l, j, bi->BSSID.octet[0],
  1331. bi->BSSID.octet[1], bi->BSSID.octet[2],
  1332. bi->BSSID.octet[3], bi->BSSID.octet[4],
  1333. bi->BSSID.octet[5]));
  1334. bi_next = (wl_bss_info_t *)((uintptr)bi +
  1335. dtoh32(bi->length));
  1336. bcopy(bi, bi_new, dtoh32(bi->length));
  1337. bi_new = (wl_bss_info_t *)((uintptr)bi_new +
  1338. dtoh32(bi_new->length));
  1339. bi = bi_next;
  1340. }
  1341. }
  1342. if (results->count == 0) {
  1343. /* Prune now empty partial scan list */
  1344. dhd_iscan_free_buf(dhdp, iscan_cur);
  1345. goto done;
  1346. }
  1347. break;
  1348. }
  1349. bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length));
  1350. }
  1351. }
  1352. iscan_cur = iscan_cur->next;
  1353. l++;
  1354. }
  1355. done:
  1356. dhd_iscan_unlock();
  1357. return 0;
  1358. }
  1359. int
  1360. dhd_iscan_remove_duplicates(void * dhdp, iscan_buf_t *iscan_cur)
  1361. {
  1362. int i = 0;
  1363. wl_iscan_results_t *list;
  1364. wl_scan_results_t *results;
  1365. wl_bss_info_t UNALIGNED *bi, *bi_new, *bi_next;
  1366. dhd_iscan_lock();
  1367. DHD_ERROR(("%s: Scan cache before delete\n",
  1368. __FUNCTION__));
  1369. dhd_iscan_print_cache(iscan_cur);
  1370. if (!iscan_cur)
  1371. goto done;
  1372. list = (wl_iscan_results_t *)iscan_cur->iscan_buf;
  1373. if (!list)
  1374. goto done;
  1375. results = (wl_scan_results_t *)&list->results;
  1376. if (!results)
  1377. goto done;
  1378. if (results->version != WL_BSS_INFO_VERSION) {
  1379. DHD_ERROR(("%s: results->version %d != WL_BSS_INFO_VERSION\n",
  1380. __FUNCTION__, results->version));
  1381. goto done;
  1382. }
  1383. bi = results->bss_info;
  1384. for (i = 0; i < results->count; i++) {
  1385. if (!bi)
  1386. break;
  1387. DHD_TRACE(("%s: Find dups for BSS[%2.2d] %X:%X:%X:%X:%X:%X\n",
  1388. __FUNCTION__, i, bi->BSSID.octet[0], bi->BSSID.octet[1], bi->BSSID.octet[2],
  1389. bi->BSSID.octet[3], bi->BSSID.octet[4], bi->BSSID.octet[5]));
  1390. dhd_iscan_delete_bss(dhdp, bi->BSSID.octet, iscan_cur);
  1391. bi = (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length));
  1392. }
  1393. done:
  1394. DHD_ERROR(("%s: Scan cache after delete\n", __FUNCTION__));
  1395. dhd_iscan_print_cache(iscan_cur);
  1396. dhd_iscan_unlock();
  1397. return 0;
  1398. }
  1399. int
  1400. dhd_iscan_request(void * dhdp, uint16 action)
  1401. {
  1402. int rc;
  1403. wl_iscan_params_t params;
  1404. dhd_pub_t *dhd = dhd_bus_pub(dhdp);
  1405. char buf[WLC_IOCTL_SMLEN];
  1406. memset(&params, 0, sizeof(wl_iscan_params_t));
  1407. memcpy(&params.params.bssid, &ether_bcast, ETHER_ADDR_LEN);
  1408. params.params.bss_type = DOT11_BSSTYPE_ANY;
  1409. params.params.scan_type = DOT11_SCANTYPE_ACTIVE;
  1410. params.params.nprobes = htod32(-1);
  1411. params.params.active_time = htod32(-1);
  1412. params.params.passive_time = htod32(-1);
  1413. params.params.home_time = htod32(-1);
  1414. params.params.channel_num = htod32(0);
  1415. params.version = htod32(ISCAN_REQ_VERSION);
  1416. params.action = htod16(action);
  1417. params.scan_duration = htod16(0);
  1418. bcm_mkiovar("iscan", (char *)&params, sizeof(wl_iscan_params_t), buf, WLC_IOCTL_SMLEN);
  1419. rc = dhd_wl_ioctl(dhdp, WLC_SET_VAR, buf, WLC_IOCTL_SMLEN);
  1420. return rc;
  1421. }
  1422. static int
  1423. dhd_iscan_get_partial_result(void *dhdp, uint *scan_count)
  1424. {
  1425. wl_iscan_results_t *list_buf;
  1426. wl_iscan_results_t list;
  1427. wl_scan_results_t *results;
  1428. iscan_buf_t *iscan_cur;
  1429. int status = -1;
  1430. dhd_pub_t *dhd = dhd_bus_pub(dhdp);
  1431. int rc;
  1432. iscan_cur = dhd_iscan_allocate_buf(dhd, &iscan_chain);
  1433. if (!iscan_cur) {
  1434. DHD_ERROR(("%s: Failed to allocate node\n", __FUNCTION__));
  1435. dhd_iscan_free_buf(dhdp, 0);
  1436. dhd_iscan_request(dhdp, WL_SCAN_ACTION_ABORT);
  1437. dhd_ind_scan_confirm(dhdp, FALSE);
  1438. goto fail;
  1439. }
  1440. dhd_iscan_lock();
  1441. memset(iscan_cur->iscan_buf, 0, WLC_IW_ISCAN_MAXLEN);
  1442. list_buf = (wl_iscan_results_t*)iscan_cur->iscan_buf;
  1443. results = &list_buf->results;
  1444. results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE;
  1445. results->version = 0;
  1446. results->count = 0;
  1447. memset(&list, 0, sizeof(list));
  1448. list.results.buflen = htod32(WLC_IW_ISCAN_MAXLEN);
  1449. bcm_mkiovar("iscanresults", (char *)&list, WL_ISCAN_RESULTS_FIXED_SIZE,
  1450. iscan_cur->iscan_buf, WLC_IW_ISCAN_MAXLEN);
  1451. rc = dhd_wl_ioctl(dhdp, WLC_GET_VAR, iscan_cur->iscan_buf, WLC_IW_ISCAN_MAXLEN);
  1452. results->buflen = dtoh32(results->buflen);
  1453. results->version = dtoh32(results->version);
  1454. *scan_count = results->count = dtoh32(results->count);
  1455. status = dtoh32(list_buf->status);
  1456. dhd_iscan_unlock();
  1457. if (!(*scan_count))
  1458. dhd_iscan_free_buf(dhdp, iscan_cur);
  1459. else
  1460. dhd_iscan_remove_duplicates(dhdp, iscan_cur);
  1461. fail:
  1462. return status;
  1463. }
  1464. #endif