summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/wl12xx
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/wl12xx')
-rw-r--r--drivers/net/wireless/wl12xx/Makefile2
-rw-r--r--drivers/net/wireless/wl12xx/acx.c17
-rw-r--r--drivers/net/wireless/wl12xx/acx.h149
-rw-r--r--drivers/net/wireless/wl12xx/boot.c105
-rw-r--r--drivers/net/wireless/wl12xx/boot.h10
-rw-r--r--drivers/net/wireless/wl12xx/cmd.c169
-rw-r--r--drivers/net/wireless/wl12xx/cmd.h148
-rw-r--r--drivers/net/wireless/wl12xx/conf.h54
-rw-r--r--drivers/net/wireless/wl12xx/debug.h1
-rw-r--r--drivers/net/wireless/wl12xx/debugfs.c241
-rw-r--r--drivers/net/wireless/wl12xx/event.c154
-rw-r--r--drivers/net/wireless/wl12xx/event.h20
-rw-r--r--drivers/net/wireless/wl12xx/init.c55
-rw-r--r--drivers/net/wireless/wl12xx/io.c59
-rw-r--r--drivers/net/wireless/wl12xx/io.h2
-rw-r--r--drivers/net/wireless/wl12xx/main.c1115
-rw-r--r--drivers/net/wireless/wl12xx/ps.c38
-rw-r--r--drivers/net/wireless/wl12xx/ps.h2
-rw-r--r--drivers/net/wireless/wl12xx/reg.h27
-rw-r--r--drivers/net/wireless/wl12xx/rx.c2
-rw-r--r--drivers/net/wireless/wl12xx/scan.c62
-rw-r--r--drivers/net/wireless/wl12xx/scan.h2
-rw-r--r--drivers/net/wireless/wl12xx/sdio.c29
-rw-r--r--drivers/net/wireless/wl12xx/spi.c8
-rw-r--r--drivers/net/wireless/wl12xx/testmode.c50
-rw-r--r--drivers/net/wireless/wl12xx/tx.c116
-rw-r--r--drivers/net/wireless/wl12xx/tx.h6
-rw-r--r--drivers/net/wireless/wl12xx/wl12xx.h53
-rw-r--r--drivers/net/wireless/wl12xx/wl12xx_80211.h2
29 files changed, 1702 insertions, 996 deletions
diff --git a/drivers/net/wireless/wl12xx/Makefile b/drivers/net/wireless/wl12xx/Makefile
index fe67262ba19f..98f289c907a9 100644
--- a/drivers/net/wireless/wl12xx/Makefile
+++ b/drivers/net/wireless/wl12xx/Makefile
@@ -11,3 +11,5 @@ obj-$(CONFIG_WL12XX_SDIO) += wl12xx_sdio.o
# small builtin driver bit
obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx_platform_data.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c
index 7537c401a448..bc96db0683a5 100644
--- a/drivers/net/wireless/wl12xx/acx.c
+++ b/drivers/net/wireless/wl12xx/acx.c
@@ -34,12 +34,14 @@
#include "reg.h"
#include "ps.h"
-int wl1271_acx_wake_up_conditions(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+int wl1271_acx_wake_up_conditions(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ u8 wake_up_event, u8 listen_interval)
{
struct acx_wake_up_condition *wake_up;
int ret;
- wl1271_debug(DEBUG_ACX, "acx wake up conditions");
+ wl1271_debug(DEBUG_ACX, "acx wake up conditions (wake_up_event %d listen_interval %d)",
+ wake_up_event, listen_interval);
wake_up = kzalloc(sizeof(*wake_up), GFP_KERNEL);
if (!wake_up) {
@@ -48,8 +50,8 @@ int wl1271_acx_wake_up_conditions(struct wl1271 *wl, struct wl12xx_vif *wlvif)
}
wake_up->role_id = wlvif->role_id;
- wake_up->wake_up_event = wl->conf.conn.wake_up_event;
- wake_up->listen_interval = wl->conf.conn.listen_interval;
+ wake_up->wake_up_event = wake_up_event;
+ wake_up->listen_interval = listen_interval;
ret = wl1271_cmd_configure(wl, ACX_WAKE_UP_CONDITIONS,
wake_up, sizeof(*wake_up));
@@ -1459,9 +1461,10 @@ out:
return ret;
}
-int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime)
+int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ u64 *mactime)
{
- struct wl1271_acx_fw_tsf_information *tsf_info;
+ struct wl12xx_acx_fw_tsf_information *tsf_info;
int ret;
tsf_info = kzalloc(sizeof(*tsf_info), GFP_KERNEL);
@@ -1470,6 +1473,8 @@ int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime)
goto out;
}
+ tsf_info->role_id = wlvif->role_id;
+
ret = wl1271_cmd_interrogate(wl, ACX_TSF_INFO,
tsf_info, sizeof(*tsf_info));
if (ret < 0) {
diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h
index 69892b40c2df..a28fc044034c 100644
--- a/drivers/net/wireless/wl12xx/acx.h
+++ b/drivers/net/wireless/wl12xx/acx.h
@@ -995,15 +995,17 @@ struct wl1271_acx_ba_receiver_setup {
u8 padding[2];
} __packed;
-struct wl1271_acx_fw_tsf_information {
+struct wl12xx_acx_fw_tsf_information {
struct acx_header header;
+ u8 role_id;
+ u8 padding1[3];
__le32 current_tsf_high;
__le32 current_tsf_low;
__le32 last_bttt_high;
__le32 last_tbtt_low;
u8 last_dtim_count;
- u8 padding[3];
+ u8 padding2[3];
} __packed;
struct wl1271_acx_ps_rx_streaming {
@@ -1151,79 +1153,81 @@ struct wl12xx_acx_config_hangover {
} __packed;
enum {
- ACX_WAKE_UP_CONDITIONS = 0x0002,
- ACX_MEM_CFG = 0x0003,
- ACX_SLOT = 0x0004,
- ACX_AC_CFG = 0x0007,
- ACX_MEM_MAP = 0x0008,
- ACX_AID = 0x000A,
- ACX_MEDIUM_USAGE = 0x000F,
- ACX_TX_QUEUE_CFG = 0x0011, /* FIXME: only used by wl1251 */
- ACX_STATISTICS = 0x0013, /* Debug API */
- ACX_PWR_CONSUMPTION_STATISTICS = 0x0014,
- ACX_FEATURE_CFG = 0x0015,
- ACX_TID_CFG = 0x001A,
- ACX_PS_RX_STREAMING = 0x001B,
- ACX_BEACON_FILTER_OPT = 0x001F,
- ACX_NOISE_HIST = 0x0021,
- ACX_HDK_VERSION = 0x0022, /* ??? */
- ACX_PD_THRESHOLD = 0x0023,
- ACX_TX_CONFIG_OPT = 0x0024,
- ACX_CCA_THRESHOLD = 0x0025,
- ACX_EVENT_MBOX_MASK = 0x0026,
- ACX_CONN_MONIT_PARAMS = 0x002D,
- ACX_BCN_DTIM_OPTIONS = 0x0031,
- ACX_SG_ENABLE = 0x0032,
- ACX_SG_CFG = 0x0033,
- ACX_FM_COEX_CFG = 0x0034,
- ACX_BEACON_FILTER_TABLE = 0x0038,
- ACX_ARP_IP_FILTER = 0x0039,
- ACX_ROAMING_STATISTICS_TBL = 0x003B,
- ACX_RATE_POLICY = 0x003D,
- ACX_CTS_PROTECTION = 0x003E,
- ACX_SLEEP_AUTH = 0x003F,
- ACX_PREAMBLE_TYPE = 0x0040,
- ACX_ERROR_CNT = 0x0041,
- ACX_IBSS_FILTER = 0x0044,
- ACX_SERVICE_PERIOD_TIMEOUT = 0x0045,
- ACX_TSF_INFO = 0x0046,
- ACX_CONFIG_PS_WMM = 0x0049,
- ACX_ENABLE_RX_DATA_FILTER = 0x004A,
- ACX_SET_RX_DATA_FILTER = 0x004B,
- ACX_GET_DATA_FILTER_STATISTICS = 0x004C,
- ACX_RX_CONFIG_OPT = 0x004E,
- ACX_FRAG_CFG = 0x004F,
- ACX_BET_ENABLE = 0x0050,
- ACX_RSSI_SNR_TRIGGER = 0x0051,
- ACX_RSSI_SNR_WEIGHTS = 0x0052,
- ACX_KEEP_ALIVE_MODE = 0x0053,
- ACX_SET_KEEP_ALIVE_CONFIG = 0x0054,
- ACX_BA_SESSION_INIT_POLICY = 0x0055,
- ACX_BA_SESSION_RX_SETUP = 0x0056,
- ACX_PEER_HT_CAP = 0x0057,
- ACX_HT_BSS_OPERATION = 0x0058,
- ACX_COEX_ACTIVITY = 0x0059,
- ACX_BURST_MODE = 0x005C,
- ACX_SET_RATE_MGMT_PARAMS = 0x005D,
- ACX_SET_RATE_ADAPT_PARAMS = 0x0060,
- ACX_SET_DCO_ITRIM_PARAMS = 0x0061,
- ACX_GEN_FW_CMD = 0x0070,
- ACX_HOST_IF_CFG_BITMAP = 0x0071,
- ACX_MAX_TX_FAILURE = 0x0072,
- ACX_UPDATE_INCONNECTION_STA_LIST = 0x0073,
- DOT11_RX_MSDU_LIFE_TIME = 0x1004,
- DOT11_CUR_TX_PWR = 0x100D,
- DOT11_RX_DOT11_MODE = 0x1012,
- DOT11_RTS_THRESHOLD = 0x1013,
- DOT11_GROUP_ADDRESS_TBL = 0x1014,
- ACX_PM_CONFIG = 0x1016,
- ACX_CONFIG_PS = 0x1017,
- ACX_CONFIG_HANGOVER = 0x1018,
+ ACX_WAKE_UP_CONDITIONS = 0x0000,
+ ACX_MEM_CFG = 0x0001,
+ ACX_SLOT = 0x0002,
+ ACX_AC_CFG = 0x0003,
+ ACX_MEM_MAP = 0x0004,
+ ACX_AID = 0x0005,
+ ACX_MEDIUM_USAGE = 0x0006,
+ ACX_STATISTICS = 0x0007,
+ ACX_PWR_CONSUMPTION_STATISTICS = 0x0008,
+ ACX_TID_CFG = 0x0009,
+ ACX_PS_RX_STREAMING = 0x000A,
+ ACX_BEACON_FILTER_OPT = 0x000B,
+ ACX_NOISE_HIST = 0x000C,
+ ACX_HDK_VERSION = 0x000D,
+ ACX_PD_THRESHOLD = 0x000E,
+ ACX_TX_CONFIG_OPT = 0x000F,
+ ACX_CCA_THRESHOLD = 0x0010,
+ ACX_EVENT_MBOX_MASK = 0x0011,
+ ACX_CONN_MONIT_PARAMS = 0x0012,
+ ACX_DISABLE_BROADCASTS = 0x0013,
+ ACX_BCN_DTIM_OPTIONS = 0x0014,
+ ACX_SG_ENABLE = 0x0015,
+ ACX_SG_CFG = 0x0016,
+ ACX_FM_COEX_CFG = 0x0017,
+ ACX_BEACON_FILTER_TABLE = 0x0018,
+ ACX_ARP_IP_FILTER = 0x0019,
+ ACX_ROAMING_STATISTICS_TBL = 0x001A,
+ ACX_RATE_POLICY = 0x001B,
+ ACX_CTS_PROTECTION = 0x001C,
+ ACX_SLEEP_AUTH = 0x001D,
+ ACX_PREAMBLE_TYPE = 0x001E,
+ ACX_ERROR_CNT = 0x001F,
+ ACX_IBSS_FILTER = 0x0020,
+ ACX_SERVICE_PERIOD_TIMEOUT = 0x0021,
+ ACX_TSF_INFO = 0x0022,
+ ACX_CONFIG_PS_WMM = 0x0023,
+ ACX_ENABLE_RX_DATA_FILTER = 0x0024,
+ ACX_SET_RX_DATA_FILTER = 0x0025,
+ ACX_GET_DATA_FILTER_STATISTICS = 0x0026,
+ ACX_RX_CONFIG_OPT = 0x0027,
+ ACX_FRAG_CFG = 0x0028,
+ ACX_BET_ENABLE = 0x0029,
+ ACX_RSSI_SNR_TRIGGER = 0x002A,
+ ACX_RSSI_SNR_WEIGHTS = 0x002B,
+ ACX_KEEP_ALIVE_MODE = 0x002C,
+ ACX_SET_KEEP_ALIVE_CONFIG = 0x002D,
+ ACX_BA_SESSION_INIT_POLICY = 0x002E,
+ ACX_BA_SESSION_RX_SETUP = 0x002F,
+ ACX_PEER_HT_CAP = 0x0030,
+ ACX_HT_BSS_OPERATION = 0x0031,
+ ACX_COEX_ACTIVITY = 0x0032,
+ ACX_BURST_MODE = 0x0033,
+ ACX_SET_RATE_MGMT_PARAMS = 0x0034,
+ ACX_GET_RATE_MGMT_PARAMS = 0x0035,
+ ACX_SET_RATE_ADAPT_PARAMS = 0x0036,
+ ACX_SET_DCO_ITRIM_PARAMS = 0x0037,
+ ACX_GEN_FW_CMD = 0x0038,
+ ACX_HOST_IF_CFG_BITMAP = 0x0039,
+ ACX_MAX_TX_FAILURE = 0x003A,
+ ACX_UPDATE_INCONNECTION_STA_LIST = 0x003B,
+ DOT11_RX_MSDU_LIFE_TIME = 0x003C,
+ DOT11_CUR_TX_PWR = 0x003D,
+ DOT11_RTS_THRESHOLD = 0x003E,
+ DOT11_GROUP_ADDRESS_TBL = 0x003F,
+ ACX_PM_CONFIG = 0x0040,
+ ACX_CONFIG_PS = 0x0041,
+ ACX_CONFIG_HANGOVER = 0x0042,
+ ACX_FEATURE_CFG = 0x0043,
+ ACX_PROTECTION_CFG = 0x0044,
};
int wl1271_acx_wake_up_conditions(struct wl1271 *wl,
- struct wl12xx_vif *wlvif);
+ struct wl12xx_vif *wlvif,
+ u8 wake_up_event, u8 listen_interval);
int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth);
int wl1271_acx_tx_power(struct wl1271 *wl, struct wl12xx_vif *wlvif,
int power);
@@ -1296,7 +1300,8 @@ int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
u16 ssn, bool enable, u8 peer_hlid);
-int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
+int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ u64 *mactime);
int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable);
int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl, struct wl12xx_vif *wlvif);
diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c
index 8f9cf5a816ea..954101d03f06 100644
--- a/drivers/net/wireless/wl12xx/boot.c
+++ b/drivers/net/wireless/wl12xx/boot.c
@@ -33,65 +33,6 @@
#include "event.h"
#include "rx.h"
-static struct wl1271_partition_set part_table[PART_TABLE_LEN] = {
- [PART_DOWN] = {
- .mem = {
- .start = 0x00000000,
- .size = 0x000177c0
- },
- .reg = {
- .start = REGISTERS_BASE,
- .size = 0x00008800
- },
- .mem2 = {
- .start = 0x00000000,
- .size = 0x00000000
- },
- .mem3 = {
- .start = 0x00000000,
- .size = 0x00000000
- },
- },
-
- [PART_WORK] = {
- .mem = {
- .start = 0x00040000,
- .size = 0x00014fc0
- },
- .reg = {
- .start = REGISTERS_BASE,
- .size = 0x0000a000
- },
- .mem2 = {
- .start = 0x003004f8,
- .size = 0x00000004
- },
- .mem3 = {
- .start = 0x00040404,
- .size = 0x00000000
- },
- },
-
- [PART_DRPW] = {
- .mem = {
- .start = 0x00040000,
- .size = 0x00014fc0
- },
- .reg = {
- .start = DRPW_BASE,
- .size = 0x00006000
- },
- .mem2 = {
- .start = 0x00000000,
- .size = 0x00000000
- },
- .mem3 = {
- .start = 0x00000000,
- .size = 0x00000000
- }
- }
-};
-
static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag)
{
u32 cpu_ctrl;
@@ -181,13 +122,13 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
return -ENOMEM;
}
- memcpy(&partition, &part_table[PART_DOWN], sizeof(partition));
+ memcpy(&partition, &wl12xx_part_table[PART_DOWN], sizeof(partition));
partition.mem.start = dest;
wl1271_set_partition(wl, &partition);
/* 10.1 set partition limit and chunk num */
chunk_num = 0;
- partition_limit = part_table[PART_DOWN].mem.size;
+ partition_limit = wl12xx_part_table[PART_DOWN].mem.size;
while (chunk_num < fw_data_len / CHUNK_SIZE) {
/* 10.2 update partition, if needed */
@@ -195,7 +136,7 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
if (addr > partition_limit) {
addr = dest + chunk_num * CHUNK_SIZE;
partition_limit = chunk_num * CHUNK_SIZE +
- part_table[PART_DOWN].mem.size;
+ wl12xx_part_table[PART_DOWN].mem.size;
partition.mem.start = addr;
wl1271_set_partition(wl, &partition);
}
@@ -317,12 +258,12 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
}
/* update current MAC address to NVS */
- nvs_ptr[11] = wl->mac_addr[0];
- nvs_ptr[10] = wl->mac_addr[1];
- nvs_ptr[6] = wl->mac_addr[2];
- nvs_ptr[5] = wl->mac_addr[3];
- nvs_ptr[4] = wl->mac_addr[4];
- nvs_ptr[3] = wl->mac_addr[5];
+ nvs_ptr[11] = wl->addresses[0].addr[0];
+ nvs_ptr[10] = wl->addresses[0].addr[1];
+ nvs_ptr[6] = wl->addresses[0].addr[2];
+ nvs_ptr[5] = wl->addresses[0].addr[3];
+ nvs_ptr[4] = wl->addresses[0].addr[4];
+ nvs_ptr[3] = wl->addresses[0].addr[5];
/*
* Layout before the actual NVS tables:
@@ -383,7 +324,7 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
nvs_len -= nvs_ptr - (u8 *)wl->nvs;
/* Now we must set the partition correctly */
- wl1271_set_partition(wl, &part_table[PART_WORK]);
+ wl1271_set_partition(wl, &wl12xx_part_table[PART_WORK]);
/* Copy the NVS tables to a new block to ensure alignment */
nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL);
@@ -492,7 +433,7 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
wl->event_box_addr = wl1271_read32(wl, REG_EVENT_MAILBOX_PTR);
/* set the working partition to its "running" mode offset */
- wl1271_set_partition(wl, &part_table[PART_WORK]);
+ wl1271_set_partition(wl, &wl12xx_part_table[PART_WORK]);
wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x",
wl->cmd_box_addr, wl->event_box_addr);
@@ -507,8 +448,7 @@ 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 |
- DISCONNECT_EVENT_COMPLETE_ID |
+ ROLE_STOP_COMPLETE_EVENT_ID |
RSSI_SNR_TRIGGER_0_EVENT_ID |
PSPOLL_DELIVERY_FAILURE_EVENT_ID |
SOFT_GEMINI_SENSE_EVENT_ID |
@@ -547,19 +487,6 @@ static int wl1271_boot_write_irq_polarity(struct wl1271 *wl)
return 0;
}
-static void wl1271_boot_hw_version(struct wl1271 *wl)
-{
- u32 fuse;
-
- if (wl->chip.id == CHIP_ID_1283_PG20)
- fuse = wl1271_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1);
- else
- fuse = wl1271_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1);
- fuse = (fuse & PG_VER_MASK) >> PG_VER_OFFSET;
-
- wl->hw_pg_ver = (s8)fuse;
-}
-
static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl)
{
u16 spare_reg;
@@ -698,7 +625,7 @@ static int wl127x_boot_clk(struct wl1271 *wl)
u32 pause;
u32 clk;
- if (((wl->hw_pg_ver & PG_MAJOR_VER_MASK) >> PG_MAJOR_VER_OFFSET) < 3)
+ if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3)
wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION;
if (wl->ref_clock == CONF_REF_CLK_19_2_E ||
@@ -753,8 +680,6 @@ int wl1271_load_firmware(struct wl1271 *wl)
u32 tmp, clk;
int selected_clock = -1;
- wl1271_boot_hw_version(wl);
-
if (wl->chip.id == CHIP_ID_1283_PG20) {
ret = wl128x_boot_clk(wl, &selected_clock);
if (ret < 0)
@@ -769,7 +694,7 @@ int wl1271_load_firmware(struct wl1271 *wl)
wl1271_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL);
udelay(500);
- wl1271_set_partition(wl, &part_table[PART_DRPW]);
+ wl1271_set_partition(wl, &wl12xx_part_table[PART_DRPW]);
/* Read-modify-write DRPW_SCRATCH_START register (see next state)
to be used by DRPw FW. The RTRIM value will be added by the FW
@@ -788,7 +713,7 @@ int wl1271_load_firmware(struct wl1271 *wl)
wl1271_write32(wl, DRPW_SCRATCH_START, clk);
- wl1271_set_partition(wl, &part_table[PART_WORK]);
+ wl1271_set_partition(wl, &wl12xx_part_table[PART_WORK]);
/* Disable interrupts */
wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL);
diff --git a/drivers/net/wireless/wl12xx/boot.h b/drivers/net/wireless/wl12xx/boot.h
index 06dad9380fa7..c3adc09f403d 100644
--- a/drivers/net/wireless/wl12xx/boot.h
+++ b/drivers/net/wireless/wl12xx/boot.h
@@ -55,16 +55,6 @@ struct wl1271_static_data {
#define OCP_REG_CLK_POLARITY 0x0cb2
#define OCP_REG_CLK_PULL 0x0cb4
-#define WL127X_REG_FUSE_DATA_2_1 0x050a
-#define WL128X_REG_FUSE_DATA_2_1 0x2152
-#define PG_VER_MASK 0x3c
-#define PG_VER_OFFSET 2
-
-#define PG_MAJOR_VER_MASK 0x3
-#define PG_MAJOR_VER_OFFSET 0x0
-#define PG_MINOR_VER_MASK 0xc
-#define PG_MINOR_VER_OFFSET 0x2
-
#define CMD_MBOX_ADDRESS 0x407B4
#define POLARITY_LOW BIT(1)
diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c
index 25990bd38be6..3414fc11e9ba 100644
--- a/drivers/net/wireless/wl12xx/cmd.c
+++ b/drivers/net/wireless/wl12xx/cmd.c
@@ -459,23 +459,39 @@ out:
int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
{
+ unsigned long flags;
u8 link = find_first_zero_bit(wl->links_map, WL12XX_MAX_LINKS);
if (link >= WL12XX_MAX_LINKS)
return -EBUSY;
+ /* these bits are used by op_tx */
+ spin_lock_irqsave(&wl->wl_lock, flags);
__set_bit(link, wl->links_map);
__set_bit(link, wlvif->links_map);
+ spin_unlock_irqrestore(&wl->wl_lock, flags);
*hlid = link;
return 0;
}
void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
{
+ unsigned long flags;
+
if (*hlid == WL12XX_INVALID_LINK_ID)
return;
+ /* these bits are used by op_tx */
+ spin_lock_irqsave(&wl->wl_lock, flags);
__clear_bit(*hlid, wl->links_map);
__clear_bit(*hlid, wlvif->links_map);
+ spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+ /*
+ * At this point op_tx() will not add more packets to the queues. We
+ * can purge them.
+ */
+ wl1271_tx_reset_link_queues(wl, *hlid);
+
*hlid = WL12XX_INVALID_LINK_ID;
}
@@ -515,7 +531,7 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
goto out_free;
}
cmd->device.hlid = wlvif->dev_hlid;
- cmd->device.session = wlvif->session_counter;
+ cmd->device.session = wl12xx_get_new_session_id(wl, wlvif);
wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d",
cmd->role_id, cmd->device.hlid, cmd->device.session);
@@ -566,7 +582,7 @@ static int wl12xx_cmd_role_stop_dev(struct wl1271 *wl,
goto out_free;
}
- ret = wl1271_cmd_wait_for_event(wl, DISCONNECT_EVENT_COMPLETE_ID);
+ ret = wl1271_cmd_wait_for_event(wl, ROLE_STOP_COMPLETE_EVENT_ID);
if (ret < 0) {
wl1271_error("cmd role stop dev event completion error");
goto out_free;
@@ -715,6 +731,8 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
cmd->ap.beacon_interval = cpu_to_le16(wlvif->beacon_int);
cmd->ap.dtim_interval = bss_conf->dtim_period;
cmd->ap.beacon_expiry = WL1271_AP_DEF_BEACON_EXP;
+ /* FIXME: Change when adding DFS */
+ cmd->ap.reset_tsf = 1; /* By default reset AP TSF */
cmd->channel = wlvif->channel;
if (!bss_conf->hidden_ssid) {
@@ -994,7 +1012,7 @@ out:
}
int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
- u8 ps_mode)
+ u8 ps_mode, u16 auto_ps_timeout)
{
struct wl1271_cmd_ps_params *ps_params = NULL;
int ret = 0;
@@ -1009,6 +1027,7 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
ps_params->role_id = wlvif->role_id;
ps_params->ps_mode = ps_mode;
+ ps_params->auto_ps_timeout = auto_ps_timeout;
ret = wl1271_cmd_send(wl, CMD_SET_PS_MODE, ps_params,
sizeof(*ps_params), 0);
@@ -1022,13 +1041,15 @@ out:
return ret;
}
-int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
- void *buf, size_t buf_len, int index, u32 rates)
+int wl1271_cmd_template_set(struct wl1271 *wl, u8 role_id,
+ u16 template_id, void *buf, size_t buf_len,
+ int index, u32 rates)
{
struct wl1271_cmd_template_set *cmd;
int ret = 0;
- wl1271_debug(DEBUG_CMD, "cmd template_set %d", template_id);
+ wl1271_debug(DEBUG_CMD, "cmd template_set %d (role %d)",
+ template_id, role_id);
WARN_ON(buf_len > WL1271_CMD_TEMPL_MAX_SIZE);
buf_len = min_t(size_t, buf_len, WL1271_CMD_TEMPL_MAX_SIZE);
@@ -1039,6 +1060,8 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
goto out;
}
+ /* during initialization wlvif is NULL */
+ cmd->role_id = role_id;
cmd->len = cpu_to_le16(buf_len);
cmd->template_type = template_id;
cmd->enabled_rates = cpu_to_le32(rates);
@@ -1082,7 +1105,8 @@ int wl12xx_cmd_build_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif)
ptr = skb->data;
}
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size, 0,
+ ret = wl1271_cmd_template_set(wl, wlvif->role_id,
+ CMD_TEMPL_NULL_DATA, ptr, size, 0,
wlvif->basic_rate);
out:
@@ -1105,7 +1129,7 @@ int wl12xx_cmd_build_klv_null_data(struct wl1271 *wl,
if (!skb)
goto out;
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV,
+ ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_KLV,
skb->data, skb->len,
CMD_TEMPL_KLV_IDX_NULL_DATA,
wlvif->basic_rate);
@@ -1130,7 +1154,8 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif,
if (!skb)
goto out;
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data,
+ ret = wl1271_cmd_template_set(wl, wlvif->role_id,
+ CMD_TEMPL_PS_POLL, skb->data,
skb->len, 0, wlvif->basic_rate_set);
out:
@@ -1138,9 +1163,10 @@ out:
return ret;
}
-int wl1271_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ u8 role_id, u8 band,
const u8 *ssid, size_t ssid_len,
- const u8 *ie, size_t ie_len, u8 band)
+ const u8 *ie, size_t ie_len)
{
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct sk_buff *skb;
@@ -1158,10 +1184,12 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
if (band == IEEE80211_BAND_2GHZ)
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
+ ret = wl1271_cmd_template_set(wl, role_id,
+ CMD_TEMPL_CFG_PROBE_REQ_2_4,
skb->data, skb->len, 0, rate);
else
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5,
+ ret = wl1271_cmd_template_set(wl, role_id,
+ CMD_TEMPL_CFG_PROBE_REQ_5,
skb->data, skb->len, 0, rate);
out:
@@ -1186,10 +1214,12 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[wlvif->band]);
if (wlvif->band == IEEE80211_BAND_2GHZ)
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
+ ret = wl1271_cmd_template_set(wl, wlvif->role_id,
+ CMD_TEMPL_CFG_PROBE_REQ_2_4,
skb->data, skb->len, 0, rate);
else
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5,
+ ret = wl1271_cmd_template_set(wl, wlvif->role_id,
+ CMD_TEMPL_CFG_PROBE_REQ_5,
skb->data, skb->len, 0, rate);
if (ret < 0)
@@ -1199,32 +1229,34 @@ out:
return skb;
}
-int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif,
- __be32 ip_addr)
+int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
- int ret;
+ int ret, extra;
+ u16 fc;
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
- struct wl12xx_arp_rsp_template tmpl;
+ struct sk_buff *skb;
+ struct wl12xx_arp_rsp_template *tmpl;
struct ieee80211_hdr_3addr *hdr;
struct arphdr *arp_hdr;
- memset(&tmpl, 0, sizeof(tmpl));
+ skb = dev_alloc_skb(sizeof(*hdr) + sizeof(__le16) + sizeof(*tmpl) +
+ WL1271_EXTRA_SPACE_MAX);
+ if (!skb) {
+ wl1271_error("failed to allocate buffer for arp rsp template");
+ return -ENOMEM;
+ }
- /* mac80211 header */
- hdr = &tmpl.hdr;
- hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
- IEEE80211_STYPE_DATA |
- IEEE80211_FCTL_TODS);
- memcpy(hdr->addr1, vif->bss_conf.bssid, ETH_ALEN);
- memcpy(hdr->addr2, vif->addr, ETH_ALEN);
- memset(hdr->addr3, 0xff, ETH_ALEN);
+ skb_reserve(skb, sizeof(*hdr) + WL1271_EXTRA_SPACE_MAX);
+
+ tmpl = (struct wl12xx_arp_rsp_template *)skb_put(skb, sizeof(*tmpl));
+ memset(tmpl, 0, sizeof(tmpl));
/* llc layer */
- memcpy(tmpl.llc_hdr, rfc1042_header, sizeof(rfc1042_header));
- tmpl.llc_type = cpu_to_be16(ETH_P_ARP);
+ memcpy(tmpl->llc_hdr, rfc1042_header, sizeof(rfc1042_header));
+ tmpl->llc_type = cpu_to_be16(ETH_P_ARP);
/* arp header */
- arp_hdr = &tmpl.arp_hdr;
+ arp_hdr = &tmpl->arp_hdr;
arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER);
arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP);
arp_hdr->ar_hln = ETH_ALEN;
@@ -1232,13 +1264,59 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif,
arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY);
/* arp payload */
- memcpy(tmpl.sender_hw, vif->addr, ETH_ALEN);
- tmpl.sender_ip = ip_addr;
+ memcpy(tmpl->sender_hw, vif->addr, ETH_ALEN);
+ tmpl->sender_ip = wlvif->ip_addr;
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_ARP_RSP,
- &tmpl, sizeof(tmpl), 0,
- wlvif->basic_rate);
+ /* encryption space */
+ switch (wlvif->encryption_type) {
+ case KEY_TKIP:
+ extra = WL1271_EXTRA_SPACE_TKIP;
+ break;
+ case KEY_AES:
+ extra = WL1271_EXTRA_SPACE_AES;
+ break;
+ case KEY_NONE:
+ case KEY_WEP:
+ case KEY_GEM:
+ extra = 0;
+ break;
+ default:
+ wl1271_warning("Unknown encryption type: %d",
+ wlvif->encryption_type);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (extra) {
+ u8 *space = skb_push(skb, extra);
+ memset(space, 0, extra);
+ }
+
+ /* QoS header - BE */
+ if (wlvif->sta.qos)
+ memset(skb_push(skb, sizeof(__le16)), 0, sizeof(__le16));
+ /* mac80211 header */
+ hdr = (struct ieee80211_hdr_3addr *)skb_push(skb, sizeof(*hdr));
+ memset(hdr, 0, sizeof(hdr));
+ fc = IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS;
+ if (wlvif->sta.qos)
+ fc |= IEEE80211_STYPE_QOS_DATA;
+ else
+ fc |= IEEE80211_STYPE_DATA;
+ if (wlvif->encryption_type != KEY_NONE)
+ fc |= IEEE80211_FCTL_PROTECTED;
+
+ hdr->frame_control = cpu_to_le16(fc);
+ memcpy(hdr->addr1, vif->bss_conf.bssid, ETH_ALEN);
+ memcpy(hdr->addr2, vif->addr, ETH_ALEN);
+ memset(hdr->addr3, 0xff, ETH_ALEN);
+
+ ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_ARP_RSP,
+ skb->data, skb->len, 0,
+ wlvif->basic_rate);
+out:
+ dev_kfree_skb(skb);
return ret;
}
@@ -1260,7 +1338,8 @@ int wl1271_build_qos_null_data(struct wl1271 *wl, struct ieee80211_vif *vif)
/* FIXME: not sure what priority to use here */
template.qos_ctrl = cpu_to_le16(0);
- return wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, &template,
+ return wl1271_cmd_template_set(wl, wlvif->role_id,
+ CMD_TEMPL_QOS_NULL_DATA, &template,
sizeof(template), 0,
wlvif->basic_rate);
}
@@ -1739,11 +1818,20 @@ int wl12xx_croc(struct wl1271 *wl, u8 role_id)
goto out;
__clear_bit(role_id, wl->roc_map);
+
+ /*
+ * Rearm the tx watchdog when removing the last ROC. This prevents
+ * recoveries due to just finished ROCs - when Tx hasn't yet had
+ * a chance to get out.
+ */
+ if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES)
+ wl12xx_rearm_tx_watchdog_locked(wl);
out:
return ret;
}
int wl12xx_cmd_channel_switch(struct wl1271 *wl,
+ struct wl12xx_vif *wlvif,
struct ieee80211_channel_switch *ch_switch)
{
struct wl12xx_cmd_channel_switch *cmd;
@@ -1757,10 +1845,13 @@ int wl12xx_cmd_channel_switch(struct wl1271 *wl,
goto out;
}
+ cmd->role_id = wlvif->role_id;
cmd->channel = ch_switch->channel->hw_value;
cmd->switch_time = ch_switch->count;
- cmd->tx_suspend = ch_switch->block_tx;
- cmd->flush = 0; /* this value is ignored by the FW */
+ cmd->stop_tx = ch_switch->block_tx;
+
+ /* FIXME: control from mac80211 in the future */
+ cmd->post_switch_tx_disable = 0; /* Enable TX on the target channel */
ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
if (ret < 0) {
diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h
index 3f7d0b93c24d..de217d92516b 100644
--- a/drivers/net/wireless/wl12xx/cmd.h
+++ b/drivers/net/wireless/wl12xx/cmd.h
@@ -51,22 +51,23 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len);
int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len);
int wl1271_cmd_data_path(struct wl1271 *wl, bool enable);
int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
- u8 ps_mode);
+ u8 ps_mode, u16 auto_ps_timeout);
int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
size_t len);
-int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
- void *buf, size_t buf_len, int index, u32 rates);
+int wl1271_cmd_template_set(struct wl1271 *wl, u8 role_id,
+ u16 template_id, void *buf, size_t buf_len,
+ int index, u32 rates);
int wl12xx_cmd_build_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u16 aid);
-int wl1271_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ u8 role_id, u8 band,
const u8 *ssid, size_t ssid_len,
- const u8 *ie, size_t ie_len, u8 band);
+ const u8 *ie, size_t ie_len);
struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct sk_buff *skb);
-int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif,
- __be32 ip_addr);
+int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl1271_build_qos_null_data(struct wl1271 *wl, struct ieee80211_vif *vif);
int wl12xx_cmd_build_klv_null_data(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
@@ -89,6 +90,7 @@ int wl12xx_cmd_config_fwlog(struct wl1271 *wl);
int wl12xx_cmd_start_fwlog(struct wl1271 *wl);
int wl12xx_cmd_stop_fwlog(struct wl1271 *wl);
int wl12xx_cmd_channel_switch(struct wl1271 *wl,
+ struct wl12xx_vif *wlvif,
struct ieee80211_channel_switch *ch_switch);
int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl);
int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif,
@@ -96,62 +98,65 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif,
void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid);
enum wl1271_commands {
- CMD_INTERROGATE = 1, /*use this to read information elements*/
- CMD_CONFIGURE = 2, /*use this to write information elements*/
- CMD_ENABLE_RX = 3,
- CMD_ENABLE_TX = 4,
- CMD_DISABLE_RX = 5,
- CMD_DISABLE_TX = 6,
- CMD_SCAN = 8,
- CMD_STOP_SCAN = 9,
- CMD_SET_KEYS = 12,
- CMD_READ_MEMORY = 13,
- CMD_WRITE_MEMORY = 14,
- CMD_SET_TEMPLATE = 19,
- CMD_TEST = 23,
- CMD_NOISE_HIST = 28,
- CMD_QUIET_ELEMENT_SET_STATE = 29,
- CMD_SET_BCN_MODE = 33,
- CMD_MEASUREMENT = 34,
- CMD_STOP_MEASUREMENT = 35,
- CMD_SET_PS_MODE = 37,
- CMD_CHANNEL_SWITCH = 38,
- CMD_STOP_CHANNEL_SWICTH = 39,
- CMD_AP_DISCOVERY = 40,
- CMD_STOP_AP_DISCOVERY = 41,
- CMD_HEALTH_CHECK = 45,
- CMD_DEBUG = 46,
- CMD_TRIGGER_SCAN_TO = 47,
- CMD_CONNECTION_SCAN_CFG = 48,
- CMD_CONNECTION_SCAN_SSID_CFG = 49,
- CMD_START_PERIODIC_SCAN = 50,
- CMD_STOP_PERIODIC_SCAN = 51,
- CMD_SET_PEER_STATE = 52,
- CMD_REMAIN_ON_CHANNEL = 53,
- CMD_CANCEL_REMAIN_ON_CHANNEL = 54,
-
- CMD_CONFIG_FWLOGGER = 55,
- CMD_START_FWLOGGER = 56,
- CMD_STOP_FWLOGGER = 57,
-
- /* AP commands */
- CMD_ADD_PEER = 62,
- CMD_REMOVE_PEER = 63,
+ CMD_INTERROGATE = 1, /* use this to read information elements */
+ CMD_CONFIGURE = 2, /* use this to write information elements */
+ CMD_ENABLE_RX = 3,
+ CMD_ENABLE_TX = 4,
+ CMD_DISABLE_RX = 5,
+ CMD_DISABLE_TX = 6,
+ CMD_SCAN = 7,
+ CMD_STOP_SCAN = 8,
+ CMD_SET_KEYS = 9,
+ CMD_READ_MEMORY = 10,
+ CMD_WRITE_MEMORY = 11,
+ CMD_SET_TEMPLATE = 12,
+ CMD_TEST = 13,
+ CMD_NOISE_HIST = 14,
+ CMD_QUIET_ELEMENT_SET_STATE = 15,
+ CMD_SET_BCN_MODE = 16,
+
+ CMD_MEASUREMENT = 17,
+ CMD_STOP_MEASUREMENT = 18,
+ CMD_SET_PS_MODE = 19,
+ CMD_CHANNEL_SWITCH = 20,
+ CMD_STOP_CHANNEL_SWICTH = 21,
+ CMD_AP_DISCOVERY = 22,
+ CMD_STOP_AP_DISCOVERY = 23,
+ CMD_HEALTH_CHECK = 24,
+ CMD_DEBUG = 25,
+ CMD_TRIGGER_SCAN_TO = 26,
+ CMD_CONNECTION_SCAN_CFG = 27,
+ CMD_CONNECTION_SCAN_SSID_CFG = 28,
+ CMD_START_PERIODIC_SCAN = 29,
+ CMD_STOP_PERIODIC_SCAN = 30,
+ CMD_SET_PEER_STATE = 31,
+ CMD_REMAIN_ON_CHANNEL = 32,
+ CMD_CANCEL_REMAIN_ON_CHANNEL = 33,
+ CMD_CONFIG_FWLOGGER = 34,
+ CMD_START_FWLOGGER = 35,
+ CMD_STOP_FWLOGGER = 36,
+
+ /* Access point commands */
+ CMD_ADD_PEER = 37,
+ CMD_REMOVE_PEER = 38,
/* Role API */
- CMD_ROLE_ENABLE = 70,
- CMD_ROLE_DISABLE = 71,
- CMD_ROLE_START = 72,
- CMD_ROLE_STOP = 73,
+ CMD_ROLE_ENABLE = 39,
+ CMD_ROLE_DISABLE = 40,
+ CMD_ROLE_START = 41,
+ CMD_ROLE_STOP = 42,
- /* WIFI Direct */
- CMD_WFD_START_DISCOVERY = 80,
- CMD_WFD_STOP_DISCOVERY = 81,
- CMD_WFD_ATTRIBUTE_CONFIG = 82,
+ /* DFS */
+ CMD_START_RADAR_DETECTION = 43,
+ CMD_STOP_RADAR_DETECTION = 44,
- CMD_NOP = 100,
+ /* WIFI Direct */
+ CMD_WFD_START_DISCOVERY = 45,
+ CMD_WFD_STOP_DISCOVERY = 46,
+ CMD_WFD_ATTRIBUTE_CONFIG = 47,
+ CMD_NOP = 48,
+ CMD_LAST_COMMAND,
- NUM_COMMANDS,
MAX_COMMAND_ID = 0xFFFF,
};
@@ -191,7 +196,7 @@ enum cmd_templ {
/* unit ms */
#define WL1271_COMMAND_TIMEOUT 2000
#define WL1271_CMD_TEMPL_DFLT_SIZE 252
-#define WL1271_CMD_TEMPL_MAX_SIZE 548
+#define WL1271_CMD_TEMPL_MAX_SIZE 512
#define WL1271_EVENT_TIMEOUT 750
struct wl1271_cmd_header {
@@ -339,7 +344,9 @@ struct wl12xx_cmd_role_start {
u8 ssid_len;
u8 ssid[IEEE80211_MAX_SSID_LEN];
- u8 padding_1[5];
+ u8 reset_tsf;
+
+ u8 padding_1[4];
} __packed ap;
};
} __packed;
@@ -364,14 +371,18 @@ struct cmd_enabledisable_path {
struct wl1271_cmd_template_set {
struct wl1271_cmd_header header;
- __le16 len;
+ u8 role_id;
u8 template_type;
+ __le16 len;
u8 index; /* relevant only for KLV_TEMPLATE type */
+ u8 padding[3];
+
__le32 enabled_rates;
u8 short_retry_limit;
u8 long_retry_limit;
u8 aflags;
u8 reserved;
+
u8 template_data[WL1271_CMD_TEMPL_MAX_SIZE];
} __packed;
@@ -388,6 +399,7 @@ struct wl1271_tim {
} __packed;
enum wl1271_cmd_ps_mode {
+ STATION_AUTO_PS_MODE, /* Dynamic Power Save */
STATION_ACTIVE_MODE,
STATION_POWER_SAVE_MODE
};
@@ -397,7 +409,7 @@ struct wl1271_cmd_ps_params {
u8 role_id;
u8 ps_mode; /* STATION_* */
- u8 padding[2];
+ u16 auto_ps_timeout;
} __packed;
/* HW encryption keys */
@@ -695,14 +707,18 @@ struct wl12xx_cmd_stop_fwlog {
struct wl12xx_cmd_channel_switch {
struct wl1271_cmd_header header;
+ u8 role_id;
+
/* The new serving channel */
u8 channel;
/* Relative time of the serving channel switch in TBTT units */
u8 switch_time;
- /* 1: Suspend TX till switch time; 0: Do not suspend TX */
- u8 tx_suspend;
- /* 1: Flush TX at switch time; 0: Do not flush */
- u8 flush;
+ /* Stop the role TX, should expect it after radar detection */
+ u8 stop_tx;
+ /* The target channel tx status 1-stopped 0-open*/
+ u8 post_switch_tx_disable;
+
+ u8 padding[3];
} __packed;
struct wl12xx_cmd_stop_channel_switch {
diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h
index 1bcfb017058d..3e581e19424c 100644
--- a/drivers/net/wireless/wl12xx/conf.h
+++ b/drivers/net/wireless/wl12xx/conf.h
@@ -66,7 +66,8 @@ enum {
};
enum {
- CONF_HW_RXTX_RATE_MCS7 = 0,
+ CONF_HW_RXTX_RATE_MCS7_SGI = 0,
+ CONF_HW_RXTX_RATE_MCS7,
CONF_HW_RXTX_RATE_MCS6,
CONF_HW_RXTX_RATE_MCS5,
CONF_HW_RXTX_RATE_MCS4,
@@ -91,6 +92,10 @@ enum {
CONF_HW_RXTX_RATE_UNSUPPORTED = 0xff
};
+/* Rates between and including these are MCS rates */
+#define CONF_HW_RXTX_RATE_MCS_MIN CONF_HW_RXTX_RATE_MCS7_SGI
+#define CONF_HW_RXTX_RATE_MCS_MAX CONF_HW_RXTX_RATE_MCS0
+
enum {
CONF_SG_DISABLE = 0,
CONF_SG_PROTECTIVE,
@@ -312,6 +317,10 @@ enum {
CONF_AP_BT_ACL_VAL_BT_SERVE_TIME,
CONF_AP_BT_ACL_VAL_WL_SERVE_TIME,
+ /* CTS Diluting params */
+ CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH,
+ CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER,
+
CONF_SG_TEMP_PARAM_1,
CONF_SG_TEMP_PARAM_2,
CONF_SG_TEMP_PARAM_3,
@@ -681,6 +690,9 @@ struct conf_tx_settings {
*/
u8 tmpl_short_retry_limit;
u8 tmpl_long_retry_limit;
+
+ /* Time in ms for Tx watchdog timer to expire */
+ u32 tx_watchdog_timeout;
};
enum {
@@ -810,6 +822,19 @@ struct conf_conn_settings {
u8 listen_interval;
/*
+ * Firmware wakeup conditions during suspend
+ * Range: CONF_WAKE_UP_EVENT_*
+ */
+ u8 suspend_wake_up_event;
+
+ /*
+ * Listen interval during suspend.
+ * Currently will be in DTIMs (1-10)
+ *
+ */
+ u8 suspend_listen_interval;
+
+ /*
* Enable or disable the beacon filtering.
*
* Range: CONF_BCN_FILT_MODE_*
@@ -868,13 +893,6 @@ struct conf_conn_settings {
u8 ps_poll_threshold;
/*
- * PS Poll failure recovery ACTIVE period length
- *
- * Range: u32 (ms)
- */
- u32 ps_poll_recovery_period;
-
- /*
* Configuration of signal average weights.
*/
struct conf_sig_weights sig_weights;
@@ -922,6 +940,18 @@ struct conf_conn_settings {
u8 psm_entry_nullfunc_retries;
/*
+ * Specifies the dynamic PS timeout in ms that will be used
+ * by the FW when in AUTO_PS mode
+ */
+ u16 dynamic_ps_timeout;
+
+ /*
+ * Specifies whether dynamic PS should be disabled and PSM forced.
+ * This is required for certain WiFi certification tests.
+ */
+ u8 forced_ps;
+
+ /*
*
* Specifies the interval of the connection keep-alive null-func
* frame in ms.
@@ -1055,6 +1085,14 @@ struct conf_scan_settings {
*/
u16 num_probe_reqs;
+ /*
+ * Scan trigger (split scan) timeout. The FW will split the scan
+ * operation into slices of the given time and allow the FW to schedule
+ * other tasks in between.
+ *
+ * Range: u32 Microsecs
+ */
+ u32 split_scan_timeout;
};
struct conf_sched_scan_settings {
diff --git a/drivers/net/wireless/wl12xx/debug.h b/drivers/net/wireless/wl12xx/debug.h
index b85fd8c41e8f..ec0fdc25b280 100644
--- a/drivers/net/wireless/wl12xx/debug.h
+++ b/drivers/net/wireless/wl12xx/debug.h
@@ -51,6 +51,7 @@ enum {
DEBUG_FILTERS = BIT(15),
DEBUG_ADHOC = BIT(16),
DEBUG_AP = BIT(17),
+ DEBUG_PROBE = BIT(18),
DEBUG_MASTER = (DEBUG_ADHOC | DEBUG_AP),
DEBUG_ALL = ~0,
};
diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c
index 15eb3a9c30ca..e1cf72765965 100644
--- a/drivers/net/wireless/wl12xx/debugfs.c
+++ b/drivers/net/wireless/wl12xx/debugfs.c
@@ -113,7 +113,7 @@ static void wl1271_debugfs_update_stats(struct wl1271 *wl)
if (ret < 0)
goto out;
- if (wl->state == WL1271_STATE_ON &&
+ if (wl->state == WL1271_STATE_ON && !wl->plt &&
time_after(jiffies, wl->stats.fw_stats_update +
msecs_to_jiffies(WL1271_DEBUGFS_STATS_LIFETIME))) {
wl1271_acx_statistics(wl, wl->stats.fw_stats);
@@ -312,6 +312,181 @@ static const struct file_operations start_recovery_ops = {
.llseek = default_llseek,
};
+static ssize_t dynamic_ps_timeout_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wl1271 *wl = file->private_data;
+
+ return wl1271_format_buffer(user_buf, count,
+ ppos, "%d\n",
+ wl->conf.conn.dynamic_ps_timeout);
+}
+
+static ssize_t dynamic_ps_timeout_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wl1271 *wl = file->private_data;
+ struct wl12xx_vif *wlvif;
+ unsigned long value;
+ int ret;
+
+ ret = kstrtoul_from_user(user_buf, count, 10, &value);
+ if (ret < 0) {
+ wl1271_warning("illegal value in dynamic_ps");
+ return -EINVAL;
+ }
+
+ if (value < 1 || value > 65535) {
+ wl1271_warning("dyanmic_ps_timeout is not in valid range");
+ return -ERANGE;
+ }
+
+ mutex_lock(&wl->mutex);
+
+ wl->conf.conn.dynamic_ps_timeout = value;
+
+ if (wl->state == WL1271_STATE_OFF)
+ goto out;
+
+ ret = wl1271_ps_elp_wakeup(wl);
+ if (ret < 0)
+ goto out;
+
+ /* In case we're already in PSM, trigger it again to set new timeout
+ * immediately without waiting for re-association
+ */
+
+ wl12xx_for_each_wlvif_sta(wl, wlvif) {
+ if (test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags))
+ wl1271_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE);
+ }
+
+ wl1271_ps_elp_sleep(wl);
+
+out:
+ mutex_unlock(&wl->mutex);
+ return count;
+}
+
+static const struct file_operations dynamic_ps_timeout_ops = {
+ .read = dynamic_ps_timeout_read,
+ .write = dynamic_ps_timeout_write,
+ .open = wl1271_open_file_generic,
+ .llseek = default_llseek,
+};
+
+static ssize_t forced_ps_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wl1271 *wl = file->private_data;
+
+ return wl1271_format_buffer(user_buf, count,
+ ppos, "%d\n",
+ wl->conf.conn.forced_ps);
+}
+
+static ssize_t forced_ps_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wl1271 *wl = file->private_data;
+ struct wl12xx_vif *wlvif;
+ unsigned long value;
+ int ret, ps_mode;
+
+ ret = kstrtoul_from_user(user_buf, count, 10, &value);
+ if (ret < 0) {
+ wl1271_warning("illegal value in forced_ps");
+ return -EINVAL;
+ }
+
+ if (value != 1 && value != 0) {
+ wl1271_warning("forced_ps should be either 0 or 1");
+ return -ERANGE;
+ }
+
+ mutex_lock(&wl->mutex);
+
+ if (wl->conf.conn.forced_ps == value)
+ goto out;
+
+ wl->conf.conn.forced_ps = value;
+
+ if (wl->state == WL1271_STATE_OFF)
+ goto out;
+
+ ret = wl1271_ps_elp_wakeup(wl);
+ if (ret < 0)
+ goto out;
+
+ /* In case we're already in PSM, trigger it again to switch mode
+ * immediately without waiting for re-association
+ */
+
+ ps_mode = value ? STATION_POWER_SAVE_MODE : STATION_AUTO_PS_MODE;
+
+ wl12xx_for_each_wlvif_sta(wl, wlvif) {
+ if (test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags))
+ wl1271_ps_set_mode(wl, wlvif, ps_mode);
+ }
+
+ wl1271_ps_elp_sleep(wl);
+
+out:
+ mutex_unlock(&wl->mutex);
+ return count;
+}
+
+static const struct file_operations forced_ps_ops = {
+ .read = forced_ps_read,
+ .write = forced_ps_write,
+ .open = wl1271_open_file_generic,
+ .llseek = default_llseek,
+};
+
+static ssize_t split_scan_timeout_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wl1271 *wl = file->private_data;
+
+ return wl1271_format_buffer(user_buf, count,
+ ppos, "%d\n",
+ wl->conf.scan.split_scan_timeout / 1000);
+}
+
+static ssize_t split_scan_timeout_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wl1271 *wl = file->private_data;
+ unsigned long value;
+ int ret;
+
+ ret = kstrtoul_from_user(user_buf, count, 10, &value);
+ if (ret < 0) {
+ wl1271_warning("illegal value in split_scan_timeout");
+ return -EINVAL;
+ }
+
+ if (value == 0)
+ wl1271_info("split scan will be disabled");
+
+ mutex_lock(&wl->mutex);
+
+ wl->conf.scan.split_scan_timeout = value * 1000;
+
+ mutex_unlock(&wl->mutex);
+ return count;
+}
+
+static const struct file_operations split_scan_timeout_ops = {
+ .read = split_scan_timeout_read,
+ .write = split_scan_timeout_write,
+ .open = wl1271_open_file_generic,
+ .llseek = default_llseek,
+};
+
static ssize_t driver_state_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -446,6 +621,7 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
VIF_STATE_PRINT_INT(sta.basic_rate_idx);
VIF_STATE_PRINT_INT(sta.ap_rate_idx);
VIF_STATE_PRINT_INT(sta.p2p_rate_idx);
+ VIF_STATE_PRINT_INT(sta.qos);
} else {
VIF_STATE_PRINT_INT(ap.global_hlid);
VIF_STATE_PRINT_INT(ap.bcast_hlid);
@@ -471,7 +647,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
VIF_STATE_PRINT_INT(default_key);
VIF_STATE_PRINT_INT(aid);
VIF_STATE_PRINT_INT(session_counter);
- VIF_STATE_PRINT_INT(ps_poll_failures);
VIF_STATE_PRINT_INT(psm_entry_retry);
VIF_STATE_PRINT_INT(power_level);
VIF_STATE_PRINT_INT(rssi_thold);
@@ -562,6 +737,64 @@ static const struct file_operations dtim_interval_ops = {
.llseek = default_llseek,
};
+
+
+static ssize_t suspend_dtim_interval_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wl1271 *wl = file->private_data;
+ u8 value;
+
+ if (wl->conf.conn.suspend_wake_up_event == CONF_WAKE_UP_EVENT_DTIM ||
+ wl->conf.conn.suspend_wake_up_event == CONF_WAKE_UP_EVENT_N_DTIM)
+ value = wl->conf.conn.suspend_listen_interval;
+ else
+ value = 0;
+
+ return wl1271_format_buffer(user_buf, count, ppos, "%d\n", value);
+}
+
+static ssize_t suspend_dtim_interval_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wl1271 *wl = file->private_data;
+ unsigned long value;
+ int ret;
+
+ ret = kstrtoul_from_user(user_buf, count, 10, &value);
+ if (ret < 0) {
+ wl1271_warning("illegal value for suspend_dtim_interval");
+ return -EINVAL;
+ }
+
+ if (value < 1 || value > 10) {
+ wl1271_warning("suspend_dtim value is not in valid range");
+ return -ERANGE;
+ }
+
+ mutex_lock(&wl->mutex);
+
+ wl->conf.conn.suspend_listen_interval = value;
+ /* for some reason there are different event types for 1 and >1 */
+ if (value == 1)
+ wl->conf.conn.suspend_wake_up_event = CONF_WAKE_UP_EVENT_DTIM;
+ else
+ wl->conf.conn.suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM;
+
+ mutex_unlock(&wl->mutex);
+ return count;
+}
+
+
+static const struct file_operations suspend_dtim_interval_ops = {
+ .read = suspend_dtim_interval_read,
+ .write = suspend_dtim_interval_write,
+ .open = wl1271_open_file_generic,
+ .llseek = default_llseek,
+};
+
static ssize_t beacon_interval_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -886,8 +1119,12 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl,
DEBUGFS_ADD(driver_state, rootdir);
DEBUGFS_ADD(vifs_state, rootdir);
DEBUGFS_ADD(dtim_interval, rootdir);
+ DEBUGFS_ADD(suspend_dtim_interval, rootdir);
DEBUGFS_ADD(beacon_interval, rootdir);
DEBUGFS_ADD(beacon_filtering, rootdir);
+ DEBUGFS_ADD(dynamic_ps_timeout, rootdir);
+ DEBUGFS_ADD(forced_ps, rootdir);
+ DEBUGFS_ADD(split_scan_timeout, rootdir);
streaming = debugfs_create_dir("rx_streaming", rootdir);
if (!streaming || IS_ERR(streaming))
diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c
index d3280df68f5d..c953717f38eb 100644
--- a/drivers/net/wireless/wl12xx/event.c
+++ b/drivers/net/wireless/wl12xx/event.c
@@ -30,133 +30,6 @@
#include "scan.h"
#include "wl12xx_80211.h"
-void wl1271_pspoll_work(struct work_struct *work)
-{
- struct ieee80211_vif *vif;
- struct wl12xx_vif *wlvif;
- struct delayed_work *dwork;
- struct wl1271 *wl;
- int ret;
-
- dwork = container_of(work, struct delayed_work, work);
- wlvif = container_of(dwork, struct wl12xx_vif, pspoll_work);
- vif = container_of((void *)wlvif, struct ieee80211_vif, drv_priv);
- wl = wlvif->wl;
-
- wl1271_debug(DEBUG_EVENT, "pspoll work");
-
- mutex_lock(&wl->mutex);
-
- if (unlikely(wl->state == WL1271_STATE_OFF))
- goto out;
-
- if (!test_and_clear_bit(WLVIF_FLAG_PSPOLL_FAILURE, &wlvif->flags))
- goto out;
-
- if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
- goto out;
-
- /*
- * if we end up here, then we were in powersave when the pspoll
- * delivery failure occurred, and no-one changed state since, so
- * we should go back to powersave.
- */
- ret = wl1271_ps_elp_wakeup(wl);
- if (ret < 0)
- goto out;
-
- wl1271_ps_set_mode(wl, wlvif, STATION_POWER_SAVE_MODE,
- wlvif->basic_rate, true);
-
- wl1271_ps_elp_sleep(wl);
-out:
- mutex_unlock(&wl->mutex);
-};
-
-static void wl1271_event_pspoll_delivery_fail(struct wl1271 *wl,
- struct wl12xx_vif *wlvif)
-{
- int delay = wl->conf.conn.ps_poll_recovery_period;
- int ret;
-
- wlvif->ps_poll_failures++;
- if (wlvif->ps_poll_failures == 1)
- wl1271_info("AP with dysfunctional ps-poll, "
- "trying to work around it.");
-
- /* force active mode receive data from the AP */
- if (test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) {
- ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE,
- wlvif->basic_rate, true);
- if (ret < 0)
- return;
- set_bit(WLVIF_FLAG_PSPOLL_FAILURE, &wlvif->flags);
- ieee80211_queue_delayed_work(wl->hw, &wlvif->pspoll_work,
- msecs_to_jiffies(delay));
- }
-
- /*
- * If already in active mode, lets we should be getting data from
- * the AP right away. If we enter PSM too fast after this, and data
- * remains on the AP, we will get another event like this, and we'll
- * go into active once more.
- */
-}
-
-static int wl1271_event_ps_report(struct wl1271 *wl,
- struct wl12xx_vif *wlvif,
- struct event_mailbox *mbox,
- bool *beacon_loss)
-{
- int ret = 0;
- u32 total_retries = wl->conf.conn.psm_entry_retries;
-
- wl1271_debug(DEBUG_EVENT, "ps_status: 0x%x", mbox->ps_status);
-
- switch (mbox->ps_status) {
- case EVENT_ENTER_POWER_SAVE_FAIL:
- wl1271_debug(DEBUG_PSM, "PSM entry failed");
-
- if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) {
- /* remain in active mode */
- wlvif->psm_entry_retry = 0;
- break;
- }
-
- if (wlvif->psm_entry_retry < total_retries) {
- wlvif->psm_entry_retry++;
- ret = wl1271_ps_set_mode(wl, wlvif,
- STATION_POWER_SAVE_MODE,
- wlvif->basic_rate, true);
- } else {
- wl1271_info("No ack to nullfunc from AP.");
- wlvif->psm_entry_retry = 0;
- *beacon_loss = true;
- }
- break;
- case EVENT_ENTER_POWER_SAVE_SUCCESS:
- wlvif->psm_entry_retry = 0;
-
- /*
- * BET has only a minor effect in 5GHz and masks
- * channel switch IEs, so we only enable BET on 2.4GHz
- */
- if (wlvif->band == IEEE80211_BAND_2GHZ)
- /* enable beacon early termination */
- ret = wl1271_acx_bet_enable(wl, wlvif, true);
-
- if (wlvif->ps_compl) {
- complete(wlvif->ps_compl);
- wlvif->ps_compl = NULL;
- }
- break;
- default:
- break;
- }
-
- return ret;
-}
-
static void wl1271_event_rssi_trigger(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct event_mailbox *mbox)
@@ -205,21 +78,13 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif)
static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl,
u8 enable)
{
- struct ieee80211_vif *vif;
struct wl12xx_vif *wlvif;
if (enable) {
- /* disable dynamic PS when requested by the firmware */
- wl12xx_for_each_wlvif_sta(wl, wlvif) {
- vif = wl12xx_wlvif_to_vif(wlvif);
- ieee80211_disable_dyn_ps(vif);
- }
set_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags);
} else {
clear_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags);
wl12xx_for_each_wlvif_sta(wl, wlvif) {
- vif = wl12xx_wlvif_to_vif(wlvif);
- ieee80211_enable_dyn_ps(vif);
wl1271_recalc_rx_streaming(wl, wlvif);
}
}
@@ -237,7 +102,6 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
{
struct ieee80211_vif *vif;
struct wl12xx_vif *wlvif;
- int ret;
u32 vector;
bool beacon_loss = false;
bool disconnect_sta = false;
@@ -293,21 +157,6 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
beacon_loss = true;
}
- if (vector & PS_REPORT_EVENT_ID) {
- wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT");
- wl12xx_for_each_wlvif_sta(wl, wlvif) {
- ret = wl1271_event_ps_report(wl, wlvif,
- mbox, &beacon_loss);
- if (ret < 0)
- return ret;
- }
- }
-
- if (vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID)
- wl12xx_for_each_wlvif_sta(wl, wlvif) {
- wl1271_event_pspoll_delivery_fail(wl, wlvif);
- }
-
if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
/* TODO: check actual multi-role support */
wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT");
@@ -344,7 +193,6 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
/* TODO: configure only the relevant vif */
wl12xx_for_each_wlvif_sta(wl, wlvif) {
- struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
bool success;
if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
@@ -352,6 +200,8 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
continue;
success = mbox->channel_switch_status ? false : true;
+ vif = wl12xx_wlvif_to_vif(wlvif);
+
ieee80211_chswitch_done(vif, success);
}
}
diff --git a/drivers/net/wireless/wl12xx/event.h b/drivers/net/wireless/wl12xx/event.h
index 1d878ba47bf4..057d193d3525 100644
--- a/drivers/net/wireless/wl12xx/event.h
+++ b/drivers/net/wireless/wl12xx/event.h
@@ -51,10 +51,10 @@ enum {
SCAN_COMPLETE_EVENT_ID = BIT(10),
WFD_DISCOVERY_COMPLETE_EVENT_ID = BIT(11),
AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(12),
- PS_REPORT_EVENT_ID = BIT(13),
+ RESERVED1 = BIT(13),
PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(14),
- DISCONNECT_EVENT_COMPLETE_ID = BIT(15),
- /* BIT(16) is reserved */
+ ROLE_STOP_COMPLETE_EVENT_ID = BIT(15),
+ RADAR_DETECTED_EVENT_ID = BIT(16),
CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17),
BSS_LOSE_EVENT_ID = BIT(18),
REGAINED_BSS_EVENT_ID = BIT(19),
@@ -94,9 +94,9 @@ struct event_mailbox {
u8 soft_gemini_sense_info;
u8 soft_gemini_protective_info;
s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
- u8 channel_switch_status;
+ u8 change_auto_mode_timeout;
u8 scheduled_scan_status;
- u8 ps_status;
+ u8 reserved4;
/* tuned channel (roc) */
u8 roc_channel;
@@ -119,17 +119,21 @@ struct event_mailbox {
u8 rx_ba_allowed;
u8 reserved_6[2];
+ /* Channel switch results */
+
+ u8 channel_switch_role_id;
+ u8 channel_switch_status;
+ u8 reserved_7[2];
+
u8 ps_poll_delivery_failure_role_ids;
u8 stopped_role_ids;
u8 started_role_ids;
- u8 change_auto_mode_timeout;
- u8 reserved_7[12];
+ u8 reserved_8[9];
} __packed;
int wl1271_event_unmask(struct wl1271 *wl);
void wl1271_event_mbox_config(struct wl1271 *wl);
int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
-void wl1271_pspoll_work(struct work_struct *work);
#endif
diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c
index ca7ee59e4505..203fbebf09eb 100644
--- a/drivers/net/wireless/wl12xx/init.c
+++ b/drivers/net/wireless/wl12xx/init.c
@@ -37,54 +37,64 @@
int wl1271_init_templates_config(struct wl1271 *wl)
{
int ret, i;
+ size_t max_size;
/* send empty templates for fw memory reservation */
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL,
- WL1271_CMD_TEMPL_DFLT_SIZE,
+ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
+ CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL,
+ WL1271_CMD_TEMPL_MAX_SIZE,
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5,
- NULL, WL1271_CMD_TEMPL_DFLT_SIZE, 0,
+ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
+ CMD_TEMPL_CFG_PROBE_REQ_5,
+ NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0,
WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL,
+ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
+ CMD_TEMPL_NULL_DATA, NULL,
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,
+ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
+ CMD_TEMPL_PS_POLL, NULL,
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,
+ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
+ CMD_TEMPL_QOS_NULL_DATA, NULL,
sizeof
(struct ieee80211_qos_hdr),
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE, NULL,
+ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
+ CMD_TEMPL_PROBE_RESPONSE, NULL,
WL1271_CMD_TEMPL_DFLT_SIZE,
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, NULL,
+ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
+ CMD_TEMPL_BEACON, NULL,
WL1271_CMD_TEMPL_DFLT_SIZE,
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_ARP_RSP, NULL,
- sizeof
- (struct wl12xx_arp_rsp_template),
+ max_size = sizeof(struct wl12xx_arp_rsp_template) +
+ WL1271_EXTRA_SPACE_MAX;
+ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
+ CMD_TEMPL_ARP_RSP, NULL,
+ max_size,
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
@@ -93,19 +103,22 @@ int wl1271_init_templates_config(struct wl1271 *wl)
* Put very large empty placeholders for all templates. These
* reserve memory for later.
*/
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_PROBE_RESPONSE, NULL,
+ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
+ CMD_TEMPL_AP_PROBE_RESPONSE, NULL,
WL1271_CMD_TEMPL_MAX_SIZE,
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_BEACON, NULL,
+ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
+ CMD_TEMPL_AP_BEACON, NULL,
WL1271_CMD_TEMPL_MAX_SIZE,
0, WL1271_RATE_AUTOMATIC);
if (ret < 0)
return ret;
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP, NULL,
+ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
+ CMD_TEMPL_DEAUTH_AP, NULL,
sizeof
(struct wl12xx_disconn_template),
0, WL1271_RATE_AUTOMATIC);
@@ -113,7 +126,8 @@ int wl1271_init_templates_config(struct wl1271 *wl)
return ret;
for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) {
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, NULL,
+ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
+ CMD_TEMPL_KLV, NULL,
sizeof(struct ieee80211_qos_hdr),
i, WL1271_RATE_AUTOMATIC);
if (ret < 0)
@@ -140,7 +154,8 @@ static int wl1271_ap_init_deauth_template(struct wl1271 *wl,
IEEE80211_STYPE_DEAUTH);
rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP,
+ ret = wl1271_cmd_template_set(wl, wlvif->role_id,
+ CMD_TEMPL_DEAUTH_AP,
tmpl, sizeof(*tmpl), 0, rate);
out:
@@ -172,7 +187,8 @@ static int wl1271_ap_init_null_template(struct wl1271 *wl,
memcpy(nullfunc->addr3, vif->addr, ETH_ALEN);
rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, nullfunc,
+ ret = wl1271_cmd_template_set(wl, wlvif->role_id,
+ CMD_TEMPL_NULL_DATA, nullfunc,
sizeof(*nullfunc), 0, rate);
out:
@@ -204,7 +220,8 @@ static int wl1271_ap_init_qos_null_template(struct wl1271 *wl,
memcpy(qosnull->addr3, vif->addr, ETH_ALEN);
rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, qosnull,
+ ret = wl1271_cmd_template_set(wl, wlvif->role_id,
+ CMD_TEMPL_QOS_NULL_DATA, qosnull,
sizeof(*qosnull), 0, rate);
out:
diff --git a/drivers/net/wireless/wl12xx/io.c b/drivers/net/wireless/wl12xx/io.c
index 079ad380e8ff..c574a3b31e31 100644
--- a/drivers/net/wireless/wl12xx/io.c
+++ b/drivers/net/wireless/wl12xx/io.c
@@ -45,6 +45,65 @@
#define OCP_STATUS_REQ_FAILED 0x20000
#define OCP_STATUS_RESP_ERROR 0x30000
+struct wl1271_partition_set wl12xx_part_table[PART_TABLE_LEN] = {
+ [PART_DOWN] = {
+ .mem = {
+ .start = 0x00000000,
+ .size = 0x000177c0
+ },
+ .reg = {
+ .start = REGISTERS_BASE,
+ .size = 0x00008800
+ },
+ .mem2 = {
+ .start = 0x00000000,
+ .size = 0x00000000
+ },
+ .mem3 = {
+ .start = 0x00000000,
+ .size = 0x00000000
+ },
+ },
+
+ [PART_WORK] = {
+ .mem = {
+ .start = 0x00040000,
+ .size = 0x00014fc0
+ },
+ .reg = {
+ .start = REGISTERS_BASE,
+ .size = 0x0000a000
+ },
+ .mem2 = {
+ .start = 0x003004f8,
+ .size = 0x00000004
+ },
+ .mem3 = {
+ .start = 0x00040404,
+ .size = 0x00000000
+ },
+ },
+
+ [PART_DRPW] = {
+ .mem = {
+ .start = 0x00040000,
+ .size = 0x00014fc0
+ },
+ .reg = {
+ .start = DRPW_BASE,
+ .size = 0x00006000
+ },
+ .mem2 = {
+ .start = 0x00000000,
+ .size = 0x00000000
+ },
+ .mem3 = {
+ .start = 0x00000000,
+ .size = 0x00000000
+ }
+ }
+};
+
bool wl1271_set_block_size(struct wl1271 *wl)
{
if (wl->if_ops->set_block_size) {
diff --git a/drivers/net/wireless/wl12xx/io.h b/drivers/net/wireless/wl12xx/io.h
index d398cbcea986..4fb3dab8c3b2 100644
--- a/drivers/net/wireless/wl12xx/io.h
+++ b/drivers/net/wireless/wl12xx/io.h
@@ -43,6 +43,8 @@
#define HW_ACCESS_PRAM_MAX_RANGE 0x3c000
+extern struct wl1271_partition_set wl12xx_part_table[PART_TABLE_LEN];
+
struct wl1271;
void wl1271_disable_interrupts(struct wl1271 *wl);
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index d5f55a149de5..39002363611e 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1,3 +1,4 @@
+
/*
* This file is part of wl1271
*
@@ -115,6 +116,9 @@ static struct conf_drv_settings default_conf = {
[CONF_AP_CONNECTION_PROTECTION_TIME] = 0,
[CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25,
[CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25,
+ /* CTS Diluting params */
+ [CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0,
+ [CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0,
},
.state = CONF_SG_PROTECTIVE,
},
@@ -213,10 +217,13 @@ static struct conf_drv_settings default_conf = {
.basic_rate_5 = CONF_HW_BIT_RATE_6MBPS,
.tmpl_short_retry_limit = 10,
.tmpl_long_retry_limit = 10,
+ .tx_watchdog_timeout = 5000,
},
.conn = {
.wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
.listen_interval = 1,
+ .suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM,
+ .suspend_listen_interval = 3,
.bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED,
.bcn_filt_ie_count = 2,
.bcn_filt_ie = {
@@ -235,12 +242,13 @@ static struct conf_drv_settings default_conf = {
.broadcast_timeout = 20000,
.rx_broadcast_in_ps = 1,
.ps_poll_threshold = 10,
- .ps_poll_recovery_period = 700,
.bet_enable = CONF_BET_MODE_ENABLE,
.bet_max_consecutive = 50,
.psm_entry_retries = 8,
.psm_exit_retries = 16,
.psm_entry_nullfunc_retries = 3,
+ .dynamic_ps_timeout = 200,
+ .forced_ps = false,
.keep_alive_interval = 55000,
.max_listen_interval = 20,
},
@@ -265,6 +273,7 @@ static struct conf_drv_settings default_conf = {
.min_dwell_time_passive = 100000,
.max_dwell_time_passive = 100000,
.num_probe_reqs = 2,
+ .split_scan_timeout = 50000,
},
.sched_scan = {
/* sched_scan requires dwell times in TU instead of TU/1000 */
@@ -384,15 +393,15 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
static void wl1271_op_stop(struct ieee80211_hw *hw);
static void wl1271_free_ap_keys(struct wl1271 *wl, struct wl12xx_vif *wlvif);
-static DEFINE_MUTEX(wl_list_mutex);
-static LIST_HEAD(wl_list);
-
-static int wl1271_check_operstate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
- unsigned char operstate)
+static int wl12xx_set_authorized(struct wl1271 *wl,
+ struct wl12xx_vif *wlvif)
{
int ret;
- if (operstate != IF_OPER_UP)
+ if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS))
+ return -EINVAL;
+
+ if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
return 0;
if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags))
@@ -407,76 +416,6 @@ static int wl1271_check_operstate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
wl1271_info("Association completed.");
return 0;
}
-static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
- void *arg)
-{
- struct net_device *dev = arg;
- struct wireless_dev *wdev;
- struct wiphy *wiphy;
- struct ieee80211_hw *hw;
- struct wl1271 *wl;
- struct wl1271 *wl_temp;
- struct wl12xx_vif *wlvif;
- int ret = 0;
-
- /* Check that this notification is for us. */
- if (what != NETDEV_CHANGE)
- return NOTIFY_DONE;
-
- wdev = dev->ieee80211_ptr;
- if (wdev == NULL)
- return NOTIFY_DONE;
-
- wiphy = wdev->wiphy;
- if (wiphy == NULL)
- return NOTIFY_DONE;
-
- hw = wiphy_priv(wiphy);
- if (hw == NULL)
- return NOTIFY_DONE;
-
- wl_temp = hw->priv;
- mutex_lock(&wl_list_mutex);
- list_for_each_entry(wl, &wl_list, list) {
- if (wl == wl_temp)
- break;
- }
- mutex_unlock(&wl_list_mutex);
- if (wl != wl_temp)
- return NOTIFY_DONE;
-
- mutex_lock(&wl->mutex);
-
- if (wl->state == WL1271_STATE_OFF)
- goto out;
-
- if (dev->operstate != IF_OPER_UP)
- goto out;
- /*
- * The correct behavior should be just getting the appropriate wlvif
- * from the given dev, but currently we don't have a mac80211
- * interface for it.
- */
- wl12xx_for_each_wlvif_sta(wl, wlvif) {
- struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
-
- if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
- continue;
-
- ret = wl1271_ps_elp_wakeup(wl);
- if (ret < 0)
- goto out;
-
- wl1271_check_operstate(wl, wlvif,
- ieee80211_get_operstate(vif));
-
- wl1271_ps_elp_sleep(wl);
- }
-out:
- mutex_unlock(&wl->mutex);
-
- return NOTIFY_OK;
-}
static int wl1271_reg_notify(struct wiphy *wiphy,
struct regulatory_request *request)
@@ -615,6 +554,80 @@ static void wl1271_rx_streaming_timer(unsigned long data)
ieee80211_queue_work(wl->hw, &wlvif->rx_streaming_disable_work);
}
+/* wl->mutex must be taken */
+void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl)
+{
+ /* if the watchdog is not armed, don't do anything */
+ if (wl->tx_allocated_blocks == 0)
+ return;
+
+ cancel_delayed_work(&wl->tx_watchdog_work);
+ ieee80211_queue_delayed_work(wl->hw, &wl->tx_watchdog_work,
+ msecs_to_jiffies(wl->conf.tx.tx_watchdog_timeout));
+}
+
+static void wl12xx_tx_watchdog_work(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct wl1271 *wl;
+
+ dwork = container_of(work, struct delayed_work, work);
+ wl = container_of(dwork, struct wl1271, tx_watchdog_work);
+
+ mutex_lock(&wl->mutex);
+
+ if (unlikely(wl->state == WL1271_STATE_OFF))
+ goto out;
+
+ /* Tx went out in the meantime - everything is ok */
+ if (unlikely(wl->tx_allocated_blocks == 0))
+ goto out;
+
+ /*
+ * if a ROC is in progress, we might not have any Tx for a long
+ * time (e.g. pending Tx on the non-ROC channels)
+ */
+ if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) {
+ wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to ROC",
+ wl->conf.tx.tx_watchdog_timeout);
+ wl12xx_rearm_tx_watchdog_locked(wl);
+ goto out;
+ }
+
+ /*
+ * if a scan is in progress, we might not have any Tx for a long
+ * time
+ */
+ if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
+ wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms due to scan",
+ wl->conf.tx.tx_watchdog_timeout);
+ wl12xx_rearm_tx_watchdog_locked(wl);
+ goto out;
+ }
+
+ /*
+ * AP might cache a frame for a long time for a sleeping station,
+ * so rearm the timer if there's an AP interface with stations. If
+ * Tx is genuinely stuck we will most hopefully discover it when all
+ * stations are removed due to inactivity.
+ */
+ if (wl->active_sta_count) {
+ wl1271_debug(DEBUG_TX, "No Tx (in FW) for %d ms. AP has "
+ " %d stations",
+ wl->conf.tx.tx_watchdog_timeout,
+ wl->active_sta_count);
+ wl12xx_rearm_tx_watchdog_locked(wl);
+ goto out;
+ }
+
+ wl1271_error("Tx stuck (in FW) for %d ms. Starting recovery",
+ wl->conf.tx.tx_watchdog_timeout);
+ wl12xx_queue_recovery_work(wl);
+
+out:
+ mutex_unlock(&wl->mutex);
+}
+
static void wl1271_conf_init(struct wl1271 *wl)
{
@@ -672,8 +685,6 @@ static int wl1271_plt_init(struct wl1271 *wl)
if (ret < 0)
return ret;
}
- if (ret < 0)
- return ret;
/* Chip-specific initializations */
ret = wl1271_chip_specific_init(wl);
@@ -809,6 +820,18 @@ static void wl12xx_fw_status(struct wl1271 *wl,
wl->tx_allocated_blocks -= freed_blocks;
+ /*
+ * If the FW freed some blocks:
+ * If we still have allocated blocks - re-arm the timer, Tx is
+ * not stuck. Otherwise, cancel the timer (no Tx currently).
+ */
+ if (freed_blocks) {
+ if (wl->tx_allocated_blocks)
+ wl12xx_rearm_tx_watchdog_locked(wl);
+ else
+ cancel_delayed_work(&wl->tx_watchdog_work);
+ }
+
avail = le32_to_cpu(status->tx_total) - wl->tx_allocated_blocks;
/*
@@ -985,16 +1008,70 @@ out:
return IRQ_HANDLED;
}
-static int wl1271_fetch_firmware(struct wl1271 *wl)
+struct vif_counter_data {
+ u8 counter;
+
+ struct ieee80211_vif *cur_vif;
+ bool cur_vif_running;
+};
+
+static void wl12xx_vif_count_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct vif_counter_data *counter = data;
+
+ counter->counter++;
+ if (counter->cur_vif == vif)
+ counter->cur_vif_running = true;
+}
+
+/* caller must not hold wl->mutex, as it might deadlock */
+static void wl12xx_get_vif_count(struct ieee80211_hw *hw,
+ struct ieee80211_vif *cur_vif,
+ struct vif_counter_data *data)
+{
+ memset(data, 0, sizeof(*data));
+ data->cur_vif = cur_vif;
+
+ ieee80211_iterate_active_interfaces(hw,
+ wl12xx_vif_count_iter, data);
+}
+
+static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt)
{
const struct firmware *fw;
const char *fw_name;
+ enum wl12xx_fw_type fw_type;
int ret;
- if (wl->chip.id == CHIP_ID_1283_PG20)
- fw_name = WL128X_FW_NAME;
- else
- fw_name = WL127X_FW_NAME;
+ if (plt) {
+ fw_type = WL12XX_FW_TYPE_PLT;
+ if (wl->chip.id == CHIP_ID_1283_PG20)
+ fw_name = WL128X_PLT_FW_NAME;
+ else
+ fw_name = WL127X_PLT_FW_NAME;
+ } else {
+ /*
+ * we can't call wl12xx_get_vif_count() here because
+ * wl->mutex is taken, so use the cached last_vif_count value
+ */
+ if (wl->last_vif_count > 1) {
+ fw_type = WL12XX_FW_TYPE_MULTI;
+ if (wl->chip.id == CHIP_ID_1283_PG20)
+ fw_name = WL128X_FW_NAME_MULTI;
+ else
+ fw_name = WL127X_FW_NAME_MULTI;
+ } else {
+ fw_type = WL12XX_FW_TYPE_NORMAL;
+ if (wl->chip.id == CHIP_ID_1283_PG20)
+ fw_name = WL128X_FW_NAME_SINGLE;
+ else
+ fw_name = WL127X_FW_NAME_SINGLE;
+ }
+ }
+
+ if (wl->fw_type == fw_type)
+ return 0;
wl1271_debug(DEBUG_BOOT, "booting firmware %s", fw_name);
@@ -1013,6 +1090,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl)
}
vfree(wl->fw);
+ wl->fw_type = WL12XX_FW_TYPE_NONE;
wl->fw_len = fw->size;
wl->fw = vmalloc(wl->fw_len);
@@ -1024,7 +1102,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl)
memcpy(wl->fw, fw->data, wl->fw_len);
ret = 0;
-
+ wl->fw_type = fw_type;
out:
release_firmware(fw);
@@ -1152,7 +1230,7 @@ static void wl1271_recovery_work(struct work_struct *work)
mutex_lock(&wl->mutex);
- if (wl->state != WL1271_STATE_ON)
+ if (wl->state != WL1271_STATE_ON || wl->plt)
goto out_unlock;
/* Avoid a recursive recovery */
@@ -1163,7 +1241,8 @@ static void wl1271_recovery_work(struct work_struct *work)
wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x",
wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4));
- BUG_ON(bug_on_recovery);
+ BUG_ON(bug_on_recovery &&
+ !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
/*
* Advance security sequence number to overcome potential progress
@@ -1232,10 +1311,9 @@ static int wl1271_setup(struct wl1271 *wl)
return 0;
}
-static int wl1271_chip_wakeup(struct wl1271 *wl)
+static int wl12xx_set_power_on(struct wl1271 *wl)
{
- struct wl1271_partition_set partition;
- int ret = 0;
+ int ret;
msleep(WL1271_PRE_POWER_ON_SLEEP);
ret = wl1271_power_on(wl);
@@ -1245,20 +1323,22 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
wl1271_io_reset(wl);
wl1271_io_init(wl);
- /* We don't need a real memory partition here, because we only want
- * to use the registers at this point. */
- memset(&partition, 0, sizeof(partition));
- partition.reg.start = REGISTERS_BASE;
- partition.reg.size = REGISTERS_DOWN_SIZE;
- wl1271_set_partition(wl, &partition);
+ wl1271_set_partition(wl, &wl12xx_part_table[PART_DOWN]);
/* ELP module wake up */
wl1271_fw_wakeup(wl);
- /* whal_FwCtrl_BootSm() */
+out:
+ return ret;
+}
+
+static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt)
+{
+ int ret = 0;
- /* 0. read chip id from CHIP_ID */
- wl->chip.id = wl1271_read32(wl, CHIP_ID_B);
+ ret = wl12xx_set_power_on(wl);
+ if (ret < 0)
+ goto out;
/*
* For wl127x based devices we could use the default block
@@ -1307,11 +1387,9 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
goto out;
}
- if (wl->fw == NULL) {
- ret = wl1271_fetch_firmware(wl);
- if (ret < 0)
- goto out;
- }
+ ret = wl12xx_fetch_firmware(wl, plt);
+ if (ret < 0)
+ goto out;
/* No NVS from netlink, try to get it from the filesystem */
if (wl->nvs == NULL) {
@@ -1343,7 +1421,7 @@ int wl1271_plt_start(struct wl1271 *wl)
while (retries) {
retries--;
- ret = wl1271_chip_wakeup(wl);
+ ret = wl12xx_chip_wakeup(wl, true);
if (ret < 0)
goto power_off;
@@ -1355,7 +1433,8 @@ int wl1271_plt_start(struct wl1271 *wl)
if (ret < 0)
goto irq_disable;
- wl->state = WL1271_STATE_PLT;
+ wl->plt = true;
+ wl->state = WL1271_STATE_ON;
wl1271_notice("firmware booted in PLT mode (%s)",
wl->chip.fw_ver_str);
@@ -1391,41 +1470,52 @@ out:
return ret;
}
-static int __wl1271_plt_stop(struct wl1271 *wl)
+int wl1271_plt_stop(struct wl1271 *wl)
{
int ret = 0;
wl1271_notice("power down");
- if (wl->state != WL1271_STATE_PLT) {
+ /*
+ * Interrupts must be disabled before setting the state to OFF.
+ * Otherwise, the interrupt handler might be called and exit without
+ * reading the interrupt status.
+ */
+ wl1271_disable_interrupts(wl);
+ mutex_lock(&wl->mutex);
+ if (!wl->plt) {
+ mutex_unlock(&wl->mutex);
+
+ /*
+ * This will not necessarily enable interrupts as interrupts
+ * may have been disabled when op_stop was called. It will,
+ * however, balance the above call to disable_interrupts().
+ */
+ wl1271_enable_interrupts(wl);
+
wl1271_error("cannot power down because not in PLT "
"state: %d", wl->state);
ret = -EBUSY;
goto out;
}
- wl1271_power_off(wl);
-
- wl->state = WL1271_STATE_OFF;
- wl->rx_counter = 0;
-
mutex_unlock(&wl->mutex);
- wl1271_disable_interrupts(wl);
+
wl1271_flush_deferred_work(wl);
cancel_work_sync(&wl->netstack_work);
cancel_work_sync(&wl->recovery_work);
- mutex_lock(&wl->mutex);
-out:
- return ret;
-}
-
-int wl1271_plt_stop(struct wl1271 *wl)
-{
- int ret;
+ cancel_delayed_work_sync(&wl->elp_work);
+ cancel_delayed_work_sync(&wl->tx_watchdog_work);
mutex_lock(&wl->mutex);
- ret = __wl1271_plt_stop(wl);
+ wl1271_power_off(wl);
+ wl->flags = 0;
+ wl->state = WL1271_STATE_OFF;
+ wl->plt = false;
+ wl->rx_counter = 0;
mutex_unlock(&wl->mutex);
+
+out:
return ret;
}
@@ -1457,7 +1547,8 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
goto out;
}
- wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q);
+ wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d len %d",
+ hlid, q, skb->len);
skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
wl->tx_queue_count[q]++;
@@ -1555,10 +1646,6 @@ static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
}
-static struct notifier_block wl1271_dev_notifier = {
- .notifier_call = wl1271_dev_notify,
-};
-
#ifdef CONFIG_PM
static int wl1271_configure_suspend_sta(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
@@ -1574,38 +1661,16 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl,
if (ret < 0)
goto out_unlock;
- /* enter psm if needed*/
- if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) {
- DECLARE_COMPLETION_ONSTACK(compl);
-
- wlvif->ps_compl = &compl;
- ret = wl1271_ps_set_mode(wl, wlvif, STATION_POWER_SAVE_MODE,
- wlvif->basic_rate, true);
- if (ret < 0)
- goto out_sleep;
-
- /* we must unlock here so we will be able to get events */
- wl1271_ps_elp_sleep(wl);
- mutex_unlock(&wl->mutex);
+ ret = wl1271_acx_wake_up_conditions(wl, wlvif,
+ wl->conf.conn.suspend_wake_up_event,
+ wl->conf.conn.suspend_listen_interval);
- ret = wait_for_completion_timeout(
- &compl, msecs_to_jiffies(WL1271_PS_COMPLETE_TIMEOUT));
+ if (ret < 0)
+ wl1271_error("suspend: set wake up conditions failed: %d", ret);
- mutex_lock(&wl->mutex);
- if (ret <= 0) {
- wl1271_warning("couldn't enter ps mode!");
- ret = -EBUSY;
- goto out_cleanup;
- }
- ret = wl1271_ps_elp_wakeup(wl);
- if (ret < 0)
- goto out_cleanup;
- }
-out_sleep:
wl1271_ps_elp_sleep(wl);
-out_cleanup:
- wlvif->ps_compl = NULL;
+
out_unlock:
mutex_unlock(&wl->mutex);
return ret;
@@ -1648,11 +1713,11 @@ static int wl1271_configure_suspend(struct wl1271 *wl,
static void wl1271_configure_resume(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
- int ret;
- bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
+ int ret = 0;
bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
+ bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
- if (!is_sta && !is_ap)
+ if ((!is_ap) && (!is_sta))
return;
mutex_lock(&wl->mutex);
@@ -1661,12 +1726,16 @@ static void wl1271_configure_resume(struct wl1271 *wl,
goto out;
if (is_sta) {
- /* exit psm if it wasn't configured */
- if (!test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags))
- wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE,
- wlvif->basic_rate, true);
+ ret = wl1271_acx_wake_up_conditions(wl, wlvif,
+ wl->conf.conn.wake_up_event,
+ wl->conf.conn.listen_interval);
+
+ if (ret < 0)
+ wl1271_error("resume: wake up conditions failed: %d",
+ ret);
+
} else if (is_ap) {
- wl1271_acx_beacon_filter_opt(wl, wlvif, false);
+ ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
}
wl1271_ps_elp_sleep(wl);
@@ -1684,6 +1753,8 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
WARN_ON(!wow || !wow->any);
+ wl1271_tx_flush(wl);
+
wl->wow_enabled = true;
wl12xx_for_each_wlvif(wl, wlvif) {
ret = wl1271_configure_suspend(wl, wlvif);
@@ -1709,9 +1780,6 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
wl1271_enable_interrupts(wl);
flush_work(&wl->tx_work);
- wl12xx_for_each_wlvif(wl, wlvif) {
- flush_delayed_work(&wlvif->pspoll_work);
- }
flush_delayed_work(&wl->elp_work);
return 0;
@@ -1778,11 +1846,25 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
+ /*
+ * Interrupts must be disabled before setting the state to OFF.
+ * Otherwise, the interrupt handler might be called and exit without
+ * reading the interrupt status.
+ */
+ wl1271_disable_interrupts(wl);
mutex_lock(&wl->mutex);
if (wl->state == WL1271_STATE_OFF) {
mutex_unlock(&wl->mutex);
+
+ /*
+ * This will not necessarily enable interrupts as interrupts
+ * may have been disabled when op_stop was called. It will,
+ * however, balance the above call to disable_interrupts().
+ */
+ wl1271_enable_interrupts(wl);
return;
}
+
/*
* this must be before the cancel_work calls below, so that the work
* functions don't perform further work.
@@ -1790,16 +1872,12 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
wl->state = WL1271_STATE_OFF;
mutex_unlock(&wl->mutex);
- mutex_lock(&wl_list_mutex);
- list_del(&wl->list);
- mutex_unlock(&wl_list_mutex);
-
- wl1271_disable_interrupts(wl);
wl1271_flush_deferred_work(wl);
cancel_delayed_work_sync(&wl->scan_complete_work);
cancel_work_sync(&wl->netstack_work);
cancel_work_sync(&wl->tx_work);
cancel_delayed_work_sync(&wl->elp_work);
+ cancel_delayed_work_sync(&wl->tx_watchdog_work);
/* let's notify MAC80211 about the remaining pending TX frames */
wl12xx_tx_reset(wl, true);
@@ -1969,7 +2047,6 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
wl1271_rx_streaming_enable_work);
INIT_WORK(&wlvif->rx_streaming_disable_work,
wl1271_rx_streaming_disable_work);
- INIT_DELAYED_WORK(&wlvif->pspoll_work, wl1271_pspoll_work);
INIT_LIST_HEAD(&wlvif->list);
setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
@@ -1986,7 +2063,7 @@ static bool wl12xx_init_fw(struct wl1271 *wl)
while (retries) {
retries--;
- ret = wl1271_chip_wakeup(wl);
+ ret = wl12xx_chip_wakeup(wl, false);
if (ret < 0)
goto power_off;
@@ -2051,30 +2128,77 @@ static bool wl12xx_dev_role_started(struct wl12xx_vif *wlvif)
return wlvif->dev_hlid != WL12XX_INVALID_LINK_ID;
}
+/*
+ * Check whether a fw switch (i.e. moving from one loaded
+ * fw to another) is needed. This function is also responsible
+ * for updating wl->last_vif_count, so it must be called before
+ * loading a non-plt fw (so the correct fw (single-role/multi-role)
+ * will be used).
+ */
+static bool wl12xx_need_fw_change(struct wl1271 *wl,
+ struct vif_counter_data vif_counter_data,
+ bool add)
+{
+ enum wl12xx_fw_type current_fw = wl->fw_type;
+ u8 vif_count = vif_counter_data.counter;
+
+ if (test_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags))
+ return false;
+
+ /* increase the vif count if this is a new vif */
+ if (add && !vif_counter_data.cur_vif_running)
+ vif_count++;
+
+ wl->last_vif_count = vif_count;
+
+ /* no need for fw change if the device is OFF */
+ if (wl->state == WL1271_STATE_OFF)
+ return false;
+
+ if (vif_count > 1 && current_fw == WL12XX_FW_TYPE_NORMAL)
+ return true;
+ if (vif_count <= 1 && current_fw == WL12XX_FW_TYPE_MULTI)
+ return true;
+
+ return false;
+}
+
+/*
+ * Enter "forced psm". Make sure the sta is in psm against the ap,
+ * to make the fw switch a bit more disconnection-persistent.
+ */
+static void wl12xx_force_active_psm(struct wl1271 *wl)
+{
+ struct wl12xx_vif *wlvif;
+
+ wl12xx_for_each_wlvif_sta(wl, wlvif) {
+ wl1271_ps_set_mode(wl, wlvif, STATION_POWER_SAVE_MODE);
+ }
+}
+
static int wl1271_op_add_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+ struct vif_counter_data vif_count;
int ret = 0;
u8 role_type;
bool booted = false;
+ vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+ IEEE80211_VIF_SUPPORTS_CQM_RSSI;
+
wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
ieee80211_vif_type_p2p(vif), vif->addr);
+ wl12xx_get_vif_count(hw, vif, &vif_count);
+
mutex_lock(&wl->mutex);
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out_unlock;
- if (wl->vif) {
- wl1271_debug(DEBUG_MAC80211,
- "multiple vifs are not supported yet");
- ret = -EBUSY;
- goto out;
- }
-
/*
* in some very corner case HW recovery scenarios its possible to
* get here before __wl1271_op_remove_interface is complete, so
@@ -2086,6 +2210,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
goto out;
}
+
ret = wl12xx_init_vif_data(wl, vif);
if (ret < 0)
goto out;
@@ -2097,6 +2222,14 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
goto out;
}
+ if (wl12xx_need_fw_change(wl, vif_count, true)) {
+ wl12xx_force_active_psm(wl);
+ set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags);
+ mutex_unlock(&wl->mutex);
+ wl1271_recovery_work(&wl->recovery_work);
+ return 0;
+ }
+
/*
* TODO: after the nvs issue will be solved, move this block
* to start(), and make sure here the driver is ON.
@@ -2106,7 +2239,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
* we still need this in order to configure the fw
* while uploading the nvs
*/
- memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
+ memcpy(wl->addresses[0].addr, vif->addr, ETH_ALEN);
booted = wl12xx_init_fw(wl);
if (!booted) {
@@ -2139,7 +2272,6 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
if (ret < 0)
goto out;
- wl->vif = vif;
list_add(&wlvif->list, &wl->wlvif_list);
set_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags);
@@ -2152,11 +2284,6 @@ out:
out_unlock:
mutex_unlock(&wl->mutex);
- mutex_lock(&wl_list_mutex);
- if (!ret)
- list_add(&wl->list, &wl_list);
- mutex_unlock(&wl_list_mutex);
-
return ret;
}
@@ -2172,20 +2299,20 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
if (!test_and_clear_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
return;
- wl->vif = NULL;
-
/* because of hardware recovery, we may get here twice */
if (wl->state != WL1271_STATE_ON)
return;
wl1271_info("down");
- /* enable dyn ps just in case (if left on due to fw crash etc) */
- if (wlvif->bss_type == BSS_TYPE_STA_BSS)
- ieee80211_enable_dyn_ps(vif);
-
if (wl->scan.state != WL1271_SCAN_STATE_IDLE &&
wl->scan_vif == vif) {
+ /*
+ * Rearm the tx watchdog just before idling scan. This
+ * prevents just-finished scans from triggering the watchdog
+ */
+ wl12xx_rearm_tx_watchdog_locked(wl);
+
wl->scan.state = WL1271_SCAN_STATE_IDLE;
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
wl->scan_vif = NULL;
@@ -2250,10 +2377,10 @@ deinit:
wl->sta_count--;
mutex_unlock(&wl->mutex);
+
del_timer_sync(&wlvif->rx_streaming_timer);
cancel_work_sync(&wlvif->rx_streaming_enable_work);
cancel_work_sync(&wlvif->rx_streaming_disable_work);
- cancel_delayed_work_sync(&wlvif->pspoll_work);
mutex_lock(&wl->mutex);
}
@@ -2264,7 +2391,10 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct wl12xx_vif *iter;
+ struct vif_counter_data vif_count;
+ bool cancel_recovery = true;
+ wl12xx_get_vif_count(hw, vif, &vif_count);
mutex_lock(&wl->mutex);
if (wl->state == WL1271_STATE_OFF ||
@@ -2283,20 +2413,34 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
break;
}
WARN_ON(iter != wlvif);
+ if (wl12xx_need_fw_change(wl, vif_count, false)) {
+ wl12xx_force_active_psm(wl);
+ set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags);
+ wl12xx_queue_recovery_work(wl);
+ cancel_recovery = false;
+ }
out:
mutex_unlock(&wl->mutex);
- cancel_work_sync(&wl->recovery_work);
+ if (cancel_recovery)
+ cancel_work_sync(&wl->recovery_work);
}
static int wl12xx_op_change_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
enum nl80211_iftype new_type, bool p2p)
{
+ struct wl1271 *wl = hw->priv;
+ int ret;
+
+ set_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags);
wl1271_op_remove_interface(hw, vif);
- vif->type = ieee80211_iftype_p2p(new_type, p2p);
+ vif->type = new_type;
vif->p2p = p2p;
- return wl1271_op_add_interface(hw, vif);
+ ret = wl1271_op_add_interface(hw, vif);
+
+ clear_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags);
+ return ret;
}
static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
@@ -2317,6 +2461,9 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
wl1271_info("JOIN while associated.");
+ /* clear encryption type */
+ wlvif->encryption_type = KEY_NONE;
+
if (set_assoc)
set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
@@ -2467,71 +2614,61 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
wl1271_warning("rate policy for channel "
"failed %d", ret);
- if (test_bit(WLVIF_FLAG_STA_ASSOCIATED,
- &wlvif->flags)) {
- if (wl12xx_dev_role_started(wlvif)) {
- /* roaming */
- ret = wl12xx_croc(wl,
- wlvif->dev_role_id);
- if (ret < 0)
- return ret;
- }
- ret = wl1271_join(wl, wlvif, false);
+ /*
+ * change the ROC channel. do it only if we are
+ * not idle. otherwise, CROC will be called
+ * anyway.
+ */
+ if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED,
+ &wlvif->flags) &&
+ wl12xx_dev_role_started(wlvif) &&
+ !(conf->flags & IEEE80211_CONF_IDLE)) {
+ ret = wl12xx_stop_dev(wl, wlvif);
if (ret < 0)
- wl1271_warning("cmd join on channel "
- "failed %d", ret);
- } else {
- /*
- * change the ROC channel. do it only if we are
- * not idle. otherwise, CROC will be called
- * anyway.
- */
- if (wl12xx_dev_role_started(wlvif) &&
- !(conf->flags & IEEE80211_CONF_IDLE)) {
- ret = wl12xx_stop_dev(wl, wlvif);
- if (ret < 0)
- return ret;
+ return ret;
- ret = wl12xx_start_dev(wl, wlvif);
- if (ret < 0)
- return ret;
- }
+ ret = wl12xx_start_dev(wl, wlvif);
+ if (ret < 0)
+ return ret;
}
}
}
- /*
- * if mac80211 changes the PSM mode, make sure the mode is not
- * incorrectly changed after the pspoll failure active window.
- */
- if (changed & IEEE80211_CONF_CHANGE_PS)
- clear_bit(WLVIF_FLAG_PSPOLL_FAILURE, &wlvif->flags);
+ if ((changed & IEEE80211_CONF_CHANGE_PS) && !is_ap) {
- if (conf->flags & IEEE80211_CONF_PS &&
- !test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags)) {
- set_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags);
+ if ((conf->flags & IEEE80211_CONF_PS) &&
+ test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
+ !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
- /*
- * We enter PSM only if we're already associated.
- * If we're not, we'll enter it when joining an SSID,
- * through the bss_info_changed() hook.
- */
- if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
- wl1271_debug(DEBUG_PSM, "psm enabled");
- ret = wl1271_ps_set_mode(wl, wlvif,
- STATION_POWER_SAVE_MODE,
- wlvif->basic_rate, true);
- }
- } else if (!(conf->flags & IEEE80211_CONF_PS) &&
- test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags)) {
- wl1271_debug(DEBUG_PSM, "psm disabled");
+ int ps_mode;
+ char *ps_mode_str;
+
+ if (wl->conf.conn.forced_ps) {
+ ps_mode = STATION_POWER_SAVE_MODE;
+ ps_mode_str = "forced";
+ } else {
+ ps_mode = STATION_AUTO_PS_MODE;
+ ps_mode_str = "auto";
+ }
+
+ wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str);
+
+ ret = wl1271_ps_set_mode(wl, wlvif, ps_mode);
- clear_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags);
+ if (ret < 0)
+ wl1271_warning("enter %s ps failed %d",
+ ps_mode_str, ret);
+
+ } else if (!(conf->flags & IEEE80211_CONF_PS) &&
+ test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
+
+ wl1271_debug(DEBUG_PSM, "auto ps disabled");
- if (test_bit(WLVIF_FLAG_PSM, &wlvif->flags))
ret = wl1271_ps_set_mode(wl, wlvif,
- STATION_ACTIVE_MODE,
- wlvif->basic_rate, true);
+ STATION_ACTIVE_MODE);
+ if (ret < 0)
+ wl1271_warning("exit auto ps failed %d", ret);
+ }
}
if (conf->power_level != wlvif->power_level) {
@@ -2971,6 +3108,21 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
wl1271_error("Could not add or replace key");
goto out_sleep;
}
+
+ /*
+ * reconfiguring arp response if the unicast (or common)
+ * encryption key type was changed
+ */
+ if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
+ (sta || key_type == KEY_WEP) &&
+ wlvif->encryption_type != key_type) {
+ wlvif->encryption_type = key_type;
+ ret = wl1271_cmd_build_arp_rsp(wl, wlvif);
+ if (ret < 0) {
+ wl1271_warning("build arp rsp failed: %d", ret);
+ goto out_sleep;
+ }
+ }
break;
case DISABLE_KEY:
@@ -3004,8 +3156,6 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
struct cfg80211_scan_request *req)
{
struct wl1271 *wl = hw->priv;
- struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-
int ret;
u8 *ssid = NULL;
size_t len = 0;
@@ -3033,17 +3183,13 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
if (ret < 0)
goto out;
- if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
- test_bit(wlvif->role_id, wl->roc_map)) {
+ /* fail if there is any role in ROC */
+ if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES) {
/* don't allow scanning right now */
ret = -EBUSY;
goto out_sleep;
}
- /* cancel ROC before scanning */
- if (wl12xx_dev_role_started(wlvif))
- wl12xx_stop_dev(wl, wlvif);
-
ret = wl1271_scan(hw->priv, vif, ssid, len, req);
out_sleep:
wl1271_ps_elp_sleep(wl);
@@ -3078,6 +3224,13 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
if (ret < 0)
goto out_sleep;
}
+
+ /*
+ * Rearm the tx watchdog just before idling scan. This
+ * prevents just-finished scans from triggering the watchdog
+ */
+ wl12xx_rearm_tx_watchdog_locked(wl);
+
wl->scan.state = WL1271_SCAN_STATE_IDLE;
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
wl->scan_vif = NULL;
@@ -3105,6 +3258,11 @@ static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
mutex_lock(&wl->mutex);
+ if (wl->state == WL1271_STATE_OFF) {
+ ret = -EAGAIN;
+ goto out;
+ }
+
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
@@ -3136,6 +3294,9 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
mutex_lock(&wl->mutex);
+ if (wl->state == WL1271_STATE_OFF)
+ goto out;
+
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
@@ -3263,6 +3424,7 @@ static void wl12xx_remove_vendor_ie(struct sk_buff *skb,
static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl, u32 rates,
struct ieee80211_vif *vif)
{
+ struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct sk_buff *skb;
int ret;
@@ -3270,7 +3432,7 @@ static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl, u32 rates,
if (!skb)
return -EOPNOTSUPP;
- ret = wl1271_cmd_template_set(wl,
+ ret = wl1271_cmd_template_set(wl, wlvif->role_id,
CMD_TEMPL_AP_PROBE_RESPONSE,
skb->data,
skb->len, 0,
@@ -3294,7 +3456,7 @@ static int wl1271_ap_set_probe_resp_tmpl_legacy(struct wl1271 *wl,
/* no need to change probe response if the SSID is set correctly */
if (wlvif->ssid_len > 0)
- return wl1271_cmd_template_set(wl,
+ return wl1271_cmd_template_set(wl, wlvif->role_id,
CMD_TEMPL_AP_PROBE_RESPONSE,
probe_rsp_data,
probe_rsp_len, 0,
@@ -3331,7 +3493,7 @@ static int wl1271_ap_set_probe_resp_tmpl_legacy(struct wl1271 *wl,
ptr, probe_rsp_len - (ptr - probe_rsp_data));
templ_len += probe_rsp_len - (ptr - probe_rsp_data);
- return wl1271_cmd_template_set(wl,
+ return wl1271_cmd_template_set(wl, wlvif->role_id,
CMD_TEMPL_AP_PROBE_RESPONSE,
probe_rsp_templ,
templ_len, 0,
@@ -3428,7 +3590,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
CMD_TEMPL_BEACON;
- ret = wl1271_cmd_template_set(wl, tmpl_id,
+ ret = wl1271_cmd_template_set(wl, wlvif->role_id, tmpl_id,
beacon->data,
beacon->len, 0,
min_rate);
@@ -3467,7 +3629,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
beacon->len,
min_rate);
else
- ret = wl1271_cmd_template_set(wl,
+ ret = wl1271_cmd_template_set(wl, wlvif->role_id,
CMD_TEMPL_PROBE_RESPONSE,
beacon->data,
beacon->len, 0,
@@ -3592,10 +3754,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
ibss_joined = true;
} else {
if (test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED,
- &wlvif->flags)) {
+ &wlvif->flags))
wl1271_unjoin(wl, wlvif);
- wl12xx_start_dev(wl, wlvif);
- }
}
}
@@ -3613,7 +3773,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
do_join = true;
}
- if (changed & BSS_CHANGED_IDLE) {
+ if (changed & BSS_CHANGED_IDLE && !is_ibss) {
ret = wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle);
if (ret < 0)
wl1271_warning("idle mode change failed %d", ret);
@@ -3631,7 +3791,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
wlvif->rssi_thold = bss_conf->cqm_rssi_thold;
}
- if (changed & BSS_CHANGED_BSSID)
+ if (changed & BSS_CHANGED_BSSID &&
+ (is_ibss || bss_conf->assoc))
if (!is_zero_ether_addr(bss_conf->bssid)) {
ret = wl12xx_cmd_build_null_data(wl, wlvif);
if (ret < 0)
@@ -3668,10 +3829,9 @@ sta_not_found:
u32 rates;
int ieoffset;
wlvif->aid = bss_conf->aid;
+ wlvif->beacon_int = bss_conf->beacon_int;
set_assoc = true;
- wlvif->ps_poll_failures = 0;
-
/*
* use basic rates from AP, and determine lowest rate
* to use with control frames.
@@ -3731,9 +3891,6 @@ sta_not_found:
dev_kfree_skb(wlvif->probereq);
wlvif->probereq = NULL;
- /* re-enable dynamic ps - just in case */
- ieee80211_enable_dyn_ps(vif);
-
/* revert back to minimum rates for the current band */
wl1271_set_band_rate(wl, wlvif);
wlvif->basic_rate =
@@ -3753,7 +3910,6 @@ sta_not_found:
/* restore the bssid filter and go to dummy bssid */
if (was_assoc) {
- u32 conf_flags = wl->hw->conf.flags;
/*
* we might have to disable roc, if there was
* no IF_OPER_UP notification.
@@ -3776,7 +3932,7 @@ sta_not_found:
}
wl1271_unjoin(wl, wlvif);
- if (!(conf_flags & IEEE80211_CONF_IDLE))
+ if (!bss_conf->idle)
wl12xx_start_dev(wl, wlvif);
}
}
@@ -3807,34 +3963,6 @@ sta_not_found:
if (ret < 0)
goto out;
- if (changed & BSS_CHANGED_ARP_FILTER) {
- __be32 addr = bss_conf->arp_addr_list[0];
- WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS);
-
- if (bss_conf->arp_addr_cnt == 1 &&
- bss_conf->arp_filter_enabled) {
- /*
- * The template should have been configured only upon
- * association. however, it seems that the correct ip
- * isn't being set (when sending), so we have to
- * reconfigure the template upon every ip change.
- */
- ret = wl1271_cmd_build_arp_rsp(wl, wlvif, addr);
- if (ret < 0) {
- wl1271_warning("build arp rsp failed: %d", ret);
- goto out;
- }
-
- ret = wl1271_acx_arp_ip_filter(wl, wlvif,
- ACX_ARP_FILTER_ARP_FILTERING,
- addr);
- } else
- ret = wl1271_acx_arp_ip_filter(wl, wlvif, 0, addr);
-
- if (ret < 0)
- goto out;
- }
-
if (do_join) {
ret = wl1271_join(wl, wlvif, set_assoc);
if (ret < 0) {
@@ -3848,8 +3976,8 @@ sta_not_found:
if (ret < 0)
goto out;
- wl1271_check_operstate(wl, wlvif,
- ieee80211_get_operstate(vif));
+ if (test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags))
+ wl12xx_set_authorized(wl, wlvif);
}
/*
* stop device role if started (we might already be in
@@ -3860,19 +3988,6 @@ sta_not_found:
if (ret < 0)
goto out;
}
-
- /* If we want to go in PSM but we're not there yet */
- if (test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags) &&
- !test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) {
- enum wl1271_cmd_ps_mode mode;
-
- mode = STATION_POWER_SAVE_MODE;
- ret = wl1271_ps_set_mode(wl, wlvif, mode,
- wlvif->basic_rate,
- true);
- if (ret < 0)
- goto out;
- }
}
/* Handle new association with HT. Do this after join. */
@@ -3914,6 +4029,41 @@ sta_not_found:
}
}
+ /* Handle arp filtering. Done after join. */
+ if ((changed & BSS_CHANGED_ARP_FILTER) ||
+ (!is_ibss && (changed & BSS_CHANGED_QOS))) {
+ __be32 addr = bss_conf->arp_addr_list[0];
+ wlvif->sta.qos = bss_conf->qos;
+ WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS);
+
+ if (bss_conf->arp_addr_cnt == 1 &&
+ bss_conf->arp_filter_enabled) {
+ wlvif->ip_addr = addr;
+ /*
+ * The template should have been configured only upon
+ * association. however, it seems that the correct ip
+ * isn't being set (when sending), so we have to
+ * reconfigure the template upon every ip change.
+ */
+ ret = wl1271_cmd_build_arp_rsp(wl, wlvif);
+ if (ret < 0) {
+ wl1271_warning("build arp rsp failed: %d", ret);
+ goto out;
+ }
+
+ ret = wl1271_acx_arp_ip_filter(wl, wlvif,
+ (ACX_ARP_FILTER_ARP_FILTERING |
+ ACX_ARP_FILTER_AUTO_ARP),
+ addr);
+ } else {
+ wlvif->ip_addr = 0;
+ ret = wl1271_acx_arp_ip_filter(wl, wlvif, 0, addr);
+ }
+
+ if (ret < 0)
+ goto out;
+ }
+
out:
return;
}
@@ -4009,6 +4159,7 @@ static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw,
{
struct wl1271 *wl = hw->priv;
+ struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
u64 mactime = ULLONG_MAX;
int ret;
@@ -4023,7 +4174,7 @@ static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw,
if (ret < 0)
goto out;
- ret = wl1271_acx_tsf_info(wl, &mactime);
+ ret = wl12xx_acx_tsf_info(wl, wlvif, &mactime);
if (ret < 0)
goto out_sleep;
@@ -4085,107 +4236,155 @@ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
clear_bit(hlid, wlvif->ap.sta_hlid_map);
memset(wl->links[hlid].addr, 0, ETH_ALEN);
wl->links[hlid].ba_bitmap = 0;
- wl1271_tx_reset_link_queues(wl, hlid);
__clear_bit(hlid, &wl->ap_ps_map);
__clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
wl12xx_free_link(wl, wlvif, &hlid);
wl->active_sta_count--;
+
+ /*
+ * rearm the tx watchdog when the last STA is freed - give the FW a
+ * chance to return STA-buffered packets before complaining.
+ */
+ if (wl->active_sta_count == 0)
+ wl12xx_rearm_tx_watchdog_locked(wl);
}
-static int wl1271_op_sta_add(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
+static int wl12xx_sta_add(struct wl1271 *wl,
+ struct wl12xx_vif *wlvif,
+ struct ieee80211_sta *sta)
{
- struct wl1271 *wl = hw->priv;
- struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct wl1271_station *wl_sta;
int ret = 0;
u8 hlid;
- mutex_lock(&wl->mutex);
-
- if (unlikely(wl->state == WL1271_STATE_OFF))
- goto out;
-
- if (wlvif->bss_type != BSS_TYPE_AP_BSS)
- goto out;
-
wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);
ret = wl1271_allocate_sta(wl, wlvif, sta);
if (ret < 0)
- goto out;
+ return ret;
wl_sta = (struct wl1271_station *)sta->drv_priv;
hlid = wl_sta->hlid;
- ret = wl1271_ps_elp_wakeup(wl);
- if (ret < 0)
- goto out_free_sta;
-
ret = wl12xx_cmd_add_peer(wl, wlvif, sta, hlid);
if (ret < 0)
- goto out_sleep;
+ wl1271_free_sta(wl, wlvif, hlid);
- ret = wl12xx_cmd_set_peer_state(wl, hlid);
- if (ret < 0)
- goto out_sleep;
+ return ret;
+}
- ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true, hlid);
- if (ret < 0)
- goto out_sleep;
+static int wl12xx_sta_remove(struct wl1271 *wl,
+ struct wl12xx_vif *wlvif,
+ struct ieee80211_sta *sta)
+{
+ struct wl1271_station *wl_sta;
+ int ret = 0, id;
-out_sleep:
- wl1271_ps_elp_sleep(wl);
+ wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
+
+ wl_sta = (struct wl1271_station *)sta->drv_priv;
+ id = wl_sta->hlid;
+ if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map)))
+ return -EINVAL;
-out_free_sta:
+ ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid);
if (ret < 0)
- wl1271_free_sta(wl, wlvif, hlid);
+ return ret;
-out:
- mutex_unlock(&wl->mutex);
+ wl1271_free_sta(wl, wlvif, wl_sta->hlid);
return ret;
}
-static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_sta *sta)
+static int wl12xx_update_sta_state(struct wl1271 *wl,
+ struct wl12xx_vif *wlvif,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_state old_state,
+ enum ieee80211_sta_state new_state)
{
- struct wl1271 *wl = hw->priv;
- struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct wl1271_station *wl_sta;
- int ret = 0, id;
+ u8 hlid;
+ bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS;
+ bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
+ int ret;
- mutex_lock(&wl->mutex);
+ wl_sta = (struct wl1271_station *)sta->drv_priv;
+ hlid = wl_sta->hlid;
- if (unlikely(wl->state == WL1271_STATE_OFF))
- goto out;
+ /* Add station (AP mode) */
+ if (is_ap &&
+ old_state == IEEE80211_STA_NOTEXIST &&
+ new_state == IEEE80211_STA_NONE)
+ return wl12xx_sta_add(wl, wlvif, sta);
+
+ /* Remove station (AP mode) */
+ if (is_ap &&
+ old_state == IEEE80211_STA_NONE &&
+ new_state == IEEE80211_STA_NOTEXIST) {
+ /* must not fail */
+ wl12xx_sta_remove(wl, wlvif, sta);
+ return 0;
+ }
- if (wlvif->bss_type != BSS_TYPE_AP_BSS)
- goto out;
+ /* Authorize station (AP mode) */
+ if (is_ap &&
+ new_state == IEEE80211_STA_AUTHORIZED) {
+ ret = wl12xx_cmd_set_peer_state(wl, hlid);
+ if (ret < 0)
+ return ret;
- wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
+ ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true,
+ hlid);
+ return ret;
+ }
- wl_sta = (struct wl1271_station *)sta->drv_priv;
- id = wl_sta->hlid;
- if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map)))
+ /* Authorize station */
+ if (is_sta &&
+ new_state == IEEE80211_STA_AUTHORIZED) {
+ set_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
+ return wl12xx_set_authorized(wl, wlvif);
+ }
+
+ if (is_sta &&
+ old_state == IEEE80211_STA_AUTHORIZED &&
+ new_state == IEEE80211_STA_ASSOC) {
+ clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
+ return 0;
+ }
+
+ return 0;
+}
+
+static int wl12xx_op_sta_state(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_state old_state,
+ enum ieee80211_sta_state new_state)
+{
+ struct wl1271 *wl = hw->priv;
+ struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+ int ret;
+
+ wl1271_debug(DEBUG_MAC80211, "mac80211 sta %d state=%d->%d",
+ sta->aid, old_state, new_state);
+
+ mutex_lock(&wl->mutex);
+
+ if (unlikely(wl->state == WL1271_STATE_OFF)) {
+ ret = -EBUSY;
goto out;
+ }
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
- ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid);
- if (ret < 0)
- goto out_sleep;
+ ret = wl12xx_update_sta_state(wl, wlvif, sta, old_state, new_state);
- wl1271_free_sta(wl, wlvif, wl_sta->hlid);
-
-out_sleep:
wl1271_ps_elp_sleep(wl);
-
out:
mutex_unlock(&wl->mutex);
+ if (new_state < old_state)
+ return 0;
return ret;
}
@@ -4354,6 +4553,8 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch");
+ wl1271_tx_flush(wl);
+
mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1271_STATE_OFF)) {
@@ -4370,7 +4571,7 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
/* TODO: change mac80211 to pass vif as param */
wl12xx_for_each_wlvif_sta(wl, wlvif) {
- ret = wl12xx_cmd_channel_switch(wl, ch_switch);
+ ret = wl12xx_cmd_channel_switch(wl, wlvif, ch_switch);
if (!ret)
set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
@@ -4464,6 +4665,7 @@ static struct ieee80211_channel wl1271_channels[] = {
/* mapping to indexes for wl1271_rates */
static const u8 wl1271_rate_to_idx_2ghz[] = {
/* MCS rates are used only with 11n */
+ 7, /* CONF_HW_RXTX_RATE_MCS7_SGI */
7, /* CONF_HW_RXTX_RATE_MCS7 */
6, /* CONF_HW_RXTX_RATE_MCS6 */
5, /* CONF_HW_RXTX_RATE_MCS5 */
@@ -4585,6 +4787,7 @@ static struct ieee80211_channel wl1271_channels_5ghz[] = {
/* mapping to indexes for wl1271_rates_5ghz */
static const u8 wl1271_rate_to_idx_5ghz[] = {
/* MCS rates are used only with 11n */
+ 7, /* CONF_HW_RXTX_RATE_MCS7_SGI */
7, /* CONF_HW_RXTX_RATE_MCS7 */
6, /* CONF_HW_RXTX_RATE_MCS6 */
5, /* CONF_HW_RXTX_RATE_MCS5 */
@@ -4650,8 +4853,7 @@ static const struct ieee80211_ops wl1271_ops = {
.conf_tx = wl1271_op_conf_tx,
.get_tsf = wl1271_op_get_tsf,
.get_survey = wl1271_op_get_survey,
- .sta_add = wl1271_op_sta_add,
- .sta_remove = wl1271_op_sta_remove,
+ .sta_state = wl12xx_op_sta_state,
.ampdu_action = wl1271_op_ampdu_action,
.tx_frames_pending = wl1271_tx_frames_pending,
.set_bitrate_mask = wl12xx_set_bitrate_mask,
@@ -4825,13 +5027,120 @@ static struct bin_attribute fwlog_attr = {
.read = wl1271_sysfs_read_fwlog,
};
+static bool wl12xx_mac_in_fuse(struct wl1271 *wl)
+{
+ bool supported = false;
+ u8 major, minor;
+
+ if (wl->chip.id == CHIP_ID_1283_PG20) {
+ major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver);
+ minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver);
+
+ /* in wl128x we have the MAC address if the PG is >= (2, 1) */
+ if (major > 2 || (major == 2 && minor >= 1))
+ supported = true;
+ } else {
+ major = WL127X_PG_GET_MAJOR(wl->hw_pg_ver);
+ minor = WL127X_PG_GET_MINOR(wl->hw_pg_ver);
+
+ /* in wl127x we have the MAC address if the PG is >= (3, 1) */
+ if (major == 3 && minor >= 1)
+ supported = true;
+ }
+
+ wl1271_debug(DEBUG_PROBE,
+ "PG Ver major = %d minor = %d, MAC %s present",
+ major, minor, supported ? "is" : "is not");
+
+ return supported;
+}
+
+static void wl12xx_derive_mac_addresses(struct wl1271 *wl,
+ u32 oui, u32 nic, int n)
+{
+ int i;
+
+ wl1271_debug(DEBUG_PROBE, "base address: oui %06x nic %06x, n %d",
+ oui, nic, n);
+
+ if (nic + n - 1 > 0xffffff)
+ wl1271_warning("NIC part of the MAC address wraps around!");
+
+ for (i = 0; i < n; i++) {
+ wl->addresses[i].addr[0] = (u8)(oui >> 16);
+ wl->addresses[i].addr[1] = (u8)(oui >> 8);
+ wl->addresses[i].addr[2] = (u8) oui;
+ wl->addresses[i].addr[3] = (u8)(nic >> 16);
+ wl->addresses[i].addr[4] = (u8)(nic >> 8);
+ wl->addresses[i].addr[5] = (u8) nic;
+ nic++;
+ }
+
+ wl->hw->wiphy->n_addresses = n;
+ wl->hw->wiphy->addresses = wl->addresses;
+}
+
+static void wl12xx_get_fuse_mac(struct wl1271 *wl)
+{
+ u32 mac1, mac2;
+
+ wl1271_set_partition(wl, &wl12xx_part_table[PART_DRPW]);
+
+ mac1 = wl1271_read32(wl, WL12XX_REG_FUSE_BD_ADDR_1);
+ mac2 = wl1271_read32(wl, WL12XX_REG_FUSE_BD_ADDR_2);
+
+ /* these are the two parts of the BD_ADDR */
+ wl->fuse_oui_addr = ((mac2 & 0xffff) << 8) +
+ ((mac1 & 0xff000000) >> 24);
+ wl->fuse_nic_addr = mac1 & 0xffffff;
+
+ wl1271_set_partition(wl, &wl12xx_part_table[PART_DOWN]);
+}
+
+static int wl12xx_get_hw_info(struct wl1271 *wl)
+{
+ int ret;
+ u32 die_info;
+
+ ret = wl12xx_set_power_on(wl);
+ if (ret < 0)
+ goto out;
+
+ wl->chip.id = wl1271_read32(wl, CHIP_ID_B);
+
+ if (wl->chip.id == CHIP_ID_1283_PG20)
+ die_info = wl1271_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1);
+ else
+ die_info = wl1271_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1);
+
+ wl->hw_pg_ver = (s8) (die_info & PG_VER_MASK) >> PG_VER_OFFSET;
+
+ if (!wl12xx_mac_in_fuse(wl)) {
+ wl->fuse_oui_addr = 0;
+ wl->fuse_nic_addr = 0;
+ } else {
+ wl12xx_get_fuse_mac(wl);
+ }
+
+ wl1271_power_off(wl);
+out:
+ return ret;
+}
+
static int wl1271_register_hw(struct wl1271 *wl)
{
int ret;
+ u32 oui_addr = 0, nic_addr = 0;
if (wl->mac80211_registered)
return 0;
+ ret = wl12xx_get_hw_info(wl);
+ if (ret < 0) {
+ wl1271_error("couldn't get hw info");
+ goto out;
+ }
+
ret = wl1271_fetch_nvs(wl);
if (ret == 0) {
/* NOTE: The wl->nvs->nvs element must be first, in
@@ -4840,39 +5149,42 @@ static int wl1271_register_hw(struct wl1271 *wl)
*/
u8 *nvs_ptr = (u8 *)wl->nvs;
- wl->mac_addr[0] = nvs_ptr[11];
- wl->mac_addr[1] = nvs_ptr[10];
- wl->mac_addr[2] = nvs_ptr[6];
- wl->mac_addr[3] = nvs_ptr[5];
- wl->mac_addr[4] = nvs_ptr[4];
- wl->mac_addr[5] = nvs_ptr[3];
+ oui_addr =
+ (nvs_ptr[11] << 16) + (nvs_ptr[10] << 8) + nvs_ptr[6];
+ nic_addr =
+ (nvs_ptr[5] << 16) + (nvs_ptr[4] << 8) + nvs_ptr[3];
}
- SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
+ /* if the MAC address is zeroed in the NVS derive from fuse */
+ if (oui_addr == 0 && nic_addr == 0) {
+ oui_addr = wl->fuse_oui_addr;
+ /* fuse has the BD_ADDR, the WLAN addresses are the next two */
+ nic_addr = wl->fuse_nic_addr + 1;
+ }
+
+ wl12xx_derive_mac_addresses(wl, oui_addr, nic_addr, 2);
ret = ieee80211_register_hw(wl->hw);
if (ret < 0) {
wl1271_error("unable to register mac80211 hw: %d", ret);
- return ret;
+ goto out;
}
wl->mac80211_registered = true;
wl1271_debugfs_init(wl);
- register_netdevice_notifier(&wl1271_dev_notifier);
-
wl1271_notice("loaded");
- return 0;
+out:
+ return ret;
}
static void wl1271_unregister_hw(struct wl1271 *wl)
{
- if (wl->state == WL1271_STATE_PLT)
- __wl1271_plt_stop(wl);
+ if (wl->plt)
+ wl1271_plt_stop(wl);
- unregister_netdevice_notifier(&wl1271_dev_notifier);
ieee80211_unregister_hw(wl->hw);
wl->mac80211_registered = false;
@@ -4889,7 +5201,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
};
/* The tx descriptor buffer and the TKIP space. */
- wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
+ wl->hw->extra_tx_headroom = WL1271_EXTRA_SPACE_TKIP +
sizeof(struct wl1271_tx_hw_descr);
/* unit us */
@@ -4898,17 +5210,17 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
- IEEE80211_HW_BEACON_FILTER |
IEEE80211_HW_SUPPORTS_PS |
+ IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
IEEE80211_HW_SUPPORTS_UAPSD |
IEEE80211_HW_HAS_RATE_CONTROL |
IEEE80211_HW_CONNECTION_MONITOR |
- IEEE80211_HW_SUPPORTS_CQM_RSSI |
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_SPECTRUM_MGMT |
IEEE80211_HW_AP_LINK_PS |
IEEE80211_HW_AMPDU_AGGREGATION |
- IEEE80211_HW_TX_AMPDU_SETUP_IN_HW;
+ IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
+ IEEE80211_HW_SCAN_WHILE_IDLE;
wl->hw->wiphy->cipher_suites = cipher_suites;
wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
@@ -4924,10 +5236,10 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
* should be the maximum length possible for a template, without
* the IEEE80211 header of the template
*/
- wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_DFLT_SIZE -
+ wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
sizeof(struct ieee80211_header);
- wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_DFLT_SIZE -
+ wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
sizeof(struct ieee80211_header);
wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
@@ -4993,7 +5305,6 @@ static struct ieee80211_hw *wl1271_alloc_hw(void)
wl = hw->priv;
memset(wl, 0, sizeof(*wl));
- INIT_LIST_HEAD(&wl->list);
INIT_LIST_HEAD(&wl->wlvif_list);
wl->hw = hw;
@@ -5010,6 +5321,7 @@ static struct ieee80211_hw *wl1271_alloc_hw(void)
INIT_WORK(&wl->tx_work, wl1271_tx_work);
INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
+ INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work);
wl->freezable_wq = create_freezable_workqueue("wl12xx_wq");
if (!wl->freezable_wq) {
@@ -5021,7 +5333,6 @@ static struct ieee80211_hw *wl1271_alloc_hw(void)
wl->rx_counter = 0;
wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
wl->band = IEEE80211_BAND_2GHZ;
- wl->vif = NULL;
wl->flags = 0;
wl->sg_enabled = true;
wl->hw_pg_ver = -1;
@@ -5046,6 +5357,7 @@ static struct ieee80211_hw *wl1271_alloc_hw(void)
spin_lock_init(&wl->wl_lock);
wl->state = WL1271_STATE_OFF;
+ wl->fw_type = WL12XX_FW_TYPE_NONE;
mutex_init(&wl->mutex);
/* Apply default driver configuration. */
@@ -5113,6 +5425,7 @@ static int wl1271_free_hw(struct wl1271 *wl)
vfree(wl->fw);
wl->fw = NULL;
+ wl->fw_type = WL12XX_FW_TYPE_NONE;
kfree(wl->nvs);
wl->nvs = NULL;
@@ -5299,7 +5612,7 @@ module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(debug_level, "wl12xx debugging level");
module_param_named(fwlog, fwlog_param, charp, 0);
-MODULE_PARM_DESC(keymap,
+MODULE_PARM_DESC(fwlog,
"FW logger options: continuous, ondemand, dbgpins or disable");
module_param(bug_on_recovery, bool, S_IRUSR | S_IWUSR);
diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c
index a2bdacdd7e1d..78f598b4f97b 100644
--- a/drivers/net/wireless/wl12xx/ps.c
+++ b/drivers/net/wireless/wl12xx/ps.c
@@ -56,7 +56,7 @@ void wl1271_elp_work(struct work_struct *work)
if (wlvif->bss_type == BSS_TYPE_AP_BSS)
goto out;
- if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags) &&
+ if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) &&
test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
goto out;
}
@@ -69,8 +69,6 @@ out:
mutex_unlock(&wl->mutex);
}
-#define ELP_ENTRY_DELAY 5
-
/* Routines to toggle sleep mode while in ELP */
void wl1271_ps_elp_sleep(struct wl1271 *wl)
{
@@ -84,13 +82,13 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl)
if (wlvif->bss_type == BSS_TYPE_AP_BSS)
return;
- if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags) &&
+ if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) &&
test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
return;
}
ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
- msecs_to_jiffies(ELP_ENTRY_DELAY));
+ msecs_to_jiffies(wl->conf.conn.dynamic_ps_timeout));
}
int wl1271_ps_elp_wakeup(struct wl1271 *wl)
@@ -160,28 +158,39 @@ out:
}
int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
- enum wl1271_cmd_ps_mode mode, u32 rates, bool send)
+ enum wl1271_cmd_ps_mode mode)
{
int ret;
+ u16 timeout = wl->conf.conn.dynamic_ps_timeout;
switch (mode) {
+ case STATION_AUTO_PS_MODE:
case STATION_POWER_SAVE_MODE:
- wl1271_debug(DEBUG_PSM, "entering psm");
+ wl1271_debug(DEBUG_PSM, "entering psm (mode=%d,timeout=%u)",
+ mode, timeout);
- ret = wl1271_acx_wake_up_conditions(wl, wlvif);
+ ret = wl1271_acx_wake_up_conditions(wl, wlvif,
+ wl->conf.conn.wake_up_event,
+ wl->conf.conn.listen_interval);
if (ret < 0) {
wl1271_error("couldn't set wake up conditions");
return ret;
}
- ret = wl1271_cmd_ps_mode(wl, wlvif, STATION_POWER_SAVE_MODE);
+ ret = wl1271_cmd_ps_mode(wl, wlvif, mode, timeout);
if (ret < 0)
return ret;
- set_bit(WLVIF_FLAG_PSM, &wlvif->flags);
+ set_bit(WLVIF_FLAG_IN_PS, &wlvif->flags);
+
+ /* enable beacon early termination. Not relevant for 5GHz */
+ if (wlvif->band == IEEE80211_BAND_2GHZ) {
+ ret = wl1271_acx_bet_enable(wl, wlvif, true);
+ if (ret < 0)
+ return ret;
+ }
break;
case STATION_ACTIVE_MODE:
- default:
wl1271_debug(DEBUG_PSM, "leaving psm");
/* disable beacon early termination */
@@ -191,12 +200,15 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
return ret;
}
- ret = wl1271_cmd_ps_mode(wl, wlvif, STATION_ACTIVE_MODE);
+ ret = wl1271_cmd_ps_mode(wl, wlvif, mode, 0);
if (ret < 0)
return ret;
- clear_bit(WLVIF_FLAG_PSM, &wlvif->flags);
+ clear_bit(WLVIF_FLAG_IN_PS, &wlvif->flags);
break;
+ default:
+ wl1271_warning("trying to set ps to unsupported mode %d", mode);
+ ret = -EINVAL;
}
return ret;
diff --git a/drivers/net/wireless/wl12xx/ps.h b/drivers/net/wireless/wl12xx/ps.h
index a12052f02026..5f19d4fbbf27 100644
--- a/drivers/net/wireless/wl12xx/ps.h
+++ b/drivers/net/wireless/wl12xx/ps.h
@@ -28,7 +28,7 @@
#include "acx.h"
int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
- enum wl1271_cmd_ps_mode mode, u32 rates, bool send);
+ enum wl1271_cmd_ps_mode mode);
void wl1271_ps_elp_sleep(struct wl1271 *wl);
int wl1271_ps_elp_wakeup(struct wl1271 *wl);
void wl1271_elp_work(struct work_struct *work);
diff --git a/drivers/net/wireless/wl12xx/reg.h b/drivers/net/wireless/wl12xx/reg.h
index df34d5977b98..340db324bc26 100644
--- a/drivers/net/wireless/wl12xx/reg.h
+++ b/drivers/net/wireless/wl12xx/reg.h
@@ -525,4 +525,31 @@ b12-b0 - Supported Rate indicator bits as defined below.
*/
#define INTR_TRIG_TX_PROC1 BIT(18)
+#define WL127X_REG_FUSE_DATA_2_1 0x050a
+#define WL128X_REG_FUSE_DATA_2_1 0x2152
+#define PG_VER_MASK 0x3c
+#define PG_VER_OFFSET 2
+
+#define WL127X_PG_MAJOR_VER_MASK 0x3
+#define WL127X_PG_MAJOR_VER_OFFSET 0x0
+#define WL127X_PG_MINOR_VER_MASK 0xc
+#define WL127X_PG_MINOR_VER_OFFSET 0x2
+
+#define WL128X_PG_MAJOR_VER_MASK 0xc
+#define WL128X_PG_MAJOR_VER_OFFSET 0x2
+#define WL128X_PG_MINOR_VER_MASK 0x3
+#define WL128X_PG_MINOR_VER_OFFSET 0x0
+
+#define WL127X_PG_GET_MAJOR(pg_ver) ((pg_ver & WL127X_PG_MAJOR_VER_MASK) >> \
+ WL127X_PG_MAJOR_VER_OFFSET)
+#define WL127X_PG_GET_MINOR(pg_ver) ((pg_ver & WL127X_PG_MINOR_VER_MASK) >> \
+ WL127X_PG_MINOR_VER_OFFSET)
+#define WL128X_PG_GET_MAJOR(pg_ver) ((pg_ver & WL128X_PG_MAJOR_VER_MASK) >> \
+ WL128X_PG_MAJOR_VER_OFFSET)
+#define WL128X_PG_GET_MINOR(pg_ver) ((pg_ver & WL128X_PG_MINOR_VER_MASK) >> \
+ WL128X_PG_MINOR_VER_OFFSET)
+
+#define WL12XX_REG_FUSE_BD_ADDR_1 0x00310eb4
+#define WL12XX_REG_FUSE_BD_ADDR_2 0x00310eb8
+
#endif
diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c
index 4fbd2a722ffa..cfa6071704c5 100644
--- a/drivers/net/wireless/wl12xx/rx.c
+++ b/drivers/net/wireless/wl12xx/rx.c
@@ -113,7 +113,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
* In PLT mode we seem to get frames and mac80211 warns about them,
* workaround this by not retrieving them at all.
*/
- if (unlikely(wl->state == WL1271_STATE_PLT))
+ if (unlikely(wl->plt))
return -EINVAL;
/* the data read starts with the descriptor */
diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c
index e24111ececc5..fcba055ef196 100644
--- a/drivers/net/wireless/wl12xx/scan.c
+++ b/drivers/net/wireless/wl12xx/scan.c
@@ -38,7 +38,6 @@ void wl1271_scan_complete_work(struct work_struct *work)
struct ieee80211_vif *vif;
struct wl12xx_vif *wlvif;
int ret;
- bool is_sta, is_ibss;
dwork = container_of(work, struct delayed_work, work);
wl = container_of(dwork, struct wl1271, scan_complete_work);
@@ -56,6 +55,12 @@ void wl1271_scan_complete_work(struct work_struct *work)
vif = wl->scan_vif;
wlvif = wl12xx_vif_to_data(vif);
+ /*
+ * Rearm the tx watchdog just before idling scan. This
+ * prevents just-finished scans from triggering the watchdog
+ */
+ wl12xx_rearm_tx_watchdog_locked(wl);
+
wl->scan.state = WL1271_SCAN_STATE_IDLE;
memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
wl->scan.req = NULL;
@@ -70,15 +75,6 @@ void wl1271_scan_complete_work(struct work_struct *work)
wl1271_cmd_build_ap_probe_req(wl, wlvif, wlvif->probereq);
}
- /* return to ROC if needed */
- is_sta = (wlvif->bss_type == BSS_TYPE_STA_BSS);
- is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
- if (((is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) ||
- (is_ibss && !test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags))) &&
- !test_bit(wlvif->dev_role_id, wl->roc_map)) {
- /* restore remain on channel */
- wl12xx_start_dev(wl, wlvif);
- }
wl1271_ps_elp_sleep(wl);
if (wl->scan.failed) {
@@ -182,14 +178,23 @@ static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif,
goto out;
}
+ if (wl->conf.scan.split_scan_timeout)
+ scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN;
+
if (passive)
scan_options |= WL1271_SCAN_OPT_PASSIVE;
- if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID)) {
+ if (wlvif->bss_type == BSS_TYPE_AP_BSS ||
+ test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+ cmd->params.role_id = wlvif->role_id;
+ else
+ cmd->params.role_id = wlvif->dev_role_id;
+
+ if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) {
ret = -EINVAL;
goto out;
}
- cmd->params.role_id = wlvif->role_id;
+
cmd->params.scan_options = cpu_to_le16(scan_options);
cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
@@ -202,7 +207,7 @@ static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif,
cmd->params.tx_rate = cpu_to_le32(basic_rate);
cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs;
- cmd->params.tid_trigger = 0;
+ cmd->params.tid_trigger = CONF_TX_AC_ANY_TID;
cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
if (band == IEEE80211_BAND_2GHZ)
@@ -217,16 +222,17 @@ static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif,
memcpy(cmd->addr, vif->addr, ETH_ALEN);
- ret = wl1271_cmd_build_probe_req(wl, wlvif, wl->scan.ssid,
- wl->scan.ssid_len, wl->scan.req->ie,
- wl->scan.req->ie_len, band);
+ ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+ cmd->params.role_id, band,
+ wl->scan.ssid, wl->scan.ssid_len,
+ wl->scan.req->ie,
+ wl->scan.req->ie_len);
if (ret < 0) {
wl1271_error("PROBE request template failed");
goto out;
}
- /* disable the timeout */
- trigger->timeout = 0;
+ trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout);
ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger,
sizeof(*trigger), 0);
if (ret < 0) {
@@ -658,11 +664,13 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
}
if (!force_passive && cfg->active[0]) {
- ret = wl1271_cmd_build_probe_req(wl, wlvif, req->ssids[0].ssid,
+ u8 band = IEEE80211_BAND_2GHZ;
+ ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+ wlvif->dev_role_id, band,
+ req->ssids[0].ssid,
req->ssids[0].ssid_len,
- ies->ie[IEEE80211_BAND_2GHZ],
- ies->len[IEEE80211_BAND_2GHZ],
- IEEE80211_BAND_2GHZ);
+ ies->ie[band],
+ ies->len[band]);
if (ret < 0) {
wl1271_error("2.4GHz PROBE request template failed");
goto out;
@@ -670,11 +678,13 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
}
if (!force_passive && cfg->active[1]) {
- ret = wl1271_cmd_build_probe_req(wl, wlvif, req->ssids[0].ssid,
+ u8 band = IEEE80211_BAND_5GHZ;
+ ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+ wlvif->dev_role_id, band,
+ req->ssids[0].ssid,
req->ssids[0].ssid_len,
- ies->ie[IEEE80211_BAND_5GHZ],
- ies->len[IEEE80211_BAND_5GHZ],
- IEEE80211_BAND_5GHZ);
+ ies->ie[band],
+ ies->len[band]);
if (ret < 0) {
wl1271_error("5GHz PROBE request template failed");
goto out;
diff --git a/drivers/net/wireless/wl12xx/scan.h b/drivers/net/wireless/wl12xx/scan.h
index a7ed43dc08c9..96ff457a3a0b 100644
--- a/drivers/net/wireless/wl12xx/scan.h
+++ b/drivers/net/wireless/wl12xx/scan.h
@@ -48,7 +48,7 @@ void wl1271_scan_sched_scan_results(struct wl1271 *wl);
#define WL1271_SCAN_CURRENT_TX_PWR 0
#define WL1271_SCAN_OPT_ACTIVE 0
#define WL1271_SCAN_OPT_PASSIVE 1
-#define WL1271_SCAN_OPT_TRIGGERED_SCAN 2
+#define WL1271_SCAN_OPT_SPLIT_SCAN 2
#define WL1271_SCAN_OPT_PRIORITY_HIGH 4
/* scan even if we fail to enter psm */
#define WL1271_SCAN_OPT_FORCE 8
diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c
index 468a50553fac..4b3c32774bae 100644
--- a/drivers/net/wireless/wl12xx/sdio.c
+++ b/drivers/net/wireless/wl12xx/sdio.c
@@ -74,6 +74,8 @@ static void wl12xx_sdio_raw_read(struct device *child, int addr, void *buf,
struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
struct sdio_func *func = dev_to_sdio_func(glue->dev);
+ sdio_claim_host(func);
+
if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
dev_dbg(child->parent, "sdio read 52 addr 0x%x, byte 0x%02x\n",
@@ -88,6 +90,8 @@ static void wl12xx_sdio_raw_read(struct device *child, int addr, void *buf,
addr, len);
}
+ sdio_release_host(func);
+
if (ret)
dev_err(child->parent, "sdio read failed (%d)\n", ret);
}
@@ -99,6 +103,8 @@ static void wl12xx_sdio_raw_write(struct device *child, int addr, void *buf,
struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent);
struct sdio_func *func = dev_to_sdio_func(glue->dev);
+ sdio_claim_host(func);
+
if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
dev_dbg(child->parent, "sdio write 52 addr 0x%x, byte 0x%02x\n",
@@ -113,6 +119,8 @@ static void wl12xx_sdio_raw_write(struct device *child, int addr, void *buf,
ret = sdio_memcpy_toio(func, addr, buf, len);
}
+ sdio_release_host(func);
+
if (ret)
dev_err(child->parent, "sdio write failed (%d)\n", ret);
}
@@ -136,6 +144,7 @@ static int wl12xx_sdio_power_on(struct wl12xx_sdio_glue *glue)
sdio_claim_host(func);
sdio_enable_func(func);
+ sdio_release_host(func);
out:
return ret;
@@ -146,6 +155,7 @@ static int wl12xx_sdio_power_off(struct wl12xx_sdio_glue *glue)
int ret;
struct sdio_func *func = dev_to_sdio_func(glue->dev);
+ sdio_claim_host(func);
sdio_disable_func(func);
sdio_release_host(func);
@@ -314,9 +324,6 @@ static int wl1271_suspend(struct device *dev)
dev_err(dev, "error while trying to keep power\n");
goto out;
}
-
- /* release host */
- sdio_release_host(func);
}
out:
return ret;
@@ -324,15 +331,7 @@ out:
static int wl1271_resume(struct device *dev)
{
- struct sdio_func *func = dev_to_sdio_func(dev);
- struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func);
- struct wl1271 *wl = platform_get_drvdata(glue->core);
-
dev_dbg(dev, "wl1271 resume\n");
- if (wl->wow_enabled) {
- /* claim back host */
- sdio_claim_host(func);
- }
return 0;
}
@@ -371,5 +370,9 @@ module_exit(wl1271_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
-MODULE_FIRMWARE(WL127X_FW_NAME);
-MODULE_FIRMWARE(WL128X_FW_NAME);
+MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE);
+MODULE_FIRMWARE(WL127X_FW_NAME_MULTI);
+MODULE_FIRMWARE(WL127X_PLT_FW_NAME);
+MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE);
+MODULE_FIRMWARE(WL128X_FW_NAME_MULTI);
+MODULE_FIRMWARE(WL128X_PLT_FW_NAME);
diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c
index 92caa7ce6053..2fc18a8dcce8 100644
--- a/drivers/net/wireless/wl12xx/spi.c
+++ b/drivers/net/wireless/wl12xx/spi.c
@@ -433,6 +433,10 @@ module_exit(wl1271_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
-MODULE_FIRMWARE(WL127X_FW_NAME);
-MODULE_FIRMWARE(WL128X_FW_NAME);
+MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE);
+MODULE_FIRMWARE(WL127X_FW_NAME_MULTI);
+MODULE_FIRMWARE(WL127X_PLT_FW_NAME);
+MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE);
+MODULE_FIRMWARE(WL128X_FW_NAME_MULTI);
+MODULE_FIRMWARE(WL128X_PLT_FW_NAME);
MODULE_ALIAS("spi:wl1271");
diff --git a/drivers/net/wireless/wl12xx/testmode.c b/drivers/net/wireless/wl12xx/testmode.c
index 25093c0cb0ed..1e93bb9c0246 100644
--- a/drivers/net/wireless/wl12xx/testmode.c
+++ b/drivers/net/wireless/wl12xx/testmode.c
@@ -30,6 +30,7 @@
#include "acx.h"
#include "reg.h"
#include "ps.h"
+#include "io.h"
#define WL1271_TM_MAX_DATA_LENGTH 1024
@@ -41,6 +42,7 @@ enum wl1271_tm_commands {
WL1271_TM_CMD_NVS_PUSH, /* Not in use. Keep to not break ABI */
WL1271_TM_CMD_SET_PLT_MODE,
WL1271_TM_CMD_RECOVER,
+ WL1271_TM_CMD_GET_MAC,
__WL1271_TM_CMD_AFTER_LAST
};
@@ -264,6 +266,52 @@ static int wl1271_tm_cmd_recover(struct wl1271 *wl, struct nlattr *tb[])
return 0;
}
+static int wl12xx_tm_cmd_get_mac(struct wl1271 *wl, struct nlattr *tb[])
+{
+ struct sk_buff *skb;
+ u8 mac_addr[ETH_ALEN];
+ int ret = 0;
+
+ mutex_lock(&wl->mutex);
+
+ if (!wl->plt) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (wl->fuse_oui_addr == 0 && wl->fuse_nic_addr == 0) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ mac_addr[0] = (u8)(wl->fuse_oui_addr >> 16);
+ mac_addr[1] = (u8)(wl->fuse_oui_addr >> 8);
+ mac_addr[2] = (u8) wl->fuse_oui_addr;
+ mac_addr[3] = (u8)(wl->fuse_nic_addr >> 16);
+ mac_addr[4] = (u8)(wl->fuse_nic_addr >> 8);
+ mac_addr[5] = (u8) wl->fuse_nic_addr;
+
+ skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, ETH_ALEN);
+ if (!skb) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ NLA_PUT(skb, WL1271_TM_ATTR_DATA, ETH_ALEN, mac_addr);
+ ret = cfg80211_testmode_reply(skb);
+ if (ret < 0)
+ goto out;
+
+out:
+ mutex_unlock(&wl->mutex);
+ return ret;
+
+nla_put_failure:
+ kfree_skb(skb);
+ ret = -EMSGSIZE;
+ goto out;
+}
+
int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len)
{
struct wl1271 *wl = hw->priv;
@@ -288,6 +336,8 @@ int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len)
return wl1271_tm_cmd_set_plt_mode(wl, tb);
case WL1271_TM_CMD_RECOVER:
return wl1271_tm_cmd_recover(wl, tb);
+ case WL1271_TM_CMD_GET_MAC:
+ return wl12xx_tm_cmd_get_mac(wl, tb);
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index 4508ccd78328..43ae49143d68 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -77,35 +77,6 @@ static void wl1271_free_tx_id(struct wl1271 *wl, int id)
}
}
-static int wl1271_tx_update_filters(struct wl1271 *wl,
- struct wl12xx_vif *wlvif,
- struct sk_buff *skb)
-{
- struct ieee80211_hdr *hdr;
- int ret;
-
- hdr = (struct ieee80211_hdr *)skb->data;
-
- /*
- * stop bssid-based filtering before transmitting authentication
- * requests. this way the hw will never drop authentication
- * responses coming from BSSIDs it isn't familiar with (e.g. on
- * roaming)
- */
- if (!ieee80211_is_auth(hdr->frame_control))
- return 0;
-
- if (wlvif->dev_hlid != WL12XX_INVALID_LINK_ID)
- goto out;
-
- wl1271_debug(DEBUG_CMD, "starting device role for roaming");
- ret = wl12xx_start_dev(wl, wlvif);
- if (ret < 0)
- goto out;
-out:
- return 0;
-}
-
static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
struct sk_buff *skb)
{
@@ -187,8 +158,6 @@ u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
if (wlvif->bss_type == BSS_TYPE_AP_BSS)
return wl12xx_tx_get_hlid_ap(wl, wlvif, skb);
- wl1271_tx_update_filters(wl, wlvif, skb);
-
if ((test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) &&
!ieee80211_is_auth(hdr->frame_control) &&
@@ -257,6 +226,10 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
wl->tx_blocks_available -= total_blocks;
wl->tx_allocated_blocks += total_blocks;
+ /* If the FW was empty before, arm the Tx watchdog */
+ if (wl->tx_allocated_blocks == total_blocks)
+ wl12xx_rearm_tx_watchdog_locked(wl);
+
ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
wl->tx_allocated_pkts[ac]++;
@@ -286,16 +259,20 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
int aligned_len, ac, rate_idx;
s64 hosttime;
u16 tx_attr = 0;
+ __le16 frame_control;
+ struct ieee80211_hdr *hdr;
+ u8 *frame_start;
bool is_dummy;
desc = (struct wl1271_tx_hw_descr *) skb->data;
+ frame_start = (u8 *)(desc + 1);
+ hdr = (struct ieee80211_hdr *)(frame_start + extra);
+ frame_control = hdr->frame_control;
/* relocate space for security header */
if (extra) {
- void *framestart = skb->data + sizeof(*desc);
- u16 fc = *(u16 *)(framestart + extra);
- int hdrlen = ieee80211_hdrlen(cpu_to_le16(fc));
- memmove(framestart, framestart + extra, hdrlen);
+ int hdrlen = ieee80211_hdrlen(frame_control);
+ memmove(frame_start, hdr, hdrlen);
}
/* configure packet life time */
@@ -384,6 +361,11 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
desc->wl127x_mem.total_mem_blocks);
}
+ /* for WEP shared auth - no fw encryption is needed */
+ if (ieee80211_is_auth(frame_control) &&
+ ieee80211_has_protected(frame_control))
+ tx_attr |= TX_HW_ATTR_HOST_ENCRYPT;
+
desc->tx_attr = cpu_to_le16(tx_attr);
}
@@ -408,7 +390,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
if (info->control.hw_key &&
info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
- extra = WL1271_TKIP_IV_SPACE;
+ extra = WL1271_EXTRA_SPACE_TKIP;
if (info->control.hw_key) {
bool is_wep;
@@ -549,6 +531,7 @@ static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl,
if (skb) {
int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
spin_lock_irqsave(&wl->wl_lock, flags);
+ WARN_ON_ONCE(wl->tx_queue_count[q] <= 0);
wl->tx_queue_count[q]--;
spin_unlock_irqrestore(&wl->wl_lock, flags);
}
@@ -593,6 +576,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
struct wl12xx_vif *wlvif = wl->last_wlvif;
struct sk_buff *skb = NULL;
+ /* continue from last wlvif (round robin) */
if (wlvif) {
wl12xx_for_each_wlvif_continue(wl, wlvif) {
skb = wl12xx_vif_skb_dequeue(wl, wlvif);
@@ -603,7 +587,11 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
}
}
- /* do another pass */
+ /* dequeue from the system HLID before the restarting wlvif list */
+ if (!skb)
+ skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]);
+
+ /* do a new pass over the wlvif list */
if (!skb) {
wl12xx_for_each_wlvif(wl, wlvif) {
skb = wl12xx_vif_skb_dequeue(wl, wlvif);
@@ -611,12 +599,16 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
wl->last_wlvif = wlvif;
break;
}
+
+ /*
+ * No need to continue after last_wlvif. The previous
+ * pass should have found it.
+ */
+ if (wlvif == wl->last_wlvif)
+ break;
}
}
- if (!skb)
- skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]);
-
if (!skb &&
test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) {
int q;
@@ -624,6 +616,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
skb = wl->dummy_packet;
q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
spin_lock_irqsave(&wl->wl_lock, flags);
+ WARN_ON_ONCE(wl->tx_queue_count[q] <= 0);
wl->tx_queue_count[q]--;
spin_unlock_irqrestore(&wl->wl_lock, flags);
}
@@ -795,6 +788,18 @@ out:
mutex_unlock(&wl->mutex);
}
+static u8 wl1271_tx_get_rate_flags(u8 rate_class_index)
+{
+ u8 flags = 0;
+
+ if (rate_class_index >= CONF_HW_RXTX_RATE_MCS_MIN &&
+ rate_class_index <= CONF_HW_RXTX_RATE_MCS_MAX)
+ flags |= IEEE80211_TX_RC_MCS;
+ if (rate_class_index == CONF_HW_RXTX_RATE_MCS7_SGI)
+ flags |= IEEE80211_TX_RC_SHORT_GI;
+ return flags;
+}
+
static void wl1271_tx_complete_packet(struct wl1271 *wl,
struct wl1271_tx_hw_res_descr *result)
{
@@ -804,6 +809,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
struct sk_buff *skb;
int id = result->id;
int rate = -1;
+ u8 rate_flags = 0;
u8 retries = 0;
/* check for id legality */
@@ -830,6 +836,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
info->flags |= IEEE80211_TX_STAT_ACK;
rate = wl1271_rate_to_idx(result->rate_class_index,
wlvif->band);
+ rate_flags = wl1271_tx_get_rate_flags(result->rate_class_index);
retries = result->ack_failures;
} else if (result->status == TX_RETRY_EXCEEDED) {
wl->stats.excessive_retries++;
@@ -838,7 +845,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
info->status.rates[0].idx = rate;
info->status.rates[0].count = retries;
- info->status.rates[0].flags = 0;
+ info->status.rates[0].flags = rate_flags;
info->status.ack_signal = -1;
wl->stats.retry_count += result->ack_failures;
@@ -869,8 +876,9 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
if (info->control.hw_key &&
info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
- memmove(skb->data + WL1271_TKIP_IV_SPACE, skb->data, hdrlen);
- skb_pull(skb, WL1271_TKIP_IV_SPACE);
+ memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data,
+ hdrlen);
+ skb_pull(skb, WL1271_EXTRA_SPACE_TKIP);
}
wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x"
@@ -966,7 +974,6 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
else
wlvif->sta.ba_rx_bitmap = 0;
- wl1271_tx_reset_link_queues(wl, i);
wl->links[i].allocated_pkts = 0;
wl->links[i].prev_freed_pkts = 0;
}
@@ -980,8 +987,14 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
struct sk_buff *skb;
struct ieee80211_tx_info *info;
- for (i = 0; i < NUM_TX_QUEUES; i++)
- wl->tx_queue_count[i] = 0;
+ /* only reset the queues if something bad happened */
+ if (WARN_ON_ONCE(wl1271_tx_total_queue_count(wl) != 0)) {
+ for (i = 0; i < WL12XX_MAX_LINKS; i++)
+ wl1271_tx_reset_link_queues(wl, i);
+
+ for (i = 0; i < NUM_TX_QUEUES; i++)
+ wl->tx_queue_count[i] = 0;
+ }
wl->stopped_queues_map = 0;
@@ -1012,9 +1025,9 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
info->control.hw_key->cipher ==
WLAN_CIPHER_SUITE_TKIP) {
int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
- memmove(skb->data + WL1271_TKIP_IV_SPACE,
+ memmove(skb->data + WL1271_EXTRA_SPACE_TKIP,
skb->data, hdrlen);
- skb_pull(skb, WL1271_TKIP_IV_SPACE);
+ skb_pull(skb, WL1271_EXTRA_SPACE_TKIP);
}
info->status.rates[0].idx = -1;
@@ -1031,6 +1044,7 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
void wl1271_tx_flush(struct wl1271 *wl)
{
unsigned long timeout;
+ int i;
timeout = jiffies + usecs_to_jiffies(WL1271_TX_FLUSH_TIMEOUT);
while (!time_after(jiffies, timeout)) {
@@ -1048,6 +1062,12 @@ void wl1271_tx_flush(struct wl1271 *wl)
}
wl1271_warning("Unable to flush all TX buffers, timed out.");
+
+ /* forcibly flush all Tx buffers on our queues */
+ mutex_lock(&wl->mutex);
+ for (i = 0; i < WL12XX_MAX_LINKS; i++)
+ wl1271_tx_reset_link_queues(wl, i);
+ mutex_unlock(&wl->mutex);
}
u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set)
diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h
index 2dbb24e6d541..5cf8c32d40d1 100644
--- a/drivers/net/wireless/wl12xx/tx.h
+++ b/drivers/net/wireless/wl12xx/tx.h
@@ -39,6 +39,7 @@
#define TX_HW_ATTR_LAST_WORD_PAD (BIT(10) | BIT(11))
#define TX_HW_ATTR_TX_CMPLT_REQ BIT(12)
#define TX_HW_ATTR_TX_DUMMY_REQ BIT(13)
+#define TX_HW_ATTR_HOST_ENCRYPT BIT(14)
#define TX_HW_ATTR_OFST_SAVE_RETRIES 0
#define TX_HW_ATTR_OFST_HEADER_PAD 1
@@ -51,7 +52,9 @@
#define TX_HW_RESULT_QUEUE_LEN_MASK 0xf
#define WL1271_TX_ALIGN_TO 4
-#define WL1271_TKIP_IV_SPACE 4
+#define WL1271_EXTRA_SPACE_TKIP 4
+#define WL1271_EXTRA_SPACE_AES 8
+#define WL1271_EXTRA_SPACE_MAX 8
/* Used for management frames and dummy packets */
#define WL1271_TID_MGMT 7
@@ -224,5 +227,6 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids);
/* from main.c */
void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);
+void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl);
#endif
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index b2b09cd02022..749a15a75d38 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -35,8 +35,14 @@
#include "conf.h"
#include "ini.h"
-#define WL127X_FW_NAME "ti-connectivity/wl127x-fw-3.bin"
-#define WL128X_FW_NAME "ti-connectivity/wl128x-fw-3.bin"
+#define WL127X_FW_NAME_MULTI "ti-connectivity/wl127x-fw-4-mr.bin"
+#define WL127X_FW_NAME_SINGLE "ti-connectivity/wl127x-fw-4-sr.bin"
+
+#define WL128X_FW_NAME_MULTI "ti-connectivity/wl128x-fw-4-mr.bin"
+#define WL128X_FW_NAME_SINGLE "ti-connectivity/wl128x-fw-4-sr.bin"
+
+#define WL127X_PLT_FW_NAME "ti-connectivity/wl127x-fw-4-plt.bin"
+#define WL128X_PLT_FW_NAME "ti-connectivity/wl128x-fw-4-plt.bin"
/*
* wl127x and wl128x are using the same NVS file name. However, the
@@ -90,7 +96,13 @@
enum wl1271_state {
WL1271_STATE_OFF,
WL1271_STATE_ON,
- WL1271_STATE_PLT,
+};
+
+enum wl12xx_fw_type {
+ WL12XX_FW_TYPE_NONE,
+ WL12XX_FW_TYPE_NORMAL,
+ WL12XX_FW_TYPE_MULTI,
+ WL12XX_FW_TYPE_PLT,
};
enum wl1271_partition_type {
@@ -247,15 +259,17 @@ enum wl12xx_flags {
WL1271_FLAG_PENDING_WORK,
WL1271_FLAG_SOFT_GEMINI,
WL1271_FLAG_RECOVERY_IN_PROGRESS,
+ WL1271_FLAG_VIF_CHANGE_IN_PROGRESS,
+ WL1271_FLAG_INTENDED_FW_RECOVERY,
};
enum wl12xx_vif_flags {
WLVIF_FLAG_INITIALIZED,
WLVIF_FLAG_STA_ASSOCIATED,
+ WLVIF_FLAG_STA_AUTHORIZED,
WLVIF_FLAG_IBSS_JOINED,
WLVIF_FLAG_AP_STARTED,
- WLVIF_FLAG_PSM,
- WLVIF_FLAG_PSM_REQUESTED,
+ WLVIF_FLAG_IN_PS,
WLVIF_FLAG_STA_STATE_SENT,
WLVIF_FLAG_RX_STREAMING_STARTED,
WLVIF_FLAG_PSPOLL_FAILURE,
@@ -295,6 +309,9 @@ struct wl1271 {
spinlock_t wl_lock;
enum wl1271_state state;
+ enum wl12xx_fw_type fw_type;
+ bool plt;
+ u8 last_vif_count;
struct mutex mutex;
unsigned long flags;
@@ -313,7 +330,12 @@ struct wl1271 {
s8 hw_pg_ver;
- u8 mac_addr[ETH_ALEN];
+ /* address read from the fuse ROM */
+ u32 fuse_oui_addr;
+ u32 fuse_nic_addr;
+
+ /* we have up to 2 MAC addresses */
+ struct mac_address addresses[2];
int channel;
u8 system_hlid;
@@ -425,8 +447,6 @@ struct wl1271 {
struct wl12xx_fw_status *fw_status;
struct wl1271_tx_hw_res_if *tx_res_if;
- struct ieee80211_vif *vif;
-
/* Current chipset configuration */
struct conf_drv_settings conf;
@@ -434,8 +454,6 @@ struct wl1271 {
bool enable_11a;
- struct list_head list;
-
/* Most recently reported noise in dBm */
s8 noise;
@@ -477,6 +495,9 @@ struct wl1271 {
/* last wlvif we transmitted from */
struct wl12xx_vif *last_wlvif;
+
+ /* work to fire when Tx is stuck */
+ struct delayed_work tx_watchdog_work;
};
struct wl1271_station {
@@ -503,6 +524,8 @@ struct wl12xx_vif {
u8 basic_rate_idx;
u8 ap_rate_idx;
u8 p2p_rate_idx;
+
+ bool qos;
} sta;
struct {
u8 global_hlid;
@@ -560,12 +583,6 @@ struct wl12xx_vif {
/* Session counter for the chipset */
int session_counter;
- struct completion *ps_compl;
- struct delayed_work pspoll_work;
-
- /* counter for ps-poll delivery failures */
- int ps_poll_failures;
-
/* retry counter for PSM entries */
u8 psm_entry_retry;
@@ -575,6 +592,10 @@ struct wl12xx_vif {
int rssi_thold;
int last_rssi_event;
+ /* save the current encryption type for auto-arp config */
+ u8 encryption_type;
+ __be32 ip_addr;
+
/* RX BA constraint value */
bool ba_support;
bool ba_allowed;
diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h
index 8f0ffaf62309..22b0bc98d7b5 100644
--- a/drivers/net/wireless/wl12xx/wl12xx_80211.h
+++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h
@@ -117,7 +117,7 @@ struct wl12xx_ps_poll_template {
} __packed;
struct wl12xx_arp_rsp_template {
- struct ieee80211_hdr_3addr hdr;
+ /* not including ieee80211 header */
u8 llc_hdr[sizeof(rfc1042_header)];
__be16 llc_type;