]> rtime.felk.cvut.cz Git - linux-imx.git/commitdiff
Merge branch 'wl12xx-next' into for-linville
authorLuciano Coelho <coelho@ti.com>
Fri, 18 Jan 2013 07:40:07 +0000 (09:40 +0200)
committerLuciano Coelho <coelho@ti.com>
Fri, 18 Jan 2013 07:59:08 +0000 (09:59 +0200)
Conflicts:
drivers/net/wireless/ti/wl12xx/main.c
drivers/net/wireless/ti/wlcore/wlcore.h

47 files changed:
drivers/net/wireless/ti/wl1251/Kconfig
drivers/net/wireless/ti/wl12xx/Makefile
drivers/net/wireless/ti/wl12xx/cmd.c
drivers/net/wireless/ti/wl12xx/cmd.h
drivers/net/wireless/ti/wl12xx/event.c [new file with mode: 0644]
drivers/net/wireless/ti/wl12xx/event.h [new file with mode: 0644]
drivers/net/wireless/ti/wl12xx/main.c
drivers/net/wireless/ti/wl12xx/scan.c [new file with mode: 0644]
drivers/net/wireless/ti/wl12xx/scan.h [new file with mode: 0644]
drivers/net/wireless/ti/wl12xx/wl12xx.h
drivers/net/wireless/ti/wl18xx/Makefile
drivers/net/wireless/ti/wl18xx/acx.c
drivers/net/wireless/ti/wl18xx/acx.h
drivers/net/wireless/ti/wl18xx/cmd.c [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/cmd.h [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/conf.h
drivers/net/wireless/ti/wl18xx/event.c [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/event.h [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wl18xx/scan.c [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/scan.h [new file with mode: 0644]
drivers/net/wireless/ti/wl18xx/tx.c
drivers/net/wireless/ti/wl18xx/wl18xx.h
drivers/net/wireless/ti/wlcore/acx.c
drivers/net/wireless/ti/wlcore/acx.h
drivers/net/wireless/ti/wlcore/boot.c
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/cmd.h
drivers/net/wireless/ti/wlcore/conf.h
drivers/net/wireless/ti/wlcore/debugfs.c
drivers/net/wireless/ti/wlcore/event.c
drivers/net/wireless/ti/wlcore/event.h
drivers/net/wireless/ti/wlcore/hw_ops.h
drivers/net/wireless/ti/wlcore/init.c
drivers/net/wireless/ti/wlcore/io.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/ps.c
drivers/net/wireless/ti/wlcore/rx.c
drivers/net/wireless/ti/wlcore/rx.h
drivers/net/wireless/ti/wlcore/scan.c
drivers/net/wireless/ti/wlcore/scan.h
drivers/net/wireless/ti/wlcore/sdio.c
drivers/net/wireless/ti/wlcore/spi.c
drivers/net/wireless/ti/wlcore/tx.c
drivers/net/wireless/ti/wlcore/tx.h
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/wireless/ti/wlcore/wlcore_i.h

index 1fb65849414f347899b40bb8fcb26c212a121d1e..8fec4ed36ac2ecae7824d00e6e79aa5b5b5880c0 100644 (file)
@@ -1,6 +1,6 @@
 menuconfig WL1251
        tristate "TI wl1251 driver support"
-       depends on MAC80211 && EXPERIMENTAL && GENERIC_HARDIRQS
+       depends on MAC80211 && GENERIC_HARDIRQS
        select FW_LOADER
        select CRC7
        ---help---
index da509aa7d0092ffb95d00936908873c4d6a4af58..e6a24056b3c87267cc124c37d65a1dec72c2ed54 100644 (file)
@@ -1,3 +1,3 @@
-wl12xx-objs    = main.o cmd.o acx.o debugfs.o
+wl12xx-objs    = main.o cmd.o acx.o debugfs.o scan.o event.o
 
 obj-$(CONFIG_WL12XX)           += wl12xx.o
index 622206241e83210e4246bbfc91f089837c6bdbb8..7dc9f965037d4189467c053f0c81f35b71a33166 100644 (file)
@@ -284,3 +284,40 @@ int wl128x_cmd_radio_parms(struct wl1271 *wl)
        kfree(radio_parms);
        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;
+       int ret;
+
+       wl1271_debug(DEBUG_ACX, "cmd channel switch");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->role_id = wlvif->role_id;
+       cmd->channel = ch_switch->channel->hw_value;
+       cmd->switch_time = ch_switch->count;
+       cmd->stop_tx = ch_switch->block_tx;
+
+       /* FIXME: control from mac80211 in the future */
+       /* Enable TX on the target channel */
+       cmd->post_switch_tx_disable = 0;
+
+       ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send channel switch command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+
+out:
+       return ret;
+}
index 140a0e8829d540050102e4dde4a0ba926420fa5d..32cbad54e993ea402647ef945bcf55ac517f4283 100644 (file)
@@ -103,10 +103,30 @@ struct wl1271_ext_radio_parms_cmd {
        u8 padding[3];
 } __packed;
 
+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;
+       /* 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;
+
 int wl1271_cmd_general_parms(struct wl1271 *wl);
 int wl128x_cmd_general_parms(struct wl1271 *wl);
 int wl1271_cmd_radio_parms(struct wl1271 *wl);
 int wl128x_cmd_radio_parms(struct wl1271 *wl);
 int wl1271_cmd_ext_radio_parms(struct wl1271 *wl);
+int wl12xx_cmd_channel_switch(struct wl1271 *wl,
+                             struct wl12xx_vif *wlvif,
+                             struct ieee80211_channel_switch *ch_switch);
 
 #endif /* __WL12XX_CMD_H__ */
diff --git a/drivers/net/wireless/ti/wl12xx/event.c b/drivers/net/wireless/ti/wl12xx/event.c
new file mode 100644 (file)
index 0000000..6ac0ed7
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "event.h"
+#include "scan.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+
+int wl12xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
+                         bool *timeout)
+{
+       u32 local_event;
+
+       switch (event) {
+       case WLCORE_EVENT_ROLE_STOP_COMPLETE:
+               local_event = ROLE_STOP_COMPLETE_EVENT_ID;
+               break;
+
+       case WLCORE_EVENT_PEER_REMOVE_COMPLETE:
+               local_event = PEER_REMOVE_COMPLETE_EVENT_ID;
+               break;
+
+       default:
+               /* event not implemented */
+               return 0;
+       }
+       return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout);
+}
+
+int wl12xx_process_mailbox_events(struct wl1271 *wl)
+{
+       struct wl12xx_event_mailbox *mbox = wl->mbox;
+       u32 vector;
+
+
+       vector = le32_to_cpu(mbox->events_vector);
+       vector &= ~(le32_to_cpu(mbox->events_mask));
+
+       wl1271_debug(DEBUG_EVENT, "MBOX vector: 0x%x", vector);
+
+       if (vector & SCAN_COMPLETE_EVENT_ID) {
+               wl1271_debug(DEBUG_EVENT, "status: 0x%x",
+                            mbox->scheduled_scan_status);
+
+               if (wl->scan_wlvif)
+                       wl12xx_scan_completed(wl, wl->scan_wlvif);
+       }
+
+       if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
+               wl1271_debug(DEBUG_EVENT,
+                            "PERIODIC_SCAN_REPORT_EVENT (status 0x%0x)",
+                            mbox->scheduled_scan_status);
+
+               wlcore_scan_sched_scan_results(wl);
+       }
+
+       if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID)
+               wlcore_event_sched_scan_completed(wl,
+                                                 mbox->scheduled_scan_status);
+       if (vector & SOFT_GEMINI_SENSE_EVENT_ID)
+               wlcore_event_soft_gemini_sense(wl,
+                                              mbox->soft_gemini_sense_info);
+
+       if (vector & BSS_LOSE_EVENT_ID)
+               wlcore_event_beacon_loss(wl, 0xff);
+
+       if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID)
+               wlcore_event_rssi_trigger(wl, mbox->rssi_snr_trigger_metric);
+
+       if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)
+               wlcore_event_ba_rx_constraint(wl,
+                                             BIT(mbox->role_id),
+                                             mbox->rx_ba_allowed);
+
+       if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID)
+               wlcore_event_channel_switch(wl, 0xff,
+                                           mbox->channel_switch_status);
+
+       if (vector & DUMMY_PACKET_EVENT_ID)
+               wlcore_event_dummy_packet(wl);
+
+       /*
+        * "TX retries exceeded" has a different meaning according to mode.
+        * In AP mode the offending station is disconnected.
+        */
+       if (vector & MAX_TX_RETRY_EVENT_ID)
+               wlcore_event_max_tx_failure(wl,
+                               le16_to_cpu(mbox->sta_tx_retry_exceeded));
+
+       if (vector & INACTIVE_STA_EVENT_ID)
+               wlcore_event_inactive_sta(wl,
+                                         le16_to_cpu(mbox->sta_aging_status));
+
+       if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID)
+               wlcore_event_roc_complete(wl);
+
+       return 0;
+}
diff --git a/drivers/net/wireless/ti/wl12xx/event.h b/drivers/net/wireless/ti/wl12xx/event.h
new file mode 100644 (file)
index 0000000..a5cc3fc
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL12XX_EVENT_H__
+#define __WL12XX_EVENT_H__
+
+#include "../wlcore/wlcore.h"
+
+enum {
+       MEASUREMENT_START_EVENT_ID               = BIT(8),
+       MEASUREMENT_COMPLETE_EVENT_ID            = BIT(9),
+       SCAN_COMPLETE_EVENT_ID                   = BIT(10),
+       WFD_DISCOVERY_COMPLETE_EVENT_ID          = BIT(11),
+       AP_DISCOVERY_COMPLETE_EVENT_ID           = BIT(12),
+       RESERVED1                                = BIT(13),
+       PSPOLL_DELIVERY_FAILURE_EVENT_ID         = BIT(14),
+       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),
+       MAX_TX_RETRY_EVENT_ID                    = BIT(20),
+       DUMMY_PACKET_EVENT_ID                    = BIT(21),
+       SOFT_GEMINI_SENSE_EVENT_ID               = BIT(22),
+       CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID        = BIT(23),
+       SOFT_GEMINI_AVALANCHE_EVENT_ID           = BIT(24),
+       PLT_RX_CALIBRATION_COMPLETE_EVENT_ID     = BIT(25),
+       INACTIVE_STA_EVENT_ID                    = BIT(26),
+       PEER_REMOVE_COMPLETE_EVENT_ID            = BIT(27),
+       PERIODIC_SCAN_COMPLETE_EVENT_ID          = BIT(28),
+       PERIODIC_SCAN_REPORT_EVENT_ID            = BIT(29),
+       BA_SESSION_RX_CONSTRAINT_EVENT_ID        = BIT(30),
+       REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID      = BIT(31),
+};
+
+struct wl12xx_event_mailbox {
+       __le32 events_vector;
+       __le32 events_mask;
+       __le32 reserved_1;
+       __le32 reserved_2;
+
+       u8 number_of_scan_results;
+       u8 scan_tag;
+       u8 completed_scan_status;
+       u8 reserved_3;
+
+       u8 soft_gemini_sense_info;
+       u8 soft_gemini_protective_info;
+       s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
+       u8 change_auto_mode_timeout;
+       u8 scheduled_scan_status;
+       u8 reserved4;
+       /* tuned channel (roc) */
+       u8 roc_channel;
+
+       __le16 hlid_removed_bitmap;
+
+       /* bitmap of aged stations (by HLID) */
+       __le16 sta_aging_status;
+
+       /* bitmap of stations (by HLID) which exceeded max tx retries */
+       __le16 sta_tx_retry_exceeded;
+
+       /* discovery completed results */
+       u8 discovery_tag;
+       u8 number_of_preq_results;
+       u8 number_of_prsp_results;
+       u8 reserved_5;
+
+       /* rx ba constraint */
+       u8 role_id; /* 0xFF means any role. */
+       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 reserved_8[9];
+} __packed;
+
+int wl12xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
+                         bool *timeout);
+int wl12xx_process_mailbox_events(struct wl1271 *wl);
+
+#endif
+
index e5f5f8f391447ce3ea74a525c5e5a30090b849c6..3254bfc81a2a7953f214d2dfd523fdd8ae6ebe8a 100644 (file)
@@ -38,6 +38,8 @@
 #include "reg.h"
 #include "cmd.h"
 #include "acx.h"
+#include "scan.h"
+#include "event.h"
 #include "debugfs.h"
 
 static char *fref_param;
@@ -208,6 +210,8 @@ static struct wlcore_conf wl12xx_conf = {
                .tmpl_short_retry_limit      = 10,
                .tmpl_long_retry_limit       = 10,
                .tx_watchdog_timeout         = 5000,
+               .slow_link_thold             = 3,
+               .fast_link_thold             = 10,
        },
        .conn = {
                .wake_up_event               = CONF_WAKE_UP_EVENT_DTIM,
@@ -265,8 +269,10 @@ static struct wlcore_conf wl12xx_conf = {
        .scan = {
                .min_dwell_time_active        = 7500,
                .max_dwell_time_active        = 30000,
-               .min_dwell_time_passive       = 100000,
-               .max_dwell_time_passive       = 100000,
+               .min_dwell_time_active_long   = 25000,
+               .max_dwell_time_active_long   = 50000,
+               .dwell_time_passive           = 100000,
+               .dwell_time_dfs               = 150000,
                .num_probe_reqs               = 2,
                .split_scan_timeout           = 50000,
        },
@@ -368,6 +374,10 @@ static struct wlcore_conf wl12xx_conf = {
                .increase_time              = 1,
                .window_size                = 16,
        },
+       .recovery = {
+               .bug_on_recovery            = 0,
+               .no_recovery                = 0,
+       },
 };
 
 static struct wl12xx_priv_conf wl12xx_default_priv_conf = {
@@ -601,9 +611,9 @@ static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
 {
        int ret;
 
-       if (wl->chip.id != CHIP_ID_1283_PG20) {
+       if (wl->chip.id != CHIP_ID_128X_PG20) {
                struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
-               struct wl127x_rx_mem_pool_addr rx_mem_addr;
+               struct wl12xx_priv *priv = wl->priv;
 
                /*
                 * Choose the block we want to read
@@ -612,13 +622,13 @@ static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
                 */
                u32 mem_block = rx_desc & RX_MEM_BLOCK_MASK;
 
-               rx_mem_addr.addr = (mem_block << 8) +
+               priv->rx_mem_addr->addr = (mem_block << 8) +
                        le32_to_cpu(wl_mem_map->packet_memory_pool_start);
 
-               rx_mem_addr.addr_extra = rx_mem_addr.addr + 4;
+               priv->rx_mem_addr->addr_extra = priv->rx_mem_addr->addr + 4;
 
-               ret = wlcore_write(wl, WL1271_SLV_REG_DATA, &rx_mem_addr,
-                                  sizeof(rx_mem_addr), false);
+               ret = wlcore_write(wl, WL1271_SLV_REG_DATA, priv->rx_mem_addr,
+                                  sizeof(*priv->rx_mem_addr), false);
                if (ret < 0)
                        return ret;
        }
@@ -631,13 +641,15 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
        int ret = 0;
 
        switch (wl->chip.id) {
-       case CHIP_ID_1271_PG10:
+       case CHIP_ID_127X_PG10:
                wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
                               wl->chip.id);
 
                wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
                              WLCORE_QUIRK_DUAL_PROBE_TMPL |
-                             WLCORE_QUIRK_TKIP_HEADER_SPACE;
+                             WLCORE_QUIRK_TKIP_HEADER_SPACE |
+                             WLCORE_QUIRK_START_STA_FAILS |
+                             WLCORE_QUIRK_AP_ZERO_SESSION_ID;
                wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
                wl->mr_fw_name = WL127X_FW_NAME_MULTI;
                memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x,
@@ -646,18 +658,22 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                /* read data preparation is only needed by wl127x */
                wl->ops->prepare_read = wl127x_prepare_read;
 
-               wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER,
-                                     WL127X_MAJOR_VER, WL127X_SUBTYPE_VER,
-                                     WL127X_MINOR_VER);
+               wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER,
+                             WL127X_IFTYPE_SR_VER,  WL127X_MAJOR_SR_VER,
+                             WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER,
+                             WL127X_IFTYPE_MR_VER,  WL127X_MAJOR_MR_VER,
+                             WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);
                break;
 
-       case CHIP_ID_1271_PG20:
+       case CHIP_ID_127X_PG20:
                wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
                             wl->chip.id);
 
                wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
                              WLCORE_QUIRK_DUAL_PROBE_TMPL |
-                             WLCORE_QUIRK_TKIP_HEADER_SPACE;
+                             WLCORE_QUIRK_TKIP_HEADER_SPACE |
+                             WLCORE_QUIRK_START_STA_FAILS |
+                             WLCORE_QUIRK_AP_ZERO_SESSION_ID;
                wl->plt_fw_name = WL127X_PLT_FW_NAME;
                wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
                wl->mr_fw_name = WL127X_FW_NAME_MULTI;
@@ -667,12 +683,14 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                /* read data preparation is only needed by wl127x */
                wl->ops->prepare_read = wl127x_prepare_read;
 
-               wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER,
-                                     WL127X_MAJOR_VER, WL127X_SUBTYPE_VER,
-                                     WL127X_MINOR_VER);
+               wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER,
+                             WL127X_IFTYPE_SR_VER,  WL127X_MAJOR_SR_VER,
+                             WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER,
+                             WL127X_IFTYPE_MR_VER,  WL127X_MAJOR_MR_VER,
+                             WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);
                break;
 
-       case CHIP_ID_1283_PG20:
+       case CHIP_ID_128X_PG20:
                wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",
                             wl->chip.id);
                wl->plt_fw_name = WL128X_PLT_FW_NAME;
@@ -682,19 +700,29 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
                /* wl128x requires TX blocksize alignment */
                wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
                              WLCORE_QUIRK_DUAL_PROBE_TMPL |
-                             WLCORE_QUIRK_TKIP_HEADER_SPACE;
-
-               wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER, WL128X_IFTYPE_VER,
-                                     WL128X_MAJOR_VER, WL128X_SUBTYPE_VER,
-                                     WL128X_MINOR_VER);
+                             WLCORE_QUIRK_TKIP_HEADER_SPACE |
+                             WLCORE_QUIRK_START_STA_FAILS |
+                             WLCORE_QUIRK_AP_ZERO_SESSION_ID;
+
+               wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER,
+                             WL128X_IFTYPE_SR_VER,  WL128X_MAJOR_SR_VER,
+                             WL128X_SUBTYPE_SR_VER, WL128X_MINOR_SR_VER,
+                             WL128X_IFTYPE_MR_VER,  WL128X_MAJOR_MR_VER,
+                             WL128X_SUBTYPE_MR_VER, WL128X_MINOR_MR_VER);
                break;
-       case CHIP_ID_1283_PG10:
+       case CHIP_ID_128X_PG10:
        default:
                wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
                ret = -ENODEV;
                goto out;
        }
 
+       /* common settings */
+       wl->scan_templ_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY;
+       wl->scan_templ_id_5 = CMD_TEMPL_APP_PROBE_REQ_5_LEGACY;
+       wl->sched_scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
+       wl->sched_scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
+       wl->max_channels_5 = WL12XX_MAX_CHANNELS_5GHZ;
 out:
        return ret;
 }
@@ -1067,7 +1095,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl)
        u32 clk;
        int selected_clock = -1;
 
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                ret = wl128x_boot_clk(wl, &selected_clock);
                if (ret < 0)
                        goto out;
@@ -1098,7 +1126,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl)
 
        wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk);
 
-       if (wl->chip.id == CHIP_ID_1283_PG20)
+       if (wl->chip.id == CHIP_ID_128X_PG20)
                clk |= ((selected_clock & 0x3) << 1) << 4;
        else
                clk |= (priv->ref_clock << 1) << 4;
@@ -1152,7 +1180,7 @@ static int wl12xx_pre_upload(struct wl1271 *wl)
        /* WL1271: The reference driver skips steps 7 to 10 (jumps directly
         * to upload_fw) */
 
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                ret = wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA);
                if (ret < 0)
                        goto out;
@@ -1219,6 +1247,23 @@ static int wl12xx_boot(struct wl1271 *wl)
        if (ret < 0)
                goto out;
 
+       wl->event_mask = BSS_LOSE_EVENT_ID |
+               REGAINED_BSS_EVENT_ID |
+               SCAN_COMPLETE_EVENT_ID |
+               ROLE_STOP_COMPLETE_EVENT_ID |
+               RSSI_SNR_TRIGGER_0_EVENT_ID |
+               PSPOLL_DELIVERY_FAILURE_EVENT_ID |
+               SOFT_GEMINI_SENSE_EVENT_ID |
+               PERIODIC_SCAN_REPORT_EVENT_ID |
+               PERIODIC_SCAN_COMPLETE_EVENT_ID |
+               DUMMY_PACKET_EVENT_ID |
+               PEER_REMOVE_COMPLETE_EVENT_ID |
+               BA_SESSION_RX_CONSTRAINT_EVENT_ID |
+               REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
+               INACTIVE_STA_EVENT_ID |
+               MAX_TX_RETRY_EVENT_ID |
+               CHANNEL_SWITCH_COMPLETE_EVENT_ID;
+
        ret = wlcore_boot_run_firmware(wl);
        if (ret < 0)
                goto out;
@@ -1261,7 +1306,7 @@ static void
 wl12xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
                          u32 blks, u32 spare_blks)
 {
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                desc->wl128x_mem.total_mem_blocks = blks;
        } else {
                desc->wl127x_mem.extra_blocks = spare_blks;
@@ -1275,7 +1320,7 @@ wl12xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,
 {
        u32 aligned_len = wlcore_calc_packet_alignment(wl, skb->len);
 
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                desc->wl128x_mem.extra_bytes = aligned_len - skb->len;
                desc->length = cpu_to_le16(aligned_len >> 2);
 
@@ -1339,7 +1384,7 @@ static int wl12xx_hw_init(struct wl1271 *wl)
 {
        int ret;
 
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE;
 
                ret = wl128x_cmd_general_parms(wl);
@@ -1394,22 +1439,6 @@ static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl,
        return wlvif->rate_set;
 }
 
-static int wl12xx_identify_fw(struct wl1271 *wl)
-{
-       unsigned int *fw_ver = wl->chip.fw_ver;
-
-       /* Only new station firmwares support routing fw logs to the host */
-       if ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) &&
-           (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_FWLOG_STA_MIN))
-               wl->quirks |= WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED;
-
-       /* This feature is not yet supported for AP mode */
-       if (fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP)
-               wl->quirks |= WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED;
-
-       return 0;
-}
-
 static void wl12xx_conf_init(struct wl1271 *wl)
 {
        struct wl12xx_priv *priv = wl->priv;
@@ -1426,7 +1455,7 @@ static bool wl12xx_mac_in_fuse(struct wl1271 *wl)
        bool supported = false;
        u8 major, minor;
 
-       if (wl->chip.id == CHIP_ID_1283_PG20) {
+       if (wl->chip.id == CHIP_ID_128X_PG20) {
                major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver);
                minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver);
 
@@ -1482,7 +1511,7 @@ static int wl12xx_get_pg_ver(struct wl1271 *wl, s8 *ver)
        u16 die_info;
        int ret;
 
-       if (wl->chip.id == CHIP_ID_1283_PG20)
+       if (wl->chip.id == CHIP_ID_128X_PG20)
                ret = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1,
                                          &die_info);
        else
@@ -1589,16 +1618,46 @@ static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
        return wlcore_set_key(wl, cmd, vif, sta, key_conf);
 }
 
+static int wl12xx_set_peer_cap(struct wl1271 *wl,
+                              struct ieee80211_sta_ht_cap *ht_cap,
+                              bool allow_ht_operation,
+                              u32 rate_set, u8 hlid)
+{
+       return wl1271_acx_set_ht_capabilities(wl, ht_cap, allow_ht_operation,
+                                             hlid);
+}
+
+static bool wl12xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
+                                struct wl1271_link *lnk)
+{
+       u8 thold;
+
+       if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map))
+               thold = wl->conf.tx.fast_link_thold;
+       else
+               thold = wl->conf.tx.slow_link_thold;
+
+       return lnk->allocated_pkts < thold;
+}
+
+static bool wl12xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
+                               struct wl1271_link *lnk)
+{
+       /* any link is good for low priority */
+       return true;
+}
+
 static int wl12xx_setup(struct wl1271 *wl);
 
 static struct wlcore_ops wl12xx_ops = {
        .setup                  = wl12xx_setup,
        .identify_chip          = wl12xx_identify_chip,
-       .identify_fw            = wl12xx_identify_fw,
        .boot                   = wl12xx_boot,
        .plt_init               = wl12xx_plt_init,
        .trigger_cmd            = wl12xx_trigger_cmd,
        .ack_event              = wl12xx_ack_event,
+       .wait_for_event         = wl12xx_wait_for_event,
+       .process_mailbox_events = wl12xx_process_mailbox_events,
        .calc_tx_blocks         = wl12xx_calc_tx_blocks,
        .set_tx_desc_blocks     = wl12xx_set_tx_desc_blocks,
        .set_tx_desc_data_len   = wl12xx_set_tx_desc_data_len,
@@ -1615,9 +1674,17 @@ static struct wlcore_ops wl12xx_ops = {
        .set_rx_csum            = NULL,
        .ap_get_mimo_wide_rate_mask = NULL,
        .debugfs_init           = wl12xx_debugfs_add_files,
+       .scan_start             = wl12xx_scan_start,
+       .scan_stop              = wl12xx_scan_stop,
+       .sched_scan_start       = wl12xx_sched_scan_start,
+       .sched_scan_stop        = wl12xx_scan_sched_scan_stop,
        .get_spare_blocks       = wl12xx_get_spare_blocks,
        .set_key                = wl12xx_set_key,
+       .channel_switch         = wl12xx_cmd_channel_switch,
        .pre_pkt_send           = NULL,
+       .set_peer_cap           = wl12xx_set_peer_cap,
+       .lnk_high_prio          = wl12xx_lnk_high_prio,
+       .lnk_low_prio           = wl12xx_lnk_low_prio,
 };
 
 static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
@@ -1641,6 +1708,7 @@ static int wl12xx_setup(struct wl1271 *wl)
        wl->rtable = wl12xx_rtable;
        wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS;
        wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS;
+       wl->num_channels = 1;
        wl->num_mac_addr = WL12XX_NUM_MAC_ADDRESSES;
        wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
        wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
@@ -1693,6 +1761,10 @@ static int wl12xx_setup(struct wl1271 *wl)
                        wl1271_error("Invalid tcxo parameter %s", tcxo_param);
        }
 
+       priv->rx_mem_addr = kmalloc(sizeof(*priv->rx_mem_addr), GFP_KERNEL);
+       if (!priv->rx_mem_addr)
+               return -ENOMEM;
+
        return 0;
 }
 
@@ -1703,7 +1775,8 @@ static int wl12xx_probe(struct platform_device *pdev)
        int ret;
 
        hw = wlcore_alloc_hw(sizeof(struct wl12xx_priv),
-                            WL12XX_AGGR_BUFFER_SIZE);
+                            WL12XX_AGGR_BUFFER_SIZE,
+                            sizeof(struct wl12xx_event_mailbox));
        if (IS_ERR(hw)) {
                wl1271_error("can't allocate hw");
                ret = PTR_ERR(hw);
@@ -1725,6 +1798,21 @@ out:
        return ret;
 }
 
+static int wl12xx_remove(struct platform_device *pdev)
+{
+       struct wl1271 *wl = platform_get_drvdata(pdev);
+       struct wl12xx_priv *priv;
+
+       if (!wl)
+               goto out;
+       priv = wl->priv;
+
+       kfree(priv->rx_mem_addr);
+
+out:
+       return wlcore_remove(pdev);
+}
+
 static const struct platform_device_id wl12xx_id_table[] = {
        { "wl12xx", 0 },
        {  } /* Terminating Entry */
@@ -1733,7 +1821,7 @@ MODULE_DEVICE_TABLE(platform, wl12xx_id_table);
 
 static struct platform_driver wl12xx_driver = {
        .probe          = wl12xx_probe,
-       .remove         = wlcore_remove,
+       .remove         = wl12xx_remove,
        .id_table       = wl12xx_id_table,
        .driver = {
                .name   = "wl12xx_driver",
diff --git a/drivers/net/wireless/ti/wl12xx/scan.c b/drivers/net/wireless/ti/wl12xx/scan.c
new file mode 100644 (file)
index 0000000..affdb3e
--- /dev/null
@@ -0,0 +1,501 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/ieee80211.h>
+#include "scan.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/tx.h"
+
+static int wl1271_get_scan_channels(struct wl1271 *wl,
+                                   struct cfg80211_scan_request *req,
+                                   struct basic_scan_channel_params *channels,
+                                   enum ieee80211_band band, bool passive)
+{
+       struct conf_scan_settings *c = &wl->conf.scan;
+       int i, j;
+       u32 flags;
+
+       for (i = 0, j = 0;
+            i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS;
+            i++) {
+               flags = req->channels[i]->flags;
+
+               if (!test_bit(i, wl->scan.scanned_ch) &&
+                   !(flags & IEEE80211_CHAN_DISABLED) &&
+                   (req->channels[i]->band == band) &&
+                   /*
+                    * In passive scans, we scan all remaining
+                    * channels, even if not marked as such.
+                    * In active scans, we only scan channels not
+                    * marked as passive.
+                    */
+                   (passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) {
+                       wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
+                                    req->channels[i]->band,
+                                    req->channels[i]->center_freq);
+                       wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
+                                    req->channels[i]->hw_value,
+                                    req->channels[i]->flags);
+                       wl1271_debug(DEBUG_SCAN,
+                                    "max_antenna_gain %d, max_power %d",
+                                    req->channels[i]->max_antenna_gain,
+                                    req->channels[i]->max_power);
+                       wl1271_debug(DEBUG_SCAN, "beacon_found %d",
+                                    req->channels[i]->beacon_found);
+
+                       if (!passive) {
+                               channels[j].min_duration =
+                                       cpu_to_le32(c->min_dwell_time_active);
+                               channels[j].max_duration =
+                                       cpu_to_le32(c->max_dwell_time_active);
+                       } else {
+                               channels[j].min_duration =
+                                       cpu_to_le32(c->dwell_time_passive);
+                               channels[j].max_duration =
+                                       cpu_to_le32(c->dwell_time_passive);
+                       }
+                       channels[j].early_termination = 0;
+                       channels[j].tx_power_att = req->channels[i]->max_power;
+                       channels[j].channel = req->channels[i]->hw_value;
+
+                       memset(&channels[j].bssid_lsb, 0xff, 4);
+                       memset(&channels[j].bssid_msb, 0xff, 2);
+
+                       /* Mark the channels we already used */
+                       set_bit(i, wl->scan.scanned_ch);
+
+                       j++;
+               }
+       }
+
+       return j;
+}
+
+#define WL1271_NOTHING_TO_SCAN 1
+
+static int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           enum ieee80211_band band,
+                           bool passive, u32 basic_rate)
+{
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+       struct wl1271_cmd_scan *cmd;
+       struct wl1271_cmd_trigger_scan_to *trigger;
+       int ret;
+       u16 scan_options = 0;
+
+       /* skip active scans if we don't have SSIDs */
+       if (!passive && wl->scan.req->n_ssids == 0)
+               return WL1271_NOTHING_TO_SCAN;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
+       if (!cmd || !trigger) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (wl->conf.scan.split_scan_timeout)
+               scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN;
+
+       if (passive)
+               scan_options |= WL1271_SCAN_OPT_PASSIVE;
+
+       cmd->params.role_id = wlvif->role_id;
+
+       if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       cmd->params.scan_options = cpu_to_le16(scan_options);
+
+       cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
+                                                   cmd->channels,
+                                                   band, passive);
+       if (cmd->params.n_ch == 0) {
+               ret = WL1271_NOTHING_TO_SCAN;
+               goto out;
+       }
+
+       cmd->params.tx_rate = cpu_to_le32(basic_rate);
+       cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs;
+       cmd->params.tid_trigger = CONF_TX_AC_ANY_TID;
+       cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
+
+       if (band == IEEE80211_BAND_2GHZ)
+               cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ;
+       else
+               cmd->params.band = WL1271_SCAN_BAND_5_GHZ;
+
+       if (wl->scan.ssid_len && wl->scan.ssid) {
+               cmd->params.ssid_len = wl->scan.ssid_len;
+               memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len);
+       }
+
+       memcpy(cmd->addr, vif->addr, ETH_ALEN);
+
+       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, false);
+       if (ret < 0) {
+               wl1271_error("PROBE request template failed");
+               goto out;
+       }
+
+       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) {
+               wl1271_error("trigger scan to failed for hw scan");
+               goto out;
+       }
+
+       wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
+
+       ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("SCAN failed");
+               goto out;
+       }
+
+out:
+       kfree(cmd);
+       kfree(trigger);
+       return ret;
+}
+
+int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       struct wl1271_cmd_header *cmd = NULL;
+       int ret = 0;
+
+       if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE))
+               return -EINVAL;
+
+       wl1271_debug(DEBUG_CMD, "cmd scan stop");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd,
+                             sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("cmd stop_scan failed");
+               goto out;
+       }
+out:
+       kfree(cmd);
+       return ret;
+}
+
+void wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       int ret = 0;
+       enum ieee80211_band band;
+       u32 rate, mask;
+
+       switch (wl->scan.state) {
+       case WL1271_SCAN_STATE_IDLE:
+               break;
+
+       case WL1271_SCAN_STATE_2GHZ_ACTIVE:
+               band = IEEE80211_BAND_2GHZ;
+               mask = wlvif->bitrate_masks[band];
+               if (wl->scan.req->no_cck) {
+                       mask &= ~CONF_TX_CCK_RATES;
+                       if (!mask)
+                               mask = CONF_TX_RATE_MASK_BASIC_P2P;
+               }
+               rate = wl1271_tx_min_rate_get(wl, mask);
+               ret = wl1271_scan_send(wl, wlvif, band, false, rate);
+               if (ret == WL1271_NOTHING_TO_SCAN) {
+                       wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE;
+                       wl1271_scan_stm(wl, wlvif);
+               }
+
+               break;
+
+       case WL1271_SCAN_STATE_2GHZ_PASSIVE:
+               band = IEEE80211_BAND_2GHZ;
+               mask = wlvif->bitrate_masks[band];
+               if (wl->scan.req->no_cck) {
+                       mask &= ~CONF_TX_CCK_RATES;
+                       if (!mask)
+                               mask = CONF_TX_RATE_MASK_BASIC_P2P;
+               }
+               rate = wl1271_tx_min_rate_get(wl, mask);
+               ret = wl1271_scan_send(wl, wlvif, band, true, rate);
+               if (ret == WL1271_NOTHING_TO_SCAN) {
+                       if (wl->enable_11a)
+                               wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE;
+                       else
+                               wl->scan.state = WL1271_SCAN_STATE_DONE;
+                       wl1271_scan_stm(wl, wlvif);
+               }
+
+               break;
+
+       case WL1271_SCAN_STATE_5GHZ_ACTIVE:
+               band = IEEE80211_BAND_5GHZ;
+               rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
+               ret = wl1271_scan_send(wl, wlvif, band, false, rate);
+               if (ret == WL1271_NOTHING_TO_SCAN) {
+                       wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE;
+                       wl1271_scan_stm(wl, wlvif);
+               }
+
+               break;
+
+       case WL1271_SCAN_STATE_5GHZ_PASSIVE:
+               band = IEEE80211_BAND_5GHZ;
+               rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
+               ret = wl1271_scan_send(wl, wlvif, band, true, rate);
+               if (ret == WL1271_NOTHING_TO_SCAN) {
+                       wl->scan.state = WL1271_SCAN_STATE_DONE;
+                       wl1271_scan_stm(wl, wlvif);
+               }
+
+               break;
+
+       case WL1271_SCAN_STATE_DONE:
+               wl->scan.failed = false;
+               cancel_delayed_work(&wl->scan_complete_work);
+               ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+                                            msecs_to_jiffies(0));
+               break;
+
+       default:
+               wl1271_error("invalid scan state");
+               break;
+       }
+
+       if (ret < 0) {
+               cancel_delayed_work(&wl->scan_complete_work);
+               ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+                                            msecs_to_jiffies(0));
+       }
+}
+
+static void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd,
+                                  struct wlcore_scan_channels *cmd_channels)
+{
+       memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive));
+       memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active));
+       cmd->dfs = cmd_channels->dfs;
+       cmd->n_pactive_ch = cmd_channels->passive_active;
+
+       memcpy(cmd->channels_2, cmd_channels->channels_2,
+              sizeof(cmd->channels_2));
+       memcpy(cmd->channels_5, cmd_channels->channels_5,
+              sizeof(cmd->channels_2));
+       /* channels_4 are not supported, so no need to copy them */
+}
+
+int wl1271_scan_sched_scan_config(struct wl1271 *wl,
+                                 struct wl12xx_vif *wlvif,
+                                 struct cfg80211_sched_scan_request *req,
+                                 struct ieee80211_sched_scan_ies *ies)
+{
+       struct wl1271_cmd_sched_scan_config *cfg = NULL;
+       struct wlcore_scan_channels *cfg_channels = NULL;
+       struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
+       int i, ret;
+       bool force_passive = !req->n_ssids;
+
+       wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
+
+       cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+       if (!cfg)
+               return -ENOMEM;
+
+       cfg->role_id = wlvif->role_id;
+       cfg->rssi_threshold = c->rssi_threshold;
+       cfg->snr_threshold  = c->snr_threshold;
+       cfg->n_probe_reqs = c->num_probe_reqs;
+       /* cycles set to 0 it means infinite (until manually stopped) */
+       cfg->cycles = 0;
+       /* report APs when at least 1 is found */
+       cfg->report_after = 1;
+       /* don't stop scanning automatically when something is found */
+       cfg->terminate = 0;
+       cfg->tag = WL1271_SCAN_DEFAULT_TAG;
+       /* don't filter on BSS type */
+       cfg->bss_type = SCAN_BSS_TYPE_ANY;
+       /* currently NL80211 supports only a single interval */
+       for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++)
+               cfg->intervals[i] = cpu_to_le32(req->interval);
+
+       cfg->ssid_len = 0;
+       ret = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req);
+       if (ret < 0)
+               goto out;
+
+       cfg->filter_type = ret;
+
+       wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type);
+
+       cfg_channels = kzalloc(sizeof(*cfg_channels), GFP_KERNEL);
+       if (!cfg_channels) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (!wlcore_set_scan_chan_params(wl, cfg_channels, req->channels,
+                                        req->n_channels, req->n_ssids,
+                                        SCAN_TYPE_PERIODIC)) {
+               wl1271_error("scan channel list is empty");
+               ret = -EINVAL;
+               goto out;
+       }
+       wl12xx_adjust_channels(cfg, cfg_channels);
+
+       if (!force_passive && cfg->active[0]) {
+               u8 band = IEEE80211_BAND_2GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                                wlvif->role_id, band,
+                                                req->ssids[0].ssid,
+                                                req->ssids[0].ssid_len,
+                                                ies->ie[band],
+                                                ies->len[band], true);
+               if (ret < 0) {
+                       wl1271_error("2.4GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       if (!force_passive && cfg->active[1]) {
+               u8 band = IEEE80211_BAND_5GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                                wlvif->role_id, band,
+                                                req->ssids[0].ssid,
+                                                req->ssids[0].ssid_len,
+                                                ies->ie[band],
+                                                ies->len[band], true);
+               if (ret < 0) {
+                       wl1271_error("5GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg));
+
+       ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg,
+                             sizeof(*cfg), 0);
+       if (ret < 0) {
+               wl1271_error("SCAN configuration failed");
+               goto out;
+       }
+out:
+       kfree(cfg_channels);
+       kfree(cfg);
+       return ret;
+}
+
+int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       struct wl1271_cmd_sched_scan_start *start;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd periodic scan start");
+
+       if (wlvif->bss_type != BSS_TYPE_STA_BSS)
+               return -EOPNOTSUPP;
+
+       if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) &&
+           test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
+               return -EBUSY;
+
+       start = kzalloc(sizeof(*start), GFP_KERNEL);
+       if (!start)
+               return -ENOMEM;
+
+       start->role_id = wlvif->role_id;
+       start->tag = WL1271_SCAN_DEFAULT_TAG;
+
+       ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start,
+                             sizeof(*start), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send scan start command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(start);
+       return ret;
+}
+
+int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif  *wlvif,
+                           struct cfg80211_sched_scan_request *req,
+                           struct ieee80211_sched_scan_ies *ies)
+{
+       int ret;
+
+       ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies);
+       if (ret < 0)
+               return ret;
+
+       return wl1271_scan_sched_scan_start(wl, wlvif);
+}
+
+void wl12xx_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif)
+{
+       struct wl1271_cmd_sched_scan_stop *stop;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
+
+       /* FIXME: what to do if alloc'ing to stop fails? */
+       stop = kzalloc(sizeof(*stop), GFP_KERNEL);
+       if (!stop) {
+               wl1271_error("failed to alloc memory to send sched scan stop");
+               return;
+       }
+
+       stop->role_id = wlvif->role_id;
+       stop->tag = WL1271_SCAN_DEFAULT_TAG;
+
+       ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop,
+                             sizeof(*stop), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send sched scan stop command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(stop);
+}
+
+int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     struct cfg80211_scan_request *req)
+{
+       wl1271_scan_stm(wl, wlvif);
+       return 0;
+}
+
+void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       wl1271_scan_stm(wl, wlvif);
+}
diff --git a/drivers/net/wireless/ti/wl12xx/scan.h b/drivers/net/wireless/ti/wl12xx/scan.h
new file mode 100644 (file)
index 0000000..264af7a
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL12XX_SCAN_H__
+#define __WL12XX_SCAN_H__
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/scan.h"
+
+#define WL12XX_MAX_CHANNELS_5GHZ 23
+
+struct basic_scan_params {
+       /* Scan option flags (WL1271_SCAN_OPT_*) */
+       __le16 scan_options;
+       u8 role_id;
+       /* Number of scan channels in the list (maximum 30) */
+       u8 n_ch;
+       /* This field indicates the number of probe requests to send
+          per channel for an active scan */
+       u8 n_probe_reqs;
+       u8 tid_trigger;
+       u8 ssid_len;
+       u8 use_ssid_list;
+
+       /* Rate bit field for sending the probes */
+       __le32 tx_rate;
+
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+       /* Band to scan */
+       u8 band;
+
+       u8 scan_tag;
+       u8 padding2[2];
+} __packed;
+
+struct basic_scan_channel_params {
+       /* Duration in TU to wait for frames on a channel for active scan */
+       __le32 min_duration;
+       __le32 max_duration;
+       __le32 bssid_lsb;
+       __le16 bssid_msb;
+       u8 early_termination;
+       u8 tx_power_att;
+       u8 channel;
+       /* FW internal use only! */
+       u8 dfs_candidate;
+       u8 activity_detected;
+       u8 pad;
+} __packed;
+
+struct wl1271_cmd_scan {
+       struct wl1271_cmd_header header;
+
+       struct basic_scan_params params;
+       struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS];
+
+       /* src mac address */
+       u8 addr[ETH_ALEN];
+       u8 padding[2];
+} __packed;
+
+struct wl1271_cmd_sched_scan_config {
+       struct wl1271_cmd_header header;
+
+       __le32 intervals[SCAN_MAX_CYCLE_INTERVALS];
+
+       s8 rssi_threshold; /* for filtering (in dBm) */
+       s8 snr_threshold;  /* for filtering (in dB) */
+
+       u8 cycles;       /* maximum number of scan cycles */
+       u8 report_after; /* report when this number of results are received */
+       u8 terminate;    /* stop scanning after reporting */
+
+       u8 tag;
+       u8 bss_type; /* for filtering */
+       u8 filter_type;
+
+       u8 ssid_len;     /* For SCAN_SSID_FILTER_SPECIFIC */
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+
+       u8 n_probe_reqs; /* Number of probes requests per channel */
+
+       u8 passive[SCAN_MAX_BANDS];
+       u8 active[SCAN_MAX_BANDS];
+
+       u8 dfs;
+
+       u8 n_pactive_ch; /* number of pactive (passive until fw detects energy)
+                           channels in BG band */
+       u8 role_id;
+       u8 padding[1];
+       struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
+       struct conn_scan_ch_params channels_5[WL12XX_MAX_CHANNELS_5GHZ];
+       struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
+} __packed;
+
+struct wl1271_cmd_sched_scan_start {
+       struct wl1271_cmd_header header;
+
+       u8 tag;
+       u8 role_id;
+       u8 padding[2];
+} __packed;
+
+struct wl1271_cmd_sched_scan_stop {
+       struct wl1271_cmd_header header;
+
+       u8 tag;
+       u8 role_id;
+       u8 padding[2];
+} __packed;
+
+int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     struct cfg80211_scan_request *req);
+int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif  *wlvif,
+                           struct cfg80211_sched_scan_request *req,
+                           struct ieee80211_sched_scan_ies *ies);
+void wl12xx_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif);
+#endif
index 7182bbf6625daac99ddcf00b3581c32ee39c2ef6..d4552857480ca92d6c72461067ec54c813c421d8 100644 (file)
 
 #include "conf.h"
 
-/* minimum FW required for driver for wl127x */
+/* WiLink 6/7 chip IDs */
+#define CHIP_ID_127X_PG10              (0x04030101)
+#define CHIP_ID_127X_PG20              (0x04030111)
+#define CHIP_ID_128X_PG10              (0x05030101)
+#define CHIP_ID_128X_PG20              (0x05030111)
+
+/* FW chip version for wl127x */
 #define WL127X_CHIP_VER                6
-#define WL127X_IFTYPE_VER      3
-#define WL127X_MAJOR_VER       10
-#define WL127X_SUBTYPE_VER     2
-#define WL127X_MINOR_VER       115
+/* minimum single-role FW version for wl127x */
+#define WL127X_IFTYPE_SR_VER   3
+#define WL127X_MAJOR_SR_VER    10
+#define WL127X_SUBTYPE_SR_VER  WLCORE_FW_VER_IGNORE
+#define WL127X_MINOR_SR_VER    115
+/* minimum multi-role FW version for wl127x */
+#define WL127X_IFTYPE_MR_VER   5
+#define WL127X_MAJOR_MR_VER    7
+#define WL127X_SUBTYPE_MR_VER  WLCORE_FW_VER_IGNORE
+#define WL127X_MINOR_MR_VER    115
 
-/* minimum FW required for driver for wl128x */
+/* FW chip version for wl128x */
 #define WL128X_CHIP_VER                7
-#define WL128X_IFTYPE_VER      3
-#define WL128X_MAJOR_VER       10
-#define WL128X_SUBTYPE_VER     2
-#define WL128X_MINOR_VER       115
+/* minimum single-role FW version for wl128x */
+#define WL128X_IFTYPE_SR_VER   3
+#define WL128X_MAJOR_SR_VER    10
+#define WL128X_SUBTYPE_SR_VER  WLCORE_FW_VER_IGNORE
+#define WL128X_MINOR_SR_VER    115
+/* minimum multi-role FW version for wl128x */
+#define WL128X_IFTYPE_MR_VER   5
+#define WL128X_MAJOR_MR_VER    7
+#define WL128X_SUBTYPE_MR_VER  WLCORE_FW_VER_IGNORE
+#define WL128X_MINOR_MR_VER    42
 
 #define WL12XX_AGGR_BUFFER_SIZE        (4 * PAGE_SIZE)
 
@@ -55,6 +73,8 @@ struct wl12xx_priv {
 
        int ref_clock;
        int tcxo_clock;
+
+       struct wl127x_rx_mem_pool_addr *rx_mem_addr;
 };
 
 #endif /* __WL12XX_PRIV_H__ */
index 67c098734c7fc2b335707c96ed05d0bc002bbab9..ae2b817357857538c4296b41d1fe8cf3bf4d2674 100644 (file)
@@ -1,3 +1,3 @@
-wl18xx-objs    = main.o acx.o tx.o io.o debugfs.o
+wl18xx-objs    = main.o acx.o tx.o io.o debugfs.o scan.o cmd.o event.o
 
 obj-$(CONFIG_WL18XX)           += wl18xx.o
index 72840e23bf5992462fed5c4266919b600cc6300d..a169bb5a5dbf7984c0cdb52390492e0d6a27fbb0 100644 (file)
@@ -75,7 +75,7 @@ int wl18xx_acx_set_checksum_state(struct wl1271 *wl)
 
        acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED;
 
-       ret = wl1271_cmd_configure(wl, ACX_CHECKSUM_CONFIG, acx, sizeof(*acx));
+       ret = wl1271_cmd_configure(wl, ACX_CSUM_CONFIG, acx, sizeof(*acx));
        if (ret < 0) {
                wl1271_warning("failed to set Tx checksum state: %d", ret);
                goto out;
@@ -109,3 +109,88 @@ out:
        kfree(acx);
        return ret;
 }
+
+int wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide)
+{
+       struct wlcore_peer_ht_operation_mode *acx;
+       int ret;
+
+       wl1271_debug(DEBUG_ACX, "acx peer ht operation mode hlid %d bw %d",
+                    hlid, wide);
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+       if (!acx) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       acx->hlid = hlid;
+       acx->bandwidth = wide ? WLCORE_BANDWIDTH_40MHZ : WLCORE_BANDWIDTH_20MHZ;
+
+       ret = wl1271_cmd_configure(wl, ACX_PEER_HT_OPERATION_MODE_CFG, acx,
+                                  sizeof(*acx));
+
+       if (ret < 0) {
+               wl1271_warning("acx peer ht operation mode failed: %d", ret);
+               goto out;
+       }
+
+out:
+       kfree(acx);
+       return ret;
+
+}
+
+/*
+ * this command is basically the same as wl1271_acx_ht_capabilities,
+ * with the addition of supported rates. they should be unified in
+ * the next fw api change
+ */
+int wl18xx_acx_set_peer_cap(struct wl1271 *wl,
+                           struct ieee80211_sta_ht_cap *ht_cap,
+                           bool allow_ht_operation,
+                           u32 rate_set, u8 hlid)
+{
+       struct wlcore_acx_peer_cap *acx;
+       int ret = 0;
+       u32 ht_capabilites = 0;
+
+       wl1271_debug(DEBUG_ACX,
+                    "acx set cap ht_supp: %d ht_cap: %d rates: 0x%x",
+                    ht_cap->ht_supported, ht_cap->cap, rate_set);
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+       if (!acx) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (allow_ht_operation && ht_cap->ht_supported) {
+               /* no need to translate capabilities - use the spec values */
+               ht_capabilites = ht_cap->cap;
+
+               /*
+                * this bit is not employed by the spec but only by FW to
+                * indicate peer HT support
+                */
+               ht_capabilites |= WL12XX_HT_CAP_HT_OPERATION;
+
+               /* get data from A-MPDU parameters field */
+               acx->ampdu_max_length = ht_cap->ampdu_factor;
+               acx->ampdu_min_spacing = ht_cap->ampdu_density;
+       }
+
+       acx->hlid = hlid;
+       acx->ht_capabilites = cpu_to_le32(ht_capabilites);
+       acx->supported_rates = cpu_to_le32(rate_set);
+
+       ret = wl1271_cmd_configure(wl, ACX_PEER_CAP, acx, sizeof(*acx));
+       if (ret < 0) {
+               wl1271_warning("acx ht capabilities setting failed: %d", ret);
+               goto out;
+       }
+
+out:
+       kfree(acx);
+       return ret;
+}
index e2609a6b7341d6048b05bc9bcee6071090da0dbc..0e636def1217b9c150f323fcbbc8bd5214bf0386 100644 (file)
 #include "../wlcore/acx.h"
 
 enum {
-       ACX_CLEAR_STATISTICS             = 0x0047,
+       ACX_NS_IPV6_FILTER               = 0x0050,
+       ACX_PEER_HT_OPERATION_MODE_CFG   = 0x0051,
+       ACX_CSUM_CONFIG                  = 0x0052,
+       ACX_SIM_CONFIG                   = 0x0053,
+       ACX_CLEAR_STATISTICS             = 0x0054,
+       ACX_AUTO_RX_STREAMING            = 0x0055,
+       ACX_PEER_CAP                     = 0x0056
 };
 
 /* numbers of bits the length field takes (add 1 for the actual number) */
@@ -278,10 +284,57 @@ struct wl18xx_acx_clear_statistics {
        struct acx_header header;
 };
 
+enum wlcore_bandwidth {
+       WLCORE_BANDWIDTH_20MHZ,
+       WLCORE_BANDWIDTH_40MHZ,
+};
+
+struct wlcore_peer_ht_operation_mode {
+       struct acx_header header;
+
+       u8 hlid;
+       u8 bandwidth; /* enum wlcore_bandwidth */
+       u8 padding[2];
+};
+
+/*
+ * ACX_PEER_CAP
+ * this struct is very similar to wl1271_acx_ht_capabilities, with the
+ * addition of supported rates
+ */
+struct wlcore_acx_peer_cap {
+       struct acx_header header;
+
+       /* bitmask of capability bits supported by the peer */
+       __le32 ht_capabilites;
+
+       /* rates supported by the remote peer */
+       __le32 supported_rates;
+
+       /* Indicates to which link these capabilities apply. */
+       u8 hlid;
+
+       /*
+        * This the maximum A-MPDU length supported by the AP. The FW may not
+        * exceed this length when sending A-MPDUs
+        */
+       u8 ampdu_max_length;
+
+       /* This is the minimal spacing required when sending A-MPDUs to the AP*/
+       u8 ampdu_min_spacing;
+
+       u8 padding;
+} __packed;
+
 int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
                                  u32 sdio_blk_size, u32 extra_mem_blks,
                                  u32 len_field_size);
 int wl18xx_acx_set_checksum_state(struct wl1271 *wl);
 int wl18xx_acx_clear_statistics(struct wl1271 *wl);
+int wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide);
+int wl18xx_acx_set_peer_cap(struct wl1271 *wl,
+                           struct ieee80211_sta_ht_cap *ht_cap,
+                           bool allow_ht_operation,
+                           u32 rate_set, u8 hlid);
 
 #endif /* __WL18XX_ACX_H__ */
diff --git a/drivers/net/wireless/ti/wl18xx/cmd.c b/drivers/net/wireless/ti/wl18xx/cmd.c
new file mode 100644 (file)
index 0000000..1d1f6cc
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+#include "../wlcore/hw_ops.h"
+
+#include "cmd.h"
+
+int wl18xx_cmd_channel_switch(struct wl1271 *wl,
+                             struct wl12xx_vif *wlvif,
+                             struct ieee80211_channel_switch *ch_switch)
+{
+       struct wl18xx_cmd_channel_switch *cmd;
+       u32 supported_rates;
+       int ret;
+
+       wl1271_debug(DEBUG_ACX, "cmd channel switch");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->role_id = wlvif->role_id;
+       cmd->channel = ch_switch->channel->hw_value;
+       cmd->switch_time = ch_switch->count;
+       cmd->stop_tx = ch_switch->block_tx;
+
+       switch (ch_switch->channel->band) {
+       case IEEE80211_BAND_2GHZ:
+               cmd->band = WLCORE_BAND_2_4GHZ;
+               break;
+       case IEEE80211_BAND_5GHZ:
+               cmd->band = WLCORE_BAND_5GHZ;
+               break;
+       default:
+               wl1271_error("invalid channel switch band: %d",
+                            ch_switch->channel->band);
+               ret = -EINVAL;
+               goto out_free;
+       }
+
+       supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES |
+                         wlcore_hw_sta_get_ap_rate_mask(wl, wlvif);
+       if (wlvif->p2p)
+               supported_rates &= ~CONF_TX_CCK_RATES;
+       cmd->local_supported_rates = cpu_to_le32(supported_rates);
+       cmd->channel_type = wlvif->channel_type;
+
+       ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send channel switch command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+out:
+       return ret;
+}
diff --git a/drivers/net/wireless/ti/wl18xx/cmd.h b/drivers/net/wireless/ti/wl18xx/cmd.h
new file mode 100644 (file)
index 0000000..6687d10
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2011 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_CMD_H__
+#define __WL18XX_CMD_H__
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/acx.h"
+
+struct wl18xx_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;
+       /* Stop the role TX, should expect it after radar detection */
+       u8 stop_tx;
+
+       __le32 local_supported_rates;
+
+       u8 channel_type;
+       u8 band;
+
+       u8 padding[2];
+} __packed;
+
+int wl18xx_cmd_channel_switch(struct wl1271 *wl,
+                             struct wl12xx_vif *wlvif,
+                             struct ieee80211_channel_switch *ch_switch);
+
+#endif
index 4d426cc2027426b1ed7941b2d0ce250befdfd778..b5f114857191591fabc9bbd06cab4b2f46f8be8c 100644 (file)
 #define __WL18XX_CONF_H__
 
 #define WL18XX_CONF_MAGIC      0x10e100ca
-#define WL18XX_CONF_VERSION    (WLCORE_CONF_VERSION | 0x0003)
+#define WL18XX_CONF_VERSION    (WLCORE_CONF_VERSION | 0x0005)
 #define WL18XX_CONF_MASK       0x0000ffff
 #define WL18XX_CONF_SIZE       (WLCORE_CONF_SIZE + \
                                 sizeof(struct wl18xx_priv_conf))
 
 #define NUM_OF_CHANNELS_11_ABG 150
 #define NUM_OF_CHANNELS_11_P 7
-#define WL18XX_NUM_OF_SUB_BANDS 9
 #define SRF_TABLE_LEN 16
 #define PIN_MUXING_SIZE 2
+#define WL18XX_TRACE_LOSS_GAPS_TX 10
+#define WL18XX_TRACE_LOSS_GAPS_RX 18
 
 struct wl18xx_mac_and_phy_params {
        u8 phy_standalone;
-       u8 rdl;
+       u8 spare0;
        u8 enable_clpc;
        u8 enable_tx_low_pwr_on_siso_rdl;
        u8 auto_detect;
@@ -69,18 +70,26 @@ struct wl18xx_mac_and_phy_params {
        u8 pwr_limit_reference_11_abg;
        u8 per_chan_pwr_limit_arr_11p[NUM_OF_CHANNELS_11_P];
        u8 pwr_limit_reference_11p;
-       u8 per_sub_band_tx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
-       u8 per_sub_band_rx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
+       u8 spare1[9];
+       u8 spare2[9];
        u8 primary_clock_setting_time;
        u8 clock_valid_on_wake_up;
        u8 secondary_clock_setting_time;
        u8 board_type;
        /* enable point saturation */
        u8 psat;
-       /* low/medium/high Tx power in dBm */
+       /* low/medium/high Tx power in dBm for STA-HP BG */
        s8 low_power_val;
        s8 med_power_val;
        s8 high_power_val;
+       s8 per_sub_band_tx_trace_loss[WL18XX_TRACE_LOSS_GAPS_TX];
+       s8 per_sub_band_rx_trace_loss[WL18XX_TRACE_LOSS_GAPS_RX];
+       u8 tx_rf_margin;
+       /* low/medium/high Tx power in dBm for other role */
+       s8 low_power_val_2nd;
+       s8 med_power_val_2nd;
+       s8 high_power_val_2nd;
+
        u8 padding[1];
 } __packed;
 
diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c
new file mode 100644 (file)
index 0000000..c9199d7
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of wl12xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "event.h"
+#include "scan.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/debug.h"
+
+int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
+                         bool *timeout)
+{
+       u32 local_event;
+
+       switch (event) {
+       case WLCORE_EVENT_PEER_REMOVE_COMPLETE:
+               local_event = PEER_REMOVE_COMPLETE_EVENT_ID;
+               break;
+
+       case WLCORE_EVENT_DFS_CONFIG_COMPLETE:
+               local_event = DFS_CHANNELS_CONFIG_COMPLETE_EVENT;
+               break;
+
+       default:
+               /* event not implemented */
+               return 0;
+       }
+       return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout);
+}
+
+int wl18xx_process_mailbox_events(struct wl1271 *wl)
+{
+       struct wl18xx_event_mailbox *mbox = wl->mbox;
+       u32 vector;
+
+       vector = le32_to_cpu(mbox->events_vector);
+       wl1271_debug(DEBUG_EVENT, "MBOX vector: 0x%x", vector);
+
+       if (vector & SCAN_COMPLETE_EVENT_ID) {
+               wl1271_debug(DEBUG_EVENT, "scan results: %d",
+                            mbox->number_of_scan_results);
+
+               if (wl->scan_wlvif)
+                       wl18xx_scan_completed(wl, wl->scan_wlvif);
+       }
+
+       if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
+               wl1271_debug(DEBUG_EVENT,
+                            "PERIODIC_SCAN_REPORT_EVENT (results %d)",
+                            mbox->number_of_sched_scan_results);
+
+               wlcore_scan_sched_scan_results(wl);
+       }
+
+       if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID)
+               wlcore_event_sched_scan_completed(wl, 1);
+
+       if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID)
+               wlcore_event_rssi_trigger(wl, mbox->rssi_snr_trigger_metric);
+
+       if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)
+               wlcore_event_ba_rx_constraint(wl,
+                               le16_to_cpu(mbox->rx_ba_role_id_bitmap),
+                               le16_to_cpu(mbox->rx_ba_allowed_bitmap));
+
+       if (vector & BSS_LOSS_EVENT_ID)
+               wlcore_event_beacon_loss(wl,
+                                        le16_to_cpu(mbox->bss_loss_bitmap));
+
+       if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID)
+               wlcore_event_channel_switch(wl,
+                       le16_to_cpu(mbox->channel_switch_role_id_bitmap),
+                       true);
+
+       if (vector & DUMMY_PACKET_EVENT_ID)
+               wlcore_event_dummy_packet(wl);
+
+       /*
+        * "TX retries exceeded" has a different meaning according to mode.
+        * In AP mode the offending station is disconnected.
+        */
+       if (vector & MAX_TX_FAILURE_EVENT_ID)
+               wlcore_event_max_tx_failure(wl,
+                               le32_to_cpu(mbox->tx_retry_exceeded_bitmap));
+
+       if (vector & INACTIVE_STA_EVENT_ID)
+               wlcore_event_inactive_sta(wl,
+                               le32_to_cpu(mbox->inactive_sta_bitmap));
+
+       if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID)
+               wlcore_event_roc_complete(wl);
+
+       return 0;
+}
diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h
new file mode 100644 (file)
index 0000000..398f3d2
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_EVENT_H__
+#define __WL18XX_EVENT_H__
+
+#include "../wlcore/wlcore.h"
+
+enum {
+       SCAN_COMPLETE_EVENT_ID                   = BIT(8),
+       RADAR_DETECTED_EVENT_ID                  = BIT(9),
+       CHANNEL_SWITCH_COMPLETE_EVENT_ID         = BIT(10),
+       BSS_LOSS_EVENT_ID                        = BIT(11),
+       MAX_TX_FAILURE_EVENT_ID                  = BIT(12),
+       DUMMY_PACKET_EVENT_ID                    = BIT(13),
+       INACTIVE_STA_EVENT_ID                    = BIT(14),
+       PEER_REMOVE_COMPLETE_EVENT_ID            = BIT(15),
+       PERIODIC_SCAN_COMPLETE_EVENT_ID          = BIT(16),
+       BA_SESSION_RX_CONSTRAINT_EVENT_ID        = BIT(17),
+       REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID      = BIT(18),
+       DFS_CHANNELS_CONFIG_COMPLETE_EVENT       = BIT(19),
+       PERIODIC_SCAN_REPORT_EVENT_ID            = BIT(20),
+};
+
+struct wl18xx_event_mailbox {
+       __le32 events_vector;
+
+       u8 number_of_scan_results;
+       u8 number_of_sched_scan_results;
+
+       __le16 channel_switch_role_id_bitmap;
+
+       s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
+
+       /* bitmap of removed links */
+       __le32 hlid_removed_bitmap;
+
+       /* rx ba constraint */
+       __le16 rx_ba_role_id_bitmap; /* 0xfff means any role. */
+       __le16 rx_ba_allowed_bitmap;
+
+       /* bitmap of roc completed (by role id) */
+       __le16 roc_completed_bitmap;
+
+       /* bitmap of stations (by role id) with bss loss */
+       __le16 bss_loss_bitmap;
+
+       /* bitmap of stations (by HLID) which exceeded max tx retries */
+       __le32 tx_retry_exceeded_bitmap;
+
+       /* bitmap of inactive stations (by HLID) */
+       __le32 inactive_sta_bitmap;
+} __packed;
+
+int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
+                         bool *timeout);
+int wl18xx_process_mailbox_events(struct wl1271 *wl);
+
+#endif
index 8d8c1f8c63b79b8775a70c85fb4b4669380afbb7..0be1cfc17a86f3878d30da87159237eacdaba33b 100644 (file)
 
 #include "reg.h"
 #include "conf.h"
+#include "cmd.h"
 #include "acx.h"
 #include "tx.h"
 #include "wl18xx.h"
 #include "io.h"
+#include "scan.h"
+#include "event.h"
 #include "debugfs.h"
 
 #define WL18XX_RX_CHECKSUM_MASK      0x40
@@ -334,6 +337,8 @@ static struct wlcore_conf wl18xx_conf = {
                .tmpl_short_retry_limit      = 10,
                .tmpl_long_retry_limit       = 10,
                .tx_watchdog_timeout         = 5000,
+               .slow_link_thold             = 3,
+               .fast_link_thold             = 30,
        },
        .conn = {
                .wake_up_event               = CONF_WAKE_UP_EVENT_DTIM,
@@ -391,8 +396,10 @@ static struct wlcore_conf wl18xx_conf = {
        .scan = {
                .min_dwell_time_active        = 7500,
                .max_dwell_time_active        = 30000,
-               .min_dwell_time_passive       = 100000,
-               .max_dwell_time_passive       = 100000,
+               .min_dwell_time_active_long   = 25000,
+               .max_dwell_time_active_long   = 50000,
+               .dwell_time_passive           = 100000,
+               .dwell_time_dfs               = 150000,
                .num_probe_reqs               = 2,
                .split_scan_timeout           = 50000,
        },
@@ -489,6 +496,10 @@ static struct wlcore_conf wl18xx_conf = {
                .increase_time              = 1,
                .window_size                = 16,
        },
+       .recovery = {
+               .bug_on_recovery            = 0,
+               .no_recovery                = 0,
+       },
 };
 
 static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
@@ -501,7 +512,6 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
                .clock_valid_on_wake_up         = 0x00,
                .secondary_clock_setting_time   = 0x05,
                .board_type                     = BOARD_TYPE_HDK_18XX,
-               .rdl                            = 0x01,
                .auto_detect                    = 0x00,
                .dedicated_fem                  = FEM_NONE,
                .low_band_component             = COMPONENT_3_WAY_SWITCH,
@@ -517,14 +527,39 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
                .enable_clpc                    = 0x00,
                .enable_tx_low_pwr_on_siso_rdl  = 0x00,
                .rx_profile                     = 0x00,
-               .pwr_limit_reference_11_abg     = 0xc8,
+               .pwr_limit_reference_11_abg     = 0x64,
+               .per_chan_pwr_limit_arr_11abg   = {
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+               .pwr_limit_reference_11p        = 0x64,
+               .per_chan_pwr_limit_arr_11p     = { 0xff, 0xff, 0xff, 0xff,
+                                                   0xff, 0xff, 0xff },
                .psat                           = 0,
-               .low_power_val                  = 0x00,
-               .med_power_val                  = 0x0a,
-               .high_power_val                 = 0x1e,
+               .low_power_val                  = 0x08,
+               .med_power_val                  = 0x12,
+               .high_power_val                 = 0x18,
+               .low_power_val_2nd              = 0x05,
+               .med_power_val_2nd              = 0x0a,
+               .high_power_val_2nd             = 0x14,
                .external_pa_dc2dc              = 0,
-               .number_of_assembled_ant2_4     = 1,
+               .number_of_assembled_ant2_4     = 2,
                .number_of_assembled_ant5       = 1,
+               .tx_rf_margin                   = 1,
        },
 };
 
@@ -595,7 +630,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = {
 };
 
 /* TODO: maybe move to a new header file? */
-#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw.bin"
+#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-2.bin"
 
 static int wl18xx_identify_chip(struct wl1271 *wl)
 {
@@ -608,15 +643,18 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
                wl->sr_fw_name = WL18XX_FW_NAME;
                /* wl18xx uses the same firmware for PLT */
                wl->plt_fw_name = WL18XX_FW_NAME;
-               wl->quirks |= WLCORE_QUIRK_NO_ELP |
-                             WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
+               wl->quirks |= WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
                              WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
                              WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN |
-                             WLCORE_QUIRK_TX_PAD_LAST_FRAME;
-
-               wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER, WL18XX_IFTYPE_VER,
-                                     WL18XX_MAJOR_VER, WL18XX_SUBTYPE_VER,
-                                     WL18XX_MINOR_VER);
+                             WLCORE_QUIRK_TX_PAD_LAST_FRAME |
+                             WLCORE_QUIRK_REGDOMAIN_CONF |
+                             WLCORE_QUIRK_DUAL_PROBE_TMPL;
+
+               wlcore_set_min_fw_ver(wl, WL18XX_CHIP_VER,
+                                     WL18XX_IFTYPE_VER,  WL18XX_MAJOR_VER,
+                                     WL18XX_SUBTYPE_VER, WL18XX_MINOR_VER,
+                                     /* there's no separate multi-role FW */
+                                     0, 0, 0, 0);
                break;
        case CHIP_ID_185x_PG10:
                wl1271_warning("chip id 0x%x (185x PG10) is deprecated",
@@ -630,6 +668,11 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
                goto out;
        }
 
+       wl->scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
+       wl->scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
+       wl->sched_scan_templ_id_2_4 = CMD_TEMPL_PROBE_REQ_2_4_PERIODIC;
+       wl->sched_scan_templ_id_5 = CMD_TEMPL_PROBE_REQ_5_PERIODIC;
+       wl->max_channels_5 = WL18XX_MAX_CHANNELS_5GHZ;
 out:
        return ret;
 }
@@ -843,6 +886,20 @@ static int wl18xx_boot(struct wl1271 *wl)
        if (ret < 0)
                goto out;
 
+       wl->event_mask = BSS_LOSS_EVENT_ID |
+               SCAN_COMPLETE_EVENT_ID |
+               RSSI_SNR_TRIGGER_0_EVENT_ID |
+               PERIODIC_SCAN_COMPLETE_EVENT_ID |
+               PERIODIC_SCAN_REPORT_EVENT_ID |
+               DUMMY_PACKET_EVENT_ID |
+               PEER_REMOVE_COMPLETE_EVENT_ID |
+               BA_SESSION_RX_CONSTRAINT_EVENT_ID |
+               REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
+               INACTIVE_STA_EVENT_ID |
+               MAX_TX_FAILURE_EVENT_ID |
+               CHANNEL_SWITCH_COMPLETE_EVENT_ID |
+               DFS_CHANNELS_CONFIG_COMPLETE_EVENT;
+
        ret = wlcore_boot_run_firmware(wl);
        if (ret < 0)
                goto out;
@@ -964,7 +1021,7 @@ static int wl18xx_hw_init(struct wl1271 *wl)
 
        /* (re)init private structures. Relevant on recovery as well. */
        priv->last_fw_rls_idx = 0;
-       priv->extra_spare_vif_count = 0;
+       priv->extra_spare_key_count = 0;
 
        /* set the default amount of spare blocks in the bitmap */
        ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_BLOCK_SPARE);
@@ -1022,7 +1079,12 @@ static bool wl18xx_is_mimo_supported(struct wl1271 *wl)
 {
        struct wl18xx_priv *priv = wl->priv;
 
-       return priv->conf.phy.number_of_assembled_ant2_4 >= 2;
+       /* only support MIMO with multiple antennas, and when SISO
+        * is not forced through config
+        */
+       return (priv->conf.phy.number_of_assembled_ant2_4 >= 2) &&
+              (priv->conf.ht.mode != HT_MODE_WIDE) &&
+              (priv->conf.ht.mode != HT_MODE_SISO20);
 }
 
 /*
@@ -1223,8 +1285,8 @@ static int wl18xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
 {
        struct wl18xx_priv *priv = wl->priv;
 
-       /* If we have VIFs requiring extra spare, indulge them */
-       if (priv->extra_spare_vif_count)
+       /* If we have keys requiring extra spare, indulge them */
+       if (priv->extra_spare_key_count)
                return WL18XX_TX_HW_EXTRA_BLOCK_SPARE;
 
        return WL18XX_TX_HW_BLOCK_SPARE;
@@ -1236,42 +1298,48 @@ static int wl18xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
                          struct ieee80211_key_conf *key_conf)
 {
        struct wl18xx_priv *priv = wl->priv;
-       bool change_spare = false;
+       bool change_spare = false, special_enc;
        int ret;
 
+       wl1271_debug(DEBUG_CRYPT, "extra spare keys before: %d",
+                    priv->extra_spare_key_count);
+
+       special_enc = key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
+                     key_conf->cipher == WLAN_CIPHER_SUITE_TKIP;
+
+       ret = wlcore_set_key(wl, cmd, vif, sta, key_conf);
+       if (ret < 0)
+               goto out;
+
        /*
-        * when adding the first or removing the last GEM/TKIP interface,
+        * when adding the first or removing the last GEM/TKIP key,
         * we have to adjust the number of spare blocks.
         */
-       change_spare = (key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
-               key_conf->cipher == WLAN_CIPHER_SUITE_TKIP) &&
-               ((priv->extra_spare_vif_count == 0 && cmd == SET_KEY) ||
-                (priv->extra_spare_vif_count == 1 && cmd == DISABLE_KEY));
+       if (special_enc) {
+               if (cmd == SET_KEY) {
+                       /* first key */
+                       change_spare = (priv->extra_spare_key_count == 0);
+                       priv->extra_spare_key_count++;
+               } else if (cmd == DISABLE_KEY) {
+                       /* last key */
+                       change_spare = (priv->extra_spare_key_count == 1);
+                       priv->extra_spare_key_count--;
+               }
+       }
 
-       /* no need to change spare - just regular set_key */
-       if (!change_spare)
-               return wlcore_set_key(wl, cmd, vif, sta, key_conf);
+       wl1271_debug(DEBUG_CRYPT, "extra spare keys after: %d",
+                    priv->extra_spare_key_count);
 
-       ret = wlcore_set_key(wl, cmd, vif, sta, key_conf);
-       if (ret < 0)
+       if (!change_spare)
                goto out;
 
        /* key is now set, change the spare blocks */
-       if (cmd == SET_KEY) {
+       if (priv->extra_spare_key_count)
                ret = wl18xx_set_host_cfg_bitmap(wl,
                                        WL18XX_TX_HW_EXTRA_BLOCK_SPARE);
-               if (ret < 0)
-                       goto out;
-
-               priv->extra_spare_vif_count++;
-       } else {
+       else
                ret = wl18xx_set_host_cfg_bitmap(wl,
                                        WL18XX_TX_HW_BLOCK_SPARE);
-               if (ret < 0)
-                       goto out;
-
-               priv->extra_spare_vif_count--;
-       }
 
 out:
        return ret;
@@ -1296,6 +1364,92 @@ static u32 wl18xx_pre_pkt_send(struct wl1271 *wl,
        return buf_offset;
 }
 
+static void wl18xx_sta_rc_update(struct wl1271 *wl,
+                                struct wl12xx_vif *wlvif,
+                                struct ieee80211_sta *sta,
+                                u32 changed)
+{
+       bool wide = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update wide %d", wide);
+
+       if (!(changed & IEEE80211_RC_BW_CHANGED))
+               return;
+
+       mutex_lock(&wl->mutex);
+
+       /* sanity */
+       if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS))
+               goto out;
+
+       /* ignore the change before association */
+       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+               goto out;
+
+       /*
+        * If we started out as wide, we can change the operation mode. If we
+        * thought this was a 20mhz AP, we have to reconnect
+        */
+       if (wlvif->sta.role_chan_type == NL80211_CHAN_HT40MINUS ||
+           wlvif->sta.role_chan_type == NL80211_CHAN_HT40PLUS)
+               wl18xx_acx_peer_ht_operation_mode(wl, wlvif->sta.hlid, wide);
+       else
+               ieee80211_connection_loss(wl12xx_wlvif_to_vif(wlvif));
+
+out:
+       mutex_unlock(&wl->mutex);
+}
+
+static int wl18xx_set_peer_cap(struct wl1271 *wl,
+                              struct ieee80211_sta_ht_cap *ht_cap,
+                              bool allow_ht_operation,
+                              u32 rate_set, u8 hlid)
+{
+       return wl18xx_acx_set_peer_cap(wl, ht_cap, allow_ht_operation,
+                                      rate_set, hlid);
+}
+
+static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
+                                struct wl1271_link *lnk)
+{
+       u8 thold;
+       struct wl18xx_fw_status_priv *status_priv =
+               (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+       u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+
+       /* suspended links are never high priority */
+       if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
+               return false;
+
+       /* the priority thresholds are taken from FW */
+       if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) &&
+           !test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map))
+               thold = status_priv->tx_fast_link_prio_threshold;
+       else
+               thold = status_priv->tx_slow_link_prio_threshold;
+
+       return lnk->allocated_pkts < thold;
+}
+
+static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
+                               struct wl1271_link *lnk)
+{
+       u8 thold;
+       struct wl18xx_fw_status_priv *status_priv =
+               (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+       u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
+
+       if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
+               thold = status_priv->tx_suspend_threshold;
+       else if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) &&
+                !test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map))
+               thold = status_priv->tx_fast_stop_threshold;
+       else
+               thold = status_priv->tx_slow_stop_threshold;
+
+       return lnk->allocated_pkts < thold;
+}
+
 static int wl18xx_setup(struct wl1271 *wl);
 
 static struct wlcore_ops wl18xx_ops = {
@@ -1305,6 +1459,8 @@ static struct wlcore_ops wl18xx_ops = {
        .plt_init       = wl18xx_plt_init,
        .trigger_cmd    = wl18xx_trigger_cmd,
        .ack_event      = wl18xx_ack_event,
+       .wait_for_event = wl18xx_wait_for_event,
+       .process_mailbox_events = wl18xx_process_mailbox_events,
        .calc_tx_blocks = wl18xx_calc_tx_blocks,
        .set_tx_desc_blocks = wl18xx_set_tx_desc_blocks,
        .set_tx_desc_data_len = wl18xx_set_tx_desc_data_len,
@@ -1320,16 +1476,26 @@ static struct wlcore_ops wl18xx_ops = {
        .ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask,
        .get_mac        = wl18xx_get_mac,
        .debugfs_init   = wl18xx_debugfs_add_files,
+       .scan_start     = wl18xx_scan_start,
+       .scan_stop      = wl18xx_scan_stop,
+       .sched_scan_start       = wl18xx_sched_scan_start,
+       .sched_scan_stop        = wl18xx_scan_sched_scan_stop,
        .handle_static_data     = wl18xx_handle_static_data,
        .get_spare_blocks = wl18xx_get_spare_blocks,
        .set_key        = wl18xx_set_key,
+       .channel_switch = wl18xx_cmd_channel_switch,
        .pre_pkt_send   = wl18xx_pre_pkt_send,
+       .sta_rc_update  = wl18xx_sta_rc_update,
+       .set_peer_cap   = wl18xx_set_peer_cap,
+       .lnk_high_prio  = wl18xx_lnk_high_prio,
+       .lnk_low_prio   = wl18xx_lnk_low_prio,
 };
 
 /* HT cap appropriate for wide channels in 2Ghz */
 static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = {
        .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
-              IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40,
+              IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1343,7 +1509,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = {
 /* HT cap appropriate for wide channels in 5Ghz */
 static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = {
        .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
-              IEEE80211_HT_CAP_SUP_WIDTH_20_40,
+              IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1356,7 +1523,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = {
 
 /* HT cap appropriate for SISO 20 */
 static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {
-       .cap = IEEE80211_HT_CAP_SGI_20,
+       .cap = IEEE80211_HT_CAP_SGI_20 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1369,7 +1537,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {
 
 /* HT cap appropriate for MIMO rates in 20mhz channel */
 static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = {
-       .cap = IEEE80211_HT_CAP_SGI_20,
+       .cap = IEEE80211_HT_CAP_SGI_20 |
+              IEEE80211_HT_CAP_GRN_FLD,
        .ht_supported = true,
        .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
        .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1387,7 +1556,8 @@ static int wl18xx_setup(struct wl1271 *wl)
 
        wl->rtable = wl18xx_rtable;
        wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS;
-       wl->num_rx_desc = WL18XX_NUM_TX_DESCRIPTORS;
+       wl->num_rx_desc = WL18XX_NUM_RX_DESCRIPTORS;
+       wl->num_channels = 2;
        wl->num_mac_addr = WL18XX_NUM_MAC_ADDRESSES;
        wl->band_rate_to_idx = wl18xx_band_rate_to_idx;
        wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX;
@@ -1506,7 +1676,8 @@ static int wl18xx_probe(struct platform_device *pdev)
        int ret;
 
        hw = wlcore_alloc_hw(sizeof(struct wl18xx_priv),
-                            WL18XX_AGGR_BUFFER_SIZE);
+                            WL18XX_AGGR_BUFFER_SIZE,
+                            sizeof(struct wl18xx_event_mailbox));
        if (IS_ERR(hw)) {
                wl1271_error("can't allocate hw");
                ret = PTR_ERR(hw);
diff --git a/drivers/net/wireless/ti/wl18xx/scan.c b/drivers/net/wireless/ti/wl18xx/scan.c
new file mode 100644 (file)
index 0000000..09d9445
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/ieee80211.h>
+#include "scan.h"
+#include "../wlcore/debug.h"
+
+static void wl18xx_adjust_channels(struct wl18xx_cmd_scan_params *cmd,
+                                  struct wlcore_scan_channels *cmd_channels)
+{
+       memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive));
+       memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active));
+       cmd->dfs = cmd_channels->dfs;
+       cmd->passive_active = cmd_channels->passive_active;
+
+       memcpy(cmd->channels_2, cmd_channels->channels_2,
+              sizeof(cmd->channels_2));
+       memcpy(cmd->channels_5, cmd_channels->channels_5,
+              sizeof(cmd->channels_2));
+       /* channels_4 are not supported, so no need to copy them */
+}
+
+static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           struct cfg80211_scan_request *req)
+{
+       struct wl18xx_cmd_scan_params *cmd;
+       struct wlcore_scan_channels *cmd_channels = NULL;
+       int ret;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->role_id = wlvif->role_id;
+
+       if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       cmd->scan_type = SCAN_TYPE_SEARCH;
+       cmd->rssi_threshold = -127;
+       cmd->snr_threshold = 0;
+
+       cmd->bss_type = SCAN_BSS_TYPE_ANY;
+
+       cmd->ssid_from_list = 0;
+       cmd->filter = 0;
+       cmd->add_broadcast = 0;
+
+       cmd->urgency = 0;
+       cmd->protect = 0;
+
+       cmd->n_probe_reqs = wl->conf.scan.num_probe_reqs;
+       cmd->terminate_after = 0;
+
+       /* configure channels */
+       WARN_ON(req->n_ssids > 1);
+
+       cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL);
+       if (!cmd_channels) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       wlcore_set_scan_chan_params(wl, cmd_channels, req->channels,
+                                   req->n_channels, req->n_ssids,
+                                   SCAN_TYPE_SEARCH);
+       wl18xx_adjust_channels(cmd, cmd_channels);
+
+       /*
+        * all the cycles params (except total cycles) should
+        * remain 0 for normal scan
+        */
+       cmd->total_cycles = 1;
+
+       if (req->no_cck)
+               cmd->rate = WL18XX_SCAN_RATE_6;
+
+       cmd->tag = WL1271_SCAN_DEFAULT_TAG;
+
+       if (req->n_ssids) {
+               cmd->ssid_len = req->ssids[0].ssid_len;
+               memcpy(cmd->ssid, req->ssids[0].ssid, cmd->ssid_len);
+       }
+
+       /* TODO: per-band ies? */
+       if (cmd->active[0]) {
+               u8 band = IEEE80211_BAND_2GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                cmd->role_id, band,
+                                req->ssids ? req->ssids[0].ssid : NULL,
+                                req->ssids ? req->ssids[0].ssid_len : 0,
+                                req->ie,
+                                req->ie_len,
+                                false);
+               if (ret < 0) {
+                       wl1271_error("2.4GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       if (cmd->active[1] || cmd->dfs) {
+               u8 band = IEEE80211_BAND_5GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                cmd->role_id, band,
+                                req->ssids ? req->ssids[0].ssid : NULL,
+                                req->ssids ? req->ssids[0].ssid_len : 0,
+                                req->ie,
+                                req->ie_len,
+                                false);
+               if (ret < 0) {
+                       wl1271_error("5GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
+
+       ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("SCAN failed");
+               goto out;
+       }
+
+out:
+       kfree(cmd_channels);
+       kfree(cmd);
+       return ret;
+}
+
+void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       wl->scan.failed = false;
+       cancel_delayed_work(&wl->scan_complete_work);
+       ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+                                    msecs_to_jiffies(0));
+}
+
+static
+int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
+                                 struct wl12xx_vif *wlvif,
+                                 struct cfg80211_sched_scan_request *req,
+                                 struct ieee80211_sched_scan_ies *ies)
+{
+       struct wl18xx_cmd_scan_params *cmd;
+       struct wlcore_scan_channels *cmd_channels = NULL;
+       struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
+       int ret;
+       int filter_type;
+
+       wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
+
+       filter_type = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req);
+       if (filter_type < 0)
+               return filter_type;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->role_id = wlvif->role_id;
+
+       if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       cmd->scan_type = SCAN_TYPE_PERIODIC;
+       cmd->rssi_threshold = c->rssi_threshold;
+       cmd->snr_threshold = c->snr_threshold;
+
+       /* don't filter on BSS type */
+       cmd->bss_type = SCAN_BSS_TYPE_ANY;
+
+       cmd->ssid_from_list = 1;
+       if (filter_type == SCAN_SSID_FILTER_LIST)
+               cmd->filter = 1;
+       cmd->add_broadcast = 0;
+
+       cmd->urgency = 0;
+       cmd->protect = 0;
+
+       cmd->n_probe_reqs = c->num_probe_reqs;
+       /* don't stop scanning automatically when something is found */
+       cmd->terminate_after = 0;
+
+       cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL);
+       if (!cmd_channels) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       /* configure channels */
+       wlcore_set_scan_chan_params(wl, cmd_channels, req->channels,
+                                   req->n_channels, req->n_ssids,
+                                   SCAN_TYPE_PERIODIC);
+       wl18xx_adjust_channels(cmd, cmd_channels);
+
+       cmd->short_cycles_sec = 0;
+       cmd->long_cycles_sec = cpu_to_le16(req->interval);
+       cmd->short_cycles_count = 0;
+
+       cmd->total_cycles = 0;
+
+       cmd->tag = WL1271_SCAN_DEFAULT_TAG;
+
+       /* create a PERIODIC_SCAN_REPORT_EVENT whenever we've got a match */
+       cmd->report_threshold = 1;
+       cmd->terminate_on_report = 0;
+
+       if (cmd->active[0]) {
+               u8 band = IEEE80211_BAND_2GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                cmd->role_id, band,
+                                req->ssids ? req->ssids[0].ssid : NULL,
+                                req->ssids ? req->ssids[0].ssid_len : 0,
+                                ies->ie[band],
+                                ies->len[band],
+                                true);
+               if (ret < 0) {
+                       wl1271_error("2.4GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       if (cmd->active[1] || cmd->dfs) {
+               u8 band = IEEE80211_BAND_5GHZ;
+               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
+                                cmd->role_id, band,
+                                req->ssids ? req->ssids[0].ssid : NULL,
+                                req->ssids ? req->ssids[0].ssid_len : 0,
+                                ies->ie[band],
+                                ies->len[band],
+                                true);
+               if (ret < 0) {
+                       wl1271_error("5GHz PROBE request template failed");
+                       goto out;
+               }
+       }
+
+       wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
+
+       ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("SCAN failed");
+               goto out;
+       }
+
+out:
+       kfree(cmd_channels);
+       kfree(cmd);
+       return ret;
+}
+
+int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           struct cfg80211_sched_scan_request *req,
+                           struct ieee80211_sched_scan_ies *ies)
+{
+       return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies);
+}
+
+static int __wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                              u8 scan_type)
+{
+       struct wl18xx_cmd_scan_stop *stop;
+       int ret;
+
+       wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
+
+       stop = kzalloc(sizeof(*stop), GFP_KERNEL);
+       if (!stop) {
+               wl1271_error("failed to alloc memory to send sched scan stop");
+               return -ENOMEM;
+       }
+
+       stop->role_id = wlvif->role_id;
+       stop->scan_type = scan_type;
+
+       ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, stop, sizeof(*stop), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send sched scan stop command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(stop);
+       return ret;
+}
+
+void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_PERIODIC);
+}
+int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     struct cfg80211_scan_request *req)
+{
+       return wl18xx_scan_send(wl, wlvif, req);
+}
+
+int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       return __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_SEARCH);
+}
diff --git a/drivers/net/wireless/ti/wl18xx/scan.h b/drivers/net/wireless/ti/wl18xx/scan.h
new file mode 100644 (file)
index 0000000..eadee42
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * This file is part of wl18xx
+ *
+ * Copyright (C) 2012 Texas Instruments. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __WL18XX_SCAN_H__
+#define __WL18XX_SCAN_H__
+
+#include "../wlcore/wlcore.h"
+#include "../wlcore/cmd.h"
+#include "../wlcore/scan.h"
+
+struct tracking_ch_params {
+       struct conn_scan_ch_params channel;
+
+       __le32 bssid_lsb;
+       __le16 bssid_msb;
+
+       u8 padding[2];
+} __packed;
+
+/* probe request rate */
+enum
+{
+       WL18XX_SCAN_RATE_1      = 0,
+       WL18XX_SCAN_RATE_5_5    = 1,
+       WL18XX_SCAN_RATE_6      = 2,
+};
+
+#define WL18XX_MAX_CHANNELS_5GHZ 32
+
+struct wl18xx_cmd_scan_params {
+       struct wl1271_cmd_header header;
+
+       u8 role_id;
+       u8 scan_type;
+
+       s8 rssi_threshold; /* for filtering (in dBm) */
+       s8 snr_threshold;  /* for filtering (in dB) */
+
+       u8 bss_type;       /* for filtering */
+       u8 ssid_from_list; /* use ssid from configured ssid list */
+       u8 filter;         /* forward only results with matching ssids */
+
+       /*
+        * add broadcast ssid in addition to the configured ssids.
+        * the driver should add dummy entry for it (?).
+        */
+       u8 add_broadcast;
+
+       u8 urgency;
+       u8 protect;      /* ??? */
+       u8 n_probe_reqs;    /* Number of probes requests per channel */
+       u8 terminate_after; /* early terminate scan operation */
+
+       u8 passive[SCAN_MAX_BANDS]; /* number of passive scan channels */
+       u8 active[SCAN_MAX_BANDS];  /* number of active scan channels */
+       u8 dfs;            /* number of dfs channels in 5ghz */
+       u8 passive_active; /* number of passive before active channels 2.4ghz */
+
+       __le16 short_cycles_sec;
+       __le16 long_cycles_sec;
+       u8 short_cycles_count;
+       u8 total_cycles; /* 0 - infinite */
+       u8 padding[2];
+
+       union {
+               struct {
+                       struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
+                       struct conn_scan_ch_params channels_5[WL18XX_MAX_CHANNELS_5GHZ];
+                       struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
+               };
+               struct tracking_ch_params channels_tracking[WL1271_SCAN_MAX_CHANNELS];
+       } ;
+
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+       u8 ssid_len;     /* For SCAN_SSID_FILTER_SPECIFIC */
+       u8 tag;
+       u8 rate;
+
+       /* send SCAN_REPORT_EVENT in periodic scans after each cycle
+       * if number of results >= report_threshold. Must be 0 for
+       * non periodic scans
+       */
+       u8 report_threshold;
+
+       /* Should periodic scan stop after a report event was created.
+       * Must be 0 for non periodic scans.
+       */
+       u8 terminate_on_report;
+
+       u8 padding1[3];
+} __packed;
+
+struct wl18xx_cmd_scan_stop {
+       struct wl1271_cmd_header header;
+
+       u8 role_id;
+       u8 scan_type;
+       u8 padding[2];
+} __packed;
+
+int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                     struct cfg80211_scan_request *req);
+int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           struct cfg80211_sched_scan_request *req,
+                           struct ieee80211_sched_scan_ies *ies);
+void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+#endif
index 5b1fb10d9fd7c4ae084d5865ebb07e185df8afa5..57c694396647f71adaea386cd4465df00a5611a8 100644 (file)
 #include "wl18xx.h"
 #include "tx.h"
 
+static
+void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif,
+                            struct ieee80211_tx_rate *rate)
+{
+       u8 fw_rate = wl->fw_status_2->counters.tx_last_rate;
+
+       if (fw_rate > CONF_HW_RATE_INDEX_MAX) {
+               wl1271_error("last Tx rate invalid: %d", fw_rate);
+               rate->idx = 0;
+               rate->flags = 0;
+               return;
+       }
+
+       if (fw_rate <= CONF_HW_RATE_INDEX_54MBPS) {
+               rate->idx = fw_rate;
+               rate->flags = 0;
+       } else {
+               rate->flags = IEEE80211_TX_RC_MCS;
+               rate->idx = fw_rate - CONF_HW_RATE_INDEX_MCS0;
+
+               /* SGI modifier is counted as a separate rate */
+               if (fw_rate >= CONF_HW_RATE_INDEX_MCS7_SGI)
+                       (rate->idx)--;
+               if (fw_rate == CONF_HW_RATE_INDEX_MCS15_SGI)
+                       (rate->idx)--;
+
+               /* this also covers the 40Mhz SGI case (= MCS15) */
+               if (fw_rate == CONF_HW_RATE_INDEX_MCS7_SGI ||
+                   fw_rate == CONF_HW_RATE_INDEX_MCS15_SGI)
+                       rate->flags |= IEEE80211_TX_RC_SHORT_GI;
+
+               if (fw_rate > CONF_HW_RATE_INDEX_MCS7_SGI && vif) {
+                       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+                       if (wlvif->channel_type == NL80211_CHAN_HT40MINUS ||
+                           wlvif->channel_type == NL80211_CHAN_HT40PLUS) {
+                               /* adjustment needed for range 0-7 */
+                               rate->idx -= 8;
+                               rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+                       }
+               }
+       }
+}
+
 static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
 {
        struct ieee80211_tx_info *info;
@@ -44,7 +87,6 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
        /* a zero bit indicates Tx success */
        tx_success = !(tx_stat_byte & BIT(WL18XX_TX_STATUS_STAT_BIT_IDX));
 
-
        skb = wl->tx_frames[id];
        info = IEEE80211_SKB_CB(skb);
 
@@ -56,11 +98,13 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
        /* update the TX status info */
        if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_ACK))
                info->flags |= IEEE80211_TX_STAT_ACK;
+       /*
+        * first pass info->control.vif while it's valid, and then fill out
+        * the info->status structures
+        */
+       wl18xx_get_last_tx_rate(wl, info->control.vif, &info->status.rates[0]);
 
-       /* no real data about Tx completion */
-       info->status.rates[0].idx = -1;
-       info->status.rates[0].count = 0;
-       info->status.rates[0].flags = 0;
+       info->status.rates[0].count = 1; /* no data about retries */
        info->status.ack_signal = -1;
 
        if (!tx_success)
index 96a1e438d677fd1f14ea125338c824c25e34b228..b6739e79efcfa33bc45c18ad9f205bcdbc2dce5a 100644 (file)
 
 /* minimum FW required for driver */
 #define WL18XX_CHIP_VER                8
-#define WL18XX_IFTYPE_VER      2
-#define WL18XX_MAJOR_VER       0
-#define WL18XX_SUBTYPE_VER     0
-#define WL18XX_MINOR_VER       100
+#define WL18XX_IFTYPE_VER      5
+#define WL18XX_MAJOR_VER       WLCORE_FW_VER_IGNORE
+#define WL18XX_SUBTYPE_VER     WLCORE_FW_VER_IGNORE
+#define WL18XX_MINOR_VER       28
 
 #define WL18XX_CMD_MAX_SIZE          740
 
@@ -49,8 +49,8 @@ struct wl18xx_priv {
        /* Index of last released Tx desc in FW */
        u8 last_fw_rls_idx;
 
-       /* number of VIFs requiring extra spare mem-blocks */
-       int extra_spare_vif_count;
+       /* number of keys requiring extra spare mem-blocks */
+       int extra_spare_key_count;
 };
 
 #define WL18XX_FW_MAX_TX_STATUS_DESC 33
@@ -68,7 +68,43 @@ struct wl18xx_fw_status_priv {
         */
        u8 released_tx_desc[WL18XX_FW_MAX_TX_STATUS_DESC];
 
-       u8 padding[2];
+       /* A bitmap representing the currently suspended links. The suspend
+        * is short lived, for multi-channel Tx requirements.
+        */
+       __le32 link_suspend_bitmap;
+
+       /* packet threshold for an "almost empty" AC,
+        * for Tx schedulng purposes
+        */
+       u8 tx_ac_threshold;
+
+       /* number of packets to queue up for a link in PS */
+       u8 tx_ps_threshold;
+
+       /* number of packet to queue up for a suspended link */
+       u8 tx_suspend_threshold;
+
+       /* Should have less than this number of packets in queue of a slow
+        * link to qualify as high priority link
+        */
+       u8 tx_slow_link_prio_threshold;
+
+       /* Should have less than this number of packets in queue of a fast
+        * link to qualify as high priority link
+        */
+       u8 tx_fast_link_prio_threshold;
+
+       /* Should have less than this number of packets in queue of a slow
+        * link before we stop queuing up packets for it.
+        */
+       u8 tx_slow_stop_threshold;
+
+       /* Should have less than this number of packets in queue of a fast
+        * link before we stop queuing up packets for it.
+        */
+       u8 tx_fast_stop_threshold;
+
+       u8 padding[3];
 };
 
 #define WL18XX_PHY_VERSION_MAX_LEN 20
index ce108a736bd0a3bdc5b1832fa09f02bdb82f412f..c79654323396cb6fd73b5dff82b44f72e1148d08 100644 (file)
@@ -1340,6 +1340,8 @@ out:
        kfree(acx);
        return ret;
 }
+EXPORT_SYMBOL_GPL(wl1271_acx_set_ht_capabilities);
+
 
 int wl1271_acx_set_ht_information(struct wl1271 *wl,
                                   struct wl12xx_vif *wlvif,
@@ -1433,13 +1435,22 @@ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
        acx->win_size = wl->conf.ht.rx_ba_win_size;
        acx->ssn = ssn;
 
-       ret = wl1271_cmd_configure(wl, ACX_BA_SESSION_RX_SETUP, acx,
-                                  sizeof(*acx));
+       ret = wlcore_cmd_configure_failsafe(wl, ACX_BA_SESSION_RX_SETUP, acx,
+                                           sizeof(*acx),
+                                           BIT(CMD_STATUS_NO_RX_BA_SESSION));
        if (ret < 0) {
                wl1271_warning("acx ba receiver session failed: %d", ret);
                goto out;
        }
 
+       /* sometimes we can't start the session */
+       if (ret == CMD_STATUS_NO_RX_BA_SESSION) {
+               wl1271_warning("no fw rx ba on tid %d", tid_index);
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ret = 0;
 out:
        kfree(acx);
        return ret;
index d03215d6b3bd2bc698cb38b465e51427798d37ec..126536c6a3933929f683940de3cc8670988caaea 100644 (file)
@@ -1025,7 +1025,6 @@ enum {
        ACX_CONFIG_HANGOVER              = 0x0042,
        ACX_FEATURE_CFG                  = 0x0043,
        ACX_PROTECTION_CFG               = 0x0044,
-       ACX_CHECKSUM_CONFIG              = 0x0045,
 };
 
 
index 375ea574eafb3b7e09b936f8b24f9a1d04b21e19..b58ae5fc14879f2c073305b8f36ccc180400ba94 100644 (file)
@@ -84,47 +84,57 @@ out:
 static int wlcore_validate_fw_ver(struct wl1271 *wl)
 {
        unsigned int *fw_ver = wl->chip.fw_ver;
-       unsigned int *min_ver = wl->min_fw_ver;
+       unsigned int *min_ver = (wl->fw_type == WL12XX_FW_TYPE_NORMAL) ?
+               wl->min_sr_fw_ver : wl->min_mr_fw_ver;
+       char min_fw_str[32] = "";
+       int i;
 
        /* the chip must be exactly equal */
-       if (min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP])
+       if ((min_ver[FW_VER_CHIP] != WLCORE_FW_VER_IGNORE) &&
+           (min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP]))
                goto fail;
 
-       /* always check the next digit if all previous ones are equal */
-
-       if (min_ver[FW_VER_IF_TYPE] < fw_ver[FW_VER_IF_TYPE])
-               goto out;
-       else if (min_ver[FW_VER_IF_TYPE] > fw_ver[FW_VER_IF_TYPE])
+       /* the firmware type must be equal */
+       if ((min_ver[FW_VER_IF_TYPE] != WLCORE_FW_VER_IGNORE) &&
+           (min_ver[FW_VER_IF_TYPE] != fw_ver[FW_VER_IF_TYPE]))
                goto fail;
 
-       if (min_ver[FW_VER_MAJOR] < fw_ver[FW_VER_MAJOR])
-               goto out;
-       else if (min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR])
+       /* the project number must be equal */
+       if ((min_ver[FW_VER_SUBTYPE] != WLCORE_FW_VER_IGNORE) &&
+           (min_ver[FW_VER_SUBTYPE] != fw_ver[FW_VER_SUBTYPE]))
                goto fail;
 
-       if (min_ver[FW_VER_SUBTYPE] < fw_ver[FW_VER_SUBTYPE])
-               goto out;
-       else if (min_ver[FW_VER_SUBTYPE] > fw_ver[FW_VER_SUBTYPE])
+       /* the API version must be greater or equal */
+       if ((min_ver[FW_VER_MAJOR] != WLCORE_FW_VER_IGNORE) &&
+                (min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR]))
                goto fail;
 
-       if (min_ver[FW_VER_MINOR] < fw_ver[FW_VER_MINOR])
-               goto out;
-       else if (min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR])
+       /* if the API version is equal... */
+       if (((min_ver[FW_VER_MAJOR] == WLCORE_FW_VER_IGNORE) ||
+            (min_ver[FW_VER_MAJOR] == fw_ver[FW_VER_MAJOR])) &&
+           /* ...the minor must be greater or equal */
+           ((min_ver[FW_VER_MINOR] != WLCORE_FW_VER_IGNORE) &&
+            (min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR])))
                goto fail;
 
-out:
        return 0;
 
 fail:
-       wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is outdated.\n"
-                    "Please use at least FW %u.%u.%u.%u.%u.\n"
-                    "You can get more information at:\n"
-                    "http://wireless.kernel.org/en/users/Drivers/wl12xx",
+       for (i = 0; i < NUM_FW_VER; i++)
+               if (min_ver[i] == WLCORE_FW_VER_IGNORE)
+                       snprintf(min_fw_str, sizeof(min_fw_str),
+                                 "%s*.", min_fw_str);
+               else
+                       snprintf(min_fw_str, sizeof(min_fw_str),
+                                 "%s%u.", min_fw_str, min_ver[i]);
+
+       wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is invalid.\n"
+                    "Please use at least FW %s\n"
+                    "You can get the latest firmwares at:\n"
+                    "git://github.com/TI-OpenLink/firmwares.git",
                     fw_ver[FW_VER_CHIP], fw_ver[FW_VER_IF_TYPE],
                     fw_ver[FW_VER_MAJOR], fw_ver[FW_VER_SUBTYPE],
-                    fw_ver[FW_VER_MINOR], min_ver[FW_VER_CHIP],
-                    min_ver[FW_VER_IF_TYPE], min_ver[FW_VER_MAJOR],
-                    min_ver[FW_VER_SUBTYPE], min_ver[FW_VER_MINOR]);
+                    fw_ver[FW_VER_MINOR], min_fw_str);
        return -EINVAL;
 }
 
@@ -491,7 +501,7 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
        if (ret < 0)
                return ret;
 
-       wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox);
+       wl->mbox_ptr[1] = wl->mbox_ptr[0] + wl->mbox_size;
 
        wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x",
                     wl->mbox_ptr[0], wl->mbox_ptr[1]);
@@ -508,23 +518,6 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
         */
 
        /* unmask required mbox events  */
-       wl->event_mask = BSS_LOSE_EVENT_ID |
-               REGAINED_BSS_EVENT_ID |
-               SCAN_COMPLETE_EVENT_ID |
-               ROLE_STOP_COMPLETE_EVENT_ID |
-               RSSI_SNR_TRIGGER_0_EVENT_ID |
-               PSPOLL_DELIVERY_FAILURE_EVENT_ID |
-               SOFT_GEMINI_SENSE_EVENT_ID |
-               PERIODIC_SCAN_REPORT_EVENT_ID |
-               PERIODIC_SCAN_COMPLETE_EVENT_ID |
-               DUMMY_PACKET_EVENT_ID |
-               PEER_REMOVE_COMPLETE_EVENT_ID |
-               BA_SESSION_RX_CONSTRAINT_EVENT_ID |
-               REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
-               INACTIVE_STA_EVENT_ID |
-               MAX_TX_RETRY_EVENT_ID |
-               CHANNEL_SWITCH_COMPLETE_EVENT_ID;
-
        ret = wl1271_event_unmask(wl);
        if (ret < 0) {
                wl1271_error("EVENT mask setting failed");
index 27f83f72a93bb9c946535886043750fbdadee632..1201aca9c89a4cfde99474c38435521a28663d4e 100644 (file)
  * @id: command id
  * @buf: buffer containing the command, must work with dma
  * @len: length of the buffer
+ * return the cmd status code on success.
  */
-int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
-                   size_t res_len)
+static int __wlcore_cmd_send(struct wl1271 *wl, u16 id, void *buf,
+                            size_t len, size_t res_len)
 {
        struct wl1271_cmd_header *cmd;
        unsigned long timeout;
        u32 intr;
-       int ret = 0;
+       int ret;
        u16 status;
        u16 poll_count = 0;
 
@@ -71,7 +72,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
 
        ret = wlcore_write(wl, wl->cmd_box_addr, buf, len, false);
        if (ret < 0)
-               goto fail;
+               return ret;
 
        /*
         * TODO: we just need this because one bit is in a different
@@ -79,19 +80,18 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
         */
        ret = wl->ops->trigger_cmd(wl, wl->cmd_box_addr, buf, len);
        if (ret < 0)
-               goto fail;
+               return ret;
 
        timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT);
 
        ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr);
        if (ret < 0)
-               goto fail;
+               return ret;
 
        while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) {
                if (time_after(jiffies, timeout)) {
                        wl1271_error("command complete timeout");
-                       ret = -ETIMEDOUT;
-                       goto fail;
+                       return -ETIMEDOUT;
                }
 
                poll_count++;
@@ -102,7 +102,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
 
                ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr);
                if (ret < 0)
-                       goto fail;
+                       return ret;
        }
 
        /* read back the status code of the command */
@@ -111,33 +111,66 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
 
        ret = wlcore_read(wl, wl->cmd_box_addr, cmd, res_len, false);
        if (ret < 0)
-               goto fail;
+               return ret;
 
        status = le16_to_cpu(cmd->status);
-       if (status != CMD_STATUS_SUCCESS) {
-               wl1271_error("command execute failure %d", status);
-               ret = -EIO;
-               goto fail;
-       }
 
        ret = wlcore_write_reg(wl, REG_INTERRUPT_ACK,
                               WL1271_ACX_INTR_CMD_COMPLETE);
+       if (ret < 0)
+               return ret;
+
+       return status;
+}
+
+/*
+ * send command to fw and return cmd status on success
+ * valid_rets contains a bitmap of allowed error codes
+ */
+int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len,
+                            size_t res_len, unsigned long valid_rets)
+{
+       int ret = __wlcore_cmd_send(wl, id, buf, len, res_len);
+
        if (ret < 0)
                goto fail;
 
-       return 0;
+       /* success is always a valid status */
+       valid_rets |= BIT(CMD_STATUS_SUCCESS);
 
+       if (ret >= MAX_COMMAND_STATUS ||
+           !test_bit(ret, &valid_rets)) {
+               wl1271_error("command execute failure %d", ret);
+               ret = -EIO;
+               goto fail;
+       }
+       return ret;
 fail:
        wl12xx_queue_recovery_work(wl);
        return ret;
 }
+EXPORT_SYMBOL_GPL(wl1271_cmd_send);
+
+/*
+ * wrapper for wlcore_cmd_send that accept only CMD_STATUS_SUCCESS
+ * return 0 on success.
+ */
+int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
+                   size_t res_len)
+{
+       int ret = wlcore_cmd_send_failsafe(wl, id, buf, len, res_len, 0);
+
+       if (ret < 0)
+               return ret;
+       return 0;
+}
 
 /*
  * Poll the mailbox event field until any of the bits in the mask is set or a
  * timeout occurs (WL1271_EVENT_TIMEOUT in msecs)
  */
-static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
-                                               u32 mask, bool *timeout)
+int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
+                                        u32 mask, bool *timeout)
 {
        u32 *events_vector;
        u32 event;
@@ -187,20 +220,7 @@ out:
        kfree(events_vector);
        return ret;
 }
-
-static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
-{
-       int ret;
-       bool timeout = false;
-
-       ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask, &timeout);
-       if (ret != 0 || timeout) {
-               wl12xx_queue_recovery_work(wl);
-               return ret;
-       }
-
-       return 0;
-}
+EXPORT_SYMBOL_GPL(wlcore_cmd_wait_for_event_or_timeout);
 
 int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type,
                           u8 *role_id)
@@ -278,6 +298,16 @@ out:
        return ret;
 }
 
+static int wlcore_get_new_session_id(struct wl1271 *wl, u8 hlid)
+{
+       if (wl->session_ids[hlid] >= SESSION_COUNTER_MAX)
+               wl->session_ids[hlid] = 0;
+
+       wl->session_ids[hlid]++;
+
+       return wl->session_ids[hlid];
+}
+
 int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
 {
        unsigned long flags;
@@ -285,12 +315,21 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
        if (link >= WL12XX_MAX_LINKS)
                return -EBUSY;
 
+       wl->session_ids[link] = wlcore_get_new_session_id(wl, link);
+
        /* 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);
+
+       /* take the last "freed packets" value from the current FW status */
+       wl->links[link].prev_freed_pkts =
+                       wl->fw_status_2->counters.tx_lnk_free_pkts[link];
+       wl->links[link].wlvif = wlvif;
        *hlid = link;
+
+       wl->active_link_count++;
        return 0;
 }
 
@@ -307,24 +346,21 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
        __clear_bit(*hlid, wlvif->links_map);
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 
+       wl->links[*hlid].allocated_pkts = 0;
+       wl->links[*hlid].prev_freed_pkts = 0;
+       wl->links[*hlid].ba_bitmap = 0;
+       memset(wl->links[*hlid].addr, 0, ETH_ALEN);
+
        /*
         * At this point op_tx() will not add more packets to the queues. We
         * can purge them.
         */
        wl1271_tx_reset_link_queues(wl, *hlid);
+       wl->links[*hlid].wlvif = NULL;
 
        *hlid = WL12XX_INVALID_LINK_ID;
-}
-
-static int wl12xx_get_new_session_id(struct wl1271 *wl,
-                                    struct wl12xx_vif *wlvif)
-{
-       if (wlvif->session_counter >= SESSION_COUNTER_MAX)
-               wlvif->session_counter = 0;
-
-       wlvif->session_counter++;
-
-       return wlvif->session_counter;
+       wl->active_link_count--;
+       WARN_ON_ONCE(wl->active_link_count < 0);
 }
 
 static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
@@ -345,7 +381,9 @@ static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
 }
 
 static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
-                                    struct wl12xx_vif *wlvif)
+                                    struct wl12xx_vif *wlvif,
+                                    enum ieee80211_band band,
+                                    int channel)
 {
        struct wl12xx_cmd_role_start *cmd;
        int ret;
@@ -359,9 +397,9 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
        wl1271_debug(DEBUG_CMD, "cmd role start dev %d", wlvif->dev_role_id);
 
        cmd->role_id = wlvif->dev_role_id;
-       if (wlvif->band == IEEE80211_BAND_5GHZ)
+       if (band == IEEE80211_BAND_5GHZ)
                cmd->band = WLCORE_BAND_5GHZ;
-       cmd->channel = wlvif->channel;
+       cmd->channel = channel;
 
        if (wlvif->dev_hlid == WL12XX_INVALID_LINK_ID) {
                ret = wl12xx_allocate_link(wl, wlvif, &wlvif->dev_hlid);
@@ -369,7 +407,7 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
                        goto out_free;
        }
        cmd->device.hlid = wlvif->dev_hlid;
-       cmd->device.session = wl12xx_get_new_session_id(wl, wlvif);
+       cmd->device.session = wl->session_ids[wlvif->dev_hlid];
 
        wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d",
                     cmd->role_id, cmd->device.hlid, cmd->device.session);
@@ -420,12 +458,6 @@ static int wl12xx_cmd_role_stop_dev(struct wl1271 *wl,
                goto out_free;
        }
 
-       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;
-       }
-
        wl12xx_free_link(wl, wlvif, &wlvif->dev_hlid);
 
 out_free:
@@ -439,6 +471,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
        struct wl12xx_cmd_role_start *cmd;
+       u32 supported_rates;
        int ret;
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@@ -459,7 +492,14 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        cmd->sta.ssid_len = wlvif->ssid_len;
        memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len);
        memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN);
-       cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set);
+
+       supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES |
+                         wlcore_hw_sta_get_ap_rate_mask(wl, wlvif);
+       if (wlvif->p2p)
+               supported_rates &= ~CONF_TX_CCK_RATES;
+
+       cmd->sta.local_rates = cpu_to_le32(supported_rates);
+
        cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
 
        if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) {
@@ -468,7 +508,11 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                        goto out_free;
        }
        cmd->sta.hlid = wlvif->sta.hlid;
-       cmd->sta.session = wl12xx_get_new_session_id(wl, wlvif);
+       cmd->sta.session = wl->session_ids[wlvif->sta.hlid];
+       /*
+        * We don't have the correct remote rates in this stage. the rates
+        * will be reconfigured later, after authorization.
+        */
        cmd->sta.remote_rates = cpu_to_le32(wlvif->rate_set);
 
        wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d "
@@ -482,6 +526,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                goto err_hlid;
        }
 
+       wlvif->sta.role_chan_type = wlvif->channel_type;
        goto out_free;
 
 err_hlid:
@@ -500,7 +545,6 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct wl12xx_cmd_role_stop *cmd;
        int ret;
-       bool timeout = false;
 
        if (WARN_ON(wlvif->sta.hlid == WL12XX_INVALID_LINK_ID))
                return -EINVAL;
@@ -523,17 +567,6 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                goto out_free;
        }
 
-       /*
-        * Sometimes the firmware doesn't send this event, so we just
-        * time out without failing.  Queue recovery for other
-        * failures.
-        */
-       ret = wl1271_cmd_wait_for_event_or_timeout(wl,
-                                                  ROLE_STOP_COMPLETE_EVENT_ID,
-                                                  &timeout);
-       if (ret)
-               wl12xx_queue_recovery_work(wl);
-
        wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid);
 
 out_free:
@@ -579,12 +612,15 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        cmd->ap.bss_index = WL1271_AP_BSS_INDEX;
        cmd->ap.global_hlid = wlvif->ap.global_hlid;
        cmd->ap.broadcast_hlid = wlvif->ap.bcast_hlid;
+       cmd->ap.global_session_id = wl->session_ids[wlvif->ap.global_hlid];
+       cmd->ap.bcast_session_id = wl->session_ids[wlvif->ap.bcast_hlid];
        cmd->ap.basic_rate_set = cpu_to_le32(wlvif->basic_rate_set);
        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->ap.wmm = wlvif->wmm_enabled;
        cmd->channel = wlvif->channel;
        cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
 
@@ -599,8 +635,10 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                memcpy(cmd->ap.ssid, bss_conf->ssid, bss_conf->ssid_len);
        }
 
-       supported_rates = CONF_TX_AP_ENABLED_RATES | CONF_TX_MCS_RATES |
+       supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES |
                wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif);
+       if (wlvif->p2p)
+               supported_rates &= ~CONF_TX_CCK_RATES;
 
        wl1271_debug(DEBUG_CMD, "cmd role start ap with supported_rates 0x%08x",
                     supported_rates);
@@ -799,8 +837,11 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len)
  * @id: acx id
  * @buf: buffer containing acx, including all headers, must work with dma
  * @len: length of buf
+ * @valid_rets: bitmap of valid cmd status codes (i.e. return values).
+ * return the cmd status on success.
  */
-int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
+int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf,
+                                 size_t len, unsigned long valid_rets)
 {
        struct acx_header *acx = buf;
        int ret;
@@ -812,12 +853,26 @@ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
        /* payload length, does not include any headers */
        acx->len = cpu_to_le16(len - sizeof(*acx));
 
-       ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len, 0);
+       ret = wlcore_cmd_send_failsafe(wl, CMD_CONFIGURE, acx, len, 0,
+                                      valid_rets);
        if (ret < 0) {
                wl1271_warning("CONFIGURE command NOK");
                return ret;
        }
 
+       return ret;
+}
+
+/*
+ * wrapper for wlcore_cmd_configure that accepts only success status.
+ * return 0 on success
+ */
+int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
+{
+       int ret = wlcore_cmd_configure_failsafe(wl, id, buf, len, 0);
+
+       if (ret < 0)
+               return ret;
        return 0;
 }
 EXPORT_SYMBOL_GPL(wl1271_cmd_configure);
@@ -1034,8 +1089,8 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        struct sk_buff *skb;
        int ret;
        u32 rate;
-       u16 template_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4;
-       u16 template_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5;
+       u16 template_id_2_4 = wl->scan_templ_id_2_4;
+       u16 template_id_5 = wl->scan_templ_id_5;
 
        skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
                                     ie_len);
@@ -1048,10 +1103,10 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
        wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len);
 
-       if (!sched_scan &&
+       if (sched_scan &&
            (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) {
-               template_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4;
-               template_id_5 = CMD_TEMPL_APP_PROBE_REQ_5;
+               template_id_2_4 = wl->sched_scan_templ_id_2_4;
+               template_id_5 = wl->sched_scan_templ_id_5;
        }
 
        rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
@@ -1068,6 +1123,7 @@ out:
        dev_kfree_skb(skb);
        return ret;
 }
+EXPORT_SYMBOL_GPL(wl12xx_cmd_build_probe_req);
 
 struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
                                              struct wl12xx_vif *wlvif,
@@ -1379,7 +1435,8 @@ out:
        return ret;
 }
 
-int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid)
+int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             u8 hlid)
 {
        struct wl12xx_cmd_set_peer_state *cmd;
        int ret = 0;
@@ -1395,6 +1452,10 @@ int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid)
        cmd->hlid = hlid;
        cmd->state = WL1271_CMD_STA_STATE_CONNECTED;
 
+       /* wmm param is valid only for station role */
+       if (wlvif->bss_type == BSS_TYPE_STA_BSS)
+               cmd->wmm = wlvif->wmm_enabled;
+
        ret = wl1271_cmd_send(wl, CMD_SET_PEER_STATE, cmd, sizeof(*cmd), 0);
        if (ret < 0) {
                wl1271_error("failed to send set peer state command");
@@ -1429,6 +1490,7 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        cmd->hlid = hlid;
        cmd->sp_len = sta->max_sp;
        cmd->wmm = sta->wme ? 1 : 0;
+       cmd->session_id = wl->session_ids[hlid];
 
        for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++)
                if (sta->wme && (sta->uapsd_queues & BIT(i)))
@@ -1490,9 +1552,10 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid)
                goto out_free;
        }
 
-       ret = wl1271_cmd_wait_for_event_or_timeout(wl,
-                                          PEER_REMOVE_COMPLETE_EVENT_ID,
-                                          &timeout);
+       ret = wl->ops->wait_for_event(wl,
+                                     WLCORE_EVENT_PEER_REMOVE_COMPLETE,
+                                     &timeout);
+
        /*
         * We are ok with a timeout here. The event is sometimes not sent
         * due to a firmware bug. In case of another error (like SDIO timeout)
@@ -1508,6 +1571,131 @@ out:
        return ret;
 }
 
+static int wlcore_get_reg_conf_ch_idx(enum ieee80211_band band, u16 ch)
+{
+       int idx = -1;
+
+       switch (band) {
+       case IEEE80211_BAND_5GHZ:
+               if (ch >= 8 && ch <= 16)
+                       idx = ((ch-8)/4 + 18);
+               else if (ch >= 34 && ch <= 64)
+                       idx = ((ch-34)/2 + 3 + 18);
+               else if (ch >= 100 && ch <= 140)
+                       idx = ((ch-100)/4 + 15 + 18);
+               else if (ch >= 149 && ch <= 165)
+                       idx = ((ch-149)/4 + 26 + 18);
+               else
+                       idx = -1;
+               break;
+       case IEEE80211_BAND_2GHZ:
+               if (ch >= 1 && ch <= 14)
+                       idx = ch - 1;
+               else
+                       idx = -1;
+               break;
+       default:
+               wl1271_error("get reg conf ch idx - unknown band: %d",
+                            (int)band);
+       }
+
+       return idx;
+}
+
+void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel,
+                                    enum ieee80211_band band)
+{
+       int ch_bit_idx = 0;
+
+       if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
+               return;
+
+       ch_bit_idx = wlcore_get_reg_conf_ch_idx(band, channel);
+
+       if (ch_bit_idx > 0 && ch_bit_idx <= WL1271_MAX_CHANNELS)
+               set_bit(ch_bit_idx, (long *)wl->reg_ch_conf_pending);
+}
+
+int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl)
+{
+       struct wl12xx_cmd_regdomain_dfs_config *cmd = NULL;
+       int ret = 0, i, b, ch_bit_idx;
+       struct ieee80211_channel *channel;
+       u32 tmp_ch_bitmap[2];
+       u16 ch;
+       struct wiphy *wiphy = wl->hw->wiphy;
+       struct ieee80211_supported_band *band;
+       bool timeout = false;
+
+       if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
+               return 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd reg domain config");
+
+       memset(tmp_ch_bitmap, 0, sizeof(tmp_ch_bitmap));
+
+       for (b = IEEE80211_BAND_2GHZ; b <= IEEE80211_BAND_5GHZ; b++) {
+               band = wiphy->bands[b];
+               for (i = 0; i < band->n_channels; i++) {
+                       channel = &band->channels[i];
+                       ch = channel->hw_value;
+
+                       if (channel->flags & (IEEE80211_CHAN_DISABLED |
+                                             IEEE80211_CHAN_RADAR |
+                                             IEEE80211_CHAN_PASSIVE_SCAN))
+                               continue;
+
+                       ch_bit_idx = wlcore_get_reg_conf_ch_idx(b, ch);
+                       if (ch_bit_idx < 0)
+                               continue;
+
+                       set_bit(ch_bit_idx, (long *)tmp_ch_bitmap);
+               }
+       }
+
+       tmp_ch_bitmap[0] |= wl->reg_ch_conf_pending[0];
+       tmp_ch_bitmap[1] |= wl->reg_ch_conf_pending[1];
+
+       if (!memcmp(tmp_ch_bitmap, wl->reg_ch_conf_last, sizeof(tmp_ch_bitmap)))
+               goto out;
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       cmd->ch_bit_map1 = cpu_to_le32(tmp_ch_bitmap[0]);
+       cmd->ch_bit_map2 = cpu_to_le32(tmp_ch_bitmap[1]);
+
+       wl1271_debug(DEBUG_CMD,
+                    "cmd reg domain bitmap1: 0x%08x, bitmap2: 0x%08x",
+                    cmd->ch_bit_map1, cmd->ch_bit_map2);
+
+       ret = wl1271_cmd_send(wl, CMD_DFS_CHANNEL_CONFIG, cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send reg domain dfs config");
+               goto out;
+       }
+
+       ret = wl->ops->wait_for_event(wl,
+                                     WLCORE_EVENT_DFS_CONFIG_COMPLETE,
+                                     &timeout);
+       if (ret < 0 || timeout) {
+               wl1271_error("reg domain conf %serror",
+                            timeout ? "completion " : "");
+               ret = timeout ? -ETIMEDOUT : ret;
+               goto out;
+       }
+
+       memcpy(wl->reg_ch_conf_last, tmp_ch_bitmap, sizeof(tmp_ch_bitmap));
+       memset(wl->reg_ch_conf_pending, 0, sizeof(wl->reg_ch_conf_pending));
+
+out:
+       kfree(cmd);
+       return ret;
+}
+
 int wl12xx_cmd_config_fwlog(struct wl1271 *wl)
 {
        struct wl12xx_cmd_config_fwlog *cmd;
@@ -1593,12 +1781,12 @@ out:
 }
 
 static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-                         u8 role_id)
+                         u8 role_id, enum ieee80211_band band, u8 channel)
 {
        struct wl12xx_cmd_roc *cmd;
        int ret = 0;
 
-       wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", wlvif->channel, role_id);
+       wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", channel, role_id);
 
        if (WARN_ON(role_id == WL12XX_INVALID_ROLE_ID))
                return -EINVAL;
@@ -1610,8 +1798,8 @@ static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        }
 
        cmd->role_id = role_id;
-       cmd->channel = wlvif->channel;
-       switch (wlvif->band) {
+       cmd->channel = channel;
+       switch (band) {
        case IEEE80211_BAND_2GHZ:
                cmd->band = WLCORE_BAND_2_4GHZ;
                break;
@@ -1666,30 +1854,18 @@ out:
        return ret;
 }
 
-int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id)
+int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id,
+              enum ieee80211_band band, u8 channel)
 {
        int ret = 0;
-       bool is_first_roc;
 
        if (WARN_ON(test_bit(role_id, wl->roc_map)))
                return 0;
 
-       is_first_roc = (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >=
-                       WL12XX_MAX_ROLES);
-
-       ret = wl12xx_cmd_roc(wl, wlvif, role_id);
+       ret = wl12xx_cmd_roc(wl, wlvif, role_id, band, channel);
        if (ret < 0)
                goto out;
 
-       if (is_first_roc) {
-               ret = wl1271_cmd_wait_for_event(wl,
-                                          REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID);
-               if (ret < 0) {
-                       wl1271_error("cmd roc event completion error");
-                       goto out;
-               }
-       }
-
        __set_bit(role_id, wl->roc_map);
 out:
        return ret;
@@ -1719,43 +1895,7 @@ 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;
-       int ret;
-
-       wl1271_debug(DEBUG_ACX, "cmd channel switch");
-
-       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
-       if (!cmd) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       cmd->role_id = wlvif->role_id;
-       cmd->channel = ch_switch->channel->hw_value;
-       cmd->switch_time = ch_switch->count;
-       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) {
-               wl1271_error("failed to send channel switch command");
-               goto out_free;
-       }
-
-out_free:
-       kfree(cmd);
-
-out:
-       return ret;
-}
-
-int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl)
+int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct wl12xx_cmd_stop_channel_switch *cmd;
        int ret;
@@ -1768,6 +1908,8 @@ int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl)
                goto out;
        }
 
+       cmd->role_id = wlvif->role_id;
+
        ret = wl1271_cmd_send(wl, CMD_STOP_CHANNEL_SWICTH, cmd, sizeof(*cmd), 0);
        if (ret < 0) {
                wl1271_error("failed to stop channel switch command");
@@ -1782,7 +1924,8 @@ out:
 }
 
 /* start dev role and roc on its channel */
-int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                    enum ieee80211_band band, int channel)
 {
        int ret;
 
@@ -1797,11 +1940,11 @@ int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        if (ret < 0)
                goto out;
 
-       ret = wl12xx_cmd_role_start_dev(wl, wlvif);
+       ret = wl12xx_cmd_role_start_dev(wl, wlvif, band, channel);
        if (ret < 0)
                goto out_disable;
 
-       ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id);
+       ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id, band, channel);
        if (ret < 0)
                goto out_stop;
 
index 2409f3d71f63ddd457cc571b7a76798234d16e0b..fd34123047cdd0255f5a48b7826ddd117b14814d 100644 (file)
@@ -31,6 +31,8 @@ struct acx_header;
 
 int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
                    size_t res_len);
+int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len,
+                            size_t res_len, unsigned long valid_rets);
 int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type,
                           u8 *role_id);
 int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id);
@@ -39,11 +41,14 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl12xx_cmd_role_stop_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl12xx_cmd_role_start_ibss(struct wl1271 *wl, struct wl12xx_vif *wlvif);
-int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                    enum ieee80211_band band, int channel);
 int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer);
 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 wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf,
+                                 size_t len, unsigned long valid_rets);
 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, u16 auto_ps_timeout);
@@ -75,22 +80,30 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                          u16 action, u8 id, u8 key_type,
                          u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
                          u16 tx_seq_16);
-int wl12xx_cmd_set_peer_state(struct wl1271 *wl, u8 hlid);
-int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id);
+int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             u8 hlid);
+int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id,
+              enum ieee80211_band band, u8 channel);
 int wl12xx_croc(struct wl1271 *wl, u8 role_id);
 int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                        struct ieee80211_sta *sta, u8 hlid);
 int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid);
+void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel,
+                                    enum ieee80211_band band);
+int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl);
 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_cmd_stop_channel_switch(struct wl1271 *wl,
+                                  struct wl12xx_vif *wlvif);
 int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                         u8 *hlid);
 void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid);
+int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
+                                        u32 mask, bool *timeout);
 
 enum wl1271_commands {
        CMD_INTERROGATE = 1, /* use this to read information elements */
@@ -149,8 +162,11 @@ enum wl1271_commands {
        CMD_WFD_START_DISCOVERY = 45,
        CMD_WFD_STOP_DISCOVERY  = 46,
        CMD_WFD_ATTRIBUTE_CONFIG        = 47,
-       CMD_NOP                 = 48,
-       CMD_LAST_COMMAND,
+       CMD_GENERIC_CFG                 = 48,
+       CMD_NOP                         = 49,
+
+       /* start of 18xx specific commands */
+       CMD_DFS_CHANNEL_CONFIG          = 60,
 
        MAX_COMMAND_ID = 0xFFFF,
 };
@@ -167,8 +183,8 @@ enum cmd_templ {
        CMD_TEMPL_PS_POLL,
        CMD_TEMPL_KLV,
        CMD_TEMPL_DISCONNECT,
-       CMD_TEMPL_APP_PROBE_REQ_2_4,
-       CMD_TEMPL_APP_PROBE_REQ_5,
+       CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY,
+       CMD_TEMPL_APP_PROBE_REQ_5_LEGACY,
        CMD_TEMPL_BAR,           /* for firmware internal use only */
        CMD_TEMPL_CTS,           /*
                                  * For CTS-to-self (FastCTS) mechanism
@@ -179,6 +195,8 @@ enum cmd_templ {
        CMD_TEMPL_DEAUTH_AP,
        CMD_TEMPL_TEMPORARY,
        CMD_TEMPL_LINK_MEASUREMENT_REPORT,
+       CMD_TEMPL_PROBE_REQ_2_4_PERIODIC,
+       CMD_TEMPL_PROBE_REQ_5_PERIODIC,
 
        CMD_TEMPL_MAX = 0xff
 };
@@ -220,7 +238,8 @@ enum {
        CMD_STATUS_FW_RESET             = 22, /* Driver internal use.*/
        CMD_STATUS_TEMPLATE_OOM         = 23,
        CMD_STATUS_NO_RX_BA_SESSION     = 24,
-       MAX_COMMAND_STATUS              = 0xff
+
+       MAX_COMMAND_STATUS
 };
 
 #define CMDMBOX_HEADER_LEN 4
@@ -345,7 +364,15 @@ struct wl12xx_cmd_role_start {
 
                        u8 reset_tsf;
 
-                       u8 padding_1[4];
+                       /*
+                        * ap supports wmm (note that there is additional
+                        * per-sta wmm configuration)
+                        */
+                       u8 wmm;
+
+                       u8 bcast_session_id;
+                       u8 global_session_id;
+                       u8 padding_1[1];
                } __packed ap;
        };
 } __packed;
@@ -515,7 +542,14 @@ struct wl12xx_cmd_set_peer_state {
 
        u8 hlid;
        u8 state;
-       u8 padding[2];
+
+       /*
+        * wmm is relevant for sta role only.
+        * ap role configures the per-sta wmm params in
+        * the add_peer command.
+        */
+       u8 wmm;
+       u8 padding[1];
 } __packed;
 
 struct wl12xx_cmd_roc {
@@ -558,7 +592,7 @@ struct wl12xx_cmd_add_peer {
        u8 bss_index;
        u8 sp_len;
        u8 wmm;
-       u8 padding1;
+       u8 session_id;
 } __packed;
 
 struct wl12xx_cmd_remove_peer {
@@ -597,6 +631,13 @@ enum wl12xx_fwlogger_output {
        WL12XX_FWLOG_OUTPUT_HOST,
 };
 
+struct wl12xx_cmd_regdomain_dfs_config {
+       struct wl1271_cmd_header header;
+
+       __le32 ch_bit_map1;
+       __le32 ch_bit_map2;
+} __packed;
+
 struct wl12xx_cmd_config_fwlog {
        struct wl1271_cmd_header header;
 
@@ -626,27 +667,13 @@ struct wl12xx_cmd_stop_fwlog {
        struct wl1271_cmd_header header;
 } __packed;
 
-struct wl12xx_cmd_channel_switch {
+struct wl12xx_cmd_stop_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;
-       /* 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 {
-       struct wl1271_cmd_header header;
-} __packed;
-
 /* Used to check radio status after calibration */
 #define MAX_TLV_LENGTH         500
 #define TEST_CMD_P2G_CAL       2       /* TX BiP */
index 9e40760bafe17b43121585d63bacfaa1925611b3..2b96ff821341103ac935ade4765409b21e142409 100644 (file)
@@ -57,20 +57,49 @@ enum {
 };
 
 enum {
-       CONF_HW_RATE_INDEX_1MBPS   = 0,
-       CONF_HW_RATE_INDEX_2MBPS   = 1,
-       CONF_HW_RATE_INDEX_5_5MBPS = 2,
-       CONF_HW_RATE_INDEX_6MBPS   = 3,
-       CONF_HW_RATE_INDEX_9MBPS   = 4,
-       CONF_HW_RATE_INDEX_11MBPS  = 5,
-       CONF_HW_RATE_INDEX_12MBPS  = 6,
-       CONF_HW_RATE_INDEX_18MBPS  = 7,
-       CONF_HW_RATE_INDEX_22MBPS  = 8,
-       CONF_HW_RATE_INDEX_24MBPS  = 9,
-       CONF_HW_RATE_INDEX_36MBPS  = 10,
-       CONF_HW_RATE_INDEX_48MBPS  = 11,
-       CONF_HW_RATE_INDEX_54MBPS  = 12,
-       CONF_HW_RATE_INDEX_MAX     = CONF_HW_RATE_INDEX_54MBPS,
+       CONF_HW_RATE_INDEX_1MBPS      = 0,
+       CONF_HW_RATE_INDEX_2MBPS      = 1,
+       CONF_HW_RATE_INDEX_5_5MBPS    = 2,
+       CONF_HW_RATE_INDEX_11MBPS     = 3,
+       CONF_HW_RATE_INDEX_6MBPS      = 4,
+       CONF_HW_RATE_INDEX_9MBPS      = 5,
+       CONF_HW_RATE_INDEX_12MBPS     = 6,
+       CONF_HW_RATE_INDEX_18MBPS     = 7,
+       CONF_HW_RATE_INDEX_24MBPS     = 8,
+       CONF_HW_RATE_INDEX_36MBPS     = 9,
+       CONF_HW_RATE_INDEX_48MBPS     = 10,
+       CONF_HW_RATE_INDEX_54MBPS     = 11,
+       CONF_HW_RATE_INDEX_MCS0       = 12,
+       CONF_HW_RATE_INDEX_MCS1       = 13,
+       CONF_HW_RATE_INDEX_MCS2       = 14,
+       CONF_HW_RATE_INDEX_MCS3       = 15,
+       CONF_HW_RATE_INDEX_MCS4       = 16,
+       CONF_HW_RATE_INDEX_MCS5       = 17,
+       CONF_HW_RATE_INDEX_MCS6       = 18,
+       CONF_HW_RATE_INDEX_MCS7       = 19,
+       CONF_HW_RATE_INDEX_MCS7_SGI   = 20,
+       CONF_HW_RATE_INDEX_MCS0_40MHZ = 21,
+       CONF_HW_RATE_INDEX_MCS1_40MHZ = 22,
+       CONF_HW_RATE_INDEX_MCS2_40MHZ = 23,
+       CONF_HW_RATE_INDEX_MCS3_40MHZ = 24,
+       CONF_HW_RATE_INDEX_MCS4_40MHZ = 25,
+       CONF_HW_RATE_INDEX_MCS5_40MHZ = 26,
+       CONF_HW_RATE_INDEX_MCS6_40MHZ = 27,
+       CONF_HW_RATE_INDEX_MCS7_40MHZ = 28,
+       CONF_HW_RATE_INDEX_MCS7_40MHZ_SGI = 29,
+
+       /* MCS8+ rates overlap with 40Mhz rates */
+       CONF_HW_RATE_INDEX_MCS8       = 21,
+       CONF_HW_RATE_INDEX_MCS9       = 22,
+       CONF_HW_RATE_INDEX_MCS10      = 23,
+       CONF_HW_RATE_INDEX_MCS11      = 24,
+       CONF_HW_RATE_INDEX_MCS12      = 25,
+       CONF_HW_RATE_INDEX_MCS13      = 26,
+       CONF_HW_RATE_INDEX_MCS14      = 27,
+       CONF_HW_RATE_INDEX_MCS15      = 28,
+       CONF_HW_RATE_INDEX_MCS15_SGI  = 29,
+
+       CONF_HW_RATE_INDEX_MAX        = CONF_HW_RATE_INDEX_MCS7_40MHZ_SGI,
 };
 
 #define CONF_HW_RXTX_RATE_UNSUPPORTED 0xff
@@ -415,11 +444,11 @@ struct conf_rx_settings {
 #define CONF_TX_RATE_MASK_BASIC_P2P    CONF_HW_BIT_RATE_6MBPS
 
 /*
- * Rates supported for data packets when operating as AP. Note the absence
+ * Rates supported for data packets when operating as STA/AP. Note the absence
  * of the 22Mbps rate. There is a FW limitation on 12 rates so we must drop
  * one. The rate dropped is not mandatory under any operating mode.
  */
-#define CONF_TX_AP_ENABLED_RATES       (CONF_HW_BIT_RATE_1MBPS | \
+#define CONF_TX_ENABLED_RATES       (CONF_HW_BIT_RATE_1MBPS |    \
        CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS |      \
        CONF_HW_BIT_RATE_6MBPS | CONF_HW_BIT_RATE_9MBPS |        \
        CONF_HW_BIT_RATE_11MBPS | CONF_HW_BIT_RATE_12MBPS |      \
@@ -677,6 +706,18 @@ struct conf_tx_settings {
 
        /* Time in ms for Tx watchdog timer to expire */
        u32 tx_watchdog_timeout;
+
+       /*
+        * when a slow link has this much packets pending, it becomes a low
+        * priority link, scheduling-wise
+        */
+       u8 slow_link_thold;
+
+       /*
+        * when a fast link has this much packets pending, it becomes a low
+        * priority link, scheduling-wise
+        */
+       u8 fast_link_thold;
 } __packed;
 
 enum {
@@ -1047,6 +1088,7 @@ struct conf_roam_trigger_settings {
 struct conf_scan_settings {
        /*
         * The minimum time to wait on each channel for active scans
+        * This value will be used whenever there's a connected interface.
         *
         * Range: u32 tu/1000
         */
@@ -1054,24 +1096,37 @@ struct conf_scan_settings {
 
        /*
         * The maximum time to wait on each channel for active scans
+        * This value will be currently used whenever there's a
+        * connected interface. It shouldn't exceed 30000 (~30ms) to avoid
+        * possible interference of voip traffic going on while scanning.
         *
         * Range: u32 tu/1000
         */
        u32 max_dwell_time_active;
 
-       /*
-        * The minimum time to wait on each channel for passive scans
+       /* The minimum time to wait on each channel for active scans
+        * when it's possible to have longer scan dwell times.
+        * Currently this is used whenever we're idle on all interfaces.
+        * Longer dwell times improve detection of networks within a
+        * single scan.
         *
         * Range: u32 tu/1000
         */
-       u32 min_dwell_time_passive;
+       u32 min_dwell_time_active_long;
 
-       /*
-        * The maximum time to wait on each channel for passive scans
+       /* The maximum time to wait on each channel for active scans
+        * when it's possible to have longer scan dwell times.
+        * See min_dwell_time_active_long
         *
         * Range: u32 tu/1000
         */
-       u32 max_dwell_time_passive;
+       u32 max_dwell_time_active_long;
+
+       /* time to wait on the channel for passive scans (in TU/1000) */
+       u32 dwell_time_passive;
+
+       /* time to wait on the channel for DFS scans (in TU/1000) */
+       u32 dwell_time_dfs;
 
        /*
         * Number of probe requests to transmit on each active scan channel
@@ -1276,12 +1331,20 @@ struct conf_hangover_settings {
        u8 window_size;
 } __packed;
 
+struct conf_recovery_settings {
+       /* BUG() on fw recovery */
+       u8 bug_on_recovery;
+
+       /* Prevent HW recovery. FW will remain stuck. */
+       u8 no_recovery;
+} __packed;
+
 /*
  * The conf version consists of 4 bytes.  The two MSB are the wlcore
  * version, the two LSB are the lower driver's private conf
  * version.
  */
-#define WLCORE_CONF_VERSION    (0x0002 << 16)
+#define WLCORE_CONF_VERSION    (0x0005 << 16)
 #define WLCORE_CONF_MASK       0xffff0000
 #define WLCORE_CONF_SIZE       (sizeof(struct wlcore_conf_header) +    \
                                 sizeof(struct wlcore_conf))
@@ -1309,6 +1372,7 @@ struct wlcore_conf {
        struct conf_fwlog fwlog;
        struct conf_rate_policy_settings rate;
        struct conf_hangover_settings hangover;
+       struct conf_recovery_settings recovery;
 } __packed;
 
 struct wlcore_conf_file {
index c86bb00c24884d355e93af45917b30defe86f220..e70a7c864865e0decdaff4e307285235e4622b30 100644 (file)
@@ -490,7 +490,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
        DRIVER_STATE_PRINT_HEX(chip.id);
        DRIVER_STATE_PRINT_STR(chip.fw_ver_str);
        DRIVER_STATE_PRINT_STR(chip.phy_fw_ver_str);
-       DRIVER_STATE_PRINT_INT(sched_scanning);
+       DRIVER_STATE_PRINT_INT(recovery_count);
 
 #undef DRIVER_STATE_PRINT_INT
 #undef DRIVER_STATE_PRINT_LONG
@@ -560,7 +560,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
                if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
                    wlvif->bss_type == BSS_TYPE_IBSS) {
                        VIF_STATE_PRINT_INT(sta.hlid);
-                       VIF_STATE_PRINT_INT(sta.ba_rx_bitmap);
                        VIF_STATE_PRINT_INT(sta.basic_rate_idx);
                        VIF_STATE_PRINT_INT(sta.ap_rate_idx);
                        VIF_STATE_PRINT_INT(sta.p2p_rate_idx);
@@ -577,6 +576,10 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
                        VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]);
                }
                VIF_STATE_PRINT_INT(last_tx_hlid);
+               VIF_STATE_PRINT_INT(tx_queue_count[0]);
+               VIF_STATE_PRINT_INT(tx_queue_count[1]);
+               VIF_STATE_PRINT_INT(tx_queue_count[2]);
+               VIF_STATE_PRINT_INT(tx_queue_count[3]);
                VIF_STATE_PRINT_LHEX(links_map[0]);
                VIF_STATE_PRINT_NSTR(ssid, wlvif->ssid_len);
                VIF_STATE_PRINT_INT(band);
@@ -589,7 +592,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
                VIF_STATE_PRINT_INT(beacon_int);
                VIF_STATE_PRINT_INT(default_key);
                VIF_STATE_PRINT_INT(aid);
-               VIF_STATE_PRINT_INT(session_counter);
                VIF_STATE_PRINT_INT(psm_entry_retry);
                VIF_STATE_PRINT_INT(power_level);
                VIF_STATE_PRINT_INT(rssi_thold);
@@ -993,7 +995,7 @@ static ssize_t sleep_auth_write(struct file *file,
                return -EINVAL;
        }
 
-       if (value < 0 || value > WL1271_PSM_MAX) {
+       if (value > WL1271_PSM_MAX) {
                wl1271_warning("sleep_auth must be between 0 and %d",
                               WL1271_PSM_MAX);
                return -ERANGE;
index 48907054d493134354ad22b0305cbc45a89bde2f..70f289aa1bc6dd8f7991fae5eacf958e35b6d4f2 100644 (file)
 #include "scan.h"
 #include "wl12xx_80211.h"
 
-static void wl1271_event_rssi_trigger(struct wl1271 *wl,
-                                     struct wl12xx_vif *wlvif,
-                                     struct event_mailbox *mbox)
+void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr)
 {
-       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_vif *vif;
        enum nl80211_cqm_rssi_threshold_event event;
-       s8 metric = mbox->rssi_snr_trigger_metric[0];
+       s8 metric = metric_arr[0];
 
        wl1271_debug(DEBUG_EVENT, "RSSI trigger metric: %d", metric);
 
-       if (metric <= wlvif->rssi_thold)
-               event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
-       else
-               event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
-
-       if (event != wlvif->last_rssi_event)
-               ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL);
-       wlvif->last_rssi_event = event;
+       /* TODO: check actual multi-role support */
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               if (metric <= wlvif->rssi_thold)
+                       event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
+               else
+                       event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+
+               vif = wl12xx_wlvif_to_vif(wlvif);
+               if (event != wlvif->last_rssi_event)
+                       ieee80211_cqm_rssi_notify(vif, event, GFP_KERNEL);
+               wlvif->last_rssi_event = event;
+       }
 }
+EXPORT_SYMBOL_GPL(wlcore_event_rssi_trigger);
 
 static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
 
        if (wlvif->bss_type != BSS_TYPE_AP_BSS) {
-               if (!wlvif->sta.ba_rx_bitmap)
+               u8 hlid = wlvif->sta.hlid;
+               if (!wl->links[hlid].ba_bitmap)
                        return;
-               ieee80211_stop_rx_ba_session(vif, wlvif->sta.ba_rx_bitmap,
+               ieee80211_stop_rx_ba_session(vif, wl->links[hlid].ba_bitmap,
                                             vif->bss_conf.bssid);
        } else {
                u8 hlid;
@@ -74,8 +79,7 @@ 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)
+void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable)
 {
        struct wl12xx_vif *wlvif;
 
@@ -87,201 +91,169 @@ static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl,
                        wl1271_recalc_rx_streaming(wl, wlvif);
                }
        }
-
 }
+EXPORT_SYMBOL_GPL(wlcore_event_soft_gemini_sense);
 
-static void wl1271_event_mbox_dump(struct event_mailbox *mbox)
+void wlcore_event_sched_scan_completed(struct wl1271 *wl,
+                                      u8 status)
 {
-       wl1271_debug(DEBUG_EVENT, "MBOX DUMP:");
-       wl1271_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector);
-       wl1271_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask);
+       wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT (status 0x%0x)",
+                    status);
+
+       if (wl->sched_vif) {
+               ieee80211_sched_scan_stopped(wl->hw);
+               wl->sched_vif = NULL;
+       }
 }
+EXPORT_SYMBOL_GPL(wlcore_event_sched_scan_completed);
 
-static int wl1271_event_process(struct wl1271 *wl)
+void wlcore_event_ba_rx_constraint(struct wl1271 *wl,
+                                  unsigned long roles_bitmap,
+                                  unsigned long allowed_bitmap)
 {
-       struct event_mailbox *mbox = wl->mbox;
-       struct ieee80211_vif *vif;
        struct wl12xx_vif *wlvif;
-       u32 vector;
-       bool disconnect_sta = false;
-       unsigned long sta_bitmap = 0;
-       int ret;
-
-       wl1271_event_mbox_dump(mbox);
-
-       vector = le32_to_cpu(mbox->events_vector);
-       vector &= ~(le32_to_cpu(mbox->events_mask));
-       wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector);
 
-       if (vector & SCAN_COMPLETE_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "status: 0x%x",
-                            mbox->scheduled_scan_status);
-
-               wl1271_scan_stm(wl, wl->scan_vif);
-       }
+       wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx allowed=0x%lx",
+                    __func__, roles_bitmap, allowed_bitmap);
 
-       if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT "
-                            "(status 0x%0x)", mbox->scheduled_scan_status);
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
+                   !test_bit(wlvif->role_id , &roles_bitmap))
+                       continue;
 
-               wl1271_scan_sched_scan_results(wl);
+               wlvif->ba_allowed = !!test_bit(wlvif->role_id,
+                                              &allowed_bitmap);
+               if (!wlvif->ba_allowed)
+                       wl1271_stop_ba_event(wl, wlvif);
        }
+}
+EXPORT_SYMBOL_GPL(wlcore_event_ba_rx_constraint);
 
-       if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT "
-                            "(status 0x%0x)", mbox->scheduled_scan_status);
-               if (wl->sched_scanning) {
-                       ieee80211_sched_scan_stopped(wl->hw);
-                       wl->sched_scanning = false;
-               }
-       }
+void wlcore_event_channel_switch(struct wl1271 *wl,
+                                unsigned long roles_bitmap,
+                                bool success)
+{
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_vif *vif;
 
-       if (vector & SOFT_GEMINI_SENSE_EVENT_ID)
-               wl12xx_event_soft_gemini_sense(wl,
-                                              mbox->soft_gemini_sense_info);
+       wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d",
+                    __func__, roles_bitmap, success);
 
-       /*
-        * We are HW_MONITOR device. On beacon loss - queue
-        * connection loss work. Cancel it on REGAINED event.
-        */
-       if (vector & BSS_LOSE_EVENT_ID) {
-               /* TODO: check for multi-role */
-               int delay = wl->conf.conn.synch_fail_thold *
-                                       wl->conf.conn.bss_lose_timeout;
-               wl1271_info("Beacon loss detected.");
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
+                   !test_bit(wlvif->role_id , &roles_bitmap))
+                       continue;
 
-               /*
-                * if the work is already queued, it should take place. We
-                * don't want to delay the connection loss indication
-                * any more.
-                */
-               ieee80211_queue_delayed_work(wl->hw, &wl->connection_loss_work,
-                                            msecs_to_jiffies(delay));
+               if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
+                                       &wlvif->flags))
+                       continue;
 
-               wl12xx_for_each_wlvif_sta(wl, wlvif) {
-                       vif = wl12xx_wlvif_to_vif(wlvif);
+               vif = wl12xx_wlvif_to_vif(wlvif);
 
-                       ieee80211_cqm_rssi_notify(
-                                       vif,
-                                       NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
-                                       GFP_KERNEL);
-               }
+               ieee80211_chswitch_done(vif, success);
+               cancel_delayed_work(&wlvif->channel_switch_work);
        }
+}
+EXPORT_SYMBOL_GPL(wlcore_event_channel_switch);
 
-       if (vector & REGAINED_BSS_EVENT_ID) {
-               /* TODO: check for multi-role */
-               wl1271_info("Beacon regained.");
-               cancel_delayed_work(&wl->connection_loss_work);
-
-               /* sanity check - we can't lose and gain the beacon together */
-               WARN(vector & BSS_LOSE_EVENT_ID,
-                    "Concurrent beacon loss and gain from FW");
-       }
+void wlcore_event_dummy_packet(struct wl1271 *wl)
+{
+       wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
+       wl1271_tx_dummy_packet(wl);
+}
+EXPORT_SYMBOL_GPL(wlcore_event_dummy_packet);
 
-       if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
-               /* TODO: check actual multi-role support */
-               wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT");
-               wl12xx_for_each_wlvif_sta(wl, wlvif) {
-                       wl1271_event_rssi_trigger(wl, wlvif, mbox);
+static void wlcore_disconnect_sta(struct wl1271 *wl, unsigned long sta_bitmap)
+{
+       u32 num_packets = wl->conf.tx.max_tx_retries;
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_vif *vif;
+       struct ieee80211_sta *sta;
+       const u8 *addr;
+       int h;
+
+       for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) {
+               bool found = false;
+               /* find the ap vif connected to this sta */
+               wl12xx_for_each_wlvif_ap(wl, wlvif) {
+                       if (!test_bit(h, wlvif->ap.sta_hlid_map))
+                               continue;
+                       found = true;
+                       break;
                }
-       }
+               if (!found)
+                       continue;
 
-       if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) {
-               u8 role_id = mbox->role_id;
-               wl1271_debug(DEBUG_EVENT, "BA_SESSION_RX_CONSTRAINT_EVENT_ID. "
-                            "ba_allowed = 0x%x, role_id=%d",
-                            mbox->rx_ba_allowed, role_id);
+               vif = wl12xx_wlvif_to_vif(wlvif);
+               addr = wl->links[h].addr;
 
-               wl12xx_for_each_wlvif(wl, wlvif) {
-                       if (role_id != 0xff && role_id != wlvif->role_id)
-                               continue;
-
-                       wlvif->ba_allowed = !!mbox->rx_ba_allowed;
-                       if (!wlvif->ba_allowed)
-                               wl1271_stop_ba_event(wl, wlvif);
+               rcu_read_lock();
+               sta = ieee80211_find_sta(vif, addr);
+               if (sta) {
+                       wl1271_debug(DEBUG_EVENT, "remove sta %d", h);
+                       ieee80211_report_low_ack(sta, num_packets);
                }
+               rcu_read_unlock();
        }
+}
 
-       if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "CHANNEL_SWITCH_COMPLETE_EVENT_ID. "
-                                         "status = 0x%x",
-                                         mbox->channel_switch_status);
-               /*
-                * That event uses for two cases:
-                * 1) channel switch complete with status=0
-                * 2) channel switch failed status=1
-                */
-
-               /* TODO: configure only the relevant vif */
-               wl12xx_for_each_wlvif_sta(wl, wlvif) {
-                       bool success;
-
-                       if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS,
-                                               &wlvif->flags))
-                               continue;
-
-                       success = mbox->channel_switch_status ? false : true;
-                       vif = wl12xx_wlvif_to_vif(wlvif);
+void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap)
+{
+       wl1271_debug(DEBUG_EVENT, "MAX_TX_FAILURE_EVENT_ID");
+       wlcore_disconnect_sta(wl, sta_bitmap);
+}
+EXPORT_SYMBOL_GPL(wlcore_event_max_tx_failure);
 
-                       ieee80211_chswitch_done(vif, success);
-               }
-       }
+void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap)
+{
+       wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID");
+       wlcore_disconnect_sta(wl, sta_bitmap);
+}
+EXPORT_SYMBOL_GPL(wlcore_event_inactive_sta);
 
-       if ((vector & DUMMY_PACKET_EVENT_ID)) {
-               wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
-               ret = wl1271_tx_dummy_packet(wl);
-               if (ret < 0)
-                       return ret;
-       }
+void wlcore_event_roc_complete(struct wl1271 *wl)
+{
+       wl1271_debug(DEBUG_EVENT, "REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID");
+       if (wl->roc_vif)
+               ieee80211_ready_on_channel(wl->hw);
+}
+EXPORT_SYMBOL_GPL(wlcore_event_roc_complete);
 
+void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap)
+{
        /*
-        * "TX retries exceeded" has a different meaning according to mode.
-        * In AP mode the offending station is disconnected.
+        * We are HW_MONITOR device. On beacon loss - queue
+        * connection loss work. Cancel it on REGAINED event.
         */
-       if (vector & MAX_TX_RETRY_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID");
-               sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded);
-               disconnect_sta = true;
-       }
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_vif *vif;
+       int delay = wl->conf.conn.synch_fail_thold *
+                               wl->conf.conn.bss_lose_timeout;
 
-       if (vector & INACTIVE_STA_EVENT_ID) {
-               wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID");
-               sta_bitmap |= le16_to_cpu(mbox->sta_aging_status);
-               disconnect_sta = true;
-       }
+       wl1271_info("Beacon loss detected. roles:0x%lx", roles_bitmap);
 
-       if (disconnect_sta) {
-               u32 num_packets = wl->conf.tx.max_tx_retries;
-               struct ieee80211_sta *sta;
-               const u8 *addr;
-               int h;
-
-               for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) {
-                       bool found = false;
-                       /* find the ap vif connected to this sta */
-                       wl12xx_for_each_wlvif_ap(wl, wlvif) {
-                               if (!test_bit(h, wlvif->ap.sta_hlid_map))
-                                       continue;
-                               found = true;
-                               break;
-                       }
-                       if (!found)
-                               continue;
+       wl12xx_for_each_wlvif_sta(wl, wlvif) {
+               if (wlvif->role_id == WL12XX_INVALID_ROLE_ID ||
+                   !test_bit(wlvif->role_id , &roles_bitmap))
+                       continue;
 
-                       vif = wl12xx_wlvif_to_vif(wlvif);
-                       addr = wl->links[h].addr;
+               /*
+                * if the work is already queued, it should take place.
+                * We don't want to delay the connection loss
+                * indication any more.
+                */
+               ieee80211_queue_delayed_work(wl->hw,
+                                            &wlvif->connection_loss_work,
+                                            msecs_to_jiffies(delay));
 
-                       rcu_read_lock();
-                       sta = ieee80211_find_sta(vif, addr);
-                       if (sta) {
-                               wl1271_debug(DEBUG_EVENT, "remove sta %d", h);
-                               ieee80211_report_low_ack(sta, num_packets);
-                       }
-                       rcu_read_unlock();
-               }
+               vif = wl12xx_wlvif_to_vif(wlvif);
+               ieee80211_cqm_rssi_notify(
+                               vif,
+                               NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
+                               GFP_KERNEL);
        }
-       return 0;
 }
+EXPORT_SYMBOL_GPL(wlcore_event_beacon_loss);
 
 int wl1271_event_unmask(struct wl1271 *wl)
 {
@@ -305,12 +277,12 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num)
 
        /* first we read the mbox descriptor */
        ret = wlcore_read(wl, wl->mbox_ptr[mbox_num], wl->mbox,
-                         sizeof(*wl->mbox), false);
+                         wl->mbox_size, false);
        if (ret < 0)
                return ret;
 
        /* process the descriptor */
-       ret = wl1271_event_process(wl);
+       ret = wl->ops->process_mailbox_events(wl);
        if (ret < 0)
                return ret;
 
index 8adf18d6c58f74c9bbdaa90474a83466ece2b50b..acc7a59d3828f1c40f62e09c7344b72d5942d9e3 100644 (file)
@@ -46,33 +46,17 @@ enum {
        RSSI_SNR_TRIGGER_5_EVENT_ID              = BIT(5),
        RSSI_SNR_TRIGGER_6_EVENT_ID              = BIT(6),
        RSSI_SNR_TRIGGER_7_EVENT_ID              = BIT(7),
-       MEASUREMENT_START_EVENT_ID               = BIT(8),
-       MEASUREMENT_COMPLETE_EVENT_ID            = BIT(9),
-       SCAN_COMPLETE_EVENT_ID                   = BIT(10),
-       WFD_DISCOVERY_COMPLETE_EVENT_ID          = BIT(11),
-       AP_DISCOVERY_COMPLETE_EVENT_ID           = BIT(12),
-       RESERVED1                                = BIT(13),
-       PSPOLL_DELIVERY_FAILURE_EVENT_ID         = BIT(14),
-       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),
-       MAX_TX_RETRY_EVENT_ID                    = BIT(20),
-       DUMMY_PACKET_EVENT_ID                    = BIT(21),
-       SOFT_GEMINI_SENSE_EVENT_ID               = BIT(22),
-       CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID        = BIT(23),
-       SOFT_GEMINI_AVALANCHE_EVENT_ID           = BIT(24),
-       PLT_RX_CALIBRATION_COMPLETE_EVENT_ID     = BIT(25),
-       INACTIVE_STA_EVENT_ID                    = BIT(26),
-       PEER_REMOVE_COMPLETE_EVENT_ID            = BIT(27),
-       PERIODIC_SCAN_COMPLETE_EVENT_ID          = BIT(28),
-       PERIODIC_SCAN_REPORT_EVENT_ID            = BIT(29),
-       BA_SESSION_RX_CONSTRAINT_EVENT_ID        = BIT(30),
-       REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID      = BIT(31),
+
        EVENT_MBOX_ALL_EVENT_ID                  = 0x7fffffff,
 };
 
+/* events the driver might want to wait for */
+enum wlcore_wait_event {
+       WLCORE_EVENT_ROLE_STOP_COMPLETE,
+       WLCORE_EVENT_PEER_REMOVE_COMPLETE,
+       WLCORE_EVENT_DFS_CONFIG_COMPLETE
+};
+
 enum {
        EVENT_ENTER_POWER_SAVE_FAIL = 0,
        EVENT_ENTER_POWER_SAVE_SUCCESS,
@@ -80,61 +64,24 @@ enum {
 
 #define NUM_OF_RSSI_SNR_TRIGGERS 8
 
-struct event_mailbox {
-       __le32 events_vector;
-       __le32 events_mask;
-       __le32 reserved_1;
-       __le32 reserved_2;
-
-       u8 number_of_scan_results;
-       u8 scan_tag;
-       u8 completed_scan_status;
-       u8 reserved_3;
-
-       u8 soft_gemini_sense_info;
-       u8 soft_gemini_protective_info;
-       s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS];
-       u8 change_auto_mode_timeout;
-       u8 scheduled_scan_status;
-       u8 reserved4;
-       /* tuned channel (roc) */
-       u8 roc_channel;
-
-       __le16 hlid_removed_bitmap;
-
-       /* bitmap of aged stations (by HLID) */
-       __le16 sta_aging_status;
-
-       /* bitmap of stations (by HLID) which exceeded max tx retries */
-       __le16 sta_tx_retry_exceeded;
-
-       /* discovery completed results */
-       u8 discovery_tag;
-       u8 number_of_preq_results;
-       u8 number_of_prsp_results;
-       u8 reserved_5;
-
-       /* rx ba constraint */
-       u8 role_id; /* 0xFF means any role. */
-       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 reserved_8[9];
-} __packed;
-
 struct wl1271;
 
 int wl1271_event_unmask(struct wl1271 *wl);
 int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
 
+void wlcore_event_soft_gemini_sense(struct wl1271 *wl, u8 enable);
+void wlcore_event_sched_scan_completed(struct wl1271 *wl,
+                                      u8 status);
+void wlcore_event_ba_rx_constraint(struct wl1271 *wl,
+                                  unsigned long roles_bitmap,
+                                  unsigned long allowed_bitmap);
+void wlcore_event_channel_switch(struct wl1271 *wl,
+                                unsigned long roles_bitmap,
+                                bool success);
+void wlcore_event_beacon_loss(struct wl1271 *wl, unsigned long roles_bitmap);
+void wlcore_event_dummy_packet(struct wl1271 *wl);
+void wlcore_event_max_tx_failure(struct wl1271 *wl, unsigned long sta_bitmap);
+void wlcore_event_inactive_sta(struct wl1271 *wl, unsigned long sta_bitmap);
+void wlcore_event_roc_complete(struct wl1271 *wl);
+void wlcore_event_rssi_trigger(struct wl1271 *wl, s8 *metric_arr);
 #endif
index 2673d783ec1ecdda33d6dcd6f265f12dc6930c5e..7fd260c02a0a15aa2a0b8c92867a09fc9af96739 100644 (file)
@@ -201,4 +201,45 @@ wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len)
        return buf_offset;
 }
 
+static inline void
+wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                       struct ieee80211_sta *sta, u32 changed)
+{
+       if (wl->ops->sta_rc_update)
+               wl->ops->sta_rc_update(wl, wlvif, sta, changed);
+}
+
+static inline int
+wlcore_hw_set_peer_cap(struct wl1271 *wl,
+                      struct ieee80211_sta_ht_cap *ht_cap,
+                      bool allow_ht_operation,
+                      u32 rate_set, u8 hlid)
+{
+       if (wl->ops->set_peer_cap)
+               return wl->ops->set_peer_cap(wl, ht_cap, allow_ht_operation,
+                                            rate_set, hlid);
+
+       return 0;
+}
+
+static inline bool
+wlcore_hw_lnk_high_prio(struct wl1271 *wl, u8 hlid,
+                       struct wl1271_link *lnk)
+{
+       if (!wl->ops->lnk_high_prio)
+               BUG_ON(1);
+
+       return wl->ops->lnk_high_prio(wl, hlid, lnk);
+}
+
+static inline bool
+wlcore_hw_lnk_low_prio(struct wl1271 *wl, u8 hlid,
+                      struct wl1271_link *lnk)
+{
+       if (!wl->ops->lnk_low_prio)
+               BUG_ON(1);
+
+       return wl->ops->lnk_low_prio(wl, hlid, lnk);
+}
+
 #endif
index 32d157f62f3116f32dfc2b562aad770c0e9da907..5c6f11e157d9b0016633dfe932f34984fedeb65c 100644 (file)
@@ -41,14 +41,14 @@ int wl1271_init_templates_config(struct wl1271 *wl)
 
        /* send empty templates for fw memory reservation */
        ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
-                                     CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL,
+                                     wl->scan_templ_id_2_4, NULL,
                                      WL1271_CMD_TEMPL_MAX_SIZE,
                                      0, WL1271_RATE_AUTOMATIC);
        if (ret < 0)
                return ret;
 
        ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
-                                     CMD_TEMPL_CFG_PROBE_REQ_5,
+                                     wl->scan_templ_id_5,
                                      NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0,
                                      WL1271_RATE_AUTOMATIC);
        if (ret < 0)
@@ -56,14 +56,16 @@ int wl1271_init_templates_config(struct wl1271 *wl)
 
        if (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL) {
                ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
-                                             CMD_TEMPL_APP_PROBE_REQ_2_4, NULL,
+                                             wl->sched_scan_templ_id_2_4,
+                                             NULL,
                                              WL1271_CMD_TEMPL_MAX_SIZE,
                                              0, WL1271_RATE_AUTOMATIC);
                if (ret < 0)
                        return ret;
 
                ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
-                                             CMD_TEMPL_APP_PROBE_REQ_5, NULL,
+                                             wl->sched_scan_templ_id_5,
+                                             NULL,
                                              WL1271_CMD_TEMPL_MAX_SIZE,
                                              0, WL1271_RATE_AUTOMATIC);
                if (ret < 0)
@@ -463,7 +465,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
                supported_rates = CONF_TX_OFDM_RATES;
        else
-               supported_rates = CONF_TX_AP_ENABLED_RATES;
+               supported_rates = CONF_TX_ENABLED_RATES;
 
        /* unconditionally enable HT rates */
        supported_rates |= CONF_TX_MCS_RATES;
@@ -575,9 +577,6 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif)
                /* Configure for power according to debugfs */
                if (sta_auth != WL1271_PSM_ILLEGAL)
                        ret = wl1271_acx_sleep_auth(wl, sta_auth);
-               /* Configure for power always on */
-               else if (wl->quirks & WLCORE_QUIRK_NO_ELP)
-                       ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
                /* Configure for ELP power saving */
                else
                        ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
@@ -679,6 +678,10 @@ int wl1271_hw_init(struct wl1271 *wl)
        if (ret < 0)
                return ret;
 
+       ret = wlcore_cmd_regdomain_config_locked(wl);
+       if (ret < 0)
+               return ret;
+
        /* Bluetooth WLAN coexistence */
        ret = wl1271_init_pta(wl);
        if (ret < 0)
index f48530fec14fb3ba9563e1763c2c38fa46b9488d..af7d9f9b3b4db2140006fb0639af3ff028af79e5 100644 (file)
@@ -105,13 +105,13 @@ static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr,
 {
        int ret;
 
-       ret = wlcore_raw_read(wl, addr, &wl->buffer_32,
-                             sizeof(wl->buffer_32), false);
+       ret = wlcore_raw_read(wl, addr, wl->buffer_32,
+                             sizeof(*wl->buffer_32), false);
        if (ret < 0)
                return ret;
 
        if (val)
-               *val = le32_to_cpu(wl->buffer_32);
+               *val = le32_to_cpu(*wl->buffer_32);
 
        return 0;
 }
@@ -119,9 +119,9 @@ static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr,
 static inline int __must_check wlcore_raw_write32(struct wl1271 *wl, int addr,
                                                  u32 val)
 {
-       wl->buffer_32 = cpu_to_le32(val);
-       return wlcore_raw_write(wl, addr, &wl->buffer_32,
-                               sizeof(wl->buffer_32), false);
+       *wl->buffer_32 = cpu_to_le32(val);
+       return wlcore_raw_write(wl, addr, wl->buffer_32,
+                               sizeof(*wl->buffer_32), false);
 }
 
 static inline int __must_check wlcore_read(struct wl1271 *wl, int addr,
index d7de06359ae1f71478bdbf47ba5b9140c9083eb3..d42b58086f2ae841262c21540021d594c7c385f4 100644 (file)
@@ -56,8 +56,8 @@
 #define WL1271_BOOT_RETRIES 3
 
 static char *fwlog_param;
-static bool bug_on_recovery;
-static bool no_recovery;
+static int bug_on_recovery = -1;
+static int no_recovery     = -1;
 
 static void __wl1271_op_remove_interface(struct wl1271 *wl,
                                         struct ieee80211_vif *vif,
@@ -79,12 +79,10 @@ static int wl12xx_set_authorized(struct wl1271 *wl,
        if (test_and_set_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags))
                return 0;
 
-       ret = wl12xx_cmd_set_peer_state(wl, wlvif->sta.hlid);
+       ret = wl12xx_cmd_set_peer_state(wl, wlvif, wlvif->sta.hlid);
        if (ret < 0)
                return ret;
 
-       wl12xx_croc(wl, wlvif->role_id);
-
        wl1271_info("Association completed.");
        return 0;
 }
@@ -95,6 +93,8 @@ static int wl1271_reg_notify(struct wiphy *wiphy,
        struct ieee80211_supported_band *band;
        struct ieee80211_channel *ch;
        int i;
+       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+       struct wl1271 *wl = hw->priv;
 
        band = wiphy->bands[IEEE80211_BAND_5GHZ];
        for (i = 0; i < band->n_channels; i++) {
@@ -108,6 +108,9 @@ static int wl1271_reg_notify(struct wiphy *wiphy,
 
        }
 
+       if (likely(wl->state == WLCORE_STATE_ON))
+               wlcore_regdomain_config(wl);
+
        return 0;
 }
 
@@ -303,6 +306,7 @@ out:
 static void wlcore_adjust_conf(struct wl1271 *wl)
 {
        /* Adjust settings according to optional module parameters */
+
        if (fwlog_param) {
                if (!strcmp(fwlog_param, "continuous")) {
                        wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
@@ -318,16 +322,22 @@ static void wlcore_adjust_conf(struct wl1271 *wl)
                        wl1271_error("Unknown fwlog parameter %s", fwlog_param);
                }
        }
+
+       if (bug_on_recovery != -1)
+               wl->conf.recovery.bug_on_recovery = (u8) bug_on_recovery;
+
+       if (no_recovery != -1)
+               wl->conf.recovery.no_recovery = (u8) no_recovery;
 }
 
 static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
                                        struct wl12xx_vif *wlvif,
                                        u8 hlid, u8 tx_pkts)
 {
-       bool fw_ps, single_sta;
+       bool fw_ps, single_link;
 
        fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
-       single_sta = (wl->active_sta_count == 1);
+       single_link = (wl->active_link_count == 1);
 
        /*
         * Wake up from high level PS if the STA is asleep with too little
@@ -338,10 +348,10 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
 
        /*
         * Start high-level PS if the STA is asleep with enough blocks in FW.
-        * Make an exception if this is the only connected station. In this
-        * case FW-memory congestion is not a problem.
+        * Make an exception if this is the only connected link. In this
+        * case FW-memory congestion is less of a problem.
         */
-       else if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
+       else if (!single_link && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
                wl12xx_ps_link_start(wl, wlvif, hlid, true);
 }
 
@@ -349,11 +359,8 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                                           struct wl12xx_vif *wlvif,
                                           struct wl_fw_status_2 *status)
 {
-       struct wl1271_link *lnk;
        u32 cur_fw_ps_map;
-       u8 hlid, cnt;
-
-       /* TODO: also use link_fast_bitmap here */
+       u8 hlid;
 
        cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
        if (wl->ap_fw_ps_map != cur_fw_ps_map) {
@@ -365,17 +372,9 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                wl->ap_fw_ps_map = cur_fw_ps_map;
        }
 
-       for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS) {
-               lnk = &wl->links[hlid];
-               cnt = status->counters.tx_lnk_free_pkts[hlid] -
-                       lnk->prev_freed_pkts;
-
-               lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[hlid];
-               lnk->allocated_pkts -= cnt;
-
+       for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS)
                wl12xx_irq_ps_regulate_link(wl, wlvif, hlid,
-                                           lnk->allocated_pkts);
-       }
+                                           wl->links[hlid].allocated_pkts);
 }
 
 static int wlcore_fw_status(struct wl1271 *wl,
@@ -389,6 +388,7 @@ static int wlcore_fw_status(struct wl1271 *wl,
        int i;
        size_t status_len;
        int ret;
+       struct wl1271_link *lnk;
 
        status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
                sizeof(*status_2) + wl->fw_status_priv_len;
@@ -414,6 +414,17 @@ static int wlcore_fw_status(struct wl1271 *wl,
                wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i];
        }
 
+
+       for_each_set_bit(i, wl->links_map, WL12XX_MAX_LINKS) {
+               lnk = &wl->links[i];
+               /* prevent wrap-around in freed-packets counter */
+               lnk->allocated_pkts -=
+                       (status_2->counters.tx_lnk_free_pkts[i] -
+                        lnk->prev_freed_pkts) & 0xff;
+
+               lnk->prev_freed_pkts = status_2->counters.tx_lnk_free_pkts[i];
+       }
+
        /* prevent wrap-around in total blocks counter */
        if (likely(wl->tx_blocks_freed <=
                   le32_to_cpu(status_2->total_released_blks)))
@@ -466,6 +477,8 @@ static int wlcore_fw_status(struct wl1271 *wl,
        wl->time_offset = (timespec_to_ns(&ts) >> 10) -
                (s64)le32_to_cpu(status_2->fw_localtime);
 
+       wl->fw_fast_lnk_map = le32_to_cpu(status_2->link_fast_bitmap);
+
        return 0;
 }
 
@@ -802,11 +815,13 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
        /*
         * Make sure the chip is awake and the logger isn't active.
-        * Do not send a stop fwlog command if the fw is hanged.
+        * Do not send a stop fwlog command if the fw is hanged or if
+        * dbgpins are used (due to some fw bug).
         */
        if (wl1271_ps_elp_wakeup(wl))
                goto out;
-       if (!wl->watchdog_recovery)
+       if (!wl->watchdog_recovery &&
+           wl->conf.fwlog.output != WL12XX_FWLOG_OUTPUT_DBG_PINS)
                wl12xx_cmd_stop_fwlog(wl);
 
        /* Read the first memory block address */
@@ -874,7 +889,8 @@ static void wlcore_print_recovery(struct wl1271 *wl)
        if (ret < 0)
                return;
 
-       wl1271_info("pc: 0x%x, hint_sts: 0x%08x", pc, hint_sts);
+       wl1271_info("pc: 0x%x, hint_sts: 0x%08x count: %d",
+                               pc, hint_sts, ++wl->recovery_count);
 
        wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
 }
@@ -897,10 +913,10 @@ static void wl1271_recovery_work(struct work_struct *work)
                wlcore_print_recovery(wl);
        }
 
-       BUG_ON(bug_on_recovery &&
+       BUG_ON(wl->conf.recovery.bug_on_recovery &&
               !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
 
-       if (no_recovery) {
+       if (wl->conf.recovery.no_recovery) {
                wl1271_info("No recovery (chosen on module load). Fw will remain stuck.");
                goto out_unlock;
        }
@@ -920,11 +936,6 @@ static void wl1271_recovery_work(struct work_struct *work)
        /* Prevent spurious TX during FW restart */
        wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
 
-       if (wl->sched_scanning) {
-               ieee80211_sched_scan_stopped(wl->hw);
-               wl->sched_scanning = false;
-       }
-
        /* reboot the chipset */
        while (!list_empty(&wl->wlvif_list)) {
                wlvif = list_first_entry(&wl->wlvif_list,
@@ -1141,7 +1152,6 @@ int wl1271_plt_stop(struct wl1271 *wl)
        cancel_work_sync(&wl->recovery_work);
        cancel_delayed_work_sync(&wl->elp_work);
        cancel_delayed_work_sync(&wl->tx_watchdog_work);
-       cancel_delayed_work_sync(&wl->connection_loss_work);
 
        mutex_lock(&wl->mutex);
        wl1271_power_off(wl);
@@ -1169,9 +1179,13 @@ static void wl1271_op_tx(struct ieee80211_hw *hw,
        int q, mapping;
        u8 hlid;
 
-       if (vif)
-               wlvif = wl12xx_vif_to_data(vif);
+       if (!vif) {
+               wl1271_debug(DEBUG_TX, "DROP skb with no vif");
+               ieee80211_free_txskb(hw, skb);
+               return;
+       }
 
+       wlvif = wl12xx_vif_to_data(vif);
        mapping = skb_get_queue_mapping(skb);
        q = wl1271_tx_get_queue(mapping);
 
@@ -1185,9 +1199,9 @@ static void wl1271_op_tx(struct ieee80211_hw *hw,
         * allow these packets through.
         */
        if (hlid == WL12XX_INVALID_LINK_ID ||
-           (wlvif && !test_bit(hlid, wlvif->links_map)) ||
-            (wlcore_is_queue_stopped(wl, q) &&
-             !wlcore_is_queue_stopped_by_reason(wl, q,
+           (!test_bit(hlid, wlvif->links_map)) ||
+            (wlcore_is_queue_stopped_locked(wl, wlvif, q) &&
+             !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q,
                        WLCORE_QUEUE_STOP_REASON_WATERMARK))) {
                wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q);
                ieee80211_free_txskb(hw, skb);
@@ -1199,16 +1213,17 @@ static void wl1271_op_tx(struct ieee80211_hw *hw,
        skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
 
        wl->tx_queue_count[q]++;
+       wlvif->tx_queue_count[q]++;
 
        /*
         * The workqueue is slow to process the tx_queue and we need stop
         * the queue here, otherwise the queue will get too long.
         */
-       if (wl->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK &&
-           !wlcore_is_queue_stopped_by_reason(wl, q,
+       if (wlvif->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK &&
+           !wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, q,
                                        WLCORE_QUEUE_STOP_REASON_WATERMARK)) {
                wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q);
-               wlcore_stop_queue_locked(wl, q,
+               wlcore_stop_queue_locked(wl, wlvif, q,
                                         WLCORE_QUEUE_STOP_REASON_WATERMARK);
        }
 
@@ -1843,11 +1858,10 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
        cancel_work_sync(&wl->tx_work);
        cancel_delayed_work_sync(&wl->elp_work);
        cancel_delayed_work_sync(&wl->tx_watchdog_work);
-       cancel_delayed_work_sync(&wl->connection_loss_work);
 
        /* let's notify MAC80211 about the remaining pending TX frames */
-       wl12xx_tx_reset(wl);
        mutex_lock(&wl->mutex);
+       wl12xx_tx_reset(wl);
 
        wl1271_power_off(wl);
        /*
@@ -1870,14 +1884,17 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
        wl->time_offset = 0;
        wl->ap_fw_ps_map = 0;
        wl->ap_ps_map = 0;
-       wl->sched_scanning = false;
        wl->sleep_auth = WL1271_PSM_ILLEGAL;
        memset(wl->roles_map, 0, sizeof(wl->roles_map));
        memset(wl->links_map, 0, sizeof(wl->links_map));
        memset(wl->roc_map, 0, sizeof(wl->roc_map));
+       memset(wl->session_ids, 0, sizeof(wl->session_ids));
        wl->active_sta_count = 0;
+       wl->active_link_count = 0;
 
        /* The system link is always allocated */
+       wl->links[WL12XX_SYSTEM_HLID].allocated_pkts = 0;
+       wl->links[WL12XX_SYSTEM_HLID].prev_freed_pkts = 0;
        __set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
 
        /*
@@ -1903,6 +1920,12 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
        wl->tx_res_if = NULL;
        kfree(wl->target_mem_map);
        wl->target_mem_map = NULL;
+
+       /*
+        * FW channels must be re-calibrated after recovery,
+        * clear the last Reg-Domain channel configuration.
+        */
+       memset(wl->reg_ch_conf_last, 0, sizeof(wl->reg_ch_conf_last));
 }
 
 static void wlcore_op_stop(struct ieee80211_hw *hw)
@@ -1918,6 +1941,71 @@ static void wlcore_op_stop(struct ieee80211_hw *hw)
        mutex_unlock(&wl->mutex);
 }
 
+static void wlcore_channel_switch_work(struct work_struct *work)
+{
+       struct delayed_work *dwork;
+       struct wl1271 *wl;
+       struct ieee80211_vif *vif;
+       struct wl12xx_vif *wlvif;
+       int ret;
+
+       dwork = container_of(work, struct delayed_work, work);
+       wlvif = container_of(dwork, struct wl12xx_vif, channel_switch_work);
+       wl = wlvif->wl;
+
+       wl1271_info("channel switch failed (role_id: %d).", wlvif->role_id);
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       /* check the channel switch is still ongoing */
+       if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags))
+               goto out;
+
+       vif = wl12xx_wlvif_to_vif(wlvif);
+       ieee80211_chswitch_done(vif, false);
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       wl12xx_cmd_stop_channel_switch(wl, wlvif);
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
+static void wlcore_connection_loss_work(struct work_struct *work)
+{
+       struct delayed_work *dwork;
+       struct wl1271 *wl;
+       struct ieee80211_vif *vif;
+       struct wl12xx_vif *wlvif;
+
+       dwork = container_of(work, struct delayed_work, work);
+       wlvif = container_of(dwork, struct wl12xx_vif, connection_loss_work);
+       wl = wlvif->wl;
+
+       wl1271_info("Connection loss work (role_id: %d).", wlvif->role_id);
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       /* Call mac80211 connection loss */
+       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+               goto out;
+
+       vif = wl12xx_wlvif_to_vif(wlvif);
+       ieee80211_connection_loss(vif);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
 static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx)
 {
        u8 policy = find_first_zero_bit(wl->rate_policies_map,
@@ -2037,15 +2125,15 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
                for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
                        wl12xx_allocate_rate_policy(wl,
                                                &wlvif->ap.ucast_rate_idx[i]);
-               wlvif->basic_rate_set = CONF_TX_AP_ENABLED_RATES;
+               wlvif->basic_rate_set = CONF_TX_ENABLED_RATES;
                /*
                 * TODO: check if basic_rate shouldn't be
                 * wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
                 * instead (the same thing for STA above).
                */
-               wlvif->basic_rate = CONF_TX_AP_ENABLED_RATES;
+               wlvif->basic_rate = CONF_TX_ENABLED_RATES;
                /* TODO: this seems to be used only for STA, check it */
-               wlvif->rate_set = CONF_TX_AP_ENABLED_RATES;
+               wlvif->rate_set = CONF_TX_ENABLED_RATES;
        }
 
        wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate;
@@ -2065,6 +2153,10 @@ 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->channel_switch_work,
+                         wlcore_channel_switch_work);
+       INIT_DELAYED_WORK(&wlvif->connection_loss_work,
+                         wlcore_connection_loss_work);
        INIT_LIST_HEAD(&wlvif->list);
 
        setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
@@ -2198,6 +2290,81 @@ static void wl12xx_force_active_psm(struct wl1271 *wl)
        }
 }
 
+struct wlcore_hw_queue_iter_data {
+       unsigned long hw_queue_map[BITS_TO_LONGS(WLCORE_NUM_MAC_ADDRESSES)];
+       /* current vif */
+       struct ieee80211_vif *vif;
+       /* is the current vif among those iterated */
+       bool cur_running;
+};
+
+static void wlcore_hw_queue_iter(void *data, u8 *mac,
+                                struct ieee80211_vif *vif)
+{
+       struct wlcore_hw_queue_iter_data *iter_data = data;
+
+       if (WARN_ON_ONCE(vif->hw_queue[0] == IEEE80211_INVAL_HW_QUEUE))
+               return;
+
+       if (iter_data->cur_running || vif == iter_data->vif) {
+               iter_data->cur_running = true;
+               return;
+       }
+
+       __set_bit(vif->hw_queue[0] / NUM_TX_QUEUES, iter_data->hw_queue_map);
+}
+
+static int wlcore_allocate_hw_queue_base(struct wl1271 *wl,
+                                        struct wl12xx_vif *wlvif)
+{
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+       struct wlcore_hw_queue_iter_data iter_data = {};
+       int i, q_base;
+
+       iter_data.vif = vif;
+
+       /* mark all bits taken by active interfaces */
+       ieee80211_iterate_active_interfaces_atomic(wl->hw,
+                                       IEEE80211_IFACE_ITER_RESUME_ALL,
+                                       wlcore_hw_queue_iter, &iter_data);
+
+       /* the current vif is already running in mac80211 (resume/recovery) */
+       if (iter_data.cur_running) {
+               wlvif->hw_queue_base = vif->hw_queue[0];
+               wl1271_debug(DEBUG_MAC80211,
+                            "using pre-allocated hw queue base %d",
+                            wlvif->hw_queue_base);
+
+               /* interface type might have changed type */
+               goto adjust_cab_queue;
+       }
+
+       q_base = find_first_zero_bit(iter_data.hw_queue_map,
+                                    WLCORE_NUM_MAC_ADDRESSES);
+       if (q_base >= WLCORE_NUM_MAC_ADDRESSES)
+               return -EBUSY;
+
+       wlvif->hw_queue_base = q_base * NUM_TX_QUEUES;
+       wl1271_debug(DEBUG_MAC80211, "allocating hw queue base: %d",
+                    wlvif->hw_queue_base);
+
+       for (i = 0; i < NUM_TX_QUEUES; i++) {
+               wl->queue_stop_reasons[wlvif->hw_queue_base + i] = 0;
+               /* register hw queues in mac80211 */
+               vif->hw_queue[i] = wlvif->hw_queue_base + i;
+       }
+
+adjust_cab_queue:
+       /* the last places are reserved for cab queues per interface */
+       if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+               vif->cab_queue = NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES +
+                                wlvif->hw_queue_base / NUM_TX_QUEUES;
+       else
+               vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
+
+       return 0;
+}
+
 static int wl1271_op_add_interface(struct ieee80211_hw *hw,
                                   struct ieee80211_vif *vif)
 {
@@ -2244,6 +2411,10 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
                goto out;
        }
 
+       ret = wlcore_allocate_hw_queue_base(wl, wlvif);
+       if (ret < 0)
+               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);
@@ -2314,7 +2485,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
        wl1271_info("down");
 
        if (wl->scan.state != WL1271_SCAN_STATE_IDLE &&
-           wl->scan_vif == vif) {
+           wl->scan_wlvif == wlvif) {
                /*
                 * Rearm the tx watchdog just before idling scan. This
                 * prevents just-finished scans from triggering the watchdog
@@ -2323,11 +2494,21 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
 
                wl->scan.state = WL1271_SCAN_STATE_IDLE;
                memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
-               wl->scan_vif = NULL;
+               wl->scan_wlvif = NULL;
                wl->scan.req = NULL;
                ieee80211_scan_completed(wl->hw, true);
        }
 
+       if (wl->sched_vif == wlvif) {
+               ieee80211_sched_scan_stopped(wl->hw);
+               wl->sched_vif = NULL;
+       }
+
+       if (wl->roc_vif == vif) {
+               wl->roc_vif = NULL;
+               ieee80211_remain_on_channel_expired(wl->hw);
+       }
+
        if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
                /* disable active roles */
                ret = wl1271_ps_elp_wakeup(wl);
@@ -2396,9 +2577,6 @@ deinit:
                /* Configure for power according to debugfs */
                if (sta_auth != WL1271_PSM_ILLEGAL)
                        wl1271_acx_sleep_auth(wl, sta_auth);
-               /* Configure for power always on */
-               else if (wl->quirks & WLCORE_QUIRK_NO_ELP)
-                       wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
                /* Configure for ELP power saving */
                else
                        wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
@@ -2410,6 +2588,7 @@ unlock:
        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->connection_loss_work);
 
        mutex_lock(&wl->mutex);
 }
@@ -2468,8 +2647,7 @@ static int wl12xx_op_change_interface(struct ieee80211_hw *hw,
        return ret;
 }
 
-static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-                         bool set_assoc)
+static int wlcore_join(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret;
        bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
@@ -2489,18 +2667,111 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        /* clear encryption type */
        wlvif->encryption_type = KEY_NONE;
 
-       if (set_assoc)
-               set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
-
        if (is_ibss)
                ret = wl12xx_cmd_role_start_ibss(wl, wlvif);
-       else
+       else {
+               if (wl->quirks & WLCORE_QUIRK_START_STA_FAILS) {
+                       /*
+                        * TODO: this is an ugly workaround for wl12xx fw
+                        * bug - we are not able to tx/rx after the first
+                        * start_sta, so make dummy start+stop calls,
+                        * and then call start_sta again.
+                        * this should be fixed in the fw.
+                        */
+                       wl12xx_cmd_role_start_sta(wl, wlvif);
+                       wl12xx_cmd_role_stop_sta(wl, wlvif);
+               }
+
                ret = wl12xx_cmd_role_start_sta(wl, wlvif);
+       }
+
+       return ret;
+}
+
+static int wl1271_ssid_set(struct wl12xx_vif *wlvif, struct sk_buff *skb,
+                           int offset)
+{
+       u8 ssid_len;
+       const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
+                                        skb->len - offset);
+
+       if (!ptr) {
+               wl1271_error("No SSID in IEs!");
+               return -ENOENT;
+       }
+
+       ssid_len = ptr[1];
+       if (ssid_len > IEEE80211_MAX_SSID_LEN) {
+               wl1271_error("SSID is too long!");
+               return -EINVAL;
+       }
+
+       wlvif->ssid_len = ssid_len;
+       memcpy(wlvif->ssid, ptr+2, ssid_len);
+       return 0;
+}
+
+static int wlcore_set_ssid(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+       struct sk_buff *skb;
+       int ieoffset;
+
+       /* we currently only support setting the ssid from the ap probe req */
+       if (wlvif->bss_type != BSS_TYPE_STA_BSS)
+               return -EINVAL;
+
+       skb = ieee80211_ap_probereq_get(wl->hw, vif);
+       if (!skb)
+               return -EINVAL;
+
+       ieoffset = offsetof(struct ieee80211_mgmt,
+                           u.probe_req.variable);
+       wl1271_ssid_set(wlvif, skb, ieoffset);
+       dev_kfree_skb(skb);
+
+       return 0;
+}
+
+static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           struct ieee80211_bss_conf *bss_conf,
+                           u32 sta_rate_set)
+{
+       int ieoffset;
+       int ret;
+
+       wlvif->aid = bss_conf->aid;
+       wlvif->channel_type = cfg80211_get_chandef_type(&bss_conf->chandef);
+       wlvif->beacon_int = bss_conf->beacon_int;
+       wlvif->wmm_enabled = bss_conf->qos;
+
+       set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
+
+       /*
+        * with wl1271, we don't need to update the
+        * beacon_int and dtim_period, because the firmware
+        * updates it by itself when the first beacon is
+        * received after a join.
+        */
+       ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
        if (ret < 0)
-               goto out;
+               return ret;
 
-       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
-               goto out;
+       /*
+        * Get a template for hardware connection maintenance
+        */
+       dev_kfree_skb(wlvif->probereq);
+       wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
+                                                       wlvif,
+                                                       NULL);
+       ieoffset = offsetof(struct ieee80211_mgmt,
+                           u.probe_req.variable);
+       wl1271_ssid_set(wlvif, wlvif->probereq, ieoffset);
+
+       /* enable the connection monitoring feature */
+       ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
+       if (ret < 0)
+               return ret;
 
        /*
         * The join command disable the keep-alive mode, shut down its process,
@@ -2510,35 +2781,83 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
         */
        ret = wl1271_acx_keep_alive_mode(wl, wlvif, true);
        if (ret < 0)
-               goto out;
+               return ret;
 
        ret = wl1271_acx_aid(wl, wlvif, wlvif->aid);
        if (ret < 0)
-               goto out;
+               return ret;
 
        ret = wl12xx_cmd_build_klv_null_data(wl, wlvif);
        if (ret < 0)
-               goto out;
+               return ret;
 
        ret = wl1271_acx_keep_alive_config(wl, wlvif,
                                           wlvif->sta.klv_template_id,
                                           ACX_KEEP_ALIVE_TPL_VALID);
        if (ret < 0)
-               goto out;
+               return ret;
+
+       /*
+        * The default fw psm configuration is AUTO, while mac80211 default
+        * setting is off (ACTIVE), so sync the fw with the correct value.
+        */
+       ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE);
+       if (ret < 0)
+               return ret;
+
+       if (sta_rate_set) {
+               wlvif->rate_set =
+                       wl1271_tx_enabled_rates_get(wl,
+                                                   sta_rate_set,
+                                                   wlvif->band);
+               ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+               if (ret < 0)
+                       return ret;
+       }
 
-out:
        return ret;
 }
 
-static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
        int ret;
+       bool sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
+
+       /* make sure we are connected (sta) joined */
+       if (sta &&
+           !test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+               return false;
+
+       /* make sure we are joined (ibss) */
+       if (!sta &&
+           test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags))
+               return false;
+
+       if (sta) {
+               /* use defaults when not associated */
+               wlvif->aid = 0;
+
+               /* free probe-request template */
+               dev_kfree_skb(wlvif->probereq);
+               wlvif->probereq = NULL;
+
+               /* disable connection monitor features */
+               ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
+               if (ret < 0)
+                       return ret;
+
+               /* Disable the keep-alive feature */
+               ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
+               if (ret < 0)
+                       return ret;
+       }
 
        if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
                struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
 
-               wl12xx_cmd_stop_channel_switch(wl);
+               wl12xx_cmd_stop_channel_switch(wl, wlvif);
                ieee80211_chswitch_done(vif, false);
+               cancel_delayed_work(&wlvif->channel_switch_work);
        }
 
        /* invalidate keep-alive template */
@@ -2546,17 +2865,11 @@ static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                                     wlvif->sta.klv_template_id,
                                     ACX_KEEP_ALIVE_TPL_INVALID);
 
-       /* to stop listening to a channel, we disconnect */
-       ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
-       if (ret < 0)
-               goto out;
-
        /* reset TX security counters on a clean disconnect */
        wlvif->tx_security_last_seq_lsb = 0;
        wlvif->tx_security_seq = 0;
 
-out:
-       return ret;
+       return 0;
 }
 
 static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
@@ -2565,195 +2878,38 @@ static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        wlvif->rate_set = wlvif->basic_rate_set;
 }
 
-static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-                                 bool idle)
+static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                            struct ieee80211_conf *conf, u32 changed)
 {
        int ret;
-       bool cur_idle = !test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
-
-       if (idle == cur_idle)
-               return 0;
 
-       if (idle) {
-               /* no need to croc if we weren't busy (e.g. during boot) */
-               if (wl12xx_dev_role_started(wlvif)) {
-                       ret = wl12xx_stop_dev(wl, wlvif);
-                       if (ret < 0)
-                               goto out;
-               }
-               wlvif->rate_set =
-                       wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
-               ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+       if (conf->power_level != wlvif->power_level) {
+               ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level);
                if (ret < 0)
-                       goto out;
-               clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
-       } else {
-               /* The current firmware only supports sched_scan in idle */
-               if (wl->sched_scanning) {
-                       wl1271_scan_sched_scan_stop(wl, wlvif);
-                       ieee80211_sched_scan_stopped(wl->hw);
-               }
+                       return ret;
 
-               ret = wl12xx_start_dev(wl, wlvif);
-               if (ret < 0)
-                       goto out;
-               set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
+               wlvif->power_level = conf->power_level;
        }
 
-out:
-       return ret;
+       return 0;
 }
 
-static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
-                            struct ieee80211_conf *conf, u32 changed)
+static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
 {
-       bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
-       int channel, ret;
-
-       channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif;
+       struct ieee80211_conf *conf = &hw->conf;
+       int ret = 0;
 
-       /* if the channel changes while joined, join again */
-       if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
-           ((wlvif->band != conf->channel->band) ||
-            (wlvif->channel != channel) ||
-            (wlvif->channel_type != conf->channel_type))) {
-               /* send all pending packets */
-               ret = wlcore_tx_work_locked(wl);
-               if (ret < 0)
-                       return ret;
-
-               wlvif->band = conf->channel->band;
-               wlvif->channel = channel;
-               wlvif->channel_type = conf->channel_type;
-
-               if (is_ap) {
-                       wl1271_set_band_rate(wl, wlvif);
-                       ret = wl1271_init_ap_rates(wl, wlvif);
-                       if (ret < 0)
-                               wl1271_error("AP rate policy change failed %d",
-                                            ret);
-               } else {
-                       /*
-                        * FIXME: the mac80211 should really provide a fixed
-                        * rate to use here. for now, just use the smallest
-                        * possible rate for the band as a fixed rate for
-                        * association frames and other control messages.
-                        */
-                       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
-                               wl1271_set_band_rate(wl, wlvif);
-
-                       wlvif->basic_rate =
-                               wl1271_tx_min_rate_get(wl,
-                                                      wlvif->basic_rate_set);
-                       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
-                       if (ret < 0)
-                               wl1271_warning("rate policy for channel "
-                                              "failed %d", ret);
-
-                       /*
-                        * 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)
-                                       return ret;
-
-                               ret = wl12xx_start_dev(wl, wlvif);
-                               if (ret < 0)
-                                       return ret;
-                       }
-               }
-       }
-
-       if ((changed & IEEE80211_CONF_CHANGE_PS) && !is_ap) {
-
-               if ((conf->flags & IEEE80211_CONF_PS) &&
-                   test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
-                   !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
-
-                       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);
-
-                       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");
-
-                       ret = wl1271_ps_set_mode(wl, wlvif,
-                                                STATION_ACTIVE_MODE);
-                       if (ret < 0)
-                               wl1271_warning("exit auto ps failed %d", ret);
-               }
-       }
-
-       if (conf->power_level != wlvif->power_level) {
-               ret = wl1271_acx_tx_power(wl, wlvif, conf->power_level);
-               if (ret < 0)
-                       return ret;
-
-               wlvif->power_level = conf->power_level;
-       }
-
-       return 0;
-}
-
-static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
-{
-       struct wl1271 *wl = hw->priv;
-       struct wl12xx_vif *wlvif;
-       struct ieee80211_conf *conf = &hw->conf;
-       int channel, ret = 0;
-
-       channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
-
-       wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s"
+       wl1271_debug(DEBUG_MAC80211, "mac80211 config psm %s power %d %s"
                     " changed 0x%x",
-                    channel,
                     conf->flags & IEEE80211_CONF_PS ? "on" : "off",
                     conf->power_level,
                     conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
                         changed);
 
-       /*
-        * mac80211 will go to idle nearly immediately after transmitting some
-        * frames, such as the deauth. To make sure those frames reach the air,
-        * wait here until the TX queue is fully flushed.
-        */
-       if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) ||
-           ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
-            (conf->flags & IEEE80211_CONF_IDLE)))
-               wl1271_tx_flush(wl);
-
        mutex_lock(&wl->mutex);
 
-       /* we support configuring the channel and band even while off */
-       if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
-               wl->band = conf->channel->band;
-               wl->channel = channel;
-               wl->channel_type = conf->channel_type;
-       }
-
        if (changed & IEEE80211_CONF_CHANGE_POWER)
                wl->power_level = conf->power_level;
 
@@ -3073,10 +3229,7 @@ static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                 * stop the queues and flush to ensure the next packets are
                 * in sync with FW spare block accounting
                 */
-               mutex_lock(&wl->mutex);
                wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
-               mutex_unlock(&wl->mutex);
-
                wl1271_tx_flush(wl);
        }
 
@@ -3202,6 +3355,29 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
 }
 EXPORT_SYMBOL_GPL(wlcore_set_key);
 
+void wlcore_regdomain_config(struct wl1271 *wl)
+{
+       int ret;
+
+       if (!(wl->quirks & WLCORE_QUIRK_REGDOMAIN_CONF))
+               return;
+
+       mutex_lock(&wl->mutex);
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wlcore_cmd_regdomain_config_locked(wl);
+       if (ret < 0) {
+               wl12xx_queue_recovery_work(wl);
+               goto out;
+       }
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
 static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
                             struct cfg80211_scan_request *req)
@@ -3241,7 +3417,7 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
                goto out_sleep;
        }
 
-       ret = wl1271_scan(hw->priv, vif, ssid, len, req);
+       ret = wlcore_scan(hw->priv, vif, ssid, len, req);
 out_sleep:
        wl1271_ps_elp_sleep(wl);
 out:
@@ -3254,6 +3430,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif)
 {
        struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 cancel hw scan");
@@ -3271,7 +3448,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
                goto out;
 
        if (wl->scan.state != WL1271_SCAN_STATE_DONE) {
-               ret = wl1271_scan_stop(wl);
+               ret = wl->ops->scan_stop(wl, wlvif);
                if (ret < 0)
                        goto out_sleep;
        }
@@ -3284,7 +3461,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
 
        wl->scan.state = WL1271_SCAN_STATE_IDLE;
        memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
-       wl->scan_vif = NULL;
+       wl->scan_wlvif = NULL;
        wl->scan.req = NULL;
        ieee80211_scan_completed(wl->hw, true);
 
@@ -3318,15 +3495,11 @@ static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
-       ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies);
+       ret = wl->ops->sched_scan_start(wl, wlvif, req, ies);
        if (ret < 0)
                goto out_sleep;
 
-       ret = wl1271_scan_sched_scan_start(wl, wlvif);
-       if (ret < 0)
-               goto out_sleep;
-
-       wl->sched_scanning = true;
+       wl->sched_vif = wlvif;
 
 out_sleep:
        wl1271_ps_elp_sleep(wl);
@@ -3353,7 +3526,7 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
-       wl1271_scan_sched_scan_stop(wl, wlvif);
+       wl->ops->sched_scan_stop(wl, wlvif);
 
        wl1271_ps_elp_sleep(wl);
 out:
@@ -3418,30 +3591,6 @@ out:
        return ret;
 }
 
-static int wl1271_ssid_set(struct ieee80211_vif *vif, struct sk_buff *skb,
-                           int offset)
-{
-       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-       u8 ssid_len;
-       const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
-                                        skb->len - offset);
-
-       if (!ptr) {
-               wl1271_error("No SSID in IEs!");
-               return -ENOENT;
-       }
-
-       ssid_len = ptr[1];
-       if (ssid_len > IEEE80211_MAX_SSID_LEN) {
-               wl1271_error("SSID is too long!");
-               return -EINVAL;
-       }
-
-       wlvif->ssid_len = ssid_len;
-       memcpy(wlvif->ssid, ptr+2, ssid_len);
-       return 0;
-}
-
 static void wl12xx_remove_ie(struct sk_buff *skb, u8 eid, int ieoffset)
 {
        int len;
@@ -3622,7 +3771,7 @@ static int wlcore_set_beacon_template(struct wl1271 *wl,
 
        wl1271_debug(DEBUG_MASTER, "beacon updated");
 
-       ret = wl1271_ssid_set(vif, beacon, ieoffset);
+       ret = wl1271_ssid_set(wlvif, beacon, ieoffset);
        if (ret < 0) {
                dev_kfree_skb(beacon);
                goto out;
@@ -3639,6 +3788,12 @@ static int wlcore_set_beacon_template(struct wl1271 *wl,
                goto out;
        }
 
+       wlvif->wmm_enabled =
+               cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+                                       WLAN_OUI_TYPE_MICROSOFT_WMM,
+                                       beacon->data + ieoffset,
+                                       beacon->len - ieoffset);
+
        /*
         * In case we already have a probe-resp beacon set explicitly
         * by usermode, don't use the beacon data.
@@ -3692,7 +3847,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
        bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
        int ret = 0;
 
-       if ((changed & BSS_CHANGED_BEACON_INT)) {
+       if (changed & BSS_CHANGED_BEACON_INT) {
                wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d",
                        bss_conf->beacon_int);
 
@@ -3705,7 +3860,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
                wl1271_ap_set_probe_resp_tmpl(wl, rate, vif);
        }
 
-       if ((changed & BSS_CHANGED_BEACON)) {
+       if (changed & BSS_CHANGED_BEACON) {
                ret = wlcore_set_beacon_template(wl, vif, is_ap);
                if (ret < 0)
                        goto out;
@@ -3726,7 +3881,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int ret = 0;
 
-       if ((changed & BSS_CHANGED_BASIC_RATES)) {
+       if (changed & BSS_CHANGED_BASIC_RATES) {
                u32 rates = bss_conf->basic_rates;
 
                wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates,
@@ -3757,7 +3912,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
        if (ret < 0)
                goto out;
 
-       if ((changed & BSS_CHANGED_BEACON_ENABLED)) {
+       if (changed & BSS_CHANGED_BEACON_ENABLED) {
                if (bss_conf->enable_beacon) {
                        if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
                                ret = wl12xx_cmd_role_start_ap(wl, wlvif);
@@ -3804,6 +3959,79 @@ out:
        return;
 }
 
+static int wlcore_set_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                           struct ieee80211_bss_conf *bss_conf,
+                           u32 sta_rate_set)
+{
+       u32 rates;
+       int ret;
+
+       wl1271_debug(DEBUG_MAC80211,
+            "changed_bssid: %pM, aid: %d, bcn_int: %d, brates: 0x%x sta_rate_set: 0x%x",
+            bss_conf->bssid, bss_conf->aid,
+            bss_conf->beacon_int,
+            bss_conf->basic_rates, sta_rate_set);
+
+       wlvif->beacon_int = bss_conf->beacon_int;
+       rates = bss_conf->basic_rates;
+       wlvif->basic_rate_set =
+               wl1271_tx_enabled_rates_get(wl, rates,
+                                           wlvif->band);
+       wlvif->basic_rate =
+               wl1271_tx_min_rate_get(wl,
+                                      wlvif->basic_rate_set);
+
+       if (sta_rate_set)
+               wlvif->rate_set =
+                       wl1271_tx_enabled_rates_get(wl,
+                                               sta_rate_set,
+                                               wlvif->band);
+
+       /* we only support sched_scan while not connected */
+       if (wl->sched_vif == wlvif)
+               wl->ops->sched_scan_stop(wl, wlvif);
+
+       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       ret = wl12xx_cmd_build_null_data(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       ret = wl1271_build_qos_null_data(wl, wl12xx_wlvif_to_vif(wlvif));
+       if (ret < 0)
+               return ret;
+
+       wlcore_set_ssid(wl, wlvif);
+
+       set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
+
+       return 0;
+}
+
+static int wlcore_clear_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+       int ret;
+
+       /* revert back to minimum rates for the current band */
+       wl1271_set_band_rate(wl, wlvif);
+       wlvif->basic_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
+
+       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
+           test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) {
+               ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
+               if (ret < 0)
+                       return ret;
+       }
+
+       clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
+       return 0;
+}
 /* STA/IBSS mode changes */
 static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                                        struct ieee80211_vif *vif,
@@ -3811,7 +4039,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                                        u32 changed)
 {
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-       bool do_join = false, set_assoc = false;
+       bool do_join = false;
        bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
        bool ibss_joined = false;
        u32 sta_rate_set = 0;
@@ -3832,9 +4060,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                        set_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags);
                        ibss_joined = true;
                } else {
-                       if (test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED,
-                                              &wlvif->flags))
-                               wl1271_unjoin(wl, wlvif);
+                       wlcore_unset_assoc(wl, wlvif);
+                       wl12xx_cmd_role_stop_sta(wl, wlvif);
                }
        }
 
@@ -3852,13 +4079,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                do_join = true;
        }
 
-       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);
-       }
-
-       if ((changed & BSS_CHANGED_CQM)) {
+       if (changed & BSS_CHANGED_CQM) {
                bool enable = false;
                if (bss_conf->cqm_rssi_thold)
                        enable = true;
@@ -3870,150 +4091,39 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                wlvif->rssi_thold = bss_conf->cqm_rssi_thold;
        }
 
-       if (changed & BSS_CHANGED_BSSID)
-               if (!is_zero_ether_addr(bss_conf->bssid)) {
-                       ret = wl12xx_cmd_build_null_data(wl, wlvif);
-                       if (ret < 0)
-                               goto out;
-
-                       ret = wl1271_build_qos_null_data(wl, vif);
-                       if (ret < 0)
-                               goto out;
-               }
-
-       if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) {
+       if (changed & (BSS_CHANGED_BSSID | BSS_CHANGED_HT |
+                      BSS_CHANGED_ASSOC)) {
                rcu_read_lock();
                sta = ieee80211_find_sta(vif, bss_conf->bssid);
-               if (!sta)
-                       goto sta_not_found;
-
-               /* save the supp_rates of the ap */
-               sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band];
-               if (sta->ht_cap.ht_supported)
-                       sta_rate_set |=
-                         (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) |
-                         (sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET);
-               sta_ht_cap = sta->ht_cap;
-               sta_exists = true;
-
-sta_not_found:
+               if (sta) {
+                       u8 *rx_mask = sta->ht_cap.mcs.rx_mask;
+
+                       /* save the supp_rates of the ap */
+                       sta_rate_set = sta->supp_rates[wlvif->band];
+                       if (sta->ht_cap.ht_supported)
+                               sta_rate_set |=
+                                       (rx_mask[0] << HW_HT_RATES_OFFSET) |
+                                       (rx_mask[1] << HW_MIMO_RATES_OFFSET);
+                       sta_ht_cap = sta->ht_cap;
+                       sta_exists = true;
+               }
+
                rcu_read_unlock();
        }
 
-       if ((changed & BSS_CHANGED_ASSOC)) {
-               if (bss_conf->assoc) {
-                       u32 rates;
-                       int ieoffset;
-                       wlvif->aid = bss_conf->aid;
-                       wlvif->channel_type =
-                               cfg80211_get_chandef_type(&bss_conf->chandef);
-                       wlvif->beacon_int = bss_conf->beacon_int;
-                       do_join = true;
-                       set_assoc = true;
-
-                       /*
-                        * use basic rates from AP, and determine lowest rate
-                        * to use with control frames.
-                        */
-                       rates = bss_conf->basic_rates;
-                       wlvif->basic_rate_set =
-                               wl1271_tx_enabled_rates_get(wl, rates,
-                                                           wlvif->band);
-                       wlvif->basic_rate =
-                               wl1271_tx_min_rate_get(wl,
-                                                      wlvif->basic_rate_set);
-                       if (sta_rate_set)
-                               wlvif->rate_set =
-                                       wl1271_tx_enabled_rates_get(wl,
-                                                               sta_rate_set,
-                                                               wlvif->band);
-                       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
-                       if (ret < 0)
-                               goto out;
-
-                       /*
-                        * with wl1271, we don't need to update the
-                        * beacon_int and dtim_period, because the firmware
-                        * updates it by itself when the first beacon is
-                        * received after a join.
-                        */
-                       ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
+       if (changed & BSS_CHANGED_BSSID) {
+               if (!is_zero_ether_addr(bss_conf->bssid)) {
+                       ret = wlcore_set_bssid(wl, wlvif, bss_conf,
+                                              sta_rate_set);
                        if (ret < 0)
                                goto out;
 
-                       /*
-                        * Get a template for hardware connection maintenance
-                        */
-                       dev_kfree_skb(wlvif->probereq);
-                       wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
-                                                                       wlvif,
-                                                                       NULL);
-                       ieoffset = offsetof(struct ieee80211_mgmt,
-                                           u.probe_req.variable);
-                       wl1271_ssid_set(vif, wlvif->probereq, ieoffset);
-
-                       /* enable the connection monitoring feature */
-                       ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
-                       if (ret < 0)
-                               goto out;
+                       /* Need to update the BSSID (for filtering etc) */
+                       do_join = true;
                } else {
-                       /* use defaults when not associated */
-                       bool was_assoc =
-                           !!test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED,
-                                                &wlvif->flags);
-                       bool was_ifup =
-                           !!test_and_clear_bit(WLVIF_FLAG_STA_STATE_SENT,
-                                                &wlvif->flags);
-                       wlvif->aid = 0;
-
-                       /* free probe-request template */
-                       dev_kfree_skb(wlvif->probereq);
-                       wlvif->probereq = NULL;
-
-                       /* revert back to minimum rates for the current band */
-                       wl1271_set_band_rate(wl, wlvif);
-                       wlvif->basic_rate =
-                               wl1271_tx_min_rate_get(wl,
-                                                      wlvif->basic_rate_set);
-                       ret = wl1271_acx_sta_rate_policies(wl, wlvif);
-                       if (ret < 0)
-                               goto out;
-
-                       /* disable connection monitor features */
-                       ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
-
-                       /* Disable the keep-alive feature */
-                       ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
+                       ret = wlcore_clear_bssid(wl, wlvif);
                        if (ret < 0)
                                goto out;
-
-                       /* restore the bssid filter and go to dummy bssid */
-                       if (was_assoc) {
-                               /*
-                                * we might have to disable roc, if there was
-                                * no IF_OPER_UP notification.
-                                */
-                               if (!was_ifup) {
-                                       ret = wl12xx_croc(wl, wlvif->role_id);
-                                       if (ret < 0)
-                                               goto out;
-                               }
-                               /*
-                                * (we also need to disable roc in case of
-                                * roaming on the same channel. until we will
-                                * have a better flow...)
-                                */
-                               if (test_bit(wlvif->dev_role_id, wl->roc_map)) {
-                                       ret = wl12xx_croc(wl,
-                                                         wlvif->dev_role_id);
-                                       if (ret < 0)
-                                               goto out;
-                               }
-
-                               wl1271_unjoin(wl, wlvif);
-                               if (!bss_conf->idle)
-                                       wl12xx_start_dev(wl, wlvif);
-                       }
                }
        }
 
@@ -4043,71 +4153,87 @@ sta_not_found:
                goto out;
 
        if (do_join) {
-               ret = wl1271_join(wl, wlvif, set_assoc);
+               ret = wlcore_join(wl, wlvif);
                if (ret < 0) {
                        wl1271_warning("cmd join failed %d", ret);
                        goto out;
                }
+       }
 
-               /* ROC until connected (after EAPOL exchange) */
-               if (!is_ibss) {
-                       ret = wl12xx_roc(wl, wlvif, wlvif->role_id);
+       if (changed & BSS_CHANGED_ASSOC) {
+               if (bss_conf->assoc) {
+                       ret = wlcore_set_assoc(wl, wlvif, bss_conf,
+                                              sta_rate_set);
                        if (ret < 0)
                                goto out;
 
                        if (test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags))
                                wl12xx_set_authorized(wl, wlvif);
+               } else {
+                       wlcore_unset_assoc(wl, wlvif);
                }
-               /*
-                * stop device role if started (we might already be in
-                * STA/IBSS role).
-                */
-               if (wl12xx_dev_role_started(wlvif)) {
-                       ret = wl12xx_stop_dev(wl, wlvif);
+       }
+
+       if (changed & BSS_CHANGED_PS) {
+               if ((bss_conf->ps) &&
+                   test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) &&
+                   !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
+                       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);
                        if (ret < 0)
-                               goto out;
+                               wl1271_warning("enter %s ps failed %d",
+                                              ps_mode_str, ret);
+               } else if (!bss_conf->ps &&
+                          test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) {
+                       wl1271_debug(DEBUG_PSM, "auto ps disabled");
+
+                       ret = wl1271_ps_set_mode(wl, wlvif,
+                                                STATION_ACTIVE_MODE);
+                       if (ret < 0)
+                               wl1271_warning("exit auto ps failed %d", ret);
                }
        }
 
        /* Handle new association with HT. Do this after join. */
-       if (sta_exists) {
-               if ((changed & BSS_CHANGED_HT) &&
-                   (bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) {
-                       ret = wl1271_acx_set_ht_capabilities(wl,
-                                                            &sta_ht_cap,
-                                                            true,
-                                                            wlvif->sta.hlid);
-                       if (ret < 0) {
-                               wl1271_warning("Set ht cap true failed %d",
-                                              ret);
-                               goto out;
-                       }
+       if (sta_exists &&
+           (changed & BSS_CHANGED_HT)) {
+               bool enabled =
+                       bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT;
+
+               ret = wlcore_hw_set_peer_cap(wl,
+                                            &sta_ht_cap,
+                                            enabled,
+                                            wlvif->rate_set,
+                                            wlvif->sta.hlid);
+               if (ret < 0) {
+                       wl1271_warning("Set ht cap failed %d", ret);
+                       goto out;
+
                }
-               /* handle new association without HT and disassociation */
-               else if (changed & BSS_CHANGED_ASSOC) {
-                       ret = wl1271_acx_set_ht_capabilities(wl,
-                                                            &sta_ht_cap,
-                                                            false,
-                                                            wlvif->sta.hlid);
+
+               if (enabled) {
+                       ret = wl1271_acx_set_ht_information(wl, wlvif,
+                                               bss_conf->ht_operation_mode);
                        if (ret < 0) {
-                               wl1271_warning("Set ht cap false failed %d",
+                               wl1271_warning("Set ht information failed %d",
                                               ret);
                                goto out;
                        }
                }
        }
 
-       /* Handle HT information change. Done after join. */
-       if ((changed & BSS_CHANGED_HT) &&
-           (bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) {
-               ret = wl1271_acx_set_ht_information(wl, wlvif,
-                                       bss_conf->ht_operation_mode);
-               if (ret < 0) {
-                       wl1271_warning("Set ht information failed %d", ret);
-                       goto out;
-               }
-       }
-
        /* Handle arp filtering. Done after join. */
        if ((changed & BSS_CHANGED_ARP_FILTER) ||
            (!is_ibss && (changed & BSS_CHANGED_QOS))) {
@@ -4157,15 +4283,15 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
        bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
        int ret;
 
-       wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
-                    (int)changed);
+       wl1271_debug(DEBUG_MAC80211, "mac80211 bss info role %d changed 0x%x",
+                    wlvif->role_id, (int)changed);
 
        /*
         * make sure to cancel pending disconnections if our association
         * state changed
         */
        if (!is_ap && (changed & BSS_CHANGED_ASSOC))
-               cancel_delayed_work_sync(&wl->connection_loss_work);
+               cancel_delayed_work_sync(&wlvif->connection_loss_work);
 
        if (is_ap && (changed & BSS_CHANGED_BEACON_ENABLED) &&
            !bss_conf->enable_beacon)
@@ -4194,6 +4320,76 @@ out:
        mutex_unlock(&wl->mutex);
 }
 
+static int wlcore_op_add_chanctx(struct ieee80211_hw *hw,
+                                struct ieee80211_chanctx_conf *ctx)
+{
+       wl1271_debug(DEBUG_MAC80211, "mac80211 add chanctx %d (type %d)",
+                    ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
+                    cfg80211_get_chandef_type(&ctx->def));
+       return 0;
+}
+
+static void wlcore_op_remove_chanctx(struct ieee80211_hw *hw,
+                                    struct ieee80211_chanctx_conf *ctx)
+{
+       wl1271_debug(DEBUG_MAC80211, "mac80211 remove chanctx %d (type %d)",
+                    ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
+                    cfg80211_get_chandef_type(&ctx->def));
+}
+
+static void wlcore_op_change_chanctx(struct ieee80211_hw *hw,
+                                    struct ieee80211_chanctx_conf *ctx,
+                                    u32 changed)
+{
+       wl1271_debug(DEBUG_MAC80211,
+                    "mac80211 change chanctx %d (type %d) changed 0x%x",
+                    ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
+                    cfg80211_get_chandef_type(&ctx->def), changed);
+}
+
+static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
+                                       struct ieee80211_vif *vif,
+                                       struct ieee80211_chanctx_conf *ctx)
+{
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       int channel = ieee80211_frequency_to_channel(
+               ctx->def.chan->center_freq);
+
+       wl1271_debug(DEBUG_MAC80211,
+                    "mac80211 assign chanctx (role %d) %d (type %d)",
+                    wlvif->role_id, channel, cfg80211_get_chandef_type(&ctx->def));
+
+       mutex_lock(&wl->mutex);
+
+       wlvif->band = ctx->def.chan->band;
+       wlvif->channel = channel;
+       wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def);
+
+       /* update default rates according to the band */
+       wl1271_set_band_rate(wl, wlvif);
+
+       mutex_unlock(&wl->mutex);
+
+       return 0;
+}
+
+static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
+                                          struct ieee80211_vif *vif,
+                                          struct ieee80211_chanctx_conf *ctx)
+{
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+
+       wl1271_debug(DEBUG_MAC80211,
+                    "mac80211 unassign chanctx (role %d) %d (type %d)",
+                    wlvif->role_id,
+                    ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
+                    cfg80211_get_chandef_type(&ctx->def));
+
+       wl1271_tx_flush(wl);
+}
+
 static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif, u16 queue,
                             const struct ieee80211_tx_queue_params *params)
@@ -4321,8 +4517,6 @@ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
                return;
 
        clear_bit(hlid, wlvif->ap.sta_hlid_map);
-       memset(wl->links[hlid].addr, 0, ETH_ALEN);
-       wl->links[hlid].ba_bitmap = 0;
        __clear_bit(hlid, &wl->ap_ps_map);
        __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
        wl12xx_free_link(wl, wlvif, &hlid);
@@ -4382,6 +4576,45 @@ static int wl12xx_sta_remove(struct wl1271 *wl,
        return ret;
 }
 
+static void wlcore_roc_if_possible(struct wl1271 *wl,
+                                  struct wl12xx_vif *wlvif)
+{
+       if (find_first_bit(wl->roc_map,
+                          WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)
+               return;
+
+       if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID))
+               return;
+
+       wl12xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel);
+}
+
+static void wlcore_update_inconn_sta(struct wl1271 *wl,
+                                    struct wl12xx_vif *wlvif,
+                                    struct wl1271_station *wl_sta,
+                                    bool in_connection)
+{
+       if (in_connection) {
+               if (WARN_ON(wl_sta->in_connection))
+                       return;
+               wl_sta->in_connection = true;
+               if (!wlvif->inconn_count++)
+                       wlcore_roc_if_possible(wl, wlvif);
+       } else {
+               if (!wl_sta->in_connection)
+                       return;
+
+               wl_sta->in_connection = false;
+               wlvif->inconn_count--;
+               if (WARN_ON(wlvif->inconn_count < 0))
+                       return;
+
+               if (!wlvif->inconn_count)
+                       if (test_bit(wlvif->role_id, wl->roc_map))
+                               wl12xx_croc(wl, wlvif->role_id);
+       }
+}
+
 static int wl12xx_update_sta_state(struct wl1271 *wl,
                                   struct wl12xx_vif *wlvif,
                                   struct ieee80211_sta *sta,
@@ -4400,8 +4633,13 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
        /* Add station (AP mode) */
        if (is_ap &&
            old_state == IEEE80211_STA_NOTEXIST &&
-           new_state == IEEE80211_STA_NONE)
-               return wl12xx_sta_add(wl, wlvif, sta);
+           new_state == IEEE80211_STA_NONE) {
+               ret = wl12xx_sta_add(wl, wlvif, sta);
+               if (ret)
+                       return ret;
+
+               wlcore_update_inconn_sta(wl, wlvif, wl_sta, true);
+       }
 
        /* Remove station (AP mode) */
        if (is_ap &&
@@ -4409,35 +4647,59 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
            new_state == IEEE80211_STA_NOTEXIST) {
                /* must not fail */
                wl12xx_sta_remove(wl, wlvif, sta);
-               return 0;
+
+               wlcore_update_inconn_sta(wl, wlvif, wl_sta, false);
        }
 
        /* Authorize station (AP mode) */
        if (is_ap &&
            new_state == IEEE80211_STA_AUTHORIZED) {
-               ret = wl12xx_cmd_set_peer_state(wl, hlid);
+               ret = wl12xx_cmd_set_peer_state(wl, wlvif, hlid);
                if (ret < 0)
                        return ret;
 
                ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true,
                                                     hlid);
-               return ret;
+               if (ret)
+                       return ret;
+
+               wlcore_update_inconn_sta(wl, wlvif, wl_sta, false);
        }
 
        /* Authorize station */
        if (is_sta &&
            new_state == IEEE80211_STA_AUTHORIZED) {
                set_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
-               return wl12xx_set_authorized(wl, wlvif);
+               ret = wl12xx_set_authorized(wl, wlvif);
+               if (ret)
+                       return ret;
        }
 
        if (is_sta &&
            old_state == IEEE80211_STA_AUTHORIZED &&
            new_state == IEEE80211_STA_ASSOC) {
                clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
-               return 0;
+               clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags);
+       }
+
+       /* clear ROCs on failure or authorization */
+       if (is_sta &&
+           (new_state == IEEE80211_STA_AUTHORIZED ||
+            new_state == IEEE80211_STA_NOTEXIST)) {
+               if (test_bit(wlvif->role_id, wl->roc_map))
+                       wl12xx_croc(wl, wlvif->role_id);
        }
 
+       if (is_sta &&
+           old_state == IEEE80211_STA_NOTEXIST &&
+           new_state == IEEE80211_STA_NONE) {
+               if (find_first_bit(wl->roc_map,
+                                  WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES) {
+                       WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID);
+                       wl12xx_roc(wl, wlvif, wlvif->role_id,
+                                  wlvif->band, wlvif->channel);
+               }
+       }
        return 0;
 }
 
@@ -4502,18 +4764,18 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
 
        if (wlvif->bss_type == BSS_TYPE_STA_BSS) {
                hlid = wlvif->sta.hlid;
-               ba_bitmap = &wlvif->sta.ba_rx_bitmap;
        } else if (wlvif->bss_type == BSS_TYPE_AP_BSS) {
                struct wl1271_station *wl_sta;
 
                wl_sta = (struct wl1271_station *)sta->drv_priv;
                hlid = wl_sta->hlid;
-               ba_bitmap = &wl->links[hlid].ba_bitmap;
        } else {
                ret = -EINVAL;
                goto out;
        }
 
+       ba_bitmap = &wl->links[hlid].ba_bitmap;
+
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
                goto out;
@@ -4667,12 +4929,23 @@ 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, wlvif, ch_switch);
+               unsigned long delay_usec;
 
-               if (!ret)
-                       set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
+               ret = wl->ops->channel_switch(wl, wlvif, ch_switch);
+               if (ret)
+                       goto out_sleep;
+
+               set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
+
+               /* indicate failure 5 seconds after channel switch time */
+               delay_usec = ieee80211_tu_to_usec(wlvif->beacon_int) *
+                            ch_switch->count;
+               ieee80211_queue_delayed_work(hw, &wlvif->channel_switch_work,
+                               usecs_to_jiffies(delay_usec) +
+                               msecs_to_jiffies(5000));
        }
 
+out_sleep:
        wl1271_ps_elp_sleep(wl);
 
 out:
@@ -4686,6 +4959,144 @@ static void wlcore_op_flush(struct ieee80211_hw *hw, bool drop)
        wl1271_tx_flush(wl);
 }
 
+static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif,
+                                      struct ieee80211_channel *chan,
+                                      int duration)
+{
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       struct wl1271 *wl = hw->priv;
+       int channel, ret = 0;
+
+       channel = ieee80211_frequency_to_channel(chan->center_freq);
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 roc %d (%d)",
+                    channel, wlvif->role_id);
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       /* return EBUSY if we can't ROC right now */
+       if (WARN_ON(wl->roc_vif ||
+                   find_first_bit(wl->roc_map,
+                                  WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wl12xx_start_dev(wl, wlvif, chan->band, channel);
+       if (ret < 0)
+               goto out_sleep;
+
+       wl->roc_vif = vif;
+       ieee80211_queue_delayed_work(hw, &wl->roc_complete_work,
+                                    msecs_to_jiffies(duration));
+out_sleep:
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+       return ret;
+}
+
+static int __wlcore_roc_completed(struct wl1271 *wl)
+{
+       struct wl12xx_vif *wlvif;
+       int ret;
+
+       /* already completed */
+       if (unlikely(!wl->roc_vif))
+               return 0;
+
+       wlvif = wl12xx_vif_to_data(wl->roc_vif);
+
+       if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
+               return -EBUSY;
+
+       ret = wl12xx_stop_dev(wl, wlvif);
+       if (ret < 0)
+               return ret;
+
+       wl->roc_vif = NULL;
+
+       return 0;
+}
+
+static int wlcore_roc_completed(struct wl1271 *wl)
+{
+       int ret;
+
+       wl1271_debug(DEBUG_MAC80211, "roc complete");
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = __wlcore_roc_completed(wl);
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+
+       return ret;
+}
+
+static void wlcore_roc_complete_work(struct work_struct *work)
+{
+       struct delayed_work *dwork;
+       struct wl1271 *wl;
+       int ret;
+
+       dwork = container_of(work, struct delayed_work, work);
+       wl = container_of(dwork, struct wl1271, roc_complete_work);
+
+       ret = wlcore_roc_completed(wl);
+       if (!ret)
+               ieee80211_remain_on_channel_expired(wl->hw);
+}
+
+static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw)
+{
+       struct wl1271 *wl = hw->priv;
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 croc");
+
+       /* TODO: per-vif */
+       wl1271_tx_flush(wl);
+
+       /*
+        * we can't just flush_work here, because it might deadlock
+        * (as we might get called from the same workqueue)
+        */
+       cancel_delayed_work_sync(&wl->roc_complete_work);
+       wlcore_roc_completed(wl);
+
+       return 0;
+}
+
+static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_sta *sta,
+                                   u32 changed)
+{
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       struct wl1271 *wl = hw->priv;
+
+       wlcore_hw_sta_rc_update(wl, wlvif, sta, changed);
+}
+
 static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
 {
        struct wl1271 *wl = hw->priv;
@@ -4749,20 +5160,20 @@ static struct ieee80211_rate wl1271_rates[] = {
 
 /* can't be const, mac80211 writes to this */
 static struct ieee80211_channel wl1271_channels[] = {
-       { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
-       { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
-       { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
-       { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
-       { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
-       { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
-       { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
-       { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
-       { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
-       { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
-       { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
-       { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
-       { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
-       { .hw_value = 14, .center_freq = 2484, .max_power = 25 },
+       { .hw_value = 1, .center_freq = 2412, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 2, .center_freq = 2417, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 3, .center_freq = 2422, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 4, .center_freq = 2427, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 5, .center_freq = 2432, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 6, .center_freq = 2437, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 7, .center_freq = 2442, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 8, .center_freq = 2447, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 9, .center_freq = 2452, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 10, .center_freq = 2457, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 11, .center_freq = 2462, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 12, .center_freq = 2467, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 13, .center_freq = 2472, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 14, .center_freq = 2484, .max_power = WLCORE_MAX_TXPWR },
 };
 
 /* can't be const, mac80211 writes to this */
@@ -4803,40 +5214,40 @@ static struct ieee80211_rate wl1271_rates_5ghz[] = {
 
 /* 5 GHz band channels for WL1273 */
 static struct ieee80211_channel wl1271_channels_5ghz[] = {
-       { .hw_value = 7, .center_freq = 5035, .max_power = 25 },
-       { .hw_value = 8, .center_freq = 5040, .max_power = 25 },
-       { .hw_value = 9, .center_freq = 5045, .max_power = 25 },
-       { .hw_value = 11, .center_freq = 5055, .max_power = 25 },
-       { .hw_value = 12, .center_freq = 5060, .max_power = 25 },
-       { .hw_value = 16, .center_freq = 5080, .max_power = 25 },
-       { .hw_value = 34, .center_freq = 5170, .max_power = 25 },
-       { .hw_value = 36, .center_freq = 5180, .max_power = 25 },
-       { .hw_value = 38, .center_freq = 5190, .max_power = 25 },
-       { .hw_value = 40, .center_freq = 5200, .max_power = 25 },
-       { .hw_value = 42, .center_freq = 5210, .max_power = 25 },
-       { .hw_value = 44, .center_freq = 5220, .max_power = 25 },
-       { .hw_value = 46, .center_freq = 5230, .max_power = 25 },
-       { .hw_value = 48, .center_freq = 5240, .max_power = 25 },
-       { .hw_value = 52, .center_freq = 5260, .max_power = 25 },
-       { .hw_value = 56, .center_freq = 5280, .max_power = 25 },
-       { .hw_value = 60, .center_freq = 5300, .max_power = 25 },
-       { .hw_value = 64, .center_freq = 5320, .max_power = 25 },
-       { .hw_value = 100, .center_freq = 5500, .max_power = 25 },
-       { .hw_value = 104, .center_freq = 5520, .max_power = 25 },
-       { .hw_value = 108, .center_freq = 5540, .max_power = 25 },
-       { .hw_value = 112, .center_freq = 5560, .max_power = 25 },
-       { .hw_value = 116, .center_freq = 5580, .max_power = 25 },
-       { .hw_value = 120, .center_freq = 5600, .max_power = 25 },
-       { .hw_value = 124, .center_freq = 5620, .max_power = 25 },
-       { .hw_value = 128, .center_freq = 5640, .max_power = 25 },
-       { .hw_value = 132, .center_freq = 5660, .max_power = 25 },
-       { .hw_value = 136, .center_freq = 5680, .max_power = 25 },
-       { .hw_value = 140, .center_freq = 5700, .max_power = 25 },
-       { .hw_value = 149, .center_freq = 5745, .max_power = 25 },
-       { .hw_value = 153, .center_freq = 5765, .max_power = 25 },
-       { .hw_value = 157, .center_freq = 5785, .max_power = 25 },
-       { .hw_value = 161, .center_freq = 5805, .max_power = 25 },
-       { .hw_value = 165, .center_freq = 5825, .max_power = 25 },
+       { .hw_value = 7, .center_freq = 5035, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 8, .center_freq = 5040, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 9, .center_freq = 5045, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 11, .center_freq = 5055, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 12, .center_freq = 5060, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 16, .center_freq = 5080, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 34, .center_freq = 5170, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 36, .center_freq = 5180, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 38, .center_freq = 5190, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 40, .center_freq = 5200, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 42, .center_freq = 5210, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 44, .center_freq = 5220, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 46, .center_freq = 5230, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 48, .center_freq = 5240, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 52, .center_freq = 5260, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 56, .center_freq = 5280, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 60, .center_freq = 5300, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 64, .center_freq = 5320, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 100, .center_freq = 5500, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 104, .center_freq = 5520, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 108, .center_freq = 5540, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 112, .center_freq = 5560, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 116, .center_freq = 5580, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 120, .center_freq = 5600, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 124, .center_freq = 5620, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 128, .center_freq = 5640, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 132, .center_freq = 5660, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 136, .center_freq = 5680, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 140, .center_freq = 5700, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 149, .center_freq = 5745, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 153, .center_freq = 5765, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 157, .center_freq = 5785, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 161, .center_freq = 5805, .max_power = WLCORE_MAX_TXPWR },
+       { .hw_value = 165, .center_freq = 5825, .max_power = WLCORE_MAX_TXPWR },
 };
 
 static struct ieee80211_supported_band wl1271_band_5ghz = {
@@ -4877,6 +5288,14 @@ static const struct ieee80211_ops wl1271_ops = {
        .set_bitrate_mask = wl12xx_set_bitrate_mask,
        .channel_switch = wl12xx_op_channel_switch,
        .flush = wlcore_op_flush,
+       .remain_on_channel = wlcore_op_remain_on_channel,
+       .cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel,
+       .add_chanctx = wlcore_op_add_chanctx,
+       .remove_chanctx = wlcore_op_remove_chanctx,
+       .change_chanctx = wlcore_op_change_chanctx,
+       .assign_vif_chanctx = wlcore_op_assign_vif_chanctx,
+       .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx,
+       .sta_rc_update = wlcore_op_sta_rc_update,
        CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
 };
 
@@ -5046,34 +5465,6 @@ static struct bin_attribute fwlog_attr = {
        .read = wl1271_sysfs_read_fwlog,
 };
 
-static void wl1271_connection_loss_work(struct work_struct *work)
-{
-       struct delayed_work *dwork;
-       struct wl1271 *wl;
-       struct ieee80211_vif *vif;
-       struct wl12xx_vif *wlvif;
-
-       dwork = container_of(work, struct delayed_work, work);
-       wl = container_of(dwork, struct wl1271, connection_loss_work);
-
-       wl1271_info("Connection loss work.");
-
-       mutex_lock(&wl->mutex);
-
-       if (unlikely(wl->state != WLCORE_STATE_ON))
-               goto out;
-
-       /* Call mac80211 connection loss */
-       wl12xx_for_each_wlvif_sta(wl, wlvif) {
-               if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
-                       goto out;
-               vif = wl12xx_wlvif_to_vif(wlvif);
-               ieee80211_connection_loss(vif);
-       }
-out:
-       mutex_unlock(&wl->mutex);
-}
-
 static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic)
 {
        int i;
@@ -5119,7 +5510,7 @@ static int wl12xx_get_hw_info(struct wl1271 *wl)
 
        ret = wl12xx_set_power_on(wl);
        if (ret < 0)
-               goto out;
+               return ret;
 
        ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &wl->chip.id);
        if (ret < 0)
@@ -5209,10 +5600,9 @@ static const struct ieee80211_iface_limit wlcore_iface_limits[] = {
        },
 };
 
-static const struct ieee80211_iface_combination
+static struct ieee80211_iface_combination
 wlcore_iface_combinations[] = {
        {
-         .num_different_channels = 1,
          .max_interfaces = 3,
          .limits = wlcore_iface_limits,
          .n_limits = ARRAY_SIZE(wlcore_iface_limits),
@@ -5221,6 +5611,7 @@ wlcore_iface_combinations[] = {
 
 static int wl1271_init_ieee80211(struct wl1271 *wl)
 {
+       int i;
        static const u32 cipher_suites[] = {
                WLAN_CIPHER_SUITE_WEP40,
                WLAN_CIPHER_SUITE_WEP104,
@@ -5251,7 +5642,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
                IEEE80211_HW_AP_LINK_PS |
                IEEE80211_HW_AMPDU_AGGREGATION |
                IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
-               IEEE80211_HW_SCAN_WHILE_IDLE;
+               IEEE80211_HW_SCAN_WHILE_IDLE |
+               IEEE80211_HW_QUEUE_CONTROL;
 
        wl->hw->wiphy->cipher_suites = cipher_suites;
        wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
@@ -5273,6 +5665,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
                sizeof(struct ieee80211_header);
 
+       wl->hw->wiphy->max_remain_on_channel_duration = 5000;
+
        wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
                                WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
 
@@ -5280,6 +5674,22 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
                     ARRAY_SIZE(wl1271_channels_5ghz) >
                     WL1271_MAX_CHANNELS);
+       /*
+       * clear channel flags from the previous usage
+       * and restore max_power & max_antenna_gain values.
+       */
+       for (i = 0; i < ARRAY_SIZE(wl1271_channels); i++) {
+               wl1271_band_2ghz.channels[i].flags = 0;
+               wl1271_band_2ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
+               wl1271_band_2ghz.channels[i].max_antenna_gain = 0;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(wl1271_channels_5ghz); i++) {
+               wl1271_band_5ghz.channels[i].flags = 0;
+               wl1271_band_5ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
+               wl1271_band_5ghz.channels[i].max_antenna_gain = 0;
+       }
+
        /*
         * We keep local copies of the band structs because we need to
         * modify them on a per-device basis.
@@ -5300,7 +5710,14 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
        wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
                &wl->bands[IEEE80211_BAND_5GHZ];
 
-       wl->hw->queues = 4;
+       /*
+        * allow 4 queues per mac address we support +
+        * 1 cab queue per mac + one global offchannel Tx queue
+        */
+       wl->hw->queues = (NUM_TX_QUEUES + 1) * WLCORE_NUM_MAC_ADDRESSES + 1;
+
+       /* the last queue is the offchannel queue */
+       wl->hw->offchannel_tx_hw_queue = wl->hw->queues - 1;
        wl->hw->max_rates = 1;
 
        wl->hw->wiphy->reg_notifier = wl1271_reg_notify;
@@ -5313,6 +5730,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
                NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
 
        /* allowed interface combinations */
+       wlcore_iface_combinations[0].num_different_channels = wl->num_channels;
        wl->hw->wiphy->iface_combinations = wlcore_iface_combinations;
        wl->hw->wiphy->n_iface_combinations =
                ARRAY_SIZE(wlcore_iface_combinations);
@@ -5329,7 +5747,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
 
 #define WL1271_DEFAULT_CHANNEL 0
 
-struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
+struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
+                                    u32 mbox_size)
 {
        struct ieee80211_hw *hw;
        struct wl1271 *wl;
@@ -5371,9 +5790,8 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
        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->roc_complete_work, wlcore_roc_complete_work);
        INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work);
-       INIT_DELAYED_WORK(&wl->connection_loss_work,
-                         wl1271_connection_loss_work);
 
        wl->freezable_wq = create_freezable_workqueue("wl12xx_wq");
        if (!wl->freezable_wq) {
@@ -5389,14 +5807,15 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
        wl->flags = 0;
        wl->sg_enabled = true;
        wl->sleep_auth = WL1271_PSM_ILLEGAL;
+       wl->recovery_count = 0;
        wl->hw_pg_ver = -1;
        wl->ap_ps_map = 0;
        wl->ap_fw_ps_map = 0;
        wl->quirks = 0;
        wl->platform_quirks = 0;
-       wl->sched_scanning = false;
        wl->system_hlid = WL12XX_SYSTEM_HLID;
        wl->active_sta_count = 0;
+       wl->active_link_count = 0;
        wl->fwlog_size = 0;
        init_waitqueue_head(&wl->fwlog_waitq);
 
@@ -5436,14 +5855,24 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
                goto err_dummy_packet;
        }
 
-       wl->mbox = kmalloc(sizeof(*wl->mbox), GFP_KERNEL | GFP_DMA);
+       wl->mbox_size = mbox_size;
+       wl->mbox = kmalloc(wl->mbox_size, GFP_KERNEL | GFP_DMA);
        if (!wl->mbox) {
                ret = -ENOMEM;
                goto err_fwlog;
        }
 
+       wl->buffer_32 = kmalloc(sizeof(*wl->buffer_32), GFP_KERNEL);
+       if (!wl->buffer_32) {
+               ret = -ENOMEM;
+               goto err_mbox;
+       }
+
        return hw;
 
+err_mbox:
+       kfree(wl->mbox);
+
 err_fwlog:
        free_page((unsigned long)wl->fwlog);
 
@@ -5482,6 +5911,8 @@ int wlcore_free_hw(struct wl1271 *wl)
        device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
 
        device_remove_file(wl->dev, &dev_attr_bt_coex_state);
+       kfree(wl->buffer_32);
+       kfree(wl->mbox);
        free_page((unsigned long)wl->fwlog);
        dev_kfree_skb(wl->dummy_packet);
        free_pages((unsigned long)wl->aggr_buf, get_order(wl->aggr_buf_size));
@@ -5714,10 +6145,10 @@ module_param_named(fwlog, fwlog_param, charp, 0);
 MODULE_PARM_DESC(fwlog,
                 "FW logger options: continuous, ondemand, dbgpins or disable");
 
-module_param(bug_on_recovery, bool, S_IRUSR | S_IWUSR);
+module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR);
 MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");
 
-module_param(no_recovery, bool, S_IRUSR | S_IWUSR);
+module_param(no_recovery, int, S_IRUSR | S_IWUSR);
 MODULE_PARM_DESC(no_recovery, "Prevent HW recovery. FW will remain stuck.");
 
 MODULE_LICENSE("GPL");
index 4d1414a673fb6025f42d20e4b7da16fcaf2f6265..9b7b6e2e4fbcef50adaab008cfeb1867bf4a266d 100644 (file)
@@ -151,9 +151,6 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl)
                        wl12xx_queue_recovery_work(wl);
                        ret = -ETIMEDOUT;
                        goto err;
-               } else if (ret < 0) {
-                       wl1271_error("ELP wakeup completion error.");
-                       goto err;
                }
        }
 
@@ -242,11 +239,12 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
        struct ieee80211_tx_info *info;
        unsigned long flags;
        int filtered[NUM_TX_QUEUES];
+       struct wl1271_link *lnk = &wl->links[hlid];
 
        /* filter all frames currently in the low level queues for this hlid */
        for (i = 0; i < NUM_TX_QUEUES; i++) {
                filtered[i] = 0;
-               while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
+               while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
                        filtered[i]++;
 
                        if (WARN_ON(wl12xx_is_dummy_packet(wl, skb)))
@@ -260,8 +258,11 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
        }
 
        spin_lock_irqsave(&wl->wl_lock, flags);
-       for (i = 0; i < NUM_TX_QUEUES; i++)
+       for (i = 0; i < NUM_TX_QUEUES; i++) {
                wl->tx_queue_count[i] -= filtered[i];
+               if (lnk->wlvif)
+                       lnk->wlvif->tx_queue_count[i] -= filtered[i];
+       }
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 
        wl1271_handle_tx_low_watermark(wl);
index 9ee0ec6fd1db3d666769747e2f52c42bcdbe53e1..6791a1a6afba06702b434b1ae11a196377a0d229 100644 (file)
@@ -92,11 +92,16 @@ static void wl1271_rx_status(struct wl1271 *wl,
                status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED |
                                RX_FLAG_DECRYPTED;
 
-               if (unlikely(desc_err_code == WL1271_RX_DESC_MIC_FAIL)) {
+               if (unlikely(desc_err_code & WL1271_RX_DESC_MIC_FAIL)) {
                        status->flag |= RX_FLAG_MMIC_ERROR;
-                       wl1271_warning("Michael MIC error");
+                       wl1271_warning("Michael MIC error. Desc: 0x%x",
+                                      desc_err_code);
                }
        }
+
+       if (beacon)
+               wlcore_set_pending_regdomain_ch(wl, (u16)desc->channel,
+                                               status->band);
 }
 
 static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
@@ -108,7 +113,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
        u8 *buf;
        u8 beacon = 0;
        u8 is_data = 0;
-       u8 reserved = 0;
+       u8 reserved = 0, offset_to_data = 0;
        u16 seq_num;
        u32 pkt_data_len;
 
@@ -128,6 +133,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
 
        if (rx_align == WLCORE_RX_BUF_UNALIGNED)
                reserved = RX_BUF_ALIGN;
+       else if (rx_align == WLCORE_RX_BUF_PADDED)
+               offset_to_data = RX_BUF_ALIGN;
 
        /* the data read starts with the descriptor */
        desc = (struct wl1271_rx_descriptor *) data;
@@ -139,19 +146,15 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
                return 0;
        }
 
-       switch (desc->status & WL1271_RX_DESC_STATUS_MASK) {
        /* discard corrupted packets */
-       case WL1271_RX_DESC_DRIVER_RX_Q_FAIL:
-       case WL1271_RX_DESC_DECRYPT_FAIL:
-               wl1271_warning("corrupted packet in RX with status: 0x%x",
-                              desc->status & WL1271_RX_DESC_STATUS_MASK);
-               return -EINVAL;
-       case WL1271_RX_DESC_SUCCESS:
-       case WL1271_RX_DESC_MIC_FAIL:
-               break;
-       default:
-               wl1271_error("invalid RX descriptor status: 0x%x",
-                            desc->status & WL1271_RX_DESC_STATUS_MASK);
+       if (desc->status & WL1271_RX_DESC_DECRYPT_FAIL) {
+               hdr = (void *)(data + sizeof(*desc) + offset_to_data);
+               wl1271_warning("corrupted packet in RX: status: 0x%x len: %d",
+                              desc->status & WL1271_RX_DESC_STATUS_MASK,
+                              pkt_data_len);
+               wl1271_dump((DEBUG_RX|DEBUG_CMD), "PKT: ", data + sizeof(*desc),
+                           min(pkt_data_len,
+                               ieee80211_hdrlen(hdr->frame_control)));
                return -EINVAL;
        }
 
index 71eba18999152f6462600f703ca345a91f37e56d..3363f60fb7da6dfb05ad672b359ac5d037fbb401 100644 (file)
  * Bits 3-5 - process_id tag (AP mode FW)
  * Bits 6-7 - reserved
  */
-#define WL1271_RX_DESC_STATUS_MASK      0x03
+#define WL1271_RX_DESC_STATUS_MASK      0x07
 
 #define WL1271_RX_DESC_SUCCESS          0x00
 #define WL1271_RX_DESC_DECRYPT_FAIL     0x01
 #define WL1271_RX_DESC_MIC_FAIL         0x02
-#define WL1271_RX_DESC_DRIVER_RX_Q_FAIL 0x03
 
 #define RX_MEM_BLOCK_MASK            0xFF
 #define RX_BUF_SIZE_MASK             0xFFF00
index d00501493dfec06d9aa314c675e1834f12bb98d2..f407101e525b60a9c4bff3c402865d12f3d6afde 100644 (file)
@@ -35,7 +35,6 @@ void wl1271_scan_complete_work(struct work_struct *work)
 {
        struct delayed_work *dwork;
        struct wl1271 *wl;
-       struct ieee80211_vif *vif;
        struct wl12xx_vif *wlvif;
        int ret;
 
@@ -52,8 +51,7 @@ void wl1271_scan_complete_work(struct work_struct *work)
        if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
                goto out;
 
-       vif = wl->scan_vif;
-       wlvif = wl12xx_vif_to_data(vif);
+       wlvif = wl->scan_wlvif;
 
        /*
         * Rearm the tx watchdog just before idling scan. This
@@ -64,7 +62,7 @@ void wl1271_scan_complete_work(struct work_struct *work)
        wl->scan.state = WL1271_SCAN_STATE_IDLE;
        memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
        wl->scan.req = NULL;
-       wl->scan_vif = NULL;
+       wl->scan_wlvif = NULL;
 
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
@@ -82,6 +80,8 @@ void wl1271_scan_complete_work(struct work_struct *work)
                wl12xx_queue_recovery_work(wl);
        }
 
+       wlcore_cmd_regdomain_config_locked(wl);
+
        ieee80211_scan_completed(wl->hw, false);
 
 out:
@@ -89,371 +89,99 @@ out:
 
 }
 
-
-static int wl1271_get_scan_channels(struct wl1271 *wl,
-                                   struct cfg80211_scan_request *req,
-                                   struct basic_scan_channel_params *channels,
-                                   enum ieee80211_band band, bool passive)
-{
-       struct conf_scan_settings *c = &wl->conf.scan;
-       int i, j;
-       u32 flags;
-
-       for (i = 0, j = 0;
-            i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS;
-            i++) {
-               flags = req->channels[i]->flags;
-
-               if (!test_bit(i, wl->scan.scanned_ch) &&
-                   !(flags & IEEE80211_CHAN_DISABLED) &&
-                   (req->channels[i]->band == band) &&
-                   /*
-                    * In passive scans, we scan all remaining
-                    * channels, even if not marked as such.
-                    * In active scans, we only scan channels not
-                    * marked as passive.
-                    */
-                   (passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) {
-                       wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
-                                    req->channels[i]->band,
-                                    req->channels[i]->center_freq);
-                       wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
-                                    req->channels[i]->hw_value,
-                                    req->channels[i]->flags);
-                       wl1271_debug(DEBUG_SCAN,
-                                    "max_antenna_gain %d, max_power %d",
-                                    req->channels[i]->max_antenna_gain,
-                                    req->channels[i]->max_power);
-                       wl1271_debug(DEBUG_SCAN, "beacon_found %d",
-                                    req->channels[i]->beacon_found);
-
-                       if (!passive) {
-                               channels[j].min_duration =
-                                       cpu_to_le32(c->min_dwell_time_active);
-                               channels[j].max_duration =
-                                       cpu_to_le32(c->max_dwell_time_active);
-                       } else {
-                               channels[j].min_duration =
-                                       cpu_to_le32(c->min_dwell_time_passive);
-                               channels[j].max_duration =
-                                       cpu_to_le32(c->max_dwell_time_passive);
-                       }
-                       channels[j].early_termination = 0;
-                       channels[j].tx_power_att = req->channels[i]->max_power;
-                       channels[j].channel = req->channels[i]->hw_value;
-
-                       memset(&channels[j].bssid_lsb, 0xff, 4);
-                       memset(&channels[j].bssid_msb, 0xff, 2);
-
-                       /* Mark the channels we already used */
-                       set_bit(i, wl->scan.scanned_ch);
-
-                       j++;
-               }
-       }
-
-       return j;
-}
-
-#define WL1271_NOTHING_TO_SCAN 1
-
-static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif,
-                           enum ieee80211_band band,
-                           bool passive, u32 basic_rate)
+static void wlcore_started_vifs_iter(void *data, u8 *mac,
+                                    struct ieee80211_vif *vif)
 {
-       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-       struct wl1271_cmd_scan *cmd;
-       struct wl1271_cmd_trigger_scan_to *trigger;
-       int ret;
-       u16 scan_options = 0;
-
-       /* skip active scans if we don't have SSIDs */
-       if (!passive && wl->scan.req->n_ssids == 0)
-               return WL1271_NOTHING_TO_SCAN;
-
-       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
-       trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
-       if (!cmd || !trigger) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       if (wl->conf.scan.split_scan_timeout)
-               scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN;
-
-       if (passive)
-               scan_options |= WL1271_SCAN_OPT_PASSIVE;
-
-       cmd->params.role_id = wlvif->role_id;
-
-       if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) {
-               ret = -EINVAL;
-               goto out;
-       }
-
-       cmd->params.scan_options = cpu_to_le16(scan_options);
-
-       cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
-                                                   cmd->channels,
-                                                   band, passive);
-       if (cmd->params.n_ch == 0) {
-               ret = WL1271_NOTHING_TO_SCAN;
-               goto out;
-       }
-
-       cmd->params.tx_rate = cpu_to_le32(basic_rate);
-       cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs;
-       cmd->params.tid_trigger = CONF_TX_AC_ANY_TID;
-       cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
-
-       if (band == IEEE80211_BAND_2GHZ)
-               cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ;
-       else
-               cmd->params.band = WL1271_SCAN_BAND_5_GHZ;
-
-       if (wl->scan.ssid_len && wl->scan.ssid) {
-               cmd->params.ssid_len = wl->scan.ssid_len;
-               memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len);
-       }
-
-       memcpy(cmd->addr, vif->addr, ETH_ALEN);
-
-       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, false);
-       if (ret < 0) {
-               wl1271_error("PROBE request template failed");
-               goto out;
-       }
-
-       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) {
-               wl1271_error("trigger scan to failed for hw scan");
-               goto out;
-       }
-
-       wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
+       int *count = (int *)data;
 
-       ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
-       if (ret < 0) {
-               wl1271_error("SCAN failed");
-               goto out;
-       }
-
-out:
-       kfree(cmd);
-       kfree(trigger);
-       return ret;
+       if (!vif->bss_conf.idle)
+               (*count)++;
 }
 
-void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif)
+static int wlcore_count_started_vifs(struct wl1271 *wl)
 {
-       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
-       int ret = 0;
-       enum ieee80211_band band;
-       u32 rate, mask;
-
-       switch (wl->scan.state) {
-       case WL1271_SCAN_STATE_IDLE:
-               break;
-
-       case WL1271_SCAN_STATE_2GHZ_ACTIVE:
-               band = IEEE80211_BAND_2GHZ;
-               mask = wlvif->bitrate_masks[band];
-               if (wl->scan.req->no_cck) {
-                       mask &= ~CONF_TX_CCK_RATES;
-                       if (!mask)
-                               mask = CONF_TX_RATE_MASK_BASIC_P2P;
-               }
-               rate = wl1271_tx_min_rate_get(wl, mask);
-               ret = wl1271_scan_send(wl, vif, band, false, rate);
-               if (ret == WL1271_NOTHING_TO_SCAN) {
-                       wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE;
-                       wl1271_scan_stm(wl, vif);
-               }
-
-               break;
-
-       case WL1271_SCAN_STATE_2GHZ_PASSIVE:
-               band = IEEE80211_BAND_2GHZ;
-               mask = wlvif->bitrate_masks[band];
-               if (wl->scan.req->no_cck) {
-                       mask &= ~CONF_TX_CCK_RATES;
-                       if (!mask)
-                               mask = CONF_TX_RATE_MASK_BASIC_P2P;
-               }
-               rate = wl1271_tx_min_rate_get(wl, mask);
-               ret = wl1271_scan_send(wl, vif, band, true, rate);
-               if (ret == WL1271_NOTHING_TO_SCAN) {
-                       if (wl->enable_11a)
-                               wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE;
-                       else
-                               wl->scan.state = WL1271_SCAN_STATE_DONE;
-                       wl1271_scan_stm(wl, vif);
-               }
-
-               break;
+       int count = 0;
 
-       case WL1271_SCAN_STATE_5GHZ_ACTIVE:
-               band = IEEE80211_BAND_5GHZ;
-               rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
-               ret = wl1271_scan_send(wl, vif, band, false, rate);
-               if (ret == WL1271_NOTHING_TO_SCAN) {
-                       wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE;
-                       wl1271_scan_stm(wl, vif);
-               }
-
-               break;
-
-       case WL1271_SCAN_STATE_5GHZ_PASSIVE:
-               band = IEEE80211_BAND_5GHZ;
-               rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]);
-               ret = wl1271_scan_send(wl, vif, band, true, rate);
-               if (ret == WL1271_NOTHING_TO_SCAN) {
-                       wl->scan.state = WL1271_SCAN_STATE_DONE;
-                       wl1271_scan_stm(wl, vif);
-               }
-
-               break;
-
-       case WL1271_SCAN_STATE_DONE:
-               wl->scan.failed = false;
-               cancel_delayed_work(&wl->scan_complete_work);
-               ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
-                                            msecs_to_jiffies(0));
-               break;
-
-       default:
-               wl1271_error("invalid scan state");
-               break;
-       }
-
-       if (ret < 0) {
-               cancel_delayed_work(&wl->scan_complete_work);
-               ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
-                                            msecs_to_jiffies(0));
-       }
-}
-
-int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
-               const u8 *ssid, size_t ssid_len,
-               struct cfg80211_scan_request *req)
-{
-       /*
-        * cfg80211 should guarantee that we don't get more channels
-        * than what we have registered.
-        */
-       BUG_ON(req->n_channels > WL1271_MAX_CHANNELS);
-
-       if (wl->scan.state != WL1271_SCAN_STATE_IDLE)
-               return -EBUSY;
-
-       wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE;
-
-       if (ssid_len && ssid) {
-               wl->scan.ssid_len = ssid_len;
-               memcpy(wl->scan.ssid, ssid, ssid_len);
-       } else {
-               wl->scan.ssid_len = 0;
-       }
-
-       wl->scan_vif = vif;
-       wl->scan.req = req;
-       memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
-
-       /* we assume failure so that timeout scenarios are handled correctly */
-       wl->scan.failed = true;
-       ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
-                                    msecs_to_jiffies(WL1271_SCAN_TIMEOUT));
-
-       wl1271_scan_stm(wl, vif);
-
-       return 0;
-}
-
-int wl1271_scan_stop(struct wl1271 *wl)
-{
-       struct wl1271_cmd_header *cmd = NULL;
-       int ret = 0;
-
-       if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE))
-               return -EINVAL;
-
-       wl1271_debug(DEBUG_CMD, "cmd scan stop");
-
-       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
-       if (!cmd) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd,
-                             sizeof(*cmd), 0);
-       if (ret < 0) {
-               wl1271_error("cmd stop_scan failed");
-               goto out;
-       }
-out:
-       kfree(cmd);
-       return ret;
+       ieee80211_iterate_active_interfaces_atomic(wl->hw,
+                                       IEEE80211_IFACE_ITER_RESUME_ALL,
+                                       wlcore_started_vifs_iter, &count);
+       return count;
 }
 
 static int
-wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
-                                   struct cfg80211_sched_scan_request *req,
-                                   struct conn_scan_ch_params *channels,
-                                   u32 band, bool radar, bool passive,
-                                   int start, int max_channels,
-                                   u8 *n_pactive_ch)
+wlcore_scan_get_channels(struct wl1271 *wl,
+                        struct ieee80211_channel *req_channels[],
+                        u32 n_channels,
+                        u32 n_ssids,
+                        struct conn_scan_ch_params *channels,
+                        u32 band, bool radar, bool passive,
+                        int start, int max_channels,
+                        u8 *n_pactive_ch,
+                        int scan_type)
 {
-       struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
        int i, j;
        u32 flags;
-       bool force_passive = !req->n_ssids;
-       u32 min_dwell_time_active, max_dwell_time_active, delta_per_probe;
+       bool force_passive = !n_ssids;
+       u32 min_dwell_time_active, max_dwell_time_active;
        u32 dwell_time_passive, dwell_time_dfs;
 
-       if (band == IEEE80211_BAND_5GHZ)
-               delta_per_probe = c->dwell_time_delta_per_probe_5;
-       else
-               delta_per_probe = c->dwell_time_delta_per_probe;
+       /* configure dwell times according to scan type */
+       if (scan_type == SCAN_TYPE_SEARCH) {
+               struct conf_scan_settings *c = &wl->conf.scan;
+               bool active_vif_exists = !!wlcore_count_started_vifs(wl);
+
+               min_dwell_time_active = active_vif_exists ?
+                       c->min_dwell_time_active :
+                       c->min_dwell_time_active_long;
+               max_dwell_time_active = active_vif_exists ?
+                       c->max_dwell_time_active :
+                       c->max_dwell_time_active_long;
+               dwell_time_passive = c->dwell_time_passive;
+               dwell_time_dfs = c->dwell_time_dfs;
+       } else {
+               struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
+               u32 delta_per_probe;
 
-       min_dwell_time_active = c->base_dwell_time +
-                req->n_ssids * c->num_probe_reqs * delta_per_probe;
+               if (band == IEEE80211_BAND_5GHZ)
+                       delta_per_probe = c->dwell_time_delta_per_probe_5;
+               else
+                       delta_per_probe = c->dwell_time_delta_per_probe;
 
-       max_dwell_time_active = min_dwell_time_active + c->max_dwell_time_delta;
+               min_dwell_time_active = c->base_dwell_time +
+                        n_ssids * c->num_probe_reqs * delta_per_probe;
 
+               max_dwell_time_active = min_dwell_time_active +
+                                       c->max_dwell_time_delta;
+               dwell_time_passive = c->dwell_time_passive;
+               dwell_time_dfs = c->dwell_time_dfs;
+       }
        min_dwell_time_active = DIV_ROUND_UP(min_dwell_time_active, 1000);
        max_dwell_time_active = DIV_ROUND_UP(max_dwell_time_active, 1000);
-       dwell_time_passive = DIV_ROUND_UP(c->dwell_time_passive, 1000);
-       dwell_time_dfs = DIV_ROUND_UP(c->dwell_time_dfs, 1000);
+       dwell_time_passive = DIV_ROUND_UP(dwell_time_passive, 1000);
+       dwell_time_dfs = DIV_ROUND_UP(dwell_time_dfs, 1000);
 
        for (i = 0, j = start;
-            i < req->n_channels && j < max_channels;
+            i < n_channels && j < max_channels;
             i++) {
-               flags = req->channels[i]->flags;
+               flags = req_channels[i]->flags;
 
                if (force_passive)
                        flags |= IEEE80211_CHAN_PASSIVE_SCAN;
 
-               if ((req->channels[i]->band == band) &&
+               if ((req_channels[i]->band == band) &&
                    !(flags & IEEE80211_CHAN_DISABLED) &&
                    (!!(flags & IEEE80211_CHAN_RADAR) == radar) &&
                    /* if radar is set, we ignore the passive flag */
                    (radar ||
                     !!(flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive)) {
                        wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
-                                    req->channels[i]->band,
-                                    req->channels[i]->center_freq);
+                                    req_channels[i]->band,
+                                    req_channels[i]->center_freq);
                        wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
-                                    req->channels[i]->hw_value,
-                                    req->channels[i]->flags);
+                                    req_channels[i]->hw_value,
+                                    req_channels[i]->flags);
                        wl1271_debug(DEBUG_SCAN, "max_power %d",
-                                    req->channels[i]->max_power);
+                                    req_channels[i]->max_power);
                        wl1271_debug(DEBUG_SCAN, "min_dwell_time %d max dwell time %d",
                                     min_dwell_time_active,
                                     max_dwell_time_active);
@@ -473,10 +201,11 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
                        channels[j].max_duration =
                                cpu_to_le16(max_dwell_time_active);
 
-                       channels[j].tx_power_att = req->channels[i]->max_power;
-                       channels[j].channel = req->channels[i]->hw_value;
+                       channels[j].tx_power_att = req_channels[i]->max_power;
+                       channels[j].channel = req_channels[i]->hw_value;
 
-                       if ((band == IEEE80211_BAND_2GHZ) &&
+                       if (n_pactive_ch &&
+                           (band == IEEE80211_BAND_2GHZ) &&
                            (channels[j].channel >= 12) &&
                            (channels[j].channel <= 14) &&
                            (flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
@@ -500,51 +229,80 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
        return j - start;
 }
 
-static bool
-wl1271_scan_sched_scan_channels(struct wl1271 *wl,
-                               struct cfg80211_sched_scan_request *req,
-                               struct wl1271_cmd_sched_scan_config *cfg)
+bool
+wlcore_set_scan_chan_params(struct wl1271 *wl,
+                           struct wlcore_scan_channels *cfg,
+                           struct ieee80211_channel *channels[],
+                           u32 n_channels,
+                           u32 n_ssids,
+                           int scan_type)
 {
        u8 n_pactive_ch = 0;
 
        cfg->passive[0] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
-                                                   IEEE80211_BAND_2GHZ,
-                                                   false, true, 0,
-                                                   MAX_CHANNELS_2GHZ,
-                                                   &n_pactive_ch);
+               wlcore_scan_get_channels(wl,
+                                        channels,
+                                        n_channels,
+                                        n_ssids,
+                                        cfg->channels_2,
+                                        IEEE80211_BAND_2GHZ,
+                                        false, true, 0,
+                                        MAX_CHANNELS_2GHZ,
+                                        &n_pactive_ch,
+                                        scan_type);
        cfg->active[0] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
-                                                   IEEE80211_BAND_2GHZ,
-                                                   false, false,
-                                                   cfg->passive[0],
-                                                   MAX_CHANNELS_2GHZ,
-                                                   &n_pactive_ch);
+               wlcore_scan_get_channels(wl,
+                                        channels,
+                                        n_channels,
+                                        n_ssids,
+                                        cfg->channels_2,
+                                        IEEE80211_BAND_2GHZ,
+                                        false, false,
+                                        cfg->passive[0],
+                                        MAX_CHANNELS_2GHZ,
+                                        &n_pactive_ch,
+                                        scan_type);
        cfg->passive[1] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
-                                                   IEEE80211_BAND_5GHZ,
-                                                   false, true, 0,
-                                                   MAX_CHANNELS_5GHZ,
-                                                   &n_pactive_ch);
+               wlcore_scan_get_channels(wl,
+                                        channels,
+                                        n_channels,
+                                        n_ssids,
+                                        cfg->channels_5,
+                                        IEEE80211_BAND_5GHZ,
+                                        false, true, 0,
+                                        wl->max_channels_5,
+                                        &n_pactive_ch,
+                                        scan_type);
        cfg->dfs =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
-                                                   IEEE80211_BAND_5GHZ,
-                                                   true, true,
-                                                   cfg->passive[1],
-                                                   MAX_CHANNELS_5GHZ,
-                                                   &n_pactive_ch);
+               wlcore_scan_get_channels(wl,
+                                        channels,
+                                        n_channels,
+                                        n_ssids,
+                                        cfg->channels_5,
+                                        IEEE80211_BAND_5GHZ,
+                                        true, true,
+                                        cfg->passive[1],
+                                        wl->max_channels_5,
+                                        &n_pactive_ch,
+                                        scan_type);
        cfg->active[1] =
-               wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
-                                                   IEEE80211_BAND_5GHZ,
-                                                   false, false,
-                                                   cfg->passive[1] + cfg->dfs,
-                                                   MAX_CHANNELS_5GHZ,
-                                                   &n_pactive_ch);
+               wlcore_scan_get_channels(wl,
+                                        channels,
+                                        n_channels,
+                                        n_ssids,
+                                        cfg->channels_5,
+                                        IEEE80211_BAND_5GHZ,
+                                        false, false,
+                                        cfg->passive[1] + cfg->dfs,
+                                        wl->max_channels_5,
+                                        &n_pactive_ch,
+                                        scan_type);
+
        /* 802.11j channels are not supported yet */
        cfg->passive[2] = 0;
        cfg->active[2] = 0;
 
-       cfg->n_pactive_ch = n_pactive_ch;
+       cfg->passive_active = n_pactive_ch;
 
        wl1271_debug(DEBUG_SCAN, "    2.4GHz: active %d passive %d",
                     cfg->active[0], cfg->passive[0]);
@@ -556,10 +314,48 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl,
                cfg->passive[1] || cfg->active[1] || cfg->dfs ||
                cfg->passive[2] || cfg->active[2];
 }
+EXPORT_SYMBOL_GPL(wlcore_set_scan_chan_params);
+
+int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
+               const u8 *ssid, size_t ssid_len,
+               struct cfg80211_scan_request *req)
+{
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+
+       /*
+        * cfg80211 should guarantee that we don't get more channels
+        * than what we have registered.
+        */
+       BUG_ON(req->n_channels > WL1271_MAX_CHANNELS);
+
+       if (wl->scan.state != WL1271_SCAN_STATE_IDLE)
+               return -EBUSY;
+
+       wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE;
+
+       if (ssid_len && ssid) {
+               wl->scan.ssid_len = ssid_len;
+               memcpy(wl->scan.ssid, ssid, ssid_len);
+       } else {
+               wl->scan.ssid_len = 0;
+       }
+
+       wl->scan_wlvif = wlvif;
+       wl->scan.req = req;
+       memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
+
+       /* we assume failure so that timeout scenarios are handled correctly */
+       wl->scan.failed = true;
+       ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
+                                    msecs_to_jiffies(WL1271_SCAN_TIMEOUT));
 
+       wl->ops->scan_start(wl, wlvif, req);
+
+       return 0;
+}
 /* Returns the scan type to be used or a negative value on error */
-static int
-wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
+int
+wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl,
                                 struct wl12xx_vif *wlvif,
                                 struct cfg80211_sched_scan_request *req)
 {
@@ -662,160 +458,12 @@ out:
                return ret;
        return type;
 }
+EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_ssid_list);
 
-int wl1271_scan_sched_scan_config(struct wl1271 *wl,
-                                 struct wl12xx_vif *wlvif,
-                                 struct cfg80211_sched_scan_request *req,
-                                 struct ieee80211_sched_scan_ies *ies)
-{
-       struct wl1271_cmd_sched_scan_config *cfg = NULL;
-       struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
-       int i, ret;
-       bool force_passive = !req->n_ssids;
-
-       wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
-
-       cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
-       if (!cfg)
-               return -ENOMEM;
-
-       cfg->role_id = wlvif->role_id;
-       cfg->rssi_threshold = c->rssi_threshold;
-       cfg->snr_threshold  = c->snr_threshold;
-       cfg->n_probe_reqs = c->num_probe_reqs;
-       /* cycles set to 0 it means infinite (until manually stopped) */
-       cfg->cycles = 0;
-       /* report APs when at least 1 is found */
-       cfg->report_after = 1;
-       /* don't stop scanning automatically when something is found */
-       cfg->terminate = 0;
-       cfg->tag = WL1271_SCAN_DEFAULT_TAG;
-       /* don't filter on BSS type */
-       cfg->bss_type = SCAN_BSS_TYPE_ANY;
-       /* currently NL80211 supports only a single interval */
-       for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++)
-               cfg->intervals[i] = cpu_to_le32(req->interval);
-
-       cfg->ssid_len = 0;
-       ret = wl12xx_scan_sched_scan_ssid_list(wl, wlvif, req);
-       if (ret < 0)
-               goto out;
-
-       cfg->filter_type = ret;
-
-       wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type);
-
-       if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) {
-               wl1271_error("scan channel list is empty");
-               ret = -EINVAL;
-               goto out;
-       }
-
-       if (!force_passive && cfg->active[0]) {
-               u8 band = IEEE80211_BAND_2GHZ;
-               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
-                                                wlvif->role_id, band,
-                                                req->ssids[0].ssid,
-                                                req->ssids[0].ssid_len,
-                                                ies->ie[band],
-                                                ies->len[band], true);
-               if (ret < 0) {
-                       wl1271_error("2.4GHz PROBE request template failed");
-                       goto out;
-               }
-       }
-
-       if (!force_passive && cfg->active[1]) {
-               u8 band = IEEE80211_BAND_5GHZ;
-               ret = wl12xx_cmd_build_probe_req(wl, wlvif,
-                                                wlvif->role_id, band,
-                                                req->ssids[0].ssid,
-                                                req->ssids[0].ssid_len,
-                                                ies->ie[band],
-                                                ies->len[band], true);
-               if (ret < 0) {
-                       wl1271_error("5GHz PROBE request template failed");
-                       goto out;
-               }
-       }
-
-       wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg));
-
-       ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg,
-                             sizeof(*cfg), 0);
-       if (ret < 0) {
-               wl1271_error("SCAN configuration failed");
-               goto out;
-       }
-out:
-       kfree(cfg);
-       return ret;
-}
-
-int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif)
-{
-       struct wl1271_cmd_sched_scan_start *start;
-       int ret = 0;
-
-       wl1271_debug(DEBUG_CMD, "cmd periodic scan start");
-
-       if (wlvif->bss_type != BSS_TYPE_STA_BSS)
-               return -EOPNOTSUPP;
-
-       if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) &&
-           test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags))
-               return -EBUSY;
-
-       start = kzalloc(sizeof(*start), GFP_KERNEL);
-       if (!start)
-               return -ENOMEM;
-
-       start->role_id = wlvif->role_id;
-       start->tag = WL1271_SCAN_DEFAULT_TAG;
-
-       ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start,
-                             sizeof(*start), 0);
-       if (ret < 0) {
-               wl1271_error("failed to send scan start command");
-               goto out_free;
-       }
-
-out_free:
-       kfree(start);
-       return ret;
-}
-
-void wl1271_scan_sched_scan_results(struct wl1271 *wl)
+void wlcore_scan_sched_scan_results(struct wl1271 *wl)
 {
        wl1271_debug(DEBUG_SCAN, "got periodic scan results");
 
        ieee80211_sched_scan_results(wl->hw);
 }
-
-void wl1271_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif)
-{
-       struct wl1271_cmd_sched_scan_stop *stop;
-       int ret = 0;
-
-       wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
-
-       /* FIXME: what to do if alloc'ing to stop fails? */
-       stop = kzalloc(sizeof(*stop), GFP_KERNEL);
-       if (!stop) {
-               wl1271_error("failed to alloc memory to send sched scan stop");
-               return;
-       }
-
-       stop->role_id = wlvif->role_id;
-       stop->tag = WL1271_SCAN_DEFAULT_TAG;
-
-       ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop,
-                             sizeof(*stop), 0);
-       if (ret < 0) {
-               wl1271_error("failed to send sched scan stop command");
-               goto out_free;
-       }
-
-out_free:
-       kfree(stop);
-}
+EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_results);
index 29f3c8d6b0468265dc55528a493eee7de9a8625b..a6ab24b5c0f96ab86278f71b93aae80a11048433 100644 (file)
 
 #include "wlcore.h"
 
-int wl1271_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
+int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif,
                const u8 *ssid, size_t ssid_len,
                struct cfg80211_scan_request *req);
-int wl1271_scan_stop(struct wl1271 *wl);
 int wl1271_scan_build_probe_req(struct wl1271 *wl,
                                const u8 *ssid, size_t ssid_len,
                                const u8 *ie, size_t ie_len, u8 band);
-void wl1271_scan_stm(struct wl1271 *wl, struct ieee80211_vif *vif);
+void wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 void wl1271_scan_complete_work(struct work_struct *work);
 int wl1271_scan_sched_scan_config(struct wl1271 *wl,
                                     struct wl12xx_vif *wlvif,
                                     struct cfg80211_sched_scan_request *req,
                                     struct ieee80211_sched_scan_ies *ies);
 int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif);
-void wl1271_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif);
-void wl1271_scan_sched_scan_results(struct wl1271 *wl);
+void wlcore_scan_sched_scan_results(struct wl1271 *wl);
 
 #define WL1271_SCAN_MAX_CHANNELS       24
 #define WL1271_SCAN_DEFAULT_TAG        1
@@ -66,56 +64,6 @@ enum {
        WL1271_SCAN_STATE_DONE
 };
 
-struct basic_scan_params {
-       /* Scan option flags (WL1271_SCAN_OPT_*) */
-       __le16 scan_options;
-       u8 role_id;
-       /* Number of scan channels in the list (maximum 30) */
-       u8 n_ch;
-       /* This field indicates the number of probe requests to send
-          per channel for an active scan */
-       u8 n_probe_reqs;
-       u8 tid_trigger;
-       u8 ssid_len;
-       u8 use_ssid_list;
-
-       /* Rate bit field for sending the probes */
-       __le32 tx_rate;
-
-       u8 ssid[IEEE80211_MAX_SSID_LEN];
-       /* Band to scan */
-       u8 band;
-
-       u8 scan_tag;
-       u8 padding2[2];
-} __packed;
-
-struct basic_scan_channel_params {
-       /* Duration in TU to wait for frames on a channel for active scan */
-       __le32 min_duration;
-       __le32 max_duration;
-       __le32 bssid_lsb;
-       __le16 bssid_msb;
-       u8 early_termination;
-       u8 tx_power_att;
-       u8 channel;
-       /* FW internal use only! */
-       u8 dfs_candidate;
-       u8 activity_detected;
-       u8 pad;
-} __packed;
-
-struct wl1271_cmd_scan {
-       struct wl1271_cmd_header header;
-
-       struct basic_scan_params params;
-       struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS];
-
-       /* src mac address */
-       u8 addr[ETH_ALEN];
-       u8 padding[2];
-} __packed;
-
 struct wl1271_cmd_trigger_scan_to {
        struct wl1271_cmd_header header;
 
@@ -123,9 +71,17 @@ struct wl1271_cmd_trigger_scan_to {
 } __packed;
 
 #define MAX_CHANNELS_2GHZ      14
-#define MAX_CHANNELS_5GHZ      23
 #define MAX_CHANNELS_4GHZ      4
 
+/*
+ * This max value here is used only for the struct definition of
+ * wlcore_scan_channels. This struct is used by both 12xx
+ * and 18xx (which have different max 5ghz channels value).
+ * In order to make sure this is large enough, just use the
+ * max possible 5ghz channels.
+ */
+#define MAX_CHANNELS_5GHZ      42
+
 #define SCAN_MAX_CYCLE_INTERVALS 16
 #define SCAN_MAX_BANDS 3
 
@@ -160,43 +116,6 @@ struct conn_scan_ch_params {
        u8  padding[3];
 } __packed;
 
-struct wl1271_cmd_sched_scan_config {
-       struct wl1271_cmd_header header;
-
-       __le32 intervals[SCAN_MAX_CYCLE_INTERVALS];
-
-       s8 rssi_threshold; /* for filtering (in dBm) */
-       s8 snr_threshold;  /* for filtering (in dB) */
-
-       u8 cycles;       /* maximum number of scan cycles */
-       u8 report_after; /* report when this number of results are received */
-       u8 terminate;    /* stop scanning after reporting */
-
-       u8 tag;
-       u8 bss_type; /* for filtering */
-       u8 filter_type;
-
-       u8 ssid_len;     /* For SCAN_SSID_FILTER_SPECIFIC */
-       u8 ssid[IEEE80211_MAX_SSID_LEN];
-
-       u8 n_probe_reqs; /* Number of probes requests per channel */
-
-       u8 passive[SCAN_MAX_BANDS];
-       u8 active[SCAN_MAX_BANDS];
-
-       u8 dfs;
-
-       u8 n_pactive_ch; /* number of pactive (passive until fw detects energy)
-                           channels in BG band */
-       u8 role_id;
-       u8 padding[1];
-
-       struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
-       struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ];
-       struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
-} __packed;
-
-
 #define SCHED_SCAN_MAX_SSIDS 16
 
 enum {
@@ -220,21 +139,34 @@ struct wl1271_cmd_sched_scan_ssid_list {
        u8 padding[2];
 } __packed;
 
-struct wl1271_cmd_sched_scan_start {
-       struct wl1271_cmd_header header;
+struct wlcore_scan_channels {
+       u8 passive[SCAN_MAX_BANDS]; /* number of passive scan channels */
+       u8 active[SCAN_MAX_BANDS];  /* number of active scan channels */
+       u8 dfs;            /* number of dfs channels in 5ghz */
+       u8 passive_active; /* number of passive before active channels 2.4ghz */
 
-       u8 tag;
-       u8 role_id;
-       u8 padding[2];
-} __packed;
-
-struct wl1271_cmd_sched_scan_stop {
-       struct wl1271_cmd_header header;
+       struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
+       struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ];
+       struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ];
+};
 
-       u8 tag;
-       u8 role_id;
-       u8 padding[2];
-} __packed;
+enum {
+       SCAN_TYPE_SEARCH        = 0,
+       SCAN_TYPE_PERIODIC      = 1,
+       SCAN_TYPE_TRACKING      = 2,
+};
 
+bool
+wlcore_set_scan_chan_params(struct wl1271 *wl,
+                           struct wlcore_scan_channels *cfg,
+                           struct ieee80211_channel *channels[],
+                           u32 n_channels,
+                           u32 n_ssids,
+                           int scan_type);
+
+int
+wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl,
+                                struct wl12xx_vif *wlvif,
+                                struct cfg80211_sched_scan_request *req);
 
 #endif /* __WL1271_SCAN_H__ */
index 646f703ae739b3b84ebff7316aea31faf8fadfc2..d4f184e2efedf576168a09012650747977a812c5 100644 (file)
@@ -326,8 +326,7 @@ static void wl1271_remove(struct sdio_func *func)
        /* Undo decrement done above in wl1271_probe */
        pm_runtime_get_noresume(&func->dev);
 
-       platform_device_del(glue->core);
-       platform_device_put(glue->core);
+       platform_device_unregister(glue->core);
        kfree(glue);
 }
 
index f06f4770ce029076b8e622fed5285b0167ef7aaa..2d700b7ae14cab8e8cecbc729463ec8efb997d47 100644 (file)
@@ -270,7 +270,7 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr,
                                             void *buf, size_t len, bool fixed)
 {
        struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent);
-       struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS];
+       struct spi_transfer t[2 * (WSPI_MAX_NUM_OF_CHUNKS + 1)];
        struct spi_message m;
        u32 commands[WSPI_MAX_NUM_OF_CHUNKS];
        u32 *cmd;
@@ -407,8 +407,7 @@ static int wl1271_remove(struct spi_device *spi)
 {
        struct wl12xx_spi_glue *glue = spi_get_drvdata(spi);
 
-       platform_device_del(glue->core);
-       platform_device_put(glue->core);
+       platform_device_unregister(glue->core);
        kfree(glue);
 
        return 0;
index a90d3cd094089c82fe60db12dca55bedaa33af0f..ece392c54d9c858bd255ab78eb9e79d054337d52 100644 (file)
@@ -104,7 +104,7 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl,
                                    struct wl12xx_vif *wlvif,
                                    u8 hlid)
 {
-       bool fw_ps, single_sta;
+       bool fw_ps, single_link;
        u8 tx_pkts;
 
        if (WARN_ON(!test_bit(hlid, wlvif->links_map)))
@@ -112,15 +112,15 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl,
 
        fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
        tx_pkts = wl->links[hlid].allocated_pkts;
-       single_sta = (wl->active_sta_count == 1);
+       single_link = (wl->active_link_count == 1);
 
        /*
         * if in FW PS and there is enough data in FW we can put the link
         * into high-level PS and clean out its TX queues.
-        * Make an exception if this is the only connected station. In this
-        * case FW-memory congestion is not a problem.
+        * Make an exception if this is the only connected link. In this
+        * case FW-memory congestion is less of a problem.
         */
-       if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
+       if (!single_link && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
                wl12xx_ps_link_start(wl, wlvif, hlid, true);
 }
 
@@ -155,21 +155,18 @@ static u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                      struct sk_buff *skb, struct ieee80211_sta *sta)
 {
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-
-       if (!wlvif || wl12xx_is_dummy_packet(wl, skb))
-               return wl->system_hlid;
+       struct ieee80211_tx_info *control;
 
        if (wlvif->bss_type == BSS_TYPE_AP_BSS)
                return wl12xx_tx_get_hlid_ap(wl, wlvif, skb, sta);
 
-       if ((test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
-            test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) &&
-           !ieee80211_is_auth(hdr->frame_control) &&
-           !ieee80211_is_assoc_req(hdr->frame_control))
-               return wlvif->sta.hlid;
-       else
+       control = IEEE80211_SKB_CB(skb);
+       if (control->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+               wl1271_debug(DEBUG_TX, "tx offchannel");
                return wlvif->dev_hlid;
+       }
+
+       return wlvif->sta.hlid;
 }
 
 unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl,
@@ -224,9 +221,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
                wl->tx_allocated_pkts[ac]++;
 
-               if (!wl12xx_is_dummy_packet(wl, skb) && wlvif &&
-                   wlvif->bss_type == BSS_TYPE_AP_BSS &&
-                   test_bit(hlid, wlvif->ap.sta_hlid_map))
+               if (test_bit(hlid, wl->links_map))
                        wl->links[hlid].allocated_pkts++;
 
                ret = 0;
@@ -293,9 +288,14 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
                tx_attr |= TX_HW_ATTR_TX_DUMMY_REQ;
        } else if (wlvif) {
+               u8 session_id = wl->session_ids[hlid];
+
+               if ((wl->quirks & WLCORE_QUIRK_AP_ZERO_SESSION_ID) &&
+                   (wlvif->bss_type == BSS_TYPE_AP_BSS))
+                       session_id = 0;
+
                /* configure the tx attributes */
-               tx_attr = wlvif->session_counter <<
-                         TX_HW_ATTR_OFST_SESSION_COUNTER;
+               tx_attr = session_id << TX_HW_ATTR_OFST_SESSION_COUNTER;
        }
 
        desc->hlid = hlid;
@@ -452,20 +452,22 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,
 void wl1271_handle_tx_low_watermark(struct wl1271 *wl)
 {
        int i;
+       struct wl12xx_vif *wlvif;
 
-       for (i = 0; i < NUM_TX_QUEUES; i++) {
-               if (wlcore_is_queue_stopped_by_reason(wl, i,
-                       WLCORE_QUEUE_STOP_REASON_WATERMARK) &&
-                   wl->tx_queue_count[i] <= WL1271_TX_QUEUE_LOW_WATERMARK) {
-                       /* firmware buffer has space, restart queues */
-                       wlcore_wake_queue(wl, i,
-                                         WLCORE_QUEUE_STOP_REASON_WATERMARK);
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               for (i = 0; i < NUM_TX_QUEUES; i++) {
+                       if (wlcore_is_queue_stopped_by_reason(wl, wlvif, i,
+                                       WLCORE_QUEUE_STOP_REASON_WATERMARK) &&
+                           wlvif->tx_queue_count[i] <=
+                                       WL1271_TX_QUEUE_LOW_WATERMARK)
+                               /* firmware buffer has space, restart queues */
+                               wlcore_wake_queue(wl, wlvif, i,
+                                       WLCORE_QUEUE_STOP_REASON_WATERMARK);
                }
        }
 }
 
-static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl,
-                                               struct sk_buff_head *queues)
+static int wlcore_select_ac(struct wl1271 *wl)
 {
        int i, q = -1, ac;
        u32 min_pkts = 0xffffffff;
@@ -479,45 +481,60 @@ static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl,
         */
        for (i = 0; i < NUM_TX_QUEUES; i++) {
                ac = wl1271_tx_get_queue(i);
-               if (!skb_queue_empty(&queues[ac]) &&
-                   (wl->tx_allocated_pkts[ac] < min_pkts)) {
+               if (wl->tx_queue_count[ac] &&
+                   wl->tx_allocated_pkts[ac] < min_pkts) {
                        q = ac;
                        min_pkts = wl->tx_allocated_pkts[q];
                }
        }
 
-       if (q == -1)
-               return NULL;
-
-       return &queues[q];
+       return q;
 }
 
-static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl,
-                                             struct wl1271_link *lnk)
+static struct sk_buff *wlcore_lnk_dequeue(struct wl1271 *wl,
+                                         struct wl1271_link *lnk, u8 q)
 {
        struct sk_buff *skb;
        unsigned long flags;
-       struct sk_buff_head *queue;
 
-       queue = wl1271_select_queue(wl, lnk->tx_queue);
-       if (!queue)
-               return NULL;
-
-       skb = skb_dequeue(queue);
+       skb = skb_dequeue(&lnk->tx_queue[q]);
        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]--;
+               if (lnk->wlvif) {
+                       WARN_ON_ONCE(lnk->wlvif->tx_queue_count[q] <= 0);
+                       lnk->wlvif->tx_queue_count[q]--;
+               }
                spin_unlock_irqrestore(&wl->wl_lock, flags);
        }
 
        return skb;
 }
 
-static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
-                                             struct wl12xx_vif *wlvif,
-                                             u8 *hlid)
+static struct sk_buff *wlcore_lnk_dequeue_high_prio(struct wl1271 *wl,
+                                                   u8 hlid, u8 ac,
+                                                   u8 *low_prio_hlid)
+{
+       struct wl1271_link *lnk = &wl->links[hlid];
+
+       if (!wlcore_hw_lnk_high_prio(wl, hlid, lnk)) {
+               if (*low_prio_hlid == WL12XX_INVALID_LINK_ID &&
+                   !skb_queue_empty(&lnk->tx_queue[ac]) &&
+                   wlcore_hw_lnk_low_prio(wl, hlid, lnk))
+                       /* we found the first non-empty low priority queue */
+                       *low_prio_hlid = hlid;
+
+               return NULL;
+       }
+
+       return wlcore_lnk_dequeue(wl, lnk, ac);
+}
+
+static struct sk_buff *wlcore_vif_dequeue_high_prio(struct wl1271 *wl,
+                                                   struct wl12xx_vif *wlvif,
+                                                   u8 ac, u8 *hlid,
+                                                   u8 *low_prio_hlid)
 {
        struct sk_buff *skb = NULL;
        int i, h, start_hlid;
@@ -533,7 +550,8 @@ static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
                if (!test_bit(h, wlvif->links_map))
                        continue;
 
-               skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[h]);
+               skb = wlcore_lnk_dequeue_high_prio(wl, h, ac,
+                                                  low_prio_hlid);
                if (!skb)
                        continue;
 
@@ -553,42 +571,74 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid)
        unsigned long flags;
        struct wl12xx_vif *wlvif = wl->last_wlvif;
        struct sk_buff *skb = NULL;
+       int ac;
+       u8 low_prio_hlid = WL12XX_INVALID_LINK_ID;
+
+       ac = wlcore_select_ac(wl);
+       if (ac < 0)
+               goto out;
 
        /* continue from last wlvif (round robin) */
        if (wlvif) {
                wl12xx_for_each_wlvif_continue(wl, wlvif) {
-                       skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
-                       if (skb) {
-                               wl->last_wlvif = wlvif;
-                               break;
-                       }
+                       if (!wlvif->tx_queue_count[ac])
+                               continue;
+
+                       skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid,
+                                                          &low_prio_hlid);
+                       if (!skb)
+                               continue;
+
+                       wl->last_wlvif = wlvif;
+                       break;
                }
        }
 
        /* dequeue from the system HLID before the restarting wlvif list */
        if (!skb) {
-               skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]);
-               *hlid = wl->system_hlid;
+               skb = wlcore_lnk_dequeue_high_prio(wl, wl->system_hlid,
+                                                  ac, &low_prio_hlid);
+               if (skb) {
+                       *hlid = wl->system_hlid;
+                       wl->last_wlvif = NULL;
+               }
        }
 
-       /* do a new pass over the wlvif list */
+       /* Do a new pass over the wlvif list. But no need to continue
+        * after last_wlvif. The previous pass should have found it. */
        if (!skb) {
                wl12xx_for_each_wlvif(wl, wlvif) {
-                       skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
+                       if (!wlvif->tx_queue_count[ac])
+                               goto next;
+
+                       skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid,
+                                                          &low_prio_hlid);
                        if (skb) {
                                wl->last_wlvif = wlvif;
                                break;
                        }
 
-                       /*
-                        * No need to continue after last_wlvif. The previous
-                        * pass should have found it.
-                        */
+next:
                        if (wlvif == wl->last_wlvif)
                                break;
                }
        }
 
+       /* no high priority skbs found - but maybe a low priority one? */
+       if (!skb && low_prio_hlid != WL12XX_INVALID_LINK_ID) {
+               struct wl1271_link *lnk = &wl->links[low_prio_hlid];
+               skb = wlcore_lnk_dequeue(wl, lnk, ac);
+
+               WARN_ON(!skb); /* we checked this before */
+               *hlid = low_prio_hlid;
+
+               /* ensure proper round robin in the vif/link levels */
+               wl->last_wlvif = lnk->wlvif;
+               if (lnk->wlvif)
+                       lnk->wlvif->last_tx_hlid = low_prio_hlid;
+
+       }
+
        if (!skb &&
            test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) {
                int q;
@@ -602,6 +652,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid)
                spin_unlock_irqrestore(&wl->wl_lock, flags);
        }
 
+out:
        return skb;
 }
 
@@ -623,6 +674,8 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 
        spin_lock_irqsave(&wl->wl_lock, flags);
        wl->tx_queue_count[q]++;
+       if (wlvif)
+               wlvif->tx_queue_count[q]++;
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
 
@@ -699,7 +752,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
                bool has_data = false;
 
                wlvif = NULL;
-               if (!wl12xx_is_dummy_packet(wl, skb) && info->control.vif)
+               if (!wl12xx_is_dummy_packet(wl, skb))
                        wlvif = wl12xx_vif_to_data(info->control.vif);
                else
                        hlid = wl->system_hlid;
@@ -972,10 +1025,11 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
        unsigned long flags;
        struct ieee80211_tx_info *info;
        int total[NUM_TX_QUEUES];
+       struct wl1271_link *lnk = &wl->links[hlid];
 
        for (i = 0; i < NUM_TX_QUEUES; i++) {
                total[i] = 0;
-               while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
+               while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
                        wl1271_debug(DEBUG_TX, "link freeing skb 0x%p", skb);
 
                        if (!wl12xx_is_dummy_packet(wl, skb)) {
@@ -990,8 +1044,11 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
        }
 
        spin_lock_irqsave(&wl->wl_lock, flags);
-       for (i = 0; i < NUM_TX_QUEUES; i++)
+       for (i = 0; i < NUM_TX_QUEUES; i++) {
                wl->tx_queue_count[i] -= total[i];
+               if (lnk->wlvif)
+                       lnk->wlvif->tx_queue_count[i] -= total[i];
+       }
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 
        wl1271_handle_tx_low_watermark(wl);
@@ -1004,16 +1061,18 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 
        /* TX failure */
        for_each_set_bit(i, wlvif->links_map, WL12XX_MAX_LINKS) {
-               if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+               if (wlvif->bss_type == BSS_TYPE_AP_BSS) {
+                       /* this calls wl12xx_free_link */
                        wl1271_free_sta(wl, wlvif, i);
-               else
-                       wlvif->sta.ba_rx_bitmap = 0;
-
-               wl->links[i].allocated_pkts = 0;
-               wl->links[i].prev_freed_pkts = 0;
+               } else {
+                       u8 hlid = i;
+                       wl12xx_free_link(wl, wlvif, &hlid);
+               }
        }
        wlvif->last_tx_hlid = 0;
 
+       for (i = 0; i < NUM_TX_QUEUES; i++)
+               wlvif->tx_queue_count[i] = 0;
 }
 /* caller must hold wl->mutex and TX must be stopped */
 void wl12xx_tx_reset(struct wl1271 *wl)
@@ -1023,7 +1082,7 @@ void wl12xx_tx_reset(struct wl1271 *wl)
        struct ieee80211_tx_info *info;
 
        /* only reset the queues if something bad happened */
-       if (WARN_ON_ONCE(wl1271_tx_total_queue_count(wl) != 0)) {
+       if (wl1271_tx_total_queue_count(wl) != 0) {
                for (i = 0; i < WL12XX_MAX_LINKS; i++)
                        wl1271_tx_reset_link_queues(wl, i);
 
@@ -1135,45 +1194,48 @@ u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set)
 
        return BIT(__ffs(rate_set));
 }
+EXPORT_SYMBOL_GPL(wl1271_tx_min_rate_get);
 
-void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue,
-                             enum wlcore_queue_stop_reason reason)
+void wlcore_stop_queue_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             u8 queue, enum wlcore_queue_stop_reason reason)
 {
-       bool stopped = !!wl->queue_stop_reasons[queue];
+       int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
+       bool stopped = !!wl->queue_stop_reasons[hwq];
 
        /* queue should not be stopped for this reason */
-       WARN_ON(test_and_set_bit(reason, &wl->queue_stop_reasons[queue]));
+       WARN_ON_ONCE(test_and_set_bit(reason, &wl->queue_stop_reasons[hwq]));
 
        if (stopped)
                return;
 
-       ieee80211_stop_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue));
+       ieee80211_stop_queue(wl->hw, hwq);
 }
 
-void wlcore_stop_queue(struct wl1271 *wl, u8 queue,
+void wlcore_stop_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
                       enum wlcore_queue_stop_reason reason)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&wl->wl_lock, flags);
-       wlcore_stop_queue_locked(wl, queue, reason);
+       wlcore_stop_queue_locked(wl, wlvif, queue, reason);
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
 
-void wlcore_wake_queue(struct wl1271 *wl, u8 queue,
+void wlcore_wake_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
                       enum wlcore_queue_stop_reason reason)
 {
        unsigned long flags;
+       int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
 
        spin_lock_irqsave(&wl->wl_lock, flags);
 
        /* queue should not be clear for this reason */
-       WARN_ON(!test_and_clear_bit(reason, &wl->queue_stop_reasons[queue]));
+       WARN_ON_ONCE(!test_and_clear_bit(reason, &wl->queue_stop_reasons[hwq]));
 
-       if (wl->queue_stop_reasons[queue])
+       if (wl->queue_stop_reasons[hwq])
                goto out;
 
-       ieee80211_wake_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue));
+       ieee80211_wake_queue(wl->hw, hwq);
 
 out:
        spin_unlock_irqrestore(&wl->wl_lock, flags);
@@ -1183,48 +1245,74 @@ void wlcore_stop_queues(struct wl1271 *wl,
                        enum wlcore_queue_stop_reason reason)
 {
        int i;
+       unsigned long flags;
 
-       for (i = 0; i < NUM_TX_QUEUES; i++)
-               wlcore_stop_queue(wl, i, reason);
+       spin_lock_irqsave(&wl->wl_lock, flags);
+
+       /* mark all possible queues as stopped */
+        for (i = 0; i < WLCORE_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++)
+                WARN_ON_ONCE(test_and_set_bit(reason,
+                                             &wl->queue_stop_reasons[i]));
+
+       /* use the global version to make sure all vifs in mac80211 we don't
+        * know are stopped.
+        */
+       ieee80211_stop_queues(wl->hw);
+
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
-EXPORT_SYMBOL_GPL(wlcore_stop_queues);
 
 void wlcore_wake_queues(struct wl1271 *wl,
                        enum wlcore_queue_stop_reason reason)
 {
        int i;
+       unsigned long flags;
 
-       for (i = 0; i < NUM_TX_QUEUES; i++)
-               wlcore_wake_queue(wl, i, reason);
+       spin_lock_irqsave(&wl->wl_lock, flags);
+
+       /* mark all possible queues as awake */
+        for (i = 0; i < WLCORE_NUM_MAC_ADDRESSES * NUM_TX_QUEUES; i++)
+               WARN_ON_ONCE(!test_and_clear_bit(reason,
+                                                &wl->queue_stop_reasons[i]));
+
+       /* use the global version to make sure all vifs in mac80211 we don't
+        * know are woken up.
+        */
+       ieee80211_wake_queues(wl->hw);
+
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
-EXPORT_SYMBOL_GPL(wlcore_wake_queues);
 
-void wlcore_reset_stopped_queues(struct wl1271 *wl)
+bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl,
+                                      struct wl12xx_vif *wlvif, u8 queue,
+                                      enum wlcore_queue_stop_reason reason)
 {
-       int i;
        unsigned long flags;
+       bool stopped;
 
        spin_lock_irqsave(&wl->wl_lock, flags);
-
-       for (i = 0; i < NUM_TX_QUEUES; i++) {
-               if (!wl->queue_stop_reasons[i])
-                       continue;
-
-               wl->queue_stop_reasons[i] = 0;
-               ieee80211_wake_queue(wl->hw,
-                                    wl1271_tx_get_mac80211_queue(i));
-       }
-
+       stopped = wlcore_is_queue_stopped_by_reason_locked(wl, wlvif, queue,
+                                                          reason);
        spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+       return stopped;
 }
 
-bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue,
-                            enum wlcore_queue_stop_reason reason)
+bool wlcore_is_queue_stopped_by_reason_locked(struct wl1271 *wl,
+                                      struct wl12xx_vif *wlvif, u8 queue,
+                                      enum wlcore_queue_stop_reason reason)
 {
-       return test_bit(reason, &wl->queue_stop_reasons[queue]);
+       int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
+
+       WARN_ON_ONCE(!spin_is_locked(&wl->wl_lock));
+       return test_bit(reason, &wl->queue_stop_reasons[hwq]);
 }
 
-bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue)
+bool wlcore_is_queue_stopped_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                   u8 queue)
 {
-       return !!wl->queue_stop_reasons[queue];
+       int hwq = wlcore_tx_get_mac80211_queue(wlvif, queue);
+
+       WARN_ON_ONCE(!spin_is_locked(&wl->wl_lock));
+       return !!wl->queue_stop_reasons[hwq];
 }
index 349520d8b7240686b2e7ffa266c4f9019c5ce481..55aa4acf9105a4e703036b0b12250ae19a9a7f81 100644 (file)
@@ -207,19 +207,22 @@ static inline int wl1271_tx_get_queue(int queue)
        }
 }
 
-static inline int wl1271_tx_get_mac80211_queue(int queue)
+static inline
+int wlcore_tx_get_mac80211_queue(struct wl12xx_vif *wlvif, int queue)
 {
+       int mac_queue = wlvif->hw_queue_base;
+
        switch (queue) {
        case CONF_TX_AC_VO:
-               return 0;
+               return mac_queue + 0;
        case CONF_TX_AC_VI:
-               return 1;
+               return mac_queue + 1;
        case CONF_TX_AC_BE:
-               return 2;
+               return mac_queue + 2;
        case CONF_TX_AC_BK:
-               return 3;
+               return mac_queue + 3;
        default:
-               return 2;
+               return mac_queue + 2;
        }
 }
 
@@ -252,20 +255,26 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids);
 unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl,
                                          unsigned int packet_length);
 void wl1271_free_tx_id(struct wl1271 *wl, int id);
-void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue,
-                             enum wlcore_queue_stop_reason reason);
-void wlcore_stop_queue(struct wl1271 *wl, u8 queue,
+void wlcore_stop_queue_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             u8 queue, enum wlcore_queue_stop_reason reason);
+void wlcore_stop_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
                       enum wlcore_queue_stop_reason reason);
-void wlcore_wake_queue(struct wl1271 *wl, u8 queue,
+void wlcore_wake_queue(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 queue,
                       enum wlcore_queue_stop_reason reason);
 void wlcore_stop_queues(struct wl1271 *wl,
                        enum wlcore_queue_stop_reason reason);
 void wlcore_wake_queues(struct wl1271 *wl,
                        enum wlcore_queue_stop_reason reason);
-void wlcore_reset_stopped_queues(struct wl1271 *wl);
-bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue,
+bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl,
+                                      struct wl12xx_vif *wlvif, u8 queue,
                                       enum wlcore_queue_stop_reason reason);
-bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue);
+bool
+wlcore_is_queue_stopped_by_reason_locked(struct wl1271 *wl,
+                                        struct wl12xx_vif *wlvif,
+                                        u8 queue,
+                                        enum wlcore_queue_stop_reason reason);
+bool wlcore_is_queue_stopped_locked(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                   u8 queue);
 
 /* from main.c */
 void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);
index c3884937c007cf049b6efe0c5d158008972a7bf9..ebd8c6fad7cdda4b474bd0e49abb62dc05cf9666 100644 (file)
@@ -37,6 +37,9 @@
  */
 #define WLCORE_NUM_MAC_ADDRESSES 3
 
+/* wl12xx/wl18xx maximum transmission power (in dBm) */
+#define WLCORE_MAX_TXPWR        25
+
 /* forward declaration */
 struct wl1271_tx_hw_descr;
 enum wl_rx_buf_align;
@@ -51,6 +54,9 @@ struct wlcore_ops {
        int (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr,
                           void *buf, size_t len);
        int (*ack_event)(struct wl1271 *wl);
+       int (*wait_for_event)(struct wl1271 *wl, enum wlcore_wait_event event,
+                             bool *timeout);
+       int (*process_mailbox_events)(struct wl1271 *wl);
        u32 (*calc_tx_blocks)(struct wl1271 *wl, u32 len, u32 spare_blks);
        void (*set_tx_desc_blocks)(struct wl1271 *wl,
                                   struct wl1271_tx_hw_descr *desc,
@@ -82,12 +88,32 @@ struct wlcore_ops {
        int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir);
        int (*handle_static_data)(struct wl1271 *wl,
                                  struct wl1271_static_data *static_data);
+       int (*scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                         struct cfg80211_scan_request *req);
+       int (*scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+       int (*sched_scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                               struct cfg80211_sched_scan_request *req,
+                               struct ieee80211_sched_scan_ies *ies);
+       void (*sched_scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
        int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem);
        int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd,
                       struct ieee80211_vif *vif,
                       struct ieee80211_sta *sta,
                       struct ieee80211_key_conf *key_conf);
+       int (*channel_switch)(struct wl1271 *wl,
+                             struct wl12xx_vif *wlvif,
+                             struct ieee80211_channel_switch *ch_switch);
        u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len);
+       void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             struct ieee80211_sta *sta, u32 changed);
+       int (*set_peer_cap)(struct wl1271 *wl,
+                           struct ieee80211_sta_ht_cap *ht_cap,
+                           bool allow_ht_operation,
+                           u32 rate_set, u8 hlid);
+       bool (*lnk_high_prio)(struct wl1271 *wl, u8 hlid,
+                             struct wl1271_link *lnk);
+       bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid,
+                            struct wl1271_link *lnk);
 };
 
 enum wlcore_partitions {
@@ -202,6 +228,8 @@ struct wl1271 {
        unsigned long klv_templates_map[
                        BITS_TO_LONGS(WLCORE_MAX_KLV_TEMPLATES)];
 
+       u8 session_ids[WL12XX_MAX_LINKS];
+
        struct list_head wlvif_list;
 
        u8 sta_count;
@@ -227,7 +255,8 @@ struct wl1271 {
 
        /* Frames scheduled for transmission, not handled yet */
        int tx_queue_count[NUM_TX_QUEUES];
-       unsigned long queue_stop_reasons[NUM_TX_QUEUES];
+       unsigned long queue_stop_reasons[
+                               NUM_TX_QUEUES * WLCORE_NUM_MAC_ADDRESSES];
 
        /* Frames received, not handled yet by mac80211 */
        struct sk_buff_head deferred_rx_queue;
@@ -269,24 +298,30 @@ struct wl1271 {
        struct work_struct recovery_work;
        bool watchdog_recovery;
 
+       /* Reg domain last configuration */
+       u32 reg_ch_conf_last[2];
+       /* Reg domain pending configuration */
+       u32 reg_ch_conf_pending[2];
+
        /* Pointer that holds DMA-friendly block for the mailbox */
-       struct event_mailbox *mbox;
+       void *mbox;
 
        /* The mbox event mask */
        u32 event_mask;
 
        /* Mailbox pointers */
+       u32 mbox_size;
        u32 mbox_ptr[2];
 
        /* Are we currently scanning */
-       struct ieee80211_vif *scan_vif;
+       struct wl12xx_vif *scan_wlvif;
        struct wl1271_scan scan;
        struct delayed_work scan_complete_work;
 
-       /* Connection loss work */
-       struct delayed_work connection_loss_work;
+       struct ieee80211_vif *roc_vif;
+       struct delayed_work roc_complete_work;
 
-       bool sched_scanning;
+       struct wl12xx_vif *sched_vif;
 
        /* The current band */
        enum ieee80211_band band;
@@ -299,7 +334,7 @@ struct wl1271 {
 
        struct wl1271_stats stats;
 
-       __le32 buffer_32;
+       __le32 *buffer_32;
        u32 buffer_cmd;
        u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
 
@@ -314,6 +349,8 @@ struct wl1271 {
 
        bool enable_11a;
 
+       int recovery_count;
+
        /* Most recently reported noise in dBm */
        s8 noise;
 
@@ -333,6 +370,12 @@ struct wl1271 {
         */
        struct wl1271_link links[WL12XX_MAX_LINKS];
 
+       /* number of currently active links */
+       int active_link_count;
+
+       /* Fast/slow links bitmap according to FW */
+       u32 fw_fast_lnk_map;
+
        /* AP-mode - a bitmap of links currently in PS mode according to FW */
        u32 ap_fw_ps_map;
 
@@ -367,6 +410,12 @@ struct wl1271 {
        const char *sr_fw_name;
        const char *mr_fw_name;
 
+       u8 scan_templ_id_2_4;
+       u8 scan_templ_id_5;
+       u8 sched_scan_templ_id_2_4;
+       u8 sched_scan_templ_id_5;
+       u8 max_channels_5;
+
        /* per-chip-family private structure */
        void *priv;
 
@@ -408,20 +457,28 @@ struct wl1271 {
        /* the number of allocated MAC addresses in this chip */
        int num_mac_addr;
 
-       /* the minimum FW version required for the driver to work */
-       unsigned int min_fw_ver[NUM_FW_VER];
+       /* minimum FW version required for the driver to work in single-role */
+       unsigned int min_sr_fw_ver[NUM_FW_VER];
+
+       /* minimum FW version required for the driver to work in multi-role */
+       unsigned int min_mr_fw_ver[NUM_FW_VER];
 
        struct completion nvs_loading_complete;
+
+       /* number of concurrent channels the HW supports */
+       u32 num_channels;
 };
 
 int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
 int wlcore_remove(struct platform_device *pdev);
-struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size);
+struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
+                                    u32 mbox_size);
 int wlcore_free_hw(struct wl1271 *wl);
 int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
                   struct ieee80211_vif *vif,
                   struct ieee80211_sta *sta,
                   struct ieee80211_key_conf *key_conf);
+void wlcore_regdomain_config(struct wl1271 *wl);
 
 static inline void
 wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band,
@@ -430,16 +487,27 @@ wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band,
        memcpy(&wl->ht_cap[band], ht_cap, sizeof(*ht_cap));
 }
 
+/* Tell wlcore not to care about this element when checking the version */
+#define WLCORE_FW_VER_IGNORE   -1
+
 static inline void
 wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
-                     unsigned int iftype, unsigned int major,
-                     unsigned int subtype, unsigned int minor)
+                     unsigned int iftype_sr, unsigned int major_sr,
+                     unsigned int subtype_sr, unsigned int minor_sr,
+                     unsigned int iftype_mr, unsigned int major_mr,
+                     unsigned int subtype_mr, unsigned int minor_mr)
 {
-       wl->min_fw_ver[FW_VER_CHIP] = chip;
-       wl->min_fw_ver[FW_VER_IF_TYPE] = iftype;
-       wl->min_fw_ver[FW_VER_MAJOR] = major;
-       wl->min_fw_ver[FW_VER_SUBTYPE] = subtype;
-       wl->min_fw_ver[FW_VER_MINOR] = minor;
+       wl->min_sr_fw_ver[FW_VER_CHIP] = chip;
+       wl->min_sr_fw_ver[FW_VER_IF_TYPE] = iftype_sr;
+       wl->min_sr_fw_ver[FW_VER_MAJOR] = major_sr;
+       wl->min_sr_fw_ver[FW_VER_SUBTYPE] = subtype_sr;
+       wl->min_sr_fw_ver[FW_VER_MINOR] = minor_sr;
+
+       wl->min_mr_fw_ver[FW_VER_CHIP] = chip;
+       wl->min_mr_fw_ver[FW_VER_IF_TYPE] = iftype_mr;
+       wl->min_mr_fw_ver[FW_VER_MAJOR] = major_mr;
+       wl->min_mr_fw_ver[FW_VER_SUBTYPE] = subtype_mr;
+       wl->min_mr_fw_ver[FW_VER_MINOR] = minor_mr;
 }
 
 /* Firmware image load chunk size */
@@ -450,6 +518,9 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
 /* Each RX/TX transaction requires an end-of-transaction transfer */
 #define WLCORE_QUIRK_END_OF_TRANSACTION                BIT(0)
 
+/* the first start_role(sta) sometimes doesn't work on wl12xx */
+#define WLCORE_QUIRK_START_STA_FAILS           BIT(1)
+
 /* wl127x and SPI don't support SDIO block size alignment */
 #define WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN                BIT(2)
 
@@ -462,9 +533,6 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
 /* Older firmwares use an old NVS format */
 #define WLCORE_QUIRK_LEGACY_NVS                        BIT(5)
 
-/* Some firmwares may not support ELP */
-#define WLCORE_QUIRK_NO_ELP                    BIT(6)
-
 /* pad only the last frame in the aggregate buffer */
 #define WLCORE_QUIRK_TX_PAD_LAST_FRAME         BIT(7)
 
@@ -477,11 +545,11 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
 /* separate probe response templates for one-shot and sched scans */
 #define WLCORE_QUIRK_DUAL_PROBE_TMPL           BIT(10)
 
-/* TODO: move to the lower drivers when all usages are abstracted */
-#define CHIP_ID_1271_PG10              (0x4030101)
-#define CHIP_ID_1271_PG20              (0x4030111)
-#define CHIP_ID_1283_PG10              (0x05030101)
-#define CHIP_ID_1283_PG20              (0x05030111)
+/* Firmware requires reg domain configuration for active calibration */
+#define WLCORE_QUIRK_REGDOMAIN_CONF            BIT(11)
+
+/* The FW only support a zero session id for AP */
+#define WLCORE_QUIRK_AP_ZERO_SESSION_ID                BIT(12)
 
 /* TODO: move all these common registers and values elsewhere */
 #define HW_ACCESS_ELP_CTRL_REG         0x1FFFC
index 6678d4b18611556be7617b6ff79dab0dad851d14..20316ac328a28b3af0c231dd882b97decf725709 100644 (file)
@@ -109,17 +109,6 @@ enum {
        NUM_FW_VER
 };
 
-#define FW_VER_CHIP_WL127X 6
-#define FW_VER_CHIP_WL128X 7
-
-#define FW_VER_IF_TYPE_STA 1
-#define FW_VER_IF_TYPE_AP  2
-
-#define FW_VER_MINOR_1_SPARE_STA_MIN 58
-#define FW_VER_MINOR_1_SPARE_AP_MIN  47
-
-#define FW_VER_MINOR_FWLOG_STA_MIN 70
-
 struct wl1271_chip {
        u32 id;
        char fw_ver_str[ETHTOOL_BUSINFO_LEN];
@@ -141,7 +130,10 @@ struct wl_fw_packet_counters {
        /* Cumulative counter of released Voice memory blocks */
        u8 tx_voice_released_blks;
 
-       u8 padding[3];
+       /* Tx rate of the last transmitted packet */
+       u8 tx_last_rate;
+
+       u8 padding[2];
 } __packed;
 
 /* FW status registers */
@@ -260,6 +252,8 @@ enum wl12xx_vif_flags {
        WLVIF_FLAG_IN_USE,
 };
 
+struct wl12xx_vif;
+
 struct wl1271_link {
        /* AP-mode - TX queue per AC in link */
        struct sk_buff_head tx_queue[NUM_TX_QUEUES];
@@ -272,6 +266,9 @@ struct wl1271_link {
 
        /* bitmap of TIDs where RX BA sessions are active for this link */
        u8 ba_bitmap;
+
+       /* The wlvif this link belongs to. Might be null for global links */
+       struct wl12xx_vif *wlvif;
 };
 
 #define WL1271_MAX_RX_FILTERS 5
@@ -315,6 +312,7 @@ struct wl12xx_rx_filter {
 
 struct wl1271_station {
        u8 hlid;
+       bool in_connection;
 };
 
 struct wl12xx_vif {
@@ -332,7 +330,6 @@ struct wl12xx_vif {
        union {
                struct {
                        u8 hlid;
-                       u8 ba_rx_bitmap;
 
                        u8 basic_rate_idx;
                        u8 ap_rate_idx;
@@ -341,6 +338,8 @@ struct wl12xx_vif {
                        u8 klv_template_id;
 
                        bool qos;
+                       /* channel type we started the STA role with */
+                       enum nl80211_channel_type role_chan_type;
                } sta;
                struct {
                        u8 global_hlid;
@@ -362,6 +361,9 @@ struct wl12xx_vif {
        /* the hlid of the last transmitted skb */
        int last_tx_hlid;
 
+       /* counters of packets per AC, across all links in the vif */
+       int tx_queue_count[NUM_TX_QUEUES];
+
        unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
 
        u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
@@ -396,9 +398,6 @@ struct wl12xx_vif {
        /* Our association ID */
        u16 aid;
 
-       /* Session counter for the chipset */
-       int session_counter;
-
        /* retry counter for PSM entries */
        u8 psm_entry_retry;
 
@@ -416,11 +415,28 @@ struct wl12xx_vif {
        bool ba_support;
        bool ba_allowed;
 
+       bool wmm_enabled;
+
        /* Rx Streaming */
        struct work_struct rx_streaming_enable_work;
        struct work_struct rx_streaming_disable_work;
        struct timer_list rx_streaming_timer;
 
+       struct delayed_work channel_switch_work;
+       struct delayed_work connection_loss_work;
+
+       /* number of in connection stations */
+       int inconn_count;
+
+       /*
+        * This vif's queues are mapped to mac80211 HW queues as:
+        * VO - hw_queue_base
+        * VI - hw_queue_base + 1
+        * BE - hw_queue_base + 2
+        * BK - hw_queue_base + 3
+        */
+       int hw_queue_base;
+
        /*
         * This struct must be last!
         * data that has to be saved acrossed reconfigs (e.g. recovery)
@@ -443,6 +459,7 @@ struct wl12xx_vif {
 
 static inline struct wl12xx_vif *wl12xx_vif_to_data(struct ieee80211_vif *vif)
 {
+       WARN_ON(!vif);
        return (struct wl12xx_vif *)vif->drv_priv;
 }