Adding NOTASSOCIATED FW error to disconnect wifi
Mahesh Patil [Mon, 27 Mar 2017 20:05:09 +0000 (13:05 -0700)]
Adding NOTASSOCIATED error from FW to Disconnect wifi and
setting disconnect timer to 5 sec

Bug 200215502

Change-Id: I205f6a73780f5a5980851cb805c263e2ed9270c1
Signed-off-by: Mahesh Patil <maheshp@nvidia.com>
Reviewed-on: http://git-master/r/1465102
Tested-by: Ashutosh Jha <ajha@nvidia.com>
Reviewed-by: Ashutosh Jha <ajha@nvidia.com>

drivers/net/wireless/bcmdhd/wl_android.c [changed mode: 0755->0644]
drivers/net/wireless/bcmdhd/wl_cfg80211.c
drivers/net/wireless/bcmdhd/wl_cfg80211.h

old mode 100755 (executable)
new mode 100644 (file)
index 25b5da9..51247b8
@@ -1122,6 +1122,8 @@ int wl_android_wifi_off(struct net_device *dev)
                DHD_TRACE(("%s: dev is null\n", __FUNCTION__));
                return -EINVAL;
        }
+       wl_fw_assoc_timeout_cancel();
+
 #ifdef CONFIG_BCMDHD_CUSTOM_SYSFS_TEGRA
        tegra_sysfs_off();
 #endif
index aa621ca..dadea9a 100644 (file)
@@ -2,16 +2,16 @@
  * Linux cfg80211 driver
  *
  * Copyright (C) 1999-2015, Broadcom Corporation
- * 
+ *
  * Portions contributed by Nvidia
- * Copyright (C) 2015 NVIDIA Corporation. All rights reserved.
+ * Copyright (C) 2015-2017 NVIDIA Corporation. All rights reserved.
  *
  *      Unless you and Broadcom execute a separate written software license
  * agreement governing use of this software, this software is licensed to you
  * under the terms of the GNU General Public License version 2 (the "GPL"),
  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
  * following added to such license:
- * 
+ *
  *      As a special exception, the copyright holders of this software give you
  * permission to link this software with independent modules, and to copy and
  * distribute the resulting executable under terms of your choice, provided that
@@ -19,7 +19,7 @@
  * the license of that module.  An independent module is a module which is not
  * derived from this software.  The special exception does not apply to any
  * modifications of the software.
- * 
+ *
  *      Notwithstanding the above, under no circumstances may you combine this
  * software in any way with any other Broadcom software provided under a license
  * other than the GPL, without Broadcom's express prior written consent.
@@ -839,6 +839,10 @@ static const struct {
 };
 #endif
 
+/* watchdog work for disconnecting when fw is not associated
+for FW_ASSOC_WATCHDOG_TIME ms */
+static struct fw_assoc_timeout_work fw_assoc_timeout;
+#define FW_ASSOC_WATCHDOG_TIME (5 * 1000) /* msec */
 
 static void wl_add_remove_pm_enable_work(struct bcm_cfg80211 *cfg, bool add_remove,
        enum wl_handler_del_type type)
@@ -4178,6 +4182,10 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
        }
 
        RETURN_EIO_IF_NOT_UP(cfg);
+       /* cancel FW assoc timeout watchdog if set */
+       if (fw_assoc_timeout.fw_assoc_watchdog_started) {
+               wl_fw_assoc_timeout_cancel();
+       }
 
        /*
         * Cancel ongoing scan to sync up with sme state machine of cfg80211.
@@ -4480,6 +4488,12 @@ wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
        else
                TEGRA_SYSFS_HISTOGRAM_STAT_INC(disconnect_rssi_high);
 #endif
+
+       /* cancel FW assoc timeout watchdog if set */
+       if (fw_assoc_timeout.fw_assoc_watchdog_started) {
+               wl_fw_assoc_timeout_cancel();
+       }
+
        RETURN_EIO_IF_NOT_UP(cfg);
        act = *(bool *) wl_read_prof(cfg, dev, WL_PROF_ACT);
        curbssid = wl_read_prof(cfg, dev, WL_PROF_BSSID);
@@ -4975,6 +4989,50 @@ wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
        return -EOPNOTSUPP;
 #endif /* MFP */
 }
+static void
+fw_assoc_timeout_fn(struct work_struct *work)
+{
+       struct delayed_work *delay_work = container_of(work, struct delayed_work, work);
+       struct fw_assoc_timeout_work *fw_assoc_timeout = container_of(delay_work, struct fw_assoc_timeout_work, delay_work);
+       struct bcm_cfg80211 *cfg = fw_assoc_timeout->cfg;
+       struct net_device *dev = fw_assoc_timeout->dev;
+       scb_val_t scbval;
+       s32  err = 0;
+       u8 *curbssid = wl_read_prof(cfg, dev, WL_PROF_BSSID);
+
+       scbval.val = WLAN_REASON_DEAUTH_LEAVING;
+       memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN);
+       scbval.val = htod32(scbval.val);
+       err = wldev_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), true);
+       if (err < 0) {
+               WL_ERR(("WLC_DISASSOC error %d\n", err));
+       }
+       memset(&cfg->last_roamed_addr, 0, ETHER_ADDR_LEN);
+       /* Disconnect due to zero BSSID */
+       wl_clr_drv_status(cfg, CONNECTED, dev);
+       cfg80211_disconnected(dev, 0, NULL, 0, GFP_KERNEL);
+       wl_link_down(cfg);
+       wl_init_prof(cfg, dev);
+       fw_assoc_timeout->fw_assoc_watchdog_started = FALSE;
+       WL_ERR(("force cfg80211_disconnected\n"));
+}
+
+void
+wl_fw_assoc_timeout_init()
+{
+       INIT_DELAYED_WORK(&fw_assoc_timeout.delay_work, fw_assoc_timeout_fn);
+       fw_assoc_timeout.fw_assoc_watchdog_started = FALSE;
+}
+
+void
+wl_fw_assoc_timeout_cancel()
+{
+       if (delayed_work_pending(&fw_assoc_timeout.delay_work)) {
+               WL_ERR(("cancelling fw_assoc_watchdog work\n"));
+               cancel_delayed_work_sync(&fw_assoc_timeout.delay_work);
+               fw_assoc_timeout.fw_assoc_watchdog_started = FALSE;
+       }
+}
 
 static s32
 wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
@@ -4990,6 +5048,8 @@ wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
        s8 eabuf[ETHER_ADDR_STR_LEN];
 #endif
        dhd_pub_t *dhd =  (dhd_pub_t *)(cfg->pub);
+       bool fw_assoc_state = FALSE;
+       u32 dhd_assoc_state = 0;
        RETURN_EIO_IF_NOT_UP(cfg);
        if (wl_get_mode_by_netdev(cfg, dev) == WL_MODE_AP) {
                err = wldev_iovar_getbuf(dev, "sta_info", (struct ether_addr *)mac,
@@ -5036,14 +5096,38 @@ wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
                                }
                        }
                }
-               if (!wl_get_drv_status(cfg, CONNECTED, dev) ||
-                       (dhd_is_associated(dhd, NULL, &err) == FALSE)) {
-                       WL_ERR(("NOT assoc\n"));
-                       if (err == -ERESTARTSYS)
+               dhd_assoc_state = wl_get_drv_status(cfg, CONNECTED, dev);
+               fw_assoc_state = dhd_is_associated(dhd, NULL, &err);
+               if (!dhd_assoc_state || !fw_assoc_state) {
+                       WL_ERR(("NOT assoc, error %d\n", err));
+                       if (err == -ENODATA)
                                return err;
+                       if (!dhd_assoc_state) {
+                               WL_TRACE_HW4(("drv state is not connected \n"));
+                       }
+                       if (!fw_assoc_state) {
+                               WL_TRACE_HW4(("fw state is not associated \n"));
+                       }
+                       /* Disconnect due to fw is not associated for FW_ASSOC_WATCHDOG_TIME ms.
+                       * 'err == 0 or BCME_NOTASSOCIATED' of dhd_is_associated() and '!fw_assoc_state'
+                       * means that BSSID is null.
+                       */
+                       if (dhd_assoc_state && !fw_assoc_state && (err == BCME_NOTASSOCIATED || !err)) {
+                               if (!fw_assoc_timeout.fw_assoc_watchdog_started) {
+                                       fw_assoc_timeout.dev = dev;
+                                       fw_assoc_timeout.cfg = cfg;
+                                       schedule_delayed_work(&fw_assoc_timeout.delay_work, msecs_to_jiffies(FW_ASSOC_WATCHDOG_TIME));
+                                       fw_assoc_timeout.fw_assoc_watchdog_started = TRUE;
+                                       WL_INFORM(("fw_assoc_watchdog_started\n"));
+                               }
+                       }
                        err = -ENODEV;
                        return err;
                }
+               if (fw_assoc_timeout.fw_assoc_watchdog_started) {
+                       WL_INFORM(("cancelling fw_assoc_watchdog work\n"));
+                       wl_fw_assoc_timeout_cancel();
+               }
                curmacp = wl_read_prof(cfg, dev, WL_PROF_BSSID);
                if (memcmp(mac, curmacp, ETHER_ADDR_LEN)) {
                        WL_ERR(("Wrong Mac address: "MACDBG" != "MACDBG"\n",
@@ -11766,7 +11850,7 @@ s32 wl_cfg80211_attach(struct net_device *ndev, void *context)
        cfg->btcoex_info = wl_cfg80211_btcoex_init(cfg->wdev->netdev);
        if (!cfg->btcoex_info)
                goto cfg80211_attach_out;
-#endif 
+#endif
 
        g_bcm_cfg = cfg;
 
@@ -11776,7 +11860,8 @@ s32 wl_cfg80211_attach(struct net_device *ndev, void *context)
        if (err)
                goto cfg80211_attach_out;
 #endif
-#endif 
+#endif
+       wl_fw_assoc_timeout_init();
 
        return err;
 
@@ -11794,13 +11879,14 @@ void wl_cfg80211_detach(void *para)
        cfg = g_bcm_cfg;
 
        WL_TRACE(("In\n"));
+       wl_fw_assoc_timeout_cancel();
 
        wl_add_remove_pm_enable_work(cfg, FALSE, WL_HANDLER_DEL);
 
 #if defined(COEX_DHCP)
        wl_cfg80211_btcoex_deinit();
        cfg->btcoex_info = NULL;
-#endif 
+#endif
 
        wl_setup_rfkill(cfg, FALSE);
 #ifdef DEBUGFS_CFG80211
index 71e957f..a7aa8db 100644 (file)
@@ -635,6 +635,15 @@ struct bcm_cfg80211 {
        struct ether_addr last_roamed_addr;
 };
 
+struct fw_assoc_timeout_work {
+       struct delayed_work delay_work;
+       struct net_device *dev;
+       struct bcm_cfg80211 *cfg;
+       bool fw_assoc_watchdog_started;
+};
+void wl_fw_assoc_timeout_init(void);
+void wl_fw_assoc_timeout_cancel(void);
+
 
 static inline struct wl_bss_info *next_bss(struct wl_scan_results *list, struct wl_bss_info *bss)
 {