summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/wl12xx
diff options
context:
space:
mode:
authorJohn W. Linville <linville@tuxdriver.com>2010-04-16 00:21:34 +0400
committerJohn W. Linville <linville@tuxdriver.com>2010-04-16 00:21:34 +0400
commit5c01d5669356e13f0fb468944c1dd4c6a7e978ad (patch)
treefa43345288d7b25fac92b3b35360a177c4947313 /drivers/net/wireless/wl12xx
parentfea069152614cdeefba4b2bf80afcddb9c217fc8 (diff)
parenta5e944f1d955f3819503348426763e21e0413ba6 (diff)
downloadlinux-5c01d5669356e13f0fb468944c1dd4c6a7e978ad.tar.xz
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6 into for-davem
Conflicts: Documentation/feature-removal-schedule.txt drivers/net/wireless/ath/ath5k/phy.c drivers/net/wireless/wl12xx/wl1271_main.c
Diffstat (limited to 'drivers/net/wireless/wl12xx')
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_main.c4
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_spi.c2
-rw-r--r--drivers/net/wireless/wl12xx/wl1271.h19
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_acx.c146
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_acx.h89
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_boot.c15
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_boot.h7
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_cmd.c127
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_cmd.h13
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_conf.h158
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_event.c66
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_event.h8
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_init.c53
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_main.c393
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_ps.c6
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_rx.c83
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_rx.h2
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_sdio.c6
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_spi.c71
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_tx.c43
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_tx.h2
21 files changed, 912 insertions, 401 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c
index 56b78e4b0198..4d479708158d 100644
--- a/drivers/net/wireless/wl12xx/wl1251_main.c
+++ b/drivers/net/wireless/wl12xx/wl1251_main.c
@@ -202,8 +202,8 @@ static int wl1251_chip_wakeup(struct wl1251 *wl)
goto out;
}
- /* No NVS from netlink, try to get it from the filesystem */
- if (wl->nvs == NULL) {
+ if (wl->nvs == NULL && !wl->use_eeprom) {
+ /* No NVS from netlink, try to get it from the filesystem */
ret = wl1251_fetch_nvs(wl);
if (ret < 0)
goto out;
diff --git a/drivers/net/wireless/wl12xx/wl1251_spi.c b/drivers/net/wireless/wl12xx/wl1251_spi.c
index 3bfb59bd4635..e81474203a23 100644
--- a/drivers/net/wireless/wl12xx/wl1251_spi.c
+++ b/drivers/net/wireless/wl12xx/wl1251_spi.c
@@ -310,7 +310,7 @@ static int __devexit wl1251_spi_remove(struct spi_device *spi)
static struct spi_driver wl1251_spi_driver = {
.driver = {
- .name = "wl1251",
+ .name = DRIVER_NAME,
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 8f11506f8310..75887e74205b 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -55,6 +55,7 @@ enum {
DEBUG_ACX = BIT(13),
DEBUG_SDIO = BIT(14),
DEBUG_FILTERS = BIT(15),
+ DEBUG_ADHOC = BIT(16),
DEBUG_ALL = ~0,
};
@@ -147,14 +148,7 @@ struct wl1271_nvs_file {
*/
#undef WL1271_80211A_ENABLED
-/*
- * FIXME: for the wl1271, a busy word count of 1 here will result in a more
- * optimal SPI interface. There is some SPI bug however, causing RXS time outs
- * with this mode occasionally on boot, so lets have three for now. A value of
- * three should make sure, that the chipset will always be ready, though this
- * will impact throughput and latencies slightly.
- */
-#define WL1271_BUSY_WORD_CNT 3
+#define WL1271_BUSY_WORD_CNT 1
#define WL1271_BUSY_WORD_LEN (WL1271_BUSY_WORD_CNT * sizeof(u32))
#define WL1271_ELP_HW_STATE_ASLEEP 0
@@ -380,6 +374,7 @@ struct wl1271 {
#define WL1271_FLAG_PSM_REQUESTED (8)
#define WL1271_FLAG_IRQ_PENDING (9)
#define WL1271_FLAG_IRQ_RUNNING (10)
+#define WL1271_FLAG_IDLE (11)
unsigned long flags;
struct wl1271_partition_set part;
@@ -396,6 +391,7 @@ struct wl1271 {
u8 bssid[ETH_ALEN];
u8 mac_addr[ETH_ALEN];
u8 bss_type;
+ u8 set_bss_type;
u8 ssid[IW_ESSID_MAX_SIZE + 1];
u8 ssid_len;
int channel;
@@ -452,11 +448,15 @@ struct wl1271 {
/* currently configured rate set */
u32 sta_rate_set;
u32 basic_rate_set;
+ u32 basic_rate;
u32 rate_set;
/* The current band */
enum ieee80211_band band;
+ /* Beaconing interval (needed for ad-hoc) */
+ u32 beacon_int;
+
/* Default key (for WEP) */
u32 default_key;
@@ -473,6 +473,9 @@ struct wl1271 {
/* in dBm */
int power_level;
+ int rssi_thold;
+ int last_rssi_event;
+
struct wl1271_stats stats;
struct wl1271_debugfs debugfs;
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c
index adaa3f2485c3..2ad086efe06e 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.c
@@ -505,12 +505,17 @@ out:
return ret;
}
-int wl1271_acx_conn_monit_params(struct wl1271 *wl)
+#define ACX_CONN_MONIT_DISABLE_VALUE 0xffffffff
+
+int wl1271_acx_conn_monit_params(struct wl1271 *wl, bool enable)
{
struct acx_conn_monit_params *acx;
+ u32 threshold = ACX_CONN_MONIT_DISABLE_VALUE;
+ u32 timeout = ACX_CONN_MONIT_DISABLE_VALUE;
int ret;
- wl1271_debug(DEBUG_ACX, "acx connection monitor parameters");
+ wl1271_debug(DEBUG_ACX, "acx connection monitor parameters: %s",
+ enable ? "enabled" : "disabled");
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx) {
@@ -518,8 +523,13 @@ int wl1271_acx_conn_monit_params(struct wl1271 *wl)
goto out;
}
- acx->synch_fail_thold = cpu_to_le32(wl->conf.conn.synch_fail_thold);
- acx->bss_lose_timeout = cpu_to_le32(wl->conf.conn.bss_lose_timeout);
+ if (enable) {
+ threshold = wl->conf.conn.synch_fail_thold;
+ timeout = wl->conf.conn.bss_lose_timeout;
+ }
+
+ acx->synch_fail_thold = cpu_to_le32(threshold);
+ acx->bss_lose_timeout = cpu_to_le32(timeout);
ret = wl1271_cmd_configure(wl, ACX_CONN_MONIT_PARAMS,
acx, sizeof(*acx));
@@ -793,7 +803,7 @@ int wl1271_acx_rate_policies(struct wl1271 *wl)
/* configure one basic rate class */
idx = ACX_TX_BASIC_RATE;
- acx->rate_class[idx].enabled_rates = cpu_to_le32(wl->basic_rate_set);
+ acx->rate_class[idx].enabled_rates = cpu_to_le32(wl->basic_rate);
acx->rate_class[idx].short_retry_limit = c->short_retry_limit;
acx->rate_class[idx].long_retry_limit = c->long_retry_limit;
acx->rate_class[idx].aflags = c->aflags;
@@ -1130,3 +1140,129 @@ out:
kfree(acx);
return ret;
}
+
+int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable)
+{
+ struct wl1271_acx_keep_alive_mode *acx = NULL;
+ int ret = 0;
+
+ wl1271_debug(DEBUG_ACX, "acx keep alive mode: %d", enable);
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->enabled = enable;
+
+ ret = wl1271_cmd_configure(wl, ACX_KEEP_ALIVE_MODE, acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1271_warning("acx keep alive mode failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid)
+{
+ struct wl1271_acx_keep_alive_config *acx = NULL;
+ int ret = 0;
+
+ wl1271_debug(DEBUG_ACX, "acx keep alive config");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->period = cpu_to_le32(wl->conf.conn.keep_alive_interval);
+ acx->index = index;
+ acx->tpl_validation = tpl_valid;
+ acx->trigger = ACX_KEEP_ALIVE_NO_TX;
+
+ ret = wl1271_cmd_configure(wl, ACX_SET_KEEP_ALIVE_CONFIG,
+ acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1271_warning("acx keep alive config failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
+ s16 thold, u8 hyst)
+{
+ struct wl1271_acx_rssi_snr_trigger *acx = NULL;
+ int ret = 0;
+
+ wl1271_debug(DEBUG_ACX, "acx rssi snr trigger");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ wl->last_rssi_event = -1;
+
+ acx->pacing = cpu_to_le16(wl->conf.roam_trigger.trigger_pacing);
+ acx->metric = WL1271_ACX_TRIG_METRIC_RSSI_BEACON;
+ acx->type = WL1271_ACX_TRIG_TYPE_EDGE;
+ if (enable)
+ acx->enable = WL1271_ACX_TRIG_ENABLE;
+ else
+ acx->enable = WL1271_ACX_TRIG_DISABLE;
+
+ acx->index = WL1271_ACX_TRIG_IDX_RSSI;
+ acx->dir = WL1271_ACX_TRIG_DIR_BIDIR;
+ acx->threshold = cpu_to_le16(thold);
+ acx->hysteresis = hyst;
+
+ ret = wl1271_cmd_configure(wl, ACX_RSSI_SNR_TRIGGER, acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1271_warning("acx rssi snr trigger setting failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl)
+{
+ struct wl1271_acx_rssi_snr_avg_weights *acx = NULL;
+ struct conf_roam_trigger_settings *c = &wl->conf.roam_trigger;
+ int ret = 0;
+
+ wl1271_debug(DEBUG_ACX, "acx rssi snr avg weights");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->rssi_beacon = c->avg_weight_rssi_beacon;
+ acx->rssi_data = c->avg_weight_rssi_data;
+ acx->snr_beacon = c->avg_weight_snr_beacon;
+ acx->snr_data = c->avg_weight_snr_data;
+
+ ret = wl1271_cmd_configure(wl, ACX_RSSI_SNR_WEIGHTS, acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1271_warning("acx rssi snr trigger weights failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h
index 8e5870fa9609..420e7e2fc021 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.h
@@ -915,6 +915,84 @@ struct wl1271_acx_pm_config {
u8 padding[3];
} __attribute__ ((packed));
+struct wl1271_acx_keep_alive_mode {
+ struct acx_header header;
+
+ u8 enabled;
+ u8 padding[3];
+} __attribute__ ((packed));
+
+enum {
+ ACX_KEEP_ALIVE_NO_TX = 0,
+ ACX_KEEP_ALIVE_PERIOD_ONLY
+};
+
+enum {
+ ACX_KEEP_ALIVE_TPL_INVALID = 0,
+ ACX_KEEP_ALIVE_TPL_VALID
+};
+
+struct wl1271_acx_keep_alive_config {
+ struct acx_header header;
+
+ __le32 period;
+ u8 index;
+ u8 tpl_validation;
+ u8 trigger;
+ u8 padding;
+} __attribute__ ((packed));
+
+enum {
+ WL1271_ACX_TRIG_TYPE_LEVEL = 0,
+ WL1271_ACX_TRIG_TYPE_EDGE,
+};
+
+enum {
+ WL1271_ACX_TRIG_DIR_LOW = 0,
+ WL1271_ACX_TRIG_DIR_HIGH,
+ WL1271_ACX_TRIG_DIR_BIDIR,
+};
+
+enum {
+ WL1271_ACX_TRIG_ENABLE = 1,
+ WL1271_ACX_TRIG_DISABLE,
+};
+
+enum {
+ WL1271_ACX_TRIG_METRIC_RSSI_BEACON = 0,
+ WL1271_ACX_TRIG_METRIC_RSSI_DATA,
+ WL1271_ACX_TRIG_METRIC_SNR_BEACON,
+ WL1271_ACX_TRIG_METRIC_SNR_DATA,
+};
+
+enum {
+ WL1271_ACX_TRIG_IDX_RSSI = 0,
+ WL1271_ACX_TRIG_COUNT = 8,
+};
+
+struct wl1271_acx_rssi_snr_trigger {
+ struct acx_header header;
+
+ __le16 threshold;
+ __le16 pacing; /* 0 - 60000 ms */
+ u8 metric;
+ u8 type;
+ u8 dir;
+ u8 hysteresis;
+ u8 index;
+ u8 enable;
+ u8 padding[2];
+};
+
+struct wl1271_acx_rssi_snr_avg_weights {
+ struct acx_header header;
+
+ u8 rssi_beacon;
+ u8 rssi_data;
+ u8 snr_beacon;
+ u8 snr_data;
+};
+
enum {
ACX_WAKE_UP_CONDITIONS = 0x0002,
ACX_MEM_CFG = 0x0003,
@@ -963,8 +1041,8 @@ enum {
ACX_FRAG_CFG = 0x004F,
ACX_BET_ENABLE = 0x0050,
ACX_RSSI_SNR_TRIGGER = 0x0051,
- ACX_RSSI_SNR_WEIGHTS = 0x0051,
- ACX_KEEP_ALIVE_MODE = 0x0052,
+ ACX_RSSI_SNR_WEIGHTS = 0x0052,
+ ACX_KEEP_ALIVE_MODE = 0x0053,
ACX_SET_KEEP_ALIVE_CONFIG = 0x0054,
ACX_BA_SESSION_RESPONDER_POLICY = 0x0055,
ACX_BA_SESSION_INITIATOR_POLICY = 0x0056,
@@ -1004,7 +1082,7 @@ int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold);
int wl1271_acx_dco_itrim_params(struct wl1271 *wl);
int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter);
int wl1271_acx_beacon_filter_table(struct wl1271 *wl);
-int wl1271_acx_conn_monit_params(struct wl1271 *wl);
+int wl1271_acx_conn_monit_params(struct wl1271 *wl, bool enable);
int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable);
int wl1271_acx_sg_cfg(struct wl1271 *wl);
int wl1271_acx_cca_threshold(struct wl1271 *wl);
@@ -1031,5 +1109,10 @@ 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_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);
#endif /* __WL1271_ACX_H__ */
diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c
index 1937859d74eb..8087dc17f29d 100644
--- a/drivers/net/wireless/wl12xx/wl1271_boot.c
+++ b/drivers/net/wireless/wl12xx/wl1271_boot.c
@@ -1,7 +1,7 @@
/*
* This file is part of wl1271
*
- * Copyright (C) 2008-2009 Nokia Corporation
+ * Copyright (C) 2008-2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
@@ -411,7 +411,10 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
/* unmask required mbox events */
wl->event_mask = BSS_LOSE_EVENT_ID |
SCAN_COMPLETE_EVENT_ID |
- PS_REPORT_EVENT_ID;
+ PS_REPORT_EVENT_ID |
+ JOIN_EVENT_COMPLETE_ID |
+ DISCONNECT_EVENT_COMPLETE_ID |
+ RSSI_SNR_TRIGGER_0_EVENT_ID;
ret = wl1271_event_unmask(wl);
if (ret < 0) {
@@ -452,11 +455,15 @@ int wl1271_boot(struct wl1271 *wl)
if (REF_CLOCK != 0) {
u16 val;
- /* Set clock type */
+ /* Set clock type (open drain) */
val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE);
val &= FREF_CLK_TYPE_BITS;
- val |= CLK_REQ_PRCM;
wl1271_top_reg_write(wl, OCP_REG_CLK_TYPE, val);
+
+ /* Set clock pull mode (no pull) */
+ val = wl1271_top_reg_read(wl, OCP_REG_CLK_PULL);
+ val |= NO_PULL;
+ wl1271_top_reg_write(wl, OCP_REG_CLK_PULL, val);
} else {
u16 val;
/* Set clock polarity */
diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.h b/drivers/net/wireless/wl12xx/wl1271_boot.h
index 412443ee655a..95ecc5241959 100644
--- a/drivers/net/wireless/wl12xx/wl1271_boot.h
+++ b/drivers/net/wireless/wl12xx/wl1271_boot.h
@@ -53,10 +53,13 @@ struct wl1271_static_data {
#define OCP_REG_POLARITY 0x0064
#define OCP_REG_CLK_TYPE 0x0448
#define OCP_REG_CLK_POLARITY 0x0cb2
+#define OCP_REG_CLK_PULL 0x0cb4
-#define CMD_MBOX_ADDRESS 0x407B4
-#define POLARITY_LOW BIT(1)
+#define CMD_MBOX_ADDRESS 0x407B4
+
+#define POLARITY_LOW BIT(1)
+#define NO_PULL (BIT(14) | BIT(15))
#define FREF_CLK_TYPE_BITS 0xfffffe7f
#define CLK_REQ_PRCM 0x100
diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c
index 92254d0d6c4c..6b5ba8ec94c9 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.c
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c
@@ -1,7 +1,7 @@
/*
* This file is part of wl1271
*
- * Copyright (C) 2009 Nokia Corporation
+ * Copyright (C) 2009-2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
@@ -35,6 +35,9 @@
#include "wl1271_acx.h"
#include "wl12xx_80211.h"
#include "wl1271_cmd.h"
+#include "wl1271_event.h"
+
+#define WL1271_CMD_POLL_COUNT 5
/*
* send command to firmware
@@ -52,6 +55,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
u32 intr;
int ret = 0;
u16 status;
+ u16 poll_count = 0;
cmd = buf;
cmd->id = cpu_to_le16(id);
@@ -73,7 +77,11 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
goto out;
}
- msleep(1);
+ udelay(10);
+ poll_count++;
+ if (poll_count == WL1271_CMD_POLL_COUNT)
+ wl1271_info("cmd polling took over %d cycles",
+ poll_count);
intr = wl1271_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR);
}
@@ -249,6 +257,35 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl)
return ret;
}
+/*
+ * Poll the mailbox event field until any of the bits in the mask is set or a
+ * timeout occurs (WL1271_EVENT_TIMEOUT in msecs)
+ */
+static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
+{
+ u32 events_vector, event;
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(WL1271_EVENT_TIMEOUT);
+
+ do {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+
+ msleep(1);
+
+ /* read from both event fields */
+ wl1271_read(wl, wl->mbox_ptr[0], &events_vector,
+ sizeof(events_vector), false);
+ event = events_vector & mask;
+ wl1271_read(wl, wl->mbox_ptr[1], &events_vector,
+ sizeof(events_vector), false);
+ event |= events_vector & mask;
+ } while (!event);
+
+ return 0;
+}
+
int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type)
{
static bool do_cal = true;
@@ -281,20 +318,12 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type)
join->rx_config_options = cpu_to_le32(wl->rx_config);
join->rx_filter_options = cpu_to_le32(wl->rx_filter);
join->bss_type = bss_type;
+ join->basic_rate_set = wl->basic_rate_set;
- if (wl->band == IEEE80211_BAND_2GHZ)
- join->basic_rate_set = cpu_to_le32(CONF_HW_BIT_RATE_1MBPS |
- CONF_HW_BIT_RATE_2MBPS |
- CONF_HW_BIT_RATE_5_5MBPS |
- CONF_HW_BIT_RATE_11MBPS);
- else {
+ if (wl->band == IEEE80211_BAND_5GHZ)
join->bss_type |= WL1271_JOIN_CMD_BSS_TYPE_5GHZ;
- join->basic_rate_set = cpu_to_le32(CONF_HW_BIT_RATE_6MBPS |
- CONF_HW_BIT_RATE_12MBPS |
- CONF_HW_BIT_RATE_24MBPS);
- }
- join->beacon_interval = cpu_to_le16(WL1271_DEFAULT_BEACON_INT);
+ join->beacon_interval = cpu_to_le16(wl->beacon_int);
join->dtim_interval = WL1271_DEFAULT_DTIM_PERIOD;
join->channel = wl->channel;
@@ -319,11 +348,9 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type)
goto out_free;
}
- /*
- * ugly hack: we should wait for JOIN_EVENT_COMPLETE_ID but to
- * simplify locking we just sleep instead, for now
- */
- msleep(10);
+ ret = wl1271_cmd_wait_for_event(wl, JOIN_EVENT_COMPLETE_ID);
+ if (ret < 0)
+ wl1271_error("cmd join event completion error");
out_free:
kfree(join);
@@ -455,7 +482,7 @@ int wl1271_cmd_data_path(struct wl1271 *wl, bool enable)
if (ret < 0) {
wl1271_error("tx %s cmd for channel %d failed",
enable ? "start" : "stop", cmd->channel);
- return ret;
+ goto out;
}
wl1271_debug(DEBUG_BOOT, "tx %s cmd channel %d",
@@ -547,17 +574,21 @@ int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
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)
+ if (band == WL1271_SCAN_BAND_2_4_GHZ) {
ieee_band = IEEE80211_BAND_2GHZ;
- else if (band == WL1271_SCAN_BAND_DUAL && wl1271_11a_enabled())
+ rate = wl->conf.tx.basic_rate;
+ } 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())
+ rate = wl->conf.tx.basic_rate;
+ } else if (band == WL1271_SCAN_BAND_5_GHZ && wl1271_11a_enabled()) {
ieee_band = IEEE80211_BAND_5GHZ;
- else
+ rate = wl->conf.tx.basic_rate_5;
+ } else
return -EINVAL;
if (wl->hw->wiphy->bands[ieee_band]->channels == NULL)
@@ -584,8 +615,7 @@ int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
params->params.scan_options = cpu_to_le16(scan_options);
params->params.num_probe_requests = probe_requests;
- /* Let the fw autodetect suitable tx_rate for probes */
- params->params.tx_rate = 0;
+ params->params.tx_rate = rate;
params->params.tid_trigger = 0;
params->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
@@ -666,11 +696,12 @@ int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
out:
kfree(params);
+ kfree(trigger);
return ret;
}
int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
- void *buf, size_t buf_len)
+ void *buf, size_t buf_len, int index, u32 rates)
{
struct wl1271_cmd_template_set *cmd;
int ret = 0;
@@ -688,9 +719,10 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
cmd->len = cpu_to_le16(buf_len);
cmd->template_type = template_id;
- cmd->enabled_rates = cpu_to_le32(wl->conf.tx.rc_conf.enabled_rates);
+ cmd->enabled_rates = cpu_to_le32(rates);
cmd->short_retry_limit = wl->conf.tx.rc_conf.short_retry_limit;
cmd->long_retry_limit = wl->conf.tx.rc_conf.long_retry_limit;
+ cmd->index = index;
if (buf)
memcpy(cmd->template_data, buf, buf_len);
@@ -727,7 +759,8 @@ int wl1271_cmd_build_null_data(struct wl1271 *wl)
ptr = skb->data;
}
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size);
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size, 0,
+ WL1271_RATE_AUTOMATIC);
out:
dev_kfree_skb(skb);
@@ -738,6 +771,29 @@ out:
}
+int wl1271_cmd_build_klv_null_data(struct wl1271 *wl)
+{
+ struct sk_buff *skb = NULL;
+ int ret = -ENOMEM;
+
+ skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
+ if (!skb)
+ goto out;
+
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV,
+ skb->data, skb->len,
+ CMD_TEMPL_KLV_IDX_NULL_DATA,
+ WL1271_RATE_AUTOMATIC);
+
+out:
+ dev_kfree_skb(skb);
+ if (ret)
+ wl1271_warning("cmd build klv null data failed %d", ret);
+
+ return ret;
+
+}
+
int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid)
{
struct sk_buff *skb;
@@ -748,7 +804,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);
+ skb->len, 0, wl->basic_rate);
out:
dev_kfree_skb(skb);
@@ -773,10 +829,12 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl,
if (band == IEEE80211_BAND_2GHZ)
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
- skb->data, skb->len);
+ 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);
+ skb->data, skb->len, 0,
+ wl->conf.tx.basic_rate_5);
out:
dev_kfree_skb(skb);
@@ -801,7 +859,8 @@ int wl1271_build_qos_null_data(struct wl1271 *wl)
template.qos_ctrl = cpu_to_le16(0);
return wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, &template,
- sizeof(template));
+ sizeof(template), 0,
+ WL1271_RATE_AUTOMATIC);
}
int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id)
@@ -914,6 +973,10 @@ int wl1271_cmd_disconnect(struct wl1271 *wl)
goto out_free;
}
+ ret = wl1271_cmd_wait_for_event(wl, DISCONNECT_EVENT_COMPLETE_ID);
+ if (ret < 0)
+ wl1271_error("cmd disconnect event completion error");
+
out_free:
kfree(cmd);
diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h
index 6324bbf36843..00f78b7aa384 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.h
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h
@@ -45,13 +45,14 @@ int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len, 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);
+ void *buf, size_t buf_len, int index, u32 rates);
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,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len, u8 band);
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);
int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
u8 key_size, const u8 *key, const u8 *addr,
@@ -101,6 +102,11 @@ enum wl1271_commands {
#define MAX_CMD_PARAMS 572
+enum {
+ CMD_TEMPL_KLV_IDX_NULL_DATA = 0,
+ CMD_TEMPL_KLV_IDX_MAX = 4
+};
+
enum cmd_templ {
CMD_TEMPL_NULL_DATA = 0,
CMD_TEMPL_BEACON,
@@ -123,6 +129,7 @@ enum cmd_templ {
/* unit ms */
#define WL1271_COMMAND_TIMEOUT 2000
#define WL1271_CMD_TEMPL_MAX_SIZE 252
+#define WL1271_EVENT_TIMEOUT 100
struct wl1271_cmd_header {
__le16 id;
@@ -245,6 +252,8 @@ struct cmd_enabledisable_path {
u8 padding[3];
} __attribute__ ((packed));
+#define WL1271_RATE_AUTOMATIC 0
+
struct wl1271_cmd_template_set {
struct wl1271_cmd_header header;
@@ -511,6 +520,8 @@ enum wl1271_disconnect_type {
};
struct wl1271_cmd_disconnect {
+ struct wl1271_cmd_header header;
+
__le32 rx_config_options;
__le32 rx_filter_options;
diff --git a/drivers/net/wireless/wl12xx/wl1271_conf.h b/drivers/net/wireless/wl12xx/wl1271_conf.h
index 7fcfe06b1412..c44307c4bcf8 100644
--- a/drivers/net/wireless/wl12xx/wl1271_conf.h
+++ b/drivers/net/wireless/wl12xx/wl1271_conf.h
@@ -66,6 +66,32 @@ enum {
};
enum {
+ CONF_HW_RXTX_RATE_MCS7 = 0,
+ CONF_HW_RXTX_RATE_MCS6,
+ CONF_HW_RXTX_RATE_MCS5,
+ CONF_HW_RXTX_RATE_MCS4,
+ CONF_HW_RXTX_RATE_MCS3,
+ CONF_HW_RXTX_RATE_MCS2,
+ CONF_HW_RXTX_RATE_MCS1,
+ CONF_HW_RXTX_RATE_MCS0,
+ CONF_HW_RXTX_RATE_54,
+ CONF_HW_RXTX_RATE_48,
+ CONF_HW_RXTX_RATE_36,
+ CONF_HW_RXTX_RATE_24,
+ CONF_HW_RXTX_RATE_22,
+ CONF_HW_RXTX_RATE_18,
+ CONF_HW_RXTX_RATE_12,
+ CONF_HW_RXTX_RATE_11,
+ CONF_HW_RXTX_RATE_9,
+ CONF_HW_RXTX_RATE_6,
+ CONF_HW_RXTX_RATE_5_5,
+ CONF_HW_RXTX_RATE_2,
+ CONF_HW_RXTX_RATE_1,
+ CONF_HW_RXTX_RATE_MAX,
+ CONF_HW_RXTX_RATE_UNSUPPORTED = 0xff
+};
+
+enum {
CONF_SG_DISABLE = 0,
CONF_SG_PROTECTIVE,
CONF_SG_OPPORTUNISTIC
@@ -648,6 +674,19 @@ struct conf_tx_settings {
*/
u16 tx_compl_threshold;
+ /*
+ * The rate used for control messages and scanning on the 2.4GHz band
+ *
+ * Range: CONF_HW_BIT_RATE_* bit mask
+ */
+ u32 basic_rate;
+
+ /*
+ * The rate used for control messages and scanning on the 5GHz band
+ *
+ * Range: CONF_HW_BIT_RATE_* bit mask
+ */
+ u32 basic_rate_5;
};
enum {
@@ -717,65 +756,6 @@ enum {
CONF_TRIG_EVENT_DIR_BIDIR
};
-
-struct conf_sig_trigger {
- /*
- * The RSSI / SNR threshold value.
- *
- * FIXME: what is the range?
- */
- s16 threshold;
-
- /*
- * Minimum delay between two trigger events for this trigger in ms.
- *
- * Range: 0 - 60000
- */
- u16 pacing;
-
- /*
- * The measurement data source for this trigger.
- *
- * Range: CONF_TRIG_METRIC_*
- */
- u8 metric;
-
- /*
- * The trigger type of this trigger.
- *
- * Range: CONF_TRIG_EVENT_TYPE_*
- */
- u8 type;
-
- /*
- * The direction of the trigger.
- *
- * Range: CONF_TRIG_EVENT_DIR_*
- */
- u8 direction;
-
- /*
- * Hysteresis range of the trigger around the threshold (in dB)
- *
- * Range: u8
- */
- u8 hysteresis;
-
- /*
- * Index of the trigger rule.
- *
- * Range: 0 - CONF_MAX_RSSI_SNR_TRIGGERS-1
- */
- u8 index;
-
- /*
- * Enable / disable this rule (to use for clearing rules.)
- *
- * Range: 1 - Enabled, 2 - Not enabled
- */
- u8 enable;
-};
-
struct conf_sig_weights {
/*
@@ -894,12 +874,6 @@ struct conf_conn_settings {
u8 ps_poll_threshold;
/*
- * Configuration of signal (rssi/snr) triggers.
- */
- u8 sig_trigger_count;
- struct conf_sig_trigger sig_trigger[CONF_MAX_RSSI_SNR_TRIGGERS];
-
- /*
* Configuration of signal average weights.
*/
struct conf_sig_weights sig_weights;
@@ -929,6 +903,22 @@ struct conf_conn_settings {
* Range 0 - 255
*/
u8 psm_entry_retries;
+
+ /*
+ *
+ * Specifies the interval of the connection keep-alive null-func
+ * frame in ms.
+ *
+ * Range: 1000 - 3600000
+ */
+ u32 keep_alive_interval;
+
+ /*
+ * Maximum listen interval supported by the driver in units of beacons.
+ *
+ * Range: u16
+ */
+ u8 max_listen_interval;
};
enum {
@@ -990,6 +980,43 @@ struct conf_pm_config_settings {
bool host_fast_wakeup_support;
};
+struct conf_roam_trigger_settings {
+ /*
+ * The minimum interval between two trigger events.
+ *
+ * Range: 0 - 60000 ms
+ */
+ u16 trigger_pacing;
+
+ /*
+ * The weight for rssi/beacon average calculation
+ *
+ * Range: 0 - 255
+ */
+ u8 avg_weight_rssi_beacon;
+
+ /*
+ * The weight for rssi/data frame average calculation
+ *
+ * Range: 0 - 255
+ */
+ u8 avg_weight_rssi_data;
+
+ /*
+ * The weight for snr/beacon average calculation
+ *
+ * Range: 0 - 255
+ */
+ u8 avg_weight_snr_beacon;
+
+ /*
+ * The weight for snr/data frame average calculation
+ *
+ * Range: 0 - 255
+ */
+ u8 avg_weight_snr_data;
+};
+
struct conf_drv_settings {
struct conf_sg_settings sg;
struct conf_rx_settings rx;
@@ -998,6 +1025,7 @@ struct conf_drv_settings {
struct conf_init_settings init;
struct conf_itrim_settings itrim;
struct conf_pm_config_settings pm_config;
+ struct conf_roam_trigger_settings roam_trigger;
};
#endif
diff --git a/drivers/net/wireless/wl12xx/wl1271_event.c b/drivers/net/wireless/wl12xx/wl1271_event.c
index 4d35af96c597..cf37aa6eb137 100644
--- a/drivers/net/wireless/wl12xx/wl1271_event.c
+++ b/drivers/net/wireless/wl12xx/wl1271_event.c
@@ -31,14 +31,11 @@
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 (test_bit(WL1271_FLAG_SCANNING, &wl->flags)) {
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.
@@ -52,15 +49,6 @@ static int wl1271_event_scan_complete(struct wl1271 *wl,
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);
@@ -93,16 +81,9 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE,
true);
} else {
- wl1271_error("PSM entry failed, giving up.\n");
- /* FIXME: this may need to be reconsidered. for now it
- is not possible to indicate to the mac80211
- afterwards that PSM entry failed. To maximize
- functionality (receiving data and remaining
- associated) make sure that we are in sync with the
- AP in regard of PSM mode. */
- ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
- false);
+ wl1271_info("No ack to nullfunc from AP.");
wl->psm_entry_retry = 0;
+ *beacon_loss = true;
}
break;
case EVENT_ENTER_POWER_SAVE_SUCCESS:
@@ -144,6 +125,24 @@ static int wl1271_event_ps_report(struct wl1271 *wl,
return ret;
}
+static void wl1271_event_rssi_trigger(struct wl1271 *wl,
+ struct event_mailbox *mbox)
+{
+ enum nl80211_cqm_rssi_threshold_event event;
+ s8 metric = mbox->rssi_snr_trigger_metric[0];
+
+ wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric);
+
+ if (metric <= wl->rssi_thold)
+ event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
+ else
+ event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+
+ if (event != wl->last_rssi_event)
+ ieee80211_cqm_rssi_notify(wl->vif, event, GFP_KERNEL);
+ wl->last_rssi_event = event;
+}
+
static void wl1271_event_mbox_dump(struct event_mailbox *mbox)
{
wl1271_debug(DEBUG_EVENT, "MBOX DUMP:");
@@ -173,10 +172,13 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
* The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon
* filtering) is enabled. Without PSM, the stack will receive all
* beacons and can detect beacon loss by itself.
+ *
+ * As there's possibility that the driver disables PSM before receiving
+ * BSS_LOSE_EVENT, beacon loss has to be reported to the stack.
+ *
*/
- if (vector & BSS_LOSE_EVENT_ID &&
- test_bit(WL1271_FLAG_PSM, &wl->flags)) {
- wl1271_debug(DEBUG_EVENT, "BSS_LOSE_EVENT");
+ if (vector & BSS_LOSE_EVENT_ID) {
+ wl1271_info("Beacon loss detected.");
/* indicate to the stack, that beacons have been lost */
beacon_loss = true;
@@ -189,17 +191,15 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
return ret;
}
- if (wl->vif && beacon_loss) {
- /* Obviously, it's dangerous to release the mutex while
- we are holding many of the variables in the wl struct.
- That's why it's done last in the function, and care must
- be taken that nothing more is done after this function
- returns. */
- mutex_unlock(&wl->mutex);
- ieee80211_beacon_loss(wl->vif);
- mutex_lock(&wl->mutex);
+ if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
+ wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT");
+ if (wl->vif)
+ wl1271_event_rssi_trigger(wl, mbox);
}
+ if (wl->vif && beacon_loss)
+ ieee80211_connection_loss(wl->vif);
+
return 0;
}
diff --git a/drivers/net/wireless/wl12xx/wl1271_event.h b/drivers/net/wireless/wl12xx/wl1271_event.h
index 278f9206aa56..58371008f270 100644
--- a/drivers/net/wireless/wl12xx/wl1271_event.h
+++ b/drivers/net/wireless/wl12xx/wl1271_event.h
@@ -38,6 +38,14 @@
*/
enum {
+ RSSI_SNR_TRIGGER_0_EVENT_ID = BIT(0),
+ RSSI_SNR_TRIGGER_1_EVENT_ID = BIT(1),
+ RSSI_SNR_TRIGGER_2_EVENT_ID = BIT(2),
+ RSSI_SNR_TRIGGER_3_EVENT_ID = BIT(3),
+ RSSI_SNR_TRIGGER_4_EVENT_ID = BIT(4),
+ RSSI_SNR_TRIGGER_5_EVENT_ID = BIT(5),
+ RSSI_SNR_TRIGGER_6_EVENT_ID = BIT(6),
+ RSSI_SNR_TRIGGER_7_EVENT_ID = BIT(7),
MEASUREMENT_START_EVENT_ID = BIT(8),
MEASUREMENT_COMPLETE_EVENT_ID = BIT(9),
SCAN_COMPLETE_EVENT_ID = BIT(10),
diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c
index e3806b035d0c..4447af1557f5 100644
--- a/drivers/net/wireless/wl12xx/wl1271_init.c
+++ b/drivers/net/wireless/wl12xx/wl1271_init.c
@@ -52,50 +52,65 @@ static int wl1271_init_hwenc_config(struct wl1271 *wl)
int wl1271_init_templates_config(struct wl1271 *wl)
{
- int ret;
+ int ret, i;
/* send empty templates for fw memory reservation */
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL,
- sizeof(struct wl12xx_probe_req_template));
+ sizeof(struct wl12xx_probe_req_template),
+ 0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
if (wl1271_11a_enabled()) {
+ size_t size = sizeof(struct wl12xx_probe_req_template);
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5,
- NULL,
- sizeof(struct wl12xx_probe_req_template));
+ NULL, size, 0,
+ WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
}
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL,
- sizeof(struct wl12xx_null_data_template));
+ sizeof(struct wl12xx_null_data_template),
+ 0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, NULL,
- sizeof(struct wl12xx_ps_poll_template));
+ sizeof(struct wl12xx_ps_poll_template),
+ 0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, NULL,
sizeof
- (struct wl12xx_qos_null_data_template));
+ (struct wl12xx_qos_null_data_template),
+ 0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE, NULL,
sizeof
- (struct wl12xx_probe_resp_template));
+ (struct wl12xx_probe_resp_template),
+ 0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, NULL,
sizeof
- (struct wl12xx_beacon_template));
+ (struct wl12xx_beacon_template),
+ 0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
+ for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) {
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, NULL,
+ WL1271_CMD_TEMPL_MAX_SIZE, i,
+ WL1271_RATE_AUTOMATIC);
+ if (ret < 0)
+ return ret;
+ }
+
return 0;
}
@@ -237,7 +252,7 @@ int wl1271_hw_init(struct wl1271 *wl)
goto out_free_memmap;
/* Initialize connection monitoring thresholds */
- ret = wl1271_acx_conn_monit_params(wl);
+ ret = wl1271_acx_conn_monit_params(wl, false);
if (ret < 0)
goto out_free_memmap;
@@ -325,6 +340,24 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0)
goto out_free_memmap;
+ /* disable all keep-alive templates */
+ for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) {
+ ret = wl1271_acx_keep_alive_config(wl, i,
+ ACX_KEEP_ALIVE_TPL_INVALID);
+ if (ret < 0)
+ goto out_free_memmap;
+ }
+
+ /* disable the keep-alive feature */
+ ret = wl1271_acx_keep_alive_mode(wl, false);
+ if (ret < 0)
+ goto out_free_memmap;
+
+ /* Configure rssi/snr averaging weights */
+ ret = wl1271_acx_rssi_snr_avg_weights(wl);
+ if (ret < 0)
+ goto out_free_memmap;
+
return 0;
out_free_memmap:
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index aa970b759dd5..814f300c3f17 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -117,8 +117,7 @@ static struct conf_drv_settings default_conf = {
.tx = {
.tx_energy_detection = 0,
.rc_conf = {
- .enabled_rates = CONF_HW_BIT_RATE_1MBPS |
- CONF_HW_BIT_RATE_2MBPS,
+ .enabled_rates = 0,
.short_retry_limit = 10,
.long_retry_limit = 10,
.aflags = 0
@@ -215,11 +214,13 @@ static struct conf_drv_settings default_conf = {
},
.frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD,
.tx_compl_timeout = 700,
- .tx_compl_threshold = 4
+ .tx_compl_threshold = 4,
+ .basic_rate = CONF_HW_BIT_RATE_1MBPS,
+ .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS,
},
.conn = {
.wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
- .listen_interval = 0,
+ .listen_interval = 1,
.bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED,
.bcn_filt_ie_count = 1,
.bcn_filt_ie = {
@@ -234,38 +235,11 @@ static struct conf_drv_settings default_conf = {
.broadcast_timeout = 20000,
.rx_broadcast_in_ps = 1,
.ps_poll_threshold = 20,
- .sig_trigger_count = 2,
- .sig_trigger = {
- [0] = {
- .threshold = -75,
- .pacing = 500,
- .metric = CONF_TRIG_METRIC_RSSI_BEACON,
- .type = CONF_TRIG_EVENT_TYPE_EDGE,
- .direction = CONF_TRIG_EVENT_DIR_LOW,
- .hysteresis = 2,
- .index = 0,
- .enable = 1
- },
- [1] = {
- .threshold = -75,
- .pacing = 500,
- .metric = CONF_TRIG_METRIC_RSSI_BEACON,
- .type = CONF_TRIG_EVENT_TYPE_EDGE,
- .direction = CONF_TRIG_EVENT_DIR_HIGH,
- .hysteresis = 2,
- .index = 1,
- .enable = 1
- }
- },
- .sig_weights = {
- .rssi_bcn_avg_weight = 10,
- .rssi_pkt_avg_weight = 10,
- .snr_bcn_avg_weight = 10,
- .snr_pkt_avg_weight = 10
- },
.bet_enable = CONF_BET_MODE_ENABLE,
.bet_max_consecutive = 10,
- .psm_entry_retries = 3
+ .psm_entry_retries = 3,
+ .keep_alive_interval = 55000,
+ .max_listen_interval = 20,
},
.init = {
.radioparam = {
@@ -279,6 +253,14 @@ static struct conf_drv_settings default_conf = {
.pm_config = {
.host_clk_settling_time = 5000,
.host_fast_wakeup_support = false
+ },
+ .roam_trigger = {
+ /* FIXME: due to firmware bug, must use value 1 for now */
+ .trigger_pacing = 1,
+ .avg_weight_rssi_beacon = 20,
+ .avg_weight_rssi_data = 10,
+ .avg_weight_snr_beacon = 20,
+ .avg_weight_snr_data = 10
}
};
@@ -349,7 +331,7 @@ static int wl1271_plt_init(struct wl1271 *wl)
goto out_free_memmap;
/* Initialize connection monitoring thresholds */
- ret = wl1271_acx_conn_monit_params(wl);
+ ret = wl1271_acx_conn_monit_params(wl, false);
if (ret < 0)
goto out_free_memmap;
@@ -959,9 +941,11 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
switch (vif->type) {
case NL80211_IFTYPE_STATION:
wl->bss_type = BSS_TYPE_STA_BSS;
+ wl->set_bss_type = BSS_TYPE_STA_BSS;
break;
case NL80211_IFTYPE_ADHOC:
wl->bss_type = BSS_TYPE_IBSS;
+ wl->set_bss_type = BSS_TYPE_STA_BSS;
break;
default:
ret = -EOPNOTSUPP;
@@ -1066,6 +1050,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1);
wl->ssid_len = 0;
wl->bss_type = MAX_BSS_TYPE;
+ wl->set_bss_type = MAX_BSS_TYPE;
wl->band = IEEE80211_BAND_2GHZ;
wl->rx_counter = 0;
@@ -1088,6 +1073,14 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
wl->tx_blocks_freed[i] = 0;
wl1271_debugfs_reset(wl);
+
+ kfree(wl->fw_status);
+ wl->fw_status = NULL;
+ kfree(wl->tx_res_if);
+ wl->tx_res_if = NULL;
+ kfree(wl->target_mem_map);
+ wl->target_mem_map = NULL;
+
mutex_unlock(&wl->mutex);
}
@@ -1138,10 +1131,7 @@ static int wl1271_join_channel(struct wl1271 *wl, int channel)
/* pass through frames from all BSS */
wl1271_configure_filters(wl, FIF_OTHER_BSS);
- /* the dummy join is performed always with STATION BSS type to allow
- also ad-hoc mode to listen to the surroundings without sending any
- beacons yet. */
- ret = wl1271_cmd_join(wl, BSS_TYPE_STA_BSS);
+ ret = wl1271_cmd_join(wl, wl->set_bss_type);
if (ret < 0)
goto out;
@@ -1171,6 +1161,32 @@ out:
return ret;
}
+static void wl1271_set_band_rate(struct wl1271 *wl)
+{
+ if (wl->band == IEEE80211_BAND_2GHZ)
+ wl->basic_rate_set = wl->conf.tx.basic_rate;
+ else
+ wl->basic_rate_set = wl->conf.tx.basic_rate_5;
+}
+
+static u32 wl1271_min_rate_get(struct wl1271 *wl)
+{
+ int i;
+ u32 rate = 0;
+
+ if (!wl->basic_rate_set) {
+ WARN_ON(1);
+ wl->basic_rate_set = wl->conf.tx.basic_rate;
+ }
+
+ for (i = 0; !rate; i++) {
+ if ((wl->basic_rate_set >> i) & 0x1)
+ rate = 1 << i;
+ }
+
+ return rate;
+}
+
static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
{
struct wl1271 *wl = hw->priv;
@@ -1187,12 +1203,41 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
mutex_lock(&wl->mutex);
- wl->band = conf->channel->band;
+ if (unlikely(wl->state == WL1271_STATE_OFF))
+ goto out;
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
goto out;
+ /* if the channel changes while joined, join again */
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+ wl->band = conf->channel->band;
+ wl->channel = channel;
+
+ /*
+ * FIXME: the mac80211 should really provide a fixed rate
+ * to use here. for now, just use the smallest possible rate
+ * for the band as a fixed rate for association frames and
+ * other control messages.
+ */
+ if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
+ wl1271_set_band_rate(wl);
+
+ wl->basic_rate = wl1271_min_rate_get(wl);
+ ret = wl1271_acx_rate_policies(wl);
+ if (ret < 0)
+ wl1271_warning("rate policy for update channel "
+ "failed %d", ret);
+
+ if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
+ ret = wl1271_cmd_join(wl, wl->set_bss_type);
+ if (ret < 0)
+ wl1271_warning("cmd join to update channel "
+ "failed %d", ret);
+ }
+ }
+
if (changed & IEEE80211_CONF_CHANGE_IDLE) {
if (conf->flags & IEEE80211_CONF_IDLE &&
test_bit(WL1271_FLAG_JOINED, &wl->flags))
@@ -1201,24 +1246,17 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
wl1271_join_channel(wl, channel);
if (conf->flags & IEEE80211_CONF_IDLE) {
- wl->rate_set = CONF_TX_RATE_MASK_BASIC;
+ wl->rate_set = wl1271_min_rate_get(wl);
wl->sta_rate_set = 0;
wl1271_acx_rate_policies(wl);
- }
+ wl1271_acx_keep_alive_config(
+ wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
+ ACX_KEEP_ALIVE_TPL_INVALID);
+ set_bit(WL1271_FLAG_IDLE, &wl->flags);
+ } else
+ clear_bit(WL1271_FLAG_IDLE, &wl->flags);
}
- /* if the channel changes while joined, join again */
- if (channel != wl->channel &&
- test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
- wl->channel = channel;
- /* FIXME: maybe use CMD_CHANNEL_SWITCH for this? */
- ret = wl1271_cmd_join(wl, wl->bss_type);
- if (ret < 0)
- wl1271_warning("cmd join to update channel failed %d",
- ret);
- } else
- wl->channel = channel;
-
if (conf->flags & IEEE80211_CONF_PS &&
!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
@@ -1272,6 +1310,11 @@ static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw,
{
struct wl1271_filter_params *fp;
struct netdev_hw_addr *ha;
+ struct wl1271 *wl = hw->priv;
+ int i;
+
+ if (unlikely(wl->state == WL1271_STATE_OFF))
+ return 0;
fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
if (!fp) {
@@ -1314,15 +1357,16 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
mutex_lock(&wl->mutex);
- if (wl->state == WL1271_STATE_OFF)
+ *total &= WL1271_SUPPORTED_FILTERS;
+ changed &= WL1271_SUPPORTED_FILTERS;
+
+ if (unlikely(wl->state == WL1271_STATE_OFF))
goto out;
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
goto out;
- *total &= WL1271_SUPPORTED_FILTERS;
- changed &= WL1271_SUPPORTED_FILTERS;
if (*total & FIF_ALLMULTI)
ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
@@ -1516,10 +1560,13 @@ out:
static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
{
struct wl1271 *wl = hw->priv;
- int ret;
+ int ret = 0;
mutex_lock(&wl->mutex);
+ if (unlikely(wl->state == WL1271_STATE_OFF))
+ goto out;
+
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
goto out;
@@ -1561,6 +1608,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
enum wl1271_cmd_ps_mode mode;
struct wl1271 *wl = hw->priv;
bool do_join = false;
+ bool do_keepalive = false;
int ret;
wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed");
@@ -1571,20 +1619,29 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
if (ret < 0)
goto out;
- if (wl->bss_type == BSS_TYPE_IBSS) {
- /* FIXME: This implements rudimentary ad-hoc support -
- proper templates are on the wish list and notification
- on when they change. This patch will update the templates
- on every call to this function. */
+ if ((changed && BSS_CHANGED_BEACON_INT) &&
+ (wl->bss_type == BSS_TYPE_IBSS)) {
+ wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon interval updated: %d",
+ bss_conf->beacon_int);
+
+ wl->beacon_int = bss_conf->beacon_int;
+ do_join = true;
+ }
+
+ if ((changed && BSS_CHANGED_BEACON) &&
+ (wl->bss_type == BSS_TYPE_IBSS)) {
struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+ wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon updated");
+
if (beacon) {
struct ieee80211_hdr *hdr;
wl1271_ssid_set(wl, beacon);
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON,
beacon->data,
- beacon->len);
+ beacon->len, 0,
+ wl1271_min_rate_get(wl));
if (ret < 0) {
dev_kfree_skb(beacon);
@@ -1599,7 +1656,8 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
ret = wl1271_cmd_template_set(wl,
CMD_TEMPL_PROBE_RESPONSE,
beacon->data,
- beacon->len);
+ beacon->len, 0,
+ wl1271_min_rate_get(wl));
dev_kfree_skb(beacon);
if (ret < 0)
goto out_sleep;
@@ -1609,6 +1667,30 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
}
}
+ if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
+ (wl->bss_type == BSS_TYPE_IBSS)) {
+ wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
+ bss_conf->enable_beacon ? "enabled" : "disabled");
+
+ if (bss_conf->enable_beacon)
+ wl->set_bss_type = BSS_TYPE_IBSS;
+ else
+ wl->set_bss_type = BSS_TYPE_STA_BSS;
+ do_join = true;
+ }
+
+ if (changed & BSS_CHANGED_CQM) {
+ bool enable = false;
+ if (bss_conf->cqm_rssi_thold)
+ enable = true;
+ ret = wl1271_acx_rssi_snr_trigger(wl, enable,
+ bss_conf->cqm_rssi_thold,
+ bss_conf->cqm_rssi_hyst);
+ if (ret < 0)
+ goto out;
+ wl->rssi_thold = bss_conf->cqm_rssi_thold;
+ }
+
if ((changed & BSS_CHANGED_BSSID) &&
/*
* Now we know the correct bssid, so we send a new join command
@@ -1630,10 +1712,23 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_ASSOC) {
if (bss_conf->assoc) {
+ u32 rates;
wl->aid = bss_conf->aid;
set_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
/*
+ * use basic rates from AP, and determine lowest rate
+ * to use with control frames.
+ */
+ rates = bss_conf->basic_rates;
+ wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
+ rates);
+ wl->basic_rate = wl1271_min_rate_get(wl);
+ ret = wl1271_acx_rate_policies(wl);
+ if (ret < 0)
+ goto out_sleep;
+
+ /*
* with wl1271, we don't need to update the
* beacon_int and dtim_period, because the firmware
* updates it by itself when the first beacon is
@@ -1643,7 +1738,30 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
if (ret < 0)
goto out_sleep;
- ret = wl1271_acx_aid(wl, wl->aid);
+ /*
+ * 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.
+ */
+ ret = wl1271_cmd_build_probe_req(wl, NULL, 0,
+ NULL, 0, wl->band);
+
+ /* Enable the keep-alive feature */
+ ret = wl1271_acx_keep_alive_mode(wl, true);
+ if (ret < 0)
+ goto out_sleep;
+
+ /*
+ * This is awkward. The keep-alive configs must be done
+ * *after* the join command, because otherwise it will
+ * not work, but it must only be done *once* because
+ * otherwise the firmware will start complaining.
+ */
+ do_keepalive = true;
+
+ /* enable the connection monitoring feature */
+ ret = wl1271_acx_conn_monit_params(wl, true);
if (ret < 0)
goto out_sleep;
@@ -1659,6 +1777,22 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
/* use defaults when not associated */
clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
wl->aid = 0;
+
+ /* revert back to minimum rates for the current band */
+ wl1271_set_band_rate(wl);
+ wl->basic_rate = wl1271_min_rate_get(wl);
+ ret = wl1271_acx_rate_policies(wl);
+ if (ret < 0)
+ goto out_sleep;
+
+ /* disable connection monitor features */
+ ret = wl1271_acx_conn_monit_params(wl, false);
+
+ /* Disable the keep-alive feature */
+ ret = wl1271_acx_keep_alive_mode(wl, false);
+
+ if (ret < 0)
+ goto out_sleep;
}
}
@@ -1693,7 +1827,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
}
if (do_join) {
- ret = wl1271_cmd_join(wl, wl->bss_type);
+ ret = wl1271_cmd_join(wl, wl->set_bss_type);
if (ret < 0) {
wl1271_warning("cmd join failed %d", ret);
goto out_sleep;
@@ -1701,6 +1835,29 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
set_bit(WL1271_FLAG_JOINED, &wl->flags);
}
+ /*
+ * The JOIN operation shuts down the firmware keep-alive as a side
+ * effect, and the ACX_AID will start the keep-alive as a side effect.
+ * Hence, for non-IBSS, the ACX_AID must always happen *after* the
+ * JOIN operation, and the template config after the ACX_AID.
+ */
+ if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
+ ret = wl1271_acx_aid(wl, wl->aid);
+ if (ret < 0)
+ goto out_sleep;
+ }
+
+ if (do_keepalive) {
+ ret = wl1271_cmd_build_klv_null_data(wl);
+ if (ret < 0)
+ goto out_sleep;
+ ret = wl1271_acx_keep_alive_config(
+ wl, CMD_TEMPL_KLV_IDX_NULL_DATA,
+ ACX_KEEP_ALIVE_TPL_VALID);
+ if (ret < 0)
+ goto out_sleep;
+ }
+
out_sleep:
wl1271_ps_elp_sleep(wl);
@@ -1812,6 +1969,36 @@ static struct ieee80211_channel wl1271_channels[] = {
{ .hw_value = 13, .center_freq = 2472, .max_power = 25 },
};
+/* mapping to indexes for wl1271_rates */
+const static u8 wl1271_rate_to_idx_2ghz[] = {
+ /* MCS rates are used only with 11n */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS7 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS6 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS5 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS4 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS3 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS2 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS1 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS0 */
+
+ 11, /* CONF_HW_RXTX_RATE_54 */
+ 10, /* CONF_HW_RXTX_RATE_48 */
+ 9, /* CONF_HW_RXTX_RATE_36 */
+ 8, /* CONF_HW_RXTX_RATE_24 */
+
+ /* TI-specific rate */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
+
+ 7, /* CONF_HW_RXTX_RATE_18 */
+ 6, /* CONF_HW_RXTX_RATE_12 */
+ 3, /* CONF_HW_RXTX_RATE_11 */
+ 5, /* CONF_HW_RXTX_RATE_9 */
+ 4, /* CONF_HW_RXTX_RATE_6 */
+ 2, /* CONF_HW_RXTX_RATE_5_5 */
+ 1, /* CONF_HW_RXTX_RATE_2 */
+ 0 /* CONF_HW_RXTX_RATE_1 */
+};
+
/* can't be const, mac80211 writes to this */
static struct ieee80211_supported_band wl1271_band_2ghz = {
.channels = wl1271_channels,
@@ -1894,6 +2081,35 @@ static struct ieee80211_channel wl1271_channels_5ghz[] = {
{ .hw_value = 165, .center_freq = 5825},
};
+/* mapping to indexes for wl1271_rates_5ghz */
+const static u8 wl1271_rate_to_idx_5ghz[] = {
+ /* MCS rates are used only with 11n */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS7 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS6 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS5 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS4 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS3 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS2 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS1 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_MCS0 */
+
+ 7, /* CONF_HW_RXTX_RATE_54 */
+ 6, /* CONF_HW_RXTX_RATE_48 */
+ 5, /* CONF_HW_RXTX_RATE_36 */
+ 4, /* CONF_HW_RXTX_RATE_24 */
+
+ /* TI-specific rate */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_22 */
+
+ 3, /* CONF_HW_RXTX_RATE_18 */
+ 2, /* CONF_HW_RXTX_RATE_12 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_11 */
+ 1, /* CONF_HW_RXTX_RATE_9 */
+ 0, /* CONF_HW_RXTX_RATE_6 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_5_5 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED, /* CONF_HW_RXTX_RATE_2 */
+ CONF_HW_RXTX_RATE_UNSUPPORTED /* CONF_HW_RXTX_RATE_1 */
+};
static struct ieee80211_supported_band wl1271_band_5ghz = {
.channels = wl1271_channels_5ghz,
@@ -1902,6 +2118,11 @@ static struct ieee80211_supported_band wl1271_band_5ghz = {
.n_bitrates = ARRAY_SIZE(wl1271_rates_5ghz),
};
+const static u8 *wl1271_band_rate_to_idx[] = {
+ [IEEE80211_BAND_2GHZ] = wl1271_rate_to_idx_2ghz,
+ [IEEE80211_BAND_5GHZ] = wl1271_rate_to_idx_5ghz
+};
+
static const struct ieee80211_ops wl1271_ops = {
.start = wl1271_op_start,
.stop = wl1271_op_stop,
@@ -1919,6 +2140,27 @@ static const struct ieee80211_ops wl1271_ops = {
CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
};
+
+u8 wl1271_rate_to_idx(struct wl1271 *wl, int rate)
+{
+ u8 idx;
+
+ BUG_ON(wl->band >= sizeof(wl1271_band_rate_to_idx)/sizeof(u8 *));
+
+ if (unlikely(rate >= CONF_HW_RXTX_RATE_MAX)) {
+ wl1271_error("Illegal RX rate from HW: %d", rate);
+ return 0;
+ }
+
+ idx = wl1271_band_rate_to_idx[wl->band][rate];
+ if (unlikely(idx == CONF_HW_RXTX_RATE_UNSUPPORTED)) {
+ wl1271_error("Unsupported RX rate from HW: %d", rate);
+ return 0;
+ }
+
+ return idx;
+}
+
static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -2021,13 +2263,16 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
/* unit us */
/* FIXME: find a proper value */
wl->hw->channel_change_time = 10000;
+ wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_NOISE_DBM |
IEEE80211_HW_BEACON_FILTER |
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_SUPPORTS_UAPSD |
- IEEE80211_HW_HAS_RATE_CONTROL;
+ IEEE80211_HW_HAS_RATE_CONTROL |
+ IEEE80211_HW_CONNECTION_MONITOR |
+ IEEE80211_HW_SUPPORTS_CQM_RSSI;
wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC);
@@ -2038,6 +2283,7 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz;
wl->hw->queues = 4;
+ wl->hw->max_rates = 1;
SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
@@ -2053,7 +2299,6 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
struct platform_device *plat_dev = NULL;
struct wl1271 *wl;
int i, ret;
- static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
if (!hw) {
@@ -2083,6 +2328,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
wl->channel = WL1271_DEFAULT_CHANNEL;
+ wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
wl->default_key = 0;
wl->rx_counter = 0;
wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
@@ -2090,6 +2336,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
wl->psm_entry_retry = 0;
wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
+ wl->basic_rate = CONF_TX_RATE_MASK_BASIC;
wl->rate_set = CONF_TX_RATE_MASK_BASIC;
wl->sta_rate_set = 0;
wl->band = IEEE80211_BAND_2GHZ;
@@ -2105,13 +2352,6 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
wl->state = WL1271_STATE_OFF;
mutex_init(&wl->mutex);
- /*
- * FIXME: we should use a zero MAC address here, but for now we
- * generate a random Nokia address.
- */
- memcpy(wl->mac_addr, nokia_oui, 3);
- get_random_bytes(wl->mac_addr + 3, 3);
-
/* Apply default driver configuration. */
wl1271_conf_init(wl);
@@ -2157,7 +2397,6 @@ int wl1271_free_hw(struct wl1271 *wl)
wl1271_debugfs_exit(wl);
- kfree(wl->target_mem_map);
vfree(wl->fw);
wl->fw = NULL;
kfree(wl->nvs);
diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.c b/drivers/net/wireless/wl12xx/wl1271_ps.c
index 5a04482b9353..a5e60e0403e5 100644
--- a/drivers/net/wireless/wl12xx/wl1271_ps.c
+++ b/drivers/net/wireless/wl12xx/wl1271_ps.c
@@ -40,7 +40,8 @@ void wl1271_elp_work(struct work_struct *work)
mutex_lock(&wl->mutex);
if (test_bit(WL1271_FLAG_IN_ELP, &wl->flags) ||
- !test_bit(WL1271_FLAG_PSM, &wl->flags))
+ (!test_bit(WL1271_FLAG_PSM, &wl->flags) &&
+ !test_bit(WL1271_FLAG_IDLE, &wl->flags)))
goto out;
wl1271_debug(DEBUG_PSM, "chip to elp");
@@ -56,7 +57,8 @@ out:
/* Routines to toggle sleep mode while in ELP */
void wl1271_ps_elp_sleep(struct wl1271 *wl)
{
- if (test_bit(WL1271_FLAG_PSM, &wl->flags)) {
+ if (test_bit(WL1271_FLAG_PSM, &wl->flags) ||
+ test_bit(WL1271_FLAG_IDLE, &wl->flags)) {
cancel_delayed_work(&wl->elp_work);
ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
msecs_to_jiffies(ELP_ENTRY_DELAY));
diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.c b/drivers/net/wireless/wl12xx/wl1271_rx.c
index 6f1b732ae43b..57f4bfd959c8 100644
--- a/drivers/net/wireless/wl12xx/wl1271_rx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_rx.c
@@ -43,66 +43,6 @@ static u32 wl1271_rx_get_buf_size(struct wl1271_fw_status *status,
RX_BUF_SIZE_MASK) >> RX_BUF_SIZE_SHIFT_DIV;
}
-/* The values of this table must match the wl1271_rates[] array */
-static u8 wl1271_rx_rate_to_idx[] = {
- /* MCS rates are used only with 11n */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS7 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS6 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS5 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS4 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS3 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS2 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS1 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS0 */
-
- 11, /* WL1271_RATE_54 */
- 10, /* WL1271_RATE_48 */
- 9, /* WL1271_RATE_36 */
- 8, /* WL1271_RATE_24 */
-
- /* TI-specific rate */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_22 */
-
- 7, /* WL1271_RATE_18 */
- 6, /* WL1271_RATE_12 */
- 3, /* WL1271_RATE_11 */
- 5, /* WL1271_RATE_9 */
- 4, /* WL1271_RATE_6 */
- 2, /* WL1271_RATE_5_5 */
- 1, /* WL1271_RATE_2 */
- 0 /* WL1271_RATE_1 */
-};
-
-/* The values of this table must match the wl1271_rates[] array */
-static u8 wl1271_5_ghz_rx_rate_to_idx[] = {
- /* MCS rates are used only with 11n */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS7 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS6 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS5 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS4 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS3 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS2 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS1 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS0 */
-
- 7, /* WL1271_RATE_54 */
- 6, /* WL1271_RATE_48 */
- 5, /* WL1271_RATE_36 */
- 4, /* WL1271_RATE_24 */
-
- /* TI-specific rate */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_22 */
-
- 3, /* WL1271_RATE_18 */
- 2, /* WL1271_RATE_12 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_11 */
- 1, /* WL1271_RATE_9 */
- 0, /* WL1271_RATE_6 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_5_5 */
- WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_2 */
- WL1271_RX_RATE_UNSUPPORTED /* WL1271_RATE_1 */
-};
-
static void wl1271_rx_status(struct wl1271 *wl,
struct wl1271_rx_descriptor *desc,
struct ieee80211_rx_status *status,
@@ -110,20 +50,8 @@ static void wl1271_rx_status(struct wl1271 *wl,
{
memset(status, 0, sizeof(struct ieee80211_rx_status));
- if ((desc->flags & WL1271_RX_DESC_BAND_MASK) ==
- WL1271_RX_DESC_BAND_BG) {
- status->band = IEEE80211_BAND_2GHZ;
- status->rate_idx = wl1271_rx_rate_to_idx[desc->rate];
- } else if ((desc->flags & WL1271_RX_DESC_BAND_MASK) ==
- WL1271_RX_DESC_BAND_A) {
- status->band = IEEE80211_BAND_5GHZ;
- status->rate_idx = wl1271_5_ghz_rx_rate_to_idx[desc->rate];
- } else
- wl1271_warning("unsupported band 0x%x",
- desc->flags & WL1271_RX_DESC_BAND_MASK);
-
- if (unlikely(status->rate_idx == WL1271_RX_RATE_UNSUPPORTED))
- wl1271_warning("unsupported rate");
+ 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
@@ -133,13 +61,6 @@ static void wl1271_rx_status(struct wl1271 *wl,
*/
status->signal = desc->rssi;
- /*
- * FIXME: In wl1251, the SNR should be divided by two. In wl1271 we
- * need to divide by two for now, but TI has been discussing about
- * changing it. This needs to be rechecked.
- */
- status->noise = desc->rssi - (desc->snr >> 1);
-
status->freq = ieee80211_channel_to_frequency(desc->channel);
if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) {
diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.h b/drivers/net/wireless/wl12xx/wl1271_rx.h
index 1ae6d1783ed4..b89be4758e78 100644
--- a/drivers/net/wireless/wl12xx/wl1271_rx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_rx.h
@@ -43,7 +43,6 @@
#define RX_MAX_PACKET_ID 3
#define NUM_RX_PKT_DESC_MOD_MASK 7
-#define WL1271_RX_RATE_UNSUPPORTED 0xFF
#define RX_DESC_VALID_FCS 0x0001
#define RX_DESC_MATCH_RXADDR1 0x0002
@@ -117,5 +116,6 @@ struct wl1271_rx_descriptor {
} __attribute__ ((packed));
void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status);
+u8 wl1271_rate_to_idx(struct wl1271 *wl, int rate);
#endif
diff --git a/drivers/net/wireless/wl12xx/wl1271_sdio.c b/drivers/net/wireless/wl12xx/wl1271_sdio.c
index 3c03de74dbfc..d3d6f302f705 100644
--- a/drivers/net/wireless/wl12xx/wl1271_sdio.c
+++ b/drivers/net/wireless/wl12xx/wl1271_sdio.c
@@ -117,7 +117,7 @@ static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
else
ret = sdio_memcpy_fromio(func, buf, addr, len);
- wl1271_debug(DEBUG_SDIO, "sdio read 53 addr 0x%x, %d bytes",
+ wl1271_debug(DEBUG_SDIO, "sdio read 53 addr 0x%x, %zu bytes",
addr, len);
wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
}
@@ -138,7 +138,7 @@ static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
wl1271_debug(DEBUG_SDIO, "sdio write 52 addr 0x%x, byte 0x%02x",
addr, ((u8 *)buf)[0]);
} else {
- wl1271_debug(DEBUG_SDIO, "sdio write 53 addr 0x%x, %d bytes",
+ wl1271_debug(DEBUG_SDIO, "sdio write 53 addr 0x%x, %zu bytes",
addr, len);
wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
@@ -255,7 +255,7 @@ static void __devexit wl1271_remove(struct sdio_func *func)
}
static struct sdio_driver wl1271_sdio_driver = {
- .name = "wl1271",
+ .name = "wl1271_sdio",
.id_table = wl1271_devices,
.probe = wl1271_probe,
.remove = __devexit_p(wl1271_remove),
diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c
index 256e84ad0baf..5189b812f939 100644
--- a/drivers/net/wireless/wl12xx/wl1271_spi.c
+++ b/drivers/net/wireless/wl12xx/wl1271_spi.c
@@ -105,6 +105,7 @@ static void wl1271_spi_reset(struct wl1271 *wl)
spi_message_add_tail(&t, &m);
spi_sync(wl_to_spi(wl), &m);
+ kfree(cmd);
wl1271_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN);
}
@@ -159,47 +160,24 @@ static void wl1271_spi_init(struct wl1271 *wl)
spi_message_add_tail(&t, &m);
spi_sync(wl_to_spi(wl), &m);
+ kfree(cmd);
wl1271_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN);
}
#define WL1271_BUSY_WORD_TIMEOUT 1000
-/* FIXME: Check busy words, removed due to SPI bug */
-#if 0
-static void wl1271_spi_read_busy(struct wl1271 *wl, void *buf, size_t len)
+static int wl1271_spi_read_busy(struct wl1271 *wl)
{
struct spi_transfer t[1];
struct spi_message m;
u32 *busy_buf;
int num_busy_bytes = 0;
- wl1271_info("spi read BUSY!");
-
- /*
- * Look for the non-busy word in the read buffer, and if found,
- * read in the remaining data into the buffer.
- */
- busy_buf = (u32 *)buf;
- for (; (u32)busy_buf < (u32)buf + len; busy_buf++) {
- num_busy_bytes += sizeof(u32);
- if (*busy_buf & 0x1) {
- spi_message_init(&m);
- memset(t, 0, sizeof(t));
- memmove(buf, busy_buf, len - num_busy_bytes);
- t[0].rx_buf = buf + (len - num_busy_bytes);
- t[0].len = num_busy_bytes;
- spi_message_add_tail(&t[0], &m);
- spi_sync(wl_to_spi(wl), &m);
- return;
- }
- }
-
/*
* Read further busy words from SPI until a non-busy word is
* encountered, then read the data itself into the buffer.
*/
- wl1271_info("spi read BUSY-polling needed!");
num_busy_bytes = WL1271_BUSY_WORD_TIMEOUT;
busy_buf = wl->buffer_busyword;
@@ -209,28 +187,21 @@ static void wl1271_spi_read_busy(struct wl1271 *wl, void *buf, size_t len)
memset(t, 0, sizeof(t));
t[0].rx_buf = busy_buf;
t[0].len = sizeof(u32);
+ t[0].cs_change = true;
spi_message_add_tail(&t[0], &m);
spi_sync(wl_to_spi(wl), &m);
- if (*busy_buf & 0x1) {
- spi_message_init(&m);
- memset(t, 0, sizeof(t));
- t[0].rx_buf = buf;
- t[0].len = len;
- spi_message_add_tail(&t[0], &m);
- spi_sync(wl_to_spi(wl), &m);
- return;
- }
+ if (*busy_buf & 0x1)
+ return 0;
}
/* The SPI bus is unresponsive, the read failed. */
- memset(buf, 0, len);
wl1271_error("SPI read busy-word timeout!\n");
+ return -ETIMEDOUT;
}
-#endif
static void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf,
- size_t len, bool fixed)
+ size_t len, bool fixed)
{
struct spi_transfer t[3];
struct spi_message m;
@@ -253,22 +224,32 @@ static void wl1271_spi_raw_read(struct wl1271 *wl, int addr, void *buf,
t[0].tx_buf = cmd;
t[0].len = 4;
+ t[0].cs_change = true;
spi_message_add_tail(&t[0], &m);
/* Busy and non busy words read */
t[1].rx_buf = busy_buf;
t[1].len = WL1271_BUSY_WORD_LEN;
+ t[1].cs_change = true;
spi_message_add_tail(&t[1], &m);
- t[2].rx_buf = buf;
- t[2].len = len;
- spi_message_add_tail(&t[2], &m);
-
spi_sync(wl_to_spi(wl), &m);
- /* FIXME: Check busy words, removed due to SPI bug */
- /* if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1))
- wl1271_spi_read_busy(wl, buf, len); */
+ if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1) &&
+ wl1271_spi_read_busy(wl)) {
+ memset(buf, 0, len);
+ return;
+ }
+
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+
+ t[0].rx_buf = buf;
+ t[0].len = len;
+ t[0].cs_change = true;
+ spi_message_add_tail(&t[0], &m);
+
+ spi_sync(wl_to_spi(wl), &m);
wl1271_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd));
wl1271_dump(DEBUG_SPI, "spi_read buf <- ", buf, len);
@@ -443,7 +424,7 @@ static int __devexit wl1271_remove(struct spi_device *spi)
static struct spi_driver wl1271_spi_driver = {
.driver = {
- .name = "wl1271",
+ .name = "wl1271_spi",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c
index 6d109df9a0a0..62db79508ddf 100644
--- a/drivers/net/wireless/wl12xx/wl1271_tx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_tx.c
@@ -220,7 +220,7 @@ static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb)
return ret;
}
-static u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
+u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
{
struct ieee80211_supported_band *band;
u32 enabled_rates = 0;
@@ -304,6 +304,8 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
struct ieee80211_tx_info *info;
struct sk_buff *skb;
int id = result->id;
+ int rate = -1;
+ u8 retries = 0;
/* check for id legality */
if (unlikely(id >= ACX_TX_DESCRIPTORS || wl->tx_frames[id] == NULL)) {
@@ -314,19 +316,22 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
skb = wl->tx_frames[id];
info = IEEE80211_SKB_CB(skb);
- /* update packet status */
- if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) {
- if (result->status == TX_SUCCESS)
+ /* update the TX status info */
+ if (result->status == TX_SUCCESS) {
+ if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))
info->flags |= IEEE80211_TX_STAT_ACK;
- if (result->status & TX_RETRY_EXCEEDED) {
- /* FIXME */
- /* info->status.excessive_retries = 1; */
- wl->stats.excessive_retries++;
- }
+ rate = wl1271_rate_to_idx(wl, result->rate_class_index);
+ retries = result->ack_failures;
+ } else if (result->status == TX_RETRY_EXCEEDED) {
+ wl->stats.excessive_retries++;
+ retries = result->ack_failures;
}
- /* FIXME */
- /* info->status.retry_count = result->ack_failures; */
+ info->status.rates[0].idx = rate;
+ info->status.rates[0].count = retries;
+ info->status.rates[0].flags = 0;
+ info->status.ack_signal = -1;
+
wl->stats.retry_count += result->ack_failures;
/* update security sequence number */
@@ -350,8 +355,6 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
result->id, skb, result->ack_failures,
result->rate_class_index, result->status);
- /* FIXME: do we need to tell the stack about the used rate? */
-
/* return the packet to the stack */
ieee80211_tx_status(wl->hw, skb);
wl->tx_frames[result->id] = NULL;
@@ -413,31 +416,19 @@ void wl1271_tx_flush(struct wl1271 *wl)
{
int i;
struct sk_buff *skb;
- struct ieee80211_tx_info *info;
/* TX failure */
/* control->flags = 0; FIXME */
while ((skb = skb_dequeue(&wl->tx_queue))) {
- info = IEEE80211_SKB_CB(skb);
-
wl1271_debug(DEBUG_TX, "flushing skb 0x%p", skb);
-
- if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS))
- continue;
-
ieee80211_tx_status(wl->hw, skb);
}
for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
if (wl->tx_frames[i] != NULL) {
skb = wl->tx_frames[i];
- info = IEEE80211_SKB_CB(skb);
-
- if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS))
- continue;
-
- ieee80211_tx_status(wl->hw, skb);
wl->tx_frames[i] = NULL;
+ ieee80211_tx_status(wl->hw, skb);
}
}
diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.h b/drivers/net/wireless/wl12xx/wl1271_tx.h
index 5e6c27a57415..3b8b7ac253fd 100644
--- a/drivers/net/wireless/wl12xx/wl1271_tx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_tx.h
@@ -159,5 +159,7 @@ static inline int wl1271_tx_ac_to_tid(int ac)
void wl1271_tx_work(struct work_struct *work);
void wl1271_tx_complete(struct wl1271 *wl);
void wl1271_tx_flush(struct wl1271 *wl);
+u8 wl1271_rate_to_idx(struct wl1271 *wl, int rate);
+u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set);
#endif