wl1271: Fix setting of the hardware connection monitoring probe-req template
authorJuuso Oikarinen <juuso.oikarinen@nokia.com>
Wed, 24 Nov 2010 06:16:57 +0000 (08:16 +0200)
committerLuciano Coelho <luciano.coelho@nokia.com>
Fri, 26 Nov 2010 13:33:41 +0000 (15:33 +0200)
The probe-request template used in the hardware connection monitoring feature
thus far has been an empty one, without the SSID IE and without supported rate
IEs. This causes problems with some AP's.

Additionally, after connected scans, the template for connection maintenance
would remain to be the one last used for scanning - potentially incorrect.

Fix these by getting a pre-filled directed probe-request template for the
associated-to AP from mac80211.

Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
drivers/net/wireless/wl12xx/cmd.c
drivers/net/wireless/wl12xx/cmd.h
drivers/net/wireless/wl12xx/main.c
drivers/net/wireless/wl12xx/scan.c
drivers/net/wireless/wl12xx/wl12xx.h

index f3d0541aaad6297307551ffbd585225d5911f018..8e438e27e49651bf93a09ea57e2bbe2395b5675e 100644 (file)
@@ -611,6 +611,34 @@ out:
        return ret;
 }
 
+struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
+                                             struct sk_buff *skb)
+{
+       int ret;
+
+       if (!skb)
+               skb = ieee80211_ap_probereq_get(wl->hw, wl->vif);
+       if (!skb)
+               goto out;
+
+       wl1271_dump(DEBUG_SCAN, "AP PROBE REQ: ", skb->data, skb->len);
+
+       if (wl->band == IEEE80211_BAND_2GHZ)
+               ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
+                                             skb->data, skb->len, 0,
+                                             wl->conf.tx.basic_rate);
+       else
+               ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5,
+                                             skb->data, skb->len, 0,
+                                             wl->conf.tx.basic_rate_5);
+
+       if (ret < 0)
+               wl1271_error("Unable to set ap probe request template.");
+
+out:
+       return skb;
+}
+
 int wl1271_build_qos_null_data(struct wl1271 *wl)
 {
        struct ieee80211_qos_hdr template;
index 16d1bf814e76ca11780946f0069d30a43a3b06c7..111d112544fc3358a80a60c07539e9f831313ce5 100644 (file)
@@ -49,6 +49,8 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid);
 int wl1271_cmd_build_probe_req(struct wl1271 *wl,
                               const u8 *ssid, size_t ssid_len,
                               const u8 *ie, size_t ie_len, u8 band);
+struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
+                                             struct sk_buff *skb);
 int wl1271_build_qos_null_data(struct wl1271 *wl);
 int wl1271_cmd_build_klv_null_data(struct wl1271 *wl);
 int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id);
index 97eb186b5a8adfd70f9070f343fe4db4a2eca1f5..b2432dab4b51a1a4d5b8ec9252d7f0f0db022c3f 100644 (file)
@@ -1814,21 +1814,21 @@ out:
        return ret;
 }
 
-static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *beacon)
+static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb,
+                           int offset)
 {
-       u8 *ptr = beacon->data +
-               offsetof(struct ieee80211_mgmt, u.beacon.variable);
+       u8 *ptr = skb->data + offset;
 
        /* find the location of the ssid in the beacon */
-       while (ptr < beacon->data + beacon->len) {
+       while (ptr < skb->data + skb->len) {
                if (ptr[0] == WLAN_EID_SSID) {
                        wl->ssid_len = ptr[1];
                        memcpy(wl->ssid, ptr+2, wl->ssid_len);
                        return;
                }
-               ptr += ptr[1];
+               ptr += (ptr[1] + 2);
        }
-       wl1271_error("ad-hoc beacon template has no SSID!\n");
+       wl1271_error("No SSID in IEs!\n");
 }
 
 static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
@@ -1871,8 +1871,11 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
 
                if (beacon) {
                        struct ieee80211_hdr *hdr;
+                       int ieoffset = offsetof(struct ieee80211_mgmt,
+                                               u.beacon.variable);
+
+                       wl1271_ssid_set(wl, beacon, ieoffset);
 
-                       wl1271_ssid_set(wl, beacon);
                        ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON,
                                                      beacon->data,
                                                      beacon->len, 0,
@@ -1952,6 +1955,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
        if (changed & BSS_CHANGED_ASSOC) {
                if (bss_conf->assoc) {
                        u32 rates;
+                       int ieoffset;
                        wl->aid = bss_conf->aid;
                        set_assoc = true;
 
@@ -1980,13 +1984,13 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
                                goto out_sleep;
 
                        /*
-                        * The SSID is intentionally set to NULL here - the
-                        * firmware will set the probe request with a
-                        * broadcast SSID regardless of what we set in the
-                        * template.
+                        * Get a template for hardware connection maintenance
                         */
-                       ret = wl1271_cmd_build_probe_req(wl, NULL, 0,
-                                                        NULL, 0, wl->band);
+                       dev_kfree_skb(wl->probereq);
+                       wl->probereq = wl1271_cmd_build_ap_probe_req(wl, NULL);
+                       ieoffset = offsetof(struct ieee80211_mgmt,
+                                           u.probe_req.variable);
+                       wl1271_ssid_set(wl, wl->probereq, ieoffset);
 
                        /* enable the connection monitoring feature */
                        ret = wl1271_acx_conn_monit_params(wl, true);
@@ -2009,6 +2013,10 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
                        clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
                        wl->aid = 0;
 
+                       /* free probe-request template */
+                       dev_kfree_skb(wl->probereq);
+                       wl->probereq = NULL;
+
                        /* re-enable dynamic ps - just in case */
                        ieee80211_enable_dyn_ps(wl->vif);
 
index f3f2c5b011ee1349dc8ecafc1f19afc9580d2574..6f897b9d90caf21784342d700bba64a98b40dc09 100644 (file)
@@ -51,6 +51,10 @@ void wl1271_scan_complete_work(struct work_struct *work)
        wl->scan.req = NULL;
        ieee80211_scan_completed(wl->hw, false);
 
+       /* restore hardware connection monitoring template */
+       if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
+               wl1271_cmd_build_ap_probe_req(wl, wl->probereq);
+
        if (wl->scan.failed) {
                wl1271_info("Scan completed due to error.");
                ieee80211_queue_work(wl->hw, &wl->recovery_work);
index 3c836e6063e6bac5474eb1db1290159eaeaccdfb..9f8aa695c3afe37b1e03b89e65be37219b0bbac1 100644 (file)
@@ -431,6 +431,9 @@ struct wl1271 {
        struct wl1271_scan scan;
        struct delayed_work scan_complete_work;
 
+       /* probe-req template for the current AP */
+       struct sk_buff *probereq;
+
        /* Our association ID */
        u16 aid;