aboutsummaryrefslogtreecommitdiff
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/wl1251.h5
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_acx.c68
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_acx.h36
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_boot.c55
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_event.c15
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_init.c8
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_main.c162
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_ps.c44
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_ps.h1
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_reg.h6
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_rx.c4
-rw-r--r--drivers/net/wireless/wl12xx/wl1251_spi.c2
12 files changed, 310 insertions, 96 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1251.h b/drivers/net/wireless/wl12xx/wl1251.h
index 998e4b6252b..054533f7a12 100644
--- a/drivers/net/wireless/wl12xx/wl1251.h
+++ b/drivers/net/wireless/wl12xx/wl1251.h
@@ -269,6 +269,7 @@ struct wl1251 {
void (*set_power)(bool enable);
int irq;
+ bool use_eeprom;
enum wl1251_state state;
struct mutex mutex;
@@ -354,6 +355,8 @@ struct wl1251 {
/* is firmware in elp mode */
bool elp;
+ struct delayed_work elp_work;
+
/* we can be in psm, but not in elp, we have to differentiate */
bool psm;
@@ -374,6 +377,8 @@ struct wl1251 {
u8 buffer_busyword[WL1251_BUSY_WORD_LEN];
struct wl1251_rx_descriptor *rx_descriptor;
+ struct ieee80211_vif *vif;
+
u32 chip_id;
char fw_ver[21];
};
diff --git a/drivers/net/wireless/wl12xx/wl1251_acx.c b/drivers/net/wireless/wl12xx/wl1251_acx.c
index 10b26c4532c..acfa086dbfc 100644
--- a/drivers/net/wireless/wl12xx/wl1251_acx.c
+++ b/drivers/net/wireless/wl12xx/wl1251_acx.c
@@ -494,7 +494,7 @@ out:
return ret;
}
-int wl1251_acx_beacon_filter_opt(struct wl1251 *wl)
+int wl1251_acx_beacon_filter_opt(struct wl1251 *wl, bool enable_filter)
{
struct acx_beacon_filter_option *beacon_filter;
int ret;
@@ -507,7 +507,7 @@ int wl1251_acx_beacon_filter_opt(struct wl1251 *wl)
goto out;
}
- beacon_filter->enable = 0;
+ beacon_filter->enable = enable_filter;
beacon_filter->max_num_beacons = 0;
ret = wl1251_cmd_configure(wl, ACX_BEACON_FILTER_OPT,
@@ -525,6 +525,7 @@ out:
int wl1251_acx_beacon_filter_table(struct wl1251 *wl)
{
struct acx_beacon_filter_ie_table *ie_table;
+ int idx = 0;
int ret;
wl1251_debug(DEBUG_ACX, "acx beacon filter table");
@@ -535,8 +536,10 @@ int wl1251_acx_beacon_filter_table(struct wl1251 *wl)
goto out;
}
- ie_table->num_ie = 0;
- memset(ie_table->table, 0, BEACON_FILTER_TABLE_MAX_SIZE);
+ /* configure default beacon pass-through rules */
+ ie_table->num_ie = 1;
+ ie_table->table[idx++] = BEACON_FILTER_IE_ID_CHANNEL_SWITCH_ANN;
+ ie_table->table[idx++] = BEACON_RULE_PASS_ON_APPEARANCE;
ret = wl1251_cmd_configure(wl, ACX_BEACON_FILTER_TABLE,
ie_table, sizeof(*ie_table));
@@ -550,6 +553,35 @@ out:
return ret;
}
+int wl1251_acx_conn_monit_params(struct wl1251 *wl)
+{
+ struct acx_conn_monit_params *acx;
+ int ret;
+
+ wl1251_debug(DEBUG_ACX, "acx connection monitor parameters");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->synch_fail_thold = SYNCH_FAIL_DEFAULT_THRESHOLD;
+ acx->bss_lose_timeout = NO_BEACON_DEFAULT_TIMEOUT;
+
+ ret = wl1251_cmd_configure(wl, ACX_CONN_MONIT_PARAMS,
+ acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1251_warning("failed to set connection monitor "
+ "parameters: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
int wl1251_acx_sg_enable(struct wl1251 *wl)
{
struct acx_bt_wlan_coex *pta;
@@ -916,3 +948,31 @@ out:
kfree(mem_conf);
return ret;
}
+
+int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim)
+{
+ struct wl1251_acx_wr_tbtt_and_dtim *acx;
+ int ret;
+
+ wl1251_debug(DEBUG_ACX, "acx tbtt and dtim");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->tbtt = tbtt;
+ acx->dtim = dtim;
+
+ ret = wl1251_cmd_configure(wl, ACX_WR_TBTT_AND_DTIM,
+ acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1251_warning("failed to set tbtt and dtim: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
diff --git a/drivers/net/wireless/wl12xx/wl1251_acx.h b/drivers/net/wireless/wl12xx/wl1251_acx.h
index cafb9145950..652371432cd 100644
--- a/drivers/net/wireless/wl12xx/wl1251_acx.h
+++ b/drivers/net/wireless/wl12xx/wl1251_acx.h
@@ -450,6 +450,11 @@ struct acx_beacon_filter_option {
(BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM * \
BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE))
+#define BEACON_RULE_PASS_ON_CHANGE BIT(0)
+#define BEACON_RULE_PASS_ON_APPEARANCE BIT(1)
+
+#define BEACON_FILTER_IE_ID_CHANNEL_SWITCH_ANN (37)
+
struct acx_beacon_filter_ie_table {
struct acx_header header;
@@ -458,6 +463,16 @@ struct acx_beacon_filter_ie_table {
u8 pad[3];
} __attribute__ ((packed));
+#define SYNCH_FAIL_DEFAULT_THRESHOLD 10 /* number of beacons */
+#define NO_BEACON_DEFAULT_TIMEOUT (500) /* in microseconds */
+
+struct acx_conn_monit_params {
+ struct acx_header header;
+
+ u32 synch_fail_thold; /* number of beacons missed */
+ u32 bss_lose_timeout; /* number of TU's from synch fail */
+};
+
enum {
SG_ENABLE = 0,
SG_DISABLE,
@@ -1134,6 +1149,23 @@ struct wl1251_acx_mem_map {
u32 num_rx_mem_blocks;
} __attribute__ ((packed));
+
+struct wl1251_acx_wr_tbtt_and_dtim {
+
+ struct acx_header header;
+
+ /* Time in TUs between two consecutive beacons */
+ u16 tbtt;
+
+ /*
+ * DTIM period
+ * For BSS: Number of TBTTs in a DTIM period (range: 1-10)
+ * For IBSS: value shall be set to 1
+ */
+ u8 dtim;
+ u8 padding;
+} __attribute__ ((packed));
+
/*************************************************************************
Host Interrupt Register (WiLink -> Host)
@@ -1273,8 +1305,9 @@ int wl1251_acx_slot(struct wl1251 *wl, enum acx_slot_type slot_time);
int wl1251_acx_group_address_tbl(struct wl1251 *wl);
int wl1251_acx_service_period_timeout(struct wl1251 *wl);
int wl1251_acx_rts_threshold(struct wl1251 *wl, u16 rts_threshold);
-int wl1251_acx_beacon_filter_opt(struct wl1251 *wl);
+int wl1251_acx_beacon_filter_opt(struct wl1251 *wl, bool enable_filter);
int wl1251_acx_beacon_filter_table(struct wl1251 *wl);
+int wl1251_acx_conn_monit_params(struct wl1251 *wl);
int wl1251_acx_sg_enable(struct wl1251 *wl);
int wl1251_acx_sg_cfg(struct wl1251 *wl);
int wl1251_acx_cca_threshold(struct wl1251 *wl);
@@ -1288,5 +1321,6 @@ int wl1251_acx_statistics(struct wl1251 *wl, struct acx_statistics *stats);
int wl1251_acx_tsf_info(struct wl1251 *wl, u64 *mactime);
int wl1251_acx_rate_policies(struct wl1251 *wl);
int wl1251_acx_mem_cfg(struct wl1251 *wl);
+int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim);
#endif /* __WL1251_ACX_H__ */
diff --git a/drivers/net/wireless/wl12xx/wl1251_boot.c b/drivers/net/wireless/wl12xx/wl1251_boot.c
index 452d748e42c..2e733e7bdfd 100644
--- a/drivers/net/wireless/wl12xx/wl1251_boot.c
+++ b/drivers/net/wireless/wl12xx/wl1251_boot.c
@@ -296,8 +296,12 @@ int wl1251_boot_run_firmware(struct wl1251 *wl)
WL1251_ACX_INTR_INIT_COMPLETE;
wl1251_boot_target_enable_interrupts(wl);
- /* unmask all mbox events */
- wl->event_mask = 0xffffffff;
+ wl->event_mask = SCAN_COMPLETE_EVENT_ID | BSS_LOSE_EVENT_ID |
+ SYNCHRONIZATION_TIMEOUT_EVENT_ID |
+ ROAMING_TRIGGER_LOW_RSSI_EVENT_ID |
+ ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID |
+ REGAINED_BSS_EVENT_ID | BT_PTA_SENSE_EVENT_ID |
+ BT_PTA_PREDICTION_EVENT_ID;
ret = wl1251_event_unmask(wl);
if (ret < 0) {
@@ -314,8 +318,8 @@ int wl1251_boot_run_firmware(struct wl1251 *wl)
static int wl1251_boot_upload_firmware(struct wl1251 *wl)
{
int addr, chunk_num, partition_limit;
- size_t fw_data_len;
- u8 *p;
+ size_t fw_data_len, len;
+ u8 *p, *buf;
/* whal_FwCtrl_LoadFwImageSm() */
@@ -334,6 +338,12 @@ static int wl1251_boot_upload_firmware(struct wl1251 *wl)
return -EIO;
}
+ buf = kmalloc(CHUNK_SIZE, GFP_KERNEL);
+ if (!buf) {
+ wl1251_error("allocation for firmware upload chunk failed");
+ return -ENOMEM;
+ }
+
wl1251_set_partition(wl, WL1251_PART_DOWN_MEM_START,
WL1251_PART_DOWN_MEM_SIZE,
WL1251_PART_DOWN_REG_START,
@@ -364,7 +374,11 @@ static int wl1251_boot_upload_firmware(struct wl1251 *wl)
p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE;
wl1251_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x",
p, addr);
- wl1251_mem_write(wl, addr, p, CHUNK_SIZE);
+
+ /* need to copy the chunk for dma */
+ len = CHUNK_SIZE;
+ memcpy(buf, p, len);
+ wl1251_mem_write(wl, addr, buf, len);
chunk_num++;
}
@@ -372,9 +386,16 @@ static int wl1251_boot_upload_firmware(struct wl1251 *wl)
/* 10.4 upload the last chunk */
addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE;
p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE;
+
+ /* need to copy the chunk for dma */
+ len = fw_data_len % CHUNK_SIZE;
+ memcpy(buf, p, len);
+
wl1251_debug(DEBUG_BOOT, "uploading fw last chunk (%zu B) 0x%p to 0x%x",
- fw_data_len % CHUNK_SIZE, p, addr);
- wl1251_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE);
+ len, p, addr);
+ wl1251_mem_write(wl, addr, buf, len);
+
+ kfree(buf);
return 0;
}
@@ -473,13 +494,19 @@ int wl1251_boot(struct wl1251 *wl)
goto out;
/* 2. start processing NVS file */
- ret = wl1251_boot_upload_nvs(wl);
- if (ret < 0)
- goto out;
-
- /* write firmware's last address (ie. it's length) to
- * ACX_EEPROMLESS_IND_REG */
- wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len);
+ if (wl->use_eeprom) {
+ wl1251_reg_write32(wl, ACX_REG_EE_START, START_EEPROM_MGR);
+ msleep(4000);
+ wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, USE_EEPROM);
+ } else {
+ ret = wl1251_boot_upload_nvs(wl);
+ if (ret < 0)
+ goto out;
+
+ /* write firmware's last address (ie. it's length) to
+ * ACX_EEPROMLESS_IND_REG */
+ wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len);
+ }
/* 6. read the EEPROM parameters */
tmp = wl1251_reg_read32(wl, SCR_PAD2);
diff --git a/drivers/net/wireless/wl12xx/wl1251_event.c b/drivers/net/wireless/wl12xx/wl1251_event.c
index 00076c4a8a2..020d764f9c1 100644
--- a/drivers/net/wireless/wl12xx/wl1251_event.c
+++ b/drivers/net/wireless/wl12xx/wl1251_event.c
@@ -79,6 +79,21 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox)
}
}
+ if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID && wl->psm) {
+ wl1251_debug(DEBUG_EVENT, "SYNCHRONIZATION_TIMEOUT_EVENT");
+
+ /* indicate to the stack, that beacons have been lost */
+ ieee80211_beacon_loss(wl->vif);
+ }
+
+ if (vector & REGAINED_BSS_EVENT_ID) {
+ if (wl->psm_requested) {
+ ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
return 0;
}
diff --git a/drivers/net/wireless/wl12xx/wl1251_init.c b/drivers/net/wireless/wl12xx/wl1251_init.c
index b2ee4f468fc..5cb573383ee 100644
--- a/drivers/net/wireless/wl12xx/wl1251_init.c
+++ b/drivers/net/wireless/wl12xx/wl1251_init.c
@@ -147,7 +147,8 @@ int wl1251_hw_init_beacon_filter(struct wl1251 *wl)
{
int ret;
- ret = wl1251_acx_beacon_filter_opt(wl);
+ /* disable beacon filtering at this stage */
+ ret = wl1251_acx_beacon_filter_opt(wl, false);
if (ret < 0)
return ret;
@@ -364,6 +365,11 @@ int wl1251_hw_init(struct wl1251 *wl)
if (ret < 0)
goto out_free_data_path;
+ /* Initialize connection monitoring thresholds */
+ ret = wl1251_acx_conn_monit_params(wl);
+ if (ret < 0)
+ goto out_free_data_path;
+
/* Beacon filtering */
ret = wl1251_hw_init_beacon_filter(wl);
if (ret < 0)
diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c
index d03a07e1be7..ff4be7bf5d3 100644
--- a/drivers/net/wireless/wl12xx/wl1251_main.c
+++ b/drivers/net/wireless/wl12xx/wl1251_main.c
@@ -28,6 +28,7 @@
#include <linux/irq.h>
#include <linux/crc32.h>
#include <linux/etherdevice.h>
+#include <linux/vmalloc.h>
#include "wl1251.h"
#include "wl12xx_80211.h"
@@ -83,7 +84,7 @@ static int wl1251_fetch_firmware(struct wl1251 *wl)
}
wl->fw_len = fw->size;
- wl->fw = kmalloc(wl->fw_len, GFP_KERNEL);
+ wl->fw = vmalloc(wl->fw_len);
if (!wl->fw) {
wl1251_error("could not allocate memory for the firmware");
@@ -211,9 +212,10 @@ out:
return ret;
}
+#define WL1251_IRQ_LOOP_COUNT 10
static void wl1251_irq_work(struct work_struct *work)
{
- u32 intr;
+ u32 intr, ctr = WL1251_IRQ_LOOP_COUNT;
struct wl1251 *wl =
container_of(work, struct wl1251, irq_work);
int ret;
@@ -234,78 +236,86 @@ static void wl1251_irq_work(struct work_struct *work)
intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR);
wl1251_debug(DEBUG_IRQ, "intr: 0x%x", intr);
- if (wl->data_path) {
- wl->rx_counter =
- wl1251_mem_read32(wl, wl->data_path->rx_control_addr);
-
- /* We handle a frmware bug here */
- switch ((wl->rx_counter - wl->rx_handled) & 0xf) {
- case 0:
- wl1251_debug(DEBUG_IRQ, "RX: FW and host in sync");
- intr &= ~WL1251_ACX_INTR_RX0_DATA;
- intr &= ~WL1251_ACX_INTR_RX1_DATA;
- break;
- case 1:
- wl1251_debug(DEBUG_IRQ, "RX: FW +1");
- intr |= WL1251_ACX_INTR_RX0_DATA;
- intr &= ~WL1251_ACX_INTR_RX1_DATA;
- break;
- case 2:
- wl1251_debug(DEBUG_IRQ, "RX: FW +2");
- intr |= WL1251_ACX_INTR_RX0_DATA;
- intr |= WL1251_ACX_INTR_RX1_DATA;
- break;
- default:
- wl1251_warning("RX: FW and host out of sync: %d",
- wl->rx_counter - wl->rx_handled);
- break;
- }
-
- wl->rx_handled = wl->rx_counter;
+ do {
+ if (wl->data_path) {
+ wl->rx_counter = wl1251_mem_read32(
+ wl, wl->data_path->rx_control_addr);
+
+ /* We handle a frmware bug here */
+ switch ((wl->rx_counter - wl->rx_handled) & 0xf) {
+ case 0:
+ wl1251_debug(DEBUG_IRQ,
+ "RX: FW and host in sync");
+ intr &= ~WL1251_ACX_INTR_RX0_DATA;
+ intr &= ~WL1251_ACX_INTR_RX1_DATA;
+ break;
+ case 1:
+ wl1251_debug(DEBUG_IRQ, "RX: FW +1");
+ intr |= WL1251_ACX_INTR_RX0_DATA;
+ intr &= ~WL1251_ACX_INTR_RX1_DATA;
+ break;
+ case 2:
+ wl1251_debug(DEBUG_IRQ, "RX: FW +2");
+ intr |= WL1251_ACX_INTR_RX0_DATA;
+ intr |= WL1251_ACX_INTR_RX1_DATA;
+ break;
+ default:
+ wl1251_warning(
+ "RX: FW and host out of sync: %d",
+ wl->rx_counter - wl->rx_handled);
+ break;
+ }
+ wl->rx_handled = wl->rx_counter;
- wl1251_debug(DEBUG_IRQ, "RX counter: %d", wl->rx_counter);
- }
+ wl1251_debug(DEBUG_IRQ, "RX counter: %d",
+ wl->rx_counter);
+ }
- intr &= wl->intr_mask;
+ intr &= wl->intr_mask;
- if (intr == 0) {
- wl1251_debug(DEBUG_IRQ, "INTR is 0");
- wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK,
- ~(wl->intr_mask));
+ if (intr == 0) {
+ wl1251_debug(DEBUG_IRQ, "INTR is 0");
+ goto out_sleep;
+ }
- goto out_sleep;
- }
+ if (intr & WL1251_ACX_INTR_RX0_DATA) {
+ wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA");
+ wl1251_rx(wl);
+ }
- if (intr & WL1251_ACX_INTR_RX0_DATA) {
- wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA");
- wl1251_rx(wl);
- }
+ if (intr & WL1251_ACX_INTR_RX1_DATA) {
+ wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA");
+ wl1251_rx(wl);
+ }
- if (intr & WL1251_ACX_INTR_RX1_DATA) {
- wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA");
- wl1251_rx(wl);
- }
+ if (intr & WL1251_ACX_INTR_TX_RESULT) {
+ wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT");
+ wl1251_tx_complete(wl);
+ }
- if (intr & WL1251_ACX_INTR_TX_RESULT) {
- wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT");
- wl1251_tx_complete(wl);
- }
+ if (intr & (WL1251_ACX_INTR_EVENT_A |
+ WL1251_ACX_INTR_EVENT_B)) {
+ wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)",
+ intr);
+ if (intr & WL1251_ACX_INTR_EVENT_A)
+ wl1251_event_handle(wl, 0);
+ else
+ wl1251_event_handle(wl, 1);
+ }
- if (intr & (WL1251_ACX_INTR_EVENT_A | WL1251_ACX_INTR_EVENT_B)) {
- wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)", intr);
- if (intr & WL1251_ACX_INTR_EVENT_A)
- wl1251_event_handle(wl, 0);
- else
- wl1251_event_handle(wl, 1);
- }
+ if (intr & WL1251_ACX_INTR_INIT_COMPLETE)
+ wl1251_debug(DEBUG_IRQ,
+ "WL1251_ACX_INTR_INIT_COMPLETE");
- if (intr & WL1251_ACX_INTR_INIT_COMPLETE)
- wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_INIT_COMPLETE");
+ if (--ctr == 0)
+ break;
- wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask));
+ intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR);
+ } while (intr);
out_sleep:
+ wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask));
wl1251_ps_elp_sleep(wl);
out:
@@ -509,6 +519,12 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw,
conf->type, conf->mac_addr);
mutex_lock(&wl->mutex);
+ if (wl->vif) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ wl->vif = conf->vif;
switch (conf->type) {
case NL80211_IFTYPE_STATION:
@@ -538,7 +554,12 @@ out:
static void wl1251_op_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf)
{
+ struct wl1251 *wl = hw->priv;
+
+ mutex_lock(&wl->mutex);
wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface");
+ wl->vif = NULL;
+ mutex_unlock(&wl->mutex);
}
static int wl1251_build_null_data(struct wl1251 *wl)
@@ -555,7 +576,8 @@ static int wl1251_build_null_data(struct wl1251 *wl)
memcpy(template.header.sa, wl->mac_addr, ETH_ALEN);
template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA |
- IEEE80211_STYPE_NULLFUNC);
+ IEEE80211_STYPE_NULLFUNC |
+ IEEE80211_FCTL_TODS);
return wl1251_cmd_template_set(wl, CMD_NULL_DATA, &template,
sizeof(template));
@@ -568,7 +590,10 @@ static int wl1251_build_ps_poll(struct wl1251 *wl, u16 aid)
memcpy(template.bssid, wl->bssid, ETH_ALEN);
memcpy(template.ta, wl->mac_addr, ETH_ALEN);
- template.aid = aid;
+
+ /* aid in PS-Poll has its two MSBs each set to 1 */
+ template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid);
+
template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);
return wl1251_cmd_template_set(wl, CMD_PS_POLL, &template,
@@ -1090,8 +1115,8 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
wl->beacon_int = bss_conf->beacon_int;
wl->dtim_period = bss_conf->dtim_period;
- /* FIXME: call join */
-
+ ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int,
+ wl->dtim_period);
wl->aid = bss_conf->aid;
ret = wl1251_build_ps_poll(wl, wl->aid);
@@ -1312,7 +1337,8 @@ int wl1251_init_ieee80211(struct wl1251 *wl)
wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
IEEE80211_HW_NOISE_DBM |
- IEEE80211_HW_SUPPORTS_PS;
+ IEEE80211_HW_SUPPORTS_PS |
+ IEEE80211_HW_BEACON_FILTER;
wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
wl->hw->wiphy->max_scan_ssids = 1;
@@ -1355,6 +1381,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
skb_queue_head_init(&wl->tx_queue);
INIT_WORK(&wl->filter_work, wl1251_filter_work);
+ INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work);
wl->channel = WL1251_DEFAULT_CHANNEL;
wl->scanning = false;
wl->default_key = 0;
@@ -1372,6 +1399,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
wl->power_level = WL1251_DEFAULT_POWER_LEVEL;
wl->beacon_int = WL1251_DEFAULT_BEACON_INT;
wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD;
+ wl->vif = NULL;
for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
wl->tx_frames[i] = NULL;
@@ -1413,7 +1441,7 @@ int wl1251_free_hw(struct wl1251 *wl)
kfree(wl->target_mem_map);
kfree(wl->data_path);
- kfree(wl->fw);
+ vfree(wl->fw);
wl->fw = NULL;
kfree(wl->nvs);
wl->nvs = NULL;
diff --git a/drivers/net/wireless/wl12xx/wl1251_ps.c b/drivers/net/wireless/wl12xx/wl1251_ps.c
index c53e28727ed..9931b197ff7 100644
--- a/drivers/net/wireless/wl12xx/wl1251_ps.c
+++ b/drivers/net/wireless/wl12xx/wl1251_ps.c
@@ -28,17 +28,41 @@
#define WL1251_WAKEUP_TIMEOUT 2000
-/* Routines to toggle sleep mode while in ELP */
-void wl1251_ps_elp_sleep(struct wl1251 *wl)
+void wl1251_elp_work(struct work_struct *work)
{
+ struct delayed_work *dwork;
+ struct wl1251 *wl;
+
+ dwork = container_of(work, struct delayed_work, work);
+ wl = container_of(dwork, struct wl1251, elp_work);
+
+ wl1251_debug(DEBUG_PSM, "elp work");
+
+ mutex_lock(&wl->mutex);
+
if (wl->elp || !wl->psm)
- return;
+ goto out;
wl1251_debug(DEBUG_PSM, "chip to elp");
-
wl1251_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
-
wl->elp = true;
+
+out:
+ mutex_unlock(&wl->mutex);
+}
+
+#define ELP_ENTRY_DELAY 5
+
+/* Routines to toggle sleep mode while in ELP */
+void wl1251_ps_elp_sleep(struct wl1251 *wl)
+{
+ unsigned long delay;
+
+ if (wl->psm) {
+ cancel_delayed_work(&wl->elp_work);
+ delay = msecs_to_jiffies(ELP_ENTRY_DELAY);
+ ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, delay);
+ }
}
int wl1251_ps_elp_wakeup(struct wl1251 *wl)
@@ -119,6 +143,11 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode)
case STATION_POWER_SAVE_MODE:
wl1251_debug(DEBUG_PSM, "entering psm");
+ /* enable beacon filtering */
+ ret = wl1251_acx_beacon_filter_opt(wl, true);
+ if (ret < 0)
+ return ret;
+
ret = wl1251_acx_wake_up_conditions(wl,
WAKE_UP_EVENT_DTIM_BITMAP,
wl->listen_int);
@@ -142,6 +171,11 @@ int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode)
if (ret < 0)
return ret;
+ /* disable beacon filtering */
+ ret = wl1251_acx_beacon_filter_opt(wl, false);
+ if (ret < 0)
+ return ret;
+
ret = wl1251_acx_wake_up_conditions(wl,
WAKE_UP_EVENT_DTIM_BITMAP,
wl->listen_int);
diff --git a/drivers/net/wireless/wl12xx/wl1251_ps.h b/drivers/net/wireless/wl12xx/wl1251_ps.h
index db036fe12f2..c688ac57aee 100644
--- a/drivers/net/wireless/wl12xx/wl1251_ps.h
+++ b/drivers/net/wireless/wl12xx/wl1251_ps.h
@@ -31,6 +31,7 @@
int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode);
void wl1251_ps_elp_sleep(struct wl1251 *wl);
int wl1251_ps_elp_wakeup(struct wl1251 *wl);
+void wl1251_elp_work(struct work_struct *work);
#endif /* __WL1251_PS_H__ */
diff --git a/drivers/net/wireless/wl12xx/wl1251_reg.h b/drivers/net/wireless/wl12xx/wl1251_reg.h
index 06e1bd94a73..0ca3b432605 100644
--- a/drivers/net/wireless/wl12xx/wl1251_reg.h
+++ b/drivers/net/wireless/wl12xx/wl1251_reg.h
@@ -370,6 +370,7 @@ enum wl12xx_acx_int_reg {
EEPROM location specified in the EE_ADDR register.
The Wlan hardware hardware clears this bit automatically.
*===============================================*/
+#define EE_CTL (REGISTERS_BASE + 0x2000)
#define ACX_EE_CTL_REG EE_CTL
#define EE_WRITE 0x00000001ul
#define EE_READ 0x00000002ul
@@ -380,6 +381,7 @@ enum wl12xx_acx_int_reg {
This register specifies the address
within the EEPROM from/to which to read/write data.
===============================================*/
+#define EE_ADDR (REGISTERS_BASE + 0x2008)
#define ACX_EE_ADDR_REG EE_ADDR
/*===============================================
@@ -389,8 +391,12 @@ enum wl12xx_acx_int_reg {
data from the EEPROM or the write data
to be written to the EEPROM.
===============================================*/
+#define EE_DATA (REGISTERS_BASE + 0x2004)
#define ACX_EE_DATA_REG EE_DATA
+#define EEPROM_ACCESS_TO 10000 /* timeout counter */
+#define START_EEPROM_MGR 0x00000001
+
/*===============================================
EEPROM Base Address - 32bit RW
------------------------------------------
diff --git a/drivers/net/wireless/wl12xx/wl1251_rx.c b/drivers/net/wireless/wl12xx/wl1251_rx.c
index 601fe0d6782..f84cc89cbff 100644
--- a/drivers/net/wireless/wl12xx/wl1251_rx.c
+++ b/drivers/net/wireless/wl12xx/wl1251_rx.c
@@ -72,10 +72,6 @@ static void wl1251_rx_status(struct wl1251 *wl,
}
status->signal = desc->rssi;
- status->qual = (desc->rssi - WL1251_RX_MIN_RSSI) * 100 /
- (WL1251_RX_MAX_RSSI - WL1251_RX_MIN_RSSI);
- status->qual = min(status->qual, 100);
- status->qual = max(status->qual, 0);
/*
* FIXME: guessing that snr needs to be divided by two, otherwise
diff --git a/drivers/net/wireless/wl12xx/wl1251_spi.c b/drivers/net/wireless/wl12xx/wl1251_spi.c
index 2cf8a2169d4..9cc8c323830 100644
--- a/drivers/net/wireless/wl12xx/wl1251_spi.c
+++ b/drivers/net/wireless/wl12xx/wl1251_spi.c
@@ -270,6 +270,8 @@ static int __devinit wl1251_spi_probe(struct spi_device *spi)
return -ENODEV;
}
+ wl->use_eeprom = pdata->use_eeprom;
+
ret = request_irq(wl->irq, wl1251_irq, 0, DRIVER_NAME, wl);
if (ret < 0) {
wl1251_error("request_irq() failed: %d", ret);