wl12xx: fix race condition during recovery in AP mode
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / wl12xx / main.c
index 85cb4daac9a0ec877e00a00b6044e974e1a45a46..6dd42c9876634221d0bf4921dd1664a0f8ac6e96 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/vmalloc.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/wl12xx.h>
 
 #include "wl12xx.h"
 #include "wl12xx_80211.h"
@@ -50,7 +51,7 @@
 
 static struct conf_drv_settings default_conf = {
        .sg = {
-               .params = {
+               .sta_params = {
                        [CONF_SG_BT_PER_THRESHOLD]                  = 7500,
                        [CONF_SG_HV3_MAX_OVERRIDE]                  = 0,
                        [CONF_SG_BT_NFS_SAMPLE_INTERVAL]            = 400,
@@ -100,6 +101,61 @@ static struct conf_drv_settings default_conf = {
                        [CONF_SG_DHCP_TIME]                         = 5000,
                        [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP]  = 100,
                },
+               .ap_params = {
+                       [CONF_SG_BT_PER_THRESHOLD]                  = 7500,
+                       [CONF_SG_HV3_MAX_OVERRIDE]                  = 0,
+                       [CONF_SG_BT_NFS_SAMPLE_INTERVAL]            = 400,
+                       [CONF_SG_BT_LOAD_RATIO]                     = 50,
+                       [CONF_SG_AUTO_PS_MODE]                      = 1,
+                       [CONF_SG_AUTO_SCAN_PROBE_REQ]               = 170,
+                       [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3]   = 50,
+                       [CONF_SG_ANTENNA_CONFIGURATION]             = 0,
+                       [CONF_SG_BEACON_MISS_PERCENT]               = 60,
+                       [CONF_SG_RATE_ADAPT_THRESH]                 = 64,
+                       [CONF_SG_RATE_ADAPT_SNR]                    = 1,
+                       [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR]      = 10,
+                       [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR]      = 25,
+                       [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR]      = 25,
+                       [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR]       = 20,
+                       [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR]       = 25,
+                       [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR]       = 25,
+                       [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR]     = 7,
+                       [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR]     = 25,
+                       [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR]     = 25,
+                       [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR]      = 8,
+                       [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR]      = 25,
+                       [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR]      = 25,
+                       [CONF_SG_RXT]                               = 1200,
+                       [CONF_SG_TXT]                               = 1000,
+                       [CONF_SG_ADAPTIVE_RXT_TXT]                  = 1,
+                       [CONF_SG_PS_POLL_TIMEOUT]                   = 10,
+                       [CONF_SG_UPSD_TIMEOUT]                      = 10,
+                       [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR] = 7,
+                       [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR] = 15,
+                       [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR] = 15,
+                       [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR]  = 8,
+                       [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR]  = 20,
+                       [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR]  = 15,
+                       [CONF_SG_WLAN_ACTIVE_BT_ACL_MIN_BR]         = 20,
+                       [CONF_SG_WLAN_ACTIVE_BT_ACL_MAX_BR]         = 50,
+                       [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_BR]         = 10,
+                       [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3]  = 200,
+                       [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP] = 800,
+                       [CONF_SG_PASSIVE_SCAN_A2DP_BT_TIME]         = 75,
+                       [CONF_SG_PASSIVE_SCAN_A2DP_WLAN_TIME]       = 15,
+                       [CONF_SG_HV3_MAX_SERVED]                    = 6,
+                       [CONF_SG_DHCP_TIME]                         = 5000,
+                       [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP]  = 100,
+                       [CONF_SG_TEMP_PARAM_1]                      = 0,
+                       [CONF_SG_TEMP_PARAM_2]                      = 0,
+                       [CONF_SG_TEMP_PARAM_3]                      = 0,
+                       [CONF_SG_TEMP_PARAM_4]                      = 0,
+                       [CONF_SG_TEMP_PARAM_5]                      = 0,
+                       [CONF_SG_AP_BEACON_MISS_TX]                 = 3,
+                       [CONF_SG_RX_WINDOW_LENGTH]                  = 6,
+                       [CONF_SG_AP_CONNECTION_PROTECTION_TIME]     = 50,
+                       [CONF_SG_TEMP_PARAM_6]                      = 1,
+               },
                .state = CONF_SG_PROTECTIVE,
        },
        .rx = {
@@ -107,7 +163,7 @@ static struct conf_drv_settings default_conf = {
                .packet_detection_threshold  = 0,
                .ps_poll_timeout             = 15,
                .upsd_timeout                = 15,
-               .rts_threshold               = 2347,
+               .rts_threshold               = IEEE80211_MAX_RTS_THRESHOLD,
                .rx_cca_threshold            = 0,
                .irq_blk_threshold           = 0xFFFF,
                .irq_pkt_threshold           = 0,
@@ -153,45 +209,8 @@ static struct conf_drv_settings default_conf = {
                                .tx_op_limit = 1504,
                        },
                },
-               .ap_rc_conf                  = {
-                       [0] = {
-                               .enabled_rates = CONF_TX_AP_ENABLED_RATES,
-                               .short_retry_limit = 10,
-                               .long_retry_limit = 10,
-                               .aflags      = 0,
-                       },
-                       [1] = {
-                               .enabled_rates = CONF_TX_AP_ENABLED_RATES,
-                               .short_retry_limit = 10,
-                               .long_retry_limit = 10,
-                               .aflags      = 0,
-                       },
-                       [2] = {
-                               .enabled_rates = CONF_TX_AP_ENABLED_RATES,
-                               .short_retry_limit = 10,
-                               .long_retry_limit = 10,
-                               .aflags      = 0,
-                       },
-                       [3] = {
-                               .enabled_rates = CONF_TX_AP_ENABLED_RATES,
-                               .short_retry_limit = 10,
-                               .long_retry_limit = 10,
-                               .aflags      = 0,
-                       },
-               },
-               .ap_mgmt_conf = {
-                       .enabled_rates       = CONF_TX_AP_DEFAULT_MGMT_RATES,
-                       .short_retry_limit   = 10,
-                       .long_retry_limit    = 10,
-                       .aflags              = 0,
-               },
-               .ap_bcst_conf = {
-                       .enabled_rates       = CONF_HW_BIT_RATE_1MBPS,
-                       .short_retry_limit   = 10,
-                       .long_retry_limit    = 10,
-                       .aflags              = 0,
-               },
-               .ap_max_tx_retries = 100,
+               .max_tx_retries = 100,
+               .ap_aging_period = 300,
                .tid_conf_count = 4,
                .tid_conf = {
                        [CONF_TX_AC_BE] = {
@@ -256,7 +275,7 @@ static struct conf_drv_settings default_conf = {
                .bet_enable                  = CONF_BET_MODE_ENABLE,
                .bet_max_consecutive         = 50,
                .psm_entry_retries           = 5,
-               .psm_exit_retries            = 255,
+               .psm_exit_retries            = 16,
                .psm_entry_nullfunc_retries  = 3,
                .psm_entry_hangover_period   = 1,
                .keep_alive_interval         = 55000,
@@ -303,7 +322,7 @@ static struct conf_drv_settings default_conf = {
                .ssid_profiles                = 1,
                .rx_block_num                 = 70,
                .tx_min_block_num             = 40,
-               .dynamic_memory               = 0,
+               .dynamic_memory               = 1,
                .min_req_tx_blocks            = 100,
                .min_req_rx_blocks            = 22,
                .tx_min                       = 27,
@@ -318,9 +337,23 @@ static struct conf_drv_settings default_conf = {
                .min_req_rx_blocks            = 22,
                .tx_min                       = 27,
        },
+       .fm_coex = {
+               .enable                       = true,
+               .swallow_period               = 5,
+               .n_divider_fref_set_1         = 0xff,       /* default */
+               .n_divider_fref_set_2         = 12,
+               .m_divider_fref_set_1         = 148,
+               .m_divider_fref_set_2         = 0xffff,     /* default */
+               .coex_pll_stabilization_time  = 0xffffffff, /* default */
+               .ldo_stabilization_time       = 0xffff,     /* default */
+               .fm_disturbed_band_margin     = 0xff,       /* default */
+               .swallow_clk_diff             = 0xff,       /* default */
+       },
+       .hci_io_ds = HCI_IO_DS_6MA,
 };
 
-static void __wl1271_op_remove_interface(struct wl1271 *wl);
+static void __wl1271_op_remove_interface(struct wl1271 *wl,
+                                        bool reset_tx_queues);
 static void wl1271_free_ap_keys(struct wl1271 *wl);
 
 
@@ -505,6 +538,11 @@ static int wl1271_plt_init(struct wl1271 *wl)
        if (ret < 0)
                goto out_free_memmap;
 
+       /* FM WLAN coexistence */
+       ret = wl1271_acx_fm_coex(wl);
+       if (ret < 0)
+               goto out_free_memmap;
+
        /* Energy detection */
        ret = wl1271_init_energy_detection(wl);
        if (ret < 0)
@@ -622,7 +660,7 @@ static void wl1271_fw_status(struct wl1271 *wl,
        struct wl1271_fw_common_status *status = &full_status->common;
        struct timespec ts;
        u32 old_tx_blk_count = wl->tx_blocks_available;
-       u32 total = 0;
+       u32 freed_blocks = 0;
        int i;
 
        if (wl->bss_type == BSS_TYPE_AP_BSS) {
@@ -631,15 +669,6 @@ static void wl1271_fw_status(struct wl1271 *wl,
        } else {
                wl1271_raw_read(wl, FW_STATUS_ADDR, status,
                                sizeof(struct wl1271_fw_sta_status), false);
-
-               /* Update tx total blocks change */
-               wl->tx_total_diff +=
-                       ((struct wl1271_fw_sta_status *)status)->tx_total -
-                       wl->tx_new_total;
-
-               /* Update total tx blocks */
-               wl->tx_new_total =
-                       ((struct wl1271_fw_sta_status *)status)->tx_total;
        }
 
        wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
@@ -651,34 +680,38 @@ static void wl1271_fw_status(struct wl1271 *wl,
 
        /* update number of available TX blocks */
        for (i = 0; i < NUM_TX_QUEUES; i++) {
-               total += le32_to_cpu(status->tx_released_blks[i]) -
-                       wl->tx_blocks_freed[i];
+               freed_blocks += le32_to_cpu(status->tx_released_blks[i]) -
+                               wl->tx_blocks_freed[i];
 
                wl->tx_blocks_freed[i] =
                        le32_to_cpu(status->tx_released_blks[i]);
-
        }
 
-       /*
-        * By adding the freed blocks to tx_total_diff we are actually
-        * moving them to the RX pool.
-        */
-       wl->tx_total_diff += total;
+       wl->tx_allocated_blocks -= freed_blocks;
 
-       /* if we have positive difference, add the blocks to the TX pool */
-       if (wl->tx_total_diff >= 0) {
-               wl->tx_blocks_available += wl->tx_total_diff;
-               wl->tx_total_diff = 0;
+       if (wl->bss_type == BSS_TYPE_AP_BSS) {
+               /* Update num of allocated TX blocks per link and ps status */
+               wl1271_irq_update_links_status(wl, &full_status->ap);
+               wl->tx_blocks_available += freed_blocks;
+       } else {
+               int avail = full_status->sta.tx_total - wl->tx_allocated_blocks;
+
+               /*
+                * The FW might change the total number of TX memblocks before
+                * we get a notification about blocks being released. Thus, the
+                * available blocks calculation might yield a temporary result
+                * which is lower than the actual available blocks. Keeping in
+                * mind that only blocks that were allocated can be moved from
+                * TX to RX, tx_blocks_available should never decrease here.
+                */
+               wl->tx_blocks_available = max((int)wl->tx_blocks_available,
+                                             avail);
        }
 
        /* if more blocks are available now, tx work can be scheduled */
        if (wl->tx_blocks_available > old_tx_blk_count)
                clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
 
-       /* for AP update num of allocated TX blocks per link and ps status */
-       if (wl->bss_type == BSS_TYPE_AP_BSS)
-               wl1271_irq_update_links_status(wl, &full_status->ap);
-
        /* update the host-chipset time offset */
        getnstimeofday(&ts);
        wl->time_offset = (timespec_to_ns(&ts) >> 10) -
@@ -724,6 +757,13 @@ irqreturn_t wl1271_irq(int irq, void *cookie)
        set_bit(WL1271_FLAG_TX_PENDING, &wl->flags);
        cancel_work_sync(&wl->tx_work);
 
+       /*
+        * In case edge triggered interrupt must be used, we cannot iterate
+        * more than once without introducing race conditions with the hardirq.
+        */
+       if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
+               loopcount = 1;
+
        mutex_lock(&wl->mutex);
 
        wl1271_debug(DEBUG_IRQ, "IRQ work");
@@ -927,15 +967,25 @@ static void wl1271_recovery_work(struct work_struct *work)
        if (wl->state != WL1271_STATE_ON)
                goto out;
 
-       wl1271_info("Hardware recovery in progress.");
+       wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x",
+                   wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4));
 
        if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
                ieee80211_connection_loss(wl->vif);
 
+       /* Prevent spurious TX during FW restart */
+       ieee80211_stop_queues(wl->hw);
+
        /* reboot the chipset */
-       __wl1271_op_remove_interface(wl);
+       __wl1271_op_remove_interface(wl, false);
        ieee80211_restart_hw(wl->hw);
 
+       /*
+        * Its safe to enable TX now - the queues are stopped after a request
+        * to restart the HW.
+        */
+       ieee80211_wake_queues(wl->hw);
+
 out:
        mutex_unlock(&wl->mutex);
 }
@@ -1006,6 +1056,10 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
                wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
                             wl->chip.id);
 
+               /* end-of-transaction flag should be set in wl127x AP mode */
+               if (wl->bss_type == BSS_TYPE_AP_BSS)
+                       wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION;
+
                ret = wl1271_setup(wl);
                if (ret < 0)
                        goto out;
@@ -1017,6 +1071,8 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
                ret = wl1271_setup(wl);
                if (ret < 0)
                        goto out;
+               if (wl1271_set_block_size(wl))
+                       wl->quirks |= WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT;
                break;
        case CHIP_ID_1283_PG10:
        default:
@@ -1210,20 +1266,46 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
 
-#define TX_DUMMY_PACKET_SIZE 1400
 int wl1271_tx_dummy_packet(struct wl1271 *wl)
 {
-       struct sk_buff *skb = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&wl->wl_lock, flags);
+       set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
+       wl->tx_queue_count++;
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+       /* The FW is low on RX memory blocks, so send the dummy packet asap */
+       if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags))
+               wl1271_tx_work_locked(wl);
+
+       /*
+        * If the FW TX is busy, TX work will be scheduled by the threaded
+        * interrupt handler function
+        */
+       return 0;
+}
+
+/*
+ * The size of the dummy packet should be at least 1400 bytes. However, in
+ * order to minimize the number of bus transactions, aligning it to 512 bytes
+ * boundaries could be beneficial, performance wise
+ */
+#define TOTAL_TX_DUMMY_PACKET_SIZE (ALIGN(1400, 512))
+
+static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
+{
+       struct sk_buff *skb;
        struct ieee80211_hdr_3addr *hdr;
-       int ret = 0;
+       unsigned int dummy_packet_size;
 
-       skb = dev_alloc_skb(
-               sizeof(struct wl1271_tx_hw_descr) + sizeof(*hdr) +
-               TX_DUMMY_PACKET_SIZE);
+       dummy_packet_size = TOTAL_TX_DUMMY_PACKET_SIZE -
+                           sizeof(struct wl1271_tx_hw_descr) - sizeof(*hdr);
+
+       skb = dev_alloc_skb(TOTAL_TX_DUMMY_PACKET_SIZE);
        if (!skb) {
-               wl1271_warning("failed to allocate buffer for dummy packet");
-               ret = -ENOMEM;
-               goto out;
+               wl1271_warning("Failed to allocate a dummy packet skb");
+               return NULL;
        }
 
        skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr));
@@ -1231,29 +1313,22 @@ int wl1271_tx_dummy_packet(struct wl1271 *wl)
        hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr));
        memset(hdr, 0, sizeof(*hdr));
        hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
-                                        IEEE80211_FCTL_TODS  |
-                                        IEEE80211_STYPE_NULLFUNC);
+                                        IEEE80211_STYPE_NULLFUNC |
+                                        IEEE80211_FCTL_TODS);
 
-       memcpy(hdr->addr1, wl->bssid, ETH_ALEN);
-       memcpy(hdr->addr2, wl->mac_addr, ETH_ALEN);
-       memcpy(hdr->addr3, wl->bssid, ETH_ALEN);
+       memset(skb_put(skb, dummy_packet_size), 0, dummy_packet_size);
 
-       skb_put(skb, TX_DUMMY_PACKET_SIZE);
-
-       memset(skb->data, 0, TX_DUMMY_PACKET_SIZE);
-
-       skb->pkt_type = TX_PKT_TYPE_DUMMY_REQ;
        /* Dummy packets require the TID to be management */
        skb->priority = WL1271_TID_MGMT;
-       /* CONF_TX_AC_VO */
-       skb->queue_mapping = 0;
 
-       wl1271_op_tx(wl->hw, skb);
+       /* Initialize all fields that might be used */
+       skb_set_queue_mapping(skb, 0);
+       memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info));
 
-out:
-       return ret;
+       return skb;
 }
 
+
 static struct notifier_block wl1271_dev_notifier = {
        .notifier_call = wl1271_dev_notify,
 };
@@ -1304,6 +1379,16 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
                goto out;
        }
 
+       /*
+        * in some very corner case HW recovery scenarios its possible to
+        * get here before __wl1271_op_remove_interface is complete, so
+        * opt out if that is the case.
+        */
+       if (test_bit(WL1271_FLAG_IF_INITIALIZED, &wl->flags)) {
+               ret = -EBUSY;
+               goto out;
+       }
+
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
                wl->bss_type = BSS_TYPE_STA_BSS;
@@ -1372,6 +1457,7 @@ power_off:
 
        wl->vif = vif;
        wl->state = WL1271_STATE_ON;
+       set_bit(WL1271_FLAG_IF_INITIALIZED, &wl->flags);
        wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str);
 
        /* update hw/fw version info in wiphy struct */
@@ -1403,32 +1489,38 @@ out:
        return ret;
 }
 
-static void __wl1271_op_remove_interface(struct wl1271 *wl)
+static void __wl1271_op_remove_interface(struct wl1271 *wl,
+                                        bool reset_tx_queues)
 {
        int i;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
 
+       /* because of hardware recovery, we may get here twice */
+       if (wl->state != WL1271_STATE_ON)
+               return;
+
        wl1271_info("down");
 
        mutex_lock(&wl_list_mutex);
        list_del(&wl->list);
        mutex_unlock(&wl_list_mutex);
 
-       WARN_ON(wl->state != WL1271_STATE_ON);
-
        /* enable dyn ps just in case (if left on due to fw crash etc) */
        if (wl->bss_type == BSS_TYPE_STA_BSS)
                ieee80211_enable_dyn_ps(wl->vif);
 
        if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
                wl->scan.state = WL1271_SCAN_STATE_IDLE;
-               kfree(wl->scan.scanned_ch);
-               wl->scan.scanned_ch = NULL;
+               memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
                wl->scan.req = NULL;
                ieee80211_scan_completed(wl->hw, true);
        }
 
+       /*
+        * this must be before the cancel_work calls below, so that the work
+        * functions don't perform further work.
+        */
        wl->state = WL1271_STATE_OFF;
 
        mutex_unlock(&wl->mutex);
@@ -1444,7 +1536,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
        mutex_lock(&wl->mutex);
 
        /* let's notify MAC80211 about the remaining pending TX frames */
-       wl1271_tx_reset(wl);
+       wl1271_tx_reset(wl, reset_tx_queues);
        wl1271_power_off(wl);
 
        memset(wl->bssid, 0, ETH_ALEN);
@@ -1458,6 +1550,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
        wl->psm_entry_retry = 0;
        wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
        wl->tx_blocks_available = 0;
+       wl->tx_allocated_blocks = 0;
        wl->tx_results_count = 0;
        wl->tx_packets_count = 0;
        wl->tx_security_last_seq = 0;
@@ -1465,14 +1558,19 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
        wl->time_offset = 0;
        wl->session_counter = 0;
        wl->rate_set = CONF_TX_RATE_MASK_BASIC;
-       wl->flags = 0;
        wl->vif = NULL;
        wl->filters = 0;
        wl1271_free_ap_keys(wl);
        memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));
        wl->ap_fw_ps_map = 0;
        wl->ap_ps_map = 0;
-       wl->block_size = 0;
+
+       /*
+        * this is performed after the cancel_work calls and the associated
+        * mutex_lock, so that wl1271_op_add_interface does not accidentally
+        * get executed before all these vars have been reset.
+        */
+       wl->flags = 0;
 
        for (i = 0; i < NUM_TX_QUEUES; i++)
                wl->tx_blocks_freed[i] = 0;
@@ -1499,14 +1597,14 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
         */
        if (wl->vif) {
                WARN_ON(wl->vif != vif);
-               __wl1271_op_remove_interface(wl);
+               __wl1271_op_remove_interface(wl, true);
        }
 
        mutex_unlock(&wl->mutex);
        cancel_work_sync(&wl->recovery_work);
 }
 
-static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
+void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
 {
        wl1271_set_default_filters(wl);
 
@@ -1569,10 +1667,10 @@ static int wl1271_join(struct wl1271 *wl, bool set_assoc)
         * One of the side effects of the JOIN command is that is clears
         * WPA/WPA2 keys from the chipset. Performing a JOIN while associated
         * to a WPA/WPA2 access point will therefore kill the data-path.
-        * Currently there is no supported scenario for JOIN during
-        * association - if it becomes a supported scenario, the WPA/WPA2 keys
-        * must be handled somehow.
-        *
+        * Currently the only valid scenario for JOIN during association
+        * is on roaming, in which case we will also be given new keys.
+        * Keep the below message for now, unless it starts bothering
+        * users who really like to roam a lot :)
         */
        if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
                wl1271_info("JOIN while associated.");
@@ -1628,7 +1726,7 @@ static int wl1271_unjoin(struct wl1271 *wl)
        clear_bit(WL1271_FLAG_JOINED, &wl->flags);
        memset(wl->bssid, 0, ETH_ALEN);
 
-       /* stop filterting packets based on bssid */
+       /* stop filtering packets based on bssid */
        wl1271_configure_filters(wl, FIF_OTHER_BSS);
 
 out:
@@ -1707,7 +1805,12 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
        mutex_lock(&wl->mutex);
 
        if (unlikely(wl->state == WL1271_STATE_OFF)) {
-               ret = -EAGAIN;
+               /* we support configuring the channel and band while off */
+               if ((changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
+                       wl->band = conf->channel->band;
+                       wl->channel = channel;
+               }
+
                goto out;
        }
 
@@ -2231,7 +2334,7 @@ static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
        if (ret < 0)
                goto out;
 
-       ret = wl1271_acx_frag_threshold(wl, (u16)value);
+       ret = wl1271_acx_frag_threshold(wl, value);
        if (ret < 0)
                wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret);
 
@@ -2259,7 +2362,7 @@ static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
        if (ret < 0)
                goto out;
 
-       ret = wl1271_acx_rts_threshold(wl, (u16) value);
+       ret = wl1271_acx_rts_threshold(wl, value);
        if (ret < 0)
                wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret);
 
@@ -2402,24 +2505,19 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
 
        if ((changed & BSS_CHANGED_BASIC_RATES)) {
                u32 rates = bss_conf->basic_rates;
-               struct conf_tx_rate_class mgmt_rc;
 
                wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates);
                wl->basic_rate = wl1271_tx_min_rate_get(wl);
-               wl1271_debug(DEBUG_AP, "basic rates: 0x%x",
-                            wl->basic_rate_set);
-
-               /* update the AP management rate policy with the new rates */
-               mgmt_rc.enabled_rates = wl->basic_rate_set;
-               mgmt_rc.long_retry_limit = 10;
-               mgmt_rc.short_retry_limit = 10;
-               mgmt_rc.aflags = 0;
-               ret = wl1271_acx_ap_rate_policy(wl, &mgmt_rc,
-                                               ACX_TX_AP_MODE_MGMT_RATE);
+
+               ret = wl1271_init_ap_rates(wl);
                if (ret < 0) {
-                       wl1271_error("AP mgmt policy change failed %d", ret);
+                       wl1271_error("AP rate policy change failed %d", ret);
                        goto out;
                }
+
+               ret = wl1271_ap_init_templates(wl);
+               if (ret < 0)
+                       goto out;
        }
 
        ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed);
@@ -2452,6 +2550,24 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
                }
        }
 
+       if (changed & BSS_CHANGED_IBSS) {
+               wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d",
+                            bss_conf->ibss_joined);
+
+               if (bss_conf->ibss_joined) {
+                       u32 rates = bss_conf->basic_rates;
+                       wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
+                                                                        rates);
+                       wl->basic_rate = wl1271_tx_min_rate_get(wl);
+
+                       /* by default, use 11b rates */
+                       wl->rate_set = CONF_TX_IBSS_DEFAULT_RATES;
+                       ret = wl1271_acx_sta_rate_policies(wl);
+                       if (ret < 0)
+                               goto out;
+               }
+       }
+
        ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
        if (ret < 0)
                goto out;
@@ -2641,8 +2757,10 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                        }
                } else {
                        /* use defaults when not associated */
+                       bool was_assoc =
+                           !!test_and_clear_bit(WL1271_FLAG_STA_ASSOCIATED,
+                                                &wl->flags);
                        clear_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags);
-                       clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
                        wl->aid = 0;
 
                        /* free probe-request template */
@@ -2668,8 +2786,10 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                                goto out;
 
                        /* restore the bssid filter and go to dummy bssid */
-                       wl1271_unjoin(wl);
-                       wl1271_dummy_join(wl);
+                       if (was_assoc) {
+                               wl1271_unjoin(wl);
+                               wl1271_dummy_join(wl);
+                       }
                }
        }
 
@@ -2788,32 +2908,31 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
                conf_tid->ack_policy = CONF_ACK_POLICY_LEGACY;
                conf_tid->apsd_conf[0] = 0;
                conf_tid->apsd_conf[1] = 0;
-       } else {
-               ret = wl1271_ps_elp_wakeup(wl);
-               if (ret < 0)
-                       goto out;
+               goto out;
+       }
 
-               /*
-                * the txop is confed in units of 32us by the mac80211,
-                * we need us
-                */
-               ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
-                                       params->cw_min, params->cw_max,
-                                       params->aifs, params->txop << 5);
-               if (ret < 0)
-                       goto out_sleep;
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
 
-               ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
-                                        CONF_CHANNEL_TYPE_EDCF,
-                                        wl1271_tx_get_queue(queue),
-                                        ps_scheme, CONF_ACK_POLICY_LEGACY,
-                                        0, 0);
-               if (ret < 0)
-                       goto out_sleep;
+       /*
+        * the txop is confed in units of 32us by the mac80211,
+        * we need us
+        */
+       ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
+                               params->cw_min, params->cw_max,
+                               params->aifs, params->txop << 5);
+       if (ret < 0)
+               goto out_sleep;
+
+       ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
+                                CONF_CHANNEL_TYPE_EDCF,
+                                wl1271_tx_get_queue(queue),
+                                ps_scheme, CONF_ACK_POLICY_LEGACY,
+                                0, 0);
 
 out_sleep:
-               wl1271_ps_elp_sleep(wl);
-       }
+       wl1271_ps_elp_sleep(wl);
 
 out:
        mutex_unlock(&wl->mutex);
@@ -2902,6 +3021,12 @@ static void wl1271_free_sta(struct wl1271 *wl, u8 hlid)
        __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
 }
 
+bool wl1271_is_active_sta(struct wl1271 *wl, u8 hlid)
+{
+       int id = hlid - WL1271_AP_STA_HLID_START;
+       return test_bit(id, wl->ap_hlid_map);
+}
+
 static int wl1271_op_sta_add(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
                             struct ieee80211_sta *sta)
@@ -3046,6 +3171,28 @@ out:
        return ret;
 }
 
+static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
+{
+       struct wl1271 *wl = hw->priv;
+       bool ret = false;
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state == WL1271_STATE_OFF))
+               goto out;
+
+       /* packets are considered pending if in the TX queue or the FW */
+       ret = (wl->tx_queue_count > 0) || (wl->tx_frames_cnt > 0);
+
+       /* the above is appropriate for STA mode for PS purposes */
+       WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
+
+out:
+       mutex_unlock(&wl->mutex);
+
+       return ret;
+}
+
 /* can't be const, mac80211 writes to this */
 static struct ieee80211_rate wl1271_rates[] = {
        { .bitrate = 10,
@@ -3297,6 +3444,7 @@ static const struct ieee80211_ops wl1271_ops = {
        .sta_add = wl1271_op_sta_add,
        .sta_remove = wl1271_op_sta_remove,
        .ampdu_action = wl1271_op_ampdu_action,
+       .tx_frames_pending = wl1271_tx_frames_pending,
        CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
 };
 
@@ -3347,8 +3495,7 @@ static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
        unsigned long res;
        int ret;
 
-       ret = strict_strtoul(buf, 10, &res);
-
+       ret = kstrtoul(buf, 10, &res);
        if (ret < 0) {
                wl1271_warning("incorrect value written to bt_coex_mode");
                return count;
@@ -3485,7 +3632,6 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
                IEEE80211_HW_HAS_RATE_CONTROL |
                IEEE80211_HW_CONNECTION_MONITOR |
                IEEE80211_HW_SUPPORTS_CQM_RSSI |
-               IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                IEEE80211_HW_AP_LINK_PS;
 
        wl->hw->wiphy->cipher_suites = cipher_suites;
@@ -3502,6 +3648,10 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
        wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
                        sizeof(struct ieee80211_header);
 
+       /* make sure all our channels fit in the scanned_ch bitmask */
+       BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
+                    ARRAY_SIZE(wl1271_channels_5ghz) >
+                    WL1271_MAX_CHANNELS);
        /*
         * We keep local copies of the band structs because we need to
         * modify them on a per-device basis.
@@ -3602,7 +3752,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
        wl->ap_ps_map = 0;
        wl->ap_fw_ps_map = 0;
        wl->quirks = 0;
-       wl->block_size = 0;
+       wl->platform_quirks = 0;
 
        memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
        for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
@@ -3623,11 +3773,17 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
                goto err_hw;
        }
 
+       wl->dummy_packet = wl12xx_alloc_dummy_packet(wl);
+       if (!wl->dummy_packet) {
+               ret = -ENOMEM;
+               goto err_aggr;
+       }
+
        /* Register platform device */
        ret = platform_device_register(wl->plat_dev);
        if (ret) {
                wl1271_error("couldn't register platform device");
-               goto err_aggr;
+               goto err_dummy_packet;
        }
        dev_set_drvdata(&wl->plat_dev->dev, wl);
 
@@ -3653,6 +3809,9 @@ err_bt_coex_state:
 err_platform:
        platform_device_unregister(wl->plat_dev);
 
+err_dummy_packet:
+       dev_kfree_skb(wl->dummy_packet);
+
 err_aggr:
        free_pages((unsigned long)wl->aggr_buf, order);
 
@@ -3672,6 +3831,7 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
 int wl1271_free_hw(struct wl1271 *wl)
 {
        platform_device_unregister(wl->plat_dev);
+       dev_kfree_skb(wl->dummy_packet);
        free_pages((unsigned long)wl->aggr_buf,
                        get_order(WL1271_AGGR_BUFFER_SIZE));
        kfree(wl->plat_dev);