Subject: Sync up cfg80211 patch from staging-next tree
1.Return correct scan complete status to the cfg80211 module based on the value returned from the hardware. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=3f2fd78e68cdea8805c92b72008b9eb42f6580f6 2.A bug was observed during the reconnection phase in the WPA/WPA2-PSK scenario where the EAPOL frames were going encrypted during an auto reconnection attempt. The initial 4-way handshake would go fine but then the driver was getting a command to set default keys sometime later. Setting of an incorrect flag (TX_USAGE) in the hadrware was causing the EAPOL frames during the subsequent 4-way handshake attempts to go encrypted causing the AP to reject the station. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=e8763b5fe88a0ef8858cd614d961578d34b673a0 3.Adding state in driver to track the sme state. The connect/disconnect events from the driver were messing up the state maintained within the cfg80211 module. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=98b6d2381a2812eba27a4cec3e8242262b0e5f01 4.If a scan operation is pending and in between a disconnect event is received from firmware the scan results never get sent back to cfg80211. This causes a scan failure and yields a device/resource busy state upon retries. If a disconnect event is received and scan is pending return the scan done to the cfg80211 to enable futher scans to be issued. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=3c9d2f6c14ebbfdc97945f8f8c023f1fde86732b 5.If a heavy traffic is undergoing and a link is lost (bcn miss), wlan driver does a reconnection on its own and after connection is re-established, reports it as ROAM_EVENT to cfg. Now this event is handled as work queue. It could very well happen that by the time this event gets handled, cfg would have aged out the bss and we get the following WARN_ON in __cfg80211_roamed function in file net/wireless/sme.c. /* internal error -- how did we get to CONNECTED w/o BSS? */ if (WARN_ON(!wdev->current_bss)) { return; } To resolve the issue we report the BSS whenever we send a connect or roam event to the cfg. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=60c87f1453e8d53e14941c2d99861a21282942a5 6.If the wpa_supplicant conf file supplies both open and shared algorithm, and AP is configured as shared then connection never happens. Since it is a FMAC driver additional logic is added in driver which first detects this, then tries open algorithm for the first time and when it fails tries the shared algo. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=9ae62107fb9010e902e60259c6a461c68885d0c7 7.A call to cfg80211_get_bss should be accompanied by a call to cfg80211_put_bss in error-handling code. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=d93089df1b752e4da048cd7f4749d29120b65e55 8.implementing the cfg ops that gets called when iw dev wlan0 link is issued by user. The ops that needs to be implemented is get_station. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=10ceaac958b0b04d6939ec114b7182d78d2a0027 9.The kernel panic happens when we try to complete a pending scan request while going to suspend state. The cause for this kernel panic is accessing a freed memory (ar->arWmin). This is freed before ar6k_cfg80211_scanComplete_event() getting called where it is dereferenced. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=0d422f4237ce515678ebbb8781881e5d2e58b9d4 Signed-off-by: Samuel Chang <[email protected]> diff -ruN kernel-2.6.37.6-92.2/drivers/staging/ar6003/include/common/wmi.h kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/include/common/wmi.h --- kernel-2.6.37.6-92.2/drivers/staging/ar6003/include/common/wmi.h 2011-06-15 02:06:12.863498128 +0800 +++ kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/include/common/wmi.h 2011-06-29 08:31:52.440455388 +0800 @@ -552,6 +552,11 @@ LEAP_AUTH = 0x04, /* different from IEEE_AUTH_MODE definitions */ } DOT11_AUTH_MODE; +enum { + AUTH_IDLE, + AUTH_OPEN_IN_PROGRESS, +}; + typedef enum { NONE_AUTH = 0x01, WPA_AUTH = 0x02, diff -ruN kernel-2.6.37.6-92.2/drivers/staging/ar6003/os/linux/ar6000_drv.c kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/os/linux/ar6000_drv.c --- kernel-2.6.37.6-92.2/drivers/staging/ar6003/os/linux/ar6000_drv.c 2011-06-15 02:06:12.859498118 +0800 +++ kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/os/linux/ar6000_drv.c 2011-06-29 09:34:20.847042734 +0800 @@ -1946,6 +1946,8 @@ SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy)); wdev->netdev = dev; arPriv->arNetworkType = INFRA_NETWORK; + ar->smeState = SME_DISCONNECTED; + arPriv->arAutoAuthStage = AUTH_IDLE; #endif /* ATH6K_CONFIG_CFG80211 */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) diff -ruN kernel-2.6.37.6-92.2/drivers/staging/ar6003/os/linux/cfg80211.c kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/os/linux/cfg80211.c --- kernel-2.6.37.6-92.2/drivers/staging/ar6003/os/linux/cfg80211.c 2011-06-15 02:06:12.863498128 +0800 +++ kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/os/linux/cfg80211.c 2011-06-29 11:09:42.519414972 +0800 @@ -169,6 +169,10 @@ case NL80211_AUTHTYPE_NETWORK_EAP: arPriv->arDot11AuthMode = LEAP_AUTH; break; + case NL80211_AUTHTYPE_AUTOMATIC: + arPriv->arDot11AuthMode = OPEN_AUTH; + arPriv->arAutoAuthStage = AUTH_OPEN_IN_PROGRESS; + break; default: arPriv->arDot11AuthMode = OPEN_AUTH; AR_DEBUG_PRINTF(ATH_DEBUG_INFO, @@ -245,11 +249,11 @@ AR_SOFTC_T *ar = arPriv->arSoftc; AR_SOFTC_STA_T *arSta = &arPriv->arSta; A_STATUS status; - A_UINT8 keyUsage = 0; const unsigned char wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; unsigned char *ie = sme->ie; AR_DEBUG_PRINTF(ATH_DEBUG_INFO, ("%s: \n", __func__)); + ar->smeState = SME_CONNECTING; if(ar->arWmiReady == FALSE) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: Wmi not ready yet\n", __func__)); @@ -303,12 +307,6 @@ return -EINTR; } } - up(&arPriv->arPrivSem); - - if(down_interruptible(&arPriv->arPrivSem)) { - AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: busy, couldn't get access\n", __func__)); - return -ERESTARTSYS; - } if(arPriv->arConnected == TRUE && arPriv->arSsidLen == sme->ssid_len && @@ -317,7 +315,9 @@ status = wmi_reconnect_cmd(arPriv->arWmi, arSta->arReqBssid, arPriv->arChannelHint); - + + up(&arPriv->arPrivSem); + if (status != A_OK) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: wmi_reconnect_cmd failed\n", __func__)); return -EIO; @@ -375,14 +375,9 @@ key->cipher = arPriv->arPairwiseCrypto; arPriv->arDefTxKeyIndex = sme->key_idx; - if (arPriv->arAuthMode & (WPA_PSK_AUTH | WPA2_PSK_AUTH)) - keyUsage = GROUP_USAGE; - else - keyUsage = GROUP_USAGE | TX_USAGE; - wmi_addKey_cmd(arPriv->arWmi, sme->key_idx, arPriv->arPairwiseCrypto, - keyUsage, + GROUP_USAGE | TX_USAGE, key->key_len, NULL, key->key, KEY_OP_INIT_VAL, NULL, @@ -467,6 +462,7 @@ unsigned char *ptr_ie_buf = ie_buf; unsigned char *ieeemgmtbuf = NULL; A_UINT8 source_mac[ATH_MAC_LEN]; + AR_SOFTC_T *ar = arPriv->arSoftc; A_UINT8 assocReqIeOffset = sizeof(A_UINT16) + /* capinfo*/ sizeof(A_UINT16); /* listen interval */ @@ -480,6 +476,7 @@ assocReqLen -= assocReqIeOffset; assocRespLen -= assocRespIeOffset; + arPriv->arAutoAuthStage = AUTH_IDLE; if((ADHOC_NETWORK & networkType)) { if(NL80211_IFTYPE_ADHOC != arPriv->wdev->iftype) { @@ -508,7 +505,15 @@ ((ADHOC_NETWORK & networkType) ? WLAN_CAPABILITY_IBSS : WLAN_CAPABILITY_ESS), ((ADHOC_NETWORK & networkType) ? WLAN_CAPABILITY_IBSS : WLAN_CAPABILITY_ESS)); - if(!bss) { + /* + * Earlier we were updating the cfg about bss by making a beacon frame + * only if the entry for bss is not there. This can have some issue if + * ROAM event is generated and a heavy traffic is ongoing. The ROAM + * event is handled through a work queue and by the time it really gets + * handled, BSS would have been aged out. So it is better to update the + * cfg about BSS irrespective of its entry being present right now or + * not. + */ if (ADHOC_NETWORK & networkType) { /* construct 802.11 mgmt beacon */ if(ptr_ie_buf) { @@ -549,6 +554,7 @@ if(!ieeemgmtbuf) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: ieeeMgmtbuf alloc error\n", __func__)); + cfg80211_put_bss(bss); return; } @@ -578,7 +584,6 @@ signal, GFP_ATOMIC); A_FREE(ieeemgmtbuf); cfg80211_put_bss(bss); - } if((ADHOC_NETWORK & networkType)) { cfg80211_ibss_joined(arPriv->arNetDev, bssid, GFP_ATOMIC); @@ -587,6 +592,7 @@ if (FALSE == arPriv->arConnected) { /* inform connect result to cfg80211 */ + ar->smeState = SME_DISCONNECTED; cfg80211_connect_result(arPriv->arNetDev, bssid, assocReqIe, assocReqLen, assocRespIe, assocRespLen, @@ -648,6 +654,14 @@ A_UINT8 *bssid, A_UINT8 assocRespLen, A_UINT8 *assocInfo, A_UINT16 protocolReasonStatus) { + A_UINT16 status; + AR_SOFTC_STA_T *arSta = &arPriv->arSta; + AR_SOFTC_T *ar = (AR_SOFTC_T *)arPriv->arSoftc; + + if (arPriv->scan_request) { + cfg80211_scan_done(arPriv->scan_request, true); + arPriv->scan_request = NULL; + } AR_DEBUG_PRINTF(ATH_DEBUG_INFO, ("%s: reason=%u\n", __func__, reason)); @@ -670,18 +684,74 @@ } } - if(FALSE == arPriv->arConnected) { + if(true == arSta->arConnectPending) { if(NO_NETWORK_AVAIL == reason) { /* connect cmd failed */ - cfg80211_connect_result(arPriv->arNetDev, bssid, - NULL, 0, - NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_ATOMIC); - } + wmi_disconnect_cmd(arPriv->arWmi); + } else if (reason == DISCONNECT_CMD) { + if (arPriv->arAutoAuthStage) { + /* + * If the current auth algorithm is open try shared + * and make autoAuthStage idle. We do not make it + * leap for now being. + */ + if (arPriv->arDot11AuthMode == OPEN_AUTH) { + struct ar_key *key = NULL; + key = &arPriv->keys[arPriv->arDefTxKeyIndex]; + if (down_interruptible(&ar->arSem)) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: busy, couldn't get access\n", __func__)); + return; + } + + arPriv->arDot11AuthMode = SHARED_AUTH; + arPriv->arAutoAuthStage = AUTH_IDLE; + + wmi_addKey_cmd(arPriv->arWmi, arPriv->arDefTxKeyIndex, + arPriv->arPairwiseCrypto, + GROUP_USAGE | TX_USAGE, + key->key_len, + NULL, + key->key, KEY_OP_INIT_VAL, NULL, + NO_SYNC_WMIFLAG); + + status = wmi_connect_cmd(arPriv->arWmi, + arPriv->arNetworkType, + arPriv->arDot11AuthMode, + arPriv->arAuthMode, + arPriv->arPairwiseCrypto, + arPriv->arPairwiseCryptoLen, + arPriv->arGroupCrypto, + arPriv->arGroupCryptoLen, + arPriv->arSsidLen, + arPriv->arSsid, + arSta->arReqBssid, + arPriv->arChannelHint, + arSta->arConnectCtrlFlags); + up(&ar->arSem); + + } else if (arPriv->arDot11AuthMode == SHARED_AUTH) { + /* should not reach here */ + } + } else { + arSta->arConnectPending = false; + if (ar->smeState == SME_CONNECTING) { + cfg80211_connect_result(arPriv->arNetDev, bssid, + NULL, 0, + NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_ATOMIC); + } else { + cfg80211_disconnected(arPriv->arNetDev, + reason, + NULL, 0, + GFP_ATOMIC); + } + ar->smeState = SME_DISCONNECTED; + } + } } else { - /* connection loss due to disconnect cmd or low rssi */ - cfg80211_disconnected(arPriv->arNetDev, reason, NULL, 0, GFP_ATOMIC); + if (reason != DISCONNECT_CMD) + wmi_disconnect_cmd(arPriv->arWmi); } } @@ -818,10 +888,11 @@ if(arPriv->scan_request) { /* Translate data to cfg80211 mgmt format */ + if (arPriv->arWmi) wmi_iterate_nodes(arPriv->arWmi, ar6k_cfg80211_scan_node, arPriv->wdev->wiphy); - cfg80211_scan_done(arPriv->scan_request, - (status & A_ECANCELED) ? true : false); + cfg80211_scan_done(arPriv->scan_request, + ((status & A_ECANCELED) || (status & A_EBUSY)) ? true : false); if(arPriv->scan_request->n_ssids && arPriv->scan_request->ssids[0].ssid_len) { @@ -1032,6 +1103,7 @@ AR_SOFTC_T *ar = arPriv->arSoftc; struct ar_key *key = NULL; A_STATUS status = A_OK; + u8 key_usage; AR_DEBUG_PRINTF(ATH_DEBUG_INFO, ("%s: index %d\n", __func__, key_index)); @@ -1058,10 +1130,15 @@ return -EINVAL; } + key_usage = GROUP_USAGE; + if (WEP_CRYPT == arPriv->arPairwiseCrypto) { + key_usage |= TX_USAGE; + } + arPriv->arDefTxKeyIndex = key_index; key = &arPriv->keys[arPriv->arDefTxKeyIndex]; status = wmi_addKey_cmd(arPriv->arWmi, arPriv->arDefTxKeyIndex, - arPriv->arPairwiseCrypto, GROUP_USAGE | TX_USAGE, + arPriv->arPairwiseCrypto, key_usage, key->key_len, key->seq, key->key, KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG); if (status != A_OK) { @@ -1473,6 +1550,152 @@ WLAN_CIPHER_SUITE_CCMP, }; +bool is_rate_legacy(s32 rate) +{ + static const s32 legacy[] = { 1000, 2000, 5500, 11000, + 6000, 9000, 12000, 18000, 24000, + 36000, 48000, 54000 }; + u8 i; + + for (i = 0; i < ARRAY_SIZE(legacy); i++) { + if (rate == legacy[i]) + return true; + } + + return false; +} + +bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi) +{ + static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000, + 52000, 58500, 65000, 72200 }; + u8 i; + + for (i = 0; i < ARRAY_SIZE(ht20); i++) { + if (rate == ht20[i]) { + if (i == ARRAY_SIZE(ht20) - 1) + /* last rate uses sgi */ + *sgi = true; + else + *sgi = false; + + *mcs = i; + return true; + } + } + return false; +} + +bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi) +{ + static const s32 ht40[] = { 13500, 27000, 40500, 54000, + 81000, 108000, 121500, 135000, + 150000 }; + u8 i; + + for (i = 0; i < ARRAY_SIZE(ht40); i++) { + if (rate == ht40[i]) { + if (i == ARRAY_SIZE(ht40) - 1) + /* last rate uses sgi */ + *sgi = true; + else + *sgi = false; + + *mcs = i; + return true; + } + } + + return false; +} + +static int ar6k_get_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_info *sinfo) +{ + AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); + AR_SOFTC_T *ar = arPriv->arSoftc; + long left; + bool sgi; + s32 rate; + int ret; + u8 mcs; + + if (memcmp(mac, arPriv->arBssid, ETH_ALEN) != 0) + return -ENOENT; + + if (down_interruptible(&ar->arSem)) + return -EBUSY; + + arPriv->statsUpdatePending = true; + + ret = wmi_get_stats_cmd(arPriv->arWmi); + + if (ret != 0) { + up(&ar->arSem); + return -EIO; + } + + left = wait_event_interruptible_timeout(arPriv->arEvent, + arPriv->statsUpdatePending == false, + wmitimeout * HZ); + + up(&ar->arSem); + + if (left == 0) + return -ETIMEDOUT; + else if (left < 0) + return left; + + if (arPriv->arTargetStats.rx_bytes) { + sinfo->rx_bytes = arPriv->arTargetStats.rx_bytes; + sinfo->filled |= STATION_INFO_RX_BYTES; + sinfo->rx_packets = arPriv->arTargetStats.rx_packets; + sinfo->filled |= STATION_INFO_RX_PACKETS; + } + + if (arPriv->arTargetStats.tx_bytes) { + sinfo->tx_bytes = arPriv->arTargetStats.tx_bytes; + sinfo->filled |= STATION_INFO_TX_BYTES; + sinfo->tx_packets = arPriv->arTargetStats.tx_packets; + sinfo->filled |= STATION_INFO_TX_PACKETS; + } + + sinfo->signal = arPriv->arTargetStats.cs_rssi; + sinfo->filled |= STATION_INFO_SIGNAL; + + rate = arPriv->arTargetStats.tx_unicast_rate; + + if (is_rate_legacy(rate)) { + sinfo->txrate.legacy = rate / 100; + } else if (is_rate_ht20(rate, &mcs, &sgi)) { + if (sgi) { + sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + sinfo->txrate.mcs = mcs - 1; + } else { + sinfo->txrate.mcs = mcs; + } + + sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; + } else if (is_rate_ht40(rate, &mcs, &sgi)) { + if (sgi) { + sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + sinfo->txrate.mcs = mcs - 1; + } else { + sinfo->txrate.mcs = mcs; + } + + sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; + } else { + WARN(1, "invalid rate: %d", rate); + return 0; + } + + sinfo->filled |= STATION_INFO_TX_BITRATE; + + return 0; +} + static struct cfg80211_ops ar6k_cfg80211_ops = { .change_virtual_intf = ar6k_cfg80211_change_iface, @@ -1493,6 +1716,7 @@ .set_power_mgmt = ar6k_cfg80211_set_power_mgmt, .join_ibss = ar6k_cfg80211_join_ibss, .leave_ibss = ar6k_cfg80211_leave_ibss, + .get_station = ar6k_get_station, }; struct wireless_dev * diff -ruN kernel-2.6.37.6-92.2/drivers/staging/ar6003/os/linux/include/ar6000_drv.h kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/os/linux/include/ar6000_drv.h --- kernel-2.6.37.6-92.2/drivers/staging/ar6003/os/linux/include/ar6000_drv.h 2011-06-15 02:06:12.863498128 +0800 +++ kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/os/linux/include/ar6000_drv.h 2011-06-29 08:32:44.112711621 +0800 @@ -427,6 +427,13 @@ A_UINT8 seq_len; A_UINT32 cipher; }; + +enum { + SME_DISCONNECTED, + SME_CONNECTING, + SME_CONNECTED +}; + #endif /* ATH6K_CONFIG_CFG80211 */ @@ -574,6 +581,7 @@ A_UINT8 rxMetaVersion; A_INT32 (*exitCallback)(void *config); /* generic callback at AR6K exit */ HIF_DEVICE_OS_DEVICE_INFO osDevInfo; + A_UINT32 smeState; A_UINT16 arWlanPowerState; A_BOOL arPlatPowerOff; USER_RSSI_CPENSATION rssi_compensation_param; @@ -729,6 +737,7 @@ AR_SOFTC_T *arSoftc; A_UINT8 arHoldConnection; A_UINT8 num_sta; + A_UINT8 arAutoAuthStage; }AR_SOFTC_DEV_T; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
Subject: Sync up cfg80211 patch from staging-next tree 1.Return correct scan complete status to the cfg80211 module based on the value returned from the hardware. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=3f2fd78e68cdea8805c92b72008b9eb42f6580f6 2.A bug was observed during the reconnection phase in the WPA/WPA2-PSK scenario where the EAPOL frames were going encrypted during an auto reconnection attempt. The initial 4-way handshake would go fine but then the driver was getting a command to set default keys sometime later. Setting of an incorrect flag (TX_USAGE) in the hadrware was causing the EAPOL frames during the subsequent 4-way handshake attempts to go encrypted causing the AP to reject the station. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=e8763b5fe88a0ef8858cd614d961578d34b673a0 3.Adding state in driver to track the sme state. The connect/disconnect events from the driver were messing up the state maintained within the cfg80211 module. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=98b6d2381a2812eba27a4cec3e8242262b0e5f01 4.If a scan operation is pending and in between a disconnect event is received from firmware the scan results never get sent back to cfg80211. This causes a scan failure and yields a device/resource busy state upon retries. If a disconnect event is received and scan is pending return the scan done to the cfg80211 to enable futher scans to be issued. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=3c9d2f6c14ebbfdc97945f8f8c023f1fde86732b 5.If a heavy traffic is undergoing and a link is lost (bcn miss), wlan driver does a reconnection on its own and after connection is re-established, reports it as ROAM_EVENT to cfg. Now this event is handled as work queue. It could very well happen that by the time this event gets handled, cfg would have aged out the bss and we get the following WARN_ON in __cfg80211_roamed function in file net/wireless/sme.c. /* internal error -- how did we get to CONNECTED w/o BSS? */ if (WARN_ON(!wdev->current_bss)) { return; } To resolve the issue we report the BSS whenever we send a connect or roam event to the cfg. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=60c87f1453e8d53e14941c2d99861a21282942a5 6.If the wpa_supplicant conf file supplies both open and shared algorithm, and AP is configured as shared then connection never happens. Since it is a FMAC driver additional logic is added in driver which first detects this, then tries open algorithm for the first time and when it fails tries the shared algo. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=9ae62107fb9010e902e60259c6a461c68885d0c7 7.A call to cfg80211_get_bss should be accompanied by a call to cfg80211_put_bss in error-handling code. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=d93089df1b752e4da048cd7f4749d29120b65e55 8.implementing the cfg ops that gets called when iw dev wlan0 link is issued by user. The ops that needs to be implemented is get_station. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=10ceaac958b0b04d6939ec114b7182d78d2a0027 9.The kernel panic happens when we try to complete a pending scan request while going to suspend state. The cause for this kernel panic is accessing a freed memory (ar->arWmin). This is freed before ar6k_cfg80211_scanComplete_event() getting called where it is dereferenced. http://git.kernel.org/?p=linux/kernel/git/gregkh/staging-2.6.git;a=commitdiff;h=0d422f4237ce515678ebbb8781881e5d2e58b9d4 Signed-off-by: Samuel Chang <[email protected]> diff -ruN kernel-2.6.37.6-92.2/drivers/staging/ar6003/include/common/wmi.h kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/include/common/wmi.h --- kernel-2.6.37.6-92.2/drivers/staging/ar6003/include/common/wmi.h 2011-06-15 02:06:12.863498128 +0800 +++ kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/include/common/wmi.h 2011-06-29 08:31:52.440455388 +0800 @@ -552,6 +552,11 @@ LEAP_AUTH = 0x04, /* different from IEEE_AUTH_MODE definitions */ } DOT11_AUTH_MODE; +enum { + AUTH_IDLE, + AUTH_OPEN_IN_PROGRESS, +}; + typedef enum { NONE_AUTH = 0x01, WPA_AUTH = 0x02, diff -ruN kernel-2.6.37.6-92.2/drivers/staging/ar6003/os/linux/ar6000_drv.c kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/os/linux/ar6000_drv.c --- kernel-2.6.37.6-92.2/drivers/staging/ar6003/os/linux/ar6000_drv.c 2011-06-15 02:06:12.859498118 +0800 +++ kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/os/linux/ar6000_drv.c 2011-06-29 09:34:20.847042734 +0800 @@ -1946,6 +1946,8 @@ SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy)); wdev->netdev = dev; arPriv->arNetworkType = INFRA_NETWORK; + ar->smeState = SME_DISCONNECTED; + arPriv->arAutoAuthStage = AUTH_IDLE; #endif /* ATH6K_CONFIG_CFG80211 */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) diff -ruN kernel-2.6.37.6-92.2/drivers/staging/ar6003/os/linux/cfg80211.c kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/os/linux/cfg80211.c --- kernel-2.6.37.6-92.2/drivers/staging/ar6003/os/linux/cfg80211.c 2011-06-15 02:06:12.863498128 +0800 +++ kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/os/linux/cfg80211.c 2011-06-29 11:09:42.519414972 +0800 @@ -169,6 +169,10 @@ case NL80211_AUTHTYPE_NETWORK_EAP: arPriv->arDot11AuthMode = LEAP_AUTH; break; + case NL80211_AUTHTYPE_AUTOMATIC: + arPriv->arDot11AuthMode = OPEN_AUTH; + arPriv->arAutoAuthStage = AUTH_OPEN_IN_PROGRESS; + break; default: arPriv->arDot11AuthMode = OPEN_AUTH; AR_DEBUG_PRINTF(ATH_DEBUG_INFO, @@ -245,11 +249,11 @@ AR_SOFTC_T *ar = arPriv->arSoftc; AR_SOFTC_STA_T *arSta = &arPriv->arSta; A_STATUS status; - A_UINT8 keyUsage = 0; const unsigned char wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; unsigned char *ie = sme->ie; AR_DEBUG_PRINTF(ATH_DEBUG_INFO, ("%s: \n", __func__)); + ar->smeState = SME_CONNECTING; if(ar->arWmiReady == FALSE) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: Wmi not ready yet\n", __func__)); @@ -303,12 +307,6 @@ return -EINTR; } } - up(&arPriv->arPrivSem); - - if(down_interruptible(&arPriv->arPrivSem)) { - AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: busy, couldn't get access\n", __func__)); - return -ERESTARTSYS; - } if(arPriv->arConnected == TRUE && arPriv->arSsidLen == sme->ssid_len && @@ -317,7 +315,9 @@ status = wmi_reconnect_cmd(arPriv->arWmi, arSta->arReqBssid, arPriv->arChannelHint); - + + up(&arPriv->arPrivSem); + if (status != A_OK) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: wmi_reconnect_cmd failed\n", __func__)); return -EIO; @@ -375,14 +375,9 @@ key->cipher = arPriv->arPairwiseCrypto; arPriv->arDefTxKeyIndex = sme->key_idx; - if (arPriv->arAuthMode & (WPA_PSK_AUTH | WPA2_PSK_AUTH)) - keyUsage = GROUP_USAGE; - else - keyUsage = GROUP_USAGE | TX_USAGE; - wmi_addKey_cmd(arPriv->arWmi, sme->key_idx, arPriv->arPairwiseCrypto, - keyUsage, + GROUP_USAGE | TX_USAGE, key->key_len, NULL, key->key, KEY_OP_INIT_VAL, NULL, @@ -467,6 +462,7 @@ unsigned char *ptr_ie_buf = ie_buf; unsigned char *ieeemgmtbuf = NULL; A_UINT8 source_mac[ATH_MAC_LEN]; + AR_SOFTC_T *ar = arPriv->arSoftc; A_UINT8 assocReqIeOffset = sizeof(A_UINT16) + /* capinfo*/ sizeof(A_UINT16); /* listen interval */ @@ -480,6 +476,7 @@ assocReqLen -= assocReqIeOffset; assocRespLen -= assocRespIeOffset; + arPriv->arAutoAuthStage = AUTH_IDLE; if((ADHOC_NETWORK & networkType)) { if(NL80211_IFTYPE_ADHOC != arPriv->wdev->iftype) { @@ -508,7 +505,15 @@ ((ADHOC_NETWORK & networkType) ? WLAN_CAPABILITY_IBSS : WLAN_CAPABILITY_ESS), ((ADHOC_NETWORK & networkType) ? WLAN_CAPABILITY_IBSS : WLAN_CAPABILITY_ESS)); - if(!bss) { + /* + * Earlier we were updating the cfg about bss by making a beacon frame + * only if the entry for bss is not there. This can have some issue if + * ROAM event is generated and a heavy traffic is ongoing. The ROAM + * event is handled through a work queue and by the time it really gets + * handled, BSS would have been aged out. So it is better to update the + * cfg about BSS irrespective of its entry being present right now or + * not. + */ if (ADHOC_NETWORK & networkType) { /* construct 802.11 mgmt beacon */ if(ptr_ie_buf) { @@ -549,6 +554,7 @@ if(!ieeemgmtbuf) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: ieeeMgmtbuf alloc error\n", __func__)); + cfg80211_put_bss(bss); return; } @@ -578,7 +584,6 @@ signal, GFP_ATOMIC); A_FREE(ieeemgmtbuf); cfg80211_put_bss(bss); - } if((ADHOC_NETWORK & networkType)) { cfg80211_ibss_joined(arPriv->arNetDev, bssid, GFP_ATOMIC); @@ -587,6 +592,7 @@ if (FALSE == arPriv->arConnected) { /* inform connect result to cfg80211 */ + ar->smeState = SME_DISCONNECTED; cfg80211_connect_result(arPriv->arNetDev, bssid, assocReqIe, assocReqLen, assocRespIe, assocRespLen, @@ -648,6 +654,14 @@ A_UINT8 *bssid, A_UINT8 assocRespLen, A_UINT8 *assocInfo, A_UINT16 protocolReasonStatus) { + A_UINT16 status; + AR_SOFTC_STA_T *arSta = &arPriv->arSta; + AR_SOFTC_T *ar = (AR_SOFTC_T *)arPriv->arSoftc; + + if (arPriv->scan_request) { + cfg80211_scan_done(arPriv->scan_request, true); + arPriv->scan_request = NULL; + } AR_DEBUG_PRINTF(ATH_DEBUG_INFO, ("%s: reason=%u\n", __func__, reason)); @@ -670,18 +684,74 @@ } } - if(FALSE == arPriv->arConnected) { + if(true == arSta->arConnectPending) { if(NO_NETWORK_AVAIL == reason) { /* connect cmd failed */ - cfg80211_connect_result(arPriv->arNetDev, bssid, - NULL, 0, - NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_ATOMIC); - } + wmi_disconnect_cmd(arPriv->arWmi); + } else if (reason == DISCONNECT_CMD) { + if (arPriv->arAutoAuthStage) { + /* + * If the current auth algorithm is open try shared + * and make autoAuthStage idle. We do not make it + * leap for now being. + */ + if (arPriv->arDot11AuthMode == OPEN_AUTH) { + struct ar_key *key = NULL; + key = &arPriv->keys[arPriv->arDefTxKeyIndex]; + if (down_interruptible(&ar->arSem)) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: busy, couldn't get access\n", __func__)); + return; + } + + arPriv->arDot11AuthMode = SHARED_AUTH; + arPriv->arAutoAuthStage = AUTH_IDLE; + + wmi_addKey_cmd(arPriv->arWmi, arPriv->arDefTxKeyIndex, + arPriv->arPairwiseCrypto, + GROUP_USAGE | TX_USAGE, + key->key_len, + NULL, + key->key, KEY_OP_INIT_VAL, NULL, + NO_SYNC_WMIFLAG); + + status = wmi_connect_cmd(arPriv->arWmi, + arPriv->arNetworkType, + arPriv->arDot11AuthMode, + arPriv->arAuthMode, + arPriv->arPairwiseCrypto, + arPriv->arPairwiseCryptoLen, + arPriv->arGroupCrypto, + arPriv->arGroupCryptoLen, + arPriv->arSsidLen, + arPriv->arSsid, + arSta->arReqBssid, + arPriv->arChannelHint, + arSta->arConnectCtrlFlags); + up(&ar->arSem); + + } else if (arPriv->arDot11AuthMode == SHARED_AUTH) { + /* should not reach here */ + } + } else { + arSta->arConnectPending = false; + if (ar->smeState == SME_CONNECTING) { + cfg80211_connect_result(arPriv->arNetDev, bssid, + NULL, 0, + NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_ATOMIC); + } else { + cfg80211_disconnected(arPriv->arNetDev, + reason, + NULL, 0, + GFP_ATOMIC); + } + ar->smeState = SME_DISCONNECTED; + } + } } else { - /* connection loss due to disconnect cmd or low rssi */ - cfg80211_disconnected(arPriv->arNetDev, reason, NULL, 0, GFP_ATOMIC); + if (reason != DISCONNECT_CMD) + wmi_disconnect_cmd(arPriv->arWmi); } } @@ -818,10 +888,11 @@ if(arPriv->scan_request) { /* Translate data to cfg80211 mgmt format */ + if (arPriv->arWmi) wmi_iterate_nodes(arPriv->arWmi, ar6k_cfg80211_scan_node, arPriv->wdev->wiphy); - cfg80211_scan_done(arPriv->scan_request, - (status & A_ECANCELED) ? true : false); + cfg80211_scan_done(arPriv->scan_request, + ((status & A_ECANCELED) || (status & A_EBUSY)) ? true : false); if(arPriv->scan_request->n_ssids && arPriv->scan_request->ssids[0].ssid_len) { @@ -1032,6 +1103,7 @@ AR_SOFTC_T *ar = arPriv->arSoftc; struct ar_key *key = NULL; A_STATUS status = A_OK; + u8 key_usage; AR_DEBUG_PRINTF(ATH_DEBUG_INFO, ("%s: index %d\n", __func__, key_index)); @@ -1058,10 +1130,15 @@ return -EINVAL; } + key_usage = GROUP_USAGE; + if (WEP_CRYPT == arPriv->arPairwiseCrypto) { + key_usage |= TX_USAGE; + } + arPriv->arDefTxKeyIndex = key_index; key = &arPriv->keys[arPriv->arDefTxKeyIndex]; status = wmi_addKey_cmd(arPriv->arWmi, arPriv->arDefTxKeyIndex, - arPriv->arPairwiseCrypto, GROUP_USAGE | TX_USAGE, + arPriv->arPairwiseCrypto, key_usage, key->key_len, key->seq, key->key, KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG); if (status != A_OK) { @@ -1473,6 +1550,152 @@ WLAN_CIPHER_SUITE_CCMP, }; +bool is_rate_legacy(s32 rate) +{ + static const s32 legacy[] = { 1000, 2000, 5500, 11000, + 6000, 9000, 12000, 18000, 24000, + 36000, 48000, 54000 }; + u8 i; + + for (i = 0; i < ARRAY_SIZE(legacy); i++) { + if (rate == legacy[i]) + return true; + } + + return false; +} + +bool is_rate_ht20(s32 rate, u8 *mcs, bool *sgi) +{ + static const s32 ht20[] = { 6500, 13000, 19500, 26000, 39000, + 52000, 58500, 65000, 72200 }; + u8 i; + + for (i = 0; i < ARRAY_SIZE(ht20); i++) { + if (rate == ht20[i]) { + if (i == ARRAY_SIZE(ht20) - 1) + /* last rate uses sgi */ + *sgi = true; + else + *sgi = false; + + *mcs = i; + return true; + } + } + return false; +} + +bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi) +{ + static const s32 ht40[] = { 13500, 27000, 40500, 54000, + 81000, 108000, 121500, 135000, + 150000 }; + u8 i; + + for (i = 0; i < ARRAY_SIZE(ht40); i++) { + if (rate == ht40[i]) { + if (i == ARRAY_SIZE(ht40) - 1) + /* last rate uses sgi */ + *sgi = true; + else + *sgi = false; + + *mcs = i; + return true; + } + } + + return false; +} + +static int ar6k_get_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_info *sinfo) +{ + AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev); + AR_SOFTC_T *ar = arPriv->arSoftc; + long left; + bool sgi; + s32 rate; + int ret; + u8 mcs; + + if (memcmp(mac, arPriv->arBssid, ETH_ALEN) != 0) + return -ENOENT; + + if (down_interruptible(&ar->arSem)) + return -EBUSY; + + arPriv->statsUpdatePending = true; + + ret = wmi_get_stats_cmd(arPriv->arWmi); + + if (ret != 0) { + up(&ar->arSem); + return -EIO; + } + + left = wait_event_interruptible_timeout(arPriv->arEvent, + arPriv->statsUpdatePending == false, + wmitimeout * HZ); + + up(&ar->arSem); + + if (left == 0) + return -ETIMEDOUT; + else if (left < 0) + return left; + + if (arPriv->arTargetStats.rx_bytes) { + sinfo->rx_bytes = arPriv->arTargetStats.rx_bytes; + sinfo->filled |= STATION_INFO_RX_BYTES; + sinfo->rx_packets = arPriv->arTargetStats.rx_packets; + sinfo->filled |= STATION_INFO_RX_PACKETS; + } + + if (arPriv->arTargetStats.tx_bytes) { + sinfo->tx_bytes = arPriv->arTargetStats.tx_bytes; + sinfo->filled |= STATION_INFO_TX_BYTES; + sinfo->tx_packets = arPriv->arTargetStats.tx_packets; + sinfo->filled |= STATION_INFO_TX_PACKETS; + } + + sinfo->signal = arPriv->arTargetStats.cs_rssi; + sinfo->filled |= STATION_INFO_SIGNAL; + + rate = arPriv->arTargetStats.tx_unicast_rate; + + if (is_rate_legacy(rate)) { + sinfo->txrate.legacy = rate / 100; + } else if (is_rate_ht20(rate, &mcs, &sgi)) { + if (sgi) { + sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + sinfo->txrate.mcs = mcs - 1; + } else { + sinfo->txrate.mcs = mcs; + } + + sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; + } else if (is_rate_ht40(rate, &mcs, &sgi)) { + if (sgi) { + sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + sinfo->txrate.mcs = mcs - 1; + } else { + sinfo->txrate.mcs = mcs; + } + + sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; + } else { + WARN(1, "invalid rate: %d", rate); + return 0; + } + + sinfo->filled |= STATION_INFO_TX_BITRATE; + + return 0; +} + static struct cfg80211_ops ar6k_cfg80211_ops = { .change_virtual_intf = ar6k_cfg80211_change_iface, @@ -1493,6 +1716,7 @@ .set_power_mgmt = ar6k_cfg80211_set_power_mgmt, .join_ibss = ar6k_cfg80211_join_ibss, .leave_ibss = ar6k_cfg80211_leave_ibss, + .get_station = ar6k_get_station, }; struct wireless_dev * diff -ruN kernel-2.6.37.6-92.2/drivers/staging/ar6003/os/linux/include/ar6000_drv.h kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/os/linux/include/ar6000_drv.h --- kernel-2.6.37.6-92.2/drivers/staging/ar6003/os/linux/include/ar6000_drv.h 2011-06-15 02:06:12.863498128 +0800 +++ kernel-2.6.37.6-92.2_cfg80211_fix_patch/drivers/staging/ar6003/os/linux/include/ar6000_drv.h 2011-06-29 08:32:44.112711621 +0800 @@ -427,6 +427,13 @@ A_UINT8 seq_len; A_UINT32 cipher; }; + +enum { + SME_DISCONNECTED, + SME_CONNECTING, + SME_CONNECTED +}; + #endif /* ATH6K_CONFIG_CFG80211 */ @@ -574,6 +581,7 @@ A_UINT8 rxMetaVersion; A_INT32 (*exitCallback)(void *config); /* generic callback at AR6K exit */ HIF_DEVICE_OS_DEVICE_INFO osDevInfo; + A_UINT32 smeState; A_UINT16 arWlanPowerState; A_BOOL arPlatPowerOff; USER_RSSI_CPENSATION rssi_compensation_param; @@ -729,6 +737,7 @@ AR_SOFTC_T *arSoftc; A_UINT8 arHoldConnection; A_UINT8 num_sta; + A_UINT8 arAutoAuthStage; }AR_SOFTC_DEV_T; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
_______________________________________________ MeeGo-kernel mailing list [email protected] http://lists.meego.com/listinfo/meego-kernel
