wl1271: Added support to scan on 5 GHz band

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>
This commit is contained in:
Teemu Paasikivi 2009-10-13 12:47:50 +03:00 committed by John W. Linville
parent 311494c47f
commit abb0b3bfb2
7 changed files with 160 additions and 38 deletions

View 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;

View 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,13 +518,26 @@ 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->hw->wiphy->bands[ieee_band]->channels == NULL)
return -EINVAL;
channels = wl->hw->wiphy->bands[ieee_band]->channels;
n_ch = wl->hw->wiphy->bands[ieee_band]->n_channels;
if (wl->scanning)
return -EINVAL;
channels = wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels;
n_ch = wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->n_channels;
params = kzalloc(sizeof(*params), GFP_KERNEL);
if (!params)
return -ENOMEM;
@ -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)

View 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;

View File

@ -31,19 +31,40 @@
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;
}

View 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)

View 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);

View 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);