Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 83195cc8 authored by Vipin Mehta's avatar Vipin Mehta Committed by Greg Kroah-Hartman
Browse files

staging: ath6kl: Fixing target crash due to mismatch connect/disconnect



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: default avatarVipin Mehta <vmehta@atheros.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 3f2fd78e
Loading
Loading
Loading
Loading
+26 −24
Original line number Original line Diff line number Diff line
@@ -1943,15 +1943,12 @@ ar6000_stop_endpoint(struct net_device *dev, bool keepprofile, bool getdbglogs)
    {
    {
        if (!bypasswmi)
        if (!bypasswmi)
        {
        {
            if (ar->arConnected == true || ar->arConnectPending == true)
            bool disconnectIssued;
            {
 
                AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("%s(): Disconnect\n", __func__));
            disconnectIssued = (ar->arConnected) || (ar->arConnectPending);
            ar6000_disconnect(ar);
            if (!keepprofile) {
            if (!keepprofile) {
                    AR6000_SPIN_LOCK(&ar->arLock, 0);
                ar6000_init_profile_info(ar);
                ar6000_init_profile_info(ar);
                    AR6000_SPIN_UNLOCK(&ar->arLock, 0);
                }
                wmi_disconnect_cmd(ar->arWmi);
            }
            }


            A_UNTIMEOUT(&ar->disconnect_timer);
            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 
             * Sometimes disconnect_event will be received when the debug logs 
             * are collected.
             * are collected.
             */
             */
            if (ar->arConnected == true || ar->arConnectPending == true) {
            if (disconnectIssued) {
                if(ar->arNetworkType & AP_NETWORK) {
                if(ar->arNetworkType & AP_NETWORK) {
                    ar6000_disconnect_event(ar, DISCONNECT_CMD, bcast_mac, 0, NULL, 0);
                    ar6000_disconnect_event(ar, DISCONNECT_CMD, bcast_mac, 0, NULL, 0);
                } else {
                } else {
                    ar6000_disconnect_event(ar, DISCONNECT_CMD, ar->arBssid, 0, NULL, 0);
                    ar6000_disconnect_event(ar, DISCONNECT_CMD, ar->arBssid, 0, NULL, 0);
                }
                }
                ar->arConnected = false;
                ar->arConnectPending = false;
            }
            }
#ifdef USER_KEYS
#ifdef USER_KEYS
            ar->user_savedkeys_stat = USER_SAVEDKEYS_STAT_INIT;
            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);
    A_UNTIMEOUT(&ar->disconnect_timer);


    ar6000_init_profile_info(ar);
    ar6000_init_profile_info(ar);
    wmi_disconnect_cmd(ar->arWmi);
    ar6000_disconnect(ar);
}
}


static void ar6000_detect_error(unsigned long ptr)
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->arReqBssid, sizeof(ar->arReqBssid));
    A_MEMZERO(ar->arBssid, sizeof(ar->arBssid));
    A_MEMZERO(ar->arBssid, sizeof(ar->arBssid));
    ar->arBssChannel = 0;
    ar->arBssChannel = 0;
    ar->arConnected = false;
}
}


static void
static void
@@ -2322,13 +2316,7 @@ ar6000_close(struct net_device *dev)
    netif_stop_queue(dev);
    netif_stop_queue(dev);


#ifdef ATH6K_CONFIG_CFG80211
#ifdef ATH6K_CONFIG_CFG80211
    AR6000_SPIN_LOCK(&ar->arLock, 0);
    ar6000_disconnect(ar);
    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);
    }


    if(ar->arWmiReady == true) {
    if(ar->arWmiReady == true) {
        if (wmi_scanparams_cmd(ar->arWmi, 0xFFFF, 0,
        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);
            A_MEMCPY(wrqu.addr.sa_data, bssid, ATH_MAC_LEN);
            wireless_send_event(ar->arNetDev, IWEVEXPIRED, &wrqu, NULL);
            wireless_send_event(ar->arNetDev, IWEVEXPIRED, &wrqu, NULL);
        }
        }

        ar->arConnected = false;
        return;
        return;
    }
    }


@@ -4652,7 +4642,6 @@ ar6000_disconnect_event(AR_SOFTC_T *ar, u8 reason, u8 *bssid,
     */
     */
    if( reason == DISCONNECT_CMD)
    if( reason == DISCONNECT_CMD)
    {
    {
        ar->arConnectPending = false;
        if ((!ar->arUserBssFilter) && (ar->arWmiReady)) {
        if ((!ar->arUserBssFilter) && (ar->arWmiReady)) {
            wmi_bssfilter_cmd(ar->arWmi, NONE_BSS_FILTER, 0);
            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.groupCryptoLen = ar->arGroupCryptoLen;
    p.ctrl_flags = ar->arConnectCtrlFlags;
    p.ctrl_flags = ar->arConnectCtrlFlags;


    ar->arConnected = false;

    wmi_ap_profile_commit(ar->arWmi, &p);
    wmi_ap_profile_commit(ar->arWmi, &p);
    spin_lock_irqsave(&ar->arLock, flags);
    spin_lock_irqsave(&ar->arLock, flags);
    ar->arConnected  = true;
    ar->arConnected  = true;
@@ -6166,6 +6153,21 @@ ar6000_connect_to_ap(struct ar6_softc *ar)
    return A_ERROR;
    return A_ERROR;
}
}


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
int
ar6000_ap_mode_get_wpa_ie(struct ar6_softc *ar, struct ieee80211req_wpaie *wpaie)
ar6000_ap_mode_get_wpa_ie(struct ar6_softc *ar, struct ieee80211req_wpaie *wpaie)
{
{
+4 −3
Original line number Original line Diff line number Diff line
@@ -318,7 +318,7 @@ ar6k_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
        return 0;
        return 0;
    } else if(ar->arSsidLen == sme->ssid_len &&
    } else if(ar->arSsidLen == sme->ssid_len &&
              !A_MEMCMP(ar->arSsid, sme->ssid, ar->arSsidLen)) {
              !A_MEMCMP(ar->arSsid, sme->ssid, ar->arSsidLen)) {
	    wmi_disconnect_cmd(ar->arWmi);
	    ar6000_disconnect(ar);
    }
    }


    A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
    A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
@@ -604,7 +604,7 @@ ar6k_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
    }
    }


    reconnect_flag = 0;
    reconnect_flag = 0;
    wmi_disconnect_cmd(ar->arWmi);
    ar6000_disconnect(ar);
    A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
    A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
    ar->arSsidLen = 0;
    ar->arSsidLen = 0;


@@ -1341,6 +1341,7 @@ ar6k_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
                            ar->arSsidLen, ar->arSsid,
                            ar->arSsidLen, ar->arSsid,
                            ar->arReqBssid, ar->arChannelHint,
                            ar->arReqBssid, ar->arChannelHint,
                            ar->arConnectCtrlFlags);
                            ar->arConnectCtrlFlags);
    ar->arConnectPending = true;


    return 0;
    return 0;
}
}
@@ -1362,7 +1363,7 @@ ar6k_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
        return -EIO;
        return -EIO;
    }
    }


    wmi_disconnect_cmd(ar->arWmi);
    ar6000_disconnect(ar);
    A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
    A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
    ar->arSsidLen = 0;
    ar->arSsidLen = 0;


+1 −0
Original line number Original line Diff line number Diff line
@@ -171,6 +171,7 @@ void ap_wapi_rekey_event(struct ar6_softc *ar, u8 type, u8 *mac);
#endif
#endif


int ar6000_connect_to_ap(struct ar6_softc *ar);
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_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_wlan_state(struct ar6_softc *ar, AR6000_WLAN_STATE state);
int ar6000_set_bt_hw_state(struct ar6_softc *ar, u32 state);
int ar6000_set_bt_hw_state(struct ar6_softc *ar, u32 state);
+5 −6
Original line number Original line Diff line number Diff line
@@ -575,9 +575,10 @@ ar6000_ioctl_siwessid(struct net_device *dev,
    /* Update the arNetworkType */
    /* Update the arNetworkType */
    ar->arNetworkType = ar->arNextMode;
    ar->arNetworkType = ar->arNextMode;



    if ((prevMode != AP_NETWORK) &&
    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) ||
        if ((!data->flags) ||
            (A_MEMCMP(ar->arSsid, ssid, ar->arSsidLen) != 0) ||
            (A_MEMCMP(ar->arSsid, ssid, ar->arSsidLen) != 0) ||
@@ -594,7 +595,7 @@ ar6000_ioctl_siwessid(struct net_device *dev,
            if (ar->arWmiReady == true) {
            if (ar->arWmiReady == true) {
                reconnect_flag = 0;
                reconnect_flag = 0;
                status = wmi_setPmkid_cmd(ar->arWmi, ar->arBssid, NULL, 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));
                A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
                ar->arSsidLen = 0;
                ar->arSsidLen = 0;
                if (ar->arSkipScan == false) {
                if (ar->arSkipScan == false) {
@@ -2414,7 +2415,7 @@ ar6000_ioctl_siwmlme(struct net_device *dev,
                ar6000_init_profile_info(ar);
                ar6000_init_profile_info(ar);
                ar->arNetworkType = arNetworkType;
                ar->arNetworkType = arNetworkType;
                reconnect_flag = 0;
                reconnect_flag = 0;
                wmi_disconnect_cmd(ar->arWmi);
                ar6000_disconnect(ar);
                A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
                A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
                ar->arSsidLen = 0;
                ar->arSsidLen = 0;
                if (ar->arSkipScan == false) {
                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.
     * update the host driver association state for the STA|IBSS mode.
     */
     */
    if (ar->arNetworkType != AP_NETWORK && ar->arNextMode == AP_NETWORK) {
    if (ar->arNetworkType != AP_NETWORK && ar->arNextMode == AP_NETWORK) {
        ar->arConnectPending = false;
        ar->arConnected = false;
        /* Stop getting pkts from upper stack */
        /* Stop getting pkts from upper stack */
        netif_stop_queue(ar->arNetDev);
        netif_stop_queue(ar->arNetDev);
        A_MEMZERO(ar->arBssid, sizeof(ar->arBssid));
        A_MEMZERO(ar->arBssid, sizeof(ar->arBssid));