diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx')
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; |