update dhd to 100.10.545.11 [1/1]
[GitHub/LineageOS/G12/android_hardware_amlogic_kernel-modules_dhd-driver.git] / bcmdhd.100.10.315.x / wl_android_ext.c
index a1498ae08107ef72b0c7bd7174b553366582856c..dde787ccb1cc00b160790d34acae93d3cac95a1d 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/wireless.h>
 #if defined(WL_WIRELESS_EXT)
 #include <wl_iw.h>
-#endif
+#endif /* WL_WIRELESS_EXT */
 #include <wldev_common.h>
 #include <wlioctl.h>
 #include <bcmutils.h>
 #include <dhd_config.h>
 #ifdef WL_CFG80211
 #include <wl_cfg80211.h>
-#endif
+#endif /* WL_CFG80211 */
+#ifdef WL_ESCAN
+#include <wl_escan.h>
+#endif /* WL_ESCAN */
 
 #define AEXT_ERROR(name, arg1, args...) \
        do { \
 #define AEXT_TRACE(name, arg1, args...) \
        do { \
                if (android_msg_level & ANDROID_TRACE_LEVEL) { \
-                       printk(KERN_ERR "[dhd-%s] AEXT-TRACE) %s : " arg1, name, __func__, ## args); \
+                       printk(KERN_INFO "[dhd-%s] AEXT-TRACE) %s : " arg1, name, __func__, ## args); \
                } \
        } while (0)
 #define AEXT_INFO(name, arg1, args...) \
        do { \
                if (android_msg_level & ANDROID_INFO_LEVEL) { \
-                       printk(KERN_ERR "[dhd-%s] AEXT-INFO) %s : " arg1, name, __func__, ## args); \
+                       printk(KERN_INFO "[dhd-%s] AEXT-INFO) %s : " arg1, name, __func__, ## args); \
                } \
        } while (0)
 #define AEXT_DBG(name, arg1, args...) \
        do { \
                if (android_msg_level & ANDROID_DBG_LEVEL) { \
-                       printk(KERN_ERR "[dhd-%s] AEXT-DBG) %s : " arg1, name, __func__, ## args); \
+                       printk(KERN_INFO "[dhd-%s] AEXT-DBG) %s : " arg1, name, __func__, ## args); \
                } \
        } while (0)
 
@@ -66,7 +69,7 @@
 #define WL_SCAN_JOIN_PROBE_INTERVAL_MS                 20
 #define WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS      320
 #define WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS     400
-#endif
+#endif /* WL_CFG80211 */
 #define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
 
 #ifndef IW_CUSTOM_MAX
 #define CMD_ISAM_ENABLE                        "ISAM_ENABLE"
 #define CMD_ISAM_DISABLE               "ISAM_DISABLE"
 #define CMD_ISAM_STATUS                        "ISAM_STATUS"
+#define CMD_ISAM_PEER_PATH             "ISAM_PEER_PATH"
+#define CMD_ISAM_PARAM                 "ISAM_PARAM"
 #ifdef PROP_TXSTATUS
 #ifdef PROP_TXSTATUS_VSDB
 #include <dhd_wlfc.h>
 extern int disable_proptx;
 #endif /* PROP_TXSTATUS_VSDB */
-#endif
-#endif
+#endif /* PROP_TXSTATUS */
+#endif /* WL_EXT_IAPSTA */
 #define CMD_AUTOCHANNEL                "AUTOCHANNEL"
 #define CMD_WL         "WL"
 
 #ifdef WL_EXT_IAPSTA
 typedef enum APSTAMODE {
+       IUNKNOWN_MODE = 0,
        ISTAONLY_MODE = 1,
        IAPONLY_MODE = 2,
        ISTAAP_MODE = 3,
@@ -120,6 +126,8 @@ typedef enum APSTAMODE {
 typedef enum IFMODE {
        ISTA_MODE = 1,
        IAP_MODE,
+       IGO_MODE,
+       IGC_MODE,
        IMESH_MODE
 } ifmode_t;
 
@@ -179,6 +187,15 @@ typedef struct wl_if_info {
        authmode_t amode;
        encmode_t emode;
        char key[100];
+#if defined(WLMESH) && defined(WL_ESCAN)
+       struct wl_escan_info *escan;
+       timer_list_compat_t delay_scan;
+#endif /* WLMESH && WL_ESCAN */
+       struct delayed_work pm_enable_work;
+       struct mutex pm_sync;
+#ifdef PROPTX_MAXCOUNT
+       int transit_maxcount;
+#endif /* PROP_TXSTATUS_VSDB */
 } wl_if_info_t;
 
 #define CSA_FW_BIT             (1<<0)
@@ -189,12 +206,18 @@ typedef struct wl_apsta_params {
        struct dhd_pub *dhd;
        int ioctl_ver;
        bool init;
-       bool rsdb;
+       int rsdb;
        bool vsdb;
        uint csa;
+       uint acs;
+       bool radar;
        apstamode_t apstamode;
        wait_queue_head_t netif_change_event;
        struct mutex usr_sync;
+#if defined(WLMESH) && defined(WL_ESCAN)
+       int macs;
+       struct wl_mesh_params mesh_info;
+#endif /* WLMESH && WL_ESCAN */
 } wl_apsta_params_t;
 
 #define MAX_AP_LINK_WAIT_TIME   3000
@@ -217,8 +240,12 @@ enum wifi_isam_status {
 #define wl_chg_isam_status(cur_if, stat) \
        (change_bit(ISAM_STATUS_ ## stat, &(cur_if)->status))
 
-static int wl_ext_enable_iface(struct net_device *dev, char *ifname);
-#endif
+static int wl_ext_enable_iface(struct net_device *dev, char *ifname, int wait_up);
+static int wl_ext_disable_iface(struct net_device *dev, char *ifname);
+#if defined(WLMESH) && defined(WL_ESCAN)
+static int wl_mesh_escan_attach(dhd_pub_t *dhd, struct wl_if_info *cur_if);
+#endif /* WLMESH && WL_ESCAN */
+#endif /* WL_EXT_IAPSTA */
 
 #ifdef IDHCP
 typedef struct dhcpc_parameter {
@@ -226,7 +253,7 @@ typedef struct dhcpc_parameter {
        uint32 ip_serv;
        uint32 lease_time;
 } dhcpc_para_t;
-#endif
+#endif /* IDHCP */
 
 #ifdef WL_EXT_WOWL
 #define WL_WOWL_TCPFIN (1 << 26)
@@ -234,7 +261,7 @@ typedef struct wl_wowl_pattern2 {
        char cmd[4];
        wl_wowl_pattern_t wowl_pattern;
 } wl_wowl_pattern2_t;
-#endif
+#endif /* WL_EXT_WOWL */
 
 #ifdef WL_EXT_TCPKA
 typedef struct tcpka_conn {
@@ -266,7 +293,7 @@ typedef struct tcpka_conn_info {
        uint32 seq;
        uint32 ack;
 } tcpka_conn_sess_info_t;
-#endif
+#endif /* WL_EXT_TCPKA */
 
 static int wl_ext_wl_iovar(struct net_device *dev, char *command, int total_len);
 
@@ -432,9 +459,6 @@ wl_ext_ch_to_chanspec(int ioctl_ver, int ch,
 
                join_params->params.chanspec_num =
                        htod32(join_params->params.chanspec_num);
-               AEXT_ERROR("wlan", "join_params->params.chanspec_list[0]= %X, %d channels\n",
-                       join_params->params.chanspec_list[0],
-                       join_params->params.chanspec_num);
        }
 }
 
@@ -484,7 +508,107 @@ wl_ext_chspec_driver_to_host(int ioctl_ver, chanspec_t chanspec)
 
        return chanspec;
 }
+#endif /* WL_EXT_IAPSTA || WL_CFG80211 || WL_ESCAN */
+
+bool
+wl_ext_check_scan(struct net_device *dev, dhd_pub_t *dhdp)
+{
+#ifdef WL_CFG80211
+       struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
+#endif /* WL_CFG80211 */
+#ifdef WL_ESCAN
+       struct wl_escan_info *escan = dhdp->escan;
+#endif /* WL_ESCAN */
+
+#ifdef WL_CFG80211
+       if (wl_get_drv_status_all(cfg, SCANNING)) {
+               AEXT_ERROR(dev->name, "cfg80211 scanning...\n");
+               return TRUE;
+       }
+#endif /* WL_CFG80211 */
+
+#ifdef WL_ESCAN
+       if (escan->escan_state == ESCAN_STATE_SCANING) {
+               AEXT_ERROR(dev->name, "escan scanning...\n");
+               return TRUE;
+       }
+#endif /* WL_ESCAN */
+
+       return FALSE;
+}
+
+#if defined(WL_CFG80211) || defined(WL_ESCAN)
+void
+wl_ext_user_sync(struct dhd_pub *dhd, int ifidx, bool lock)
+{
+       struct net_device *dev = dhd_idx2net(dhd, ifidx);
+#ifdef WL_CFG80211
+       struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
+#endif /* WL_CFG80211 */
+#ifdef WL_ESCAN
+       struct wl_escan_info *escan = dhd->escan;
+#endif /* WL_ESCAN */
+
+       AEXT_INFO(dev->name, "lock=%d\n", lock);
+
+       if (lock) {
+#if defined(WL_CFG80211)
+               mutex_lock(&cfg->usr_sync);
+#endif
+#if defined(WL_ESCAN)
+               mutex_lock(&escan->usr_sync);
 #endif
+       } else {
+#if defined(WL_CFG80211)
+               mutex_unlock(&cfg->usr_sync);
+#endif
+#if defined(WL_ESCAN)
+               mutex_unlock(&escan->usr_sync);
+#endif
+       }
+}
+
+bool
+wl_ext_event_complete(struct dhd_pub *dhd, int ifidx)
+{
+       struct net_device *dev = dhd_idx2net(dhd, ifidx);
+#ifdef WL_CFG80211
+       struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
+#endif /* WL_CFG80211 */
+#ifdef WL_ESCAN
+       struct wl_escan_info *escan = dhd->escan;
+#endif /* WL_ESCAN */
+       bool complete = TRUE;
+
+#ifdef WL_CFG80211
+       if (wl_get_drv_status_all(cfg, SCANNING)) {
+               AEXT_INFO(dev->name, "SCANNING\n");
+               complete = FALSE;
+       }
+       if (wl_get_drv_status_all(cfg, CONNECTING)) {
+               AEXT_INFO(dev->name, "CONNECTING\n");
+               complete = FALSE;
+       }
+       if (wl_get_drv_status_all(cfg, DISCONNECTING)) {
+               AEXT_INFO(dev->name, "DISCONNECTING\n");
+               complete = FALSE;
+       }
+#endif /* WL_CFG80211 */
+#ifdef WL_ESCAN
+       if (escan->escan_state == ESCAN_STATE_SCANING) {
+               AEXT_INFO(dev->name, "ESCAN_STATE_SCANING\n");
+               complete = FALSE;
+       }
+#endif /* WL_ESCAN */
+       if (dhd->conf->eapol_status >= EAPOL_STATUS_4WAY_START &&
+                       dhd->conf->eapol_status < EAPOL_STATUS_4WAY_DONE) {
+               AEXT_INFO(dev->name, "4-WAY handshaking\n");
+               complete = FALSE;
+       }
+
+       return complete;
+}
+#endif /* WL_CFG80211 && WL_ESCAN */
 
 static int
 wl_ext_get_ioctl_ver(struct net_device *dev, int *ioctl_ver)
@@ -508,6 +632,38 @@ wl_ext_get_ioctl_ver(struct net_device *dev, int *ioctl_ver)
        return ret;
 }
 
+void
+wl_ext_bss_iovar_war(struct net_device *ndev, s32 *val)
+{
+       dhd_pub_t *dhd = dhd_get_pub(ndev);
+       uint chip;
+       bool need_war = false;
+
+       chip = dhd_conf_get_chip(dhd);
+
+       if (chip == BCM43362_CHIP_ID || chip == BCM4330_CHIP_ID ||
+               chip == BCM43430_CHIP_ID || chip == BCM43012_CHIP_ID ||
+               chip == BCM4345_CHIP_ID || chip == BCM4356_CHIP_ID ||
+               chip == BCM4359_CHIP_ID) {
+               need_war = true;
+       }
+
+       if (need_war) {
+               /* Few firmware branches have issues in bss iovar handling and
+                * that can't be changed since they are in production.
+                */
+               if (*val == WLC_AP_IOV_OP_MANUAL_AP_BSSCFG_CREATE) {
+                       *val = WLC_AP_IOV_OP_MANUAL_STA_BSSCFG_CREATE;
+               } else if (*val == WLC_AP_IOV_OP_MANUAL_STA_BSSCFG_CREATE) {
+                       *val = WLC_AP_IOV_OP_MANUAL_AP_BSSCFG_CREATE;
+               } else {
+                       /* Ignore for other bss enums */
+                       return;
+               }
+               AEXT_TRACE(ndev->name, "wl bss %d\n", *val);
+       }
+}
+
 static int
 wl_ext_set_chanspec(struct net_device *dev, int ioctl_ver,
        uint16 channel, chanspec_t *ret_chspec)
@@ -564,7 +720,7 @@ set_channel:
                        if ((err = wl_ext_iovar_setint(dev, "chanspec", fw_chspec)) == BCME_BADCHAN) {
                                if (bw == WL_CHANSPEC_BW_80)
                                        goto change_bw;
-                               wl_ext_ioctl(dev, WLC_SET_CHANNEL, &_chan, sizeof(_chan), 1);
+                               err = wl_ext_ioctl(dev, WLC_SET_CHANNEL, &_chan, sizeof(_chan), 1);
                                WL_MSG(dev->name, "channel %d\n", _chan);
                        } else if (err) {
                                AEXT_ERROR(dev->name, "failed to set chanspec error %d\n", err);
@@ -762,6 +918,8 @@ wl_ext_connect(struct net_device *dev, struct wl_conn_info *conn_info)
        int ioctl_ver = 0;
        char sec[32];
 
+       wl_ext_get_ioctl_ver(dev, &ioctl_ver);
+
        if (dhd->conf->chip == BCM43362_CHIP_ID)
                goto set_ssid;
 
@@ -786,7 +944,6 @@ wl_ext_connect(struct net_device *dev, struct wl_conn_info *conn_info)
                err = -ENOMEM;
                goto exit;
        }
-       wl_ext_get_ioctl_ver(dev, &ioctl_ver);
        ext_join_params->ssid.SSID_len = min((uint32)sizeof(ext_join_params->ssid.SSID),
                conn_info->ssid.SSID_len);
        memcpy(&ext_join_params->ssid.SSID, conn_info->ssid.SSID, ext_join_params->ssid.SSID_len);
@@ -846,7 +1003,8 @@ set_ssid:
        memset(&join_params, 0, sizeof(join_params));
        join_params_size = sizeof(join_params.ssid);
 
-       join_params.ssid.SSID_len = min((uint32)sizeof(join_params.ssid.SSID), conn_info->ssid.SSID_len);
+       join_params.ssid.SSID_len = min((uint32)sizeof(join_params.ssid.SSID),
+               conn_info->ssid.SSID_len);
        memcpy(&join_params.ssid.SSID, conn_info->ssid.SSID, join_params.ssid.SSID_len);
        join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len);
        if (memcmp(&ether_null, &conn_info->bssid, ETHER_ADDR_LEN))
@@ -866,9 +1024,13 @@ set_ssid:
                "Connecting with %pM channel (%d) ssid \"%s\", len (%d), sec=%s\n\n",
                &join_params.params.bssid, conn_info->channel,
                join_params.ssid.SSID, join_params.ssid.SSID_len, sec);
-       err = wl_ext_ioctl(dev, WLC_SET_SSID, &join_params,join_params_size, 1);
+       err = wl_ext_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, 1);
 
 exit:
+#ifdef WL_EXT_IAPSTA
+       if (!err)
+               wl_ext_add_remove_pm_enable_work(dev, TRUE);
+#endif /* WL_EXT_IAPSTA */
        if (iovar_buf)
                kfree(iovar_buf);
        if (ext_join_params)
@@ -880,65 +1042,296 @@ exit:
 void
 wl_ext_get_sec(struct net_device *dev, int ifmode, char *sec, int total_len)
 {
-       int auth=-1, wpa_auth=-1, wsec=0;
+       int auth=0, wpa_auth=0, wsec=0, mfp=0;
        int bytes_written=0;
 
        memset(sec, 0, total_len);
        wl_ext_iovar_getint(dev, "auth", &auth);
        wl_ext_iovar_getint(dev, "wpa_auth", &wpa_auth);
        wl_ext_iovar_getint(dev, "wsec", &wsec);
+       wldev_iovar_getint(dev, "mfp", &mfp);
 
-#if defined(WL_EXT_IAPSTA) && defined(WLMESH)
+#ifdef WL_EXT_IAPSTA
        if (ifmode == IMESH_MODE) {
-               if (auth == 0 && wpa_auth == 0) {
+               if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA_AUTH_DISABLED) {
                        bytes_written += snprintf(sec+bytes_written, total_len, "open");
-               } else if (auth == 0 && wpa_auth == 128) {
+               } else if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA2_AUTH_PSK) {
                        bytes_written += snprintf(sec+bytes_written, total_len, "sae");
                } else {
-                       bytes_written += snprintf(sec+bytes_written, total_len, "unknown");
+                       bytes_written += snprintf(sec+bytes_written, total_len, "%d/0x%x",
+                               auth, wpa_auth);
                }
        } else
-#endif
-       if (auth == 0 && wpa_auth == 0) {
-               bytes_written += snprintf(sec+bytes_written, total_len, "open");
-       } else if (auth == 1 && wpa_auth == 0) {
-               bytes_written += snprintf(sec+bytes_written, total_len, "shared");
-       } else if (auth == 0 && wpa_auth == 4) {
-               bytes_written += snprintf(sec+bytes_written, total_len, "wpapsk");
-       } else if (auth == 0 && wpa_auth == 128) {
-               bytes_written += snprintf(sec+bytes_written, total_len, "wpa2psk");
-       } else if (auth == 0 && wpa_auth == 132) {
-               bytes_written += snprintf(sec+bytes_written, total_len, "wpawpa2psk");
+#endif /* WL_EXT_IAPSTA */
+       {
+               if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA_AUTH_DISABLED) {
+                       bytes_written += snprintf(sec+bytes_written, total_len, "open");
+               } else if (auth == WL_AUTH_SHARED_KEY && wpa_auth == WPA_AUTH_DISABLED) {
+                       bytes_written += snprintf(sec+bytes_written, total_len, "shared");
+               } else if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA_AUTH_PSK) {
+                       bytes_written += snprintf(sec+bytes_written, total_len, "wpapsk");
+               } else if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA2_AUTH_PSK) {
+                       bytes_written += snprintf(sec+bytes_written, total_len, "wpa2psk");
+               } else if (auth == WL_AUTH_OPEN_SHARED && wpa_auth == WPA3_AUTH_SAE_PSK) {
+                       bytes_written += snprintf(sec+bytes_written, total_len, "wpa3");
+               } else if ((auth == WL_AUTH_OPEN_SYSTEM || auth == WL_AUTH_SAE_KEY) &&
+                               wpa_auth == 0x20) {
+                       bytes_written += snprintf(sec+bytes_written, total_len, "wpa3");
+               } else {
+                       bytes_written += snprintf(sec+bytes_written, total_len, "%d/0x%x",
+                               auth, wpa_auth);
+               }
+       }
+
+       if (mfp == WL_MFP_NONE) {
+               bytes_written += snprintf(sec+bytes_written, total_len, "/mfpn");
+       } else if (mfp == WL_MFP_CAPABLE) {
+               bytes_written += snprintf(sec+bytes_written, total_len, "/mfpc");
+       } else if (mfp == WL_MFP_REQUIRED) {
+               bytes_written += snprintf(sec+bytes_written, total_len, "/mfpr");
        } else {
-               bytes_written += snprintf(sec+bytes_written, total_len, "(%d/%d)",
-                       auth, wpa_auth);
+               bytes_written += snprintf(sec+bytes_written, total_len, "/%d", mfp);
        }
 
-#if defined(WL_EXT_IAPSTA) && defined(WLMESH)
+#ifdef WL_EXT_IAPSTA
        if (ifmode == IMESH_MODE) {
-               if (wsec == 0) {
+               if (wsec == WSEC_NONE) {
                        bytes_written += snprintf(sec+bytes_written, total_len, "/none");
                } else {
-                       bytes_written += snprintf(sec+bytes_written, total_len, "/sae");
+                       bytes_written += snprintf(sec+bytes_written, total_len, "/aes");
                }
        } else
-#endif
-       if (wsec == 0) {
-               bytes_written += snprintf(sec+bytes_written, total_len, "/none");
-       } else if (wsec == 1) {
-               bytes_written += snprintf(sec+bytes_written, total_len, "/wep");
-       } else if (wsec == 2 || wsec == 10) {
-               bytes_written += snprintf(sec+bytes_written, total_len, "/tkip");
-       } else if (wsec == 4 || wsec == 12) {
-               bytes_written += snprintf(sec+bytes_written, total_len, "/aes");
-       } else if (wsec == 6 || wsec == 14) {
-               bytes_written += snprintf(sec+bytes_written, total_len, "/tkipaes");
-       } else {
-               bytes_written += snprintf(sec+bytes_written, total_len, "/%d", wsec);
+#endif /* WL_EXT_IAPSTA */
+       {
+               if (wsec == WSEC_NONE) {
+                       bytes_written += snprintf(sec+bytes_written, total_len, "/none");
+               } else if (wsec == WEP_ENABLED) {
+                       bytes_written += snprintf(sec+bytes_written, total_len, "/wep");
+               } else if (wsec == (TKIP_ENABLED|AES_ENABLED) ||
+                               wsec == (WSEC_SWFLAG|TKIP_ENABLED|AES_ENABLED)) {
+                       bytes_written += snprintf(sec+bytes_written, total_len, "/tkipaes");
+               } else if (wsec == TKIP_ENABLED || wsec == (WSEC_SWFLAG|TKIP_ENABLED)) {
+                       bytes_written += snprintf(sec+bytes_written, total_len, "/tkip");
+               } else if (wsec == AES_ENABLED || wsec == (WSEC_SWFLAG|AES_ENABLED)) {
+                       bytes_written += snprintf(sec+bytes_written, total_len, "/aes");
+               } else {
+                       bytes_written += snprintf(sec+bytes_written, total_len, "/0x%x", wsec);
+               }
+       }
+
+}
+
+static bool
+wl_ext_dfs_chan(uint16 chan)
+{
+       if (chan >= 52 && chan <= 144)
+               return TRUE;
+       return FALSE;
+}
+
+static uint16
+wl_ext_get_default_chan(struct net_device *dev,
+       uint16 *chan_2g, uint16 *chan_5g, bool nodfs)
+{
+       struct dhd_pub *dhd = dhd_get_pub(dev);
+       uint16 chan_tmp = 0, chan = 0;
+       wl_uint32_list_t *list;
+       u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
+       s32 ret = BCME_OK;
+       int i;
+
+       *chan_2g = 0;
+       *chan_5g = 0;
+       memset(valid_chan_list, 0, sizeof(valid_chan_list));
+       list = (wl_uint32_list_t *)(void *) valid_chan_list;
+       list->count = htod32(WL_NUMCHANNELS);
+       ret = wl_ext_ioctl(dev, WLC_GET_VALID_CHANNELS, valid_chan_list,
+               sizeof(valid_chan_list), 0);
+       if (ret == 0) {
+               for (i=0; i<dtoh32(list->count); i++) {
+                       chan_tmp = dtoh32(list->element[i]);
+                       if (!dhd_conf_match_channel(dhd, chan_tmp))
+                               continue;
+                       if (chan_tmp <= 13) {
+                               *chan_2g = chan_tmp;
+                       } else {
+                               if (wl_ext_dfs_chan(chan_tmp) && nodfs)
+                                       continue;
+                               else if (chan_tmp >= 36 && chan_tmp <= 161)
+                                       *chan_5g = chan_tmp;
+                       }
+               }
+       }
+
+       return chan;
+}
+
+#if defined(SENDPROB) || (defined(WLMESH) && defined(WL_ESCAN))
+static int
+wl_ext_add_del_ie(struct net_device *dev, uint pktflag, char *ie_data, const char* add_del_cmd)
+{
+       vndr_ie_setbuf_t *vndr_ie = NULL;
+       char iovar_buf[WLC_IOCTL_SMLEN]="\0";
+       int ie_data_len = 0, tot_len = 0, iecount;
+       int err = -1;
+
+       if (!strlen(ie_data)) {
+               AEXT_ERROR(dev->name, "wrong ie %s\n", ie_data);
+               goto exit;
+       }
+
+       tot_len = (int)(sizeof(vndr_ie_setbuf_t) + ((strlen(ie_data)-2)/2));
+       vndr_ie = (vndr_ie_setbuf_t *) kzalloc(tot_len, GFP_KERNEL);
+       if (!vndr_ie) {
+               AEXT_ERROR(dev->name, "IE memory alloc failed\n");
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       /* Copy the vndr_ie SET command ("add"/"del") to the buffer */
+       strncpy(vndr_ie->cmd, add_del_cmd, VNDR_IE_CMD_LEN - 1);
+       vndr_ie->cmd[VNDR_IE_CMD_LEN - 1] = '\0';
+
+       /* Set the IE count - the buffer contains only 1 IE */
+       iecount = htod32(1);
+       memcpy((void *)&vndr_ie->vndr_ie_buffer.iecount, &iecount, sizeof(s32));
+
+       /* Set packet flag to indicate that BEACON's will contain this IE */
+       pktflag = htod32(pktflag);
+       memcpy((void *)&vndr_ie->vndr_ie_buffer.vndr_ie_list[0].pktflag, &pktflag,
+               sizeof(u32));
+
+       /* Set the IE ID */
+       vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.id = (uchar)DOT11_MNG_VS_ID;
+
+       /* Set the IE LEN */
+       vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len = (strlen(ie_data)-2)/2;
+
+       /* Set the IE OUI and DATA */
+       ie_data_len = wl_pattern_atoh(ie_data,
+               (char *)vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui);
+       if (ie_data_len <= 0) {
+               AEXT_ERROR(dev->name, "wrong ie_data_len %d\n", (int)strlen(ie_data)-2);
+               goto exit;
+       }
+
+       err = wl_ext_iovar_setbuf(dev, "vndr_ie", vndr_ie, tot_len, iovar_buf,
+               sizeof(iovar_buf), NULL);
+
+exit:
+       if (vndr_ie) {
+               kfree(vndr_ie);
        }
+       return err;
 }
+#endif /* SENDPROB || (WLMESH && WL_ESCAN) */
 
 #ifdef WL_EXT_IAPSTA
+#define WL_PM_ENABLE_TIMEOUT 10000
+#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \
+       4 && __GNUC_MINOR__ >= 6))
+#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_CONTAINER_OF(entry, ptr, type, member) \
+entry = container_of((ptr), type, member);
+#endif /* STRICT_GCC_WARNINGS */
+
+static void
+wl_ext_pm_work_handler(struct work_struct *work)
+{
+       struct wl_if_info *cur_if;
+       s32 pm = PM_FAST;
+       dhd_pub_t *dhd;
+
+       BCM_SET_CONTAINER_OF(cur_if, work, struct wl_if_info, pm_enable_work.work);
+
+       WL_TRACE(("%s: Enter\n", __FUNCTION__));
+
+       if (cur_if->dev == NULL)
+               return;
+
+#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \
+       4 && __GNUC_MINOR__ >= 6))
+_Pragma("GCC diagnostic push")
+_Pragma("GCC diagnostic ignored \"-Wcast-qual\"")
+#endif
+
+       dhd = dhd_get_pub(cur_if->dev);
+
+       if (!dhd || !dhd->up) {
+               AEXT_TRACE(cur_if->ifname, "dhd is null or not up\n");
+               return;
+       }
+       if (dhd_conf_get_pm(dhd) >= 0)
+               pm = dhd_conf_get_pm(dhd);
+       wl_ext_ioctl(cur_if->dev, WLC_SET_PM, &pm, sizeof(pm), 1);
+#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \
+       4 && __GNUC_MINOR__ >= 6))
+_Pragma("GCC diagnostic pop")
+#endif
+       DHD_PM_WAKE_UNLOCK(dhd);
+
+}
+
+void
+wl_ext_add_remove_pm_enable_work(struct net_device *dev, bool add)
+{
+       dhd_pub_t *dhd = dhd_get_pub(dev);
+       struct wl_apsta_params *apsta_params = dhd->iapsta_params;
+       struct wl_if_info *cur_if = NULL, *tmp_if = NULL;
+       u16 wq_duration = 0;
+       s32 pm = PM_OFF;
+       int i;
+
+       for (i=0; i<MAX_IF_NUM; i++) {
+               tmp_if = &apsta_params->if_info[i];
+               if (tmp_if->dev && tmp_if->dev == dev) {
+                       cur_if = tmp_if;
+                       break;
+               }
+       }
+
+       if (!cur_if)
+               return;
+
+       mutex_lock(&cur_if->pm_sync);
+       /*
+        * Make cancel and schedule work part mutually exclusive
+        * so that while cancelling, we are sure that there is no
+        * work getting scheduled.
+        */
+
+       if (delayed_work_pending(&cur_if->pm_enable_work)) {
+               cancel_delayed_work_sync(&cur_if->pm_enable_work);
+               DHD_PM_WAKE_UNLOCK(dhd);
+       }
+
+       if (add) {
+               wq_duration = (WL_PM_ENABLE_TIMEOUT);
+       }
+
+       /* It should schedule work item only if driver is up */
+       if (dhd->up) {
+               if (dhd_conf_get_pm(dhd) >= 0)
+                       pm = dhd_conf_get_pm(dhd);
+               wl_ext_ioctl(cur_if->dev, WLC_SET_PM, &pm, sizeof(pm), 1);
+               if (wq_duration) {
+                       if (schedule_delayed_work(&cur_if->pm_enable_work,
+                                       msecs_to_jiffies((const unsigned int)wq_duration))) {
+                               DHD_PM_WAKE_LOCK_TIMEOUT(dhd, wq_duration);
+                       } else {
+                               AEXT_ERROR(cur_if->ifname, "Can't schedule pm work handler\n");
+                       }
+               }
+       }
+       mutex_unlock(&cur_if->pm_sync);
+
+}
+
 static int
 wl_ext_parse_wep(char *key, struct wl_wsec_key *wsec_key)
 {
@@ -1058,35 +1451,35 @@ wl_ext_set_amode(struct wl_if_info *cur_if)
 #ifdef WLMESH
        if (cur_if->ifmode == IMESH_MODE) {
                if (amode == AUTH_SAE) {
-                       auth = 0;
-                       wpa_auth = 128;
+                       auth = WL_AUTH_OPEN_SYSTEM;
+                       wpa_auth = WPA2_AUTH_PSK;
                        AEXT_INFO(dev->name, "SAE\n");
                } else {
-                       auth = 0;
-                       wpa_auth = 0;
+                       auth = WL_AUTH_OPEN_SYSTEM;
+                       wpa_auth = WPA_AUTH_DISABLED;
                        AEXT_INFO(dev->name, "Open System\n");
                }
        } else
-#endif
+#endif /* WLMESH */
        if (amode == AUTH_OPEN) {
-               auth = 0;
-               wpa_auth = 0;
+               auth = WL_AUTH_OPEN_SYSTEM;
+               wpa_auth = WPA_AUTH_DISABLED;
                AEXT_INFO(dev->name, "Open System\n");
        } else if (amode == AUTH_SHARED) {
-               auth = 1;
-               wpa_auth = 0;
+               auth = WL_AUTH_SHARED_KEY;
+               wpa_auth = WPA_AUTH_DISABLED;
                AEXT_INFO(dev->name, "Shared Key\n");
        } else if (amode == AUTH_WPAPSK) {
-               auth = 0;
-               wpa_auth = 4;
+               auth = WL_AUTH_OPEN_SYSTEM;
+               wpa_auth = WPA_AUTH_PSK;
                AEXT_INFO(dev->name, "WPA-PSK\n");
        } else if (amode == AUTH_WPA2PSK) {
-               auth = 0;
-               wpa_auth = 128;
+               auth = WL_AUTH_OPEN_SYSTEM;
+               wpa_auth = WPA2_AUTH_PSK;
                AEXT_INFO(dev->name, "WPA2-PSK\n");
        } else if (amode == AUTH_WPAWPA2PSK) {
-               auth = 0;
-               wpa_auth = 132;
+               auth = WL_AUTH_OPEN_SYSTEM;
+               wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK;
                AEXT_INFO(dev->name, "WPA/WPA2-PSK\n");
        }
 #ifdef WLMESH
@@ -1094,7 +1487,7 @@ wl_ext_set_amode(struct wl_if_info *cur_if)
                s32 val = WL_BSSTYPE_MESH;
                wl_ext_ioctl(dev, WLC_SET_INFRA, &val, sizeof(val), 1);
        } else
-#endif
+#endif /* WLMESH */
        if (cur_if->ifmode == ISTA_MODE) {
                s32 val = WL_BSSTYPE_INFRA;
                wl_ext_ioctl(dev, WLC_SET_INFRA, &val, sizeof(val), 1);
@@ -1125,33 +1518,33 @@ wl_ext_set_emode(struct wl_apsta_params *apsta_params,
 #ifdef WLMESH
        if (cur_if->ifmode == IMESH_MODE) {
                if (amode == AUTH_SAE) {
-                       wsec = 4;
+                       wsec = AES_ENABLED;
                } else {
-                       wsec = 0;
+                       wsec = WSEC_NONE;
                }
        } else
-#endif
+#endif /* WLMESH */
        if (emode == ENC_NONE) {
-               wsec = 0;
+               wsec = WSEC_NONE;
                AEXT_INFO(dev->name, "No securiy\n");
        } else if (emode == ENC_WEP) {
-               wsec = 1;
+               wsec = WEP_ENABLED;
                wl_ext_parse_wep(key, &wsec_key);
                AEXT_INFO(dev->name, "WEP key \"%s\"\n", wsec_key.data);
        } else if (emode == ENC_TKIP) {
-               wsec = 2;
+               wsec = TKIP_ENABLED;
                psk.key_len = strlen(key);
                psk.flags = WSEC_PASSPHRASE;
                memcpy(psk.key, key, strlen(key));
                AEXT_INFO(dev->name, "TKIP key \"%s\"\n", psk.key);
        } else if (emode == ENC_AES || amode == AUTH_SAE) {
-               wsec = 4;
+               wsec = AES_ENABLED;
                psk.key_len = strlen(key);
                psk.flags = WSEC_PASSPHRASE;
                memcpy(psk.key, key, strlen(key));
                AEXT_INFO(dev->name, "AES key \"%s\"\n", psk.key);
        } else if (emode == ENC_TKIPAES) {
-               wsec = 6;
+               wsec = TKIP_ENABLED | AES_ENABLED;
                psk.key_len = strlen(key);
                psk.flags = WSEC_PASSPHRASE;
                memcpy(psk.key, key, strlen(key));
@@ -1159,7 +1552,7 @@ wl_ext_set_emode(struct wl_apsta_params *apsta_params,
        }
        if (dhd->conf->chip == BCM43430_CHIP_ID && cur_if->ifidx > 0 && wsec >= 2 &&
                        apsta_params->apstamode == ISTAAP_MODE) {
-               wsec |= 0x8; // terence 20180628: fix me, this is a workaround
+               wsec |= WSEC_SWFLAG; // terence 20180628: fix me, this is a workaround
        }
 
        wl_ext_iovar_setint(dev, "wsec", wsec);
@@ -1168,7 +1561,7 @@ wl_ext_set_emode(struct wl_apsta_params *apsta_params,
        if (cur_if->ifmode == IMESH_MODE) {
                if (amode == AUTH_SAE) {
                        s8 iovar_buf[WLC_IOCTL_SMLEN];
-                       AEXT_INFO(dev->name, "SAE key \"%s\"\n", key);
+                       AEXT_INFO(dev->name, "AES key \"%s\"\n", key);
                        wl_ext_iovar_setint(dev, "mesh_auth_proto", 1);
                        wl_ext_iovar_setint(dev, "mfp", WL_MFP_REQUIRED);
                        wl_ext_iovar_setbuf(dev, "sae_password", key, strlen(key),
@@ -1179,7 +1572,7 @@ wl_ext_set_emode(struct wl_apsta_params *apsta_params,
                        wl_ext_iovar_setint(dev, "mfp", WL_MFP_NONE);
                }
        } else
-#endif
+#endif /* WLMESH */
        if (emode == ENC_WEP) {
                wl_ext_ioctl(dev, WLC_SET_KEY, &wsec_key, sizeof(wsec_key), 1);
        } else if (emode == ENC_TKIP || emode == ENC_AES || emode == ENC_TKIPAES) {
@@ -1303,103 +1696,270 @@ set_channel:
 }
 
 static bool
-wl_ext_dfs_chan(struct wl_apsta_params *apsta_params, struct net_device *dev,
-       uint16 chan)
+wl_ext_radar_detect(struct net_device *dev)
 {
-       u32 channel = chan;
-       s32 err = BCME_OK;
-       bool dfs = FALSE;
-
-       if (chan <= CH_MAX_2G_CHANNEL)
-               return FALSE;
+       int ret = BCME_OK;
+       bool radar = FALSE;
+       s32 val = 0;
 
-       channel |= WL_CHANSPEC_BAND_5G;
-       channel |= WL_CHANSPEC_BW_20;
-       channel = wl_ext_chspec_host_to_driver(apsta_params->ioctl_ver, channel);
-       err = wl_ext_iovar_getint(dev, "per_chan_info", &channel);
-       if (!err) {
-               if (channel & WL_CHAN_PASSIVE) {
-                       dfs = TRUE;
-               }
+       if ((ret = wldev_ioctl(dev, WLC_GET_RADAR, &val, sizeof(int), false) == 0)) {
+               radar = TRUE;
        }
 
-       return dfs;
-}
-
-static void
-wl_ext_wait_netif_change(struct wl_apsta_params *apsta_params,
-       struct wl_if_info *cur_if)
-{
-       rtnl_unlock();
-       wait_event_interruptible_timeout(apsta_params->netif_change_event,
-               wl_get_isam_status(cur_if, IF_READY),
-               msecs_to_jiffies(MAX_AP_LINK_WAIT_TIME));
-       rtnl_lock();
+       return radar;
 }
 
-bool
-wl_ext_check_mesh_creating(struct net_device *net)
+static struct wl_if_info *
+wl_ext_if_enabled(struct wl_apsta_params *apsta_params, ifmode_t ifmode)
 {
-       struct dhd_pub *dhd = dhd_get_pub(net);
-       struct wl_apsta_params *apsta_params = dhd->iapsta_params;
-       struct wl_if_info *cur_if;
+       struct wl_if_info *tmp_if, *target_if = NULL;
        int i;
 
-       if (apsta_params) {
-               for (i=0; i<MAX_IF_NUM; i++) {
-                       cur_if = &apsta_params->if_info[i];
-                       if (cur_if->ifmode==IMESH_MODE && wl_get_isam_status(cur_if, IF_ADDING))
-                               return TRUE;
+       for (i=0; i<MAX_IF_NUM; i++) {
+               tmp_if = &apsta_params->if_info[i];
+               if (tmp_if && tmp_if->ifmode == ifmode &&
+                               wl_get_isam_status(tmp_if, IF_READY)) {
+                       if (wl_ext_get_chan(apsta_params, tmp_if->dev)) {
+                               target_if = tmp_if;
+                               break;
+                       }
                }
        }
-       return FALSE;
+
+       return target_if;
 }
 
-bool
-wl_ext_check_other_enabling(struct wl_apsta_params *apsta_params,
-       struct wl_if_info *cur_if)
+#ifndef WL_STATIC_IF
+s32
+wl_ext_add_del_bss(struct net_device *ndev, s32 bsscfg_idx,
+       int iftype, s32 del, u8 *addr)
 {
-       struct wl_if_info *tmp_if;
-       bool enabling = FALSE;
-       u32 timeout = 1;
-       int i;
+       s32 ret = BCME_OK;
+       s32 val = 0;
+       u8 ioctl_buf[WLC_IOCTL_SMLEN];
+       struct {
+               s32 cfg;
+               s32 val;
+               struct ether_addr ea;
+       } bss_setbuf;
 
-       for (i=0; i<MAX_IF_NUM; i++) {
-               tmp_if = &apsta_params->if_info[i];
-               if (tmp_if->dev && tmp_if->dev != cur_if->dev) {
-                       if (tmp_if->ifmode == ISTA_MODE)
-                               enabling = wl_get_isam_status(tmp_if, STA_CONNECTING);
-                       else if (tmp_if->ifmode == IAP_MODE || tmp_if->ifmode == IMESH_MODE)
-                               enabling = wl_get_isam_status(tmp_if, AP_CREATING);
-                       if (enabling)
-                               WL_MSG(cur_if->ifname, "waiting for %s[%c] enabling...\n",
-                                       tmp_if->ifname, tmp_if->prefix);
-                       if (enabling && tmp_if->ifmode == ISTA_MODE) {
-                               timeout = wait_event_interruptible_timeout(
-                                       apsta_params->netif_change_event,
-                                       !wl_get_isam_status(tmp_if, STA_CONNECTING),
-                                       msecs_to_jiffies(MAX_STA_LINK_WAIT_TIME));
-                       } else if (enabling &&
-                                       (tmp_if->ifmode == IAP_MODE || tmp_if->ifmode == IMESH_MODE)) {
-                               timeout = wait_event_interruptible_timeout(
-                                       apsta_params->netif_change_event,
-                                       !wl_get_isam_status(tmp_if, AP_CREATING),
-                                       msecs_to_jiffies(MAX_STA_LINK_WAIT_TIME));
+       AEXT_TRACE(ndev->name, "wl_iftype:%d del:%d \n", iftype, del);
+
+       bzero(&bss_setbuf, sizeof(bss_setbuf));
+
+       /* AP=2, STA=3, up=1, down=0, val=-1 */
+       if (del) {
+               val = WLC_AP_IOV_OP_DELETE;
+       } else if (iftype == WL_INTERFACE_CREATE_AP) {
+               /* Add/role change to AP Interface */
+               AEXT_TRACE(ndev->name, "Adding AP Interface\n");
+               val = WLC_AP_IOV_OP_MANUAL_AP_BSSCFG_CREATE;
+       } else if (iftype == WL_INTERFACE_CREATE_STA) {
+               /* Add/role change to STA Interface */
+               AEXT_TRACE(ndev->name, "Adding STA Interface\n");
+               val = WLC_AP_IOV_OP_MANUAL_STA_BSSCFG_CREATE;
+       } else {
+               AEXT_ERROR(ndev->name, "add_del_bss NOT supported for IFACE type:0x%x", iftype);
+               return -EINVAL;
+       }
+
+       if (!del) {
+               wl_ext_bss_iovar_war(ndev, &val);
+       }
+
+       bss_setbuf.cfg = htod32(bsscfg_idx);
+       bss_setbuf.val = htod32(val);
+
+       if (addr) {
+               memcpy(&bss_setbuf.ea.octet, addr, ETH_ALEN);
+       }
+
+       AEXT_INFO(ndev->name, "wl bss %d bssidx:%d\n", val, bsscfg_idx);
+       ret = wl_ext_iovar_setbuf(ndev, "bss", &bss_setbuf, sizeof(bss_setbuf),
+               ioctl_buf, WLC_IOCTL_SMLEN, NULL);
+       if (ret != 0)
+               WL_ERR(("'bss %d' failed with %d\n", val, ret));
+
+       return ret;
+}
+
+static int
+wl_ext_interface_ops(struct net_device *dev,
+       struct wl_apsta_params *apsta_params, int iftype, u8 *addr)
+{
+       s32 ret;
+       struct wl_interface_create_v2 iface;
+       wl_interface_create_v3_t iface_v3;
+       struct wl_interface_info_v1 *info;
+       wl_interface_info_v2_t *info_v2;
+       uint32 ifflags = 0;
+       bool use_iface_info_v2 = false;
+       u8 ioctl_buf[WLC_IOCTL_SMLEN];
+       wl_wlc_version_t wlc_ver;
+
+       /* Interface create */
+       bzero(&iface, sizeof(iface));
+
+       if (addr) {
+               ifflags |= WL_INTERFACE_MAC_USE;
+       }
+
+       ret = wldev_iovar_getbuf(dev, "wlc_ver", NULL, 0,
+               &wlc_ver, sizeof(wl_wlc_version_t), NULL);
+       if ((ret == BCME_OK) && (wlc_ver.wlc_ver_major >= 5)) {
+               ret = wldev_iovar_getbuf(dev, "interface_create",
+                       &iface, sizeof(struct wl_interface_create_v2),
+                       ioctl_buf, sizeof(ioctl_buf), NULL);
+               if ((ret == BCME_OK) && (*((uint32 *)ioctl_buf) == WL_INTERFACE_CREATE_VER_3)) {
+                       use_iface_info_v2 = true;
+                       bzero(&iface_v3, sizeof(wl_interface_create_v3_t));
+                       iface_v3.ver = WL_INTERFACE_CREATE_VER_3;
+                       iface_v3.iftype = iftype;
+                       iface_v3.flags = ifflags;
+                       if (addr) {
+                               memcpy(&iface_v3.mac_addr.octet, addr, ETH_ALEN);
                        }
-                       if (tmp_if->ifmode == ISTA_MODE)
-                               enabling = wl_get_isam_status(tmp_if, STA_CONNECTING);
-                       else if (tmp_if->ifmode == IAP_MODE || tmp_if->ifmode == IMESH_MODE)
-                               enabling = wl_get_isam_status(tmp_if, AP_CREATING);
-                       if (timeout <= 0 || enabling) {
-                               WL_MSG(cur_if->ifname, "%s[%c] is still enabling...\n",
-                                       tmp_if->ifname, tmp_if->prefix);
+                       ret = wl_ext_iovar_getbuf(dev, "interface_create",
+                               &iface_v3, sizeof(wl_interface_create_v3_t),
+                               ioctl_buf, sizeof(ioctl_buf), NULL);
+                       if (unlikely(ret)) {
+                               WL_ERR(("Interface v3 create failed!! ret %d\n", ret));
+                               return ret;
                        }
                }
        }
 
-       return enabling;
+       /* success case */
+       if (use_iface_info_v2 == true) {
+               info_v2 = (wl_interface_info_v2_t *)ioctl_buf;
+               ret = info_v2->bsscfgidx;
+       } else {
+               /* Use v1 struct */
+               iface.ver = WL_INTERFACE_CREATE_VER_2;
+               iface.iftype = iftype;
+               iface.flags = ifflags;
+               if (addr) {
+                       memcpy(&iface.mac_addr.octet, addr, ETH_ALEN);
+               }
+               ret = wldev_iovar_getbuf(dev, "interface_create",
+                       &iface, sizeof(struct wl_interface_create_v2),
+                       ioctl_buf, sizeof(ioctl_buf), NULL);
+               if (ret == BCME_OK) {
+                       info = (struct wl_interface_info_v1 *)ioctl_buf;
+                       ret = info->bsscfgidx;
+               }
+       }
+
+       AEXT_INFO(dev->name, "wl interface create success!! bssidx:%d \n", ret);
+       return ret;
+}
+
+static void
+wl_ext_wait_netif_change(struct wl_apsta_params *apsta_params,
+       struct wl_if_info *cur_if)
+{
+       rtnl_unlock();
+       wait_event_interruptible_timeout(apsta_params->netif_change_event,
+               wl_get_isam_status(cur_if, IF_READY),
+               msecs_to_jiffies(MAX_AP_LINK_WAIT_TIME));
+       rtnl_lock();
+}
+
+static void
+wl_ext_interface_create(struct net_device *dev, struct wl_apsta_params *apsta_params,
+       struct wl_if_info *cur_if, int iftype, u8 *addr)
+{
+       s32 ret;
+
+       wl_set_isam_status(cur_if, IF_ADDING);
+       ret = wl_ext_interface_ops(dev, apsta_params, iftype, addr);
+       if (ret == BCME_UNSUPPORTED) {
+               wl_ext_add_del_bss(dev, 1, iftype, 0, addr);
+       }
+       wl_ext_wait_netif_change(apsta_params, cur_if);
 }
 
+static void
+wl_ext_iapsta_intf_add(struct net_device *dev, struct wl_apsta_params *apsta_params)
+{
+       struct dhd_pub *dhd;
+       apstamode_t apstamode = apsta_params->apstamode;
+       struct wl_if_info *cur_if;
+       s8 iovar_buf[WLC_IOCTL_SMLEN];
+       wl_p2p_if_t ifreq;
+       struct ether_addr mac_addr;
+
+       dhd = dhd_get_pub(dev);
+       bzero(&mac_addr, sizeof(mac_addr));
+
+       if (apstamode == ISTAAP_MODE) {
+               cur_if = &apsta_params->if_info[IF_VIF];
+               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
+       }
+       else if (apstamode == ISTAGO_MODE) {
+               bzero(&ifreq, sizeof(wl_p2p_if_t));
+               ifreq.type = htod32(WL_P2P_IF_GO);
+               cur_if = &apsta_params->if_info[IF_VIF];
+               wl_set_isam_status(cur_if, IF_ADDING);
+               wl_ext_iovar_setbuf(dev, "p2p_ifadd", &ifreq, sizeof(ifreq),
+                       iovar_buf, WLC_IOCTL_SMLEN, NULL);
+               wl_ext_wait_netif_change(apsta_params, cur_if);
+       }
+       else if (apstamode == ISTASTA_MODE) {
+               cur_if = &apsta_params->if_info[IF_VIF];
+               memcpy(&mac_addr, dev->dev_addr, ETHER_ADDR_LEN);
+               mac_addr.octet[0] |= 0x02;
+               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_STA,
+                       (u8*)&mac_addr);
+       }
+       else if (apstamode == IDUALAP_MODE) {
+               cur_if = &apsta_params->if_info[IF_VIF];
+               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
+       }
+       else if (apstamode == ISTAAPAP_MODE) {
+               u8 rand_bytes[2] = {0, };
+               get_random_bytes(&rand_bytes, sizeof(rand_bytes));
+               cur_if = &apsta_params->if_info[IF_VIF];
+               memcpy(&mac_addr, dev->dev_addr, ETHER_ADDR_LEN);
+               mac_addr.octet[0] |= 0x02;
+               mac_addr.octet[5] += 0x01;
+               memcpy(&mac_addr.octet[3], rand_bytes, sizeof(rand_bytes));
+               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP,
+                       (u8*)&mac_addr);
+               cur_if = &apsta_params->if_info[IF_VIF2];
+               memcpy(&mac_addr, dev->dev_addr, ETHER_ADDR_LEN);
+               mac_addr.octet[0] |= 0x02;
+               mac_addr.octet[5] += 0x02;
+               memcpy(&mac_addr.octet[3], rand_bytes, sizeof(rand_bytes));
+               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP,
+                       (u8*)&mac_addr);
+       }
+#ifdef WLMESH
+       else if (apstamode == ISTAMESH_MODE) {
+               cur_if = &apsta_params->if_info[IF_VIF];
+               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_STA, NULL);
+       }
+       else if (apstamode == IMESHAP_MODE) {
+               cur_if = &apsta_params->if_info[IF_VIF];
+               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
+       }
+       else if (apstamode == ISTAAPMESH_MODE) {
+               cur_if = &apsta_params->if_info[IF_VIF];
+               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
+               cur_if = &apsta_params->if_info[IF_VIF2];
+               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_STA, NULL);
+       }
+       else if (apstamode == IMESHAPAP_MODE) {
+               cur_if = &apsta_params->if_info[IF_VIF];
+               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
+               cur_if = &apsta_params->if_info[IF_VIF2];
+               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
+       }
+#endif /* WLMESH */
+
+}
+#endif /* WL_STATIC_IF */
+
 static void
 wl_ext_iapsta_preinit(struct net_device *dev, struct wl_apsta_params *apsta_params)
 {
@@ -1408,7 +1968,7 @@ wl_ext_iapsta_preinit(struct net_device *dev, struct wl_apsta_params *apsta_para
        struct wl_if_info *cur_if;
        s8 iovar_buf[WLC_IOCTL_SMLEN];
        s32 val = 0;
-       int i, dfs = 1, disable_5g_band = 0;
+       int i;
 
        dhd = dhd_get_pub(dev);
 
@@ -1428,7 +1988,6 @@ wl_ext_iapsta_preinit(struct net_device *dev, struct wl_apsta_params *apsta_para
                        cur_if->prio = PRIO_AP;
                        cur_if->prefix = 'A';
                        snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_ap");
-                       dfs = 0;
 #ifdef WLMESH
                } else if (cur_if->ifmode == IMESH_MODE) {
                        cur_if->channel = 1;
@@ -1436,37 +1995,25 @@ wl_ext_iapsta_preinit(struct net_device *dev, struct wl_apsta_params *apsta_para
                        cur_if->prio = PRIO_MESH;
                        cur_if->prefix = 'M';
                        snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_mesh");
-                       dfs = 0;
-#endif
-               }
-       }
-
-       if (!dfs && !apsta_params->vsdb) {
-               wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
-               wl_ext_iovar_getint(dev, "disable_5g_band", &disable_5g_band);
-               disable_5g_band |= 0x6;
-               wl_ext_iovar_setint(dev, "disable_5g_band", disable_5g_band);
-               wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
-#ifdef WL_CFG80211
-               if (dhd->up) {
-                       struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
-                       wl_update_wiphybands(cfg, true);
+#ifdef WL_ESCAN
+                       if (i == 0 && apsta_params->macs)
+                               wl_mesh_escan_attach(dhd, cur_if);
+#endif /* WL_ESCAN */
+#endif /* WLMESH */
                }
-#endif
        }
 
        if (FW_SUPPORTED(dhd, rsdb)) {
                if (apstamode == IDUALAP_MODE)
-                       apsta_params->rsdb = TRUE;
+                       apsta_params->rsdb = -1;
                else if (apstamode == ISTAAPAP_MODE)
-                       apsta_params->rsdb = FALSE;
-               if (apstamode == ISTAAP_MODE || apstamode == ISTAAPAP_MODE ||
+                       apsta_params->rsdb = 0;
+               if (apstamode == ISTAAPAP_MODE || apstamode == IDUALAP_MODE ||
                                apstamode == IMESHONLY_MODE || apstamode == ISTAMESH_MODE ||
                                apstamode == IMESHAP_MODE || apstamode == ISTAAPMESH_MODE ||
                                apstamode == IMESHAPAP_MODE) {
                        wl_config_t rsdb_mode_cfg = {0, 0};
-                       if (apsta_params->rsdb)
-                               rsdb_mode_cfg.config = 1;
+                       rsdb_mode_cfg.config = apsta_params->rsdb;
                        AEXT_INFO(dev->name, "set rsdb_mode %d\n", rsdb_mode_cfg.config);
                        wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
                        wl_ext_iovar_setbuf(dev, "rsdb_mode", &rsdb_mode_cfg,
@@ -1474,7 +2021,7 @@ wl_ext_iapsta_preinit(struct net_device *dev, struct wl_apsta_params *apsta_para
                        wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
                }
        } else {
-               apsta_params->rsdb = FALSE;
+               apsta_params->rsdb = 0;
        }
 
        if (apstamode == ISTAONLY_MODE) {
@@ -1503,7 +2050,7 @@ wl_ext_iapsta_preinit(struct net_device *dev, struct wl_apsta_params *apsta_para
                                wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
                        }
                }
-#endif
+#endif /* BCMSDIO */
 #endif /* PROP_TXSTATUS_VSDB */
        }
        else if (apstamode == ISTAAP_MODE) {
@@ -1557,122 +2104,45 @@ wl_ext_iapsta_preinit(struct net_device *dev, struct wl_apsta_params *apsta_para
                wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
                // don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off
        }
-#endif
+#endif /* WLMESH */
 
        wl_ext_get_ioctl_ver(dev, &apsta_params->ioctl_ver);
        apsta_params->init = TRUE;
 
-       WL_MSG(dev->name, "apstamode=%d\n", apstamode);
-}
-
-static void
-wl_ext_interface_create(struct net_device *dev, struct wl_apsta_params *apsta_params,
-       struct wl_if_info *cur_if, int iftype, u8 *addr)
-{
-       wl_interface_create_t iface;
-       u8 iovar_buf[WLC_IOCTL_SMLEN];
-
-       bzero(&iface, sizeof(iface));
-       if (addr) {
-               iftype |= WL_INTERFACE_MAC_USE;
-       }
-       iface.ver = WL_INTERFACE_CREATE_VER;
-       iface.flags = iftype;
-       if (addr) {
-               memcpy(&iface.mac_addr.octet, addr, ETH_ALEN);
-       }
-       wl_set_isam_status(cur_if, IF_ADDING);
-       wl_ext_iovar_getbuf(dev, "interface_create", &iface, sizeof(iface),
-               iovar_buf, WLC_IOCTL_SMLEN, NULL);
-       wl_ext_wait_netif_change(apsta_params, cur_if);
-}
-
-static void
-wl_ext_iapsta_intf_add(struct net_device *dev, struct wl_apsta_params *apsta_params)
-{
-       struct dhd_pub *dhd;
-       apstamode_t apstamode = apsta_params->apstamode;
-       struct wl_if_info *cur_if;
-       wlc_ssid_t ssid = { 0, {0} };
-       s8 iovar_buf[WLC_IOCTL_SMLEN];
-       wl_p2p_if_t ifreq;
-       struct ether_addr mac_addr;
-
-       dhd = dhd_get_pub(dev);
-       bzero(&mac_addr, sizeof(mac_addr));
-
-       if (apstamode == ISTAAP_MODE) {
-               cur_if = &apsta_params->if_info[IF_VIF];
-               if (FW_SUPPORTED(dhd, rsdb)) {
-                       wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
-               } else {
-                       wl_set_isam_status(cur_if, IF_ADDING);
-                       wl_ext_iovar_setbuf_bsscfg(dev, "ssid", &ssid, sizeof(ssid),
-                               iovar_buf, WLC_IOCTL_SMLEN, 1, NULL);
-                       wl_ext_wait_netif_change(apsta_params, cur_if);
-               }
-       }
-       else if (apstamode == ISTAGO_MODE) {
-               bzero(&ifreq, sizeof(wl_p2p_if_t));
-               ifreq.type = htod32(WL_P2P_IF_GO);
-               cur_if = &apsta_params->if_info[IF_VIF];
-               wl_set_isam_status(cur_if, IF_ADDING);
-               wl_ext_iovar_setbuf(dev, "p2p_ifadd", &ifreq, sizeof(ifreq),
-                       iovar_buf, WLC_IOCTL_SMLEN, NULL);
-               wl_ext_wait_netif_change(apsta_params, cur_if);
-       }
-       else if (apstamode == ISTASTA_MODE) {
-               cur_if = &apsta_params->if_info[IF_VIF];
-               memcpy(&mac_addr, dev->dev_addr, ETHER_ADDR_LEN);
-               mac_addr.octet[0] |= 0x02;
-               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_STA,
-                       (u8*)&mac_addr);
-       }
-       else if (apstamode == IDUALAP_MODE) {
-               cur_if = &apsta_params->if_info[IF_VIF];
-               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
-       }
-       else if (apstamode == ISTAAPAP_MODE) {
-               u8 rand_bytes[2] = {0, };
-               get_random_bytes(&rand_bytes, sizeof(rand_bytes));
-               cur_if = &apsta_params->if_info[IF_VIF];
-               memcpy(&mac_addr, dev->dev_addr, ETHER_ADDR_LEN);
-               mac_addr.octet[0] |= 0x02;
-               mac_addr.octet[5] += 0x01;
-               memcpy(&mac_addr.octet[3], rand_bytes, sizeof(rand_bytes));
-               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP,
-                       (u8*)&mac_addr);
-               cur_if = &apsta_params->if_info[IF_VIF2];
-               memcpy(&mac_addr, dev->dev_addr, ETHER_ADDR_LEN);
-               mac_addr.octet[0] |= 0x02;
-               mac_addr.octet[5] += 0x02;
-               memcpy(&mac_addr.octet[3], rand_bytes, sizeof(rand_bytes));
-               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP,
-                       (u8*)&mac_addr);
-       }
-#ifdef WLMESH
-       else if (apstamode == ISTAMESH_MODE) {
-               cur_if = &apsta_params->if_info[IF_VIF];
-               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_STA, NULL);
-       }
-       else if (apstamode == IMESHAP_MODE) {
-               cur_if = &apsta_params->if_info[IF_VIF];
-               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
-       }
-       else if (apstamode == ISTAAPMESH_MODE) {
-               cur_if = &apsta_params->if_info[IF_VIF];
-               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
-               cur_if = &apsta_params->if_info[IF_VIF2];
-               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_STA, NULL);
-       }
-       else if (apstamode == IMESHAPAP_MODE) {
-               cur_if = &apsta_params->if_info[IF_VIF];
-               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
-               cur_if = &apsta_params->if_info[IF_VIF2];
-               wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
+       WL_MSG(dev->name, "apstamode=%d\n", apstamode);
+}
+
+static int
+wl_ext_isam_param(struct net_device *dev, char *command, int total_len)
+{
+       struct dhd_pub *dhd = dhd_get_pub(dev);
+       struct wl_apsta_params *apsta_params = dhd->iapsta_params;
+       int ret = -1;
+       char *pick_tmp, *data, *param;
+       int bytes_written=-1;
+
+       AEXT_TRACE(dev->name, "command=%s, len=%d\n", command, total_len);
+
+       pick_tmp = command;
+       param = bcmstrtok(&pick_tmp, " ", 0); // pick isam_param
+       param = bcmstrtok(&pick_tmp, " ", 0); // pick cmd
+       while (param != NULL) {
+               data = bcmstrtok(&pick_tmp, " ", 0); // pick data
+               if (!strcmp(param, "acs")) {
+                       if (data) {
+                               apsta_params->acs = simple_strtol(data, NULL, 0);
+                               ret = 0;
+                       } else {
+                               bytes_written = snprintf(command, total_len, "%d", apsta_params->acs);
+                               ret = bytes_written;
+                               goto exit;
+                       }
+               }
+               param = bcmstrtok(&pick_tmp, " ", 0); // pick cmd
        }
-#endif
 
+exit:
+       return ret;
 }
 
 static int
@@ -1682,11 +2152,6 @@ wl_ext_isam_init(struct net_device *dev, char *command, int total_len)
        char *pch, *pick_tmp, *pick_tmp2, *param;
        struct wl_apsta_params *apsta_params = dhd->iapsta_params;
        int i;
-       
-#ifdef WL_STATIC_IF
-       AEXT_ERROR(dev->name, "please remove WL_STATIC_IF from Makefile\n");
-       return -1;
-#endif
 
        if (apsta_params->init) {
                AEXT_ERROR(dev->name, "don't init twice\n");
@@ -1698,120 +2163,104 @@ wl_ext_isam_init(struct net_device *dev, char *command, int total_len)
        param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_init
        param = bcmstrtok(&pick_tmp, " ", 0);
        while (param != NULL) {
+               pick_tmp2 = bcmstrtok(&pick_tmp, " ", 0);
+               if (!pick_tmp2) {
+                       AEXT_ERROR(dev->name, "wrong param %s\n", param);
+                       return -1;
+               }
                if (!strcmp(param, "mode")) {
                        pch = NULL;
-                       pick_tmp2 = bcmstrtok(&pick_tmp, " ", 0);
-                       if (pick_tmp2) {
-                               if (!strcmp(pick_tmp2, "sta")) {
-                                       apsta_params->apstamode = ISTAONLY_MODE;
-                               } else if (!strcmp(pick_tmp2, "ap")) {
-                                       apsta_params->apstamode = IAPONLY_MODE;
-                               } else if (!strcmp(pick_tmp2, "sta-ap")) {
-                                       apsta_params->apstamode = ISTAAP_MODE;
-                               } else if (!strcmp(pick_tmp2, "sta-sta")) {
-                                       apsta_params->apstamode = ISTASTA_MODE;
-                                       apsta_params->vsdb = TRUE;
-                               } else if (!strcmp(pick_tmp2, "ap-ap")) {
-                                       apsta_params->apstamode = IDUALAP_MODE;
-                               } else if (!strcmp(pick_tmp2, "sta-ap-ap")) {
-                                       apsta_params->apstamode = ISTAAPAP_MODE;
-                               } else if (!strcmp(pick_tmp2, "apsta")) {
-                                       apsta_params->apstamode = ISTAAP_MODE;
-                                       apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE;
-                                       apsta_params->if_info[IF_VIF].ifmode = IAP_MODE;
-                               } else if (!strcmp(pick_tmp2, "dualap")) {
-                                       apsta_params->apstamode = IDUALAP_MODE;
-                                       apsta_params->if_info[IF_PIF].ifmode = IAP_MODE;
-                                       apsta_params->if_info[IF_VIF].ifmode = IAP_MODE;
-                               } else if (!strcmp(pick_tmp2, "sta-go") ||
-                                               !strcmp(pick_tmp2, "gosta")) {
-                                       if (!FW_SUPPORTED(dhd, p2p)) {
-                                               return -1;
-                                       }
-                                       apsta_params->apstamode = ISTAGO_MODE;
-                                       apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE;
-                                       apsta_params->if_info[IF_VIF].ifmode = IAP_MODE;
-#ifdef WLMESH
-                               } else if (!strcmp(pick_tmp2, "mesh")) {
-                                       apsta_params->apstamode = IMESHONLY_MODE;
-                               } else if (!strcmp(pick_tmp2, "sta-mesh")) {
-                                       apsta_params->apstamode = ISTAMESH_MODE;
-                               } else if (!strcmp(pick_tmp2, "sta-ap-mesh")) {
-                                       apsta_params->apstamode = ISTAAPMESH_MODE;
-                               } else if (!strcmp(pick_tmp2, "mesh-ap")) {
-                                       apsta_params->apstamode = IMESHAP_MODE;
-                               } else if (!strcmp(pick_tmp2, "mesh-ap-ap")) {
-                                       apsta_params->apstamode = IMESHAPAP_MODE;
-#endif
-                               } else {
-                                       AEXT_ERROR(dev->name, "mode [sta|ap|sta-ap|ap-ap]\n");
+                       if (!strcmp(pick_tmp2, "sta")) {
+                               apsta_params->apstamode = ISTAONLY_MODE;
+                       } else if (!strcmp(pick_tmp2, "ap")) {
+                               apsta_params->apstamode = IAPONLY_MODE;
+                       } else if (!strcmp(pick_tmp2, "sta-ap")) {
+                               apsta_params->apstamode = ISTAAP_MODE;
+                       } else if (!strcmp(pick_tmp2, "sta-sta")) {
+                               apsta_params->apstamode = ISTASTA_MODE;
+                               apsta_params->vsdb = TRUE;
+                       } else if (!strcmp(pick_tmp2, "ap-ap")) {
+                               apsta_params->apstamode = IDUALAP_MODE;
+                       } else if (!strcmp(pick_tmp2, "sta-ap-ap")) {
+                               apsta_params->apstamode = ISTAAPAP_MODE;
+                       } else if (!strcmp(pick_tmp2, "apsta")) {
+                               apsta_params->apstamode = ISTAAP_MODE;
+                               apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE;
+                               apsta_params->if_info[IF_VIF].ifmode = IAP_MODE;
+                       } else if (!strcmp(pick_tmp2, "dualap")) {
+                               apsta_params->apstamode = IDUALAP_MODE;
+                               apsta_params->if_info[IF_PIF].ifmode = IAP_MODE;
+                               apsta_params->if_info[IF_VIF].ifmode = IAP_MODE;
+                       } else if (!strcmp(pick_tmp2, "sta-go") ||
+                                       !strcmp(pick_tmp2, "gosta")) {
+                               if (!FW_SUPPORTED(dhd, p2p)) {
                                        return -1;
                                }
-                               pch = bcmstrtok(&pick_tmp2, " -", 0);
-                               for (i=0; i<MAX_IF_NUM && pch; i++) {
-                                       if (!strcmp(pch, "sta"))
-                                               apsta_params->if_info[i].ifmode = ISTA_MODE;
-                                       else if (!strcmp(pch, "ap"))
-                                               apsta_params->if_info[i].ifmode = IAP_MODE;
+                               apsta_params->apstamode = ISTAGO_MODE;
+                               apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE;
+                               apsta_params->if_info[IF_VIF].ifmode = IAP_MODE;
 #ifdef WLMESH
-                                       else if (!strcmp(pch, "mesh")) {
-                                               if (dhd->conf->fw_type != FW_TYPE_MESH) {
-                                                       AEXT_ERROR(dev->name, "wrong fw type\n");
-                                                       return -1;
-                                               }
-                                               apsta_params->if_info[i].ifmode = IMESH_MODE;
+                       } else if (!strcmp(pick_tmp2, "mesh")) {
+                               apsta_params->apstamode = IMESHONLY_MODE;
+                       } else if (!strcmp(pick_tmp2, "sta-mesh")) {
+                               apsta_params->apstamode = ISTAMESH_MODE;
+                       } else if (!strcmp(pick_tmp2, "sta-ap-mesh")) {
+                               apsta_params->apstamode = ISTAAPMESH_MODE;
+                       } else if (!strcmp(pick_tmp2, "mesh-ap")) {
+                               apsta_params->apstamode = IMESHAP_MODE;
+                       } else if (!strcmp(pick_tmp2, "mesh-ap-ap")) {
+                               apsta_params->apstamode = IMESHAPAP_MODE;
+#endif /* WLMESH */
+                       } else {
+                               AEXT_ERROR(dev->name, "mode [sta|ap|sta-ap|ap-ap]\n");
+                               return -1;
+                       }
+                       pch = bcmstrtok(&pick_tmp2, " -", 0);
+                       for (i=0; i<MAX_IF_NUM && pch; i++) {
+                               if (!strcmp(pch, "sta"))
+                                       apsta_params->if_info[i].ifmode = ISTA_MODE;
+                               else if (!strcmp(pch, "ap"))
+                                       apsta_params->if_info[i].ifmode = IAP_MODE;
+#ifdef WLMESH
+                               else if (!strcmp(pch, "mesh")) {
+                                       if (dhd->conf->fw_type != FW_TYPE_MESH) {
+                                               AEXT_ERROR(dev->name, "wrong fw type\n");
+                                               return -1;
                                        }
-#endif
-                                       pch = bcmstrtok(&pick_tmp2, " -", 0);
+                                       apsta_params->if_info[i].ifmode = IMESH_MODE;
                                }
+#endif /* WLMESH */
+                               pch = bcmstrtok(&pick_tmp2, " -", 0);
                        }
                }
                else if (!strcmp(param, "rsdb")) {
-                       pch = bcmstrtok(&pick_tmp, " ", 0);
-                       if (pch) {
-                               if (!strcmp(pch, "y")) {
-                                       apsta_params->rsdb = TRUE;
-                               } else if (!strcmp(pch, "n")) {
-                                       apsta_params->rsdb = FALSE;
-                               } else {
-                                       AEXT_ERROR(dev->name, "rsdb [y|n]\n");
-                                       return -1;
-                               }
-                       }
+                       apsta_params->rsdb = (int)simple_strtol(pick_tmp2, NULL, 0);
                } else if (!strcmp(param, "vsdb")) {
-                       pch = bcmstrtok(&pick_tmp, " ", 0);
-                       if (pch) {
-                               if (!strcmp(pch, "y")) {
-                                       apsta_params->vsdb = TRUE;
-                               } else if (!strcmp(pch, "n")) {
-                                       apsta_params->vsdb = FALSE;
-                               } else {
-                                       AEXT_ERROR(dev->name, "vsdb [y|n]\n");
-                                       return -1;
-                               }
+                       if (!strcmp(pick_tmp2, "y")) {
+                               apsta_params->vsdb = TRUE;
+                       } else if (!strcmp(pick_tmp2, "n")) {
+                               apsta_params->vsdb = FALSE;
+                       } else {
+                               AEXT_ERROR(dev->name, "vsdb [y|n]\n");
+                               return -1;
                        }
                } else if (!strcmp(param, "csa")) {
-                       pch = bcmstrtok(&pick_tmp, " ", 0);
-                       if (pch) {
-                               apsta_params->csa = (int)simple_strtol(pch, NULL, 0);
-                       }
+                       apsta_params->csa = (int)simple_strtol(pick_tmp2, NULL, 0);
+               } else if (!strcmp(param, "acs")) {
+                       apsta_params->acs = (int)simple_strtol(pick_tmp2, NULL, 0);
+#if defined(WLMESH) && defined(WL_ESCAN)
+               } else if (!strcmp(param, "macs")) {
+                       apsta_params->macs = (int)simple_strtol(pick_tmp2, NULL, 0);
+#endif /* WLMESH && WL_ESCAN */
                } else if (!strcmp(param, "ifname")) {
                        pch = NULL;
-                       pick_tmp2 = bcmstrtok(&pick_tmp, " ", 0);
-                       if (pick_tmp2)
-                               pch = bcmstrtok(&pick_tmp2, " -", 0);
+                       pch = bcmstrtok(&pick_tmp2, " -", 0);
                        for (i=0; i<MAX_IF_NUM && pch; i++) {
                                strcpy(apsta_params->if_info[i].ifname, pch);
                                pch = bcmstrtok(&pick_tmp2, " -", 0);
                        }
                } else if (!strcmp(param, "vifname")) {
-                       pch = bcmstrtok(&pick_tmp, " ", 0);
-                       if (pch)
-                               strcpy(apsta_params->if_info[IF_VIF].ifname, pch);
-                       else {
-                               AEXT_ERROR(dev->name, "vifname [wlan1]\n");
-                               return -1;
-                       }
+                       strcpy(apsta_params->if_info[IF_VIF].ifname, pick_tmp2);
                }
                param = bcmstrtok(&pick_tmp, " ", 0);
        }
@@ -1822,7 +2271,9 @@ wl_ext_isam_init(struct net_device *dev, char *command, int total_len)
        }
 
        wl_ext_iapsta_preinit(dev, apsta_params);
+#ifndef WL_STATIC_IF
        wl_ext_iapsta_intf_add(dev, apsta_params);
+#endif /* WL_STATIC_IF */
 
        return 0;
 }
@@ -2159,54 +2610,540 @@ wl_mesh_get_peer_results(struct net_device *dev, char *buf, int len)
                ret = peer_results->count;
        }
 
-       return ret;
+       return ret;
+}
+
+static int
+wl_ext_mesh_peer_status(struct net_device *dev, char *data, char *command,
+       int total_len)
+{
+       struct dhd_pub *dhd = dhd_get_pub(dev);
+       struct wl_apsta_params *apsta_params = dhd->iapsta_params;
+       int i;
+       struct wl_if_info *cur_if;
+       mesh_peer_info_dump_t *peer_results;
+       mesh_peer_info_ext_t *mpi_ext;
+       char *peer_buf = NULL;
+       int peer_len = WLC_IOCTL_MAXLEN;
+       int dump_written = 0, ret;
+
+       if (!data) {
+               peer_buf = kmalloc(peer_len, GFP_KERNEL);
+               if (peer_buf == NULL) {
+                       AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n",
+                               peer_len); 
+                       return -1;
+               }
+               for (i=0; i<MAX_IF_NUM; i++) {
+                       cur_if = &apsta_params->if_info[i];
+                       if (cur_if && dev == cur_if->dev && cur_if->ifmode == IMESH_MODE) {
+                               memset(peer_buf, 0, peer_len);
+                               ret = wl_mesh_get_peer_results(dev, peer_buf, peer_len);
+                               if (ret >= 0) {
+                                       peer_results = (mesh_peer_info_dump_t *)peer_buf;
+                                       mpi_ext = (mesh_peer_info_ext_t *)peer_results->mpi_ext;
+                                       dump_written += wl_mesh_print_peer_info(mpi_ext,
+                                               peer_results->count, command+dump_written,
+                                               total_len-dump_written);
+                               }
+                       } else if (cur_if && dev == cur_if->dev) {
+                               AEXT_ERROR(dev->name, "[%s][%c] is not mesh interface\n",
+                                       cur_if->ifname, cur_if->prefix);
+                       }
+               }
+       }
+
+       if (peer_buf)
+               kfree(peer_buf);
+       return dump_written;
+}
+
+#ifdef WL_ESCAN
+#define WL_MESH_DELAY_SCAN_MS  3000
+static void
+wl_mesh_timer(unsigned long data)
+{
+       wl_event_msg_t msg;
+       struct wl_if_info *mesh_if = (struct wl_if_info *)data;
+       struct dhd_pub *dhd;
+
+       if (!mesh_if) {
+               AEXT_ERROR("wlan", "mesh_if is not ready\n");
+               return;
+       }
+
+       if (!mesh_if->dev) {
+               AEXT_ERROR("wlan", "ifidx %d is not ready\n", mesh_if->ifidx);
+               return;
+       }
+       dhd = dhd_get_pub(mesh_if->dev);
+
+       bzero(&msg, sizeof(wl_event_msg_t));
+       AEXT_TRACE(mesh_if->dev->name, "timer expired\n");
+
+       msg.ifidx = mesh_if->ifidx;
+       msg.event_type = hton32(WLC_E_RESERVED);
+       msg.reason = 0xFFFFFFFF;
+       wl_ext_event_send(dhd->event_params, &msg, NULL);
+}
+
+static void
+wl_mesh_set_timer(struct wl_if_info *mesh_if, uint timeout)
+{
+       AEXT_TRACE(mesh_if->dev->name, "timeout=%d\n", timeout);
+
+       if (timer_pending(&mesh_if->delay_scan))
+               del_timer_sync(&mesh_if->delay_scan);
+
+       if (timeout) {
+               if (timer_pending(&mesh_if->delay_scan))
+                       del_timer_sync(&mesh_if->delay_scan);
+               mod_timer(&mesh_if->delay_scan, jiffies + msecs_to_jiffies(timeout));
+       }
+}
+
+static int
+wl_mesh_clear_vndr_ie(struct net_device *dev, uchar *oui)
+{
+       char *vndr_ie_buf = NULL;
+       vndr_ie_setbuf_t *vndr_ie = NULL;
+       ie_getbuf_t vndr_ie_tmp;
+       char *iovar_buf = NULL;
+       int err = -1, i;
+       vndr_ie_buf_t *vndr_ie_dump = NULL;
+       uchar *iebuf;
+       vndr_ie_info_t *ie_info;
+       vndr_ie_t *ie;
+
+       vndr_ie_buf = kzalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
+       if (!vndr_ie_buf) {
+               AEXT_ERROR(dev->name, "IE memory alloc failed\n");
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       iovar_buf = kzalloc(WLC_IOCTL_MEDLEN, GFP_KERNEL);
+       if (!iovar_buf) {
+               AEXT_ERROR(dev->name, "iovar_buf alloc failed\n");
+               err = -ENOMEM;
+               goto exit;
+       }
+
+       memset(iovar_buf, 0, WLC_IOCTL_MEDLEN);
+       vndr_ie_tmp.pktflag = (uint32) -1;
+       vndr_ie_tmp.id = (uint8) DOT11_MNG_PROPR_ID;
+       err = wl_ext_iovar_getbuf(dev, "vndr_ie", &vndr_ie_tmp, sizeof(vndr_ie_tmp),
+               iovar_buf, WLC_IOCTL_MEDLEN, NULL);
+       if (err)
+               goto exit;
+
+       vndr_ie_dump = (vndr_ie_buf_t *)iovar_buf;
+       if (!vndr_ie_dump->iecount)
+               goto exit;
+
+       iebuf = (uchar *)&vndr_ie_dump->vndr_ie_list[0];
+       for (i=0; i<vndr_ie_dump->iecount; i++) {
+               ie_info = (vndr_ie_info_t *) iebuf;
+               ie = &ie_info->vndr_ie_data;
+               if (memcmp(ie->oui, oui, 3))
+                       memset(ie->oui, 0, 3);
+               iebuf += sizeof(uint32) + ie->len + VNDR_IE_HDR_LEN;
+       }
+
+       vndr_ie = (vndr_ie_setbuf_t *) vndr_ie_buf;
+       strncpy(vndr_ie->cmd, "del", VNDR_IE_CMD_LEN - 1);
+       vndr_ie->cmd[VNDR_IE_CMD_LEN - 1] = '\0';
+       memcpy(&vndr_ie->vndr_ie_buffer, vndr_ie_dump, WLC_IOCTL_SMLEN-VNDR_IE_CMD_LEN-1);
+
+       memset(iovar_buf, 0, WLC_IOCTL_MEDLEN);
+       err = wl_ext_iovar_setbuf(dev, "vndr_ie", vndr_ie, WLC_IOCTL_SMLEN, iovar_buf,
+               WLC_IOCTL_MEDLEN, NULL);
+
+exit:
+       if (vndr_ie) {
+               kfree(vndr_ie);
+       }
+       if (iovar_buf) {
+               kfree(iovar_buf);
+       }
+       return err;
+}
+
+static int
+wl_mesh_clear_mesh_info(struct wl_apsta_params *apsta_params,
+       struct wl_if_info *mesh_if, bool scan)
+{
+       struct wl_mesh_params *mesh_info = &apsta_params->mesh_info;
+       uchar mesh_oui[]={0x00, 0x22, 0xf4};
+       int ret;
+
+       AEXT_TRACE(mesh_if->dev->name, "Enter\n");
+
+       ret = wl_mesh_clear_vndr_ie(mesh_if->dev, mesh_oui);
+       memset(mesh_info, 0, sizeof(struct wl_mesh_params));
+       if (scan) {
+               mesh_info->scan_channel = wl_ext_get_chan(apsta_params, mesh_if->dev);
+               wl_mesh_set_timer(mesh_if, 100);
+       }
+
+       return ret;
+}
+
+static int
+wl_mesh_update_vndr_ie(struct wl_apsta_params *apsta_params,
+       struct wl_if_info *mesh_if)
+{
+       struct wl_mesh_params *mesh_info = &apsta_params->mesh_info;
+       char *vndr_ie;
+       uchar mesh_oui[]={0x00, 0x22, 0xf4};
+       int bytes_written = 0;
+       int ret = 0, i, vndr_ie_len;
+       uint8 *peer_bssid;
+
+       wl_mesh_clear_vndr_ie(mesh_if->dev, mesh_oui);
+
+       vndr_ie_len = WLC_IOCTL_MEDLEN;
+       vndr_ie = kmalloc(vndr_ie_len, GFP_KERNEL);
+       if (vndr_ie == NULL) {
+               AEXT_ERROR(mesh_if->dev->name, "Failed to allocate buffer of %d bytes\n",
+                       WLC_IOCTL_MEDLEN); 
+               ret = -1;
+               goto exit;
+       }
+
+       bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
+               "0x%02x%02x%02x", mesh_oui[0], mesh_oui[1], mesh_oui[2]);
+
+       bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
+               "%02x%02x%02x%02x%02x%02x%02x%02x", MESH_INFO_MASTER_BSSID, ETHER_ADDR_LEN,
+               ((u8 *)(&mesh_info->master_bssid))[0], ((u8 *)(&mesh_info->master_bssid))[1],
+               ((u8 *)(&mesh_info->master_bssid))[2], ((u8 *)(&mesh_info->master_bssid))[3],
+               ((u8 *)(&mesh_info->master_bssid))[4], ((u8 *)(&mesh_info->master_bssid))[5]);
+
+       bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
+               "%02x%02x%02x", MESH_INFO_MASTER_CHANNEL, 1, mesh_info->master_channel);
+
+       bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
+               "%02x%02x%02x", MESH_INFO_HOP_CNT, 1, mesh_info->hop_cnt);
+
+       bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
+               "%02x%02x", MESH_INFO_PEER_BSSID, mesh_info->hop_cnt*ETHER_ADDR_LEN);
+       for (i=0; i<mesh_info->hop_cnt && i<MAX_HOP_LIST; i++) {
+               peer_bssid = (uint8 *)&mesh_info->peer_bssid[i];
+               bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
+                       "%02x%02x%02x%02x%02x%02x",
+                       peer_bssid[0], peer_bssid[1], peer_bssid[2],
+                       peer_bssid[3], peer_bssid[4], peer_bssid[5]);
+       }
+
+       ret = wl_ext_add_del_ie(mesh_if->dev, VNDR_IE_BEACON_FLAG|VNDR_IE_PRBRSP_FLAG,
+               vndr_ie, "add");
+       if (!ret) {
+               AEXT_INFO(mesh_if->dev->name, "mbssid=%pM, mchannel=%d, hop=%d, pbssid=%pM\n",
+                       &mesh_info->master_bssid, mesh_info->master_channel, mesh_info->hop_cnt,
+                       mesh_info->peer_bssid); 
+       }
+
+exit:
+       if (vndr_ie)
+               kfree(vndr_ie);
+       return ret;
+}
+
+static bool
+wl_mesh_update_master_info(struct wl_apsta_params *apsta_params,
+       struct wl_if_info *mesh_if)
+{
+       struct wl_mesh_params *mesh_info = &apsta_params->mesh_info;
+       struct wl_if_info *sta_if = NULL;
+       bool updated = FALSE;
+
+       sta_if = wl_ext_if_enabled(apsta_params, ISTA_MODE);
+       if (sta_if) {
+               wldev_ioctl(mesh_if->dev, WLC_GET_BSSID, &mesh_info->master_bssid,
+                       ETHER_ADDR_LEN, 0);
+               mesh_info->master_channel = wl_ext_get_chan(apsta_params, mesh_if->dev);
+               mesh_info->hop_cnt = 0;
+               memset(mesh_info->peer_bssid, 0, MAX_HOP_LIST*ETHER_ADDR_LEN);
+               if (!wl_mesh_update_vndr_ie(apsta_params, mesh_if))
+                       updated = TRUE;
+       }
+
+       return updated;
+}
+
+static bool
+wl_mesh_update_mesh_info(struct wl_apsta_params *apsta_params,
+       struct wl_if_info *mesh_if)
+{
+       struct wl_mesh_params *mesh_info = &apsta_params->mesh_info, peer_mesh_info;
+       uint32 count = 0;
+       char *dump_buf = NULL;
+       mesh_peer_info_dump_t *peer_results;
+       mesh_peer_info_ext_t *mpi_ext;
+       struct ether_addr bssid;
+       bool updated = FALSE, bss_found = FALSE;
+       uint16 cur_chan;
+
+       dump_buf = kmalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
+       if (dump_buf == NULL) {
+               AEXT_ERROR(mesh_if->dev->name, "Failed to allocate buffer of %d bytes\n",
+                       WLC_IOCTL_MAXLEN); 
+               return FALSE;
+       }
+       count = wl_mesh_get_peer_results(mesh_if->dev, dump_buf, WLC_IOCTL_MAXLEN);
+       if (count > 0) {
+               memset(&bssid, 0, ETHER_ADDR_LEN);
+               wldev_ioctl(mesh_if->dev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, 0);
+               peer_results = (mesh_peer_info_dump_t *)dump_buf;
+               mpi_ext = (mesh_peer_info_ext_t *)peer_results->mpi_ext;
+               for (count = 0; count < peer_results->count; count++) {
+                       if (mpi_ext->entry_state != MESH_SELF_PEER_ENTRY_STATE_TIMEDOUT &&
+                                       mpi_ext->peer_info.state == MESH_PEERING_ESTAB) {
+                               memset(&peer_mesh_info, 0, sizeof(struct wl_mesh_params));
+                               bss_found = wl_escan_mesh_info(mesh_if->dev, mesh_if->escan,
+                                       &mpi_ext->ea, &peer_mesh_info);
+                               if (bss_found && (mesh_info->master_channel == 0 ||
+                                               peer_mesh_info.hop_cnt <= mesh_info->hop_cnt) &&
+                                               memcmp(&peer_mesh_info.peer_bssid, &bssid, ETHER_ADDR_LEN)) {
+                                       memcpy(&mesh_info->master_bssid, &peer_mesh_info.master_bssid,
+                                               ETHER_ADDR_LEN);
+                                       mesh_info->master_channel = peer_mesh_info.master_channel;
+                                       mesh_info->hop_cnt = peer_mesh_info.hop_cnt+1;
+                                       memset(mesh_info->peer_bssid, 0, MAX_HOP_LIST*ETHER_ADDR_LEN);
+                                       memcpy(&mesh_info->peer_bssid, &mpi_ext->ea, ETHER_ADDR_LEN);
+                                       memcpy(&mesh_info->peer_bssid[1], peer_mesh_info.peer_bssid,
+                                               (MAX_HOP_LIST-1)*ETHER_ADDR_LEN);
+                                       updated = TRUE;
+                               }
+                       }
+                       mpi_ext++;
+               }
+               if (updated) {
+                       if (wl_mesh_update_vndr_ie(apsta_params, mesh_if)) {
+                               AEXT_ERROR(mesh_if->dev->name, "update failed\n");
+                               mesh_info->master_channel = 0;
+                               updated = FALSE;
+                               goto exit;
+                       }
+               }
+       }
+
+       if (!mesh_info->master_channel) {
+               wlc_ssid_t cur_ssid;
+               char sec[32];
+               bool sae = FALSE;
+               memset(&peer_mesh_info, 0, sizeof(struct wl_mesh_params));
+               wl_ext_ioctl(mesh_if->dev, WLC_GET_SSID, &cur_ssid, sizeof(cur_ssid), 0);
+               wl_ext_get_sec(mesh_if->dev, mesh_if->ifmode, sec, sizeof(sec));
+               if (strnicmp(sec, "sae/sae", strlen("sae/sae")) == 0)
+                       sae = TRUE;
+               cur_chan = wl_ext_get_chan(apsta_params, mesh_if->dev);
+               bss_found = wl_escan_mesh_peer(mesh_if->dev, mesh_if->escan, &cur_ssid, cur_chan,
+                       sae, &peer_mesh_info);
+
+               if (bss_found && peer_mesh_info.master_channel&&
+                               (cur_chan != peer_mesh_info.master_channel)) {
+                       WL_MSG(mesh_if->ifname, "moving channel %d -> %d\n",
+                               cur_chan, peer_mesh_info.master_channel);
+                       wl_ext_disable_iface(mesh_if->dev, mesh_if->ifname);
+                       mesh_if->channel = peer_mesh_info.master_channel;
+                       wl_ext_enable_iface(mesh_if->dev, mesh_if->ifname, 500);
+               }
+       }
+
+exit:
+       if (dump_buf)
+               kfree(dump_buf);
+       return updated;
+}
+
+static void
+wl_mesh_event_handler( struct wl_apsta_params *apsta_params,
+       struct wl_if_info *mesh_if, const wl_event_msg_t *e, void *data)
+{
+       struct wl_mesh_params *mesh_info = &apsta_params->mesh_info;
+       uint32 event_type = ntoh32(e->event_type);
+       uint32 status = ntoh32(e->status);
+       uint32 reason = ntoh32(e->reason);
+       wlc_ssid_t ssid;
+       int ret;
+
+       if (wl_get_isam_status(mesh_if, AP_CREATED) &&
+                       ((event_type == WLC_E_SET_SSID && status == WLC_E_STATUS_SUCCESS) ||
+                       (event_type == WLC_E_LINK && status == WLC_E_STATUS_SUCCESS &&
+                       reason == WLC_E_REASON_INITIAL_ASSOC))) {
+               if (!wl_mesh_update_master_info(apsta_params, mesh_if)) {
+                       mesh_info->scan_channel = wl_ext_get_chan(apsta_params, mesh_if->dev);
+                       wl_mesh_set_timer(mesh_if, WL_MESH_DELAY_SCAN_MS);
+               }
+       }
+       else if ((event_type == WLC_E_LINK && reason == WLC_E_LINK_BSSCFG_DIS) ||
+                       (event_type == WLC_E_LINK && status == WLC_E_STATUS_SUCCESS &&
+                       reason == WLC_E_REASON_DEAUTH)) {
+               wl_mesh_clear_mesh_info(apsta_params, mesh_if, FALSE);
+       }
+       else if (wl_get_isam_status(mesh_if, AP_CREATED) &&
+                       (event_type == WLC_E_ASSOC_IND || event_type == WLC_E_REASSOC_IND) &&
+                       reason == DOT11_SC_SUCCESS) {
+               mesh_info->scan_channel = wl_ext_get_chan(apsta_params, mesh_if->dev);
+               wl_mesh_set_timer(mesh_if, 100);
+       }
+       else if (event_type == WLC_E_DISASSOC_IND || event_type == WLC_E_DEAUTH_IND ||
+                       (event_type == WLC_E_DEAUTH && reason != DOT11_RC_RESERVED)) {
+               if (!memcmp(&mesh_info->peer_bssid, &e->addr, ETHER_ADDR_LEN))
+                       wl_mesh_clear_mesh_info(apsta_params, mesh_if, TRUE);
+       }
+       else if (wl_get_isam_status(mesh_if, AP_CREATED) &&
+                       event_type == WLC_E_RESERVED && reason == 0xFFFFFFFF) {
+               if (!wl_mesh_update_master_info(apsta_params, mesh_if)) {
+                       wl_ext_ioctl(mesh_if->dev, WLC_GET_SSID, &ssid, sizeof(ssid), 0);
+                       ret = wl_escan_set_scan(mesh_if->dev, apsta_params->dhd, &ssid,
+                               mesh_info->scan_channel, FALSE);
+                       if (ret)
+                               wl_mesh_set_timer(mesh_if, WL_MESH_DELAY_SCAN_MS);
+               }
+       }
+       else if (wl_get_isam_status(mesh_if, AP_CREATED) &&
+                       ((event_type == WLC_E_ESCAN_RESULT && status == WLC_E_STATUS_SUCCESS) ||
+                       (event_type == WLC_E_ESCAN_RESULT &&
+                       (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 || status == WLC_E_STATUS_TIMEOUT)))) {
+               if (!wl_mesh_update_master_info(apsta_params, mesh_if)) {
+                       if (!wl_mesh_update_mesh_info(apsta_params, mesh_if)) {
+                               mesh_info->scan_channel = 0;
+                               wl_mesh_set_timer(mesh_if, WL_MESH_DELAY_SCAN_MS);
+                       }
+               }
+       }
+}
+
+static void
+wl_mesh_escan_detach(dhd_pub_t *dhd, struct wl_if_info *mesh_if)
+{
+       AEXT_TRACE(mesh_if->dev->name, "Enter\n");
+
+       del_timer_sync(&mesh_if->delay_scan);
+
+       if (mesh_if->escan) {
+               mesh_if->escan = NULL;
+       }
+}
+
+static int
+wl_mesh_escan_attach(dhd_pub_t *dhd, struct wl_if_info *mesh_if)
+{
+       AEXT_TRACE(mesh_if->dev->name, "Enter\n");
+
+       mesh_if->escan = dhd->escan;
+       init_timer_compat(&mesh_if->delay_scan, wl_mesh_timer, mesh_if);
+
+       return 0;
+}
+
+static uint
+wl_mesh_update_peer_path(struct wl_if_info *mesh_if, char *command,
+       int total_len)
+{
+       struct wl_mesh_params peer_mesh_info;
+       uint32 count = 0;
+       char *dump_buf = NULL;
+       mesh_peer_info_dump_t *peer_results;
+       mesh_peer_info_ext_t *mpi_ext;
+       int bytes_written = 0, j, k;
+       bool bss_found = FALSE;
+
+       dump_buf = kmalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
+       if (dump_buf == NULL) {
+               AEXT_ERROR(mesh_if->dev->name, "Failed to allocate buffer of %d bytes\n",
+                       WLC_IOCTL_MAXLEN); 
+               return FALSE;
+       }
+       count = wl_mesh_get_peer_results(mesh_if->dev, dump_buf, WLC_IOCTL_MAXLEN);
+       if (count > 0) {
+               peer_results = (mesh_peer_info_dump_t *)dump_buf;
+               mpi_ext = (mesh_peer_info_ext_t *)peer_results->mpi_ext;
+               for (count = 0; count < peer_results->count; count++) {
+                       if (mpi_ext->entry_state != MESH_SELF_PEER_ENTRY_STATE_TIMEDOUT &&
+                                       mpi_ext->peer_info.state == MESH_PEERING_ESTAB) {
+                               memset(&peer_mesh_info, 0, sizeof(struct wl_mesh_params));
+                               bss_found = wl_escan_mesh_info(mesh_if->dev, mesh_if->escan,
+                                       &mpi_ext->ea, &peer_mesh_info);
+                               if (bss_found) {
+                                       bytes_written += snprintf(command+bytes_written, total_len,
+                                               "\npeer=%pM, hop=%d",
+                                               &mpi_ext->ea, peer_mesh_info.hop_cnt);
+                                       for (j=1; j<peer_mesh_info.hop_cnt; j++) {
+                                               bytes_written += snprintf(command+bytes_written,
+                                                       total_len, "\n");
+                                               for (k=0; k<j; k++) {
+                                                       bytes_written += snprintf(command+bytes_written,
+                                                               total_len, " ");
+                                               }
+                                               bytes_written += snprintf(command+bytes_written, total_len,
+                                                       "%pM", &peer_mesh_info.peer_bssid[j]);
+                                       }
+                               }
+                       }
+                       mpi_ext++;
+               }
+       }
+
+       if (dump_buf)
+               kfree(dump_buf);
+       return bytes_written;
 }
 
 static int
-wl_ext_mesh_peer_status(struct net_device *dev, char *data, char *command,
-       int total_len)
+wl_ext_isam_peer_path(struct net_device *dev, char *command, int total_len)
 {
        struct dhd_pub *dhd = dhd_get_pub(dev);
        struct wl_apsta_params *apsta_params = dhd->iapsta_params;
+       struct wl_mesh_params *mesh_info = &apsta_params->mesh_info;
+       struct wl_if_info *tmp_if;
+       uint16 chan = 0;
+       char *dump_buf = NULL;
+       int dump_len = WLC_IOCTL_MEDLEN;
+       int dump_written = 0;
        int i;
-       struct wl_if_info *cur_if;
-       mesh_peer_info_dump_t *peer_results;
-       mesh_peer_info_ext_t *mpi_ext;
-       char *peer_buf = NULL;
-       int peer_len = WLC_IOCTL_MAXLEN;
-       int dump_written = 0, ret;
 
-       if (!data) {
-               peer_buf = kmalloc(peer_len, GFP_KERNEL);
-               if (peer_buf == NULL) {
-                       AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n",
-                               peer_len); 
-                       return -1;
+       if (command || android_msg_level & ANDROID_INFO_LEVEL) {
+               if (command) {
+                       dump_buf = command;
+                       dump_len = total_len;
+               } else {
+                       dump_buf = kmalloc(dump_len, GFP_KERNEL);
+                       if (dump_buf == NULL) {
+                               AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n",
+                                       dump_len); 
+                               return -1;
+                       }
                }
                for (i=0; i<MAX_IF_NUM; i++) {
-                       cur_if = &apsta_params->if_info[i];
-                       if (cur_if && dev == cur_if->dev && cur_if->ifmode == IMESH_MODE) {
-                               memset(peer_buf, 0, peer_len);
-                               ret = wl_mesh_get_peer_results(dev, peer_buf, peer_len);
-                               if (ret >= 0) {
-                                       peer_results = (mesh_peer_info_dump_t *)peer_buf;
-                                       mpi_ext = (mesh_peer_info_ext_t *)peer_results->mpi_ext;
-                                       dump_written += wl_mesh_print_peer_info(mpi_ext,
-                                               peer_results->count, command+dump_written,
-                                               total_len-dump_written);
+                       tmp_if = &apsta_params->if_info[i];
+                       if (tmp_if->dev && tmp_if->ifmode == IMESH_MODE && apsta_params->macs) {
+                               chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
+                               if (chan) {
+                                       dump_written += snprintf(dump_buf+dump_written, dump_len,
+                                               "[dhd-%s-%c] mbssid=%pM, mchan=%d, hop=%d, pbssid=%pM",
+                                               tmp_if->ifname, tmp_if->prefix, &mesh_info->master_bssid,
+                                               mesh_info->master_channel, mesh_info->hop_cnt,
+                                               &mesh_info->peer_bssid);
+                                       dump_written += wl_mesh_update_peer_path(tmp_if,
+                                               dump_buf+dump_written, dump_len-dump_written);
                                }
-                       } else if (cur_if && dev == cur_if->dev) {
-                               AEXT_ERROR(dev->name, "[%s][%c] is not mesh interface\n",
-                                       cur_if->ifname, cur_if->prefix);
                        }
                }
+               AEXT_INFO(dev->name, "%s\n", dump_buf);
        }
 
-       if (peer_buf)
-               kfree(peer_buf);
+       if (!command && dump_buf)
+               kfree(dump_buf);
        return dump_written;
 }
-#endif
+#endif /* WL_ESCAN */
+#endif /* WLMESH */
 
 static int
 wl_ext_isam_status(struct net_device *dev, char *command, int total_len)
@@ -2214,7 +3151,6 @@ wl_ext_isam_status(struct net_device *dev, char *command, int total_len)
        struct dhd_pub *dhd = dhd_get_pub(dev);
        struct wl_apsta_params *apsta_params = dhd->iapsta_params;
        int i;
-       bool now_if;
        struct wl_if_info *tmp_if;
        uint16 chan = 0;
        wlc_ssid_t ssid = { 0, {0} };
@@ -2241,13 +3177,10 @@ wl_ext_isam_status(struct net_device *dev, char *command, int total_len)
                dump_written += snprintf(dump_buf+dump_written, dump_len,
                        "apstamode=%d", apsta_params->apstamode);
                for (i=0; i<MAX_IF_NUM; i++) {
-                       now_if = FALSE;
                        memset(&ssid, 0, sizeof(ssid));
                        memset(&bssid, 0, sizeof(bssid));
                        memset(&scb_val, 0, sizeof(scb_val));
                        tmp_if = &apsta_params->if_info[i];
-                       if (dev == tmp_if->dev)
-                               now_if = TRUE;
                        if (tmp_if->dev) {
                                chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
                                if (chan) {
@@ -2258,10 +3191,12 @@ wl_ext_isam_status(struct net_device *dev, char *command, int total_len)
                                        chanspec = wl_ext_get_chanspec(apsta_params, tmp_if->dev);
                                        wl_ext_get_sec(tmp_if->dev, tmp_if->ifmode, sec, sizeof(sec));
                                        dump_written += snprintf(dump_buf+dump_written, dump_len,
-                                               "\n[dhd-%s][%c-%c%s]: bssid=%pM, chan=%3d(0x%x), "
+                                               "\n[dhd-%s-%c]: bssid=%pM, chan=%3d(0x%x %sMHz), "
                                                "rssi=%3d, sec=%-15s, SSID=\"%s\"",
-                                               tmp_if->ifname, tmp_if->prefix, chan?'E':'D',
-                                               now_if?"*":" ", &bssid, chan, chanspec,
+                                               tmp_if->ifname, tmp_if->prefix, &bssid, chan, chanspec,
+                                               CHSPEC_IS20(chanspec)?"20":
+                                               CHSPEC_IS40(chanspec)?"40":
+                                               CHSPEC_IS80(chanspec)?"80":"160",
                                                dtoh32(scb_val.val), sec, ssid.SSID);
                                        if (tmp_if->ifmode == IAP_MODE) {
                                                dump_written += snprintf(dump_buf+dump_written, dump_len, "\n");
@@ -2274,12 +3209,10 @@ wl_ext_isam_status(struct net_device *dev, char *command, int total_len)
                                                dump_written += wl_ext_mesh_peer_status(tmp_if->dev, NULL,
                                                        dump_buf+dump_written, dump_len-dump_written);
                                        }
-#endif
+#endif /* WLMESH */
                                } else {
                                        dump_written += snprintf(dump_buf+dump_written, dump_len,
-                                               "\n[dhd-%s][%c-%c%s]:",
-                                               tmp_if->ifname, tmp_if->prefix, chan?'E':'D',
-                                               now_if?"*":" ");
+                                               "\n[dhd-%s-%c]:", tmp_if->ifname, tmp_if->prefix);
                                }
                        }
                }
@@ -2291,6 +3224,15 @@ wl_ext_isam_status(struct net_device *dev, char *command, int total_len)
        return dump_written;
 }
 
+static bool
+wl_ext_master_if(struct wl_if_info *cur_if)
+{
+       if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE)
+               return TRUE;
+       else
+               return FALSE;
+}
+
 static int
 wl_ext_if_down(struct wl_apsta_params *apsta_params, struct wl_if_info *cur_if)
 {
@@ -2344,10 +3286,13 @@ wl_ext_if_up(struct wl_apsta_params *apsta_params, struct wl_if_info *cur_if)
                return 0;
        }
 
-       if (wl_ext_dfs_chan(apsta_params, cur_if->dev, cur_if->channel)) {
+       if (wl_ext_dfs_chan(cur_if->channel) && !apsta_params->radar) {
                WL_MSG(cur_if->ifname, "[%c] skip DFS channel %d\n",
                        cur_if->prefix, cur_if->channel);
                return 0;
+       } else if (!cur_if->channel) {
+               WL_MSG(cur_if->ifname, "[%c] no valid channel\n", cur_if->prefix);
+               return 0;
        }
 
        WL_MSG(cur_if->ifname, "[%c] Turning on...\n", cur_if->prefix);
@@ -2419,6 +3364,7 @@ wl_ext_disable_iface(struct net_device *dev, char *ifname)
 
        if (cur_if->ifmode == ISTA_MODE) {
                wl_ext_ioctl(cur_if->dev, WLC_DISASSOC, NULL, 0, 1);
+               wl_ext_add_remove_pm_enable_work(dev, FALSE);
        } else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
                // deauthenticate all STA first
                memcpy(scbval.ea.octet, &ether_bcast, ETHER_ADDR_LEN);
@@ -2450,38 +3396,33 @@ wl_ext_disable_iface(struct net_device *dev, char *ifname)
                                dhd_wlfc_deinit(dhd);
                        }
                }
-#endif 
+#endif /* BCMSDIO */
 #endif /* PROP_TXSTATUS_VSDB */
        }
-       else if (apstamode == IDUALAP_MODE) {
+       else if (apstamode == IDUALAP_MODE || apstamode == ISTAAPAP_MODE) {
                bss_setbuf.cfg = 0xffffffff;
                bss_setbuf.val = htod32(0);
                wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf),
                        iovar_buf, WLC_IOCTL_SMLEN, NULL);
 #ifdef WLMESH
        } else if (apstamode == ISTAMESH_MODE || apstamode == IMESHAP_MODE ||
-                       apstamode == ISTAAPMESH_MODE || apstamode == IMESHAPAP_MODE ||
-                       apstamode == ISTAAPAP_MODE) {
+                       apstamode == ISTAAPMESH_MODE || apstamode == IMESHAPAP_MODE) {
                bss_setbuf.cfg = 0xffffffff;
                bss_setbuf.val = htod32(0);
                wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf),
                        iovar_buf, WLC_IOCTL_SMLEN, NULL);
-#endif
-       }
-#ifdef WLMESH
-       if ((cur_if->ifmode == IMESH_MODE) &&
-                       (apstamode == ISTAMESH_MODE || apstamode == IMESHAP_MODE ||
-                       apstamode == ISTAAPMESH_MODE || apstamode == IMESHAPAP_MODE)) {
-               int scan_assoc_time = DHD_SCAN_ASSOC_ACTIVE_TIME;
-               for (i=0; i<MAX_IF_NUM; i++) {
-                       tmp_if = &apsta_params->if_info[i];
-                       if (tmp_if->dev && tmp_if->ifmode == ISTA_MODE) {
-                               wl_ext_ioctl(tmp_if->dev, WLC_SET_SCAN_CHANNEL_TIME,
-                                       &scan_assoc_time, sizeof(scan_assoc_time), 1);
+               if (cur_if->ifmode == IMESH_MODE) {
+                       int scan_assoc_time = DHD_SCAN_ASSOC_ACTIVE_TIME;
+                       for (i=0; i<MAX_IF_NUM; i++) {
+                               tmp_if = &apsta_params->if_info[i];
+                               if (tmp_if->dev && tmp_if->ifmode == ISTA_MODE) {
+                                       wl_ext_ioctl(tmp_if->dev, WLC_SET_SCAN_CHANNEL_TIME,
+                                               &scan_assoc_time, sizeof(scan_assoc_time), 1);
+                               }
                        }
                }
+#endif /* WLMESH */
        }
-#endif
 
        wl_clr_isam_status(cur_if, AP_CREATED);
 
@@ -2532,6 +3473,34 @@ wl_ext_diff_band(uint16 chan1, uint16 chan2)
        return FALSE;
 }
 
+static uint16
+wl_ext_same_band(struct wl_apsta_params *apsta_params,
+       struct wl_if_info *cur_if, bool nodfs)
+{
+       struct wl_if_info *tmp_if;
+       uint16 tmp_chan, target_chan = 0;
+       wl_prio_t max_prio;
+       int i;
+
+       // find the max prio
+       max_prio = cur_if->prio;
+       for (i=0; i<MAX_IF_NUM; i++) {
+               tmp_if = &apsta_params->if_info[i];
+               if (cur_if != tmp_if && wl_get_isam_status(tmp_if, IF_READY) &&
+                               tmp_if->prio > max_prio) {
+                       tmp_chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
+                       if (wl_ext_dfs_chan(tmp_chan) && nodfs)
+                               continue;
+                       if (tmp_chan && !wl_ext_diff_band(cur_if->channel, tmp_chan)) {
+                               target_chan = tmp_chan;
+                               max_prio = tmp_if->prio;
+                       }
+               }
+       }
+
+       return target_chan;
+}
+
 static uint16
 wl_ext_get_vsdb_chan(struct wl_apsta_params *apsta_params,
        struct wl_if_info *cur_if, struct wl_if_info *target_if)
@@ -2589,12 +3558,10 @@ wl_ext_trigger_csa(struct wl_apsta_params *apsta_params, struct wl_if_info *cur_
        s8 iovar_buf[WLC_IOCTL_SMLEN];
        bool core_conflict = FALSE;
 
-       if (apsta_params->csa & CSA_DRV_BIT &&
-                       (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE)) {
+       if (wl_ext_master_if(cur_if) && (apsta_params->csa & CSA_DRV_BIT)) {
                if (!cur_if->channel) {
-                       WL_MSG(cur_if->ifname, "[%c] skip channel %d\n",
-                               cur_if->prefix, cur_if->channel);
-               } else if (wl_ext_dfs_chan(apsta_params, cur_if->dev, cur_if->channel)) {
+                       WL_MSG(cur_if->ifname, "[%c] no valid channel\n", cur_if->prefix);
+               } else if (wl_ext_dfs_chan(cur_if->channel) && !apsta_params->radar) {
                        WL_MSG(cur_if->ifname, "[%c] skip DFS channel %d\n",
                                cur_if->prefix, cur_if->channel);
                        wl_ext_if_down(apsta_params, cur_if);
@@ -2627,9 +3594,139 @@ wl_ext_trigger_csa(struct wl_apsta_params *apsta_params, struct wl_if_info *cur_
        return 0;
 }
 
+static void
+wl_ext_move_cur_dfs_channel(struct wl_apsta_params *apsta_params,
+       struct wl_if_info *cur_if)
+{
+       uint16 other_chan = 0, cur_chan = cur_if->channel;
+       uint16 chan_2g = 0, chan_5g = 0;
+       uint32 auto_band = WLC_BAND_2G;
+
+       if (wl_ext_master_if(cur_if) && wl_ext_dfs_chan(cur_if->channel) &&
+                       !apsta_params->radar) {
+
+               wl_ext_get_default_chan(cur_if->dev, &chan_2g, &chan_5g, TRUE);
+               if (!chan_2g && !chan_5g) {
+                       cur_if->channel = 0;
+                       WL_MSG(cur_if->ifname, "[%c] no valid channel\n", cur_if->prefix);
+                       return;
+               }
+
+               if (apsta_params->vsdb) {
+                       if (chan_5g) {
+                               cur_if->channel = chan_5g;
+                               auto_band = WLC_BAND_5G;
+                               other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
+                       } else {
+                               cur_if->channel = chan_2g;
+                               auto_band = WLC_BAND_2G;
+                               other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
+                       }
+                       if (!other_chan) {
+                               other_chan = wl_ext_autochannel(cur_if->dev, ACS_FW_BIT|ACS_DRV_BIT,
+                                       auto_band);
+                       }
+                       if (other_chan)
+                               cur_if->channel = other_chan;
+               } else if (apsta_params->rsdb) {
+                       if (chan_5g) {
+                               cur_if->channel = chan_5g;
+                               auto_band = WLC_BAND_5G;
+                               other_chan = wl_ext_same_band(apsta_params, cur_if, FALSE);
+                               if (wl_ext_dfs_chan(other_chan) && chan_2g) {
+                                       cur_if->channel = chan_2g;
+                                       auto_band = WLC_BAND_2G;
+                                       other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
+                               }
+                       } else {
+                               cur_if->channel = chan_2g;
+                               auto_band = WLC_BAND_2G;
+                               other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
+                       }
+                       if (!other_chan) {
+                               other_chan = wl_ext_autochannel(cur_if->dev, ACS_FW_BIT|ACS_DRV_BIT,
+                                       auto_band);
+                       }
+                       if (other_chan)
+                               cur_if->channel = other_chan;
+               } else {
+                       cur_if->channel = chan_5g;
+                       auto_band = WLC_BAND_5G;
+                       other_chan = wl_ext_same_band(apsta_params, cur_if, FALSE);
+                       if (wl_ext_dfs_chan(other_chan)) {
+                               cur_if->channel = 0;
+                       }
+                       else if (!other_chan) {
+                               other_chan = wl_ext_autochannel(cur_if->dev, ACS_FW_BIT|ACS_DRV_BIT,
+                                       auto_band);
+                       }
+                       if (other_chan)
+                               cur_if->channel = other_chan;
+               }
+               WL_MSG(cur_if->ifname, "[%c] move channel %d => %d\n",
+                       cur_if->prefix, cur_chan, cur_if->channel);
+       }
+}
+
+static void
+wl_ext_move_other_dfs_channel(struct wl_apsta_params *apsta_params,
+       struct wl_if_info *cur_if)
+{
+       uint16 other_chan = 0, cur_chan = cur_if->channel;
+       uint16 chan_2g = 0, chan_5g = 0;
+       uint32 auto_band = WLC_BAND_2G;
+
+       if (wl_ext_master_if(cur_if) && wl_ext_dfs_chan(cur_if->channel) &&
+                       !apsta_params->radar) {
+
+               wl_ext_get_default_chan(cur_if->dev, &chan_2g, &chan_5g, TRUE);
+               if (!chan_2g && !chan_5g) {
+                       cur_if->channel = 0;
+                       WL_MSG(cur_if->ifname, "[%c] no valid channel\n", cur_if->prefix);
+                       return;
+               }
+
+               if (apsta_params->vsdb) {
+                       if (chan_5g) {
+                               cur_if->channel = chan_5g;
+                               auto_band = WLC_BAND_5G;
+                               other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
+                       } else {
+                               cur_if->channel = chan_2g;
+                               auto_band = WLC_BAND_2G;
+                               other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
+                       }
+                       if (!other_chan) {
+                               other_chan = wl_ext_autochannel(cur_if->dev, ACS_FW_BIT|ACS_DRV_BIT,
+                                       auto_band);
+                       }
+                       if (other_chan)
+                               cur_if->channel = other_chan;
+               } else if (apsta_params->rsdb) {
+                       if (chan_2g) {
+                               cur_if->channel = chan_2g;
+                               auto_band = WLC_BAND_2G;
+                               other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
+                               if (!other_chan) {
+                                       other_chan = wl_ext_autochannel(cur_if->dev, ACS_FW_BIT|ACS_DRV_BIT,
+                                               auto_band);
+                               }
+                       } else {
+                               cur_if->channel = 0;
+                       }
+                       if (other_chan)
+                               cur_if->channel = other_chan;
+               } else {
+                       cur_if->channel = 0;
+               }
+               WL_MSG(cur_if->ifname, "[%c] move channel %d => %d\n",
+                       cur_if->prefix, cur_chan, cur_if->channel);
+       }
+}
+
 static uint16
 wl_ext_move_cur_channel(struct wl_apsta_params *apsta_params,
-       struct net_device *dev, struct wl_if_info *cur_if)
+       struct wl_if_info *cur_if)
 {
        struct wl_if_info *tmp_if, *target_if = NULL;
        uint16 tmp_chan, target_chan = 0;
@@ -2655,6 +3752,7 @@ wl_ext_move_cur_channel(struct wl_apsta_params *apsta_params,
                        }
                }
        }
+
        if (target_chan) {
                tmp_chan = wl_ext_get_chan(apsta_params, cur_if->dev);
                if (apsta_params->rsdb && tmp_chan &&
@@ -2669,20 +3767,16 @@ wl_ext_move_cur_channel(struct wl_apsta_params *apsta_params,
                        cur_if->channel = target_chan;
                }
        }
+
 exit:
-       if ((cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) &&
-                       wl_ext_dfs_chan(apsta_params, cur_if->dev, cur_if->channel)) {
-               WL_MSG(cur_if->ifname, "[%c] skip DFS channel %d\n",
-                       cur_if->prefix, cur_if->channel);
-               cur_if->channel = 0;
-       }
+       wl_ext_move_cur_dfs_channel(apsta_params, cur_if);
 
        return cur_if->channel;
 }
 
 static void
 wl_ext_move_other_channel(struct wl_apsta_params *apsta_params,
-       struct net_device *dev, struct wl_if_info *cur_if)
+       struct wl_if_info *cur_if)
 {
        struct wl_if_info *tmp_if, *target_if=NULL;
        uint16 tmp_chan, target_chan = 0;
@@ -2712,11 +3806,12 @@ wl_ext_move_other_channel(struct wl_apsta_params *apsta_params,
                WL_MSG(target_if->ifname, "channel=%d => %s channel=%d\n",
                        target_chan, cur_if->ifname, cur_if->channel);
                target_if->channel = cur_if->channel;
+               wl_ext_move_other_dfs_channel(apsta_params, target_if);
                if (apsta_params->csa == 0) {
                        wl_ext_if_down(apsta_params, target_if);
-                       wl_ext_move_other_channel(apsta_params, dev, target_if);
+                       wl_ext_move_other_channel(apsta_params, cur_if);
                        if (target_if->ifmode == ISTA_MODE || target_if->ifmode == IMESH_MODE) {
-                               wl_ext_enable_iface(target_if->dev, target_if->ifname);
+                               wl_ext_enable_iface(target_if->dev, target_if->ifname, 0);
                        } else if (target_if->ifmode == IAP_MODE) {
                                wl_ext_if_up(apsta_params, target_if);
                        }
@@ -2727,8 +3822,53 @@ wl_ext_move_other_channel(struct wl_apsta_params *apsta_params,
 
 }
 
+static bool
+wl_ext_wait_other_enabling(struct wl_apsta_params *apsta_params,
+       struct wl_if_info *cur_if)
+{
+       struct wl_if_info *tmp_if;
+       bool enabling = FALSE;
+       u32 timeout = 1;
+       int i;
+
+       for (i=0; i<MAX_IF_NUM; i++) {
+               tmp_if = &apsta_params->if_info[i];
+               if (tmp_if->dev && tmp_if->dev != cur_if->dev) {
+                       if (tmp_if->ifmode == ISTA_MODE)
+                               enabling = wl_get_isam_status(tmp_if, STA_CONNECTING);
+                       else if (tmp_if->ifmode == IAP_MODE || tmp_if->ifmode == IMESH_MODE)
+                               enabling = wl_get_isam_status(tmp_if, AP_CREATING);
+                       if (enabling)
+                               WL_MSG(cur_if->ifname, "waiting for %s[%c] enabling...\n",
+                                       tmp_if->ifname, tmp_if->prefix);
+                       if (enabling && tmp_if->ifmode == ISTA_MODE) {
+                               timeout = wait_event_interruptible_timeout(
+                                       apsta_params->netif_change_event,
+                                       !wl_get_isam_status(tmp_if, STA_CONNECTING),
+                                       msecs_to_jiffies(MAX_STA_LINK_WAIT_TIME));
+                       } else if (enabling &&
+                                       (tmp_if->ifmode == IAP_MODE || tmp_if->ifmode == IMESH_MODE)) {
+                               timeout = wait_event_interruptible_timeout(
+                                       apsta_params->netif_change_event,
+                                       !wl_get_isam_status(tmp_if, AP_CREATING),
+                                       msecs_to_jiffies(MAX_STA_LINK_WAIT_TIME));
+                       }
+                       if (tmp_if->ifmode == ISTA_MODE)
+                               enabling = wl_get_isam_status(tmp_if, STA_CONNECTING);
+                       else if (tmp_if->ifmode == IAP_MODE || tmp_if->ifmode == IMESH_MODE)
+                               enabling = wl_get_isam_status(tmp_if, AP_CREATING);
+                       if (timeout <= 0 || enabling) {
+                               WL_MSG(cur_if->ifname, "%s[%c] is still enabling...\n",
+                                       tmp_if->ifname, tmp_if->prefix);
+                       }
+               }
+       }
+
+       return enabling;
+}
+
 static int
-wl_ext_enable_iface(struct net_device *dev, char *ifname)
+wl_ext_enable_iface(struct net_device *dev, char *ifname, int wait_up)
 {
        struct dhd_pub *dhd = dhd_get_pub(dev);
        int i, ret = 0;
@@ -2769,10 +3909,29 @@ wl_ext_enable_iface(struct net_device *dev, char *ifname)
        wl_ext_isam_status(cur_if->dev, NULL, 0);
        WL_MSG(ifname, "[%c] Enabling...\n", cur_if->prefix);
 
-       wl_ext_check_other_enabling(apsta_params, cur_if);
+       wl_ext_wait_other_enabling(apsta_params, cur_if);
+
+       if (wl_ext_master_if(cur_if) && apsta_params->acs) {
+               uint16 chan_2g, chan_5g;
+               uint auto_band;
+               auto_band = WL_GET_BAND(cur_if->channel);
+               wl_ext_get_default_chan(cur_if->dev, &chan_2g, &chan_5g, TRUE);
+               if ((chan_2g && auto_band == WLC_BAND_2G) ||
+                               (chan_5g && auto_band == WLC_BAND_5G)) {
+                       cur_if->channel = wl_ext_autochannel(cur_if->dev, apsta_params->acs,
+                               auto_band);
+               } else {
+                       AEXT_ERROR(ifname, "invalid channel\n");
+                       ret = -1;
+                       goto exit;
+               }
+       }
+
+       wl_ext_move_cur_channel(apsta_params, cur_if);
 
-       wl_ext_move_cur_channel(apsta_params, dev, cur_if);
-       if (!cur_if->channel && cur_if->ifmode != ISTA_MODE) {
+       if (wl_ext_master_if(cur_if) && !cur_if->channel) {
+               AEXT_ERROR(ifname, "skip channel 0\n");
+               ret = -1;
                goto exit;
        }
 
@@ -2790,7 +3949,7 @@ wl_ext_enable_iface(struct net_device *dev, char *ifname)
                wl_clr_isam_status(cur_if, AP_CREATED);
        }
 
-       wl_ext_move_other_channel(apsta_params, dev, cur_if);
+       wl_ext_move_other_channel(apsta_params, cur_if);
 
        if (cur_if->ifidx > 0) {
                wl_ext_iovar_setbuf(cur_if->dev, "cur_etheraddr", (u8 *)cur_if->dev->dev_addr,
@@ -2810,33 +3969,15 @@ wl_ext_enable_iface(struct net_device *dev, char *ifname)
                }
        }
 
-       if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
+       if (wl_ext_master_if(cur_if)) {
                wl_ext_set_bgnmode(cur_if);
                if (!cur_if->channel) {
-#ifdef WL_CFG80211
-                       char *pick_tmp, *param;
-                       char cmd[128];
-                       uint16 cur_chan;
-                       cur_chan = 1;
-                       snprintf(cmd, 128, "get_best_channels");
-                       wl_cfg80211_get_best_channels(dev, cmd, strlen(cmd));
-                       pick_tmp = cmd;
-                       param = bcmstrtok(&pick_tmp, " ", 0);
-                       while (param != NULL) {
-                               if (!strnicmp(param, "2g=", strlen("2g="))) {
-                                       cur_chan = (int)simple_strtol(param+strlen("2g="), NULL, 10);
-                               } else if (!strnicmp(param, "5g=", strlen("5g="))) {
-                                       cur_chan = (int)simple_strtol(param+strlen("5g="), NULL, 10);
-                               }
-                               param = bcmstrtok(&pick_tmp, " ", 0);
-                       }
-                       cur_if->channel = cur_chan;
-#else
                        cur_if->channel = 1;
-#endif
                }
-               wl_ext_set_chanspec(cur_if->dev, apsta_params->ioctl_ver, cur_if->channel,
-                       &fw_chspec);
+               ret = wl_ext_set_chanspec(cur_if->dev, apsta_params->ioctl_ver,
+                       cur_if->channel, &fw_chspec);
+               if (ret)
+                       goto exit;
        }
 
        wl_ext_set_amode(cur_if);
@@ -2870,7 +4011,7 @@ wl_ext_enable_iface(struct net_device *dev, char *ifname)
                if (cur_if->ifmode == ISTA_MODE) {
                        wl_ext_connect(cur_if->dev, &conn_info);
                } else {
-                       if (apsta_params->rsdb) {
+                       if (FW_SUPPORTED(dhd, rsdb)) {
                                wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);
                        } else {
                                bss_setbuf.cfg = htod32(cur_if->bssidx);
@@ -2893,7 +4034,7 @@ wl_ext_enable_iface(struct net_device *dev, char *ifname)
                                        wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
                                }
                        }
-#endif
+#endif /* BCMSDIO */
 #endif /* PROP_TXSTATUS_VSDB */
                }
        }
@@ -2927,14 +4068,17 @@ wl_ext_enable_iface(struct net_device *dev, char *ifname)
                } else {
                        AEXT_ERROR(cur_if->ifname, "wrong ifmode %d\n", cur_if->ifmode);
                }
-#endif
+#endif /* WLMESH */
        }
 
-       if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
+       if (wait_up) {
+               OSL_SLEEP(wait_up);
+       } else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
                timeout = wait_event_interruptible_timeout(apsta_params->netif_change_event,
                        wl_get_isam_status(cur_if, AP_CREATED),
                        msecs_to_jiffies(MAX_AP_LINK_WAIT_TIME));
                if (timeout <= 0 || !wl_get_isam_status(cur_if, AP_CREATED)) {
+                       mutex_unlock(&apsta_params->usr_sync);
                        wl_ext_disable_iface(dev, cur_if->ifname);
                        WL_MSG(ifname, "[%c] failed to enable with SSID: \"%s\"\n",
                                cur_if->prefix, cur_if->ssid);
@@ -2963,7 +4107,7 @@ exit:
        if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
                wl_clr_isam_status(cur_if, AP_CREATING);
        }
-       WL_MSG(ifname, "[%c] Exit\n", cur_if->prefix);
+       WL_MSG(ifname, "[%c] Exit ret=%d\n", cur_if->prefix, ret);
        mutex_unlock(&apsta_params->usr_sync);
        return ret;
 }
@@ -2985,7 +4129,7 @@ wl_ext_iapsta_enable(struct net_device *dev, char *command, int total_len)
                        pch = bcmstrtok(&pick_tmp, " ", 0);
                        if (pch) {
                                strcpy(ifname, pch);
-                               ret = wl_ext_enable_iface(dev, ifname);
+                               ret = wl_ext_enable_iface(dev, ifname, 0);
                                if (ret)
                                        return ret;
                        } else {
@@ -2999,88 +4143,91 @@ wl_ext_iapsta_enable(struct net_device *dev, char *command, int total_len)
        return ret;
 }
 
+#ifdef PROPTX_MAXCOUNT
 int
-wl_ext_iapsta_alive_preinit(struct net_device *dev)
+wl_ext_get_wlfc_maxcount(struct dhd_pub *dhd, int ifidx)
 {
-       struct dhd_pub *dhd = dhd_get_pub(dev);
        struct wl_apsta_params *apsta_params = dhd->iapsta_params;
+       struct wl_if_info *tmp_if, *cur_if = NULL;
+       int i, maxcount = WL_TXSTATUS_FREERUNCTR_MASK;
 
-       if (apsta_params->init == TRUE) {
-               AEXT_ERROR(dev->name, "don't init twice\n");
-               return -1;
-       }
-
-       AEXT_TRACE(dev->name, "Enter\n");
-
-       apsta_params->init = TRUE;
-
-       return 0;
-}
-
-int
-wl_ext_iapsta_alive_postinit(struct net_device *dev)
-{
-       struct dhd_pub *dhd = dhd_get_pub(dev);
-       struct wl_apsta_params *apsta_params = dhd->iapsta_params;
-       s32 apsta = 0;
-       struct wl_if_info *cur_if;
-       int i;
+       if (!apsta_params->rsdb)
+               return maxcount;
 
-       wl_ext_iovar_getint(dev, "apsta", &apsta);
-       if (apsta == 1) {
-               apsta_params->apstamode = ISTAONLY_MODE;
-               apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE;
-               op_mode = DHD_FLAG_STA_MODE;
-       } else {
-               apsta_params->apstamode = IAPONLY_MODE;
-               apsta_params->if_info[IF_PIF].ifmode = IAP_MODE;
-               op_mode = DHD_FLAG_HOSTAP_MODE;
+       for (i=0; i<MAX_IF_NUM; i++) {
+               tmp_if = &apsta_params->if_info[i];
+               if (tmp_if->dev && tmp_if->ifidx == ifidx) {
+                       cur_if = tmp_if;
+                       maxcount = cur_if->transit_maxcount;
+               }
        }
-       // fix me: how to check it's ISTAAP_MODE or IDUALAP_MODE?
 
-       wl_ext_get_ioctl_ver(dev, &apsta_params->ioctl_ver);
-       WL_MSG(dev->name, "apstamode=%d\n", apsta_params->apstamode);
+       if (cur_if)
+               AEXT_INFO(cur_if->ifname, "update maxcount %d\n", maxcount);
+       else
+               AEXT_INFO("wlan", "update maxcount %d for ifidx %d\n", maxcount, ifidx);
+       return maxcount;
+}
+
+void
+wl_ext_update_wlfc_maxcount(struct dhd_pub *dhd)
+{
+       struct wl_apsta_params *apsta_params = dhd->iapsta_params;
+       struct wl_if_info *tmp_if;
+       bool band_5g = FALSE;
+       uint16 chan = 0;
+       int i, ret;
+
+       if (!apsta_params->rsdb)
+               return;
 
        for (i=0; i<MAX_IF_NUM; i++) {
-               cur_if = &apsta_params->if_info[i];
-               if (i == 1 && !strlen(cur_if->ifname))
-                       strcpy(cur_if->ifname, "wlan1");
-               if (i == 2 && !strlen(cur_if->ifname))
-                       strcpy(cur_if->ifname, "wlan2");
-               if (cur_if->ifmode == ISTA_MODE) {
-                       cur_if->channel = 0;
-                       cur_if->maxassoc = -1;
-                       wl_set_isam_status(cur_if, IF_READY);
-                       cur_if->prio = PRIO_STA;
-                       cur_if->prefix = 'S';
-                       snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_sta");
-               } else if (cur_if->ifmode == IAP_MODE) {
-                       cur_if->channel = 1;
-                       cur_if->maxassoc = -1;
-                       wl_set_isam_status(cur_if, IF_READY);
-                       cur_if->prio = PRIO_AP;
-                       cur_if->prefix = 'A';
-                       snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_ap");
-#ifdef WLMESH
-               } else if (cur_if->ifmode == IMESH_MODE) {
-                       cur_if->channel = 1;
-                       cur_if->maxassoc = -1;
-                       wl_set_isam_status(cur_if, IF_READY);
-                       cur_if->prio = PRIO_MESH;
-                       cur_if->prefix = 'M';
-                       snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_mesh");
-#endif
+               tmp_if = &apsta_params->if_info[i];
+               if (tmp_if->dev) {
+                       chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
+                       if (chan > CH_MAX_2G_CHANNEL) {
+                               tmp_if->transit_maxcount = dhd->conf->proptx_maxcnt_5g;
+                               ret = dhd_wlfc_update_maxcount(dhd, tmp_if->ifidx,
+                                       tmp_if->transit_maxcount);
+                               if (ret == 0)
+                                       AEXT_INFO(tmp_if->ifname, "updated maxcount %d\n",
+                                               tmp_if->transit_maxcount);
+                               band_5g = TRUE;
+                       }
                }
        }
 
-       return op_mode;
+       for (i=0; i<MAX_IF_NUM; i++) {
+               tmp_if = &apsta_params->if_info[i];
+               if (tmp_if->dev) {
+                       chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
+                       if ((chan == 0) || (chan <= CH_MAX_2G_CHANNEL && chan >= CH_MIN_2G_CHANNEL)) {
+                               if (chan == 0) {
+                                       tmp_if->transit_maxcount = WL_TXSTATUS_FREERUNCTR_MASK;
+                               } else if (band_5g) {
+                                       tmp_if->transit_maxcount = dhd->conf->proptx_maxcnt_2g;
+                               } else {
+                                       tmp_if->transit_maxcount = dhd->conf->proptx_maxcnt_5g;
+                               }
+                               ret = dhd_wlfc_update_maxcount(dhd, tmp_if->ifidx,
+                                       tmp_if->transit_maxcount);
+                               if (ret == 0)
+                                       AEXT_INFO(tmp_if->ifname, "updated maxcount %d\n",
+                                               tmp_if->transit_maxcount);
+                       }
+               }
+       }
 }
+#endif /* PROPTX_MAXCOUNT */
 
-int
+static int
 wl_ext_iapsta_event(struct net_device *dev,
        struct wl_apsta_params *apsta_params, wl_event_msg_t *e, void* data)
 {
        struct wl_if_info *cur_if = NULL, *tmp_if = NULL;
+#if defined(WLMESH) && defined(WL_ESCAN)
+       struct wl_if_info *mesh_if = NULL;
+#endif /* WLMESH && WL_ESCAN */
        int i;
        uint32 event_type = ntoh32(e->event_type);
        uint32 status =  ntoh32(e->status);
@@ -3094,12 +4241,21 @@ wl_ext_iapsta_event(struct net_device *dev,
                        break;
                }
        }
+#if defined(WLMESH) && defined(WL_ESCAN)
+       for (i=0; i<MAX_IF_NUM; i++) {
+               tmp_if = &apsta_params->if_info[i];
+               if (tmp_if->dev && tmp_if->ifmode == IMESH_MODE) {
+                       mesh_if = tmp_if;
+                       break;
+               }
+       }
+#endif /* WLMESH && WL_ESCAN */
        if (!cur_if || !cur_if->dev) {
                AEXT_DBG(dev->name, "ifidx %d is not ready\n", e->ifidx);
                return -1;
        }
 
-       if (cur_if->ifmode == ISTA_MODE) {
+       if (cur_if->ifmode == ISTA_MODE || cur_if->ifmode == IGC_MODE) {
                if (event_type == WLC_E_LINK) {
                        if (!(flags & WLC_EVENT_MSG_LINK)) {
                                WL_MSG(cur_if->ifname,
@@ -3107,24 +4263,46 @@ wl_ext_iapsta_event(struct net_device *dev,
                                        cur_if->prefix, &e->addr, bcmevent_get_name(event_type),
                                        event_type, reason);
                                wl_clr_isam_status(cur_if, STA_CONNECTED);
+#if defined(WLMESH) && defined(WL_ESCAN)
+                               if (mesh_if && apsta_params->macs)
+                                       wl_mesh_clear_mesh_info(apsta_params, mesh_if, TRUE);
+#endif /* WLMESH && WL_ESCAN */
                        } else {
                                WL_MSG(cur_if->ifname, "[%c] Link UP with %pM\n",
                                        cur_if->prefix, &e->addr);
                                wl_set_isam_status(cur_if, STA_CONNECTED);
+#if defined(WLMESH) && defined(WL_ESCAN)
+                               if (mesh_if && apsta_params->macs)
+                                       wl_mesh_update_master_info(apsta_params, mesh_if);
+#endif /* WLMESH && WL_ESCAN */
                        }
                        wl_clr_isam_status(cur_if, STA_CONNECTING);
                        wake_up_interruptible(&apsta_params->netif_change_event);
+#ifdef PROPTX_MAXCOUNT
+                       wl_ext_update_wlfc_maxcount(apsta_params->dhd);
+#endif /* PROPTX_MAXCOUNT */
                } else if (event_type == WLC_E_SET_SSID && status != WLC_E_STATUS_SUCCESS) {
                        WL_MSG(cur_if->ifname,
                                "connect failed event=%d, reason=%d, status=%d\n",
                                event_type, reason, status);
                        wl_clr_isam_status(cur_if, STA_CONNECTING);
                        wake_up_interruptible(&apsta_params->netif_change_event);
+#if defined(WLMESH) && defined(WL_ESCAN)
+                       if (mesh_if && apsta_params->macs)
+                               wl_mesh_clear_mesh_info(apsta_params, mesh_if, TRUE);
+#endif /* WLMESH && WL_ESCAN */
+#ifdef PROPTX_MAXCOUNT
+                       wl_ext_update_wlfc_maxcount(apsta_params->dhd);
+#endif /* PROPTX_MAXCOUNT */
                } else if (event_type == WLC_E_DEAUTH || event_type == WLC_E_DEAUTH_IND ||
                                event_type == WLC_E_DISASSOC || event_type == WLC_E_DISASSOC_IND) {
                        WL_MSG(cur_if->ifname, "[%c] Link down with %pM, %s(%d), reason %d\n",
                                cur_if->prefix, &e->addr, bcmevent_get_name(event_type),
                                event_type, reason);
+#if defined(WLMESH) && defined(WL_ESCAN)
+                       if (mesh_if && apsta_params->macs)
+                               wl_mesh_clear_mesh_info(apsta_params, mesh_if, TRUE);
+#endif /* WLMESH && WL_ESCAN */
                }
        }
        else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
@@ -3137,35 +4315,49 @@ wl_ext_iapsta_event(struct net_device *dev,
                                wl_set_isam_status(cur_if, AP_CREATED);
                                wake_up_interruptible(&apsta_params->netif_change_event);
                        } else {
+                               wl_set_isam_status(cur_if, AP_CREATED);
                                WL_MSG(cur_if->ifname, "[%c] Link up w/o creating? (etype=%d)\n",
                                        cur_if->prefix, event_type);
                        }
-               } else if ((event_type == WLC_E_LINK && reason == WLC_E_LINK_BSSCFG_DIS) ||
+#ifdef PROPTX_MAXCOUNT
+                       wl_ext_update_wlfc_maxcount(apsta_params->dhd);
+#endif /* PROPTX_MAXCOUNT */
+               }
+               else if ((event_type == WLC_E_LINK && reason == WLC_E_LINK_BSSCFG_DIS) ||
                                (event_type == WLC_E_LINK && status == WLC_E_STATUS_SUCCESS &&
                                reason == WLC_E_REASON_DEAUTH)) {
                        wl_clr_isam_status(cur_if, AP_CREATED);
                        WL_MSG(cur_if->ifname, "[%c] Link down, reason=%d\n",
                                cur_if->prefix, reason);
+#ifdef PROPTX_MAXCOUNT
+                       wl_ext_update_wlfc_maxcount(apsta_params->dhd);
+#endif /* PROPTX_MAXCOUNT */
                }
                else if ((event_type == WLC_E_ASSOC_IND || event_type == WLC_E_REASSOC_IND) &&
                                reason == DOT11_SC_SUCCESS) {
                        WL_MSG(cur_if->ifname, "[%c] connected device %pM\n",
                                cur_if->prefix, &e->addr);
                        wl_ext_isam_status(cur_if->dev, NULL, 0);
-               } else if (event_type == WLC_E_DISASSOC_IND ||
+               }
+               else if (event_type == WLC_E_DISASSOC_IND ||
                                event_type == WLC_E_DEAUTH_IND ||
                                (event_type == WLC_E_DEAUTH && reason != DOT11_RC_RESERVED)) {
-                       WL_MSG(cur_if->ifname,
+                       WL_MSG_RLMT(cur_if->ifname, &e->addr, ETHER_ADDR_LEN,
                                "[%c] disconnected device %pM, %s(%d), reason=%d\n",
                                cur_if->prefix, &e->addr, bcmevent_get_name(event_type),
                                event_type, reason);
                        wl_ext_isam_status(cur_if->dev, NULL, 0);
                }
+#if defined(WLMESH) && defined(WL_ESCAN)
+               if (cur_if->ifmode == IMESH_MODE && apsta_params->macs)
+                       wl_mesh_event_handler(apsta_params, cur_if, e, data);
+#endif /* WLMESH && WL_ESCAN */
        }
 
        return 0;
 }
 
+#ifdef WL_CFG80211
 u32
 wl_ext_iapsta_update_channel(dhd_pub_t *dhd, struct net_device *dev,
        u32 channel)
@@ -3183,21 +4375,46 @@ wl_ext_iapsta_update_channel(dhd_pub_t *dhd, struct net_device *dev,
        }
 
        if (cur_if) {
-               if (cur_if->ifmode == ISTA_MODE)
-                       wl_set_isam_status(cur_if, STA_CONNECTING);
                wl_ext_isam_status(cur_if->dev, NULL, 0);
                cur_if->channel = channel;
-               channel = wl_ext_move_cur_channel(apsta_params,
-                       apsta_params->if_info[IF_PIF].dev, cur_if);
+               if (wl_ext_master_if(cur_if) && apsta_params->acs) {
+                       uint auto_band = WL_GET_BAND(channel);
+                       cur_if->channel = wl_ext_autochannel(cur_if->dev, apsta_params->acs,
+                               auto_band);
+               }
+               channel = wl_ext_move_cur_channel(apsta_params, cur_if);
                if (channel)
-                       wl_ext_move_other_channel(apsta_params,
-                               apsta_params->if_info[IF_PIF].dev, cur_if);
+                       wl_ext_move_other_channel(apsta_params, cur_if);
+               if (cur_if->ifmode == ISTA_MODE)
+                       wl_set_isam_status(cur_if, STA_CONNECTING);
        }
 
        return channel;
 }
 
-#ifdef WL_CFG80211
+static int
+wl_ext_iftype_to_ifmode(struct net_device *net, int wl_iftype, ifmode_t *ifmode)
+{      
+       switch (wl_iftype) {
+               case WL_IF_TYPE_STA:
+                       *ifmode = ISTA_MODE;
+                       break;
+               case WL_IF_TYPE_AP:
+                       *ifmode = IAP_MODE;
+                       break;
+               case WL_IF_TYPE_P2P_GO:
+                       *ifmode = IGO_MODE;
+                       break;
+               case WL_IF_TYPE_P2P_GC:
+                       *ifmode = IGC_MODE;
+                       break;
+               default:
+                       AEXT_ERROR(net->name, "Unknown interface wl_iftype:0x%x\n", wl_iftype);
+                       return BCME_ERROR;
+       }
+       return BCME_OK;
+}
+
 void
 wl_ext_iapsta_update_iftype(struct net_device *net, int ifidx, int wl_iftype)
 {
@@ -3216,16 +4433,25 @@ wl_ext_iapsta_update_iftype(struct net_device *net, int ifidx, int wl_iftype)
                        cur_if->ifmode = ISTA_MODE;
                        cur_if->prio = PRIO_STA;
                        cur_if->prefix = 'S';
-               } else if (wl_iftype == WL_IF_TYPE_AP) {
+               } else if (wl_iftype == WL_IF_TYPE_AP && cur_if->ifmode != IMESH_MODE) {
                        cur_if->ifmode = IAP_MODE;
                        cur_if->prio = PRIO_AP;
                        cur_if->prefix = 'A';
+               } else if (wl_iftype == WL_IF_TYPE_P2P_GO) {
+                       cur_if->ifmode = IGO_MODE;
+                       cur_if->prio = PRIO_AP;
+                       cur_if->prefix = 'P';
+                       apsta_params->vsdb = TRUE;
+               } else if (wl_iftype == WL_IF_TYPE_P2P_GC) {
+                       cur_if->ifmode = IGC_MODE;
+                       cur_if->prio = PRIO_STA;
+                       cur_if->prefix = 'P';
+                       apsta_params->vsdb = TRUE;
+                       wl_ext_iovar_setint(cur_if->dev, "assoc_retry_max", 3);
                }
        }
 }
-#endif
 
-#ifdef WL_STATIC_IF
 void
 wl_ext_iapsta_ifadding(struct net_device *net, int ifidx)
 {
@@ -3239,7 +4465,144 @@ wl_ext_iapsta_ifadding(struct net_device *net, int ifidx)
                wl_set_isam_status(cur_if, IF_ADDING);
        }
 }
-#endif
+
+bool
+wl_ext_iapsta_iftype_enabled(struct net_device *net, int wl_iftype)
+{
+       struct dhd_pub *dhd = dhd_get_pub(net);
+       struct wl_apsta_params *apsta_params = dhd->iapsta_params;
+       struct wl_if_info *cur_if = NULL;
+       ifmode_t ifmode = 0;
+
+       wl_ext_iftype_to_ifmode(net, wl_iftype, &ifmode);
+       cur_if = wl_ext_if_enabled(apsta_params, ifmode);
+       if (cur_if)
+               return TRUE;
+
+       return FALSE;
+}
+
+bool
+wl_ext_iapsta_mesh_creating(struct net_device *net)
+{
+       struct dhd_pub *dhd = dhd_get_pub(net);
+       struct wl_apsta_params *apsta_params = dhd->iapsta_params;
+       struct wl_if_info *cur_if;
+       int i;
+
+       if (apsta_params) {
+               for (i=0; i<MAX_IF_NUM; i++) {
+                       cur_if = &apsta_params->if_info[i];
+                       if (cur_if->ifmode==IMESH_MODE && wl_get_isam_status(cur_if, IF_ADDING))
+                               return TRUE;
+               }
+       }
+       return FALSE;
+}
+#endif /* WL_CFG80211 */
+
+int
+wl_ext_iapsta_alive_preinit(struct net_device *dev)
+{
+       struct dhd_pub *dhd = dhd_get_pub(dev);
+       struct wl_apsta_params *apsta_params = dhd->iapsta_params;
+
+       if (apsta_params->init == TRUE) {
+               AEXT_ERROR(dev->name, "don't init twice\n");
+               return -1;
+       }
+
+       AEXT_TRACE(dev->name, "Enter\n");
+
+       apsta_params->init = TRUE;
+
+       return 0;
+}
+
+int
+wl_ext_iapsta_alive_postinit(struct net_device *dev)
+{
+       struct dhd_pub *dhd = dhd_get_pub(dev);
+       struct wl_apsta_params *apsta_params = dhd->iapsta_params;
+       s32 apsta = 0, ap = 0;
+       struct wl_if_info *cur_if;
+       int i;
+
+       wl_ext_iovar_getint(dev, "apsta", &apsta);
+       wl_ext_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap), 0);
+       if (apsta == 1 || ap == 0) {
+               apsta_params->apstamode = ISTAONLY_MODE;
+               apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE;
+               op_mode = DHD_FLAG_STA_MODE;
+       } else {
+               apsta_params->apstamode = IAPONLY_MODE;
+               apsta_params->if_info[IF_PIF].ifmode = IAP_MODE;
+               op_mode = DHD_FLAG_HOSTAP_MODE;
+       }
+       // fix me: how to check it's ISTAAP_MODE or IDUALAP_MODE?
+
+       wl_ext_get_ioctl_ver(dev, &apsta_params->ioctl_ver);
+       WL_MSG(dev->name, "apstamode=%d\n", apsta_params->apstamode);
+
+       for (i=0; i<MAX_IF_NUM; i++) {
+               cur_if = &apsta_params->if_info[i];
+               if (i == 1 && !strlen(cur_if->ifname))
+                       strcpy(cur_if->ifname, "wlan1");
+               if (i == 2 && !strlen(cur_if->ifname))
+                       strcpy(cur_if->ifname, "wlan2");
+               if (cur_if->ifmode == ISTA_MODE) {
+                       cur_if->channel = 0;
+                       cur_if->maxassoc = -1;
+                       wl_set_isam_status(cur_if, IF_READY);
+                       cur_if->prio = PRIO_STA;
+                       cur_if->prefix = 'S';
+                       snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_sta");
+               } else if (cur_if->ifmode == IAP_MODE) {
+                       cur_if->channel = 1;
+                       cur_if->maxassoc = -1;
+                       wl_set_isam_status(cur_if, IF_READY);
+                       cur_if->prio = PRIO_AP;
+                       cur_if->prefix = 'A';
+                       snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_ap");
+#ifdef WLMESH
+               } else if (cur_if->ifmode == IMESH_MODE) {
+                       cur_if->channel = 1;
+                       cur_if->maxassoc = -1;
+                       wl_set_isam_status(cur_if, IF_READY);
+                       cur_if->prio = PRIO_MESH;
+                       cur_if->prefix = 'M';
+                       snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_mesh");
+#endif /* WLMESH */
+               }
+       }
+
+       return op_mode;
+}
+
+static int
+wl_ext_iapsta_get_rsdb(struct net_device *net, struct dhd_pub *dhd)
+{
+       s8 iovar_buf[WLC_IOCTL_SMLEN];
+       wl_config_t *rsdb_p;
+       int ret = 0, rsdb = 0;
+
+       if (dhd->conf->chip == BCM4359_CHIP_ID) {
+               ret = wldev_iovar_getbuf(net, "rsdb_mode", NULL, 0,
+                       iovar_buf, WLC_IOCTL_SMLEN, NULL);
+               if (!ret) {
+                       if (dhd->conf->fw_type == FW_TYPE_MESH) {
+                               rsdb = 1;
+                       } else {
+                               rsdb_p = (wl_config_t *) iovar_buf;
+                               rsdb = rsdb_p->config;
+                       }
+               }
+       }
+
+       AEXT_INFO(net->name, "rsdb_mode=%d\n", rsdb);
+
+       return rsdb;
+}
 
 static void
 wl_ext_iapsta_postinit(struct net_device *net, struct wl_if_info *cur_if)
@@ -3250,10 +4613,11 @@ wl_ext_iapsta_postinit(struct net_device *net, struct wl_if_info *cur_if)
 
        AEXT_TRACE(cur_if->ifname, "ifidx=%d\n", cur_if->ifidx);
        if (cur_if->ifidx == 0) {
-               apsta_params->csa |= CSA_FW_BIT;
-               if (dhd->conf->chip == BCM4359_CHIP_ID) {
-                       apsta_params->rsdb = TRUE;
-               }
+               apsta_params->rsdb = wl_ext_iapsta_get_rsdb(net, dhd);
+               apsta_params->vsdb = FALSE;
+               apsta_params->csa = 0;
+               apsta_params->acs = 0;
+               apsta_params->radar = wl_ext_radar_detect(net);
                if (dhd->conf->fw_type == FW_TYPE_MESH) {
                        apsta_params->csa |= (CSA_FW_BIT | CSA_DRV_BIT);
                }
@@ -3266,15 +4630,18 @@ wl_ext_iapsta_postinit(struct net_device *net, struct wl_if_info *cur_if)
                        else
                                pm = PM_FAST;
                        wl_ext_ioctl(cur_if->dev, WLC_SET_PM, &pm, sizeof(pm), 1);
-                       wl_ext_iovar_setint(cur_if->dev, "assoc_retry_max", 30);
+                       wl_ext_iovar_setint(cur_if->dev, "assoc_retry_max", 20);
                }
 #ifdef WLMESH
                else if (cur_if->ifmode == IMESH_MODE) {
                        pm = 0;
                        wl_ext_ioctl(cur_if->dev, WLC_SET_PM, &pm, sizeof(pm), 1);
                }
-#endif
+#endif /* WLMESH */
        }
+#ifdef PROPTX_MAXCOUNT
+       wl_ext_update_wlfc_maxcount(dhd);
+#endif /* PROPTX_MAXCOUNT */
 
 }
 
@@ -3300,7 +4667,43 @@ wl_ext_iapsta_attach_name(struct net_device *net, int ifidx)
                wl_set_isam_status(cur_if, IF_READY);
 #ifndef WL_STATIC_IF
                wake_up_interruptible(&apsta_params->netif_change_event);
-#endif
+#endif /* WL_STATIC_IF */
+       }
+
+       return 0;
+}
+
+int
+wl_ext_iapsta_update_net_device(struct net_device *net, int ifidx)
+{
+       struct dhd_pub *dhd = dhd_get_pub(net);
+       struct wl_apsta_params *apsta_params = dhd->iapsta_params;
+       struct wl_if_info *cur_if = NULL, *primary_if;
+
+       AEXT_TRACE(net->name, "ifidx=%d\n", ifidx);
+       if (ifidx < MAX_IF_NUM) {
+               cur_if = &apsta_params->if_info[ifidx];
+       }
+       if (cur_if && wl_get_isam_status(cur_if, IF_ADDING)) {
+               primary_if = &apsta_params->if_info[IF_PIF];
+               if (strlen(cur_if->ifname)) {
+                       memset(net->name, 0, sizeof(IFNAMSIZ));
+                       strcpy(net->name, cur_if->ifname);
+                       net->name[IFNAMSIZ-1] = '\0';
+               }
+#ifndef WL_STATIC_IF
+               if (apsta_params->apstamode != IUNKNOWN_MODE &&
+                               apsta_params->apstamode != ISTAAPAP_MODE &&
+                               apsta_params->apstamode != ISTASTA_MODE) {
+                       memcpy(net->dev_addr, primary_if->dev->dev_addr, ETHER_ADDR_LEN);
+                       net->dev_addr[0] |= 0x02;
+                       if (ifidx >= 2) {
+                               net->dev_addr[4] ^= 0x80;
+                               net->dev_addr[4] += ifidx;
+                               net->dev_addr[5] += (ifidx-1);
+                       }
+               }
+#endif /* WL_STATIC_IF */
        }
 
        return 0;
@@ -3320,7 +4723,6 @@ wl_ext_iapsta_attach_netdev(struct net_device *net, int ifidx, uint8 bssidx)
        if (ifidx == 0) {
                memset(apsta_params, 0, sizeof(struct wl_apsta_params));
                apsta_params->dhd = dhd;
-               apsta_params->vsdb = FALSE;
                cur_if->dev = net;
                cur_if->ifidx = ifidx;
                cur_if->bssidx = bssidx;
@@ -3332,30 +4734,22 @@ wl_ext_iapsta_attach_netdev(struct net_device *net, int ifidx, uint8 bssidx)
                strcpy(cur_if->ifname, net->name);
                init_waitqueue_head(&apsta_params->netif_change_event);
                mutex_init(&apsta_params->usr_sync);
+               mutex_init(&cur_if->pm_sync);
+               INIT_DELAYED_WORK(&cur_if->pm_enable_work, wl_ext_pm_work_handler);
        } else if (cur_if && wl_get_isam_status(cur_if, IF_ADDING)) {
                primary_if = &apsta_params->if_info[IF_PIF];
                cur_if->dev = net;
-               cur_if->ifidx = ifidx;
-               cur_if->bssidx = bssidx;
-               wl_ext_event_register(net, dhd, WLC_E_LAST, wl_ext_iapsta_event,
-                       apsta_params, PRIO_EVENT_IAPSTA);
-               if (strlen(cur_if->ifname)) {
-                       memset(net->name, 0, sizeof(IFNAMSIZ));
-                       strcpy(net->name, cur_if->ifname);
-                       net->name[IFNAMSIZ-1] = '\0';
-               }
-#ifndef WL_STATIC_IF
-               if (apsta_params->apstamode != ISTAAPAP_MODE &&
-                               apsta_params->apstamode != ISTASTA_MODE) {
-                       memcpy(net->dev_addr, primary_if->dev->dev_addr, ETHER_ADDR_LEN);
-                       net->dev_addr[0] |= 0x02;
-                       if (ifidx >= 2) {
-                               net->dev_addr[4] ^= 0x80;
-                               net->dev_addr[4] += ifidx;
-                               net->dev_addr[5] += (ifidx-1);
-                       }
+               cur_if->ifidx = ifidx;
+               cur_if->bssidx = bssidx;
+               wl_ext_event_register(net, dhd, WLC_E_LAST, wl_ext_iapsta_event,
+                       apsta_params, PRIO_EVENT_IAPSTA);
+#if defined(WLMESH) && defined(WL_ESCAN)
+               if (cur_if->ifmode == IMESH_MODE && apsta_params->macs) {
+                       wl_mesh_escan_attach(dhd, cur_if);
                }
-#endif
+#endif /* WLMESH && WL_ESCAN */
+               mutex_init(&cur_if->pm_sync);
+               INIT_DELAYED_WORK(&cur_if->pm_enable_work, wl_ext_pm_work_handler);
        }
 
        return 0;
@@ -3377,10 +4771,23 @@ wl_ext_iapsta_dettach_netdev(struct net_device *net, int ifidx)
        }
 
        if (ifidx == 0) {
+               wl_ext_add_remove_pm_enable_work(net, FALSE);
                wl_ext_event_deregister(net, dhd, WLC_E_LAST, wl_ext_iapsta_event);
+#if defined(WLMESH) && defined(WL_ESCAN)
+               if (cur_if->ifmode == IMESH_MODE && apsta_params->macs) {
+                       wl_mesh_escan_detach(dhd, cur_if);
+               }
+#endif /* WLMESH && WL_ESCAN */
                memset(apsta_params, 0, sizeof(struct wl_apsta_params));
-       } else if (cur_if && wl_get_isam_status(cur_if, IF_READY)) {
+       } else if (cur_if && (wl_get_isam_status(cur_if, IF_READY) ||
+                       wl_get_isam_status(cur_if, IF_ADDING))) {
+               wl_ext_add_remove_pm_enable_work(net, FALSE);
                wl_ext_event_deregister(net, dhd, WLC_E_LAST, wl_ext_iapsta_event);
+#if defined(WLMESH) && defined(WL_ESCAN)
+               if (cur_if->ifmode == IMESH_MODE && apsta_params->macs) {
+                       wl_mesh_escan_detach(dhd, cur_if);
+               }
+#endif /* WLMESH && WL_ESCAN */
                memset(cur_if, 0, sizeof(struct wl_if_info));
        }
 
@@ -3410,7 +4817,7 @@ wl_ext_iapsta_dettach(dhd_pub_t *pub)
                pub->iapsta_params = NULL;
        }
 }
-#endif
+#endif /* WL_EXT_IAPSTA */
 
 #ifdef IDHCP
 /*
@@ -3518,7 +4925,7 @@ wl_ext_dhcpc_param(struct net_device *dev, char *data, char *command,
        exit:
                return ret;
 }
-#endif
+#endif /* IDHCP */
 
 int
 wl_ext_mkeep_alive(struct net_device *dev, char *data, char *command,
@@ -3536,7 +4943,7 @@ wl_ext_mkeep_alive(struct net_device *dev, char *data, char *command,
                        AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
                        goto exit;
                }
-               packet = kmalloc(total_len, GFP_KERNEL);
+               packet = kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
                if (packet == NULL) {
                        AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
                        goto exit;
@@ -3551,7 +4958,7 @@ wl_ext_mkeep_alive(struct net_device *dev, char *data, char *command,
                        if (id < 0)
                                id = 0;
                        ret = wl_ext_iovar_getbuf(dev, "mkeep_alive", &id, sizeof(id), buf,
-                               sizeof(buf), NULL);
+                               total_len, NULL);
                        if (!ret) {
                                mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) buf;
                                bytes_written += snprintf(command+bytes_written, total_len,
@@ -3598,10 +5005,11 @@ wl_ext_tcpka_conn_add(struct net_device *dev, char *data, char *command,
                memset(src_ip, 0, sizeof(src_ip));
                memset(dst_ip, 0, sizeof(dst_ip));
                memset(ka_payload, 0, sizeof(ka_payload));
-               sscanf(data, "%d %s %s %s %d %d %d %u %u %d %u %u %u %u %32s",
+               sscanf(data, "%d %s %s %s %d %d %d %u %u %d %u %u %u %32s",
                        &sess_id, dst_mac, src_ip, dst_ip, &ipid, &srcport, &dstport, &seq,
-                       &ack, &tcpwin, &tsval, &tsecr, &len, &ka_payload_len, ka_payload);
+                       &ack, &tcpwin, &tsval, &tsecr, &len, ka_payload);
 
+               ka_payload_len = strlen(ka_payload) / 2;
                tcpka = kmalloc(sizeof(struct tcpka_conn) + ka_payload_len, GFP_KERNEL);
                if (tcpka == NULL) {
                        AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n",
@@ -3632,8 +5040,12 @@ wl_ext_tcpka_conn_add(struct net_device *dev, char *data, char *command,
                tcpka->tsval = tsval;
                tcpka->tsecr = tsecr;
                tcpka->len = len;
+               ka_payload_len = wl_pattern_atoh(ka_payload, (char *)tcpka->ka_payload);
+               if (ka_payload_len == -1) {
+                       AEXT_ERROR(dev->name,"rejecting ka_payload=%s\n", ka_payload);
+                       goto exit;
+               }
                tcpka->ka_payload_len = ka_payload_len;
-               strncpy(tcpka->ka_payload, ka_payload, ka_payload_len);
 
                AEXT_INFO(dev->name,
                        "tcpka_conn_add %d %pM %pM %pM %d %d %d %u %u %d %u %u %u %u \"%s\"\n",
@@ -3659,7 +5071,7 @@ wl_ext_tcpka_conn_enable(struct net_device *dev, char *data, char *command,
 {
        s8 iovar_buf[WLC_IOCTL_SMLEN];
        tcpka_conn_sess_t tcpka_conn;
-       int ret;
+       int ret = 0;
        uint32 sess_id = 0, flag, interval = 0, retry_interval = 0, retry_count = 0;
 
        if (data) {
@@ -3697,7 +5109,7 @@ wl_ext_tcpka_conn_info(struct net_device *dev, char *data, char *command,
        s8 iovar_buf[WLC_IOCTL_SMLEN];
        tcpka_conn_sess_info_t *info = NULL;
        uint32 sess_id = 0;
-       int ret = 0;
+       int ret = 0, bytes_written = 0;
 
        if (data) {
                sscanf(data, "%d", &sess_id);
@@ -3706,15 +5118,20 @@ wl_ext_tcpka_conn_info(struct net_device *dev, char *data, char *command,
                        sizeof(uint32), iovar_buf, sizeof(iovar_buf), NULL);
                if (!ret) {
                        info = (tcpka_conn_sess_info_t *) iovar_buf;
-                       ret = snprintf(command, total_len, "id=%d, ipid=%d, seq=%u, ack=%u",
+                       bytes_written += snprintf(command+bytes_written, total_len,
+                               "id   :%d\n"
+                               "ipid :%d\n"
+                               "seq  :%u\n"
+                               "ack  :%u",
                                sess_id, info->ipid, info->seq, info->ack);
                        AEXT_INFO(dev->name, "%s\n", command);
+                       ret = bytes_written;
                }
        }
 
        return ret;
 }
-#endif
+#endif /* WL_EXT_TCPKA */
 
 static int
 wl_ext_rsdb_mode(struct net_device *dev, char *data, char *command,
@@ -4047,67 +5464,9 @@ wl_ext_pkt_filter_enable(struct net_device *dev, char *data, char *command,
 
        return err;
 }
-#endif
+#endif /* PKT_FILTER_SUPPORT */
 
 #ifdef SENDPROB
-static int
-wl_ext_add_del_ie(struct net_device *dev, uint pktflag, char *ie_data, const char* add_del_cmd)
-{
-       vndr_ie_setbuf_t *vndr_ie = NULL;
-       char iovar_buf[WLC_IOCTL_SMLEN]="\0";
-       int ie_data_len = 0, tot_len = 0, iecount;
-       int err = -1;
-
-       if (!strlen(ie_data)) {
-               AEXT_ERROR(dev->name, "wrong ie %s\n", ie_data);
-               goto exit;
-       }
-
-       tot_len = (int)(sizeof(vndr_ie_setbuf_t) + ((strlen(ie_data)-2)/2));
-       vndr_ie = (vndr_ie_setbuf_t *) kzalloc(tot_len, GFP_KERNEL);
-       if (!vndr_ie) {
-               AEXT_ERROR(dev->name, "IE memory alloc failed\n");
-               err = -ENOMEM;
-               goto exit;
-       }
-
-       /* Copy the vndr_ie SET command ("add"/"del") to the buffer */
-       strncpy(vndr_ie->cmd, add_del_cmd, VNDR_IE_CMD_LEN - 1);
-       vndr_ie->cmd[VNDR_IE_CMD_LEN - 1] = '\0';
-
-       /* Set the IE count - the buffer contains only 1 IE */
-       iecount = htod32(1);
-       memcpy((void *)&vndr_ie->vndr_ie_buffer.iecount, &iecount, sizeof(s32));
-
-       /* Set packet flag to indicate that BEACON's will contain this IE */
-       pktflag = htod32(pktflag);
-       memcpy((void *)&vndr_ie->vndr_ie_buffer.vndr_ie_list[0].pktflag, &pktflag,
-               sizeof(u32));
-
-       /* Set the IE ID */
-       vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.id = (uchar)DOT11_MNG_VS_ID;
-
-       /* Set the IE LEN */
-       vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len = (strlen(ie_data)-2)/2;
-
-       /* Set the IE OUI and DATA */
-       ie_data_len = wl_pattern_atoh(ie_data,
-               (char *)vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui);
-       if (ie_data_len <= 0) {
-               AEXT_ERROR(dev->name, "wrong ie_data_len %d\n", strlen(ie_data)-2);
-               goto exit;
-       }
-
-       err = wl_ext_iovar_setbuf(dev, "vndr_ie", vndr_ie, tot_len, iovar_buf,
-               sizeof(iovar_buf), NULL);
-
-exit:
-       if (vndr_ie) {
-               kfree(vndr_ie);
-       }
-       return err;
-}
-
 static int
 wl_ext_send_probreq(struct net_device *dev, char *data, char *command,
        int total_len)
@@ -4276,7 +5635,7 @@ wl_ext_recv_probresp(struct net_device *dev, char *data, char *command,
 exit:
     return err;
 }
-#endif
+#endif /* SENDPROB */
 
 static int
 wl_ext_gtk_key_info(struct net_device *dev, char *data, char *command, int total_len)
@@ -4532,7 +5891,182 @@ wl_ext_wowl_wakeind(struct net_device *dev, char *data, char *command,
 
        return ret;
 }
-#endif
+#endif /* WL_EXT_WOWL */
+
+#ifdef WL_GPIO_NOTIFY
+typedef struct notify_payload {
+       int index;
+       int len;
+       char payload[128];
+} notify_payload_t;
+
+static int
+wl_ext_gpio_notify(struct net_device *dev, char *data, char *command,
+       int total_len)
+{
+       s8 iovar_buf[WLC_IOCTL_SMLEN];
+       notify_payload_t notify, *pnotify = NULL;
+       int i, ret = 0, bytes_written = 0;
+       char frame_str[WLC_IOCTL_SMLEN+3];
+
+       if (data) {
+               memset(&notify, 0, sizeof(notify));
+               memset(frame_str, 0, sizeof(frame_str));
+               sscanf(data, "%d %s", &notify.index, frame_str);
+
+               if (notify.index < 0)
+                       notify.index = 0;
+
+               if (strlen(frame_str)) {
+                       notify.len = wl_pattern_atoh(frame_str, notify.payload);
+                       if (notify.len == -1) {
+                               AEXT_ERROR(dev->name, "rejecting pattern=%s\n", frame_str);
+                               goto exit;
+                       }
+                       AEXT_INFO(dev->name, "index=%d, len=%d\n", notify.index, notify.len);
+                       if (android_msg_level & ANDROID_INFO_LEVEL)
+                               prhex("payload", (uchar *)notify.payload, notify.len);
+                       ret = wl_ext_iovar_setbuf(dev, "bcol_gpio_noti", (char *)&notify,
+                               sizeof(notify), iovar_buf, WLC_IOCTL_SMLEN, NULL);
+               } else {
+                       AEXT_INFO(dev->name, "index=%d\n", notify.index);
+                       ret = wl_ext_iovar_getbuf(dev, "bcol_gpio_noti", &notify.index,
+                               sizeof(notify.index), iovar_buf, sizeof(iovar_buf), NULL);
+                       if (!ret) {
+                               pnotify = (notify_payload_t *)iovar_buf;
+                               bytes_written += snprintf(command+bytes_written, total_len,
+                                       "Id            :%d\n"
+                                       "Packet        :0x",
+                                       pnotify->index);
+                               for (i=0; i<pnotify->len; i++) {
+                                       bytes_written += snprintf(command+bytes_written, total_len,
+                                               "%02x", pnotify->payload[i]);
+                               }
+                               AEXT_TRACE(dev->name, "command result is\n%s\n", command);
+                               ret = bytes_written;
+                       }
+               }
+       }
+
+exit:
+       return ret;
+}
+#endif /* WL_GPIO_NOTIFY */
+
+#ifdef CSI_SUPPORT
+typedef struct csi_config {
+       /* Peer device mac address. */
+       struct ether_addr addr;
+       /* BW to be used in the measurements. This needs to be supported both by the */
+       /* device itself and the peer. */
+       uint32 bw;
+       /* Time interval between measurements (units: 1 ms). */
+       uint32 period;
+       /* CSI method */
+       uint32 method;
+} csi_config_t;
+
+typedef struct csi_list {
+       uint32 cnt;
+       csi_config_t configs[1];
+} csi_list_t;
+
+static int
+wl_ether_atoe(const char *a, struct ether_addr *n)
+{
+       char *c = NULL;
+       int i = 0;
+
+       memset(n, 0, ETHER_ADDR_LEN);
+       for (;;) {
+               n->octet[i++] = (uint8)strtoul(a, &c, 16);
+               if (!*c++ || i == ETHER_ADDR_LEN)
+                       break;
+               a = c;
+       }
+       return (i == ETHER_ADDR_LEN);
+}
+
+static int
+wl_ext_csi(struct net_device *dev, char *data, char *command, int total_len)
+{
+       csi_config_t csi, *csip;
+       csi_list_t *csi_list;
+       int ret = -1, period=-1, i;
+       char mac[32], *buf = NULL;
+       struct ether_addr ea;
+       int bytes_written = 0;
+
+       buf = kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
+       if (buf == NULL) {
+               AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
+               goto exit;
+       }
+       memset(buf, 0, WLC_IOCTL_SMLEN);
+
+       if (data) {
+               sscanf(data, "%s %d", mac, &period);
+               ret = wl_ether_atoe(mac, &ea);
+               if (!ret) {
+                       AEXT_ERROR(dev->name, "rejecting mac=%s, ret=%d\n", mac, ret);
+                       goto exit;
+               }
+               AEXT_TRACE(dev->name, "mac=%pM, period=%d", &ea, period);
+               if (period > 0) {
+                       memset(&csi, 0, sizeof(csi_config_t));
+                       bcopy(&ea, &csi.addr, ETHER_ADDR_LEN);
+                       csi.period = period;
+                       ret = wl_ext_iovar_setbuf(dev, "csi", (char *)&csi, sizeof(csi),
+                               buf, WLC_IOCTL_SMLEN, NULL);
+               } else if (period == 0) {
+                       memset(&csi, 0, sizeof(csi_config_t));
+                       bcopy(&ea, &csi.addr, ETHER_ADDR_LEN);
+                       ret = wl_ext_iovar_setbuf(dev, "csi_del", (char *)&csi, sizeof(csi),
+                               buf, WLC_IOCTL_SMLEN, NULL);
+               } else {
+                       ret = wl_ext_iovar_getbuf(dev, "csi", &ea, ETHER_ADDR_LEN, buf,
+                               WLC_IOCTL_SMLEN, NULL);
+                       if (!ret) {
+                               csip = (csi_config_t *) buf;
+                                       /* Dump all lists */
+                               bytes_written += snprintf(command+bytes_written, total_len,
+                                       "Mac    :%pM\n"
+                                       "Period :%d\n"
+                                       "BW     :%d\n"
+                                       "Method :%d\n",
+                                       &csip->addr, csip->period, csip->bw, csip->method);
+                               AEXT_TRACE(dev->name, "command result is %s\n", command);
+                               ret = bytes_written;
+                       }
+               }
+       }
+       else {
+               ret = wl_ext_iovar_getbuf(dev, "csi_list", NULL, 0, buf, WLC_IOCTL_SMLEN, NULL);
+               if (!ret) {
+                       csi_list = (csi_list_t *)buf;
+                       bytes_written += snprintf(command+bytes_written, total_len,
+                               "Total number :%d\n", csi_list->cnt);
+                       for (i=0; i<csi_list->cnt; i++) {
+                               csip = &csi_list->configs[i];
+                               bytes_written += snprintf(command+bytes_written, total_len,
+                                       "Idx    :%d\n"
+                                       "Mac    :%pM\n"
+                                       "Period :%d\n"
+                                       "BW     :%d\n"
+                                       "Method :%d\n\n",
+                                       i+1, &csip->addr, csip->period, csip->bw, csip->method);
+                       }
+                       AEXT_TRACE(dev->name, "command result is %s\n", command);
+                       ret = bytes_written;
+               }
+       }
+
+exit:
+       if (buf)
+               kfree(buf);
+       return ret;
+}
+#endif /* CSI_SUPPORT */
 
 typedef int (wl_ext_tpl_parse_t)(struct net_device *dev, char *data, char *command,
        int total_len);
@@ -4554,29 +6088,35 @@ const wl_ext_iovar_tpl_t wl_ext_iovar_tpl_list[] = {
        {WLC_GET_VAR,   WLC_SET_VAR,    "pkt_filter_add",               wl_ext_pkt_filter_add},
        {WLC_GET_VAR,   WLC_SET_VAR,    "pkt_filter_delete",    wl_ext_pkt_filter_delete},
        {WLC_GET_VAR,   WLC_SET_VAR,    "pkt_filter_enable",    wl_ext_pkt_filter_enable},
-#endif
+#endif /* PKT_FILTER_SUPPORT */
 #if defined(WL_EXT_IAPSTA) && defined(WLMESH)
        {WLC_GET_VAR,   WLC_SET_VAR,    "mesh_peer_status",     wl_ext_mesh_peer_status},
-#endif
+#endif /* WL_EXT_IAPSTA && WLMESH */
 #ifdef SENDPROB
        {WLC_GET_VAR,   WLC_SET_VAR,    "send_probreq",         wl_ext_send_probreq},
        {WLC_GET_VAR,   WLC_SET_VAR,    "send_probresp",        wl_ext_send_probresp},
        {WLC_GET_VAR,   WLC_SET_VAR,    "recv_probreq",         wl_ext_recv_probreq},
        {WLC_GET_VAR,   WLC_SET_VAR,    "recv_probresp",        wl_ext_recv_probresp},
-#endif
+#endif /* SENDPROB */
 #ifdef WL_EXT_TCPKA
        {WLC_GET_VAR,   WLC_SET_VAR,    "tcpka_conn_add",               wl_ext_tcpka_conn_add},
        {WLC_GET_VAR,   WLC_SET_VAR,    "tcpka_conn_enable",    wl_ext_tcpka_conn_enable},
        {WLC_GET_VAR,   WLC_SET_VAR,    "tcpka_conn_sess_info", wl_ext_tcpka_conn_info},
-#endif
+#endif /* WL_EXT_TCPKA */
 #ifdef WL_EXT_WOWL
        {WLC_GET_VAR,   WLC_SET_VAR,    "wowl_pattern",         wl_ext_wowl_pattern},
        {WLC_GET_VAR,   WLC_SET_VAR,    "wowl_wakeind",         wl_ext_wowl_wakeind},
-#endif
+#endif /* WL_EXT_WOWL */
 #ifdef IDHCP
        {WLC_GET_VAR,   WLC_SET_VAR,    "dhcpc_dump",           wl_ext_dhcpc_dump},
        {WLC_GET_VAR,   WLC_SET_VAR,    "dhcpc_param",          wl_ext_dhcpc_param},
-#endif
+#endif /* IDHCP */
+#ifdef WL_GPIO_NOTIFY
+       {WLC_GET_VAR,   WLC_SET_VAR,    "bcol_gpio_noti",       wl_ext_gpio_notify},
+#endif /* WL_GPIO_NOTIFY */
+#ifdef CSI_SUPPORT
+       {WLC_GET_VAR,   WLC_SET_VAR,    "csi",                          wl_ext_csi},
+#endif /* CSI_SUPPORT */
 };
 
 /*
@@ -4703,17 +6243,25 @@ wl_android_ext_priv_cmd(struct net_device *net, char *command,
        else if (strnicmp(command, CMD_ISAM_STATUS, strlen(CMD_ISAM_STATUS)) == 0) {
                *bytes_written = wl_ext_isam_status(net, command, total_len);
        }
-#endif
+       else if (strnicmp(command, CMD_ISAM_PARAM, strlen(CMD_ISAM_PARAM)) == 0) {
+               *bytes_written = wl_ext_isam_param(net, command, total_len);
+       }
+#if defined(WLMESH) && defined(WL_ESCAN)
+       else if (strnicmp(command, CMD_ISAM_PEER_PATH, strlen(CMD_ISAM_PEER_PATH)) == 0) {
+               *bytes_written = wl_ext_isam_peer_path(net, command, total_len);
+       }
+#endif /* WLMESH && WL_ESCAN */
+#endif /* WL_EXT_IAPSTA */
 #ifdef WL_CFG80211
        else if (strnicmp(command, CMD_AUTOCHANNEL, strlen(CMD_AUTOCHANNEL)) == 0) {
                *bytes_written = wl_cfg80211_autochannel(net, command, total_len);
        }
-#endif
+#endif /* WL_CFG80211 */
 #if defined(WL_WIRELESS_EXT) && defined(WL_ESCAN)
        else if (strnicmp(command, CMD_AUTOCHANNEL, strlen(CMD_AUTOCHANNEL)) == 0) {
                *bytes_written = wl_iw_autochannel(net, command, total_len);
        }
-#endif
+#endif /* WL_WIRELESS_EXT && WL_ESCAN */
        else if (strnicmp(command, CMD_WL, strlen(CMD_WL)) == 0) {
                *bytes_written = wl_ext_wl_iovar(net, command, total_len);
        }
@@ -4776,7 +6324,7 @@ wl_ext_get_best_channel(struct net_device *net,
        wl_bss_cache_ctrl_t *bss_cache_ctrl,
 #else
        struct wl_scan_results *bss_list,
-#endif
+#endif /* BSSCACHE */
        int ioctl_ver, int *best_2g_ch, int *best_5g_ch
 )
 {
@@ -4784,13 +6332,14 @@ wl_ext_get_best_channel(struct net_device *net,
        s32 i, j;
 #if defined(BSSCACHE)
        wl_bss_cache_t *node;
-#endif
+#endif /* BSSCACHE */
        int b_band[CH_MAX_2G_CHANNEL]={0}, a_band1[4]={0}, a_band4[5]={0};
        s32 cen_ch, distance, distance_2g, distance_5g, ch, min_ap=999;
        u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
        wl_uint32_list_t *list;
        int ret;
        chanspec_t chanspec;
+       struct dhd_pub *dhd = dhd_get_pub(net);
 
        memset(b_band, -1, sizeof(b_band));
        memset(a_band1, -1, sizeof(a_band1));
@@ -4807,6 +6356,8 @@ wl_ext_get_best_channel(struct net_device *net,
        } else {
                for (i = 0; i < dtoh32(list->count); i++) {
                        ch = dtoh32(list->element[i]);
+                       if (!dhd_conf_match_channel(dhd, ch))
+                               continue;
                        if (ch < CH_MAX_2G_CHANNEL)
                                b_band[ch-1] = 0;
                        else if (ch <= 48)
@@ -4824,13 +6375,13 @@ wl_ext_get_best_channel(struct net_device *net,
        for (i=0; node && i<256; i++)
 #else
        for (i=0; i < bss_list->count; i++)
-#endif
+#endif /* BSSCACHE */
        {
 #if defined(BSSCACHE)
                bi = node->results.bss_info;
 #else
                bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : bss_list->bss_info;
-#endif
+#endif /* BSSCACHE */
                chanspec = wl_ext_chspec_driver_to_host(ioctl_ver, bi->chanspec);
                cen_ch = CHSPEC_CHANNEL(bi->chanspec);
                distance = 0;
@@ -4847,7 +6398,7 @@ wl_ext_get_best_channel(struct net_device *net,
                        distance += distance_2g;
                        for (j=0; j<ARRAYSIZE(b_band); j++) {
                                if (b_band[j] >= 0 && abs(cen_ch-(1+j)) <= distance)
-                               b_band[j] += 1;
+                                       b_band[j] += 1;
                        }
                } else {
                        distance += distance_5g;
@@ -4865,7 +6416,7 @@ wl_ext_get_best_channel(struct net_device *net,
                }
 #if defined(BSSCACHE)
                node = node->next;
-#endif
+#endif /* BSSCACHE */
        }
 
        *best_2g_ch = 0;
@@ -4920,8 +6471,234 @@ wl_ext_get_best_channel(struct net_device *net,
 exit:
        return 0;
 }
+#endif /* WL_CFG80211 || WL_ESCAN */
+
+#ifdef WL_CFG80211
+#define APCS_MAX_RETRY         10
+static int
+wl_ext_fw_apcs(struct net_device *dev, uint32 band)
+{
+       int channel = 0, chosen = 0, retry = 0, ret = 0, spect = 0;
+       u8 *reqbuf = NULL;
+       uint32 buf_size;
+
+       ret = wldev_ioctl_get(dev, WLC_GET_SPECT_MANAGMENT, &spect, sizeof(spect));
+       if (ret) {
+               AEXT_ERROR(dev->name, "ACS: error getting the spect, ret=%d\n", ret);
+               goto done;
+       }
+
+       if (spect > 0) {
+               ret = wl_cfg80211_set_spect(dev, 0);
+               if (ret < 0) {
+                       AEXT_ERROR(dev->name, "ACS: error while setting spect, ret=%d\n", ret);
+                       goto done;
+               }
+       }
+
+       reqbuf = kmalloc(CHANSPEC_BUF_SIZE, GFP_KERNEL);
+       if (reqbuf == NULL) {
+               AEXT_ERROR(dev->name, "failed to allocate chanspec buffer\n");
+               goto done;
+       }
+       memset(reqbuf, 0, CHANSPEC_BUF_SIZE);
+
+       if (band == WLC_BAND_AUTO) {
+               AEXT_INFO(dev->name, "ACS full channel scan \n");
+               reqbuf[0] = htod32(0);
+       } else if (band == WLC_BAND_5G) {
+               AEXT_INFO(dev->name, "ACS 5G band scan \n");
+               if ((ret = wl_cfg80211_get_chanspecs_5g(dev, reqbuf, CHANSPEC_BUF_SIZE)) < 0) {
+                       AEXT_ERROR(dev->name, "ACS 5g chanspec retreival failed! \n");
+                       goto done;
+               }
+       } else if (band == WLC_BAND_2G) {
+               /*
+                * If channel argument is not provided/ argument 20 is provided,
+                * Restrict channel to 2GHz, 20MHz BW, No SB
+                */
+               AEXT_INFO(dev->name, "ACS 2G band scan \n");
+               if ((ret = wl_cfg80211_get_chanspecs_2g(dev, reqbuf, CHANSPEC_BUF_SIZE)) < 0) {
+                       AEXT_ERROR(dev->name, "ACS 2g chanspec retreival failed! \n");
+                       goto done;
+               }
+       } else {
+               AEXT_ERROR(dev->name, "ACS: No band chosen\n");
+               goto done;
+       }
+
+       buf_size = (band == WLC_BAND_AUTO) ? sizeof(int) : CHANSPEC_BUF_SIZE;
+       ret = wldev_ioctl_set(dev, WLC_START_CHANNEL_SEL, (void *)reqbuf,
+               buf_size);
+       if (ret < 0) {
+               AEXT_ERROR(dev->name, "can't start auto channel scan, err = %d\n", ret);
+               channel = 0;
+               goto done;
+       }
+
+       /* Wait for auto channel selection, max 3000 ms */
+       if ((band == WLC_BAND_2G) || (band == WLC_BAND_5G)) {
+               OSL_SLEEP(500);
+       } else {
+               /*
+                * Full channel scan at the minimum takes 1.2secs
+                * even with parallel scan. max wait time: 3500ms
+                */
+               OSL_SLEEP(1000);
+       }
+
+       retry = APCS_MAX_RETRY;
+       while (retry--) {
+               ret = wldev_ioctl_get(dev, WLC_GET_CHANNEL_SEL, &chosen,
+                       sizeof(chosen));
+               if (ret < 0) {
+                       chosen = 0;
+               } else {
+                       chosen = dtoh32(chosen);
+               }
+
+               if (chosen) {
+                       int chosen_band;
+                       int apcs_band;
+#ifdef D11AC_IOTYPES
+                       if (wl_cfg80211_get_ioctl_version() == 1) {
+                               channel = LCHSPEC_CHANNEL((chanspec_t)chosen);
+                       } else {
+                               channel = CHSPEC_CHANNEL((chanspec_t)chosen);
+                       }
+#else
+                       channel = CHSPEC_CHANNEL((chanspec_t)chosen);
+#endif /* D11AC_IOTYPES */
+                       apcs_band = (band == WLC_BAND_AUTO) ? WLC_BAND_2G : band;
+                       chosen_band = (channel <= CH_MAX_2G_CHANNEL) ? WLC_BAND_2G : WLC_BAND_5G;
+                       if (apcs_band == chosen_band) {
+                               WL_MSG(dev->name, "selected channel = %d\n", channel);
+                               break;
+                       }
+               }
+               AEXT_INFO(dev->name, "%d tried, ret = %d, chosen = 0x%x\n",
+                       (APCS_MAX_RETRY - retry), ret, chosen);
+               OSL_SLEEP(250);
+       }
+
+done:
+       if (spect > 0) {
+               if ((ret = wl_cfg80211_set_spect(dev, spect) < 0)) {
+                       AEXT_ERROR(dev->name, "ACS: error while setting spect\n");
+               }
+       }
+
+       if (reqbuf) {
+               kfree(reqbuf);
+       }
+
+       return channel;
+}
+#endif /* WL_CFG80211 */
+
+#ifdef WL_ESCAN
+int
+wl_ext_drv_apcs(struct net_device *dev, uint32 band)
+{
+       int ret = 0, channel = 0;
+       struct dhd_pub *dhd = dhd_get_pub(dev);
+       struct wl_escan_info *escan = NULL;
+       int retry = 0, retry_max, retry_interval = 250, up = 1;
+#ifdef WL_CFG80211
+       struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
+#endif /* WL_CFG80211 */
+
+       escan = dhd->escan;
+       if (dhd) {
+               retry_max = WL_ESCAN_TIMER_INTERVAL_MS/retry_interval;
+               ret = wldev_ioctl_get(dev, WLC_GET_UP, &up, sizeof(s32));
+               if (ret < 0 || up == 0) {
+                       ret = wldev_ioctl_set(dev, WLC_UP, &up, sizeof(s32));
+               }
+               retry = retry_max;
+               while (retry--) {
+                       if (escan->escan_state == ESCAN_STATE_SCANING
+#ifdef WL_CFG80211
+                               || wl_get_drv_status_all(cfg, SCANNING)
+#endif
+                       )
+                       {
+                               AEXT_INFO(dev->name, "Scanning %d tried, ret = %d\n",
+                                       (retry_max - retry), ret);
+                       } else {
+                               escan->autochannel = 1;
+                               ret = wl_escan_set_scan(dev, dhd, NULL, 0, TRUE);
+                               if (!ret)
+                                       break;
+                       }
+                       OSL_SLEEP(retry_interval);
+               }
+               if ((retry == 0) || (ret < 0))
+                       goto done;
+               retry = retry_max;
+               while (retry--) {
+                       if (escan->escan_state == ESCAN_STATE_IDLE) {
+                               if (band == WLC_BAND_5G)
+                                       channel = escan->best_5g_ch;
+                               else
+                                       channel = escan->best_2g_ch;
+                               WL_MSG(dev->name, "selected channel = %d\n", channel);
+                               goto done;
+                       }
+                       AEXT_INFO(dev->name, "escan_state=%d, %d tried, ret = %d\n",
+                               escan->escan_state, (retry_max - retry), ret);
+                       OSL_SLEEP(retry_interval);
+               }
+               if ((retry == 0) || (ret < 0))
+                       goto done;
+       }
+
+done:
+       if (escan)
+               escan->autochannel = 0;
+
+       return channel;
+}
+#endif /* WL_ESCAN */
+
+int
+wl_ext_autochannel(struct net_device *dev, uint acs, uint32 band)
+{
+       int channel = 0;
+       uint16 chan_2g, chan_5g;
+
+       AEXT_INFO(dev->name, "acs=0x%x, band=%d \n", acs, band);
+
+#ifdef WL_CFG80211
+       if (acs & ACS_FW_BIT) {
+               int ret = 0;
+               ret = wldev_ioctl_get(dev, WLC_GET_CHANNEL_SEL, &channel, sizeof(channel));
+               channel = 0;
+               if (ret != BCME_UNSUPPORTED)
+                       channel = wl_ext_fw_apcs(dev, band);
+               if (channel)
+                       return channel;
+       }
 #endif
 
+#ifdef WL_ESCAN
+       if (acs & ACS_DRV_BIT)
+               channel = wl_ext_drv_apcs(dev, band);
+#endif /* WL_ESCAN */
+
+       if (channel == 0) {
+               wl_ext_get_default_chan(dev, &chan_2g, &chan_5g, TRUE);
+               if (band == WLC_BAND_5G) {
+                       channel = chan_5g;
+               } else {
+                       channel = chan_2g;
+               }
+               AEXT_ERROR(dev->name, "ACS failed. Fall back to default channel (%d) \n", channel);
+       }
+
+       return channel;
+}
+
 #if defined(RSSIAVG)
 void
 wl_free_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
@@ -5215,7 +6992,7 @@ wl_get_avg_rssi(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, void *addr)
        }
        return (int16)rssi;
 }
-#endif
+#endif /* RSSIAVG */
 
 #if defined(RSSIOFFSET)
 int
@@ -5223,7 +7000,7 @@ wl_update_rssi_offset(struct net_device *net, int rssi)
 {
 #if defined(RSSIOFFSET_NEW)
        int j;
-#endif
+#endif /* RSSIOFFSET_NEW */
 
        if (!g_wifi_on)
                return rssi;
@@ -5236,10 +7013,10 @@ wl_update_rssi_offset(struct net_device *net, int rssi)
        rssi += j;
 #else
        rssi += RSSI_OFFSET;
-#endif
+#endif /* RSSIOFFSET_NEW */
        return MIN(rssi, RSSI_MAXVAL);
 }
-#endif
+#endif /* RSSIOFFSET */
 
 #if defined(BSSCACHE)
 void
@@ -5358,7 +7135,7 @@ wl_reset_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
 void dump_bss_cache(
 #if defined(RSSIAVG)
        wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
-#endif
+#endif /* RSSIAVG */
        wl_bss_cache_t *node)
 {
        int k = 0;
@@ -5369,7 +7146,7 @@ void dump_bss_cache(
                rssi = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID);
 #else
                rssi = dtoh16(node->results.bss_info->RSSI);
-#endif
+#endif /* RSSIAVG */
                AEXT_TRACE("wlan", "dump %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
                        k, &node->results.bss_info->BSSID, rssi, node->results.bss_info->SSID);
                k++;
@@ -5381,7 +7158,7 @@ void
 wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,
 #if defined(RSSIAVG)
        wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
-#endif
+#endif /* RSSIAVG */
        wl_scan_results_t *ss_list)
 {
        wl_bss_cache_t *node, *prev, *leaf, **bss_head;
@@ -5389,7 +7166,7 @@ wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,
        int i, k=0;
 #if defined(SORT_BSS_BY_RSSI)
        int16 rssi, rssi_node;
-#endif
+#endif /* SORT_BSS_BY_RSSI */
        struct osl_timespec now, timeout;
 
        if (!ss_list->count)
@@ -5461,14 +7238,14 @@ wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,
                        rssi = wl_get_avg_rssi(rssi_cache_ctrl, &leaf->results.bss_info->BSSID);
 #else
                        rssi = dtoh16(leaf->results.bss_info->RSSI);
-#endif
+#endif /* RSSIAVG */
                        for (;node;) {
 #if defined(RSSIAVG)
                                rssi_node = wl_get_avg_rssi(rssi_cache_ctrl,
                                        &node->results.bss_info->BSSID);
 #else
                                rssi_node = dtoh16(node->results.bss_info->RSSI);
-#endif
+#endif /* RSSIAVG */
                                if (rssi > rssi_node) {
                                        leaf->next = node;
                                        if (node == *bss_head)
@@ -5485,13 +7262,13 @@ wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,
 #else
                        leaf->next = *bss_head;
                        *bss_head = leaf;
-#endif
+#endif /* SORT_BSS_BY_RSSI */
                }
        }
        dump_bss_cache(
 #if defined(RSSIAVG)
                rssi_cache_ctrl,
-#endif
+#endif /* RSSIAVG */
                *bss_head);
 }
 
@@ -5501,6 +7278,6 @@ wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t *bss_cache_ctrl)
        AEXT_TRACE("wlan", "Enter\n");
        wl_free_bss_cache(bss_cache_ctrl);
 }
-#endif
+#endif /* BSSCACHE */