ath6kl: Fix 4-way handshake failure in AP and P2P GO mode
authorVasanthakumar Thiagarajan <vthiagar@qca.qualcomm.com>
Mon, 9 Apr 2012 15:21:20 +0000 (20:51 +0530)
committerKalle Valo <kvalo@qca.qualcomm.com>
Mon, 9 Apr 2012 15:33:07 +0000 (18:33 +0300)
RSN capability field of RSN IE which is generated (which is what really
advertised in beacon/probe response) differs from the one generated in
wpa_supplicant. This inconsistency in rsn IE results in 4-way handshake
failure. To fix this, configure rsn capability used in wpa_supplicant
in firmware using a new wmi command, WMI_SET_IE_CMDID. There is a bit
(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE) in fw_capabilities to advertise
this support to driver.

Signed-off-by: Subramania Sharma <sharmat@qca.qualcomm.com>
Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/core.h
drivers/net/wireless/ath/ath6kl/wmi.c
drivers/net/wireless/ath/ath6kl/wmi.h

index 900993017d09218c48370382ce6a8d55372f8ea4..86d388f5770867d0a37355ea01867f91a5e7f7a4 100644 (file)
@@ -2548,6 +2548,52 @@ static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
        return 0;
 }
 
+static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon,
+                               u8 *rsn_capab)
+{
+       const u8 *rsn_ie;
+       size_t rsn_ie_len;
+       u16 cnt;
+
+       if (!beacon->tail)
+               return -EINVAL;
+
+       rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, beacon->tail, beacon->tail_len);
+       if (!rsn_ie)
+               return -EINVAL;
+
+       rsn_ie_len = *(rsn_ie + 1);
+       /* skip element id and length */
+       rsn_ie += 2;
+
+       /* skip version, group cipher */
+       if (rsn_ie_len < 6)
+               return -EINVAL;
+       rsn_ie +=  6;
+       rsn_ie_len -= 6;
+
+       /* skip pairwise cipher suite */
+       if (rsn_ie_len < 2)
+               return -EINVAL;
+       cnt = *((u16 *) rsn_ie);
+       rsn_ie += (2 + cnt * 4);
+       rsn_ie_len -= (2 + cnt * 4);
+
+       /* skip akm suite */
+       if (rsn_ie_len < 2)
+               return -EINVAL;
+       cnt = *((u16 *) rsn_ie);
+       rsn_ie += (2 + cnt * 4);
+       rsn_ie_len -= (2 + cnt * 4);
+
+       if (rsn_ie_len < 2)
+               return -EINVAL;
+
+       memcpy(rsn_capab, rsn_ie, 2);
+
+       return 0;
+}
+
 static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
                           struct cfg80211_ap_settings *info)
 {
@@ -2560,6 +2606,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
        struct wmi_connect_cmd p;
        int res;
        int i, ret;
+       u16 rsn_capab = 0;
 
        ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__);
 
@@ -2700,6 +2747,23 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev,
                             vif->next_ch_type != NL80211_CHAN_NO_HT))
                return -EIO;
 
+       /*
+        * Get the PTKSA replay counter in the RSN IE. Supplicant
+        * will use the RSN IE in M3 message and firmware has to
+        * advertise the same in beacon/probe response. Send
+        * the complete RSN IE capability field to firmware
+        */
+       if (!ath6kl_get_rsn_capab(&info->beacon, (u8 *) &rsn_capab) &&
+           test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
+                    ar->fw_capabilities)) {
+               res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx,
+                                           WLAN_EID_RSN, WMI_RSN_IE_CAPB,
+                                           (const u8 *) &rsn_capab,
+                                           sizeof(rsn_capab));
+               if (res < 0)
+                       return res;
+       }
+
        res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p);
        if (res < 0)
                return res;
index 8e7e9480a786f6c66569c87a8df7a5aeeeff2c0e..9d67964a51ddde14fc231076b9c122554479f49b 100644 (file)
@@ -97,6 +97,9 @@ enum ath6kl_fw_capability {
         */
        ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT,
 
+       /* Firmware has support to override rsn cap of rsn ie */
+       ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE,
+
        /* this needs to be last */
        ATH6KL_FW_CAPABILITY_MAX,
 };
index efd707e692556dfa3956247332cf40a73a3f2275..7c8a9977faf5a99b19529fd383782bd30714a514 100644 (file)
@@ -3221,6 +3221,29 @@ int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type,
                                   NO_SYNC_WMIFLAG);
 }
 
+int ath6kl_wmi_set_ie_cmd(struct wmi *wmi, u8 if_idx, u8 ie_id, u8 ie_field,
+                         const u8 *ie_info, u8 ie_len)
+{
+       struct sk_buff *skb;
+       struct wmi_set_ie_cmd *p;
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*p) + ie_len);
+       if (!skb)
+               return -ENOMEM;
+
+       ath6kl_dbg(ATH6KL_DBG_WMI, "set_ie_cmd: ie_id=%u ie_ie_field=%u ie_len=%u\n",
+                  ie_id, ie_field, ie_len);
+       p = (struct wmi_set_ie_cmd *) skb->data;
+       p->ie_id = ie_id;
+       p->ie_field = ie_field;
+       p->ie_len = ie_len;
+       if (ie_info && ie_len > 0)
+               memcpy(p->ie_info, ie_info, ie_len);
+
+       return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_IE_CMDID,
+                                  NO_SYNC_WMIFLAG);
+}
+
 int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable)
 {
        struct sk_buff *skb;
index ee45d102253205d4a25c71f6eeddb1c3e367ed19..d3d2ab5c1689aa85695352d4c7dd97ad8e916a0c 100644 (file)
@@ -426,6 +426,7 @@ enum wmi_cmd_id {
        WMI_SET_FRAMERATES_CMDID,
        WMI_SET_AP_PS_CMDID,
        WMI_SET_QOS_SUPP_CMDID,
+       WMI_SET_IE_CMDID,
 
        /* WMI_THIN_RESERVED_... mark the start and end
         * values for WMI_THIN_RESERVED command IDs. These
@@ -632,6 +633,11 @@ enum wmi_mgmt_frame_type {
        WMI_NUM_MGMT_FRAME
 };
 
+enum wmi_ie_field_type {
+       WMI_RSN_IE_CAPB = 0x1,
+       WMI_IE_FULL     = 0xFF,  /* indicats full IE */
+};
+
 /* WMI_CONNECT_CMDID  */
 enum network_type {
        INFRA_NETWORK = 0x01,
@@ -1926,6 +1932,14 @@ struct wmi_set_appie_cmd {
        u8 ie_info[0];
 } __packed;
 
+struct wmi_set_ie_cmd {
+       u8 ie_id;
+       u8 ie_field;    /* enum wmi_ie_field_type */
+       u8 ie_len;
+       u8 reserved;
+       u8 ie_info[0];
+} __packed;
+
 /* Notify the WSC registration status to the target */
 #define WSC_REG_ACTIVE     1
 #define WSC_REG_INACTIVE   0
@@ -2536,6 +2550,9 @@ int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 if_idx,
 int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type,
                             const u8 *ie, u8 ie_len);
 
+int ath6kl_wmi_set_ie_cmd(struct wmi *wmi, u8 if_idx, u8 ie_id, u8 ie_field,
+                         const u8 *ie_info, u8 ie_len);
+
 /* P2P */
 int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable);