staging: ath6kl: Fixing target crash due to mismatch connect/disconnect
authorVipin Mehta <vmehta@atheros.com>
Fri, 18 Feb 2011 21:13:05 +0000 (13:13 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 18 Feb 2011 21:28:50 +0000 (13:28 -0800)
Firmware design requires a WMI_DISCONNECT_CMD for every WMI_CONNECT_CMD to
clear the firmware previous profile state. There is one case in linux host
driver where two WMI_CONNECT_CMD are given without a WMI_DISCONNECT_CMD.
This causes firmware state to mismatch causing an ASSERT. Use the driver
state variable arConnectPending to track whether a WMI_CONNECT_CMD is
issued to firmware.

Signed-off-by: Vipin Mehta <vmehta@atheros.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/staging/ath6kl/os/linux/ar6000_drv.c
drivers/staging/ath6kl/os/linux/cfg80211.c
drivers/staging/ath6kl/os/linux/include/ar6xapi_linux.h
drivers/staging/ath6kl/os/linux/wireless_ext.c

index 4f6ddf7..df9badb 100644 (file)
@@ -1943,15 +1943,12 @@ ar6000_stop_endpoint(struct net_device *dev, bool keepprofile, bool getdbglogs)
     {
         if (!bypasswmi)
         {
-            if (ar->arConnected == true || ar->arConnectPending == true)
-            {
-                AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("%s(): Disconnect\n", __func__));
-                if (!keepprofile) {
-                    AR6000_SPIN_LOCK(&ar->arLock, 0);
-                    ar6000_init_profile_info(ar);
-                    AR6000_SPIN_UNLOCK(&ar->arLock, 0);
-                }
-                wmi_disconnect_cmd(ar->arWmi);
+            bool disconnectIssued;
+            disconnectIssued = (ar->arConnected) || (ar->arConnectPending);
+            ar6000_disconnect(ar);
+            if (!keepprofile) {
+                ar6000_init_profile_info(ar);
             }
 
             A_UNTIMEOUT(&ar->disconnect_timer);
@@ -1973,14 +1970,12 @@ ar6000_stop_endpoint(struct net_device *dev, bool keepprofile, bool getdbglogs)
              * Sometimes disconnect_event will be received when the debug logs 
              * are collected.
              */
-            if (ar->arConnected == true || ar->arConnectPending == true) {
+            if (disconnectIssued) {
                 if(ar->arNetworkType & AP_NETWORK) {
                     ar6000_disconnect_event(ar, DISCONNECT_CMD, bcast_mac, 0, NULL, 0);
                 } else {
                     ar6000_disconnect_event(ar, DISCONNECT_CMD, ar->arBssid, 0, NULL, 0);
                 }
-                ar->arConnected = false;
-                ar->arConnectPending = false;
             }
 #ifdef USER_KEYS
             ar->user_savedkeys_stat = USER_SAVEDKEYS_STAT_INIT;
@@ -2163,7 +2158,7 @@ static void disconnect_timer_handler(unsigned long ptr)
     A_UNTIMEOUT(&ar->disconnect_timer);
 
     ar6000_init_profile_info(ar);
-    wmi_disconnect_cmd(ar->arWmi);
+    ar6000_disconnect(ar);
 }
 
 static void ar6000_detect_error(unsigned long ptr)
@@ -2235,7 +2230,6 @@ void ar6000_init_profile_info(AR_SOFTC_T *ar)
     A_MEMZERO(ar->arReqBssid, sizeof(ar->arReqBssid));
     A_MEMZERO(ar->arBssid, sizeof(ar->arBssid));
     ar->arBssChannel = 0;
-    ar->arConnected = false;
 }
 
 static void
@@ -2322,13 +2316,7 @@ ar6000_close(struct net_device *dev)
     netif_stop_queue(dev);
 
 #ifdef ATH6K_CONFIG_CFG80211
-    AR6000_SPIN_LOCK(&ar->arLock, 0);
-    if (ar->arConnected == true || ar->arConnectPending == true) {
-        AR6000_SPIN_UNLOCK(&ar->arLock, 0);
-        wmi_disconnect_cmd(ar->arWmi);
-    } else {
-        AR6000_SPIN_UNLOCK(&ar->arLock, 0);
-    }
+    ar6000_disconnect(ar);
 
     if(ar->arWmiReady == true) {
         if (wmi_scanparams_cmd(ar->arWmi, 0xFFFF, 0,
@@ -4608,6 +4596,8 @@ ar6000_disconnect_event(AR_SOFTC_T *ar, u8 reason, u8 *bssid,
             A_MEMCPY(wrqu.addr.sa_data, bssid, ATH_MAC_LEN);
             wireless_send_event(ar->arNetDev, IWEVEXPIRED, &wrqu, NULL);
         }
+
+        ar->arConnected = false;
         return;
     }
 
@@ -4652,7 +4642,6 @@ ar6000_disconnect_event(AR_SOFTC_T *ar, u8 reason, u8 *bssid,
      */
     if( reason == DISCONNECT_CMD)
     {
-        ar->arConnectPending = false;
         if ((!ar->arUserBssFilter) && (ar->arWmiReady)) {
             wmi_bssfilter_cmd(ar->arWmi, NONE_BSS_FILTER, 0);
         }
@@ -6084,8 +6073,6 @@ ar6000_ap_mode_profile_commit(struct ar6_softc *ar)
     p.groupCryptoLen = ar->arGroupCryptoLen;
     p.ctrl_flags = ar->arConnectCtrlFlags;
 
-    ar->arConnected = false;
-
     wmi_ap_profile_commit(ar->arWmi, &p);
     spin_lock_irqsave(&ar->arLock, flags);
     ar->arConnected  = true;
@@ -6167,6 +6154,21 @@ ar6000_connect_to_ap(struct ar6_softc *ar)
 }
 
 int
+ar6000_disconnect(struct ar6_softc *ar)
+{
+    if ((ar->arConnected == true) || (ar->arConnectPending == true)) {
+        wmi_disconnect_cmd(ar->arWmi);
+        /* 
+         * Disconnect cmd is issued, clear connectPending.
+         * arConnected will be cleard in disconnect_event notification.
+         */
+        ar->arConnectPending = false;
+    }
+
+    return 0;
+}
+
+int
 ar6000_ap_mode_get_wpa_ie(struct ar6_softc *ar, struct ieee80211req_wpaie *wpaie)
 {
     sta_t *conn = NULL;
index bc5f6a7..0f8f868 100644 (file)
@@ -318,7 +318,7 @@ ar6k_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
         return 0;
     } else if(ar->arSsidLen == sme->ssid_len &&
               !A_MEMCMP(ar->arSsid, sme->ssid, ar->arSsidLen)) {
-           wmi_disconnect_cmd(ar->arWmi);
+           ar6000_disconnect(ar);
     }
 
     A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
@@ -604,7 +604,7 @@ ar6k_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
     }
 
     reconnect_flag = 0;
-    wmi_disconnect_cmd(ar->arWmi);
+    ar6000_disconnect(ar);
     A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
     ar->arSsidLen = 0;
 
@@ -1341,6 +1341,7 @@ ar6k_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
                             ar->arSsidLen, ar->arSsid,
                             ar->arReqBssid, ar->arChannelHint,
                             ar->arConnectCtrlFlags);
+    ar->arConnectPending = true;
 
     return 0;
 }
@@ -1362,7 +1363,7 @@ ar6k_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
         return -EIO;
     }
 
-    wmi_disconnect_cmd(ar->arWmi);
+    ar6000_disconnect(ar);
     A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
     ar->arSsidLen = 0;
 
index a8d3f54..1acfb9c 100644 (file)
@@ -171,6 +171,7 @@ void ap_wapi_rekey_event(struct ar6_softc *ar, u8 type, u8 *mac);
 #endif
 
 int ar6000_connect_to_ap(struct ar6_softc *ar);
+int ar6000_disconnect(struct ar6_softc *ar);
 int ar6000_update_wlan_pwr_state(struct ar6_softc *ar, AR6000_WLAN_STATE state, bool suspending);
 int ar6000_set_wlan_state(struct ar6_softc *ar, AR6000_WLAN_STATE state);
 int ar6000_set_bt_hw_state(struct ar6_softc *ar, u32 state);
index 1f858ca..baa8663 100644 (file)
@@ -575,9 +575,10 @@ ar6000_ioctl_siwessid(struct net_device *dev,
     /* Update the arNetworkType */
     ar->arNetworkType = ar->arNextMode;
 
-
     if ((prevMode != AP_NETWORK) &&
-        ((ar->arSsidLen) || ((ar->arSsidLen == 0) && ar->arConnected) || (!data->flags)))
+        ((ar->arSsidLen) || 
+        ((ar->arSsidLen == 0) && (ar->arConnected || ar->arConnectPending)) || 
+        (!data->flags)))
     {
         if ((!data->flags) ||
             (A_MEMCMP(ar->arSsid, ssid, ar->arSsidLen) != 0) ||
@@ -594,7 +595,7 @@ ar6000_ioctl_siwessid(struct net_device *dev,
             if (ar->arWmiReady == true) {
                 reconnect_flag = 0;
                 status = wmi_setPmkid_cmd(ar->arWmi, ar->arBssid, NULL, 0);
-                status = wmi_disconnect_cmd(ar->arWmi);
+                ar6000_disconnect(ar);
                 A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
                 ar->arSsidLen = 0;
                 if (ar->arSkipScan == false) {
@@ -2414,7 +2415,7 @@ ar6000_ioctl_siwmlme(struct net_device *dev,
                 ar6000_init_profile_info(ar);
                 ar->arNetworkType = arNetworkType;
                 reconnect_flag = 0;
-                wmi_disconnect_cmd(ar->arWmi);
+                ar6000_disconnect(ar);
                 A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
                 ar->arSsidLen = 0;
                 if (ar->arSkipScan == false) {
@@ -2614,8 +2615,6 @@ ar6000_ioctl_siwcommit(struct net_device *dev,
      * update the host driver association state for the STA|IBSS mode.
      */
     if (ar->arNetworkType != AP_NETWORK && ar->arNextMode == AP_NETWORK) {
-        ar->arConnectPending = false;
-        ar->arConnected = false;
         /* Stop getting pkts from upper stack */
         netif_stop_queue(ar->arNetDev);
         A_MEMZERO(ar->arBssid, sizeof(ar->arBssid));