aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/wl12xx
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/wl12xx')
-rw-r--r--drivers/net/wireless/wl12xx/Makefile2
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_main.c1
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_spi.c1
-rw-r--r--drivers/net/wireless/wl12xx/wl1271.h24
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_acx.c41
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_acx.h15
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_boot.c4
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_cmd.c248
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_cmd.h68
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_conf.h16
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_event.c99
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_event.h1
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_main.c174
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_rx.c6
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_scan.c257
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_scan.h109
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_spi.c1
17 files changed, 579 insertions, 488 deletions
diff --git a/drivers/net/wireless/wl12xx/Makefile b/drivers/net/wireless/wl12xx/Makefile
index 27ddd2be0a91..078b4398ac1f 100644
--- a/drivers/net/wireless/wl12xx/Makefile
+++ b/drivers/net/wireless/wl12xx/Makefile
@@ -10,7 +10,7 @@ obj-$(CONFIG_WL1251_SDIO) += wl1251_sdio.o
wl1271-objs = wl1271_main.o wl1271_cmd.o wl1271_io.o \
wl1271_event.o wl1271_tx.o wl1271_rx.o \
wl1271_ps.o wl1271_acx.o wl1271_boot.o \
- wl1271_init.o wl1271_debugfs.o
+ wl1271_init.o wl1271_debugfs.o wl1271_scan.o
wl1271-$(CONFIG_NL80211_TESTMODE) += wl1271_testmode.o
obj-$(CONFIG_WL1271) += wl1271.o
diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c
index c8f268951e10..38f72f417183 100644
--- a/drivers/net/wireless/wl12xx/wl1251_main.c
+++ b/drivers/net/wireless/wl12xx/wl1251_main.c
@@ -1417,5 +1417,4 @@ EXPORT_SYMBOL_GPL(wl1251_free_hw);
MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>");
-MODULE_ALIAS("spi:wl1251");
MODULE_FIRMWARE(WL1251_FW_NAME);
diff --git a/drivers/net/wireless/wl12xx/wl1251_spi.c b/drivers/net/wireless/wl12xx/wl1251_spi.c
index e81474203a23..27fdfaaeb074 100644
--- a/drivers/net/wireless/wl12xx/wl1251_spi.c
+++ b/drivers/net/wireless/wl12xx/wl1251_spi.c
@@ -345,3 +345,4 @@ module_exit(wl1251_spi_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>");
+MODULE_ALIAS("spi:wl1251");
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index ec09f0d40ca2..53d47d7a2a1d 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -300,12 +300,10 @@ struct wl1271_rx_mem_pool_addr {
struct wl1271_scan {
struct cfg80211_scan_request *req;
+ bool *scanned_ch;
u8 state;
u8 ssid[IW_ESSID_MAX_SIZE+1];
size_t ssid_len;
- u8 active;
- u8 high_prio;
- u8 probe_requests;
};
struct wl1271_if_operations {
@@ -343,14 +341,14 @@ struct wl1271 {
#define WL1271_FLAG_JOINED (2)
#define WL1271_FLAG_GPIO_POWER (3)
#define WL1271_FLAG_TX_QUEUE_STOPPED (4)
-#define WL1271_FLAG_SCANNING (5)
-#define WL1271_FLAG_IN_ELP (6)
-#define WL1271_FLAG_PSM (7)
-#define WL1271_FLAG_PSM_REQUESTED (8)
-#define WL1271_FLAG_IRQ_PENDING (9)
-#define WL1271_FLAG_IRQ_RUNNING (10)
-#define WL1271_FLAG_IDLE (11)
-#define WL1271_FLAG_IDLE_REQUESTED (12)
+#define WL1271_FLAG_IN_ELP (5)
+#define WL1271_FLAG_PSM (6)
+#define WL1271_FLAG_PSM_REQUESTED (7)
+#define WL1271_FLAG_IRQ_PENDING (8)
+#define WL1271_FLAG_IRQ_RUNNING (9)
+#define WL1271_FLAG_IDLE (10)
+#define WL1271_FLAG_IDLE_REQUESTED (11)
+#define WL1271_FLAG_PSPOLL_FAILURE (12)
unsigned long flags;
struct wl1271_partition_set part;
@@ -445,6 +443,10 @@ struct wl1271 {
struct completion *elp_compl;
struct delayed_work elp_work;
+ struct delayed_work pspoll_work;
+
+ /* counter for ps-poll delivery failures */
+ int ps_poll_failures;
/* retry counter for PSM entries */
u8 psm_entry_retry;
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c
index e19e2f8f1e52..bb245f05af49 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.c
@@ -1075,8 +1075,7 @@ out:
return ret;
}
-int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address,
- u8 version)
+int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, __be32 address)
{
struct wl1271_acx_arp_filter *acx;
int ret;
@@ -1089,17 +1088,11 @@ int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address,
goto out;
}
- acx->version = version;
+ acx->version = ACX_IPV4_VERSION;
acx->enable = enable;
- if (enable == true) {
- if (version == ACX_IPV4_VERSION)
- memcpy(acx->address, address, ACX_IPV4_ADDR_SIZE);
- else if (version == ACX_IPV6_VERSION)
- memcpy(acx->address, address, sizeof(acx->address));
- else
- wl1271_error("Invalid IP version");
- }
+ if (enable == true)
+ memcpy(acx->address, &address, ACX_IPV4_ADDR_SIZE);
ret = wl1271_cmd_configure(wl, ACX_ARP_IP_FILTER,
acx, sizeof(*acx));
@@ -1266,3 +1259,29 @@ out:
kfree(acx);
return ret;
}
+
+int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime)
+{
+ struct wl1271_acx_fw_tsf_information *tsf_info;
+ int ret;
+
+ tsf_info = kzalloc(sizeof(*tsf_info), GFP_KERNEL);
+ if (!tsf_info) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = wl1271_cmd_interrogate(wl, ACX_TSF_INFO,
+ tsf_info, sizeof(*tsf_info));
+ if (ret < 0) {
+ wl1271_warning("acx tsf info interrogate failed");
+ goto out;
+ }
+
+ *mactime = le32_to_cpu(tsf_info->current_tsf_low) |
+ ((u64) le32_to_cpu(tsf_info->current_tsf_high) << 32);
+
+out:
+ kfree(tsf_info);
+ return ret;
+}
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h
index 4c87e601df2f..4235bc56f750 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.h
@@ -993,6 +993,17 @@ struct wl1271_acx_rssi_snr_avg_weights {
u8 snr_data;
};
+struct wl1271_acx_fw_tsf_information {
+ struct acx_header header;
+
+ __le32 current_tsf_high;
+ __le32 current_tsf_low;
+ __le32 last_bttt_high;
+ __le32 last_tbtt_low;
+ u8 last_dtim_count;
+ u8 padding[3];
+} __packed;
+
enum {
ACX_WAKE_UP_CONDITIONS = 0x0002,
ACX_MEM_CFG = 0x0003,
@@ -1106,13 +1117,13 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl);
int wl1271_acx_init_rx_interrupt(struct wl1271 *wl);
int wl1271_acx_smart_reflex(struct wl1271 *wl);
int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable);
-int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address,
- u8 version);
+int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, __be32 address);
int wl1271_acx_pm_config(struct wl1271 *wl);
int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable);
int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid);
int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
s16 thold, u8 hyst);
int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl);
+int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
#endif /* __WL1271_ACX_H__ */
diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c
index 1a36d8a2196e..f36430b0336d 100644
--- a/drivers/net/wireless/wl12xx/wl1271_boot.c
+++ b/drivers/net/wireless/wl12xx/wl1271_boot.c
@@ -414,7 +414,9 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
PS_REPORT_EVENT_ID |
JOIN_EVENT_COMPLETE_ID |
DISCONNECT_EVENT_COMPLETE_ID |
- RSSI_SNR_TRIGGER_0_EVENT_ID;
+ RSSI_SNR_TRIGGER_0_EVENT_ID |
+ PSPOLL_DELIVERY_FAILURE_EVENT_ID |
+ SOFT_GEMINI_SENSE_EVENT_ID;
ret = wl1271_event_unmask(wl);
if (ret < 0) {
diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c
index 530678e45a13..ce503ddd5a41 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.c
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c
@@ -104,100 +104,6 @@ out:
return ret;
}
-static int wl1271_cmd_cal_channel_tune(struct wl1271 *wl)
-{
- struct wl1271_cmd_cal_channel_tune *cmd;
- int ret = 0;
-
- cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
- if (!cmd)
- return -ENOMEM;
-
- cmd->test.id = TEST_CMD_CHANNEL_TUNE;
-
- cmd->band = WL1271_CHANNEL_TUNE_BAND_2_4;
- /* set up any channel, 7 is in the middle of the range */
- cmd->channel = 7;
-
- ret = wl1271_cmd_test(wl, cmd, sizeof(*cmd), 0);
- if (ret < 0)
- wl1271_warning("TEST_CMD_CHANNEL_TUNE failed");
-
- kfree(cmd);
- return ret;
-}
-
-static int wl1271_cmd_cal_update_ref_point(struct wl1271 *wl)
-{
- struct wl1271_cmd_cal_update_ref_point *cmd;
- int ret = 0;
-
- cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
- if (!cmd)
- return -ENOMEM;
-
- cmd->test.id = TEST_CMD_UPDATE_PD_REFERENCE_POINT;
-
- /* FIXME: still waiting for the correct values */
- cmd->ref_power = 0;
- cmd->ref_detector = 0;
-
- cmd->sub_band = WL1271_PD_REFERENCE_POINT_BAND_B_G;
-
- ret = wl1271_cmd_test(wl, cmd, sizeof(*cmd), 0);
- if (ret < 0)
- wl1271_warning("TEST_CMD_UPDATE_PD_REFERENCE_POINT failed");
-
- kfree(cmd);
- return ret;
-}
-
-static int wl1271_cmd_cal_p2g(struct wl1271 *wl)
-{
- struct wl1271_cmd_cal_p2g *cmd;
- int ret = 0;
-
- cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
- if (!cmd)
- return -ENOMEM;
-
- cmd->test.id = TEST_CMD_P2G_CAL;
-
- cmd->sub_band_mask = WL1271_CAL_P2G_BAND_B_G;
-
- ret = wl1271_cmd_test(wl, cmd, sizeof(*cmd), 0);
- if (ret < 0)
- wl1271_warning("TEST_CMD_P2G_CAL failed");
-
- kfree(cmd);
- return ret;
-}
-
-static int wl1271_cmd_cal(struct wl1271 *wl)
-{
- /*
- * FIXME: we must make sure that we're not sleeping when calibration
- * is done
- */
- int ret;
-
- wl1271_notice("performing tx calibration");
-
- ret = wl1271_cmd_cal_channel_tune(wl);
- if (ret < 0)
- return ret;
-
- ret = wl1271_cmd_cal_update_ref_point(wl);
- if (ret < 0)
- return ret;
-
- ret = wl1271_cmd_cal_p2g(wl);
- if (ret < 0)
- return ret;
-
- return ret;
-}
-
int wl1271_cmd_general_parms(struct wl1271 *wl)
{
struct wl1271_general_parms_cmd *gen_parms;
@@ -226,7 +132,7 @@ int wl1271_cmd_general_parms(struct wl1271 *wl)
int wl1271_cmd_radio_parms(struct wl1271 *wl)
{
struct wl1271_radio_parms_cmd *radio_parms;
- struct conf_radio_parms *rparam = &wl->conf.init.radioparam;
+ struct wl1271_ini_general_params *gp = &wl->nvs->general_params;
int ret;
if (!wl->nvs)
@@ -242,7 +148,7 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl)
memcpy(&radio_parms->static_params_2, &wl->nvs->stat_radio_params_2,
sizeof(struct wl1271_ini_band_params_2));
memcpy(&radio_parms->dyn_params_2,
- &wl->nvs->dyn_radio_params_2[rparam->fem].params,
+ &wl->nvs->dyn_radio_params_2[gp->tx_bip_fem_manufacturer].params,
sizeof(struct wl1271_ini_fem_params_2));
/* 5GHz parameters */
@@ -250,7 +156,7 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl)
&wl->nvs->stat_radio_params_5,
sizeof(struct wl1271_ini_band_params_5));
memcpy(&radio_parms->dyn_params_5,
- &wl->nvs->dyn_radio_params_5[rparam->fem].params,
+ &wl->nvs->dyn_radio_params_5[gp->tx_bip_fem_manufacturer].params,
sizeof(struct wl1271_ini_fem_params_5));
wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ",
@@ -295,20 +201,10 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type)
{
- static bool do_cal = true;
struct wl1271_cmd_join *join;
int ret, i;
u8 *bssid;
- /* FIXME: remove when we get calibration from the factory */
- if (do_cal) {
- ret = wl1271_cmd_cal(wl);
- if (ret < 0)
- wl1271_warning("couldn't calibrate");
- else
- do_cal = false;
- }
-
join = kzalloc(sizeof(*join), GFP_KERNEL);
if (!join) {
ret = -ENOMEM;
@@ -567,142 +463,6 @@ out:
return ret;
}
-int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
- struct cfg80211_scan_request *req, u8 active_scan,
- u8 high_prio, u8 band, u8 probe_requests)
-{
-
- struct wl1271_cmd_trigger_scan_to *trigger = NULL;
- struct wl1271_cmd_scan *params = NULL;
- struct ieee80211_channel *channels;
- u32 rate;
- 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;
- rate = wl->conf.tx.basic_rate;
- } else if (band == WL1271_SCAN_BAND_DUAL && wl1271_11a_enabled()) {
- ieee_band = IEEE80211_BAND_2GHZ;
- rate = wl->conf.tx.basic_rate;
- } else if (band == WL1271_SCAN_BAND_5_GHZ && wl1271_11a_enabled()) {
- ieee_band = IEEE80211_BAND_5GHZ;
- rate = wl->conf.tx.basic_rate_5;
- } 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 (test_bit(WL1271_FLAG_SCANNING, &wl->flags))
- return -EINVAL;
-
- params = kzalloc(sizeof(*params), GFP_KERNEL);
- if (!params)
- return -ENOMEM;
-
- params->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD);
- params->params.rx_filter_options =
- cpu_to_le32(CFG_RX_PRSP_EN | CFG_RX_MGMT_EN | CFG_RX_BCN_EN);
-
- if (!active_scan)
- scan_options |= WL1271_SCAN_OPT_PASSIVE;
- if (high_prio)
- scan_options |= WL1271_SCAN_OPT_PRIORITY_HIGH;
- params->params.scan_options = cpu_to_le16(scan_options);
-
- params->params.num_probe_requests = probe_requests;
- params->params.tx_rate = cpu_to_le32(rate);
- 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 =
- cpu_to_le32(WL1271_SCAN_CHAN_MIN_DURATION);
- params->channels[j].max_duration =
- cpu_to_le32(WL1271_SCAN_CHAN_MAX_DURATION);
- memset(&params->channels[j].bssid_lsb, 0xff, 4);
- memset(&params->channels[j].bssid_msb, 0xff, 2);
- params->channels[j].early_termination = 0;
- params->channels[j].tx_power_att =
- WL1271_SCAN_CURRENT_TX_PWR;
- params->channels[j].channel = channels[i].hw_value;
- j++;
- }
- }
-
- params->params.num_channels = j;
-
- if (ssid_len && ssid) {
- params->params.ssid_len = ssid_len;
- memcpy(params->params.ssid, ssid, ssid_len);
- }
-
- ret = wl1271_cmd_build_probe_req(wl, ssid, ssid_len,
- req->ie, req->ie_len, ieee_band);
- if (ret < 0) {
- wl1271_error("PROBE request template failed");
- goto out;
- }
-
- trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
- if (!trigger) {
- ret = -ENOMEM;
- goto out;
- }
-
- /* disable the timeout */
- trigger->timeout = 0;
-
- ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger,
- sizeof(*trigger), 0);
- if (ret < 0) {
- wl1271_error("trigger scan to failed for hw scan");
- goto out;
- }
-
- wl1271_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params));
-
- set_bit(WL1271_FLAG_SCANNING, &wl->flags);
- 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 (ssid_len && ssid) {
- wl->scan.ssid_len = ssid_len;
- memcpy(wl->scan.ssid, ssid, ssid_len);
- } else
- wl->scan.ssid_len = 0;
- wl->scan.req = req;
- } else
- wl->scan.req = NULL;
- }
-
- ret = wl1271_cmd_send(wl, CMD_SCAN, params, sizeof(*params), 0);
- if (ret < 0) {
- wl1271_error("SCAN failed");
- clear_bit(WL1271_FLAG_SCANNING, &wl->flags);
- goto out;
- }
-
-out:
- kfree(params);
- kfree(trigger);
- return ret;
-}
-
int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
void *buf, size_t buf_len, int index, u32 rates)
{
@@ -807,7 +567,7 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid)
goto out;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data,
- skb->len, 0, wl->basic_rate);
+ skb->len, 0, wl->basic_rate_set);
out:
dev_kfree_skb(skb);
diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h
index f5745d829c9b..af577ee8eb02 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.h
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h
@@ -41,9 +41,6 @@ int wl1271_cmd_data_path(struct wl1271 *wl, bool enable);
int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, bool send);
int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
size_t len);
-int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
- struct cfg80211_scan_request *req, 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 index, u32 rates);
int wl1271_cmd_build_null_data(struct wl1271 *wl);
@@ -350,71 +347,6 @@ struct wl1271_cmd_set_keys {
__le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY];
} __packed;
-
-#define WL1271_SCAN_MAX_CHANNELS 24
-#define WL1271_SCAN_DEFAULT_TAG 1
-#define WL1271_SCAN_CURRENT_TX_PWR 0
-#define WL1271_SCAN_OPT_ACTIVE 0
-#define WL1271_SCAN_OPT_PASSIVE 1
-#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 {
- __le32 rx_config_options;
- __le32 rx_filter_options;
- /* Scan option flags (WL1271_SCAN_OPT_*) */
- __le16 scan_options;
- /* Number of scan channels in the list (maximum 30) */
- u8 num_channels;
- /* This field indicates the number of probe requests to send
- per channel for an active scan */
- u8 num_probe_requests;
- /* Rate bit field for sending the probes */
- __le32 tx_rate;
- u8 tid_trigger;
- u8 ssid_len;
- /* in order to align */
- u8 padding1[2];
- u8 ssid[IW_ESSID_MAX_SIZE];
- /* Band to scan */
- u8 band;
- u8 use_ssid_list;
- u8 scan_tag;
- u8 padding2;
-} __packed;
-
-struct basic_scan_channel_params {
- /* Duration in TU to wait for frames on a channel for active scan */
- __le32 min_duration;
- __le32 max_duration;
- __le32 bssid_lsb;
- __le16 bssid_msb;
- u8 early_termination;
- u8 tx_power_att;
- u8 channel;
- /* FW internal use only! */
- u8 dfs_candidate;
- u8 activity_detected;
- u8 pad;
-} __packed;
-
-struct wl1271_cmd_scan {
- struct wl1271_cmd_header header;
-
- struct basic_scan_params params;
- struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS];
-} __packed;
-
-struct wl1271_cmd_trigger_scan_to {
- struct wl1271_cmd_header header;
-
- __le32 timeout;
-} __packed;
-
struct wl1271_cmd_test_header {
u8 id;
u8 padding[3];
diff --git a/drivers/net/wireless/wl12xx/wl1271_conf.h b/drivers/net/wireless/wl12xx/wl1271_conf.h
index d046d044b5bd..0435ffda8f73 100644
--- a/drivers/net/wireless/wl12xx/wl1271_conf.h
+++ b/drivers/net/wireless/wl12xx/wl1271_conf.h
@@ -874,6 +874,13 @@ struct conf_conn_settings {
u8 ps_poll_threshold;
/*
+ * PS Poll failure recovery ACTIVE period length
+ *
+ * Range: u32 (ms)
+ */
+ u32 ps_poll_recovery_period;
+
+ /*
* Configuration of signal average weights.
*/
struct conf_sig_weights sig_weights;
@@ -948,14 +955,6 @@ struct conf_radio_parms {
u8 fem;
};
-struct conf_init_settings {
- /*
- * Configure radio parameters.
- */
- struct conf_radio_parms radioparam;
-
-};
-
struct conf_itrim_settings {
/* enable dco itrim */
u8 enable;
@@ -1022,7 +1021,6 @@ struct conf_drv_settings {
struct conf_rx_settings rx;
struct conf_tx_settings tx;
struct conf_conn_settings conn;
- struct conf_init_settings init;
struct conf_itrim_settings itrim;
struct conf_pm_config_settings pm_config;
struct conf_roam_trigger_settings roam_trigger;
diff --git a/drivers/net/wireless/wl12xx/wl1271_event.c b/drivers/net/wireless/wl12xx/wl1271_event.c
index ca52cdec7a8f..25ce2cd5e3f3 100644
--- a/drivers/net/wireless/wl12xx/wl1271_event.c
+++ b/drivers/net/wireless/wl12xx/wl1271_event.c
@@ -26,36 +26,64 @@
#include "wl1271_io.h"
#include "wl1271_event.h"
#include "wl1271_ps.h"
+#include "wl1271_scan.h"
#include "wl12xx_80211.h"
-static int wl1271_event_scan_complete(struct wl1271 *wl,
- struct event_mailbox *mbox)
+void wl1271_pspoll_work(struct work_struct *work)
{
- wl1271_debug(DEBUG_EVENT, "status: 0x%x",
- mbox->scheduled_scan_status);
-
- if (test_bit(WL1271_FLAG_SCANNING, &wl->flags)) {
- if (wl->scan.state == WL1271_SCAN_BAND_DUAL) {
- /* 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.
- */
- clear_bit(WL1271_FLAG_SCANNING, &wl->flags);
- /* FIXME: ie missing! */
- wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len,
- wl->scan.req,
- wl->scan.active,
- wl->scan.high_prio,
- WL1271_SCAN_BAND_5_GHZ,
- wl->scan.probe_requests);
- } else {
- mutex_unlock(&wl->mutex);
- ieee80211_scan_completed(wl->hw, false);
- mutex_lock(&wl->mutex);
- clear_bit(WL1271_FLAG_SCANNING, &wl->flags);
- }
+ struct delayed_work *dwork;
+ struct wl1271 *wl;
+
+ dwork = container_of(work, struct delayed_work, work);
+ wl = container_of(dwork, struct wl1271, pspoll_work);
+
+ wl1271_debug(DEBUG_EVENT, "pspoll work");
+
+ mutex_lock(&wl->mutex);
+
+ if (!test_and_clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags))
+ goto out;
+
+ if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
+ goto out;
+
+ /*
+ * if we end up here, then we were in powersave when the pspoll
+ * delivery failure occurred, and no-one changed state since, so
+ * we should go back to powersave.
+ */
+ wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, true);
+
+out:
+ mutex_unlock(&wl->mutex);
+};
+
+static void wl1271_event_pspoll_delivery_fail(struct wl1271 *wl)
+{
+ int delay = wl->conf.conn.ps_poll_recovery_period;
+ int ret;
+
+ wl->ps_poll_failures++;
+ if (wl->ps_poll_failures == 1)
+ wl1271_info("AP with dysfunctional ps-poll, "
+ "trying to work around it.");
+
+ /* force active mode receive data from the AP */
+ if (test_bit(WL1271_FLAG_PSM, &wl->flags)) {
+ ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, true);
+ if (ret < 0)
+ return;
+ set_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
+ ieee80211_queue_delayed_work(wl->hw, &wl->pspoll_work,
+ msecs_to_jiffies(delay));
}
- return 0;
+
+ /*
+ * If already in active mode, lets we should be getting data from
+ * the AP right away. If we enter PSM too fast after this, and data
+ * remains on the AP, we will get another event like this, and we'll
+ * go into active once more.
+ */
}
static int wl1271_event_ps_report(struct wl1271 *wl,
@@ -163,9 +191,19 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector);
if (vector & SCAN_COMPLETE_EVENT_ID) {
- ret = wl1271_event_scan_complete(wl, mbox);
- if (ret < 0)
- return ret;
+ wl1271_debug(DEBUG_EVENT, "status: 0x%x",
+ mbox->scheduled_scan_status);
+
+ wl1271_scan_stm(wl);
+ }
+
+ /* disable dynamic PS when requested by the firmware */
+ if (vector & SOFT_GEMINI_SENSE_EVENT_ID &&
+ wl->bss_type == BSS_TYPE_STA_BSS) {
+ if (mbox->soft_gemini_sense_info)
+ ieee80211_disable_dyn_ps(wl->vif);
+ else
+ ieee80211_enable_dyn_ps(wl->vif);
}
/*
@@ -191,6 +229,9 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
return ret;
}
+ if (vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID)
+ wl1271_event_pspoll_delivery_fail(wl);
+
if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT");
if (wl->vif)
diff --git a/drivers/net/wireless/wl12xx/wl1271_event.h b/drivers/net/wireless/wl12xx/wl1271_event.h
index 43d5aeae1783..e4751667cf5e 100644
--- a/drivers/net/wireless/wl12xx/wl1271_event.h
+++ b/drivers/net/wireless/wl12xx/wl1271_event.h
@@ -121,5 +121,6 @@ struct event_mailbox {
int wl1271_event_unmask(struct wl1271 *wl);
void wl1271_event_mbox_config(struct wl1271 *wl);
int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
+void wl1271_pspoll_work(struct work_struct *work);
#endif
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index 7a14da506d78..d30de58cef90 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -28,7 +28,6 @@
#include <linux/crc32.h>
#include <linux/etherdevice.h>
#include <linux/vmalloc.h>
-#include <linux/inetdevice.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -45,6 +44,7 @@
#include "wl1271_cmd.h"
#include "wl1271_boot.h"
#include "wl1271_testmode.h"
+#include "wl1271_scan.h"
#define WL1271_BOOT_RETRIES 3
@@ -55,7 +55,7 @@ static struct conf_drv_settings default_conf = {
[CONF_SG_HV3_MAX_OVERRIDE] = 0,
[CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400,
[CONF_SG_BT_LOAD_RATIO] = 50,
- [CONF_SG_AUTO_PS_MODE] = 0,
+ [CONF_SG_AUTO_PS_MODE] = 1,
[CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
[CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
[CONF_SG_ANTENNA_CONFIGURATION] = 0,
@@ -234,18 +234,14 @@ static struct conf_drv_settings default_conf = {
.beacon_rx_timeout = 10000,
.broadcast_timeout = 20000,
.rx_broadcast_in_ps = 1,
- .ps_poll_threshold = 20,
+ .ps_poll_threshold = 10,
+ .ps_poll_recovery_period = 700,
.bet_enable = CONF_BET_MODE_ENABLE,
.bet_max_consecutive = 10,
.psm_entry_retries = 3,
.keep_alive_interval = 55000,
.max_listen_interval = 20,
},
- .init = {
- .radioparam = {
- .fem = 1,
- }
- },
.itrim = {
.enable = false,
.timeout = 50000,
@@ -818,93 +814,6 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
return NETDEV_TX_OK;
}
-static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
- void *arg)
-{
- struct net_device *dev;
- struct wireless_dev *wdev;
- struct wiphy *wiphy;
- struct ieee80211_hw *hw;
- struct wl1271 *wl;
- struct wl1271 *wl_temp;
- struct in_device *idev;
- struct in_ifaddr *ifa = arg;
- int ret = 0;
-
- /* FIXME: this ugly function should probably be implemented in the
- * mac80211, and here should only be a simple callback handling actual
- * setting of the filters. Now we need to dig up references to
- * various structures to gain access to what we need.
- * Also, because of this, there is no "initial" setting of the filter
- * in "op_start", because we don't want to dig up struct net_device
- * there - the filter will be set upon first change of the interface
- * IP address. */
-
- dev = ifa->ifa_dev->dev;
-
- wdev = dev->ieee80211_ptr;
- if (wdev == NULL)
- return NOTIFY_DONE;
-
- wiphy = wdev->wiphy;
- if (wiphy == NULL)
- return NOTIFY_DONE;
-
- hw = wiphy_priv(wiphy);
- if (hw == NULL)
- return NOTIFY_DONE;
-
- /* Check that the interface is one supported by this driver. */
- wl_temp = hw->priv;
- list_for_each_entry(wl, &wl_list, list) {
- if (wl == wl_temp)
- break;
- }
- if (wl != wl_temp)
- return NOTIFY_DONE;
-
- /* Get the interface IP address for the device. "ifa" will become
- NULL if:
- - there is no IPV4 protocol address configured
- - there are multiple (virtual) IPV4 addresses configured
- When "ifa" is NULL, filtering will be disabled.
- */
- ifa = NULL;
- idev = dev->ip_ptr;
- if (idev)
- ifa = idev->ifa_list;
-
- if (ifa && ifa->ifa_next)
- ifa = NULL;
-
- mutex_lock(&wl->mutex);
-
- if (wl->state == WL1271_STATE_OFF)
- goto out;
-
- ret = wl1271_ps_elp_wakeup(wl, false);
- if (ret < 0)
- goto out;
- if (ifa)
- ret = wl1271_acx_arp_ip_filter(wl, true,
- (u8 *)&ifa->ifa_address,
- ACX_IPV4_VERSION);
- else
- ret = wl1271_acx_arp_ip_filter(wl, false, NULL,
- ACX_IPV4_VERSION);
- wl1271_ps_elp_sleep(wl);
-
-out:
- mutex_unlock(&wl->mutex);
-
- return NOTIFY_OK;
-}
-
-static struct notifier_block wl1271_dev_notifier = {
- .notifier_call = wl1271_dev_notify,
-};
-
-
static int wl1271_op_start(struct ieee80211_hw *hw)
{
wl1271_debug(DEBUG_MAC80211, "mac80211 start");
@@ -1008,10 +917,8 @@ power_off:
out:
mutex_unlock(&wl->mutex);
- if (!ret) {
+ if (!ret)
list_add(&wl->list, &wl_list);
- register_inetaddr_notifier(&wl1271_dev_notifier);
- }
return ret;
}
@@ -1022,8 +929,6 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
struct wl1271 *wl = hw->priv;
int i;
- unregister_inetaddr_notifier(&wl1271_dev_notifier);
-
mutex_lock(&wl->mutex);
wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
@@ -1033,10 +938,17 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
WARN_ON(wl->state != WL1271_STATE_ON);
- if (test_and_clear_bit(WL1271_FLAG_SCANNING, &wl->flags)) {
+ /* enable dyn ps just in case (if left on due to fw crash etc) */
+ if (wl->bss_type == BSS_TYPE_STA_BSS)
+ ieee80211_enable_dyn_ps(wl->vif);
+
+ if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
mutex_unlock(&wl->mutex);
ieee80211_scan_completed(wl->hw, true);
mutex_lock(&wl->mutex);
+ wl->scan.state = WL1271_SCAN_STATE_IDLE;
+ kfree(wl->scan.scanned_ch);
+ wl->scan.scanned_ch = NULL;
}
wl->state = WL1271_STATE_OFF;
@@ -1047,6 +959,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
cancel_work_sync(&wl->irq_work);
cancel_work_sync(&wl->tx_work);
+ cancel_delayed_work_sync(&wl->pspoll_work);
mutex_lock(&wl->mutex);
@@ -1352,6 +1265,13 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
wl1271_warning("idle mode change failed %d", ret);
}
+ /*
+ * if mac80211 changes the PSM mode, make sure the mode is not
+ * incorrectly changed after the pspoll failure active window.
+ */
+ if (changed & IEEE80211_CONF_CHANGE_PS)
+ clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
+
if (conf->flags & IEEE80211_CONF_PS &&
!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
@@ -1634,11 +1554,9 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
goto out;
if (wl1271_11a_enabled())
- ret = wl1271_cmd_scan(hw->priv, ssid, len, req,
- 1, 0, WL1271_SCAN_BAND_DUAL, 3);
+ ret = wl1271_scan(hw->priv, ssid, len, req);
else
- ret = wl1271_cmd_scan(hw->priv, ssid, len, req,
- 1, 0, WL1271_SCAN_BAND_2_4_GHZ, 3);
+ ret = wl1271_scan(hw->priv, ssid, len, req);
wl1271_ps_elp_sleep(wl);
@@ -1811,6 +1729,8 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
wl->aid = bss_conf->aid;
set_assoc = true;
+ wl->ps_poll_failures = 0;
+
/*
* use basic rates from AP, and determine lowest rate
* to use with control frames.
@@ -1860,6 +1780,9 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
wl->aid = 0;
+ /* re-enable dynamic ps - just in case */
+ ieee80211_enable_dyn_ps(wl->vif);
+
/* revert back to minimum rates for the current band */
wl1271_set_band_rate(wl);
wl->basic_rate = wl1271_min_rate_get(wl);
@@ -1908,6 +1831,19 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
}
}
+ if (changed & BSS_CHANGED_ARP_FILTER) {
+ __be32 addr = bss_conf->arp_addr_list[0];
+ WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
+
+ if (bss_conf->arp_addr_cnt == 1 && bss_conf->arp_filter_enabled)
+ ret = wl1271_acx_arp_ip_filter(wl, true, addr);
+ else
+ ret = wl1271_acx_arp_ip_filter(wl, false, addr);
+
+ if (ret < 0)
+ goto out_sleep;
+ }
+
if (do_join) {
ret = wl1271_join(wl, set_assoc);
if (ret < 0) {
@@ -1966,6 +1902,32 @@ out:
return ret;
}
+static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw)
+{
+
+ struct wl1271 *wl = hw->priv;
+ u64 mactime = ULLONG_MAX;
+ int ret;
+
+ wl1271_debug(DEBUG_MAC80211, "mac80211 get tsf");
+
+ mutex_lock(&wl->mutex);
+
+ ret = wl1271_ps_elp_wakeup(wl, false);
+ if (ret < 0)
+ goto out;
+
+ ret = wl1271_acx_tsf_info(wl, &mactime);
+ if (ret < 0)
+ goto out_sleep;
+
+out_sleep:
+ wl1271_ps_elp_sleep(wl);
+
+out:
+ mutex_unlock(&wl->mutex);
+ return mactime;
+}
/* can't be const, mac80211 writes to this */
static struct ieee80211_rate wl1271_rates[] = {
@@ -2195,6 +2157,7 @@ static const struct ieee80211_ops wl1271_ops = {
.bss_info_changed = wl1271_op_bss_info_changed,
.set_rts_threshold = wl1271_op_set_rts_threshold,
.conf_tx = wl1271_op_conf_tx,
+ .get_tsf = wl1271_op_get_tsf,
CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
};
@@ -2407,6 +2370,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
skb_queue_head_init(&wl->tx_queue);
INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
+ INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work);
wl->channel = WL1271_DEFAULT_CHANNEL;
wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
wl->default_key = 0;
diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.c b/drivers/net/wireless/wl12xx/wl1271_rx.c
index b98fb643fab0..e98f22b3c3ba 100644
--- a/drivers/net/wireless/wl12xx/wl1271_rx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_rx.c
@@ -53,12 +53,6 @@ static void wl1271_rx_status(struct wl1271 *wl,
status->band = wl->band;
status->rate_idx = wl1271_rate_to_idx(wl, desc->rate);
- /*
- * FIXME: Add mactime handling. For IBSS (ad-hoc) we need to get the
- * timestamp from the beacon (acx_tsf_info). In BSS mode (infra) we
- * only need the mactime for monitor mode. For now the mactime is
- * not valid, so RX_FLAG_TSFT should not be set
- */
status->signal = desc->rssi;
status->freq = ieee80211_channel_to_frequency(desc->channel);
diff --git a/drivers/net/wireless/wl12xx/wl1271_scan.c b/drivers/net/wireless/wl12xx/wl1271_scan.c
new file mode 100644
index 000000000000..fec43eed8c55
--- /dev/null
+++ b/drivers/net/wireless/wl12xx/wl1271_scan.c
@@ -0,0 +1,257 @@
+/*
+ * This file is part of wl1271
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/ieee80211.h>
+
+#include "wl1271.h"
+#include "wl1271_cmd.h"
+#include "wl1271_scan.h"
+#include "wl1271_acx.h"
+
+static int wl1271_get_scan_channels(struct wl1271 *wl,
+ struct cfg80211_scan_request *req,
+ struct basic_scan_channel_params *channels,
+ enum ieee80211_band band, bool passive)
+{
+ int i, j;
+ u32 flags;
+
+ for (i = 0, j = 0;
+ i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS;
+ i++) {
+
+ flags = req->channels[i]->flags;
+
+ if (!wl->scan.scanned_ch[i] &&
+ !(flags & IEEE80211_CHAN_DISABLED) &&
+ ((!!(flags & IEEE80211_CHAN_PASSIVE_SCAN)) == passive) &&
+ (req->channels[i]->band == band)) {
+
+ wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
+ req->channels[i]->band,
+ req->channels[i]->center_freq);
+ wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
+ req->channels[i]->hw_value,
+ req->channels[i]->flags);
+ wl1271_debug(DEBUG_SCAN,
+ "max_antenna_gain %d, max_power %d",
+ req->channels[i]->max_antenna_gain,
+ req->channels[i]->max_power);
+ wl1271_debug(DEBUG_SCAN, "beacon_found %d",
+ req->channels[i]->beacon_found);
+
+ channels[j].min_duration =
+ cpu_to_le32(WL1271_SCAN_CHAN_MIN_DURATION);
+ channels[j].max_duration =
+ cpu_to_le32(WL1271_SCAN_CHAN_MAX_DURATION);
+ channels[j].early_termination = 0;
+ channels[j].tx_power_att = req->channels[i]->max_power;
+ channels[j].channel = req->channels[i]->hw_value;
+
+ memset(&channels[j].bssid_lsb, 0xff, 4);
+ memset(&channels[j].bssid_msb, 0xff, 2);
+
+ /* Mark the channels we already used */
+ wl->scan.scanned_ch[i] = true;
+
+ j++;
+ }
+ }
+
+ return j;
+}
+
+#define WL1271_NOTHING_TO_SCAN 1
+
+static int wl1271_scan_send(struct wl1271 *wl, enum ieee80211_band band,
+ bool passive, u32 basic_rate)
+{
+ struct wl1271_cmd_scan *cmd;
+ struct wl1271_cmd_trigger_scan_to *trigger;
+ int ret;
+ u16 scan_options = 0;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
+ if (!cmd || !trigger) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* We always use high priority scans */
+ scan_options = WL1271_SCAN_OPT_PRIORITY_HIGH;
+ if(passive)
+ scan_options |= WL1271_SCAN_OPT_PASSIVE;
+ cmd->params.scan_options = cpu_to_le16(scan_options);
+
+ cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
+ cmd->channels,
+ band, passive);
+ if (cmd->params.n_ch == 0) {
+ ret = WL1271_NOTHING_TO_SCAN;
+ goto out;
+ }
+
+ cmd->params.tx_rate = cpu_to_le32(basic_rate);
+ cmd->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD);
+ cmd->params.rx_filter_options =
+ cpu_to_le32(CFG_RX_PRSP_EN | CFG_RX_MGMT_EN | CFG_RX_BCN_EN);
+
+ cmd->params.n_probe_reqs = WL1271_SCAN_PROBE_REQS;
+ cmd->params.tx_rate = cpu_to_le32(basic_rate);
+ cmd->params.tid_trigger = 0;
+ cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
+
+ if (band == IEEE80211_BAND_2GHZ)
+ cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ;
+ else
+ cmd->params.band = WL1271_SCAN_BAND_5_GHZ;
+
+ if (wl->scan.ssid_len && wl->scan.ssid) {
+ cmd->params.ssid_len = wl->scan.ssid_len;
+ memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len);
+ }
+
+ ret = wl1271_cmd_build_probe_req(wl, wl->scan.ssid, wl->scan.ssid_len,
+ wl->scan.req->ie, wl->scan.req->ie_len,
+ band);
+ if (ret < 0) {
+ wl1271_error("PROBE request template failed");
+ goto out;
+ }
+
+ /* disable the timeout */
+ trigger->timeout = 0;
+ ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger,
+ sizeof(*trigger), 0);
+ if (ret < 0) {
+ wl1271_error("trigger scan to failed for hw scan");
+ goto out;
+ }
+
+ wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
+
+ ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ wl1271_error("SCAN failed");
+ goto out;
+ }
+
+out:
+ kfree(cmd);
+ kfree(trigger);
+ return ret;
+}
+
+void wl1271_scan_stm(struct wl1271 *wl)
+{
+ int ret;
+
+ switch (wl->scan.state) {
+ case WL1271_SCAN_STATE_IDLE:
+ break;
+
+ case WL1271_SCAN_STATE_2GHZ_ACTIVE:
+ ret = wl1271_scan_send(wl, IEEE80211_BAND_2GHZ, false,
+ wl->conf.tx.basic_rate);
+ if (ret == WL1271_NOTHING_TO_SCAN) {
+ wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE;
+ wl1271_scan_stm(wl);
+ }
+
+ break;
+
+ case WL1271_SCAN_STATE_2GHZ_PASSIVE:
+ ret = wl1271_scan_send(wl, IEEE80211_BAND_2GHZ, true,
+ wl->conf.tx.basic_rate);
+ if (ret == WL1271_NOTHING_TO_SCAN) {
+ if (wl1271_11a_enabled())
+ wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE;
+ else
+ wl->scan.state = WL1271_SCAN_STATE_DONE;
+ wl1271_scan_stm(wl);
+ }
+
+ break;
+
+ case WL1271_SCAN_STATE_5GHZ_ACTIVE:
+ ret = wl1271_scan_send(wl, IEEE80211_BAND_5GHZ, false,
+ wl->conf.tx.basic_rate_5);
+ if (ret == WL1271_NOTHING_TO_SCAN) {
+ wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE;
+ wl1271_scan_stm(wl);
+ }
+
+ break;
+
+ case WL1271_SCAN_STATE_5GHZ_PASSIVE:
+ ret = wl1271_scan_send(wl, IEEE80211_BAND_5GHZ, true,
+ wl->conf.tx.basic_rate_5);
+ if (ret == WL1271_NOTHING_TO_SCAN) {
+ wl->scan.state = WL1271_SCAN_STATE_DONE;
+ wl1271_scan_stm(wl);
+ }
+
+ break;
+
+ case WL1271_SCAN_STATE_DONE:
+ mutex_unlock(&wl->mutex);
+ ieee80211_scan_completed(wl->hw, false);
+ mutex_lock(&wl->mutex);
+
+ kfree(wl->scan.scanned_ch);
+ wl->scan.scanned_ch = NULL;
+
+ wl->scan.state = WL1271_SCAN_STATE_IDLE;
+ break;
+
+ default:
+ wl1271_error("invalid scan state");
+ break;
+ }
+}
+
+int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
+ struct cfg80211_scan_request *req)
+{
+ if (wl->scan.state != WL1271_SCAN_STATE_IDLE)
+ return -EBUSY;
+
+ wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE;
+
+ if (ssid_len && ssid) {
+ wl->scan.ssid_len = ssid_len;
+ memcpy(wl->scan.ssid, ssid, ssid_len);
+ } else {
+ wl->scan.ssid_len = 0;
+ }
+
+ wl->scan.req = req;
+
+ wl->scan.scanned_ch = kzalloc(req->n_channels *
+ sizeof(*wl->scan.scanned_ch),
+ GFP_KERNEL);
+ wl1271_scan_stm(wl);
+
+ return 0;
+}
diff --git a/drivers/net/wireless/wl12xx/wl1271_scan.h b/drivers/net/wireless/wl12xx/wl1271_scan.h
new file mode 100644
index 000000000000..f1815700f5f9
--- /dev/null
+++ b/drivers/net/wireless/wl12xx/wl1271_scan.h
@@ -0,0 +1,109 @@
+/*
+ * This file is part of wl1271
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ * Contact: Luciano Coelho <luciano.coelho@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL1271_SCAN_H__
+#define __WL1271_SCAN_H__
+
+#include "wl1271.h"
+
+int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
+ struct cfg80211_scan_request *req);
+int wl1271_scan_build_probe_req(struct wl1271 *wl,
+ const u8 *ssid, size_t ssid_len,
+ const u8 *ie, size_t ie_len, u8 band);
+void wl1271_scan_stm(struct wl1271 *wl);
+
+#define WL1271_SCAN_MAX_CHANNELS 24
+#define WL1271_SCAN_DEFAULT_TAG 1
+#define WL1271_SCAN_CURRENT_TX_PWR 0
+#define WL1271_SCAN_OPT_ACTIVE 0
+#define WL1271_SCAN_OPT_PASSIVE 1
+#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_PROBE_REQS 3
+
+enum {
+ WL1271_SCAN_STATE_IDLE,
+ WL1271_SCAN_STATE_2GHZ_ACTIVE,
+ WL1271_SCAN_STATE_2GHZ_PASSIVE,
+ WL1271_SCAN_STATE_5GHZ_ACTIVE,
+ WL1271_SCAN_STATE_5GHZ_PASSIVE,
+ WL1271_SCAN_STATE_DONE
+};
+
+struct basic_scan_params {
+ __le32 rx_config_options;
+ __le32 rx_filter_options;
+ /* Scan option flags (WL1271_SCAN_OPT_*) */
+ __le16 scan_options;
+ /* Number of scan channels in the list (maximum 30) */
+ u8 n_ch;
+ /* This field indicates the number of probe requests to send
+ per channel for an active scan */
+ u8 n_probe_reqs;
+ /* Rate bit field for sending the probes */
+ __le32 tx_rate;
+ u8 tid_trigger;
+ u8 ssid_len;
+ /* in order to align */
+ u8 padding1[2];
+ u8 ssid[IW_ESSID_MAX_SIZE];
+ /* Band to scan */
+ u8 band;
+ u8 use_ssid_list;
+ u8 scan_tag;
+ u8 padding2;
+} __packed;
+
+struct basic_scan_channel_params {
+ /* Duration in TU to wait for frames on a channel for active scan */
+ __le32 min_duration;
+ __le32 max_duration;
+ __le32 bssid_lsb;
+ __le16 bssid_msb;
+ u8 early_termination;
+ u8 tx_power_att;
+ u8 channel;
+ /* FW internal use only! */
+ u8 dfs_candidate;
+ u8 activity_detected;
+ u8 pad;
+} __packed;
+
+struct wl1271_cmd_scan {
+ struct wl1271_cmd_header header;
+
+ struct basic_scan_params params;
+ struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS];
+} __packed;
+
+struct wl1271_cmd_trigger_scan_to {
+ struct wl1271_cmd_header header;
+
+ __le32 timeout;
+} __packed;
+
+#endif /* __WL1271_SCAN_H__ */
diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c
index 5189b812f939..96d25fb50495 100644
--- a/drivers/net/wireless/wl12xx/wl1271_spi.c
+++ b/drivers/net/wireless/wl12xx/wl1271_spi.c
@@ -461,3 +461,4 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
MODULE_FIRMWARE(WL1271_FW_NAME);
+MODULE_ALIAS("spi:wl1271");