wl1271: Added support to scan on 5 GHz band
authorTeemu Paasikivi <ext-teemu.3.paasikivi@nokia.com>
Tue, 13 Oct 2009 09:47:50 +0000 (12:47 +0300)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 27 Oct 2009 20:48:10 +0000 (16:48 -0400)
Added support to scan 802.11a access points on 5 GHz band when using
wl1273 chip.

Signed-off-by: Teemu Paasikivi <ext-teemu.3.paasikivi@nokia.com>
Reviewed-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Signed-off-by: Luciano Coelho <luciano.coelho@nokia.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/wl12xx/wl1271.h
drivers/net/wireless/wl12xx/wl1271_cmd.c
drivers/net/wireless/wl12xx/wl1271_cmd.h
drivers/net/wireless/wl12xx/wl1271_event.c
drivers/net/wireless/wl12xx/wl1271_init.c
drivers/net/wireless/wl12xx/wl1271_main.c
drivers/net/wireless/wl12xx/wl1271_rx.c

index dc9957ee1ac3ee7fe1210a9b4ed17b583f81bd16..79a7324431511556c67bc103010da9220bf9d028 100644 (file)
@@ -299,6 +299,15 @@ struct wl1271_rx_mem_pool_addr {
        u32 addr_extra;
 };
 
+struct wl1271_scan {
+       u8 state;
+       u8 ssid[IW_ESSID_MAX_SIZE+1];
+       size_t ssid_len;
+       u8 active;
+       u8 high_prio;
+       u8 probe_requests;
+};
+
 struct wl1271 {
        struct ieee80211_hw *hw;
        bool mac80211_registered;
@@ -382,6 +391,7 @@ struct wl1271 {
 
        /* Are we currently scanning */
        bool scanning;
+       struct wl1271_scan scan;
 
        /* Our association ID */
        u16 aid;
index 204e87218d8f303b7da44ffdb32bbac237293746..6d7a40c004f15ff97bd8774c4f29429edfc2491b 100644 (file)
@@ -509,7 +509,7 @@ out:
 }
 
 int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
-                   u8 active_scan, u8 high_prio, u8 num_channels,
+                   u8 active_scan, u8 high_prio, u8 band,
                    u8 probe_requests)
 {
 
@@ -518,12 +518,25 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
        struct ieee80211_channel *channels;
        int i, j, n_ch, ret;
        u16 scan_options = 0;
+       u8 ieee_band;
+
+       if (band == WL1271_SCAN_BAND_2_4_GHZ)
+               ieee_band = IEEE80211_BAND_2GHZ;
+       else if (band == WL1271_SCAN_BAND_DUAL && wl1271_11a_enabled())
+               ieee_band = IEEE80211_BAND_2GHZ;
+       else if (band == WL1271_SCAN_BAND_5_GHZ && wl1271_11a_enabled())
+               ieee_band = IEEE80211_BAND_5GHZ;
+       else
+               return -EINVAL;
 
-       if (wl->scanning)
+       if (wl->hw->wiphy->bands[ieee_band]->channels == NULL)
                return -EINVAL;
 
-       channels = wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels;
-       n_ch = wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->n_channels;
+       channels = wl->hw->wiphy->bands[ieee_band]->channels;
+       n_ch = wl->hw->wiphy->bands[ieee_band]->n_channels;
+
+       if (wl->scanning)
+               return -EINVAL;
 
        params = kzalloc(sizeof(*params), GFP_KERNEL);
        if (!params)
@@ -540,10 +553,16 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
        params->params.scan_options = scan_options;
 
        params->params.num_probe_requests = probe_requests;
-       params->params.tx_rate = cpu_to_le32(CONF_HW_BIT_RATE_2MBPS);
+       /* Let the fw autodetect suitable tx_rate for probes */
+       params->params.tx_rate = 0;
        params->params.tid_trigger = 0;
        params->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
 
+       if (band == WL1271_SCAN_BAND_DUAL)
+               params->params.band = WL1271_SCAN_BAND_2_4_GHZ;
+       else
+               params->params.band = band;
+
        for (i = 0, j = 0; i < n_ch && i < WL1271_SCAN_MAX_CHANNELS; i++) {
                if (!(channels[i].flags & IEEE80211_CHAN_DISABLED)) {
                        params->channels[j].min_duration =
@@ -567,7 +586,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
                memcpy(params->params.ssid, ssid, len);
        }
 
-       ret = wl1271_cmd_build_probe_req(wl, ssid, len);
+       ret = wl1271_cmd_build_probe_req(wl, ssid, len, ieee_band);
        if (ret < 0) {
                wl1271_error("PROBE request template failed");
                goto out;
@@ -592,6 +611,19 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
        wl1271_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params));
 
        wl->scanning = true;
+       if (wl1271_11a_enabled()) {
+               wl->scan.state = band;
+               if (band == WL1271_SCAN_BAND_DUAL) {
+                       wl->scan.active = active_scan;
+                       wl->scan.high_prio = high_prio;
+                       wl->scan.probe_requests = probe_requests;
+                       if (len && ssid) {
+                               wl->scan.ssid_len = len;
+                               memcpy(wl->scan.ssid, ssid, len);
+                       } else
+                               wl->scan.ssid_len = 0;
+               }
+       }
 
        ret = wl1271_cmd_send(wl, CMD_SCAN, params, sizeof(*params));
        if (ret < 0) {
@@ -654,30 +686,62 @@ out:
        return ret;
 }
 
-static int wl1271_build_basic_rates(char *rates)
+static int wl1271_build_basic_rates(char *rates, u8 band)
 {
        u8 index = 0;
 
-       rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
-       rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
-       rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB;
-       rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB;
+       if (band == IEEE80211_BAND_2GHZ) {
+               rates[index++] =
+                       IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB;
+               rates[index++] =
+                       IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB;
+               rates[index++] =
+                       IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB;
+               rates[index++] =
+                       IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB;
+       } else if (band == IEEE80211_BAND_5GHZ) {
+               rates[index++] =
+                       IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_6MB;
+               rates[index++] =
+                       IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_12MB;
+               rates[index++] =
+                       IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB;
+       } else {
+               wl1271_error("build_basic_rates invalid band: %d", band);
+       }
 
        return index;
 }
 
-static int wl1271_build_extended_rates(char *rates)
+static int wl1271_build_extended_rates(char *rates, u8 band)
 {
        u8 index = 0;
 
-       rates[index++] = IEEE80211_OFDM_RATE_6MB;
-       rates[index++] = IEEE80211_OFDM_RATE_9MB;
-       rates[index++] = IEEE80211_OFDM_RATE_12MB;
-       rates[index++] = IEEE80211_OFDM_RATE_18MB;
-       rates[index++] = IEEE80211_OFDM_RATE_24MB;
-       rates[index++] = IEEE80211_OFDM_RATE_36MB;
-       rates[index++] = IEEE80211_OFDM_RATE_48MB;
-       rates[index++] = IEEE80211_OFDM_RATE_54MB;
+       if (band == IEEE80211_BAND_2GHZ) {
+               rates[index++] = IEEE80211_OFDM_RATE_6MB;
+               rates[index++] = IEEE80211_OFDM_RATE_9MB;
+               rates[index++] = IEEE80211_OFDM_RATE_12MB;
+               rates[index++] = IEEE80211_OFDM_RATE_18MB;
+               rates[index++] = IEEE80211_OFDM_RATE_24MB;
+               rates[index++] = IEEE80211_OFDM_RATE_36MB;
+               rates[index++] = IEEE80211_OFDM_RATE_48MB;
+               rates[index++] = IEEE80211_OFDM_RATE_54MB;
+       } else if (band == IEEE80211_BAND_5GHZ) {
+               rates[index++] =
+                       IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_9MB;
+               rates[index++] =
+                       IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_18MB;
+               rates[index++] =
+                       IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB;
+               rates[index++] =
+                       IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_36MB;
+               rates[index++] =
+                       IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_48MB;
+               rates[index++] =
+                       IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_54MB;
+       } else {
+               wl1271_error("build_basic_rates invalid band: %d", band);
+       }
 
        return index;
 }
@@ -720,12 +784,14 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid)
 
 }
 
-int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len)
+int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len,
+                              u8 band)
 {
        struct wl12xx_probe_req_template template;
        struct wl12xx_ie_rates *rates;
        char *ptr;
        u16 size;
+       int ret;
 
        ptr = (char *)&template;
        size = sizeof(struct ieee80211_header);
@@ -747,20 +813,25 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len)
        /* Basic Rates */
        rates = (struct wl12xx_ie_rates *)ptr;
        rates->header.id = WLAN_EID_SUPP_RATES;
-       rates->header.len = wl1271_build_basic_rates(rates->rates);
+       rates->header.len = wl1271_build_basic_rates(rates->rates, band);
        size += sizeof(struct wl12xx_ie_header) + rates->header.len;
        ptr += sizeof(struct wl12xx_ie_header) + rates->header.len;
 
        /* Extended rates */
        rates = (struct wl12xx_ie_rates *)ptr;
        rates->header.id = WLAN_EID_EXT_SUPP_RATES;
-       rates->header.len = wl1271_build_extended_rates(rates->rates);
+       rates->header.len = wl1271_build_extended_rates(rates->rates, band);
        size += sizeof(struct wl12xx_ie_header) + rates->header.len;
 
        wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size);
 
-       return wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
-                                      &template, size);
+       if (band == IEEE80211_BAND_2GHZ)
+               ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
+                                             &template, size);
+       else
+               ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5,
+                                             &template, size);
+       return ret;
 }
 
 int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id)
index 91430871c8fd896c1807dca2b18dbb10f8ffa042..33089408e75939d9aca691a1a6cecfe281299980 100644 (file)
@@ -39,13 +39,14 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode);
 int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
                           size_t len);
 int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
-                   u8 active_scan, u8 high_prio, u8 num_channels,
+                   u8 active_scan, u8 high_prio, u8 band,
                    u8 probe_requests);
 int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
                            void *buf, size_t buf_len);
 int wl1271_cmd_build_null_data(struct wl1271 *wl);
 int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid);
-int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len);
+int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len,
+                              u8 band);
 int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id);
 int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
                       u8 key_size, const u8 *key, const u8 *addr,
@@ -343,6 +344,9 @@ struct wl1271_cmd_set_keys {
 #define WL1271_SCAN_OPT_PRIORITY_HIGH  4
 #define WL1271_SCAN_CHAN_MIN_DURATION  30000  /* TU */
 #define WL1271_SCAN_CHAN_MAX_DURATION  60000  /* TU */
+#define WL1271_SCAN_BAND_2_4_GHZ 0
+#define WL1271_SCAN_BAND_5_GHZ 1
+#define WL1271_SCAN_BAND_DUAL 2
 
 struct basic_scan_params {
        u32 rx_config_options;
index 4189e97ca80537dca82fdf2d16c0a9d38b12eba6..e2d7758ba1c940665bc3fb21776e36f006d6f4fb 100644 (file)
 static int wl1271_event_scan_complete(struct wl1271 *wl,
                                      struct event_mailbox *mbox)
 {
+       int size = sizeof(struct wl12xx_probe_req_template);
        wl1271_debug(DEBUG_EVENT, "status: 0x%x",
                     mbox->scheduled_scan_status);
 
        if (wl->scanning) {
-               int size = sizeof(struct wl12xx_probe_req_template);
-               wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL,
-                                       size);
-               mutex_unlock(&wl->mutex);
-               ieee80211_scan_completed(wl->hw, false);
-               mutex_lock(&wl->mutex);
-               wl->scanning = false;
+               if (wl->scan.state == WL1271_SCAN_BAND_DUAL) {
+                       wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
+                                               NULL, size);
+                       /* 2.4 GHz band scanned, scan 5 GHz band, pretend
+                        * to the wl1271_cmd_scan function that we are not
+                        * scanning as it checks that.
+                        */
+                       wl->scanning = false;
+                       wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len,
+                                               wl->scan.active,
+                                               wl->scan.high_prio,
+                                               WL1271_SCAN_BAND_5_GHZ,
+                                               wl->scan.probe_requests);
+               } else {
+                       if (wl->scan.state == WL1271_SCAN_BAND_2_4_GHZ)
+                               wl1271_cmd_template_set(wl,
+                                               CMD_TEMPL_CFG_PROBE_REQ_2_4,
+                                               NULL, size);
+                       else
+                               wl1271_cmd_template_set(wl,
+                                               CMD_TEMPL_CFG_PROBE_REQ_5,
+                                               NULL, size);
+
+                       mutex_unlock(&wl->mutex);
+                       ieee80211_scan_completed(wl->hw, false);
+                       mutex_lock(&wl->mutex);
+                       wl->scanning = false;
+               }
        }
-
        return 0;
 }
 
index 6f21eeae5246ff9ae28a88f6a02309e6885e0d6a..417b4152feb17b5c424275a86cdd41c17120a726 100644 (file)
@@ -59,6 +59,14 @@ static int wl1271_init_templates_config(struct wl1271 *wl)
        if (ret < 0)
                return ret;
 
+       if (wl1271_11a_enabled()) {
+               ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5,
+                               NULL,
+                               sizeof(struct wl12xx_probe_req_template));
+               if (ret < 0)
+                       return ret;
+       }
+
        ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL,
                                      sizeof(struct wl12xx_null_data_template));
        if (ret < 0)
index 9b1cc8db1dddf52a38232aa1d8ec1351f63eddea..ae41a70955086188f6590ae63beecde65a6d3ef9 100644 (file)
@@ -1332,13 +1332,13 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
        struct wl1271 *wl = hw->priv;
        int ret;
        u8 *ssid = NULL;
-       size_t ssid_len = 0;
+       size_t len = 0;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan");
 
        if (req->n_ssids) {
                ssid = req->ssids[0].ssid;
-               ssid_len = req->ssids[0].ssid_len;
+               len = req->ssids[0].ssid_len;
        }
 
        mutex_lock(&wl->mutex);
@@ -1347,7 +1347,12 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
-       ret = wl1271_cmd_scan(hw->priv, ssid, ssid_len, 1, 0, 13, 3);
+       if (wl1271_11a_enabled())
+               ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
+                                     WL1271_SCAN_BAND_DUAL, 3);
+       else
+               ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
+                                     WL1271_SCAN_BAND_2_4_GHZ, 3);
 
        wl1271_ps_elp_sleep(wl);
 
index 7979b69ec52d2a1a5a031011b55f09a9fb66a2c3..66b83e922909c8ee605d6363fa1c7a570e916ed7 100644 (file)
@@ -79,6 +79,9 @@ static void wl1271_rx_status(struct wl1271 *wl,
 
        if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == WL1271_RX_DESC_BAND_BG)
                status->band = IEEE80211_BAND_2GHZ;
+       else if ((desc->flags & WL1271_RX_DESC_BAND_MASK) ==
+                WL1271_RX_DESC_BAND_A)
+               status->band = IEEE80211_BAND_5GHZ;
        else
                wl1271_warning("unsupported band 0x%x",
                               desc->flags & WL1271_RX_DESC_BAND_MASK);