summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/wl12xx
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-03-20 21:04:47 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2012-03-20 21:04:47 -0700
commit3b59bf081622b6446db77ad06c93fe23677bc533 (patch)
tree3f4bb5a27c90cc86994a1f6d3c53fbf9208003cb /drivers/net/wireless/wl12xx
parente45836fafe157df137a837093037f741ad8f4c90 (diff)
parentbbdb32cb5b73597386913d052165423b9d736145 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking merge from David Miller: "1) Move ixgbe driver over to purely page based buffering on receive. From Alexander Duyck. 2) Add receive packet steering support to e1000e, from Bruce Allan. 3) Convert TCP MD5 support over to RCU, from Eric Dumazet. 4) Reduce cpu usage in handling out-of-order TCP packets on modern systems, also from Eric Dumazet. 5) Support the IP{,V6}_UNICAST_IF socket options, making the wine folks happy, from Erich Hoover. 6) Support VLAN trunking from guests in hyperv driver, from Haiyang Zhang. 7) Support byte-queue-limtis in r8169, from Igor Maravic. 8) Outline code intended for IP_RECVTOS in IP_PKTOPTIONS existed but was never properly implemented, Jiri Benc fixed that. 9) 64-bit statistics support in r8169 and 8139too, from Junchang Wang. 10) Support kernel side dump filtering by ctmark in netfilter ctnetlink, from Pablo Neira Ayuso. 11) Support byte-queue-limits in gianfar driver, from Paul Gortmaker. 12) Add new peek socket options to assist with socket migration, from Pavel Emelyanov. 13) Add sch_plug packet scheduler whose queue is controlled by userland daemons using explicit freeze and release commands. From Shriram Rajagopalan. 14) Fix FCOE checksum offload handling on transmit, from Yi Zou." * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1846 commits) Fix pppol2tp getsockname() Remove printk from rds_sendmsg ipv6: fix incorrent ipv6 ipsec packet fragment cpsw: Hook up default ndo_change_mtu. net: qmi_wwan: fix build error due to cdc-wdm dependecy netdev: driver: ethernet: Add TI CPSW driver netdev: driver: ethernet: add cpsw address lookup engine support phy: add am79c874 PHY support mlx4_core: fix race on comm channel bonding: send igmp report for its master fs_enet: Add MPC5125 FEC support and PHY interface selection net: bpf_jit: fix BPF_S_LDX_B_MSH compilation net: update the usage of CHECKSUM_UNNECESSARY fcoe: use CHECKSUM_UNNECESSARY instead of CHECKSUM_PARTIAL on tx net: do not do gso for CHECKSUM_UNNECESSARY in netif_needs_gso ixgbe: Fix issues with SR-IOV loopback when flow control is disabled net/hyperv: Fix the code handling tx busy ixgbe: fix namespace issues when FCoE/DCB is not enabled rtlwifi: Remove unused ETH_ADDR_LEN defines igbvf: Use ETH_ALEN ... Fix up fairly trivial conflicts in drivers/isdn/gigaset/interface.c and drivers/net/usb/{Kconfig,qmi_wwan.c} as per David.
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;