wifi: update ap6356 driver to 1.363.59.144.9 [1/2]
authorRongjun Chen <rongjun.chen@amlogic.com>
Wed, 2 Aug 2017 10:59:48 +0000 (18:59 +0800)
committerRongjun Chen <rongjun.chen@amlogic.com>
Wed, 2 Aug 2017 10:59:48 +0000 (18:59 +0800)
PD# 147497

fix ap6356 p2p connect fail issue

Change-Id: I78ffc273bc00c71f3341c963ef27b80c33e772c2

bcmdhd.1.363.59.144.x.cn/wl_android_ext.c [new file with mode: 0644]
bcmdhd.1.363.59.144.x.cn/wl_escan.c [new file with mode: 0644]
bcmdhd.1.363.59.144.x.cn/wl_escan.h [new file with mode: 0644]

diff --git a/bcmdhd.1.363.59.144.x.cn/wl_android_ext.c b/bcmdhd.1.363.59.144.x.cn/wl_android_ext.c
new file mode 100644 (file)
index 0000000..cf40743
--- /dev/null
@@ -0,0 +1,2178 @@
+\r
+\r
+#include <linux/module.h>\r
+#include <linux/netdevice.h>\r
+#include <net/netlink.h>\r
+\r
+#include <wl_android.h>\r
+#include <wldev_common.h>\r
+#include <wlioctl.h>\r
+#include <bcmutils.h>\r
+#include <linux_osl.h>\r
+#include <dhd_dbg.h>\r
+#include <dngl_stats.h>\r
+#include <dhd.h>\r
+#include <dhd_config.h>\r
+\r
+#define htod32(i) i\r
+#define htod16(i) i\r
+#define dtoh32(i) i\r
+#define dtoh16(i) i\r
+#define htodchanspec(i) i\r
+#define dtohchanspec(i) i\r
+#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))\r
+\r
+#define CMD_CHANNEL                            "CHANNEL"\r
+#define CMD_CHANNELS                   "CHANNELS"\r
+#define CMD_ROAM_TRIGGER               "ROAM_TRIGGER"\r
+#define CMD_KEEP_ALIVE                 "KEEP_ALIVE"\r
+#define CMD_PM                                 "PM"\r
+#define CMD_MONITOR                            "MONITOR"\r
+#define CMD_SET_SUSPEND_BCN_LI_DTIM            "SET_SUSPEND_BCN_LI_DTIM"\r
+\r
+#ifdef WL_EXT_IAPSTA\r
+#define CMD_IAPSTA_INIT                        "IAPSTA_INIT"\r
+#define CMD_IAPSTA_CONFIG              "IAPSTA_CONFIG"\r
+#define CMD_IAPSTA_ENABLE              "IAPSTA_ENABLE"\r
+#define CMD_IAPSTA_DISABLE             "IAPSTA_DISABLE"\r
+#ifdef PROP_TXSTATUS\r
+#ifdef PROP_TXSTATUS_VSDB\r
+#include <dhd_wlfc.h>\r
+extern int disable_proptx;
+#endif /* PROP_TXSTATUS_VSDB */\r
+#endif\r
+#endif\r
+#ifdef IDHCPC\r
+#define CMD_DHCPC_ENABLE       "DHCPC_ENABLE"\r
+#define CMD_DHCPC_DUMP         "DHCPC_DUMP"\r
+#endif\r
+#define CMD_WL         "WL"\r
+\r
+#define IEEE80211_BAND_2GHZ 0\r
+#define IEEE80211_BAND_5GHZ 1\r
+\r
+int wl_ext_ioctl(struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set)\r
+{\r
+       int ret;\r
+\r
+       ret = wldev_ioctl(dev, cmd, arg, len, set);\r
+       if (ret)\r
+               ANDROID_ERROR(("%s: cmd=%d ret=%d\n", __FUNCTION__, cmd, ret));\r
+       return ret;\r
+}\r
+\r
+int wl_ext_iovar_getint(struct net_device *dev, s8 *iovar, s32 *val)\r
+{\r
+       int ret;\r
+\r
+       ret = wldev_iovar_getint(dev, iovar, val);\r
+       if (ret)\r
+               ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar, ret));\r
+\r
+       return ret;\r
+}\r
+\r
+int wl_ext_iovar_setint(struct net_device *dev, s8 *iovar, s32 val)\r
+{\r
+       int ret;\r
+\r
+       ret = wldev_iovar_setint(dev, iovar, val);\r
+       if (ret)\r
+               ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar, ret));\r
+\r
+       return ret;\r
+}\r
+\r
+int wl_ext_iovar_getbuf(struct net_device *dev, s8 *iovar_name,\r
+       void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)\r
+{\r
+       int ret;\r
+\r
+       ret = wldev_iovar_getbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync);\r
+       if (ret != 0)\r
+               ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar_name, ret));\r
+\r
+       return ret;\r
+}\r
+\r
+int wl_ext_iovar_setbuf(struct net_device *dev, s8 *iovar_name,\r
+       void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)\r
+{\r
+       int ret;\r
+\r
+       ret = wldev_iovar_setbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync);\r
+       if (ret != 0)\r
+               ANDROID_ERROR(("%s: iovar=%s, ret=%d\n", __FUNCTION__, iovar_name, ret));\r
+\r
+       return ret;\r
+}\r
+\r
+#ifdef WL_EXT_IAPSTA\r
+int wl_ext_iovar_setbuf_bsscfg(struct net_device *dev, s8 *iovar_name,\r
+       void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync)\r
+{\r
+       int ret;\r
+       \r
+       ret = wldev_iovar_setbuf_bsscfg(dev, iovar_name, param, paramlen,\r
+               buf, buflen, bsscfg_idx, buf_sync);\r
+       if (ret < 0)\r
+               ANDROID_ERROR(("%s: iovar_name=%s ret=%d\n", __FUNCTION__, iovar_name, ret));\r
+\r
+       return ret;\r
+}\r
+\r
+int wl_ext_iovar_getbuf_bsscfg(struct net_device *dev, s8 *iovar_name,\r
+       void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync)\r
+{\r
+       int ret;\r
+       \r
+       ret = wldev_iovar_getbuf_bsscfg(dev, iovar_name, param, paramlen,\r
+               buf, buflen, bsscfg_idx, buf_sync);\r
+       if (ret < 0)\r
+               ANDROID_ERROR(("%s: iovar_name=%s ret=%d\n", __FUNCTION__, iovar_name, ret));\r
+\r
+       return ret;\r
+}\r
+#endif\r
+
+/* Return a legacy chanspec given a new chanspec
+ * Returns INVCHANSPEC on error
+ */
+static chanspec_t
+wl_ext_chspec_to_legacy(chanspec_t chspec)\r
+{
+       chanspec_t lchspec;
+
+       if (wf_chspec_malformed(chspec)) {
+               ANDROID_ERROR(("wl_ext_chspec_to_legacy: input chanspec (0x%04X) malformed\n",\r
+                       chspec));
+               return INVCHANSPEC;
+       }
+
+       /* get the channel number */
+       lchspec = CHSPEC_CHANNEL(chspec);
+
+       /* convert the band */
+       if (CHSPEC_IS2G(chspec)) {
+               lchspec |= WL_LCHANSPEC_BAND_2G;
+       } else {
+               lchspec |= WL_LCHANSPEC_BAND_5G;
+       }
+
+       /* convert the bw and sideband */
+       if (CHSPEC_IS20(chspec)) {
+               lchspec |= WL_LCHANSPEC_BW_20;
+               lchspec |= WL_LCHANSPEC_CTL_SB_NONE;
+       } else if (CHSPEC_IS40(chspec)) {
+               lchspec |= WL_LCHANSPEC_BW_40;
+               if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_L) {
+                       lchspec |= WL_LCHANSPEC_CTL_SB_LOWER;
+               } else {
+                       lchspec |= WL_LCHANSPEC_CTL_SB_UPPER;
+               }
+       } else {
+               /* cannot express the bandwidth */
+               char chanbuf[CHANSPEC_STR_LEN];
+               ANDROID_ERROR((\r
+                       "wl_ext_chspec_to_legacy: unable to convert chanspec %s (0x%04X) "\r
+                       "to pre-11ac format\n",
+                       wf_chspec_ntoa(chspec, chanbuf), chspec));
+               return INVCHANSPEC;
+       }
+
+       return lchspec;
+}\r
+
+/* given a chanspec value, do the endian and chanspec version conversion to
+ * a chanspec_t value
+ * Returns INVCHANSPEC on error
+ */
+static chanspec_t
+wl_ext_chspec_host_to_driver(int ioctl_ver, chanspec_t chanspec)\r
+{
+       if (ioctl_ver == 1) {\r
+               chanspec = wl_ext_chspec_to_legacy(chanspec);\r
+               if (chanspec == INVCHANSPEC) {
+                       return chanspec;
+               }
+       }
+       chanspec = htodchanspec(chanspec);
+
+       return chanspec;
+}\r
+\r
+static int\r
+wl_ext_get_ioctl_ver(struct net_device *dev, int *ioctl_ver)\r
+{\r
+       int ret = 0;\r
+       s32 val = 0;\r
+\r
+       val = 1;\r
+       ret = wl_ext_ioctl(dev, WLC_GET_VERSION, &val, sizeof(val), 0);\r
+       if (ret) {\r
+               ANDROID_ERROR(("WLC_GET_VERSION failed, err=%d\n", ret));\r
+               return ret;\r
+       }\r
+       val = dtoh32(val);\r
+       if (val != WLC_IOCTL_VERSION && val != 1) {\r
+               ANDROID_ERROR(("Version mismatch, please upgrade. Got %d, expected %d or 1\n",\r
+                       val, WLC_IOCTL_VERSION));\r
+               return BCME_VERSION;\r
+       }\r
+       *ioctl_ver = val;\r
+\r
+       return ret;\r
+}\r
+\r
+static int\r
+wl_ext_set_chanspec(struct net_device *dev, uint16 channel)\r
+{\r
+       s32 _chan = channel;\r
+       chanspec_t chspec = 0;
+       chanspec_t fw_chspec = 0;\r
+       u32 bw = WL_CHANSPEC_BW_20;\r
+       s32 err = BCME_OK;
+       s32 bw_cap = 0;\r
+       s8 iovar_buf[WLC_IOCTL_SMLEN];\r
+       struct {
+               u32 band;
+               u32 bw_cap;
+       } param = {0, 0};\r
+       uint band;\r
+       int ioctl_ver = 0;\r
+\r
+       if (_chan <= CH_MAX_2G_CHANNEL)\r
+               band = IEEE80211_BAND_2GHZ;\r
+       else\r
+               band = IEEE80211_BAND_5GHZ;\r
+       wl_ext_get_ioctl_ver(dev, &ioctl_ver);\r
+\r
+       if (band == IEEE80211_BAND_5GHZ) {\r
+               param.band = WLC_BAND_5G;
+               err = wldev_iovar_getbuf(dev, "bw_cap", &param, sizeof(param),
+                       iovar_buf, WLC_IOCTL_SMLEN, NULL);\r
+               if (err) {
+                       if (err != BCME_UNSUPPORTED) {
+                               ANDROID_ERROR(("bw_cap failed, %d\n", err));\r
+                               return err;
+                       } else {
+                               err = wldev_iovar_getint(dev, "mimo_bw_cap", &bw_cap);
+                               if (err) {
+                                       ANDROID_ERROR(("error get mimo_bw_cap (%d)\n", err));\r
+                               }
+                               if (bw_cap != WLC_N_BW_20ALL)
+                                       bw = WL_CHANSPEC_BW_40;
+                       }
+               } else {
+                       if (WL_BW_CAP_80MHZ(iovar_buf[0]))\r
+                               bw = WL_CHANSPEC_BW_80;
+                       else if (WL_BW_CAP_40MHZ(iovar_buf[0]))\r
+                               bw = WL_CHANSPEC_BW_40;
+                       else
+                               bw = WL_CHANSPEC_BW_20;
+
+               }\r
+       }\r
+       else if (band == IEEE80211_BAND_2GHZ)\r
+               bw = WL_CHANSPEC_BW_20;\r
+\r
+set_channel:
+       chspec = wf_channel2chspec(_chan, bw);
+       if (wf_chspec_valid(chspec)) {
+               fw_chspec = wl_ext_chspec_host_to_driver(ioctl_ver, chspec);\r
+               if (fw_chspec != INVCHANSPEC) {
+                       if ((err = wldev_iovar_setint(dev, "chanspec", fw_chspec)) == BCME_BADCHAN) {\r
+                               if (bw == WL_CHANSPEC_BW_80)
+                                       goto change_bw;\r
+                               wl_ext_ioctl(dev, WLC_SET_CHANNEL, &_chan, sizeof(_chan), 1);\r
+                               printf("%s: channel %d\n", __FUNCTION__, _chan);\r
+                       } else if (err) {
+                               ANDROID_ERROR(("%s: failed to set chanspec error %d\n", __FUNCTION__, err));\r
+                       } else\r
+                               printf("%s: channel %d, 0x%x\n", __FUNCTION__, channel, chspec);\r
+               } else {
+                       ANDROID_ERROR(("%s: failed to convert host chanspec to fw chanspec\n", __FUNCTION__));\r
+                       err = BCME_ERROR;
+               }
+       } else {
+change_bw:
+               if (bw == WL_CHANSPEC_BW_80)
+                       bw = WL_CHANSPEC_BW_40;
+               else if (bw == WL_CHANSPEC_BW_40)
+                       bw = WL_CHANSPEC_BW_20;
+               else
+                       bw = 0;
+               if (bw)
+                       goto set_channel;
+               ANDROID_ERROR(("%s: Invalid chanspec 0x%x\n", __FUNCTION__, chspec));\r
+               err = BCME_ERROR;
+       }\r
+\r
+       return err;\r
+}\r
+\r
+int\r
+wl_ext_channel(struct net_device *dev, char* command, int total_len)\r
+{\r
+       int ret;\r
+       int channel=0;\r
+       channel_info_t ci;\r
+       int bytes_written = 0;\r
+\r
+       ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command));\r
+\r
+       sscanf(command, "%*s %d", &channel);\r
+\r
+       if (channel > 0) {\r
+               ret = wl_ext_set_chanspec(dev, channel);\r
+       } else {\r
+               if (!(ret = wldev_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(channel_info_t), FALSE))) {\r
+                       ANDROID_TRACE(("hw_channel %d\n", ci.hw_channel));\r
+                       ANDROID_TRACE(("target_channel %d\n", ci.target_channel));\r
+                       ANDROID_TRACE(("scan_channel %d\n", ci.scan_channel));\r
+                       bytes_written = snprintf(command, sizeof(channel_info_t)+2, "channel %d", ci.hw_channel);\r
+                       ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));\r
+                       ret = bytes_written;\r
+               }\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+int\r
+wl_ext_channels(struct net_device *dev, char* command, int total_len)\r
+{\r
+       int ret, i;\r
+       int bytes_written = -1;\r
+       u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];\r
+       wl_uint32_list_t *list;\r
+\r
+       ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command));\r
+\r
+       memset(valid_chan_list, 0, sizeof(valid_chan_list));\r
+       list = (wl_uint32_list_t *)(void *) valid_chan_list;\r
+       list->count = htod32(WL_NUMCHANNELS);\r
+       ret = wldev_ioctl(dev, WLC_GET_VALID_CHANNELS, valid_chan_list, sizeof(valid_chan_list), 0);\r
+       if (ret<0) {\r
+               ANDROID_ERROR(("%s: get channels failed with %d\n", __FUNCTION__, ret));\r
+       } else {\r
+               bytes_written = snprintf(command, total_len, "channels");\r
+               for (i = 0; i < dtoh32(list->count); i++) {\r
+                       bytes_written += snprintf(command+bytes_written, total_len, " %d", dtoh32(list->element[i]));\r
+                       printf("%d ", dtoh32(list->element[i]));\r
+               }\r
+               printf("\n");\r
+               ret = bytes_written;\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+int\r
+wl_ext_roam_trigger(struct net_device *dev, char* command, int total_len)\r
+{\r
+       int ret = 0;\r
+       int roam_trigger[2] = {0, 0};\r
+       int trigger[2]= {0, 0};\r
+       int bytes_written=-1;\r
+\r
+       sscanf(command, "%*s %10d", &roam_trigger[0]);\r
+\r
+       if (roam_trigger[0]) {\r
+               roam_trigger[1] = WLC_BAND_ALL;\r
+               ret = wldev_ioctl(dev, WLC_SET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 1);\r
+               if (ret)\r
+                       ANDROID_ERROR(("WLC_SET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret));\r
+       } else {\r
+               roam_trigger[1] = WLC_BAND_2G;\r
+               ret = wldev_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 0);\r
+               if (!ret)\r
+                       trigger[0] = roam_trigger[0];\r
+               else\r
+                       ANDROID_ERROR(("2G WLC_GET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret));\r
+\r
+               roam_trigger[1] = WLC_BAND_5G;\r
+               ret = wldev_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger, sizeof(roam_trigger), 0);\r
+               if (!ret)\r
+                       trigger[1] = roam_trigger[0];\r
+               else\r
+                       ANDROID_ERROR(("5G WLC_GET_ROAM_TRIGGER ERROR %d ret=%d\n", roam_trigger[0], ret));\r
+\r
+               ANDROID_TRACE(("roam_trigger %d %d\n", trigger[0], trigger[1]));\r
+               bytes_written = snprintf(command, total_len, "%d %d", trigger[0], trigger[1]);\r
+               ret = bytes_written;\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+static int\r
+wl_ext_pattern_atoh(char *src, char *dst)\r
+{\r
+       int i;\r
+       if (strncmp(src, "0x", 2) != 0 &&\r
+           strncmp(src, "0X", 2) != 0) {\r
+               ANDROID_ERROR(("Mask invalid format. Needs to start with 0x\n"));\r
+               return -1;\r
+       }\r
+       src = src + 2; /* Skip past 0x */\r
+       if (strlen(src) % 2 != 0) {\r
+               DHD_ERROR(("Mask invalid format. Needs to be of even length\n"));\r
+               return -1;\r
+       }\r
+       for (i = 0; *src != '\0'; i++) {\r
+               char num[3];\r
+               bcm_strncpy_s(num, sizeof(num), src, 2);\r
+               num[2] = '\0';\r
+               dst[i] = (uint8)strtoul(num, NULL, 16);\r
+               src += 2;\r
+       }\r
+       return i;\r
+}\r
+\r
+int\r
+wl_ext_keep_alive(struct net_device *dev, char *command, int total_len)\r
+{\r
+       wl_mkeep_alive_pkt_t *mkeep_alive_pktp;\r
+       int ret = -1, i;\r
+       int     id, period=-1, len_bytes=0, buf_len=0;\r
+       char data[200]="\0";\r
+       char buf[WLC_IOCTL_SMLEN]="\0", iovar_buf[WLC_IOCTL_SMLEN]="\0";\r
+       int bytes_written = -1;\r
+\r
+       ANDROID_TRACE(("%s: command = %s\n", __FUNCTION__, command));\r
+       sscanf(command, "%*s %d %d %s", &id, &period, data);\r
+       ANDROID_TRACE(("%s: id=%d, period=%d, data=%s\n", __FUNCTION__, id, period, data));\r
+\r
+       if (period >= 0) {\r
+               mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *)buf;\r
+               mkeep_alive_pktp->version = htod16(WL_MKEEP_ALIVE_VERSION);\r
+               mkeep_alive_pktp->length = htod16(WL_MKEEP_ALIVE_FIXED_LEN);\r
+               mkeep_alive_pktp->keep_alive_id = id;\r
+               buf_len += WL_MKEEP_ALIVE_FIXED_LEN;\r
+               mkeep_alive_pktp->period_msec = period;\r
+               if (strlen(data)) {\r
+                       len_bytes = wl_ext_pattern_atoh(data, (char *) mkeep_alive_pktp->data);\r
+                       buf_len += len_bytes;\r
+               }\r
+               mkeep_alive_pktp->len_bytes = htod16(len_bytes);\r
+\r
+               ret = wl_ext_iovar_setbuf(dev, "mkeep_alive", buf, buf_len,\r
+                       iovar_buf, sizeof(iovar_buf), NULL);\r
+       } else {\r
+               if (id < 0)\r
+                       id = 0;\r
+               ret = wl_ext_iovar_getbuf(dev, "mkeep_alive", &id, sizeof(id), buf, sizeof(buf), NULL);\r
+               if (ret) {\r
+                       goto exit;\r
+               } else {\r
+                       mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) buf;\r
+                       printf("Id            :%d\n"\r
+                                  "Period (msec) :%d\n"\r
+                                  "Length        :%d\n"\r
+                                  "Packet        :0x",\r
+                                  mkeep_alive_pktp->keep_alive_id,\r
+                                  dtoh32(mkeep_alive_pktp->period_msec),\r
+                                  dtoh16(mkeep_alive_pktp->len_bytes));\r
+                       for (i=0; i<mkeep_alive_pktp->len_bytes; i++) {\r
+                               printf("%02x", mkeep_alive_pktp->data[i]);\r
+                       }\r
+                       printf("\n");\r
+               }\r
+               bytes_written = snprintf(command, total_len, "mkeep_alive_period_msec %d ", dtoh32(mkeep_alive_pktp->period_msec));\r
+               bytes_written += snprintf(command+bytes_written, total_len, "0x");\r
+               for (i=0; i<mkeep_alive_pktp->len_bytes; i++) {\r
+                       bytes_written += snprintf(command+bytes_written, total_len, "%x", mkeep_alive_pktp->data[i]);\r
+               }\r
+               ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));\r
+               ret = bytes_written;\r
+       }\r
+       \r
+exit:\r
+       return ret;\r
+}\r
+\r
+int\r
+wl_ext_pm(struct net_device *dev, char *command, int total_len)\r
+{\r
+       int pm=-1, ret = -1;\r
+       char *pm_local;\r
+       int bytes_written=-1;\r
+\r
+       ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command));\r
+\r
+       sscanf(command, "%*s %d", &pm);\r
+\r
+       if (pm >= 0) {\r
+               ret = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), FALSE);\r
+               if (ret)\r
+                       ANDROID_ERROR(("WLC_SET_PM ERROR %d ret=%d\n", pm, ret));\r
+       } else {\r
+               ret = wldev_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm), FALSE);\r
+               if (!ret) {\r
+                       ANDROID_TRACE(("%s: PM = %d\n", __func__, pm));\r
+                       if (pm == PM_OFF)\r
+                               pm_local = "PM_OFF";\r
+                       else if(pm == PM_MAX)\r
+                               pm_local = "PM_MAX";\r
+                       else if(pm == PM_FAST)\r
+                               pm_local = "PM_FAST";\r
+                       else {\r
+                               pm = 0;\r
+                               pm_local = "Invalid";\r
+                       }\r
+                       bytes_written = snprintf(command, total_len, "PM %s", pm_local);\r
+                       ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));\r
+                       ret = bytes_written;\r
+               }\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+static int\r
+wl_ext_monitor(struct net_device *dev, char *command, int total_len)\r
+{\r
+       int val, ret = -1;\r
+       int bytes_written=-1;\r
+\r
+       sscanf(command, "%*s %d", &val);\r
+\r
+       if (val >=0) {\r
+               ret = wldev_ioctl(dev, WLC_SET_MONITOR, &val, sizeof(int), 1);\r
+               if (ret)\r
+                       ANDROID_ERROR(("WLC_SET_MONITOR ERROR %d ret=%d\n", val, ret));\r
+       } else {\r
+               ret = wldev_ioctl(dev, WLC_GET_MONITOR, &val, sizeof(val), FALSE);\r
+               if (!ret) {\r
+                       ANDROID_TRACE(("%s: monitor = %d\n", __FUNCTION__, val));\r
+                       bytes_written = snprintf(command, total_len, "monitor %d", val);\r
+                       ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));\r
+                       ret = bytes_written;\r
+               }\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+#ifdef WL_EXT_IAPSTA\r
+struct wl_apsta_params g_apsta_params;\r
+static int\r
+wl_ext_parse_wep(char *key, struct wl_wsec_key *wsec_key)\r
+{\r
+       char hex[] = "XX";\r
+       unsigned char *data = wsec_key->data;\r
+       char *keystr = key;\r
+\r
+       switch (strlen(keystr)) {\r
+       case 5:\r
+       case 13:\r
+       case 16:\r
+               wsec_key->len = strlen(keystr);\r
+               memcpy(data, keystr, wsec_key->len + 1);\r
+               break;\r
+       case 12:\r
+       case 28:\r
+       case 34:\r
+       case 66:\r
+               /* strip leading 0x */\r
+               if (!strnicmp(keystr, "0x", 2))\r
+                       keystr += 2;\r
+               else\r
+                       return -1;\r
+               /* fall through */\r
+       case 10:\r
+       case 26:\r
+       case 32:\r
+       case 64:\r
+               wsec_key->len = strlen(keystr) / 2;\r
+               while (*keystr) {\r
+                       strncpy(hex, keystr, 2);\r
+                       *data++ = (char) strtoul(hex, NULL, 16);\r
+                       keystr += 2;\r
+               }\r
+               break;\r
+       default:\r
+               return -1;\r
+       }\r
+\r
+       switch (wsec_key->len) {\r
+       case 5:\r
+               wsec_key->algo = CRYPTO_ALGO_WEP1;\r
+               break;\r
+       case 13:\r
+               wsec_key->algo = CRYPTO_ALGO_WEP128;\r
+               break;\r
+       case 16:\r
+               /* default to AES-CCM */\r
+               wsec_key->algo = CRYPTO_ALGO_AES_CCM;\r
+               break;\r
+       case 32:\r
+               wsec_key->algo = CRYPTO_ALGO_TKIP;\r
+               break;\r
+       default:\r
+               return -1;\r
+       }\r
+\r
+       /* Set as primary wsec_key by default */\r
+       wsec_key->flags |= WL_PRIMARY_KEY;\r
+\r
+       return 0;\r
+}\r
+\r
+static int\r
+wl_ext_set_bgnmode(struct wl_if_info *cur_if)\r
+{\r
+       struct net_device *dev = cur_if->dev;\r
+       bgnmode_t bgnmode = cur_if->bgnmode;\r
+       int val;\r
+\r
+       if (bgnmode == 0)\r
+               return 0;\r
+\r
+       wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);\r
+       if (bgnmode == IEEE80211B) {\r
+               wl_ext_iovar_setint(dev, "nmode", 0);\r
+               val = 0;\r
+               wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);\r
+               ANDROID_TRACE(("%s: Network mode: B only\n", __FUNCTION__));\r
+       } else if (bgnmode == IEEE80211G) {\r
+               wl_ext_iovar_setint(dev, "nmode", 0);\r
+               val = 2;\r
+               wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);\r
+               ANDROID_TRACE(("%s: Network mode: G only\n", __FUNCTION__));\r
+       } else if (bgnmode == IEEE80211BG) {\r
+               wl_ext_iovar_setint(dev, "nmode", 0);\r
+               val = 1;\r
+               wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);\r
+               ANDROID_TRACE(("%s: Network mode: : B/G mixed\n", __FUNCTION__));\r
+       } else if (bgnmode == IEEE80211BGN) {\r
+               wl_ext_iovar_setint(dev, "nmode", 0);\r
+               wl_ext_iovar_setint(dev, "nmode", 1);\r
+               wl_ext_iovar_setint(dev, "vhtmode", 0);\r
+               val = 1;\r
+               wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);\r
+               ANDROID_TRACE(("%s: Network mode: : B/G/N mixed\n", __FUNCTION__));\r
+       } else if (bgnmode == IEEE80211BGNAC) {\r
+               wl_ext_iovar_setint(dev, "nmode", 0);\r
+               wl_ext_iovar_setint(dev, "nmode", 1);\r
+               wl_ext_iovar_setint(dev, "vhtmode", 1);\r
+               val = 1;\r
+               wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);\r
+               ANDROID_TRACE(("%s: Network mode: : B/G/N/AC mixed\n", __FUNCTION__));\r
+       }\r
+       wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);\r
+\r
+       return 0;\r
+}\r
+\r
+static int\r
+wl_ext_set_amode(struct wl_if_info *cur_if, struct wl_apsta_params *apsta_params)\r
+{\r
+       struct net_device *dev = cur_if->dev;\r
+       authmode_t amode = cur_if->amode;\r
+       int auth=0, wpa_auth=0;\r
+\r
+       if (amode == AUTH_OPEN) {\r
+               auth = 0;\r
+               wpa_auth = 0;\r
+               ANDROID_TRACE(("%s: Authentication: Open System\n", __FUNCTION__));\r
+       } else if (amode == AUTH_SHARED) {\r
+               auth = 1;\r
+               wpa_auth = 0;\r
+               ANDROID_TRACE(("%s: Authentication: Shared Key\n", __FUNCTION__));\r
+       } else if (amode == AUTH_WPAPSK) {\r
+               auth = 0;\r
+               wpa_auth = 4;\r
+               ANDROID_TRACE(("%s: Authentication: WPA-PSK\n", __FUNCTION__));\r
+       } else if (amode == AUTH_WPA2PSK) {\r
+               auth = 0;\r
+               wpa_auth = 128;\r
+               ANDROID_TRACE(("%s: Authentication: WPA2-PSK\n", __FUNCTION__));\r
+       } else if (amode == AUTH_WPAWPA2PSK) {\r
+               auth = 0;\r
+               wpa_auth = 132;\r
+               ANDROID_TRACE(("%s: Authentication: WPA/WPA2-PSK\n", __FUNCTION__));\r
+       }\r
+       wl_ext_iovar_setint(dev, "auth", auth);\r
+\r
+       wl_ext_iovar_setint(dev, "wpa_auth", wpa_auth);\r
+\r
+       return 0;\r
+}\r
+\r
+static int\r
+wl_ext_set_emode(struct wl_if_info *cur_if, struct wl_apsta_params *apsta_params)\r
+{\r
+       struct net_device *dev = cur_if->dev;\r
+       int wsec=0;\r
+       struct wl_wsec_key wsec_key;\r
+       wsec_pmk_t psk;\r
+       encmode_t emode = cur_if->emode;\r
+       char *key = cur_if->key;\r
+\r
+       memset(&wsec_key, 0, sizeof(wsec_key));\r
+       memset(&psk, 0, sizeof(psk));\r
+       if (emode == ENC_NONE) {\r
+               wsec = 0;\r
+               ANDROID_TRACE(("%s: Encryption: No securiy\n", __FUNCTION__));\r
+       } else if (emode == ENC_WEP) {\r
+               wsec = 1;\r
+               wl_ext_parse_wep(key, &wsec_key);\r
+               ANDROID_TRACE(("%s: Encryption: WEP\n", __FUNCTION__));\r
+               ANDROID_TRACE(("%s: Key: %s\n", __FUNCTION__, wsec_key.data));\r
+       } else if (emode == ENC_TKIP) {\r
+               wsec = 2;\r
+               psk.key_len = strlen(key);\r
+               psk.flags = WSEC_PASSPHRASE;\r
+               memcpy(psk.key, key, strlen(key));\r
+               ANDROID_TRACE(("%s: Encryption: TKIP\n", __FUNCTION__));\r
+               ANDROID_TRACE(("%s: Key: %s\n", __FUNCTION__, psk.key));\r
+       } else if (emode == ENC_AES) {\r
+               wsec = 4;\r
+               psk.key_len = strlen(key);\r
+               psk.flags = WSEC_PASSPHRASE;\r
+               memcpy(psk.key, key, strlen(key));\r
+               ANDROID_TRACE(("%s: Encryption: AES\n", __FUNCTION__));\r
+               ANDROID_TRACE(("%s: Key: %s\n", __FUNCTION__, psk.key));\r
+       } else if (emode == ENC_TKIPAES) {\r
+               wsec = 6;\r
+               psk.key_len = strlen(key);\r
+               psk.flags = WSEC_PASSPHRASE;\r
+               memcpy(psk.key, key, strlen(key));\r
+               ANDROID_TRACE(("%s: Encryption: TKIP/AES\n", __FUNCTION__));\r
+               ANDROID_TRACE(("%s: Key: %s\n", __FUNCTION__, psk.key));\r
+       }\r
+\r
+       wl_ext_iovar_setint(dev, "wsec", wsec);\r
+\r
+       if (wsec == 1) {\r
+               wl_ext_ioctl(dev, WLC_SET_KEY, &wsec_key, sizeof(wsec_key), 1);\r
+       } else if (emode == ENC_TKIP || emode == ENC_AES || emode == ENC_TKIPAES) {\r
+               if (dev) {\r
+                       if (cur_if->ifmode == ISTA_MODE)\r
+                               wl_ext_iovar_setint(dev, "sup_wpa", 1);\r
+                       wl_ext_ioctl(dev, WLC_SET_WSEC_PMK, &psk, sizeof(psk), 1);\r
+               } else {\r
+                       ANDROID_ERROR(("%s: apdev is null\n", __FUNCTION__));\r
+               }\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+static int\r
+wl_ext_iapsta_init(struct net_device *dev, char *command, int total_len)\r
+{\r
+       s32 val = 0;\r
+       char *pch, *pick_tmp, *param;\r
+       wlc_ssid_t ssid = { 0, {0} };\r
+       s8 iovar_buf[WLC_IOCTL_SMLEN];\r
+       struct wl_apsta_params *apsta_params = &g_apsta_params;\r
+       wl_interface_create_t iface;\r
+       struct dhd_pub *dhd;\r
+       wl_p2p_if_t ifreq;\r
+\r
+       if (apsta_params->init) {\r
+               ANDROID_ERROR(("%s: don't init twice\n", __FUNCTION__));\r
+               return -1;\r
+       }\r
+\r
+       dhd = dhd_get_pub(dev);\r
+       memset(apsta_params, 0, sizeof(struct wl_apsta_params));\r
+\r
+       ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len));\r
+\r
+       pick_tmp = command;\r
+       param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_init\r
+       param = bcmstrtok(&pick_tmp, " ", 0);\r
+       while (param != NULL) {\r
+               if (!strcmp(param, "mode")) {\r
+                       pch = bcmstrtok(&pick_tmp, " ", 0);\r
+                       if (pch) {\r
+                               if (!strcmp(pch, "sta")) {\r
+                                       apsta_params->apstamode = ISTAONLY_MODE;\r
+                               } else if (!strcmp(pch, "ap")) {\r
+                                       apsta_params->apstamode = IAPONLY_MODE;\r
+                               } else if (!strcmp(pch, "apsta")) {\r
+                                       apsta_params->apstamode = IAPSTA_MODE;\r
+                               } else if (!strcmp(pch, "dualap")) {\r
+                                       apsta_params->apstamode = IDUALAP_MODE;\r
+                               } else if (!strcmp(pch, "gosta")) {\r
+                                       if (!FW_SUPPORTED(dhd, p2p)) {\r
+                                               return -1;\r
+                                       }\r
+                                       apsta_params->apstamode = IGOSTA_MODE;\r
+                               } else if (!strcmp(pch, "gcsta")) {\r
+                                       if (!FW_SUPPORTED(dhd, p2p)) {\r
+                                               return -1;\r
+                                       }\r
+                                       apsta_params->apstamode = IGCSTA_MODE;\r
+                               } else {\r
+                                       ANDROID_ERROR(("%s: mode [sta|ap|apsta|dualap]\n", __FUNCTION__));\r
+                                       return -1;\r
+                               }\r
+                       }\r
+               } else if (!strcmp(param, "vifname")) {\r
+                       pch = bcmstrtok(&pick_tmp, " ", 0);\r
+                       if (pch)\r
+                               strcpy(apsta_params->vif.ifname, pch);\r
+                       else {\r
+                               ANDROID_ERROR(("%s: vifname [wlan1]\n", __FUNCTION__));\r
+                               return -1;\r
+                       }\r
+               }\r
+               param = bcmstrtok(&pick_tmp, " ", 0);\r
+       }\r
+\r
+       if (apsta_params->apstamode == 0) {\r
+               ANDROID_ERROR(("%s: mode [sta|ap|apsta|dualap]\n", __FUNCTION__));\r
+               return -1;\r
+       }\r
+\r
+       apsta_params->pif.dev = dev;\r
+       apsta_params->pif.bssidx = 0;\r
+       strcpy(apsta_params->pif.ifname, dev->name);\r
+       strcpy(apsta_params->pif.ssid, "tttp");\r
+       apsta_params->pif.maxassoc = -1;\r
+       apsta_params->pif.channel = 1;\r
+\r
+       if (!strlen(apsta_params->vif.ifname))\r
+               strcpy(apsta_params->vif.ifname, "wlan1");\r
+       strcpy(apsta_params->vif.ssid, "tttv");\r
+       apsta_params->vif.maxassoc = -1;\r
+       apsta_params->vif.channel = 1;\r
+\r
+       if (apsta_params->apstamode == ISTAONLY_MODE) {\r
+               apsta_params->pif.ifmode = ISTA_MODE;\r
+               apsta_params->pif.ifstate = IF_STATE_INIT;\r
+               wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);\r
+               wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls\r
+               // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off\r
+               wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);\r
+       } else if (apsta_params->apstamode == IAPONLY_MODE) {\r
+               apsta_params->pif.ifmode = IAP_MODE;\r
+               apsta_params->pif.ifstate = IF_STATE_INIT;\r
+               wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);\r
+#ifdef ARP_OFFLOAD_SUPPORT
+               /* IF SoftAP is enabled, disable arpoe */\r
+               dhd_arp_offload_set(dhd, 0);\r
+               dhd_arp_offload_enable(dhd, FALSE);\r
+#endif /* ARP_OFFLOAD_SUPPORT */\r
+               wl_ext_iovar_setint(dev, "mpc", 0);\r
+               wl_ext_iovar_setint(dev, "apsta", 0);\r
+               val = 1;\r
+               wl_ext_ioctl(dev, WLC_SET_AP, &val, sizeof(val), 1);\r
+       } else if (apsta_params->apstamode == IAPSTA_MODE) {\r
+               apsta_params->pif.ifmode = ISTA_MODE;\r
+               apsta_params->pif.ifstate = IF_STATE_INIT;\r
+               apsta_params->vif.ifmode = IAP_MODE;\r
+               apsta_params->vif.ifstate = IF_STATE_INIT;\r
+               wl_ext_iovar_setint(dev, "mpc", 0);\r
+               wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);\r
+               wl_ext_iovar_setint(dev, "apsta", 1);\r
+               wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);\r
+               if (FW_SUPPORTED(dhd, rsdb)) {\r
+                       bzero(&iface, sizeof(wl_interface_create_t));\r
+                       iface.ver = WL_INTERFACE_CREATE_VER;\r
+                       iface.flags = WL_INTERFACE_CREATE_AP;\r
+                       wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), iovar_buf,\r
+                               WLC_IOCTL_SMLEN, 1, NULL);\r
+               } else {\r
+                       wl_ext_iovar_setbuf_bsscfg(dev, "ssid", &ssid, sizeof(ssid), iovar_buf,\r
+                               WLC_IOCTL_SMLEN, 1, NULL);\r
+               }\r
+       }\r
+       else if (apsta_params->apstamode == IDUALAP_MODE) {\r
+               apsta_params->pif.ifmode = IAP_MODE;\r
+               apsta_params->pif.ifstate = IF_STATE_INIT;\r
+               apsta_params->vif.ifmode = IAP_MODE;\r
+               apsta_params->vif.ifstate = IF_STATE_INIT;\r
+               wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);\r
+               wl_ext_iovar_setint(dev, "apsta", 0);\r
+               wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);\r
+               val = 1;\r
+               wl_ext_ioctl(dev, WLC_SET_AP, &val, sizeof(val), 1);\r
+               /* IF SoftAP is enabled, disable arpoe or wlan1 will ping fail */\r
+#ifdef ARP_OFFLOAD_SUPPORT
+               /* IF SoftAP is enabled, disable arpoe */\r
+               dhd_arp_offload_set(dhd, 0);\r
+               dhd_arp_offload_enable(dhd, FALSE);\r
+#endif /* ARP_OFFLOAD_SUPPORT */\r
+               bzero(&iface, sizeof(wl_interface_create_t));\r
+               iface.ver = WL_INTERFACE_CREATE_VER;\r
+               iface.flags = WL_INTERFACE_CREATE_AP;\r
+               wl_ext_iovar_getbuf_bsscfg(dev, "interface_create", &iface, sizeof(iface), iovar_buf,\r
+                       WLC_IOCTL_SMLEN, 1, NULL);\r
+       }\r
+       else if (apsta_params->apstamode == IGOSTA_MODE) {\r
+               apsta_params->pif.ifmode = ISTA_MODE;\r
+               apsta_params->pif.ifstate = IF_STATE_INIT;\r
+               apsta_params->vif.ifmode = IAP_MODE;\r
+               apsta_params->vif.ifstate = IF_STATE_INIT;\r
+               wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);\r
+               wl_ext_iovar_setint(dev, "apsta", 1);\r
+               wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);\r
+               bzero(&ifreq, sizeof(wl_p2p_if_t));\r
+               ifreq.type = htod32(WL_P2P_IF_GO);\r
+               wl_ext_iovar_setbuf(dev, "p2p_ifadd", &ifreq, sizeof(ifreq),\r
+                       iovar_buf, WLC_IOCTL_SMLEN, NULL);\r
+       }\r
+       else if (apsta_params->apstamode == IGCSTA_MODE) {\r
+               apsta_params->pif.ifmode = ISTA_MODE;\r
+               apsta_params->pif.ifstate = IF_STATE_INIT;\r
+               apsta_params->vif.ifmode = ISTA_MODE;\r
+               apsta_params->vif.ifstate = IF_STATE_INIT;\r
+               wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);\r
+               wl_ext_iovar_setint(dev, "apsta", 1);\r
+               wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);\r
+               bzero(&ifreq, sizeof(wl_p2p_if_t));\r
+               ifreq.type = htod32(WL_P2P_IF_CLIENT);\r
+               wl_ext_iovar_setbuf(dev, "p2p_ifadd", &ifreq, sizeof(ifreq),\r
+                       iovar_buf, WLC_IOCTL_SMLEN, NULL);\r
+       }\r
+\r
+       wl_ext_get_ioctl_ver(dev, &apsta_params->ioctl_ver);\r
+       printf("%s: apstamode=%d\n", __FUNCTION__, apsta_params->apstamode);\r
+\r
+       apsta_params->init = TRUE;\r
+\r
+       return 0;\r
+}\r
+\r
+static int\r
+wl_ext_iapsta_config(struct net_device *dev, char *command, int total_len)\r
+{\r
+       int i;\r
+       char *pch, *pick_tmp, *param;\r
+       struct wl_apsta_params *apsta_params = &g_apsta_params;\r
+       char ifname[IFNAMSIZ+1];\r
+       struct wl_if_info *cur_if = &apsta_params->pif;\r
+\r
+       if (!apsta_params->init) {\r
+               ANDROID_ERROR(("%s: please init first\n", __FUNCTION__));\r
+               return -1;\r
+       }\r
+\r
+       ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len));\r
+\r
+       pick_tmp = command;\r
+       param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_config\r
+       param = bcmstrtok(&pick_tmp, " ", 0);\r
+\r
+       if (param != NULL) {\r
+               if (strcmp(param, "ifname")) {\r
+                       ANDROID_ERROR(("%s: first arg must be ifname\n", __FUNCTION__));\r
+                       return -1;\r
+               }\r
+       }\r
+\r
+       while (param != NULL) {\r
+               if (!strcmp(param, "ifname")) {\r
+                       pch = bcmstrtok(&pick_tmp, " ", 0);\r
+                       if (pch)\r
+                               strcpy(ifname, pch);\r
+                       else {\r
+                               ANDROID_ERROR(("%s: ifname [wlanX]\n", __FUNCTION__));\r
+                               return -1;\r
+                       }\r
+                       if (!strcmp(apsta_params->pif.dev->name, ifname)) {\r
+                               cur_if = &apsta_params->pif;\r
+                       } else if (!strcmp(apsta_params->vif.ifname, ifname)) {\r
+                               cur_if = &apsta_params->vif;\r
+                       } else {\r
+                               ANDROID_ERROR(("%s: wrong ifname=%s in apstamode=%d\n", __FUNCTION__,\r
+                                       ifname, apsta_params->apstamode));\r
+                               return -1;\r
+                       }\r
+               } else if (!strcmp(param, "ssid")) {\r
+                       pch = bcmstrtok(&pick_tmp, " ", 0);\r
+                       if (pch)\r
+                               strcpy(cur_if->ssid, pch);\r
+               } else if (!strcmp(param, "bssid")) {\r
+                       pch = bcmstrtok(&pick_tmp, ": ", 0);\r
+                       for (i=0; i<6 && pch; i++) {\r
+                               ((u8 *)&cur_if->bssid)[i] = (int)simple_strtol(pch, NULL, 16);\r
+                               pch = bcmstrtok(&pick_tmp, ": ", 0);\r
+                       }\r
+               } else if (!strcmp(param, "bgnmode")) {\r
+                       pch = bcmstrtok(&pick_tmp, " ", 0);\r
+                       if (pch) {\r
+                               if (!strcmp(pch, "b"))\r
+                                       cur_if->bgnmode = IEEE80211B;\r
+                               else if (!strcmp(pch, "g"))\r
+                                       cur_if->bgnmode = IEEE80211G;\r
+                               else if (!strcmp(pch, "bg"))\r
+                                       cur_if->bgnmode = IEEE80211BG;\r
+                               else if (!strcmp(pch, "bgn"))\r
+                                       cur_if->bgnmode = IEEE80211BGN;\r
+                               else if (!strcmp(pch, "bgnac"))\r
+                                       cur_if->bgnmode = IEEE80211BGNAC;\r
+                               else {\r
+                                       ANDROID_ERROR(("%s: bgnmode [b|g|bg|bgn|bgnac]\n", __FUNCTION__));\r
+                                       return -1;\r
+                               }\r
+                       }\r
+               } else if (!strcmp(param, "hidden")) {\r
+                       pch = bcmstrtok(&pick_tmp, " ", 0);\r
+                       if (pch) {\r
+                               if (!strcmp(pch, "n"))\r
+                                       cur_if->hidden = 0;\r
+                               else if (!strcmp(pch, "y"))\r
+                                       cur_if->hidden = 1;\r
+                               else {\r
+                                       ANDROID_ERROR(("%s: hidden [y|n]\n", __FUNCTION__));\r
+                                       return -1;\r
+                               }\r
+                       }\r
+               } else if (!strcmp(param, "maxassoc")) {\r
+                       pch = bcmstrtok(&pick_tmp, " ", 0);\r
+                       if (pch)\r
+                               cur_if->maxassoc = (int)simple_strtol(pch, NULL, 10);\r
+               } else if (!strcmp(param, "chan")) {\r
+                       pch = bcmstrtok(&pick_tmp, " ", 0);\r
+                       if (pch)\r
+                               cur_if->channel = (int)simple_strtol(pch, NULL, 10);\r
+               } else if (!strcmp(param, "amode")) {\r
+                       pch = bcmstrtok(&pick_tmp, " ", 0);\r
+                       if (pch) {\r
+                               if (!strcmp(pch, "open"))\r
+                                       cur_if->amode = AUTH_OPEN;\r
+                               else if (!strcmp(pch, "shared"))\r
+                                       cur_if->amode = AUTH_SHARED;\r
+                               else if (!strcmp(pch, "wpapsk"))\r
+                                       cur_if->amode = AUTH_WPAPSK;\r
+                               else if (!strcmp(pch, "wpa2psk"))\r
+                                       cur_if->amode = AUTH_WPA2PSK;\r
+                               else if (!strcmp(pch, "wpawpa2psk")) \r
+                                       cur_if->amode = AUTH_WPAWPA2PSK;\r
+                               else {\r
+                                       ANDROID_ERROR(("%s: amode [open|shared|wpapsk|wpa2psk|wpawpa2psk]\n",\r
+                                               __FUNCTION__));\r
+                                       return -1;\r
+                               }\r
+                       }\r
+               } else if (!strcmp(param, "emode")) {\r
+                       pch = bcmstrtok(&pick_tmp, " ", 0);\r
+                       if (pch) {\r
+                               if (!strcmp(pch, "none"))\r
+                                       cur_if->emode = ENC_NONE;\r
+                               else if (!strcmp(pch, "wep"))\r
+                                       cur_if->emode = ENC_WEP;\r
+                               else if (!strcmp(pch, "tkip"))\r
+                                       cur_if->emode = ENC_TKIP;\r
+                               else if (!strcmp(pch, "aes"))\r
+                                       cur_if->emode = ENC_AES;\r
+                               else if (!strcmp(pch, "tkipaes")) \r
+                                       cur_if->emode = ENC_TKIPAES;\r
+                               else {\r
+                                       ANDROID_ERROR(("%s: emode [none|wep|tkip|aes|tkipaes]\n",\r
+                                               __FUNCTION__));\r
+                                       return -1;\r
+                               }\r
+                       }\r
+               } else if (!strcmp(param, "key")) {\r
+                       pch = bcmstrtok(&pick_tmp, " ", 0);\r
+                       if (pch) {\r
+                               strcpy(cur_if->key, pch);\r
+                       }\r
+               }\r
+               param = bcmstrtok(&pick_tmp, " ", 0);\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+static int\r
+wl_ext_iapsta_disable(struct net_device *dev, char *command, int total_len)\r
+{\r
+       char *pch, *pick_tmp, *param;\r
+       s8 iovar_buf[WLC_IOCTL_SMLEN];\r
+       wlc_ssid_t ssid = { 0, {0} };\r
+       scb_val_t scbval;\r
+       struct {\r
+               s32 tmp;\r
+               s32 cfg;
+               s32 val;
+       } bss_setbuf;\r
+       struct wl_apsta_params *apsta_params = &g_apsta_params;\r
+       apstamode_t apstamode = apsta_params->apstamode;\r
+       char ifname[IFNAMSIZ+1];\r
+       struct wl_if_info *cur_if;\r
+       struct dhd_pub *dhd;\r
+\r
+       if (!apsta_params->init) {\r
+               ANDROID_ERROR(("%s: please init first\n", __FUNCTION__));\r
+               return -1;\r
+       }\r
+\r
+       ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len));\r
+       dhd = dhd_get_pub(dev);\r
+\r
+       pick_tmp = command;\r
+       param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_disable\r
+       param = bcmstrtok(&pick_tmp, " ", 0);\r
+       while (param != NULL) {\r
+               if (!strcmp(param, "ifname")) {\r
+                       pch = bcmstrtok(&pick_tmp, " ", 0);\r
+                       if (pch)\r
+                               strcpy(ifname, pch);\r
+                       else {\r
+                               ANDROID_ERROR(("%s: ifname [wlanX]\n", __FUNCTION__));\r
+                               return -1;\r
+                       }\r
+               }\r
+               param = bcmstrtok(&pick_tmp, " ", 0);\r
+       }\r
+       if (!strcmp(apsta_params->pif.dev->name, ifname)) {\r
+               cur_if = &apsta_params->pif;\r
+       } else if (!strcmp(apsta_params->vif.ifname, ifname)) {\r
+               cur_if = &apsta_params->vif;\r
+       } else {\r
+               ANDROID_ERROR(("%s: wrong ifname=%s\n", __FUNCTION__, ifname));\r
+               return -1;\r
+       }\r
+       if (!cur_if->dev) {\r
+               ANDROID_ERROR(("%s: %s is not ready\n", __FUNCTION__, ifname));\r
+               return -1;\r
+       }\r
+\r
+       if (cur_if->ifmode == ISTA_MODE) {\r
+               wl_ext_ioctl(cur_if->dev, WLC_DISASSOC, NULL, 0, 1);\r
+       } else if (cur_if->ifmode == IAP_MODE) {\r
+               // deauthenticate all STA first\r
+               memcpy(scbval.ea.octet, &ether_bcast, ETHER_ADDR_LEN);\r
+               wl_ext_ioctl(cur_if->dev, WLC_SCB_DEAUTHENTICATE, &scbval.ea, ETHER_ADDR_LEN, 1);\r
+       }\r
+\r
+       if (apstamode == IAPONLY_MODE) {\r
+               wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);\r
+               wl_ext_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); // reset ssid\r
+               wl_ext_iovar_setint(dev, "mpc", 1);\r
+       } else if ((apstamode==IAPSTA_MODE || apstamode==IGOSTA_MODE) &&\r
+                       cur_if->ifmode == IAP_MODE) {\r
+               // vif is AP mode\r
+               bss_setbuf.tmp = 0xffffffff;\r
+               bss_setbuf.cfg = 0; // must be 0, or wlan1 can not be down\r
+               bss_setbuf.val = htod32(0);\r
+               wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf),\r
+                       iovar_buf, WLC_IOCTL_SMLEN, NULL);\r
+               wl_ext_iovar_setint(dev, "mpc", 1);\r
+#ifdef ARP_OFFLOAD_SUPPORT\r
+               /* IF SoftAP is disabled, enable arpoe back for STA mode. */\r
+               dhd_arp_offload_set(dhd, dhd_arp_mode);\r
+               dhd_arp_offload_enable(dhd, TRUE);\r
+#endif /* ARP_OFFLOAD_SUPPORT */\r
+       } else if (apstamode == IDUALAP_MODE) {\r
+               bss_setbuf.tmp = 0xffffffff;\r
+               bss_setbuf.cfg = 0; // must be 0, or wlan1 can not be down\r
+               bss_setbuf.val = htod32(0);\r
+               wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf),\r
+                       iovar_buf, WLC_IOCTL_SMLEN, NULL);\r
+       }\r
+\r
+#ifdef PROP_TXSTATUS_VSDB\r
+#if defined(BCMSDIO)\r
+       if (cur_if==&apsta_params->vif && dhd->conf->disable_proptx!=0) {\r
+               bool enabled;\r
+               dhd_wlfc_get_enable(dhd, &enabled);\r
+               if (enabled) {\r
+                       dhd_wlfc_deinit(dhd);\r
+               }\r
+       }\r
+#endif \r
+#endif /* PROP_TXSTATUS_VSDB */\r
+\r
+       cur_if->ifstate = IF_STATE_DISALBE;\r
+       printf("%s: apstamode=%d, ifname=%s\n", __FUNCTION__, apstamode, ifname);\r
+\r
+       return 0;\r
+}\r
+\r
+static int\r
+wl_ext_iapsta_enable(struct net_device *dev, char *command, int total_len)\r
+{\r
+       int ret = 0;\r
+       s32 val = 0;\r
+       char *pch, *pick_tmp, *param;\r
+       s8 iovar_buf[WLC_IOCTL_SMLEN];\r
+       wlc_ssid_t ssid = { 0, {0} };\r
+       struct {\r
+               s32 cfg;
+               s32 val;
+       } bss_setbuf;\r
+       struct wl_apsta_params *apsta_params = &g_apsta_params;\r
+       apstamode_t apstamode = apsta_params->apstamode;\r
+       char ifname[IFNAMSIZ+1];\r
+       struct wl_if_info *cur_if;\r
+       char cmd[128] = "iapsta_stop ifname ";\r
+       struct dhd_pub *dhd;\r
+\r
+       if (!apsta_params->init) {\r
+               ANDROID_ERROR(("%s: please init first\n", __FUNCTION__));\r
+               return -1;\r
+       }\r
+\r
+       ANDROID_TRACE(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len));\r
+       dhd = dhd_get_pub(dev);\r
+\r
+       pick_tmp = command;\r
+       param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_enable\r
+       param = bcmstrtok(&pick_tmp, " ", 0);\r
+       while (param != NULL) {\r
+               if (!strcmp(param, "ifname")) {\r
+                       pch = bcmstrtok(&pick_tmp, " ", 0);\r
+                       if (pch)\r
+                               strcpy(ifname, pch);\r
+                       else {\r
+                               ANDROID_ERROR(("%s: ifname [wlanX]\n", __FUNCTION__));\r
+                               return -1;\r
+                       }\r
+               }\r
+               param = bcmstrtok(&pick_tmp, " ", 0);\r
+       }\r
+       if (!strcmp(apsta_params->pif.dev->name, ifname)) {\r
+               cur_if = &apsta_params->pif;\r
+       } else if (!strcmp(apsta_params->vif.ifname, ifname)) {\r
+               cur_if = &apsta_params->vif;\r
+       } else {\r
+               ANDROID_ERROR(("%s: wrong ifname=%s\n", __FUNCTION__, ifname));\r
+               return -1;\r
+       }\r
+       if (!cur_if->dev) {\r
+               ANDROID_ERROR(("%s: %s is not ready\n", __FUNCTION__, ifname));\r
+               return -1;\r
+       }\r
+       ssid.SSID_len = strlen(cur_if->ssid);\r
+       memcpy(ssid.SSID, cur_if->ssid, ssid.SSID_len);\r
+       ANDROID_TRACE(("%s: apstamode=%d, bssidx=%d\n", __FUNCTION__, apstamode, cur_if->bssidx));\r
+\r
+       snprintf(cmd, 128, "iapsta_stop ifname %s", cur_if->ifname);\r
+       ret = wl_ext_iapsta_disable(dev, cmd, strlen(cmd));\r
+       if (ret)\r
+               goto exit;\r
+\r
+       if (cur_if == &apsta_params->vif) {\r
+               wl_ext_iovar_setbuf(cur_if->dev, "cur_etheraddr", (u8 *)cur_if->dev->dev_addr,\r
+                       ETHER_ADDR_LEN, iovar_buf, WLC_IOCTL_SMLEN, NULL);\r
+       }\r
+\r
+       // set ssid for AP\r
+       if (cur_if->ifmode == IAP_MODE) {\r
+               wl_ext_iovar_setint(dev, "mpc", 0);\r
+               if (apstamode == IAPONLY_MODE) {\r
+                       wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);\r
+               } else if (apstamode==IAPSTA_MODE || apstamode==IGOSTA_MODE) {\r
+                       wl_ext_iovar_setbuf_bsscfg(cur_if->dev, "ssid", &ssid, sizeof(ssid),\r
+                               iovar_buf, WLC_IOCTL_SMLEN, cur_if->bssidx, NULL);\r
+               }\r
+       }\r
+\r
+       if (cur_if->ifmode == IAP_MODE) {\r
+               wl_ext_set_bgnmode(cur_if);\r
+               wl_ext_set_chanspec(cur_if->dev, cur_if->channel);\r
+       }\r
+       wl_ext_set_amode(cur_if, apsta_params);\r
+       wl_ext_set_emode(cur_if, apsta_params);\r
+\r
+       if (apstamode == ISTAONLY_MODE || apstamode == IGCSTA_MODE) {\r
+               if (!ETHER_ISBCAST(&cur_if->bssid) && !ETHER_ISNULLADDR(&cur_if->bssid)) {\r
+                       printf("%s: BSSID: %pM\n", __FUNCTION__, &cur_if->bssid);\r
+                       wl_ext_ioctl(cur_if->dev, WLC_SET_BSSID, &cur_if->bssid, ETHER_ADDR_LEN, 1);\r
+               }\r
+               val = 1;\r
+               wl_ext_ioctl(dev, WLC_SET_INFRA, &val, sizeof(val), 1);\r
+       }\r
+       if (cur_if->ifmode == IAP_MODE) {\r
+               if (cur_if->maxassoc >= 0)\r
+                       wl_ext_iovar_setint(dev, "maxassoc", cur_if->maxassoc);\r
+               printf("%s: Broadcast SSID: %s\n", __FUNCTION__, cur_if->hidden ? "OFF":"ON");\r
+               // terence: fix me, hidden does not work in dualAP mode\r
+               wl_ext_ioctl(cur_if->dev, WLC_SET_CLOSED, &cur_if->hidden, sizeof(cur_if->hidden), 1);\r
+       }\r
+\r
+       if (apstamode == ISTAONLY_MODE || apstamode == IGCSTA_MODE) {\r
+               wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);\r
+       } else if (apstamode == IAPONLY_MODE) {\r
+               wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);\r
+               wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);\r
+       } else if (apstamode == IAPSTA_MODE || apstamode == IGOSTA_MODE) {\r
+               if (cur_if->ifmode == ISTA_MODE) {\r
+                       wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);\r
+               } else {\r
+                       if (FW_SUPPORTED(dhd, rsdb)) {\r
+                               wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);\r
+                       } else {\r
+                               bss_setbuf.cfg = htod32(cur_if->bssidx);\r
+                               bss_setbuf.val = htod32(1);\r
+                               wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf),\r
+                                       iovar_buf, WLC_IOCTL_SMLEN, NULL);\r
+                       }\r
+#ifdef ARP_OFFLOAD_SUPPORT
+                       /* IF SoftAP is enabled, disable arpoe */\r
+                       dhd_arp_offload_set(dhd, 0);\r
+                       dhd_arp_offload_enable(dhd, FALSE);\r
+#endif /* ARP_OFFLOAD_SUPPORT */\r
+               }\r
+       }\r
+       else if (apstamode == IDUALAP_MODE) {\r
+               wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);\r
+       }\r
+\r
+#ifdef PROP_TXSTATUS_VSDB\r
+#if defined(BCMSDIO)\r
+       if (cur_if==&apsta_params->vif && !disable_proptx) {\r
+               bool enabled;\r
+               dhd_wlfc_get_enable(dhd, &enabled);\r
+               if (!enabled) {\r
+                       dhd_wlfc_init(dhd);\r
+                       wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);\r
+               }\r
+       }\r
+#endif 
+#endif /* PROP_TXSTATUS_VSDB */\r
+\r
+       printf("%s: ifname=%s, SSID: %s\n", __FUNCTION__, ifname, cur_if->ssid);\r
+\r
+       cur_if->ifstate = IF_STATE_ENABLE;\r
+\r
+exit:\r
+       return ret;\r
+}\r
+
+void\r
+wl_android_ext_iapsta_disconnect_sta(struct net_device *dev, u32 channel)\r
+{\r
+       struct wl_apsta_params *apsta_params = &g_apsta_params;\r
+       struct wl_if_info *cur_if = &apsta_params->vif;\r
+       scb_val_t scbval;\r
+       int ret;\r
+       channel_info_t ci;\r
+       struct dhd_pub *dhd;\r
+\r
+       if (apsta_params->apstamode==IAPSTA_MODE && cur_if->ifstate==IF_STATE_ENABLE) {\r
+               dhd = dhd_get_pub(dev);\r
+               if (!FW_SUPPORTED(dhd, vsdb)) {\r
+                       if (!(ret = wldev_ioctl(cur_if->dev, WLC_GET_CHANNEL, &ci, sizeof(channel_info_t), FALSE))) {\r
+                               if (channel != ci.target_channel) {\r
+                                       printf("%s: deauthenticate all STA on vif\n", __FUNCTION__);\r
+                                       memcpy(scbval.ea.octet, &ether_bcast, ETHER_ADDR_LEN);\r
+                                       wl_ext_ioctl(cur_if->dev, WLC_SCB_DEAUTHENTICATE, &scbval.ea, ETHER_ADDR_LEN, 1);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
+
+int wl_android_ext_attach_netdev(struct net_device *net, uint8 bssidx)\r
+{\r
+       g_apsta_params.vif.dev = net;\r
+       g_apsta_params.vif.bssidx = bssidx;\r
+       if (strlen(g_apsta_params.vif.ifname)) {\r
+               memset(net->name, 0, sizeof(IFNAMSIZ));\r
+               strcpy(net->name, g_apsta_params.vif.ifname);\r
+               net->name[IFNAMSIZ - 1] = '\0';\r
+       }\r
+       if (g_apsta_params.pif.dev) {\r
+               memcpy(net->dev_addr, g_apsta_params.pif.dev->dev_addr, ETHER_ADDR_LEN);\r
+               net->dev_addr[0] |= 0x02;\r
+       }\r
+\r
+       return 0;\r
+}\r
+
+int wl_android_ext_dettach_netdev(void)\r
+{\r
+       struct wl_apsta_params *apsta_params = &g_apsta_params;\r
+\r
+       ANDROID_TRACE(("%s: Enter\n", __FUNCTION__));\r
+       memset(apsta_params, 0, sizeof(struct wl_apsta_params));\r
+\r
+       return 0;\r
+}\r
+#endif\r
+\r
+#ifdef IDHCPC\r
+int wl_ext_ip_dump(int ip, char *buf)\r
+{\r
+       unsigned char bytes[4];\r
+       int bytes_written=-1;\r
+\r
+       bytes[0] = ip & 0xFF;\r
+       bytes[1] = (ip >> 8) & 0xFF;\r
+       bytes[2] = (ip >> 16) & 0xFF;\r
+       bytes[3] = (ip >> 24) & 0xFF;   \r
+       bytes_written = sprintf(buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3]);\r
+\r
+       return bytes_written;\r
+}\r
+\r
+/*\r
+terence 20170215:\r
+dhd_priv dhcpc_dump ifname [wlan0|wlan1]\r
+dhd_priv dhcpc_enable [0|1]\r
+*/\r
+int\r
+wl_ext_dhcpc_enable(struct net_device *dev, char *command, int total_len)\r
+{\r
+       int enable = -1, ret = -1;\r
+       int bytes_written = -1;\r
+\r
+       ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command));\r
+\r
+       sscanf(command, "%*s %d", &enable);\r
+\r
+       if (enable >= 0)\r
+               ret = wl_ext_iovar_setint(dev, "dhcpc_enable", enable);\r
+       else {\r
+               ret = wl_ext_iovar_getint(dev, "dhcpc_enable", &enable);\r
+               if (!ret) {\r
+                       bytes_written = snprintf(command, total_len, "%d", enable);\r
+                       ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));\r
+                       ret = bytes_written;\r
+               }\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+int\r
+wl_ext_dhcpc_dump(struct net_device *dev, char *command, int total_len)\r
+{\r
+\r
+       int ret = 0;\r
+       int bytes_written = 0;\r
+       uint32 ip_addr;\r
+       char buf[20]="";\r
+\r
+       ret = wl_ext_iovar_getint(dev, "dhcpc_ip_addr", &ip_addr);\r
+       if (!ret) {\r
+               wl_ext_ip_dump(ip_addr, buf);\r
+               bytes_written += snprintf(command+bytes_written, total_len, "ipaddr %s ", buf);\r
+       }\r
+\r
+       ret = wl_ext_iovar_getint(dev, "dhcpc_ip_mask", &ip_addr);\r
+       if (!ret) {\r
+               wl_ext_ip_dump(ip_addr, buf);\r
+               bytes_written += snprintf(command+bytes_written, total_len, "mask %s ", buf);\r
+       }\r
+\r
+       ret = wl_ext_iovar_getint(dev, "dhcpc_ip_gateway", &ip_addr);\r
+       if (!ret) {\r
+               wl_ext_ip_dump(ip_addr, buf);\r
+               bytes_written += snprintf(command+bytes_written, total_len, "gw %s ", buf);\r
+       }\r
+\r
+       ret = wl_ext_iovar_getint(dev, "dhcpc_ip_dnsserv", &ip_addr);\r
+       if (!ret) {\r
+               wl_ext_ip_dump(ip_addr, buf);\r
+               bytes_written += snprintf(command+bytes_written, total_len, "dnsserv %s ", buf);\r
+       }\r
+\r
+       if (!bytes_written)\r
+               bytes_written = -1;\r
+       \r
+       ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));\r
+\r
+       return bytes_written;\r
+}\r
+#endif\r
+\r
+/*\r
+dhd_priv dhd [string] ==> Not ready\r
+1. Get dhd val:\r
+  Ex: dhd_priv dhd bussleep\r
+2. Set dhd val:\r
+  Ex: dhd_priv dhd bussleep 1\r
+\r
+dhd_priv wl [WLC_GET_PM]  ==> Ready to get int val\r
+dhd_priv wl [WLC_SET_PM] [int]  ==> Ready to set int val\r
+dhd_priv wl [string]  ==> Ready to get int val\r
+dhd_priv wl [string] [int]  ==> Ready to set int val\r
+Ex: get/set WLC_PM\r
+  dhd_priv wl 85\r
+  dhd_priv wl 86 1\r
+Ex: get/set mpc\r
+  dhd_priv wl mpc\r
+  dhd_priv wl mpc 1\r
+*/\r
+int\r
+wl_ext_iovar(struct net_device *dev, char *command, int total_len)\r
+{\r
+       int ret = 0;\r
+       char wl[3]="\0", arg[20]="\0", cmd_str[20]="\0", val_str[20]="\0";\r
+       int cmd=-1, val=0;\r
+       int bytes_written=-1;\r
+\r
+       ANDROID_TRACE(("%s: cmd %s\n", __FUNCTION__, command));\r
+\r
+       sscanf(command, "%s %d %s", wl, &cmd, arg);\r
+       if (cmd < 0)\r
+               sscanf(command, "%s %s %s", wl, cmd_str, val_str);\r
+\r
+       if (!strcmp(wl, "wl")) {\r
+               if (cmd>=0 && cmd!=WLC_GET_VAR && cmd!=WLC_SET_VAR) {\r
+                       ret = sscanf(arg, "%d", &val);\r
+                       if (ret > 0) { // set\r
+                               ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), TRUE);\r
+                       } else { // get\r
+                               ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), FALSE);\r
+                               if (!ret) {\r
+                                       bytes_written = snprintf(command, total_len, "%d", val);\r
+                                       ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));\r
+                                       ret = bytes_written;\r
+                               }\r
+                       }\r
+               } else if (strlen(cmd_str)) {\r
+                       ret = sscanf(val_str, "%d", &val);\r
+                       if (ret > 0) { // set\r
+                               ret = wl_ext_iovar_setint(dev, cmd_str, val);\r
+                       } else { // get\r
+                               ret = wl_ext_iovar_getint(dev, cmd_str, &val);\r
+                               if (!ret) {\r
+                                       bytes_written = snprintf(command, total_len, "%d", val);\r
+                                       ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));\r
+                                       ret = bytes_written;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+int wl_android_ext_priv_cmd(struct net_device *net, char *command, int total_len,\r
+               int *bytes_written)\r
+{\r
+       int ret = 0;\r
+\r
+       if (strnicmp(command, CMD_CHANNELS, strlen(CMD_CHANNELS)) == 0) {\r
+               *bytes_written = wl_ext_channels(net, command, total_len);\r
+       }\r
+       else if (strnicmp(command, CMD_CHANNEL, strlen(CMD_CHANNEL)) == 0) {\r
+               *bytes_written = wl_ext_channel(net, command, total_len);\r
+       }\r
+       else if (strnicmp(command, CMD_ROAM_TRIGGER, strlen(CMD_ROAM_TRIGGER)) == 0) {\r
+               *bytes_written = wl_ext_roam_trigger(net, command, total_len);\r
+       }
+       else if (strnicmp(command, CMD_KEEP_ALIVE, strlen(CMD_KEEP_ALIVE)) == 0) {\r
+               *bytes_written = wl_ext_keep_alive(net, command, total_len);\r
+       }
+       else if (strnicmp(command, CMD_PM, strlen(CMD_PM)) == 0) {\r
+               *bytes_written = wl_ext_pm(net, command, total_len);\r
+       }\r
+       else if (strnicmp(command, CMD_MONITOR, strlen(CMD_MONITOR)) == 0) {
+               *bytes_written = wl_ext_monitor(net, command, total_len);\r
+       }\r
+       else if (strnicmp(command, CMD_SET_SUSPEND_BCN_LI_DTIM, strlen(CMD_SET_SUSPEND_BCN_LI_DTIM)) == 0) {\r
+               int bcn_li_dtim;\r
+               bcn_li_dtim = (int)simple_strtol((command + strlen(CMD_SET_SUSPEND_BCN_LI_DTIM) + 1), NULL, 10);\r
+               *bytes_written = net_os_set_suspend_bcn_li_dtim(net, bcn_li_dtim);\r
+       }\r
+#ifdef WL_EXT_IAPSTA\r
+       else if (strnicmp(command, CMD_IAPSTA_INIT, strlen(CMD_IAPSTA_INIT)) == 0) {\r
+               *bytes_written = wl_ext_iapsta_init(net, command, total_len);\r
+       }\r
+       else if (strnicmp(command, CMD_IAPSTA_CONFIG, strlen(CMD_IAPSTA_CONFIG)) == 0) {\r
+               *bytes_written = wl_ext_iapsta_config(net, command, total_len);\r
+       }\r
+       else if (strnicmp(command, CMD_IAPSTA_ENABLE, strlen(CMD_IAPSTA_ENABLE)) == 0) {\r
+               *bytes_written = wl_ext_iapsta_enable(net, command, total_len);\r
+       }\r
+       else if (strnicmp(command, CMD_IAPSTA_DISABLE, strlen(CMD_IAPSTA_DISABLE)) == 0) {\r
+               *bytes_written = wl_ext_iapsta_disable(net, command, total_len);\r
+       }\r
+#endif\r
+#ifdef IDHCPC\r
+       else if (strnicmp(command, CMD_DHCPC_ENABLE, strlen(CMD_DHCPC_ENABLE)) == 0) {\r
+               *bytes_written = wl_ext_dhcpc_enable(net, command, total_len);\r
+       }\r
+       else if (strnicmp(command, CMD_DHCPC_DUMP, strlen(CMD_DHCPC_DUMP)) == 0) {\r
+               *bytes_written = wl_ext_dhcpc_dump(net, command, total_len);\r
+       }\r
+#endif\r
+       else if (strnicmp(command, CMD_WL, strlen(CMD_WL)) == 0) {\r
+               *bytes_written = wl_ext_iovar(net, command, total_len);\r
+       }\r
+       else\r
+               ret = -1;\r
+
+       return ret;\r
+}\r
+\r
+#if defined(RSSIAVG)\r
+void\r
+wl_free_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)\r
+{\r
+       wl_rssi_cache_t *node, *cur, **rssi_head;\r
+       int i=0;\r
+\r
+       rssi_head = &rssi_cache_ctrl->m_cache_head;\r
+       node = *rssi_head;\r
+\r
+       for (;node;) {\r
+               ANDROID_INFO(("%s: Free %d with BSSID %pM\n",\r
+                       __FUNCTION__, i, &node->BSSID));\r
+               cur = node;\r
+               node = cur->next;\r
+               kfree(cur);\r
+               i++;\r
+       }\r
+       *rssi_head = NULL;\r
+}\r
+\r
+void\r
+wl_delete_dirty_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)\r
+{\r
+       wl_rssi_cache_t *node, *prev, **rssi_head;\r
+       int i = -1, tmp = 0;\r
+       struct timeval now;\r
+\r
+       do_gettimeofday(&now);\r
+\r
+       rssi_head = &rssi_cache_ctrl->m_cache_head;\r
+       node = *rssi_head;\r
+       prev = node;\r
+       for (;node;) {\r
+               i++;\r
+               if (now.tv_sec > node->tv.tv_sec) {\r
+                       if (node == *rssi_head) {\r
+                               tmp = 1;\r
+                               *rssi_head = node->next;\r
+                       } else {\r
+                               tmp = 0;\r
+                               prev->next = node->next;\r
+                       }\r
+                       ANDROID_INFO(("%s: Del %d with BSSID %pM\n",\r
+                               __FUNCTION__, i, &node->BSSID));\r
+                       kfree(node);\r
+                       if (tmp == 1) {\r
+                               node = *rssi_head;\r
+                               prev = node;\r
+                       } else {\r
+                               node = prev->next;\r
+                       }\r
+                       continue;\r
+               }\r
+               prev = node;\r
+               node = node->next;\r
+       }\r
+}\r
+\r
+void\r
+wl_delete_disconnected_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, u8 *bssid)\r
+{\r
+       wl_rssi_cache_t *node, *prev, **rssi_head;\r
+       int i = -1, tmp = 0;\r
+\r
+       rssi_head = &rssi_cache_ctrl->m_cache_head;\r
+       node = *rssi_head;\r
+       prev = node;\r
+       for (;node;) {\r
+               i++;\r
+               if (!memcmp(&node->BSSID, bssid, ETHER_ADDR_LEN)) {\r
+                       if (node == *rssi_head) {\r
+                               tmp = 1;\r
+                               *rssi_head = node->next;\r
+                       } else {\r
+                               tmp = 0;\r
+                               prev->next = node->next;\r
+                       }\r
+                       ANDROID_INFO(("%s: Del %d with BSSID %pM\n",\r
+                               __FUNCTION__, i, &node->BSSID));\r
+                       kfree(node);\r
+                       if (tmp == 1) {\r
+                               node = *rssi_head;\r
+                               prev = node;\r
+                       } else {\r
+                               node = prev->next;\r
+                       }\r
+                       continue;\r
+               }\r
+               prev = node;\r
+               node = node->next;\r
+       }\r
+}\r
+\r
+void\r
+wl_reset_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)\r
+{\r
+       wl_rssi_cache_t *node, **rssi_head;\r
+\r
+       rssi_head = &rssi_cache_ctrl->m_cache_head;\r
+\r
+       /* reset dirty */\r
+       node = *rssi_head;\r
+       for (;node;) {\r
+               node->dirty += 1;\r
+               node = node->next;\r
+       }\r
+}\r
+\r
+int\r
+wl_update_connected_rssi_cache(struct net_device *net, wl_rssi_cache_ctrl_t *rssi_cache_ctrl, int *rssi_avg)\r
+{\r
+       wl_rssi_cache_t *node, *prev, *leaf, **rssi_head;\r
+       int j, k=0;\r
+       int rssi, error=0;\r
+       struct ether_addr bssid;\r
+       struct timeval now, timeout;\r
+\r
+       if (!g_wifi_on)\r
+               return 0;\r
+\r
+       error = wldev_ioctl(net, WLC_GET_BSSID, &bssid, sizeof(bssid), false);\r
+       if (error == BCME_NOTASSOCIATED) {\r
+               ANDROID_INFO(("%s: Not Associated! res:%d\n", __FUNCTION__, error));\r
+               return 0;\r
+       }\r
+       if (error) {\r
+               ANDROID_ERROR(("Could not get bssid (%d)\n", error));\r
+       }\r
+       error = wldev_get_rssi(net, &rssi);\r
+       if (error) {\r
+               ANDROID_ERROR(("Could not get rssi (%d)\n", error));\r
+               return error;\r
+       }\r
+\r
+       do_gettimeofday(&now);\r
+       timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT;\r
+       if (timeout.tv_sec < now.tv_sec) {\r
+               /*\r
+                * Integer overflow - assume long enough timeout to be assumed\r
+                * to be infinite, i.e., the timeout would never happen.\r
+                */\r
+               ANDROID_TRACE(("%s: Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu",\r
+                       __FUNCTION__, RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec));\r
+       }\r
+\r
+       /* update RSSI */\r
+       rssi_head = &rssi_cache_ctrl->m_cache_head;\r
+       node = *rssi_head;\r
+       prev = NULL;\r
+       for (;node;) {\r
+               if (!memcmp(&node->BSSID, &bssid, ETHER_ADDR_LEN)) {\r
+                       ANDROID_INFO(("%s: Update %d with BSSID %pM, RSSI=%d\n",\r
+                               __FUNCTION__, k, &bssid, rssi));\r
+                       for (j=0; j<RSSIAVG_LEN-1; j++)\r
+                               node->RSSI[j] = node->RSSI[j+1];\r
+                       node->RSSI[j] = rssi;\r
+                       node->dirty = 0;\r
+                       node->tv = timeout;\r
+                       goto exit;\r
+               }\r
+               prev = node;\r
+               node = node->next;\r
+               k++;\r
+       }\r
+\r
+       leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL);\r
+       if (!leaf) {\r
+               ANDROID_ERROR(("%s: Memory alloc failure %d\n",\r
+                       __FUNCTION__, (int)sizeof(wl_rssi_cache_t)));\r
+               return 0;\r
+       }\r
+       ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%3d in the leaf\n",\r
+                       __FUNCTION__, k, &bssid, rssi));\r
+\r
+       leaf->next = NULL;\r
+       leaf->dirty = 0;\r
+       leaf->tv = timeout;\r
+       memcpy(&leaf->BSSID, &bssid, ETHER_ADDR_LEN);\r
+       for (j=0; j<RSSIAVG_LEN; j++)\r
+               leaf->RSSI[j] = rssi;\r
+\r
+       if (!prev)\r
+               *rssi_head = leaf;\r
+       else\r
+               prev->next = leaf;\r
+\r
+exit:\r
+       *rssi_avg = (int)wl_get_avg_rssi(rssi_cache_ctrl, &bssid);\r
+\r
+       return error;\r
+}\r
+\r
+void\r
+wl_update_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, wl_scan_results_t *ss_list)\r
+{\r
+       wl_rssi_cache_t *node, *prev, *leaf, **rssi_head;\r
+       wl_bss_info_t *bi = NULL;\r
+       int i, j, k;\r
+       struct timeval now, timeout;\r
+\r
+       if (!ss_list->count)\r
+               return;\r
+\r
+       do_gettimeofday(&now);\r
+       timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT;\r
+       if (timeout.tv_sec < now.tv_sec) {\r
+               /*\r
+                * Integer overflow - assume long enough timeout to be assumed\r
+                * to be infinite, i.e., the timeout would never happen.\r
+                */\r
+               ANDROID_TRACE(("%s: Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu",\r
+                       __FUNCTION__, RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec));\r
+       }\r
+\r
+       rssi_head = &rssi_cache_ctrl->m_cache_head;\r
+\r
+       /* update RSSI */\r
+       for (i = 0; i < ss_list->count; i++) {\r
+               node = *rssi_head;\r
+               prev = NULL;\r
+               k = 0;\r
+               bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;\r
+               for (;node;) {\r
+                       if (!memcmp(&node->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {\r
+                               ANDROID_INFO(("%s: Update %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",\r
+                                       __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID));\r
+                               for (j=0; j<RSSIAVG_LEN-1; j++)\r
+                                       node->RSSI[j] = node->RSSI[j+1];\r
+                               node->RSSI[j] = dtoh16(bi->RSSI);\r
+                               node->dirty = 0;\r
+                               node->tv = timeout;\r
+                               break;\r
+                       }\r
+                       prev = node;\r
+                       node = node->next;\r
+                       k++;\r
+               }\r
+\r
+               if (node)\r
+                       continue;\r
+\r
+               leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL);\r
+               if (!leaf) {\r
+                       ANDROID_ERROR(("%s: Memory alloc failure %d\n",\r
+                               __FUNCTION__, (int)sizeof(wl_rssi_cache_t)));\r
+                       return;\r
+               }\r
+               ANDROID_INFO(("%s: Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\" in the leaf\n",\r
+                               __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID));\r
+\r
+               leaf->next = NULL;\r
+               leaf->dirty = 0;\r
+               leaf->tv = timeout;\r
+               memcpy(&leaf->BSSID, &bi->BSSID, ETHER_ADDR_LEN);\r
+               for (j=0; j<RSSIAVG_LEN; j++)\r
+                       leaf->RSSI[j] = dtoh16(bi->RSSI);\r
+\r
+               if (!prev)\r
+                       *rssi_head = leaf;\r
+               else\r
+                       prev->next = leaf;\r
+       }\r
+}\r
+\r
+int16\r
+wl_get_avg_rssi(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, void *addr)\r
+{\r
+       wl_rssi_cache_t *node, **rssi_head;\r
+       int j, rssi_sum, rssi=RSSI_MINVAL;\r
+\r
+       rssi_head = &rssi_cache_ctrl->m_cache_head;\r
+\r
+       node = *rssi_head;\r
+       for (;node;) {\r
+               if (!memcmp(&node->BSSID, addr, ETHER_ADDR_LEN)) {\r
+                       rssi_sum = 0;\r
+                       rssi = 0;\r
+                       for (j=0; j<RSSIAVG_LEN; j++)\r
+                               rssi_sum += node->RSSI[RSSIAVG_LEN-j-1];\r
+                       rssi = rssi_sum / j;\r
+                       break;\r
+               }\r
+               node = node->next;\r
+       }\r
+       rssi = MIN(rssi, RSSI_MAXVAL);\r
+       if (rssi == RSSI_MINVAL) {\r
+               ANDROID_ERROR(("%s: BSSID %pM does not in RSSI cache\n",\r
+               __FUNCTION__, addr));\r
+       }\r
+       return (int16)rssi;\r
+}\r
+#endif\r
+\r
+#if defined(RSSIOFFSET)\r
+int\r
+wl_update_rssi_offset(struct net_device *net, int rssi)\r
+{\r
+#if defined(RSSIOFFSET_NEW)\r
+       int j;\r
+#endif\r
+\r
+       if (!g_wifi_on)\r
+               return rssi;\r
+\r
+#if defined(RSSIOFFSET_NEW)\r
+       for (j=0; j<RSSI_OFFSET; j++) {\r
+               if (rssi - (RSSI_OFFSET_MINVAL+RSSI_OFFSET_INTVAL*(j+1)) < 0)\r
+                       break;\r
+       }\r
+       rssi += j;\r
+#else\r
+       rssi += RSSI_OFFSET;\r
+#endif\r
+       return MIN(rssi, RSSI_MAXVAL);\r
+}\r
+#endif\r
+\r
+#if defined(BSSCACHE)\r
+void\r
+wl_free_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)\r
+{\r
+       wl_bss_cache_t *node, *cur, **bss_head;\r
+       int i=0;\r
+\r
+       ANDROID_TRACE(("%s called\n", __FUNCTION__));\r
+\r
+       bss_head = &bss_cache_ctrl->m_cache_head;\r
+       node = *bss_head;\r
+\r
+       for (;node;) {\r
+               ANDROID_TRACE(("%s: Free %d with BSSID %pM\n",\r
+                       __FUNCTION__, i, &node->results.bss_info->BSSID));\r
+               cur = node;\r
+               node = cur->next;\r
+               kfree(cur);\r
+               i++;\r
+       }\r
+       *bss_head = NULL;\r
+}\r
+\r
+void\r
+wl_delete_dirty_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)\r
+{\r
+       wl_bss_cache_t *node, *prev, **bss_head;\r
+       int i = -1, tmp = 0;\r
+       struct timeval now;\r
+\r
+       do_gettimeofday(&now);\r
+\r
+       bss_head = &bss_cache_ctrl->m_cache_head;\r
+       node = *bss_head;\r
+       prev = node;\r
+       for (;node;) {\r
+               i++;\r
+               if (now.tv_sec > node->tv.tv_sec) {\r
+                       if (node == *bss_head) {\r
+                               tmp = 1;\r
+                               *bss_head = node->next;\r
+                       } else {\r
+                               tmp = 0;\r
+                               prev->next = node->next;\r
+                       }\r
+                       ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",\r
+                               __FUNCTION__, i, &node->results.bss_info->BSSID,\r
+                               dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID));\r
+                       kfree(node);\r
+                       if (tmp == 1) {\r
+                               node = *bss_head;\r
+                               prev = node;\r
+                       } else {\r
+                               node = prev->next;\r
+                       }\r
+                       continue;\r
+               }\r
+               prev = node;\r
+               node = node->next;\r
+       }\r
+}\r
+\r
+void\r
+wl_delete_disconnected_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl, u8 *bssid)\r
+{\r
+       wl_bss_cache_t *node, *prev, **bss_head;\r
+       int i = -1, tmp = 0;\r
+\r
+       bss_head = &bss_cache_ctrl->m_cache_head;\r
+       node = *bss_head;\r
+       prev = node;\r
+       for (;node;) {\r
+               i++;\r
+               if (!memcmp(&node->results.bss_info->BSSID, bssid, ETHER_ADDR_LEN)) {\r
+                       if (node == *bss_head) {\r
+                               tmp = 1;\r
+                               *bss_head = node->next;\r
+                       } else {\r
+                               tmp = 0;\r
+                               prev->next = node->next;\r
+                       }\r
+                       ANDROID_TRACE(("%s: Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",\r
+                               __FUNCTION__, i, &node->results.bss_info->BSSID,\r
+                               dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID));\r
+                       kfree(node);\r
+                       if (tmp == 1) {\r
+                               node = *bss_head;\r
+                               prev = node;\r
+                       } else {\r
+                               node = prev->next;\r
+                       }\r
+                       continue;\r
+               }\r
+               prev = node;\r
+               node = node->next;\r
+       }\r
+}\r
+\r
+void\r
+wl_reset_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)\r
+{\r
+       wl_bss_cache_t *node, **bss_head;\r
+\r
+       bss_head = &bss_cache_ctrl->m_cache_head;\r
+\r
+       /* reset dirty */\r
+       node = *bss_head;\r
+       for (;node;) {\r
+               node->dirty += 1;\r
+               node = node->next;\r
+       }\r
+}\r
+\r
+void dump_bss_cache(\r
+#if defined(RSSIAVG)\r
+       wl_rssi_cache_ctrl_t *rssi_cache_ctrl,\r
+#endif\r
+       wl_bss_cache_t *node)\r
+{\r
+       int k = 0;\r
+       int16 rssi;\r
+\r
+       for (;node;) {\r
+#if defined(RSSIAVG)\r
+               rssi = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID);\r
+#else\r
+               rssi = dtoh16(node->results.bss_info->RSSI);\r
+#endif\r
+               ANDROID_TRACE(("%s: dump %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",\r
+                       __FUNCTION__, k, &node->results.bss_info->BSSID, rssi, node->results.bss_info->SSID));\r
+               k++;\r
+               node = node->next;\r
+       }\r
+}\r
+\r
+void\r
+wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,\r
+#if defined(RSSIAVG)\r
+       wl_rssi_cache_ctrl_t *rssi_cache_ctrl,\r
+#endif\r
+       wl_scan_results_t *ss_list)\r
+{\r
+       wl_bss_cache_t *node, *prev, *leaf, **bss_head;\r
+       wl_bss_info_t *bi = NULL;\r
+       int i, k=0;\r
+#if defined(SORT_BSS_BY_RSSI)\r
+       int16 rssi, rssi_node;\r
+#endif\r
+       struct timeval now, timeout;\r
+\r
+       if (!ss_list->count)\r
+               return;\r
+\r
+       do_gettimeofday(&now);\r
+       timeout.tv_sec = now.tv_sec + BSSCACHE_TIMEOUT;\r
+       if (timeout.tv_sec < now.tv_sec) {\r
+               /*\r
+                * Integer overflow - assume long enough timeout to be assumed\r
+                * to be infinite, i.e., the timeout would never happen.\r
+                */\r
+               ANDROID_TRACE(("%s: Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu",\r
+                       __FUNCTION__, BSSCACHE_TIMEOUT, now.tv_sec, timeout.tv_sec));\r
+       }\r
+\r
+       bss_head = &bss_cache_ctrl->m_cache_head;\r
+\r
+       for (i=0; i < ss_list->count; i++) {\r
+               node = *bss_head;\r
+               prev = NULL;\r
+               bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;\r
+\r
+               for (;node;) {\r
+                       if (!memcmp(&node->results.bss_info->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {\r
+                               if (node == *bss_head)\r
+                                       *bss_head = node->next;\r
+                               else {\r
+                                       prev->next = node->next;\r
+                               }\r
+                               break;\r
+                       }\r
+                       prev = node;\r
+                       node = node->next;\r
+               }\r
+\r
+               leaf = kmalloc(dtoh32(bi->length) + sizeof(wl_bss_cache_t), GFP_KERNEL);\r
+               if (!leaf) {\r
+                       ANDROID_ERROR(("%s: Memory alloc failure %d\n", __FUNCTION__,\r
+                               dtoh32(bi->length) + (int)sizeof(wl_bss_cache_t)));\r
+                       return;\r
+               }\r
+               if (node) {\r
+                       kfree(node);\r
+                       node = NULL;\r
+                       ANDROID_TRACE(("%s: Update %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",\r
+                               __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID));\r
+               } else\r
+                       ANDROID_TRACE(("%s: Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",\r
+                               __FUNCTION__, k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID));\r
+\r
+               memcpy(leaf->results.bss_info, bi, dtoh32(bi->length));\r
+               leaf->next = NULL;\r
+               leaf->dirty = 0;\r
+               leaf->tv = timeout;\r
+               leaf->results.count = 1;\r
+               leaf->results.version = ss_list->version;\r
+               k++;\r
+\r
+               if (*bss_head == NULL)\r
+                       *bss_head = leaf;\r
+               else {\r
+#if defined(SORT_BSS_BY_RSSI)\r
+                       node = *bss_head;\r
+#if defined(RSSIAVG)\r
+                       rssi = wl_get_avg_rssi(rssi_cache_ctrl, &leaf->results.bss_info->BSSID);\r
+#else\r
+                       rssi = dtoh16(leaf->results.bss_info->RSSI);\r
+#endif\r
+                       for (;node;) {\r
+#if defined(RSSIAVG)\r
+                               rssi_node = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID);\r
+#else\r
+                               rssi_node = dtoh16(node->results.bss_info->RSSI);\r
+#endif\r
+                               if (rssi > rssi_node) {\r
+                                       leaf->next = node;\r
+                                       if (node == *bss_head)\r
+                                               *bss_head = leaf;\r
+                                       else\r
+                                               prev->next = leaf;\r
+                                       break;\r
+                               }\r
+                               prev = node;\r
+                               node = node->next;\r
+                       }\r
+                       if (node == NULL)\r
+                               prev->next = leaf;\r
+#else\r
+                       leaf->next = *bss_head;\r
+                       *bss_head = leaf;\r
+#endif\r
+               }\r
+       }\r
+       dump_bss_cache(\r
+#if defined(RSSIAVG)\r
+               rssi_cache_ctrl,\r
+#endif\r
+               *bss_head);\r
+}\r
+\r
+void\r
+wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t *bss_cache_ctrl)\r
+{\r
+       ANDROID_TRACE(("%s:\n", __FUNCTION__));\r
+       wl_free_bss_cache(bss_cache_ctrl);\r
+}\r
+#endif\r
+\r
+\r
diff --git a/bcmdhd.1.363.59.144.x.cn/wl_escan.c b/bcmdhd.1.363.59.144.x.cn/wl_escan.c
new file mode 100644 (file)
index 0000000..259b3d9
--- /dev/null
@@ -0,0 +1,1457 @@
+
+#if defined(WL_ESCAN)
+
+#include <typedefs.h>
+#include <linuxver.h>
+#include <osl.h>
+
+#include <bcmutils.h>
+#include <bcmendian.h>
+#include <proto/ethernet.h>
+
+#include <linux/if_arp.h>
+#include <asm/uaccess.h>
+
+#include <wlioctl.h>
+#include <wl_android.h>
+#include <wl_iw.h>
+#include <wl_escan.h>
+#include <dhd_config.h>
+
+/* message levels */
+#define ESCAN_ERROR_LEVEL      0x0001
+#define ESCAN_SCAN_LEVEL       0x0002
+#define ESCAN_TRACE_LEVEL      0x0004
+
+#define ESCAN_ERROR(x) \
+       do { \
+               if (iw_msg_level & ESCAN_ERROR_LEVEL) { \
+                       printf(KERN_ERR "ESCAN-ERROR) ");       \
+                       printf x; \
+               } \
+       } while (0)
+#define ESCAN_SCAN(x) \
+       do { \
+               if (iw_msg_level & ESCAN_SCAN_LEVEL) { \
+                       printf(KERN_ERR "ESCAN-SCAN) ");        \
+                       printf x; \
+               } \
+       } while (0)
+#define ESCAN_TRACE(x) \
+       do { \
+               if (iw_msg_level & ESCAN_TRACE_LEVEL) { \
+                       printf(KERN_ERR "ESCAN-TRACE) ");       \
+                       printf x; \
+               } \
+       } while (0)
+
+/* IOCTL swapping mode for Big Endian host with Little Endian dongle.  Default to off */
+#define htod32(i) (i)
+#define htod16(i) (i)
+#define dtoh32(i) (i)
+#define dtoh16(i) (i)
+#define htodchanspec(i) (i)
+#define dtohchanspec(i) (i)
+
+#define wl_escan_get_buf(a) ((wl_scan_results_t *) (a)->escan_buf)
+
+#define for_each_bss(list, bss, __i)   \
+       for (__i = 0; __i < list->count && __i < IW_MAX_AP; __i++, bss = next_bss(list, bss))
+
+#define wl_escan_set_sync_id(a) ((a) = htod16(0x1234))
+
+#ifdef ESCAN_BUF_OVERFLOW_MGMT
+#define BUF_OVERFLOW_MGMT_COUNT 3
+typedef struct {
+       int RSSI;
+       int length;
+       struct ether_addr BSSID;
+} removal_element_t;
+#endif /* ESCAN_BUF_OVERFLOW_MGMT */
+
+struct wl_escan_info *g_escan = NULL;
+
+#if defined(RSSIAVG)
+static wl_rssi_cache_ctrl_t g_rssi_cache_ctrl;
+static wl_rssi_cache_ctrl_t g_connected_rssi_cache_ctrl;
+#endif
+#if defined(BSSCACHE)
+static wl_bss_cache_ctrl_t g_bss_cache_ctrl;
+#endif
+
+/* Return a new chanspec given a legacy chanspec
+ * Returns INVCHANSPEC on error
+ */
+static chanspec_t
+wl_chspec_from_legacy(chanspec_t legacy_chspec)
+{
+       chanspec_t chspec;
+
+       /* get the channel number */
+       chspec = LCHSPEC_CHANNEL(legacy_chspec);
+
+       /* convert the band */
+       if (LCHSPEC_IS2G(legacy_chspec)) {
+               chspec |= WL_CHANSPEC_BAND_2G;
+       } else {
+               chspec |= WL_CHANSPEC_BAND_5G;
+       }
+
+       /* convert the bw and sideband */
+       if (LCHSPEC_IS20(legacy_chspec)) {
+               chspec |= WL_CHANSPEC_BW_20;
+       } else {
+               chspec |= WL_CHANSPEC_BW_40;
+               if (LCHSPEC_CTL_SB(legacy_chspec) == WL_LCHANSPEC_CTL_SB_LOWER) {
+                       chspec |= WL_CHANSPEC_CTL_SB_L;
+               } else {
+                       chspec |= WL_CHANSPEC_CTL_SB_U;
+               }
+       }
+
+       if (wf_chspec_malformed(chspec)) {
+               ESCAN_ERROR(("wl_chspec_from_legacy: output chanspec (0x%04X) malformed\n",
+                       chspec));
+               return INVCHANSPEC;
+       }
+
+       return chspec;
+}
+
+/* Return a legacy chanspec given a new chanspec
+ * Returns INVCHANSPEC on error
+ */
+static chanspec_t
+wl_chspec_to_legacy(chanspec_t chspec)
+{
+       chanspec_t lchspec;
+
+       if (wf_chspec_malformed(chspec)) {
+               ESCAN_ERROR(("wl_chspec_to_legacy: input chanspec (0x%04X) malformed\n",
+                       chspec));
+               return INVCHANSPEC;
+       }
+
+       /* get the channel number */
+       lchspec = CHSPEC_CHANNEL(chspec);
+
+       /* convert the band */
+       if (CHSPEC_IS2G(chspec)) {
+               lchspec |= WL_LCHANSPEC_BAND_2G;
+       } else {
+               lchspec |= WL_LCHANSPEC_BAND_5G;
+       }
+
+       /* convert the bw and sideband */
+       if (CHSPEC_IS20(chspec)) {
+               lchspec |= WL_LCHANSPEC_BW_20;
+               lchspec |= WL_LCHANSPEC_CTL_SB_NONE;
+       } else if (CHSPEC_IS40(chspec)) {
+               lchspec |= WL_LCHANSPEC_BW_40;
+               if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_L) {
+                       lchspec |= WL_LCHANSPEC_CTL_SB_LOWER;
+               } else {
+                       lchspec |= WL_LCHANSPEC_CTL_SB_UPPER;
+               }
+       } else {
+               /* cannot express the bandwidth */
+               char chanbuf[CHANSPEC_STR_LEN];
+               ESCAN_ERROR((
+                       "wl_chspec_to_legacy: unable to convert chanspec %s (0x%04X) "
+                       "to pre-11ac format\n",
+                       wf_chspec_ntoa(chspec, chanbuf), chspec));
+               return INVCHANSPEC;
+       }
+
+       return lchspec;
+}
+
+/* given a chanspec value from the driver, do the endian and chanspec version conversion to
+ * a chanspec_t value
+ * Returns INVCHANSPEC on error
+ */
+static chanspec_t
+wl_chspec_driver_to_host(int ioctl_ver, chanspec_t chanspec)
+{
+       chanspec = dtohchanspec(chanspec);
+       if (ioctl_ver == 1) {
+               chanspec = wl_chspec_from_legacy(chanspec);
+       }
+
+       return chanspec;
+}
+
+/* given a chanspec value, do the endian and chanspec version conversion to
+ * a chanspec_t value
+ * Returns INVCHANSPEC on error
+ */
+static chanspec_t
+wl_chspec_host_to_driver(chanspec_t chanspec)
+{
+       if (1) {
+               chanspec = wl_chspec_to_legacy(chanspec);
+               if (chanspec == INVCHANSPEC) {
+                       return chanspec;
+               }
+       }
+       chanspec = htodchanspec(chanspec);
+
+       return chanspec;
+}
+
+/* given a channel value, do the endian and chanspec version conversion to
+ * a chanspec_t value
+ * Returns INVCHANSPEC on error
+ */
+static chanspec_t
+wl_ch_host_to_driver(s32 bssidx, u16 channel)
+{
+       chanspec_t chanspec;
+
+       chanspec = channel & WL_CHANSPEC_CHAN_MASK;
+
+       if (channel <= CH_MAX_2G_CHANNEL)
+               chanspec |= WL_CHANSPEC_BAND_2G;
+       else
+               chanspec |= WL_CHANSPEC_BAND_5G;
+
+       chanspec |= WL_CHANSPEC_BW_20;
+
+       chanspec |= WL_CHANSPEC_CTL_SB_NONE;
+
+       return wl_chspec_host_to_driver(chanspec);
+}
+
+static inline struct wl_bss_info *next_bss(struct wl_scan_results *list, struct wl_bss_info *bss)
+{
+       return bss = bss ?
+               (struct wl_bss_info *)((uintptr) bss + dtoh32(bss->length)) : list->bss_info;
+}
+
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+
+static int
+rssi_to_qual(int rssi)
+{
+       if (rssi <= WL_IW_RSSI_NO_SIGNAL)
+               return 0;
+       else if (rssi <= WL_IW_RSSI_VERY_LOW)
+               return 1;
+       else if (rssi <= WL_IW_RSSI_LOW)
+               return 2;
+       else if (rssi <= WL_IW_RSSI_GOOD)
+               return 3;
+       else if (rssi <= WL_IW_RSSI_VERY_GOOD)
+               return 4;
+       else
+               return 5;
+}
+
+#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \
+       4 && __GNUC_MINOR__ >= 6))
+#define BCM_SET_LIST_FIRST_ENTRY(entry, ptr, type, member) \
+_Pragma("GCC diagnostic push") \
+_Pragma("GCC diagnostic ignored \"-Wcast-qual\"") \
+(entry) = list_first_entry((ptr), type, member); \
+_Pragma("GCC diagnostic pop") \
+
+#define BCM_SET_CONTAINER_OF(entry, ptr, type, member) \
+_Pragma("GCC diagnostic push") \
+_Pragma("GCC diagnostic ignored \"-Wcast-qual\"") \
+entry = container_of((ptr), type, member); \
+_Pragma("GCC diagnostic pop") \
+
+#else
+#define BCM_SET_LIST_FIRST_ENTRY(entry, ptr, type, member) \
+(entry) = list_first_entry((ptr), type, member); \
+
+#define BCM_SET_CONTAINER_OF(entry, ptr, type, member) \
+entry = container_of((ptr), type, member); \
+
+#endif /* STRICT_GCC_WARNINGS */
+
+static unsigned long wl_lock_eq(struct wl_escan_info *escan)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&escan->eq_lock, flags);
+       return flags;
+}
+
+static void wl_unlock_eq(struct wl_escan_info *escan, unsigned long flags)
+{
+       spin_unlock_irqrestore(&escan->eq_lock, flags);
+}
+
+static void wl_init_eq(struct wl_escan_info *escan)
+{
+       spin_lock_init(&escan->eq_lock);
+       INIT_LIST_HEAD(&escan->eq_list);
+}
+
+static void wl_flush_eq(struct wl_escan_info *escan)
+{
+       struct escan_event_q *e;
+       unsigned long flags;
+
+       flags = wl_lock_eq(escan);
+       while (!list_empty_careful(&escan->eq_list)) {
+               BCM_SET_LIST_FIRST_ENTRY(e, &escan->eq_list, struct escan_event_q, eq_list);
+               list_del(&e->eq_list);
+               kfree(e);
+       }
+       wl_unlock_eq(escan, flags);
+}
+
+static struct escan_event_q *wl_deq_event(struct wl_escan_info *escan)
+{
+       struct escan_event_q *e = NULL;
+       unsigned long flags;
+
+       flags = wl_lock_eq(escan);
+       if (likely(!list_empty(&escan->eq_list))) {
+               BCM_SET_LIST_FIRST_ENTRY(e, &escan->eq_list, struct escan_event_q, eq_list);
+               list_del(&e->eq_list);
+       }
+       wl_unlock_eq(escan, flags);
+
+       return e;
+}
+
+/*
+ * push event to tail of the queue
+ */
+
+static s32
+wl_enq_event(struct wl_escan_info *escan, struct net_device *ndev, u32 event,
+       const wl_event_msg_t *msg, void *data)
+{
+       struct escan_event_q *e;
+       s32 err = 0;
+       uint32 evtq_size;
+       uint32 data_len;
+       unsigned long flags;
+       gfp_t aflags;
+
+       data_len = 0;
+       if (data)
+               data_len = ntoh32(msg->datalen);
+       evtq_size = sizeof(struct escan_event_q) + data_len;
+       aflags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
+       e = kzalloc(evtq_size, aflags);
+       if (unlikely(!e)) {
+               ESCAN_ERROR(("event alloc failed\n"));
+               return -ENOMEM;
+       }
+       e->etype = event;
+       memcpy(&e->emsg, msg, sizeof(wl_event_msg_t));
+       if (data)
+               memcpy(e->edata, data, data_len);
+       flags = wl_lock_eq(escan);
+       list_add_tail(&e->eq_list, &escan->eq_list);
+       wl_unlock_eq(escan, flags);
+
+       return err;
+}
+
+static void wl_put_event(struct escan_event_q *e)
+{
+       kfree(e);
+}
+
+static void wl_wakeup_event(struct wl_escan_info *escan)
+{
+       dhd_pub_t *dhd = (dhd_pub_t *)(escan->pub);
+
+       if (dhd->up && (escan->event_tsk.thr_pid >= 0)) {
+               up(&escan->event_tsk.sema);
+       }
+}
+
+static s32 wl_escan_event_handler(void *data)
+{
+       struct wl_escan_info *escan = NULL;
+       struct escan_event_q *e;
+       tsk_ctl_t *tsk = (tsk_ctl_t *)data;
+
+       escan = (struct wl_escan_info *)tsk->parent;
+
+       printf("tsk Enter, tsk = 0x%p\n", tsk);
+
+       while (down_interruptible (&tsk->sema) == 0) {
+               SMP_RD_BARRIER_DEPENDS();
+               if (tsk->terminated) {
+                       break;
+               }
+               while (escan && (e = wl_deq_event(escan))) {
+                       ESCAN_TRACE(("dev=%p, event type (%d), ifidx: %d bssidx: %d \n",
+                               escan->dev, e->etype, e->emsg.ifidx, e->emsg.bsscfgidx));
+
+                       if (e->emsg.ifidx > WL_MAX_IFS) {
+                               ESCAN_ERROR(("Event ifidx not in range. val:%d \n", e->emsg.ifidx));
+                               goto fail;
+                       }
+
+                       if (escan->dev && escan->evt_handler[e->etype]) {
+                               dhd_pub_t *dhd = (struct dhd_pub *)(escan->pub);
+                               if (dhd->busstate == DHD_BUS_DOWN) {
+                                       ESCAN_ERROR((": BUS is DOWN.\n"));
+                               } else {
+                                       escan->evt_handler[e->etype](escan, &e->emsg, e->edata);
+                               }
+                       } else {
+                               ESCAN_TRACE(("Unknown Event (%d): ignoring\n", e->etype));
+                       }
+fail:
+                       wl_put_event(e);
+                       DHD_EVENT_WAKE_UNLOCK(escan->pub);
+               }
+       }
+       printf("%s: was terminated\n", __FUNCTION__);
+       complete_and_exit(&tsk->completed, 0);
+       return 0;
+}
+
+void
+wl_escan_event(struct net_device *ndev, const wl_event_msg_t * e, void *data)
+{
+       u32 event_type = ntoh32(e->event_type);
+       struct wl_escan_info *escan = g_escan;
+
+       if (!escan || !escan->dev) {
+               return;
+       }
+
+       if (escan->event_tsk.thr_pid == -1) {
+               ESCAN_ERROR(("Event handler is not created\n"));
+               return;
+       }
+
+       if (escan == NULL) {
+               ESCAN_ERROR(("Stale event ignored\n"));
+               return;
+       }
+
+       if (event_type == WLC_E_PFN_NET_FOUND) {
+               ESCAN_TRACE(("PNOEVENT: PNO_NET_FOUND\n"));
+       }
+       else if (event_type == WLC_E_PFN_NET_LOST) {
+               ESCAN_TRACE(("PNOEVENT: PNO_NET_LOST\n"));
+       }
+
+       DHD_EVENT_WAKE_LOCK(escan->pub);
+       if (likely(!wl_enq_event(escan, ndev, event_type, e, data))) {
+               wl_wakeup_event(escan);
+       } else {
+               DHD_EVENT_WAKE_UNLOCK(escan->pub);
+       }
+}
+
+static s32 wl_escan_inform_bss(struct wl_escan_info *escan)
+{
+       struct wl_scan_results *bss_list;
+       s32 err = 0;
+#if defined(RSSIAVG)
+       int rssi;
+#endif
+
+       bss_list = escan->bss_list;
+
+       /* Delete disconnected cache */
+#if defined(BSSCACHE)
+       wl_delete_disconnected_bss_cache(&g_bss_cache_ctrl, (u8*)&escan->disconnected_bssid);
+#if defined(RSSIAVG)
+       wl_delete_disconnected_rssi_cache(&g_rssi_cache_ctrl, (u8*)&escan->disconnected_bssid);
+#endif
+#endif
+
+       /* Update cache */
+#if defined(RSSIAVG)
+       wl_update_rssi_cache(&g_rssi_cache_ctrl, bss_list);
+       if (!in_atomic())
+               wl_update_connected_rssi_cache(escan->dev, &g_rssi_cache_ctrl, &rssi);
+#endif
+#if defined(BSSCACHE)
+       wl_update_bss_cache(&g_bss_cache_ctrl,
+#if defined(RSSIAVG)
+               &g_rssi_cache_ctrl,
+#endif
+               bss_list);
+#endif
+
+       /* delete dirty cache */
+#if defined(RSSIAVG)
+       wl_delete_dirty_rssi_cache(&g_rssi_cache_ctrl);
+       wl_reset_rssi_cache(&g_rssi_cache_ctrl);
+#endif
+#if defined(BSSCACHE)
+       wl_delete_dirty_bss_cache(&g_bss_cache_ctrl);
+       wl_reset_bss_cache(&g_bss_cache_ctrl);
+#endif
+
+       ESCAN_TRACE(("scanned AP count (%d)\n", bss_list->count));
+
+       return err;
+}
+
+static wl_scan_params_t *
+wl_escan_alloc_params(int channel, int nprobes, int *out_params_size)
+{
+       wl_scan_params_t *params;
+       int params_size;
+       int num_chans;
+       int bssidx = 0;
+
+       *out_params_size = 0;
+
+       /* Our scan params only need space for 1 channel and 0 ssids */
+       params_size = WL_SCAN_PARAMS_FIXED_SIZE + 1 * sizeof(uint16);
+       params = (wl_scan_params_t*) kzalloc(params_size, GFP_KERNEL);
+       if (params == NULL) {
+               ESCAN_ERROR(("mem alloc failed (%d bytes)\n", params_size));
+               return params;
+       }
+       memset(params, 0, params_size);
+       params->nprobes = nprobes;
+
+       num_chans = (channel == 0) ? 0 : 1;
+
+       memcpy(&params->bssid, &ether_bcast, ETHER_ADDR_LEN);
+       params->bss_type = DOT11_BSSTYPE_ANY;
+       params->scan_type = DOT11_SCANTYPE_ACTIVE;
+       params->nprobes = htod32(1);
+       params->active_time = htod32(-1);
+       params->passive_time = htod32(-1);
+       params->home_time = htod32(10);
+       if (channel == -1)
+               params->channel_list[0] = htodchanspec(channel);
+       else
+               params->channel_list[0] = wl_ch_host_to_driver(bssidx, channel);
+
+       /* Our scan params have 1 channel and 0 ssids */
+       params->channel_num = htod32((0 << WL_SCAN_PARAMS_NSSID_SHIFT) |
+               (num_chans & WL_SCAN_PARAMS_COUNT_MASK));
+
+       *out_params_size = params_size; /* rtn size to the caller */
+       return params;
+}
+
+static void wl_escan_abort(struct wl_escan_info *escan)
+{
+       wl_scan_params_t *params = NULL;
+       s32 params_size = 0;
+       s32 err = BCME_OK;
+       if (!in_atomic()) {
+               /* Our scan params only need space for 1 channel and 0 ssids */
+               params = wl_escan_alloc_params(-1, 0, &params_size);
+               if (params == NULL) {
+                       ESCAN_ERROR(("scan params allocation failed \n"));
+                       err = -ENOMEM;
+               } else {
+                       /* Do a scan abort to stop the driver's scan engine */
+                       err = wldev_ioctl(escan->dev, WLC_SCAN, params, params_size, true);
+                       if (err < 0) {
+                               ESCAN_ERROR(("scan abort  failed \n"));
+                       }
+                       kfree(params);
+               }
+       }
+}
+
+static s32 wl_notify_escan_complete(struct wl_escan_info *escan, bool fw_abort)
+{
+       s32 err = BCME_OK;
+       int cmd = 0;
+#if WIRELESS_EXT > 13
+       union iwreq_data wrqu;
+       char extra[IW_CUSTOM_MAX + 1];
+
+       memset(extra, 0, sizeof(extra));
+#endif
+
+       ESCAN_TRACE(("Enter\n"));
+
+       if (!escan || !escan->dev) {
+               ESCAN_ERROR(("escan or dev is null\n"));
+               err = BCME_ERROR;
+               goto out;
+       }
+       if (fw_abort && !in_atomic())
+               wl_escan_abort(escan);
+
+       if (timer_pending(&escan->scan_timeout))
+               del_timer_sync(&escan->scan_timeout);
+#if defined(ESCAN_RESULT_PATCH)
+       escan->bss_list = wl_escan_get_buf(escan);
+       wl_escan_inform_bss(escan);
+#endif /* ESCAN_RESULT_PATCH */
+
+#if WIRELESS_EXT > 13
+#if WIRELESS_EXT > 14
+       cmd = SIOCGIWSCAN;
+#endif
+       ESCAN_TRACE(("event WLC_E_SCAN_COMPLETE\n"));
+       // terence 20150224: fix "wlan0: (WE) : Wireless Event too big (65306)"
+       memset(&wrqu, 0, sizeof(wrqu));
+       if (cmd) {
+               if (cmd == SIOCGIWSCAN) {
+                       wireless_send_event(escan->dev, cmd, &wrqu, NULL);
+               } else
+                       wireless_send_event(escan->dev, cmd, &wrqu, extra);
+       }
+#endif
+
+out:
+       return err;
+}
+
+#ifdef ESCAN_BUF_OVERFLOW_MGMT
+static void
+wl_cfg80211_find_removal_candidate(wl_bss_info_t *bss, removal_element_t *candidate)
+{
+       int idx;
+       for (idx = 0; idx < BUF_OVERFLOW_MGMT_COUNT; idx++) {
+               int len = BUF_OVERFLOW_MGMT_COUNT - idx - 1;
+               if (bss->RSSI < candidate[idx].RSSI) {
+                       if (len)
+                               memcpy(&candidate[idx + 1], &candidate[idx],
+                                       sizeof(removal_element_t) * len);
+                       candidate[idx].RSSI = bss->RSSI;
+                       candidate[idx].length = bss->length;
+                       memcpy(&candidate[idx].BSSID, &bss->BSSID, ETHER_ADDR_LEN);
+                       return;
+               }
+       }
+}
+
+static void
+wl_cfg80211_remove_lowRSSI_info(wl_scan_results_t *list, removal_element_t *candidate,
+       wl_bss_info_t *bi)
+{
+       int idx1, idx2;
+       int total_delete_len = 0;
+       for (idx1 = 0; idx1 < BUF_OVERFLOW_MGMT_COUNT; idx1++) {
+               int cur_len = WL_SCAN_RESULTS_FIXED_SIZE;
+               wl_bss_info_t *bss = NULL;
+               if (candidate[idx1].RSSI >= bi->RSSI)
+                       continue;
+               for (idx2 = 0; idx2 < list->count; idx2++) {
+                       bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length)) :
+                               list->bss_info;
+                       if (!bcmp(&candidate[idx1].BSSID, &bss->BSSID, ETHER_ADDR_LEN) &&
+                               candidate[idx1].RSSI == bss->RSSI &&
+                               candidate[idx1].length == dtoh32(bss->length)) {
+                               u32 delete_len = dtoh32(bss->length);
+                               ESCAN_TRACE(("delete scan info of " MACDBG " to add new AP\n",
+                                       MAC2STRDBG(bss->BSSID.octet)));
+                               if (idx2 < list->count -1) {
+                                       memmove((u8 *)bss, (u8 *)bss + delete_len,
+                                               list->buflen - cur_len - delete_len);
+                               }
+                               list->buflen -= delete_len;
+                               list->count--;
+                               total_delete_len += delete_len;
+                               /* if delete_len is greater than or equal to result length */
+                               if (total_delete_len >= bi->length) {
+                                       return;
+                               }
+                               break;
+                       }
+                       cur_len += dtoh32(bss->length);
+               }
+       }
+}
+#endif /* ESCAN_BUF_OVERFLOW_MGMT */
+
+static s32 wl_escan_handler(struct wl_escan_info *escan,
+       const wl_event_msg_t *e, void *data)
+{
+       s32 err = BCME_OK;
+       s32 status = ntoh32(e->status);
+       wl_bss_info_t *bi;
+       wl_escan_result_t *escan_result;
+       wl_bss_info_t *bss = NULL;
+       wl_scan_results_t *list;
+       u32 bi_length;
+       u32 i;
+       u16 channel;
+
+       ESCAN_TRACE(("enter event type : %d, status : %d \n",
+               ntoh32(e->event_type), ntoh32(e->status)));
+
+       mutex_lock(&escan->usr_sync);
+       escan_result = (wl_escan_result_t *)data;
+
+       if (escan->escan_state != ESCAN_STATE_SCANING) {
+               ESCAN_TRACE(("Not my scan\n"));
+               goto exit;
+       }
+
+       if (status == WLC_E_STATUS_PARTIAL) {
+               ESCAN_TRACE(("WLC_E_STATUS_PARTIAL \n"));
+               if (!escan_result) {
+                       ESCAN_ERROR(("Invalid escan result (NULL pointer)\n"));
+                       goto exit;
+               }
+               if (dtoh16(escan_result->bss_count) != 1) {
+                       ESCAN_ERROR(("Invalid bss_count %d: ignoring\n", escan_result->bss_count));
+                       goto exit;
+               }
+               bi = escan_result->bss_info;
+               if (!bi) {
+                       ESCAN_ERROR(("Invalid escan bss info (NULL pointer)\n"));
+                       goto exit;
+               }
+               bi_length = dtoh32(bi->length);
+               if (bi_length != (dtoh32(escan_result->buflen) - WL_ESCAN_RESULTS_FIXED_SIZE)) {
+                       ESCAN_ERROR(("Invalid bss_info length %d: ignoring\n", bi_length));
+                       goto exit;
+               }
+
+               /* +++++ terence 20130524: skip invalid bss */
+               channel =
+                       bi->ctl_ch ? bi->ctl_ch : CHSPEC_CHANNEL(wl_chspec_driver_to_host(escan->ioctl_ver, bi->chanspec));
+               if (!dhd_conf_match_channel(escan->pub, channel))
+                       goto exit;
+               /* ----- terence 20130524: skip invalid bss */
+
+               {
+                       int cur_len = WL_SCAN_RESULTS_FIXED_SIZE;
+#ifdef ESCAN_BUF_OVERFLOW_MGMT
+                       removal_element_t candidate[BUF_OVERFLOW_MGMT_COUNT];
+                       int remove_lower_rssi = FALSE;
+
+                       bzero(candidate, sizeof(removal_element_t)*BUF_OVERFLOW_MGMT_COUNT);
+#endif /* ESCAN_BUF_OVERFLOW_MGMT */
+
+                       list = wl_escan_get_buf(escan);
+#ifdef ESCAN_BUF_OVERFLOW_MGMT
+                       if (bi_length > ESCAN_BUF_SIZE - list->buflen)
+                               remove_lower_rssi = TRUE;
+#endif /* ESCAN_BUF_OVERFLOW_MGMT */
+
+                       ESCAN_TRACE(("%s("MACDBG") RSSI %d flags 0x%x length %d\n", bi->SSID,
+                               MAC2STRDBG(bi->BSSID.octet), bi->RSSI, bi->flags, bi->length));
+                       for (i = 0; i < list->count; i++) {
+                               bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length))
+                                       : list->bss_info;
+#ifdef ESCAN_BUF_OVERFLOW_MGMT
+                               ESCAN_TRACE(("%s("MACDBG"), i=%d bss: RSSI %d list->count %d\n",
+                                       bss->SSID, MAC2STRDBG(bss->BSSID.octet),
+                                       i, bss->RSSI, list->count));
+
+                               if (remove_lower_rssi)
+                                       wl_cfg80211_find_removal_candidate(bss, candidate);
+#endif /* ESCAN_BUF_OVERFLOW_MGMT */
+                               if (!bcmp(&bi->BSSID, &bss->BSSID, ETHER_ADDR_LEN) &&
+                                       (CHSPEC_BAND(wl_chspec_driver_to_host(escan->ioctl_ver, bi->chanspec))
+                                       == CHSPEC_BAND(wl_chspec_driver_to_host(escan->ioctl_ver, bss->chanspec))) &&
+                                       bi->SSID_len == bss->SSID_len &&
+                                       !bcmp(bi->SSID, bss->SSID, bi->SSID_len)) {
+
+                                       /* do not allow beacon data to update
+                                       *the data recd from a probe response
+                                       */
+                                       if (!(bss->flags & WL_BSS_FLAGS_FROM_BEACON) &&
+                                               (bi->flags & WL_BSS_FLAGS_FROM_BEACON))
+                                               goto exit;
+
+                                       ESCAN_TRACE(("%s("MACDBG"), i=%d prev: RSSI %d"
+                                               " flags 0x%x, new: RSSI %d flags 0x%x\n",
+                                               bss->SSID, MAC2STRDBG(bi->BSSID.octet), i,
+                                               bss->RSSI, bss->flags, bi->RSSI, bi->flags));
+
+                                       if ((bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) ==
+                                               (bi->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL)) {
+                                               /* preserve max RSSI if the measurements are
+                                               * both on-channel or both off-channel
+                                               */
+                                               ESCAN_TRACE(("%s("MACDBG"), same onchan"
+                                               ", RSSI: prev %d new %d\n",
+                                               bss->SSID, MAC2STRDBG(bi->BSSID.octet),
+                                               bss->RSSI, bi->RSSI));
+                                               bi->RSSI = MAX(bss->RSSI, bi->RSSI);
+                                       } else if ((bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) &&
+                                               (bi->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) == 0) {
+                                               /* preserve the on-channel rssi measurement
+                                               * if the new measurement is off channel
+                                               */
+                                               ESCAN_TRACE(("%s("MACDBG"), prev onchan"
+                                               ", RSSI: prev %d new %d\n",
+                                               bss->SSID, MAC2STRDBG(bi->BSSID.octet),
+                                               bss->RSSI, bi->RSSI));
+                                               bi->RSSI = bss->RSSI;
+                                               bi->flags |= WL_BSS_FLAGS_RSSI_ONCHANNEL;
+                                       }
+                                       if (dtoh32(bss->length) != bi_length) {
+                                               u32 prev_len = dtoh32(bss->length);
+
+                                               ESCAN_TRACE(("bss info replacement"
+                                                       " is occured(bcast:%d->probresp%d)\n",
+                                                       bss->ie_length, bi->ie_length));
+                                               ESCAN_TRACE(("%s("MACDBG"), replacement!(%d -> %d)\n",
+                                               bss->SSID, MAC2STRDBG(bi->BSSID.octet),
+                                               prev_len, bi_length));
+
+                                               if (list->buflen - prev_len + bi_length
+                                                       > ESCAN_BUF_SIZE) {
+                                                       ESCAN_ERROR(("Buffer is too small: keep the"
+                                                               " previous result of this AP\n"));
+                                                       /* Only update RSSI */
+                                                       bss->RSSI = bi->RSSI;
+                                                       bss->flags |= (bi->flags
+                                                               & WL_BSS_FLAGS_RSSI_ONCHANNEL);
+                                                       goto exit;
+                                               }
+
+                                               if (i < list->count - 1) {
+                                                       /* memory copy required by this case only */
+                                                       memmove((u8 *)bss + bi_length,
+                                                               (u8 *)bss + prev_len,
+                                                               list->buflen - cur_len - prev_len);
+                                               }
+                                               list->buflen -= prev_len;
+                                               list->buflen += bi_length;
+                                       }
+                                       list->version = dtoh32(bi->version);
+                                       memcpy((u8 *)bss, (u8 *)bi, bi_length);
+                                       goto exit;
+                               }
+                               cur_len += dtoh32(bss->length);
+                       }
+                       if (bi_length > ESCAN_BUF_SIZE - list->buflen) {
+#ifdef ESCAN_BUF_OVERFLOW_MGMT
+                               wl_cfg80211_remove_lowRSSI_info(list, candidate, bi);
+                               if (bi_length > ESCAN_BUF_SIZE - list->buflen) {
+                                       ESCAN_TRACE(("RSSI(" MACDBG ") is too low(%d) to add Buffer\n",
+                                               MAC2STRDBG(bi->BSSID.octet), bi->RSSI));
+                                       goto exit;
+                               }
+#else
+                               ESCAN_ERROR(("Buffer is too small: ignoring\n"));
+                               goto exit;
+#endif /* ESCAN_BUF_OVERFLOW_MGMT */
+                       }
+
+                       if (strlen(bi->SSID) == 0) { // terence: fix for hidden SSID
+                               ESCAN_SCAN(("Skip hidden SSID %pM\n", &bi->BSSID));
+                               goto exit;
+                       }
+
+                       memcpy(&(((char *)list)[list->buflen]), bi, bi_length);
+                       list->version = dtoh32(bi->version);
+                       list->buflen += bi_length;
+                       list->count++;
+               }
+       }
+       else if (status == WLC_E_STATUS_SUCCESS) {
+               escan->escan_state = ESCAN_STATE_IDLE;
+
+                       ESCAN_TRACE(("ESCAN COMPLETED\n"));
+                       escan->bss_list = wl_escan_get_buf(escan);
+                       ESCAN_TRACE(("SCAN COMPLETED: scanned AP count=%d\n",
+                               escan->bss_list->count));
+                       wl_escan_inform_bss(escan);
+                       wl_notify_escan_complete(escan, false);
+
+       } else if ((status == WLC_E_STATUS_ABORT) || (status == WLC_E_STATUS_NEWSCAN) ||
+               (status == WLC_E_STATUS_11HQUIET) || (status == WLC_E_STATUS_CS_ABORT) ||
+               (status == WLC_E_STATUS_NEWASSOC)) {
+               /* Handle all cases of scan abort */
+               escan->escan_state = ESCAN_STATE_IDLE;
+               ESCAN_TRACE(("ESCAN ABORT reason: %d\n", status));
+               wl_escan_inform_bss(escan);
+               wl_notify_escan_complete(escan, false);
+       } else if (status == WLC_E_STATUS_TIMEOUT) {
+               ESCAN_ERROR(("WLC_E_STATUS_TIMEOUT\n"));
+               ESCAN_ERROR(("reason[0x%x]\n", e->reason));
+               if (e->reason == 0xFFFFFFFF) {
+                       wl_notify_escan_complete(escan, true);
+               }
+       } else {
+               ESCAN_ERROR(("unexpected Escan Event %d : abort\n", status));
+               escan->escan_state = ESCAN_STATE_IDLE;
+               escan->bss_list = wl_escan_get_buf(escan);
+               ESCAN_TRACE(("SCAN ABORTED(UNEXPECTED): scanned AP count=%d\n",
+                               escan->bss_list->count));
+               wl_escan_inform_bss(escan);
+               wl_notify_escan_complete(escan, false);
+       }
+exit:
+       mutex_unlock(&escan->usr_sync);
+       return err;
+}
+
+static int
+wl_escan_prep(struct wl_escan_info *escan, wl_uint32_list_t *list,
+       wl_scan_params_t *params, wlc_ssid_t *ssid)
+{
+       int err = 0;
+       wl_scan_results_t *results;
+       s32 offset;
+       char *ptr;
+       int i = 0, j = 0;
+       wlc_ssid_t ssid_tmp;
+       u32 n_channels = 0;
+       uint channel;
+       chanspec_t chanspec;
+
+       results = wl_escan_get_buf(escan);
+       results->version = 0;
+       results->count = 0;
+       results->buflen = WL_SCAN_RESULTS_FIXED_SIZE;
+       escan->escan_state = ESCAN_STATE_SCANING;
+
+       /* Arm scan timeout timer */
+       mod_timer(&escan->scan_timeout, jiffies + msecs_to_jiffies(WL_ESCAN_TIMER_INTERVAL_MS));
+
+       memcpy(&params->bssid, &ether_bcast, ETHER_ADDR_LEN);
+       params->bss_type = DOT11_BSSTYPE_ANY;
+       params->scan_type = 0;
+       params->nprobes = -1;
+       params->active_time = -1;
+       params->passive_time = -1;
+       params->home_time = -1;
+       params->channel_num = 0;
+
+       params->nprobes = htod32(params->nprobes);
+       params->active_time = htod32(params->active_time);
+       params->passive_time = htod32(params->passive_time);
+       params->home_time = htod32(params->home_time);
+
+       n_channels = dtoh32(list->count);
+       /* Copy channel array if applicable */
+       ESCAN_SCAN(("### List of channelspecs to scan ###\n"));
+       if (n_channels > 0) {
+               for (i = 0; i < n_channels; i++) {
+                       channel = dtoh32(list->element[i]);
+                       if (!dhd_conf_match_channel(escan->pub, channel))
+                               continue;
+                       chanspec = WL_CHANSPEC_BW_20;
+                       if (chanspec == INVCHANSPEC) {
+                               ESCAN_ERROR(("Invalid chanspec! Skipping channel\n"));
+                               continue;
+                       }
+                       if (channel <= CH_MAX_2G_CHANNEL) {
+                               chanspec |= WL_CHANSPEC_BAND_2G;
+                       } else {
+                               chanspec |= WL_CHANSPEC_BAND_5G;
+                       }
+                       params->channel_list[j] = channel;
+                       params->channel_list[j] &= WL_CHANSPEC_CHAN_MASK;
+                       params->channel_list[j] |= chanspec;
+                       ESCAN_SCAN(("Chan : %d, Channel spec: %x \n",
+                               channel, params->channel_list[j]));
+                       params->channel_list[j] = wl_chspec_host_to_driver(params->channel_list[j]);
+                       j++;
+               }
+       } else {
+               ESCAN_SCAN(("Scanning all channels\n"));
+       }
+
+       if (ssid && ssid->SSID_len) {
+               /* Copy ssid array if applicable */
+               ESCAN_SCAN(("### List of SSIDs to scan ###\n"));
+               offset = offsetof(wl_scan_params_t, channel_list) + n_channels * sizeof(u16);
+               offset = roundup(offset, sizeof(u32));
+               ptr = (char*)params + offset;
+
+               ESCAN_SCAN(("0: Broadcast scan\n"));
+               memset(&ssid_tmp, 0, sizeof(wlc_ssid_t));
+               ssid_tmp.SSID_len = 0;
+               memcpy(ptr, &ssid_tmp, sizeof(wlc_ssid_t));
+               ptr += sizeof(wlc_ssid_t);
+
+               memset(&ssid_tmp, 0, sizeof(wlc_ssid_t));
+               ssid_tmp.SSID_len = ssid->SSID_len;
+               memcpy(ssid_tmp.SSID, ssid->SSID, ssid->SSID_len);
+               memcpy(ptr, &ssid_tmp, sizeof(wlc_ssid_t));
+               ptr += sizeof(wlc_ssid_t);
+               ESCAN_SCAN(("1: scan for %s size=%d\n", ssid_tmp.SSID, ssid_tmp.SSID_len));
+               /* Adding mask to channel numbers */
+               params->channel_num =
+               htod32((2 << WL_SCAN_PARAMS_NSSID_SHIFT) |
+                      (n_channels & WL_SCAN_PARAMS_COUNT_MASK));
+       }
+       else {
+               ESCAN_SCAN(("Broadcast scan\n"));
+       }
+
+       return err;
+}
+
+static int wl_escan_reset(void) {
+       struct wl_escan_info *escan = g_escan;
+
+       if (timer_pending(&escan->scan_timeout))
+               del_timer_sync(&escan->scan_timeout);
+       escan->escan_state = ESCAN_STATE_IDLE;
+
+       return 0;
+}
+
+static void wl_escan_timeout(unsigned long data)
+{
+       wl_event_msg_t msg;
+       struct wl_escan_info *escan = (struct wl_escan_info *)data;
+       struct wl_scan_results *bss_list;
+       struct wl_bss_info *bi = NULL;
+       s32 i;
+       u32 channel;
+
+       bss_list = wl_escan_get_buf(escan);
+       if (!bss_list) {
+               ESCAN_ERROR(("bss_list is null. Didn't receive any partial scan results\n"));
+       } else {
+               ESCAN_ERROR(("%s: scanned AP count (%d)\n", __FUNCTION__, bss_list->count));
+               bi = next_bss(bss_list, bi);
+               for_each_bss(bss_list, bi, i) {
+                       channel = wf_chspec_ctlchan(wl_chspec_driver_to_host(escan->ioctl_ver, bi->chanspec));
+                       ESCAN_ERROR(("SSID :%s  Channel :%d\n", bi->SSID, channel));
+               }
+       }
+
+       if (!escan->dev) {
+               ESCAN_ERROR(("No dev present\n"));
+               return;
+       }
+
+       bzero(&msg, sizeof(wl_event_msg_t));
+       ESCAN_ERROR(("timer expired\n"));
+
+       msg.event_type = hton32(WLC_E_ESCAN_RESULT);
+       msg.status = hton32(WLC_E_STATUS_TIMEOUT);
+       msg.reason = 0xFFFFFFFF;
+       wl_escan_event(escan->dev, &msg, NULL);
+
+       // terence 20130729: workaround to fix out of memory in firmware
+//     if (dhd_conf_get_chip(dhd_get_pub(dev)) == BCM43362_CHIP_ID) {
+//             ESCAN_ERROR(("Send hang event\n"));
+//             net_os_send_hang_message(dev);
+//     }
+}
+
+int
+wl_escan_set_scan(
+       struct net_device *dev,
+       struct iw_request_info *info,
+       union iwreq_data *wrqu,
+       char *extra
+)
+{
+       s32 err = BCME_OK;
+       s32 params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_escan_params_t, params));
+       wl_escan_params_t *params = NULL;
+       scb_val_t scbval;
+       static int cnt = 0;
+       struct wl_escan_info *escan = NULL;
+       wlc_ssid_t ssid;
+       u32 n_channels = 0;
+       wl_uint32_list_t *list;
+       u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
+       s32 val = 0;
+
+       ESCAN_TRACE(("Enter \n"));
+
+       escan = g_escan;
+       if (!escan) {
+               ESCAN_ERROR(("device is not ready\n"));           \
+               return -EIO;
+       }
+       mutex_lock(&escan->usr_sync);
+
+       if (!escan->ioctl_ver) {
+               val = 1;
+               if ((err = wldev_ioctl(dev, WLC_GET_VERSION, &val, sizeof(int), false) < 0)) {
+                       ANDROID_ERROR(("WLC_GET_VERSION failed, err=%d\n", err));
+                       goto exit;
+               }
+               val = dtoh32(val);
+               if (val != WLC_IOCTL_VERSION && val != 1) {
+                       ANDROID_ERROR(("Version mismatch, please upgrade. Got %d, expected %d or 1\n",
+                               val, WLC_IOCTL_VERSION));
+                       goto exit;
+               }
+               escan->ioctl_ver = val;
+               printf("%s: ioctl_ver=%d\n", __FUNCTION__, val);
+       }
+
+       /* default Broadcast scan */
+       memset(&ssid, 0, sizeof(ssid));
+
+#if WIRELESS_EXT > 17
+       /* check for given essid */
+       if (wrqu->data.length == sizeof(struct iw_scan_req)) {
+               if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
+                       struct iw_scan_req *req = (struct iw_scan_req *)extra;
+                       ssid.SSID_len = MIN(sizeof(ssid.SSID), req->essid_len);
+                       memcpy(ssid.SSID, req->essid, ssid.SSID_len);
+                       ssid.SSID_len = htod32(ssid.SSID_len);
+               }
+       }
+#endif
+       if (escan->escan_state == ESCAN_STATE_SCANING) {
+               ESCAN_ERROR(("Scanning already\n"));
+               goto exit;
+       }
+
+       /* if scan request is not empty parse scan request paramters */
+       memset(valid_chan_list, 0, sizeof(valid_chan_list));
+       list = (wl_uint32_list_t *)(void *) valid_chan_list;
+       list->count = htod32(WL_NUMCHANNELS);
+       err = wldev_ioctl(escan->dev, WLC_GET_VALID_CHANNELS, valid_chan_list, sizeof(valid_chan_list), false);
+       if (err != 0) {
+               ESCAN_ERROR(("%s: get channels failed with %d\n", __FUNCTION__, err));
+               goto exit;
+       }
+       n_channels = dtoh32(list->count);
+       /* Allocate space for populating ssids in wl_escan_params_t struct */
+       if (dtoh32(list->count) % 2)
+               /* If n_channels is odd, add a padd of u16 */
+               params_size += sizeof(u16) * (n_channels + 1);
+       else
+               params_size += sizeof(u16) * n_channels;
+       if (ssid.SSID_len) {
+               params_size += sizeof(struct wlc_ssid) * 2;
+       }
+
+       params = (wl_escan_params_t *) kzalloc(params_size, GFP_KERNEL);
+       if (params == NULL) {
+               err = -ENOMEM;
+               goto exit;
+       }
+       wl_escan_prep(escan, list, &params->params, &ssid);
+
+       params->version = htod32(ESCAN_REQ_VERSION);
+       params->action =  htod16(WL_SCAN_ACTION_START);
+       wl_escan_set_sync_id(params->sync_id);
+       if (params_size + sizeof("escan") >= WLC_IOCTL_MEDLEN) {
+               ESCAN_ERROR(("ioctl buffer length not sufficient\n"));
+               kfree(params);
+               err = -ENOMEM;
+               goto exit;
+       }
+       params->params.scan_type = DOT11_SCANTYPE_ACTIVE;
+       ESCAN_TRACE(("Passive scan_type %d\n", params->params.scan_type));
+
+       err = wldev_iovar_setbuf(dev, "escan", params, params_size,
+               escan->escan_ioctl_buf, WLC_IOCTL_MEDLEN, NULL);
+       if (unlikely(err)) {
+               if (err == BCME_EPERM)
+                       /* Scan Not permitted at this point of time */
+                       ESCAN_TRACE(("Escan not permitted at this time (%d)\n", err));
+               else
+                       ESCAN_ERROR(("Escan set error (%d)\n", err));
+               wl_escan_reset();
+       }
+       kfree(params);
+
+exit:
+       if (unlikely(err)) {
+               /* Don't print Error incase of Scan suppress */
+               if ((err == BCME_EPERM))
+                       ESCAN_TRACE(("Escan failed: Scan Suppressed \n"));
+               else {
+                       cnt++;
+                       ESCAN_ERROR(("error (%d), cnt=%d\n", err, cnt));
+                       // terence 20140111: send disassoc to firmware
+                       if (cnt >= 4) {
+                               memset(&scbval, 0, sizeof(scb_val_t));
+                               wldev_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), true);
+                               ESCAN_ERROR(("Send disassoc to break the busy dev=%p\n", dev));
+                               cnt = 0;
+                       }
+               }
+       } else {
+               cnt = 0;
+       }
+       mutex_unlock(&escan->usr_sync);
+       return err;
+}
+
+int
+wl_escan_get_scan(
+       struct net_device *dev,
+       struct iw_request_info *info,
+       struct iw_point *dwrq,
+       char *extra
+)
+{
+       s32 err = BCME_OK;
+       struct iw_event iwe;
+       int i, j;
+       char *event = extra, *end = extra + dwrq->length, *value;
+       int16 rssi;
+       int channel;
+       wl_bss_info_t *bi = NULL;
+       struct wl_escan_info *escan = g_escan;
+       struct wl_scan_results *bss_list;
+#if defined(BSSCACHE)
+       wl_bss_cache_t *node;
+#endif
+
+       ESCAN_TRACE(("%s: %s SIOCGIWSCAN, len=%d\n", __FUNCTION__, dev->name, dwrq->length));
+
+       if (!extra)
+               return -EINVAL;
+
+       mutex_lock(&escan->usr_sync);
+
+       /* Check for scan in progress */
+       if (escan->escan_state == ESCAN_STATE_SCANING) {
+               ESCAN_TRACE(("%s: SIOCGIWSCAN GET still scanning\n", dev->name));
+               err = -EAGAIN;
+               goto exit;
+       }
+
+#if defined(BSSCACHE)
+       bss_list = &g_bss_cache_ctrl.m_cache_head->results;
+       node = g_bss_cache_ctrl.m_cache_head;
+       for (i=0; node && i<IW_MAX_AP; i++)
+#else
+       bss_list = escan->bss_list;
+       bi = next_bss(bss_list, bi);
+       for_each_bss(bss_list, bi, i)
+#endif
+       {
+#if defined(BSSCACHE)
+               bi = node->results.bss_info;
+#endif
+               /* overflow check cover fields before wpa IEs */
+               if (event + ETHER_ADDR_LEN + bi->SSID_len + IW_EV_UINT_LEN + IW_EV_FREQ_LEN +
+                       IW_EV_QUAL_LEN >= end) {
+                       err = -E2BIG;
+                       goto exit;
+               }
+
+#if defined(RSSIAVG)
+               rssi = wl_get_avg_rssi(&g_rssi_cache_ctrl, &bi->BSSID);
+               if (rssi == RSSI_MINVAL)
+                       rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL);
+#else
+               // terence 20150419: limit the max. rssi to -2 or the bss will be filtered out in android OS
+               rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL);
+#endif
+               channel = wf_chspec_ctlchan(wl_chspec_driver_to_host(escan->ioctl_ver, bi->chanspec));
+               ESCAN_SCAN(("%s: BSSID="MACSTR", channel=%d, RSSI=%d, SSID=\"%s\"\n",
+               __FUNCTION__, MAC2STR(bi->BSSID.octet), channel, rssi, bi->SSID));
+
+               /* First entry must be the BSSID */
+               iwe.cmd = SIOCGIWAP;
+               iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+               memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN);
+               event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN);
+
+               /* SSID */
+               iwe.u.data.length = dtoh32(bi->SSID_len);
+               iwe.cmd = SIOCGIWESSID;
+               iwe.u.data.flags = 1;
+               event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID);
+
+               /* Mode */
+               if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) {
+                       iwe.cmd = SIOCGIWMODE;
+                       if (dtoh16(bi->capability) & DOT11_CAP_ESS)
+                               iwe.u.mode = IW_MODE_INFRA;
+                       else
+                               iwe.u.mode = IW_MODE_ADHOC;
+                       event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN);
+               }
+
+               /* Channel */
+               iwe.cmd = SIOCGIWFREQ;
+#if 1
+               iwe.u.freq.m = wf_channel2mhz(channel, channel <= CH_MAX_2G_CHANNEL ?
+                               WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G);
+#else
+               iwe.u.freq.m = wf_channel2mhz(bi->n_cap ?
+                               bi->ctl_ch : CHSPEC_CHANNEL(bi->chanspec),
+                               CHSPEC_CHANNEL(bi->chanspec) <= CH_MAX_2G_CHANNEL ?
+                               WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G);
+#endif
+               iwe.u.freq.e = 6;
+               event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN);
+
+               /* Channel quality */
+               iwe.cmd = IWEVQUAL;
+               iwe.u.qual.qual = rssi_to_qual(rssi);
+               iwe.u.qual.level = 0x100 + rssi;
+               iwe.u.qual.noise = 0x100 + bi->phy_noise;
+               event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN);
+
+               wl_iw_handle_scanresults_ies(&event, end, info, bi);
+
+               /* Encryption */
+               iwe.cmd = SIOCGIWENCODE;
+               if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY)
+                       iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+               else
+                       iwe.u.data.flags = IW_ENCODE_DISABLED;
+               iwe.u.data.length = 0;
+               event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event);
+
+               /* Rates */
+               if (bi->rateset.count <= sizeof(bi->rateset.rates)) {
+                       if (event + IW_MAX_BITRATES*IW_EV_PARAM_LEN >= end) {
+                               err = -E2BIG;
+                               goto exit;
+                       }
+                       value = event + IW_EV_LCP_LEN;
+                       iwe.cmd = SIOCGIWRATE;
+                       /* Those two flags are ignored... */
+                       iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+                       for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) {
+                               iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000;
+                               value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe,
+                                       IW_EV_PARAM_LEN);
+                       }
+                       event = value;
+               }
+#if defined(BSSCACHE)
+               node = node->next;
+#endif
+       }
+
+       dwrq->length = event - extra;
+       dwrq->flags = 0;        /* todo */
+       ESCAN_SCAN(("scanned AP count (%d)\n", i));
+exit:
+       mutex_unlock(&escan->usr_sync);
+       return err;
+}
+
+static s32 wl_create_event_handler(struct wl_escan_info *escan)
+{
+       int ret = 0;
+       ESCAN_TRACE(("Enter \n"));
+
+       /* Do not use DHD in cfg driver */
+       escan->event_tsk.thr_pid = -1;
+
+       PROC_START(wl_escan_event_handler, escan, &escan->event_tsk, 0, "wl_escan_handler");
+       if (escan->event_tsk.thr_pid < 0)
+               ret = -ENOMEM;
+       return ret;
+}
+
+static void wl_destroy_event_handler(struct wl_escan_info *escan)
+{
+       if (escan->event_tsk.thr_pid >= 0)
+               PROC_STOP(&escan->event_tsk);
+}
+
+static void wl_escan_deinit(void)
+{
+       struct wl_escan_info *escan = g_escan;
+
+       printf("%s: Enter\n", __FUNCTION__);
+       if (!escan) {
+               ESCAN_ERROR(("device is not ready\n"));           \
+               return;
+       }
+       wl_destroy_event_handler(escan);
+       wl_flush_eq(escan);
+       del_timer_sync(&escan->scan_timeout);
+
+#if defined(RSSIAVG)
+       wl_free_rssi_cache(&g_rssi_cache_ctrl);
+#endif
+#if defined(BSSCACHE)
+       wl_free_bss_cache(&g_bss_cache_ctrl);
+#endif
+}
+
+static s32 wl_escan_init(void)
+{
+       struct wl_escan_info *escan = g_escan;
+       int err = 0;
+
+       printf("%s: Enter\n", __FUNCTION__);
+       if (!escan) {
+               ESCAN_ERROR(("device is not ready\n"));           \
+               return -EIO;
+       }
+
+       /* Init scan_timeout timer */
+       init_timer(&escan->scan_timeout);
+       escan->scan_timeout.data = (unsigned long) escan;
+       escan->scan_timeout.function = wl_escan_timeout;
+
+       if (wl_create_event_handler(escan)) {
+               err = -ENOMEM;
+               goto err;
+       }
+       memset(escan->evt_handler, 0, sizeof(escan->evt_handler));
+
+       escan->evt_handler[WLC_E_ESCAN_RESULT] = wl_escan_handler;
+       escan->escan_state = ESCAN_STATE_IDLE;
+
+       mutex_init(&escan->usr_sync);
+
+       return 0;
+err:
+       wl_escan_deinit();
+       return err;
+}
+
+void wl_escan_detach(void)
+{
+       struct wl_escan_info *escan = g_escan;
+
+       printf("%s: Enter\n", __FUNCTION__);
+
+       if (!escan) {
+               ESCAN_ERROR(("device is not ready\n"));           \
+               return;
+       }
+
+       wl_escan_deinit();
+
+       if (escan->escan_ioctl_buf) {
+               kfree(escan->escan_ioctl_buf);
+               escan->escan_ioctl_buf = NULL;
+       }
+
+       kfree(escan);
+       g_escan = NULL;
+}
+
+int
+wl_escan_attach(struct net_device *dev, void * dhdp)
+{
+       struct wl_escan_info *escan = NULL;
+
+       printf("%s: Enter\n", __FUNCTION__);
+
+       if (!dev)
+               return 0;
+
+       escan = kmalloc(sizeof(struct wl_escan_info), GFP_KERNEL);
+       if (!escan)
+               return -ENOMEM;
+       memset(escan, 0, sizeof(struct wl_escan_info));
+
+       /* we only care about main interface so save a global here */
+       g_escan = escan;
+       escan->dev = dev;
+       escan->pub = dhdp;
+       escan->escan_state = ESCAN_STATE_IDLE;
+
+       escan->escan_ioctl_buf = (void *)kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
+       if (unlikely(!escan->escan_ioctl_buf)) {
+               ESCAN_ERROR(("Ioctl buf alloc failed\n"));
+               goto err ;
+       }
+       wl_init_eq(escan);
+#ifdef WL_ESCAN
+       wl_escan_init();
+#endif
+
+       return 0;
+err:
+       wl_escan_detach();
+       return -ENOMEM;
+}
+
+#endif /* WL_ESCAN */
+
diff --git a/bcmdhd.1.363.59.144.x.cn/wl_escan.h b/bcmdhd.1.363.59.144.x.cn/wl_escan.h
new file mode 100644 (file)
index 0000000..6be090a
--- /dev/null
@@ -0,0 +1,75 @@
+\r
+#ifndef _wl_escan_\r
+#define _wl_escan_\r
+\r
+#include <linux/wireless.h>\r
+#include <wl_iw.h>\r
+#include <dngl_stats.h>\r
+#include <dhd.h>\r
+#include <linux/time.h>\r
+\r
+\r
+#ifdef DHD_MAX_IFS\r
+#define WL_MAX_IFS DHD_MAX_IFS\r
+#else\r
+#define WL_MAX_IFS 16\r
+#endif\r
+\r
+#define ESCAN_BUF_SIZE (64 * 1024)\r
+\r
+#define WL_ESCAN_TIMER_INTERVAL_MS     10000 /* Scan timeout */\r
+\r
+/* event queue for cfg80211 main event */\r
+struct escan_event_q {\r
+       struct list_head eq_list;\r
+       u32 etype;\r
+       wl_event_msg_t emsg;\r
+       s8 edata[1];\r
+};\r
+\r
+/* donlge escan state */\r
+enum escan_state {\r
+       ESCAN_STATE_IDLE,\r
+       ESCAN_STATE_SCANING\r
+};\r
+\r
+struct wl_escan_info;\r
+\r
+typedef s32(*ESCAN_EVENT_HANDLER) (struct wl_escan_info *escan,\r
+                            const wl_event_msg_t *e, void *data);\r
+\r
+typedef struct wl_escan_info {\r
+       struct net_device *dev;\r
+       dhd_pub_t *pub;\r
+       struct timer_list scan_timeout;   /* Timer for catch scan event timeout */\r
+       int    escan_state;\r
+       int ioctl_ver;\r
+\r
+       char ioctlbuf[WLC_IOCTL_SMLEN];\r
+       u8 escan_buf[ESCAN_BUF_SIZE];\r
+       struct wl_scan_results *bss_list;\r
+       struct wl_scan_results *scan_results;\r
+       struct ether_addr disconnected_bssid;\r
+       u8 *escan_ioctl_buf;\r
+       spinlock_t eq_lock;     /* for event queue synchronization */\r
+       struct list_head eq_list;       /* used for event queue */\r
+       tsk_ctl_t event_tsk;            /* task of main event handler thread */\r
+       ESCAN_EVENT_HANDLER evt_handler[WLC_E_LAST];\r
+       struct mutex usr_sync;  /* maily for up/down synchronization */\r
+} wl_escan_info_t;\r
+\r
+void wl_escan_event(struct net_device *ndev, const wl_event_msg_t * e, void *data);\r
+\r
+int wl_escan_set_scan(\r
+       struct net_device *dev,\r
+       struct iw_request_info *info,\r
+       union iwreq_data *wrqu,\r
+       char *extra\r
+);\r
+int wl_escan_get_scan(struct net_device *dev,  struct iw_request_info *info,\r
+       struct iw_point *dwrq, char *extra);\r
+int wl_escan_attach(struct net_device *dev, void * dhdp);\r
+void wl_escan_detach(void);\r
+\r
+#endif /* _wl_escan_ */\r
+\r