wl12xx: AP mode - support hidden SSID
[pandora-kernel.git] / drivers / net / wireless / wl12xx / main.c
index 7fcdfa3..02b5c00 100644 (file)
 
 static struct conf_drv_settings default_conf = {
        .sg = {
-               .sta_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]                     = 200,
-                       [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]                 = 12,
-                       [CONF_SG_RATE_ADAPT_SNR]                    = 0,
-                       [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR]      = 10,
-                       [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR]      = 30,
-                       [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR]      = 8,
-                       [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR]       = 20,
-                       [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR]       = 50,
-                       /* Note: with UPSD, this should be 4 */
-                       [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR]       = 8,
-                       [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]     = 20,
-                       /* Note: with UPDS, this should be 15 */
-                       [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR]      = 8,
-                       /* Note: with UPDS, this should be 50 */
-                       [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR]      = 40,
-                       /* Note: with UPDS, this should be 10 */
-                       [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR]      = 20,
-                       [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,
-               },
-               .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,
+               .params = {
+                       [CONF_SG_ACL_BT_MASTER_MIN_BR] = 10,
+                       [CONF_SG_ACL_BT_MASTER_MAX_BR] = 180,
+                       [CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10,
+                       [CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180,
+                       [CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10,
+                       [CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80,
+                       [CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10,
+                       [CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80,
+                       [CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8,
+                       [CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8,
+                       [CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20,
+                       [CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20,
+                       [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20,
+                       [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35,
+                       [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16,
+                       [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35,
+                       [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32,
+                       [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50,
+                       [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28,
+                       [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50,
+                       [CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10,
+                       [CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20,
+                       [CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75,
+                       [CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15,
+                       [CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27,
+                       [CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17,
+                       /* active scan params */
+                       [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
+                       [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
+                       [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
+                       /* passive scan params */
+                       [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800,
+                       [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200,
+                       [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
+                       /* passive scan in dual antenna params */
+                       [CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0,
+                       [CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0,
+                       [CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0,
+                       /* general params */
+                       [CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1,
+                       [CONF_SG_ANTENNA_CONFIGURATION] = 0,
+                       [CONF_SG_BEACON_MISS_PERCENT] = 60,
+                       [CONF_SG_DHCP_TIME] = 5000,
+                       [CONF_SG_RXT] = 1200,
+                       [CONF_SG_TXT] = 1000,
+                       [CONF_SG_ADAPTIVE_RXT_TXT] = 1,
+                       [CONF_SG_GENERAL_USAGE_BIT_MAP] = 3,
+                       [CONF_SG_HV3_MAX_SERVED] = 6,
+                       [CONF_SG_PS_POLL_TIMEOUT] = 10,
+                       [CONF_SG_UPSD_TIMEOUT] = 10,
+                       [CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2,
+                       [CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5,
+                       [CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30,
+                       /* AP params */
+                       [CONF_AP_BEACON_MISS_TX] = 3,
+                       [CONF_AP_RX_WINDOW_AFTER_BEACON] = 10,
+                       [CONF_AP_BEACON_WINDOW_INTERVAL] = 2,
+                       [CONF_AP_CONNECTION_PROTECTION_TIME] = 0,
+                       [CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25,
+                       [CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25,
                },
                .state = CONF_SG_PROTECTIVE,
        },
@@ -279,10 +236,9 @@ static struct conf_drv_settings default_conf = {
                .ps_poll_recovery_period     = 700,
                .bet_enable                  = CONF_BET_MODE_ENABLE,
                .bet_max_consecutive         = 50,
-               .psm_entry_retries           = 5,
+               .psm_entry_retries           = 8,
                .psm_exit_retries            = 16,
                .psm_entry_nullfunc_retries  = 3,
-               .psm_entry_hangover_period   = 1,
                .keep_alive_interval         = 55000,
                .max_listen_interval         = 20,
        },
@@ -310,8 +266,8 @@ static struct conf_drv_settings default_conf = {
        },
        .sched_scan = {
                /* sched_scan requires dwell times in TU instead of TU/1000 */
-               .min_dwell_time_active = 8,
-               .max_dwell_time_active = 30,
+               .min_dwell_time_active = 30,
+               .max_dwell_time_active = 60,
                .dwell_time_passive    = 100,
                .dwell_time_dfs        = 150,
                .num_probe_reqs        = 2,
@@ -329,8 +285,10 @@ static struct conf_drv_settings default_conf = {
                },
        },
        .ht = {
+               .rx_ba_win_size = 8,
                .tx_ba_win_size = 64,
                .inactivity_timeout = 10000,
+               .tx_ba_tid_bitmap = CONF_TX_BA_ENABLED_TID_BITMAP,
        },
        .mem_wl127x = {
                .num_stations                 = 1,
@@ -379,9 +337,44 @@ static struct conf_drv_settings default_conf = {
                .threshold                    = 0,
        },
        .hci_io_ds = HCI_IO_DS_6MA,
+       .rate = {
+               .rate_retry_score = 32000,
+               .per_add = 8192,
+               .per_th1 = 2048,
+               .per_th2 = 4096,
+               .max_per = 8100,
+               .inverse_curiosity_factor = 5,
+               .tx_fail_low_th = 4,
+               .tx_fail_high_th = 10,
+               .per_alpha_shift = 4,
+               .per_add_shift = 13,
+               .per_beta1_shift = 10,
+               .per_beta2_shift = 8,
+               .rate_check_up = 2,
+               .rate_check_down = 12,
+               .rate_retry_policy = {
+                       0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00,
+               },
+       },
+       .hangover = {
+               .recover_time               = 0,
+               .hangover_period            = 20,
+               .dynamic_mode               = 1,
+               .early_termination_mode     = 1,
+               .max_period                 = 20,
+               .min_period                 = 1,
+               .increase_delta             = 1,
+               .decrease_delta             = 2,
+               .quiet_time                 = 4,
+               .increase_time              = 1,
+               .window_size                = 16,
+       },
 };
 
 static char *fwlog_param;
+static bool bug_on_recovery;
 
 static void __wl1271_op_remove_interface(struct wl1271 *wl,
                                         bool reset_tx_queues);
@@ -415,10 +408,12 @@ static int wl1271_check_operstate(struct wl1271 *wl, unsigned char operstate)
        if (test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags))
                return 0;
 
-       ret = wl1271_cmd_set_sta_state(wl);
+       ret = wl12xx_cmd_set_peer_state(wl, wl->sta_hlid);
        if (ret < 0)
                return ret;
 
+       wl12xx_croc(wl, wl->role_id);
+
        wl1271_info("Association completed.");
        return 0;
 }
@@ -718,7 +713,7 @@ static int wl1271_plt_init(struct wl1271 *wl)
        if (ret < 0)
                goto out_free_memmap;
 
-       ret = wl1271_acx_sta_mem_cfg(wl);
+       ret = wl12xx_acx_mem_cfg(wl);
        if (ret < 0)
                goto out_free_memmap;
 
@@ -773,34 +768,52 @@ static int wl1271_plt_init(struct wl1271 *wl)
        return ret;
 }
 
-#if 0
-static void wl1271_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_blks)
+static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_pkts)
 {
-       bool fw_ps;
+       bool fw_ps, single_sta;
 
        /* only regulate station links */
        if (hlid < WL1271_AP_STA_HLID_START)
                return;
 
        fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
+       single_sta = (wl->active_sta_count == 1);
 
        /*
         * Wake up from high level PS if the STA is asleep with too little
-        * blocks in FW or if the STA is awake.
+        * packets in FW or if the STA is awake.
         */
-       if (!fw_ps || tx_blks < WL1271_PS_STA_MAX_BLOCKS)
+       if (!fw_ps || tx_pkts < WL1271_PS_STA_MAX_PACKETS)
                wl1271_ps_link_end(wl, hlid);
 
-       /* Start high-level PS if the STA is asleep with enough blocks in FW */
-       else if (fw_ps && tx_blks >= WL1271_PS_STA_MAX_BLOCKS)
+       /*
+        * 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.
+        */
+       else if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
                wl1271_ps_link_start(wl, hlid, true);
 }
 
-static void wl1271_irq_update_links_status(struct wl1271 *wl,
-                                      struct wl1271_fw_ap_status *status)
+bool wl1271_is_active_sta(struct wl1271 *wl, u8 hlid)
+{
+       int id;
+
+       /* global/broadcast "stations" are always active */
+       if (hlid < WL1271_AP_STA_HLID_START)
+               return true;
+
+       id = hlid - WL1271_AP_STA_HLID_START;
+       return test_bit(id, wl->ap_hlid_map);
+}
+
+static void wl12xx_irq_update_links_status(struct wl1271 *wl,
+                                          struct wl12xx_fw_status *status)
 {
        u32 cur_fw_ps_map;
-       u8 hlid;
+       u8 hlid, cnt;
+
+       /* TODO: also use link_fast_bitmap here */
 
        cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
        if (wl->ap_fw_ps_map != cur_fw_ps_map) {
@@ -813,35 +826,30 @@ static void wl1271_irq_update_links_status(struct wl1271 *wl,
        }
 
        for (hlid = WL1271_AP_STA_HLID_START; hlid < AP_MAX_LINKS; hlid++) {
-               u8 cnt = status->tx_lnk_free_blks[hlid] -
-                       wl->links[hlid].prev_freed_blks;
+               if (!wl1271_is_active_sta(wl, hlid))
+                       continue;
 
-               wl->links[hlid].prev_freed_blks =
-                       status->tx_lnk_free_blks[hlid];
-               wl->links[hlid].allocated_blks -= cnt;
+               cnt = status->tx_lnk_free_pkts[hlid] -
+                     wl->links[hlid].prev_freed_pkts;
 
-               wl1271_irq_ps_regulate_link(wl, hlid,
-                                           wl->links[hlid].allocated_blks);
+               wl->links[hlid].prev_freed_pkts =
+                       status->tx_lnk_free_pkts[hlid];
+               wl->links[hlid].allocated_pkts -= cnt;
+
+               wl12xx_irq_ps_regulate_link(wl, hlid,
+                                           wl->links[hlid].allocated_pkts);
        }
 }
-#endif
 
-static void wl1271_fw_status(struct wl1271 *wl,
-                            struct wl1271_fw_full_status *full_status)
+static void wl12xx_fw_status(struct wl1271 *wl,
+                            struct wl12xx_fw_status *status)
 {
-       struct wl1271_fw_common_status *status = &full_status->common;
        struct timespec ts;
        u32 old_tx_blk_count = wl->tx_blocks_available;
-       u32 freed_blocks = 0;
+       int avail, freed_blocks;
        int i;
 
-       if (wl->bss_type == BSS_TYPE_AP_BSS) {
-               wl1271_raw_read(wl, FW_STATUS_ADDR, status,
-                               sizeof(struct wl1271_fw_ap_status), false);
-       } else {
-               wl1271_raw_read(wl, FW_STATUS_ADDR, status,
-                               sizeof(struct wl1271_fw_sta_status), false);
-       }
+       wl1271_raw_read(wl, FW_STATUS_ADDR, status, sizeof(*status), false);
 
        wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
                     "drv_rx_counter = %d, tx_results_counter = %d)",
@@ -850,42 +858,49 @@ static void wl1271_fw_status(struct wl1271 *wl,
                     status->drv_rx_counter,
                     status->tx_results_counter);
 
-       /* update number of available TX blocks */
        for (i = 0; i < NUM_TX_QUEUES; i++) {
-               freed_blocks += le32_to_cpu(status->tx_released_blks[i]) -
-                               wl->tx_blocks_freed[i];
+               /* prevent wrap-around in freed-packets counter */
+               wl->tx_allocated_pkts[i] -=
+                               (status->tx_released_pkts[i] -
+                               wl->tx_pkts_freed[i]) & 0xff;
 
-               wl->tx_blocks_freed[i] =
-                       le32_to_cpu(status->tx_released_blks[i]);
+               wl->tx_pkts_freed[i] = status->tx_released_pkts[i];
        }
 
+       /* prevent wrap-around in total blocks counter */
+       if (likely(wl->tx_blocks_freed <=
+                  le32_to_cpu(status->total_released_blks)))
+               freed_blocks = le32_to_cpu(status->total_released_blks) -
+                              wl->tx_blocks_freed;
+       else
+               freed_blocks = 0x100000000LL - wl->tx_blocks_freed +
+                              le32_to_cpu(status->total_released_blks);
+
+       wl->tx_blocks_freed = le32_to_cpu(status->total_released_blks);
+
        wl->tx_allocated_blocks -= freed_blocks;
 
-       if (wl->bss_type == BSS_TYPE_AP_BSS) {
-               /* Update num of allocated TX blocks per link and ps status */
-#if 0
-               wl1271_irq_update_links_status(wl, &full_status->ap);
-#endif
-               wl->tx_blocks_available += freed_blocks;
-       } else {
-               int avail = full_status->sta.tx_total - wl->tx_allocated_blocks;
+       avail = le32_to_cpu(status->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);
-       }
+       /*
+        * 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)
+               wl12xx_irq_update_links_status(wl, status);
+
        /* update the host-chipset time offset */
        getnstimeofday(&ts);
        wl->time_offset = (timespec_to_ns(&ts) >> 10) -
@@ -958,8 +973,8 @@ irqreturn_t wl1271_irq(int irq, void *cookie)
                clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
                smp_mb__after_clear_bit();
 
-               wl1271_fw_status(wl, wl->fw_status);
-               intr = le32_to_cpu(wl->fw_status->common.intr);
+               wl12xx_fw_status(wl, wl->fw_status);
+               intr = le32_to_cpu(wl->fw_status->intr);
                intr &= WL1271_INTR_MASK;
                if (!intr) {
                        done = true;
@@ -978,7 +993,7 @@ irqreturn_t wl1271_irq(int irq, void *cookie)
                if (likely(intr & WL1271_ACX_INTR_DATA)) {
                        wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
 
-                       wl1271_rx(wl, &wl->fw_status->common);
+                       wl12xx_rx(wl, wl->fw_status);
 
                        /* Check if any tx blocks were freed */
                        spin_lock_irqsave(&wl->wl_lock, flags);
@@ -995,7 +1010,7 @@ irqreturn_t wl1271_irq(int irq, void *cookie)
                        }
 
                        /* check for tx results */
-                       if (wl->fw_status->common.tx_results_counter !=
+                       if (wl->fw_status->tx_results_counter !=
                            (wl->tx_results_count & 0xff))
                                wl1271_tx_complete(wl);
 
@@ -1169,8 +1184,8 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
                wl12xx_cmd_stop_fwlog(wl);
 
        /* Read the first memory block address */
-       wl1271_fw_status(wl, wl->fw_status);
-       first_addr = __le32_to_cpu(wl->fw_status->sta.log_start_addr);
+       wl12xx_fw_status(wl, wl->fw_status);
+       first_addr = le32_to_cpu(wl->fw_status->log_start_addr);
        if (!first_addr)
                goto out;
 
@@ -1186,7 +1201,7 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
                 * of each memory block hold the hardware address of the next
                 * one. The last memory block points to the first one.
                 */
-               addr = __le32_to_cpup((__le32 *)block);
+               addr = le32_to_cpup((__le32 *)block);
                if (!wl12xx_copy_fwlog(wl, block + sizeof(addr),
                                       WL12XX_HW_BLOCK_SIZE - sizeof(addr)))
                        break;
@@ -1216,6 +1231,8 @@ static void wl1271_recovery_work(struct work_struct *work)
        wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x",
                    wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4));
 
+       BUG_ON(bug_on_recovery);
+
        /*
         * Advance security sequence number to overcome potential progress
         * in the firmware during recovery. This doens't hurt if the network is
@@ -1225,9 +1242,6 @@ static void wl1271_recovery_work(struct work_struct *work)
            test_bit(WL1271_FLAG_AP_STARTED, &wl->flags))
                wl->tx_security_seq += WL1271_TX_SQN_POST_RECOVERY_PADDING;
 
-       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);
 
@@ -1484,10 +1498,25 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
        q = wl1271_tx_get_queue(mapping);
 
        if (wl->bss_type == BSS_TYPE_AP_BSS)
-               hlid = wl1271_tx_get_hlid(skb);
+               hlid = wl12xx_tx_get_hlid_ap(wl, skb);
 
        spin_lock_irqsave(&wl->wl_lock, flags);
 
+       /* queue the packet */
+       if (wl->bss_type == BSS_TYPE_AP_BSS) {
+               if (!wl1271_is_active_sta(wl, hlid)) {
+                       wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d",
+                                    hlid, q);
+                       dev_kfree_skb(skb);
+                       goto out;
+               }
+
+               wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q);
+               skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
+       } else {
+               skb_queue_tail(&wl->tx_queue[q], skb);
+       }
+
        wl->tx_queue_count[q]++;
 
        /*
@@ -1500,14 +1529,6 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
                set_bit(q, &wl->stopped_queues_map);
        }
 
-       /* queue the packet */
-       if (wl->bss_type == BSS_TYPE_AP_BSS) {
-               wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q);
-               skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);
-       } else {
-               skb_queue_tail(&wl->tx_queue[q], skb);
-       }
-
        /*
         * The chip specific setup must run before the first TX packet -
         * before that, the tx_work will not be initialized!
@@ -1517,13 +1538,20 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
            !test_bit(WL1271_FLAG_TX_PENDING, &wl->flags))
                ieee80211_queue_work(wl->hw, &wl->tx_work);
 
+out:
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 }
 
 int wl1271_tx_dummy_packet(struct wl1271 *wl)
 {
        unsigned long flags;
-       int q = wl1271_tx_get_queue(skb_get_queue_mapping(wl->dummy_packet));
+       int q;
+
+       /* no need to queue a new dummy packet if one is already pending */
+       if (test_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags))
+               return 0;
+
+       q = wl1271_tx_get_queue(skb_get_queue_mapping(wl->dummy_packet));
 
        spin_lock_irqsave(&wl->wl_lock, flags);
        set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
@@ -1653,7 +1681,7 @@ static int wl1271_configure_suspend_ap(struct wl1271 *wl)
        if (ret < 0)
                goto out_unlock;
 
-       ret = wl1271_acx_set_ap_beacon_filter(wl, true);
+       ret = wl1271_acx_beacon_filter_opt(wl, true);
 
        wl1271_ps_elp_sleep(wl);
 out_unlock:
@@ -1691,7 +1719,7 @@ static void wl1271_configure_resume(struct wl1271 *wl)
                        wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE,
                                           wl->basic_rate, true);
        } else if (is_ap) {
-               wl1271_acx_set_ap_beacon_filter(wl, false);
+               wl1271_acx_beacon_filter_opt(wl, false);
        }
 
        wl1271_ps_elp_sleep(wl);
@@ -1793,6 +1821,30 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
        wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
 }
 
+static u8 wl12xx_get_role_type(struct wl1271 *wl)
+{
+       switch (wl->bss_type) {
+       case BSS_TYPE_AP_BSS:
+               if (wl->p2p)
+                       return WL1271_ROLE_P2P_GO;
+               else
+                       return WL1271_ROLE_AP;
+
+       case BSS_TYPE_STA_BSS:
+               if (wl->p2p)
+                       return WL1271_ROLE_P2P_CL;
+               else
+                       return WL1271_ROLE_STA;
+
+       case BSS_TYPE_IBSS:
+               return WL1271_ROLE_IBSS;
+
+       default:
+               wl1271_error("invalid bss_type: %d", wl->bss_type);
+       }
+       return WL12XX_INVALID_ROLE_TYPE;
+}
+
 static int wl1271_op_add_interface(struct ieee80211_hw *hw,
                                   struct ieee80211_vif *vif)
 {
@@ -1800,10 +1852,11 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
        struct wiphy *wiphy = hw->wiphy;
        int retries = WL1271_BOOT_RETRIES;
        int ret = 0;
+       u8 role_type;
        bool booted = false;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
-                    vif->type, vif->addr);
+                    ieee80211_vif_type_p2p(vif), vif->addr);
 
        mutex_lock(&wl->mutex);
        if (wl->vif) {
@@ -1823,7 +1876,10 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
                goto out;
        }
 
-       switch (vif->type) {
+       switch (ieee80211_vif_type_p2p(vif)) {
+       case NL80211_IFTYPE_P2P_CLIENT:
+               wl->p2p = 1;
+               /* fall-through */
        case NL80211_IFTYPE_STATION:
                wl->bss_type = BSS_TYPE_STA_BSS;
                wl->set_bss_type = BSS_TYPE_STA_BSS;
@@ -1832,6 +1888,9 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
                wl->bss_type = BSS_TYPE_IBSS;
                wl->set_bss_type = BSS_TYPE_STA_BSS;
                break;
+       case NL80211_IFTYPE_P2P_GO:
+               wl->p2p = 1;
+               /* fall-through */
        case NL80211_IFTYPE_AP:
                wl->bss_type = BSS_TYPE_AP_BSS;
                break;
@@ -1840,6 +1899,11 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
                goto out;
        }
 
+       role_type = wl12xx_get_role_type(wl);
+       if (role_type == WL12XX_INVALID_ROLE_TYPE) {
+               ret = -EINVAL;
+               goto out;
+       }
        memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
 
        if (wl->state != WL1271_STATE_OFF) {
@@ -1859,6 +1923,25 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
                if (ret < 0)
                        goto power_off;
 
+               if (wl->bss_type == BSS_TYPE_STA_BSS ||
+                   wl->bss_type == BSS_TYPE_IBSS) {
+                       /*
+                        * The device role is a special role used for
+                        * rx and tx frames prior to association (as
+                        * the STA role can get packets only from
+                        * its associated bssid)
+                        */
+                       ret = wl12xx_cmd_role_enable(wl,
+                                                        WL1271_ROLE_DEVICE,
+                                                        &wl->dev_role_id);
+                       if (ret < 0)
+                               goto irq_disable;
+               }
+
+               ret = wl12xx_cmd_role_enable(wl, role_type, &wl->role_id);
+               if (ret < 0)
+                       goto irq_disable;
+
                ret = wl1271_hw_init(wl);
                if (ret < 0)
                        goto irq_disable;
@@ -1923,7 +2006,7 @@ out:
 static void __wl1271_op_remove_interface(struct wl1271 *wl,
                                         bool reset_tx_queues)
 {
-       int i;
+       int ret, i;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
 
@@ -1948,6 +2031,31 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
                ieee80211_scan_completed(wl->hw, true);
        }
 
+       if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
+               /* disable active roles */
+               ret = wl1271_ps_elp_wakeup(wl);
+               if (ret < 0)
+                       goto deinit;
+
+               if (wl->bss_type == BSS_TYPE_STA_BSS) {
+                       ret = wl12xx_cmd_role_disable(wl, &wl->dev_role_id);
+                       if (ret < 0)
+                               goto deinit;
+               }
+
+               ret = wl12xx_cmd_role_disable(wl, &wl->role_id);
+               if (ret < 0)
+                       goto deinit;
+
+               wl1271_ps_elp_sleep(wl);
+       }
+deinit:
+       /* clear all hlids (except system_hlid) */
+       wl->sta_hlid = WL12XX_INVALID_LINK_ID;
+       wl->dev_hlid = WL12XX_INVALID_LINK_ID;
+       wl->ap_bcast_hlid = WL12XX_INVALID_LINK_ID;
+       wl->ap_global_hlid = WL12XX_INVALID_LINK_ID;
+
        /*
         * this must be before the cancel_work calls below, so that the work
         * functions don't perform further work.
@@ -1978,6 +2086,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
        wl->ssid_len = 0;
        wl->bss_type = MAX_BSS_TYPE;
        wl->set_bss_type = MAX_BSS_TYPE;
+       wl->p2p = 0;
        wl->band = IEEE80211_BAND_2GHZ;
 
        wl->rx_counter = 0;
@@ -1991,11 +2100,21 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
        wl->session_counter = 0;
        wl->rate_set = CONF_TX_RATE_MASK_BASIC;
        wl->vif = NULL;
+       wl->tx_spare_blocks = TX_HW_BLOCK_SPARE_DEFAULT;
        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->sched_scanning = false;
+       wl->role_id = WL12XX_INVALID_ROLE_ID;
+       wl->dev_role_id = WL12XX_INVALID_ROLE_ID;
+       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));
+       wl->active_sta_count = 0;
+
+       /* The system link is always allocated */
+       __set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
 
        /*
         * this is performed after the cancel_work calls and the associated
@@ -2004,8 +2123,12 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
         */
        wl->flags = 0;
 
-       for (i = 0; i < NUM_TX_QUEUES; i++)
-               wl->tx_blocks_freed[i] = 0;
+       wl->tx_blocks_freed = 0;
+
+       for (i = 0; i < NUM_TX_QUEUES; i++) {
+               wl->tx_pkts_freed[i] = 0;
+               wl->tx_allocated_pkts[i] = 0;
+       }
 
        wl1271_debugfs_reset(wl);
 
@@ -2036,28 +2159,10 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
        cancel_work_sync(&wl->recovery_work);
 }
 
-static int wl1271_dummy_join(struct wl1271 *wl)
-{
-       int ret = 0;
-       /* we need to use a dummy BSSID for now */
-       static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
-                                                 0xad, 0xbe, 0xef };
-
-       memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
-
-       ret = wl1271_cmd_join(wl, wl->set_bss_type);
-       if (ret < 0)
-               goto out;
-
-       set_bit(WL1271_FLAG_JOINED, &wl->flags);
-
-out:
-       return ret;
-}
-
 static int wl1271_join(struct wl1271 *wl, bool set_assoc)
 {
        int ret;
+       bool is_ibss = (wl->bss_type == BSS_TYPE_IBSS);
 
        /*
         * One of the side effects of the JOIN command is that is clears
@@ -2074,12 +2179,13 @@ static int wl1271_join(struct wl1271 *wl, bool set_assoc)
        if (set_assoc)
                set_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
 
-       ret = wl1271_cmd_join(wl, wl->set_bss_type);
+       if (is_ibss)
+               ret = wl12xx_cmd_role_start_ibss(wl);
+       else
+               ret = wl12xx_cmd_role_start_sta(wl);
        if (ret < 0)
                goto out;
 
-       set_bit(WL1271_FLAG_JOINED, &wl->flags);
-
        if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
                goto out;
 
@@ -2115,11 +2221,10 @@ static int wl1271_unjoin(struct wl1271 *wl)
        int ret;
 
        /* to stop listening to a channel, we disconnect */
-       ret = wl1271_cmd_disconnect(wl);
+       ret = wl12xx_cmd_role_stop_sta(wl);
        if (ret < 0)
                goto out;
 
-       clear_bit(WL1271_FLAG_JOINED, &wl->flags);
        memset(wl->bssid, 0, ETH_ALEN);
 
        /* reset TX security counters on a clean disconnect */
@@ -2132,10 +2237,25 @@ out:
 
 static void wl1271_set_band_rate(struct wl1271 *wl)
 {
-       if (wl->band == IEEE80211_BAND_2GHZ)
+       if (wl->band == IEEE80211_BAND_2GHZ) {
                wl->basic_rate_set = wl->conf.tx.basic_rate;
-       else
+               wl->rate_set = wl->conf.tx.basic_rate;
+       } else {
                wl->basic_rate_set = wl->conf.tx.basic_rate_5;
+               wl->rate_set = wl->conf.tx.basic_rate_5;
+       }
+
+}
+
+static bool wl12xx_is_roc(struct wl1271 *wl)
+{
+       u8 role_id;
+
+       role_id = find_first_bit(wl->roc_map, WL12XX_MAX_ROLES);
+       if (role_id >= WL12XX_MAX_ROLES)
+               return false;
+
+       return true;
 }
 
 static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle)
@@ -2143,8 +2263,13 @@ static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle)
        int ret;
 
        if (idle) {
-               if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
-                       ret = wl1271_unjoin(wl);
+               /* no need to croc if we weren't busy (e.g. during boot) */
+               if (wl12xx_is_roc(wl)) {
+                       ret = wl12xx_croc(wl, wl->dev_role_id);
+                       if (ret < 0)
+                               goto out;
+
+                       ret = wl12xx_cmd_role_stop_dev(wl);
                        if (ret < 0)
                                goto out;
                }
@@ -2159,18 +2284,17 @@ static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle)
                        goto out;
                set_bit(WL1271_FLAG_IDLE, &wl->flags);
        } else {
-               /* increment the session counter */
-               wl->session_counter++;
-               if (wl->session_counter >= SESSION_COUNTER_MAX)
-                       wl->session_counter = 0;
-
                /* The current firmware only supports sched_scan in idle */
                if (wl->sched_scanning) {
                        wl1271_scan_sched_scan_stop(wl);
                        ieee80211_sched_scan_stopped(wl->hw);
                }
 
-               ret = wl1271_dummy_join(wl);
+               ret = wl12xx_cmd_role_start_dev(wl);
+               if (ret < 0)
+                       goto out;
+
+               ret = wl12xx_roc(wl, wl->dev_role_id);
                if (ret < 0)
                        goto out;
                clear_bit(WL1271_FLAG_IDLE, &wl->flags);
@@ -2231,6 +2355,8 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
        if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
            ((wl->band != conf->channel->band) ||
             (wl->channel != channel))) {
+               /* send all pending packets */
+               wl1271_tx_work_locked(wl);
                wl->band = conf->channel->band;
                wl->channel = channel;
 
@@ -2250,11 +2376,34 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
                                wl1271_warning("rate policy for channel "
                                               "failed %d", ret);
 
-                       if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
+                       if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
+                               if (wl12xx_is_roc(wl)) {
+                                       /* roaming */
+                                       ret = wl12xx_croc(wl, wl->dev_role_id);
+                                       if (ret < 0)
+                                               goto out_sleep;
+                               }
                                ret = wl1271_join(wl, false);
                                if (ret < 0)
                                        wl1271_warning("cmd join on channel "
                                                       "failed %d", ret);
+                       } else {
+                               /*
+                                * change the ROC channel. do it only if we are
+                                * not idle. otherwise, CROC will be called
+                                * anyway.
+                                */
+                               if (wl12xx_is_roc(wl) &&
+                                   !(conf->flags & IEEE80211_CONF_IDLE)) {
+                                       ret = wl12xx_croc(wl, wl->dev_role_id);
+                                       if (ret < 0)
+                                               goto out_sleep;
+
+                                       ret = wl12xx_roc(wl, wl->dev_role_id);
+                                       if (ret < 0)
+                                               wl1271_warning("roc failed %d",
+                                                              ret);
+                               }
                        }
                }
        }
@@ -2470,14 +2619,19 @@ static int wl1271_ap_init_hwenc(struct wl1271 *wl)
        bool wep_key_added = false;
 
        for (i = 0; i < MAX_NUM_KEYS; i++) {
+               u8 hlid;
                if (wl->recorded_ap_keys[i] == NULL)
                        break;
 
                key = wl->recorded_ap_keys[i];
+               hlid = key->hlid;
+               if (hlid == WL12XX_INVALID_LINK_ID)
+                       hlid = wl->ap_bcast_hlid;
+
                ret = wl1271_cmd_set_ap_key(wl, KEY_ADD_OR_REPLACE,
                                            key->id, key->key_type,
                                            key->key_size, key->key,
-                                           key->hlid, key->tx_seq_32,
+                                           hlid, key->tx_seq_32,
                                            key->tx_seq_16);
                if (ret < 0)
                        goto out;
@@ -2487,7 +2641,8 @@ static int wl1271_ap_init_hwenc(struct wl1271 *wl)
        }
 
        if (wep_key_added) {
-               ret = wl1271_cmd_set_ap_default_wep_key(wl, wl->default_key);
+               ret = wl12xx_cmd_set_default_wep_key(wl, wl->default_key,
+                                                    wl->ap_bcast_hlid);
                if (ret < 0)
                        goto out;
        }
@@ -2512,7 +2667,7 @@ static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
                        wl_sta = (struct wl1271_station *)sta->drv_priv;
                        hlid = wl_sta->hlid;
                } else {
-                       hlid = WL1271_AP_BROADCAST_HLID;
+                       hlid = wl->ap_bcast_hlid;
                }
 
                if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
@@ -2542,6 +2697,17 @@ static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
                        0xff, 0xff, 0xff, 0xff, 0xff, 0xff
                };
 
+               /*
+                * A STA set to GEM cipher requires 2 tx spare blocks.
+                * Return to default value when GEM cipher key is removed
+                */
+               if (key_type == KEY_GEM) {
+                       if (action == KEY_ADD_OR_REPLACE)
+                               wl->tx_spare_blocks = 2;
+                       else if (action == KEY_REMOVE)
+                               wl->tx_spare_blocks = TX_HW_BLOCK_SPARE_DEFAULT;
+               }
+
                addr = sta ? sta->addr : bcast_addr;
 
                if (is_zero_ether_addr(addr)) {
@@ -2556,6 +2722,11 @@ static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
                if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr))
                        return 0;
 
+               /* don't remove key if hlid was already deleted */
+               if (action == KEY_REMOVE &&
+                   wl->sta_hlid == WL12XX_INVALID_LINK_ID)
+                       return 0;
+
                ret = wl1271_cmd_set_sta_key(wl, action,
                                             id, key_type, key_size,
                                             key, addr, tx_seq_32,
@@ -2565,8 +2736,9 @@ static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
 
                /* the default WEP key needs to be configured at least once */
                if (key_type == KEY_WEP) {
-                       ret = wl1271_cmd_set_sta_default_wep_key(wl,
-                                                       wl->default_key);
+                       ret = wl12xx_cmd_set_default_wep_key(wl,
+                                                            wl->default_key,
+                                                            wl->sta_hlid);
                        if (ret < 0)
                                return ret;
                }
@@ -2708,10 +2880,20 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
-       ret = wl1271_scan(hw->priv, ssid, len, req);
+       /* cancel ROC before scanning */
+       if (wl12xx_is_roc(wl)) {
+               if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
+                       /* don't allow scanning right now */
+                       ret = -EBUSY;
+                       goto out_sleep;
+               }
+               wl12xx_croc(wl, wl->dev_role_id);
+               wl12xx_cmd_role_stop_dev(wl);
+       }
 
+       ret = wl1271_scan(hw->priv, ssid, len, req);
+out_sleep:
        wl1271_ps_elp_sleep(wl);
-
 out:
        mutex_unlock(&wl->mutex);
 
@@ -2889,6 +3071,93 @@ static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb,
        return 0;
 }
 
+static void wl12xx_remove_ie(struct sk_buff *skb, u8 eid, int ieoffset)
+{
+       int len;
+       const u8 *next, *end = skb->data + skb->len;
+       u8 *ie = (u8 *)cfg80211_find_ie(eid, skb->data + ieoffset,
+                                       skb->len - ieoffset);
+       if (!ie)
+               return;
+       len = ie[1] + 2;
+       next = ie + len;
+       memmove(ie, next, end - next);
+       skb_trim(skb, skb->len - len);
+}
+
+static void wl12xx_remove_vendor_ie(struct sk_buff *skb,
+                                           unsigned int oui, u8 oui_type,
+                                           int ieoffset)
+{
+       int len;
+       const u8 *next, *end = skb->data + skb->len;
+       u8 *ie = (u8 *)cfg80211_find_vendor_ie(oui, oui_type,
+                                              skb->data + ieoffset,
+                                              skb->len - ieoffset);
+       if (!ie)
+               return;
+       len = ie[1] + 2;
+       next = ie + len;
+       memmove(ie, next, end - next);
+       skb_trim(skb, skb->len - len);
+}
+
+static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl,
+                                        u8 *probe_rsp_data,
+                                        size_t probe_rsp_len,
+                                        u32 rates)
+{
+       struct ieee80211_bss_conf *bss_conf = &wl->vif->bss_conf;
+       u8 probe_rsp_templ[WL1271_CMD_TEMPL_MAX_SIZE];
+       int ssid_ie_offset, ie_offset, templ_len;
+       const u8 *ptr;
+
+       /* no need to change probe response if the SSID is set correctly */
+       if (wl->ssid_len > 0)
+               return wl1271_cmd_template_set(wl,
+                                              CMD_TEMPL_AP_PROBE_RESPONSE,
+                                              probe_rsp_data,
+                                              probe_rsp_len, 0,
+                                              rates);
+
+       if (probe_rsp_len + bss_conf->ssid_len > WL1271_CMD_TEMPL_MAX_SIZE) {
+               wl1271_error("probe_rsp template too big");
+               return -EINVAL;
+       }
+
+       /* start searching from IE offset */
+       ie_offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+
+       ptr = cfg80211_find_ie(WLAN_EID_SSID, probe_rsp_data + ie_offset,
+                              probe_rsp_len - ie_offset);
+       if (!ptr) {
+               wl1271_error("No SSID in beacon!");
+               return -EINVAL;
+       }
+
+       ssid_ie_offset = ptr - probe_rsp_data;
+       ptr += (ptr[1] + 2);
+
+       memcpy(probe_rsp_templ, probe_rsp_data, ssid_ie_offset);
+
+       /* insert SSID from bss_conf */
+       probe_rsp_templ[ssid_ie_offset] = WLAN_EID_SSID;
+       probe_rsp_templ[ssid_ie_offset + 1] = bss_conf->ssid_len;
+       memcpy(probe_rsp_templ + ssid_ie_offset + 2,
+              bss_conf->ssid, bss_conf->ssid_len);
+       templ_len = ssid_ie_offset + 2 + bss_conf->ssid_len;
+
+       memcpy(probe_rsp_templ + ssid_ie_offset + 2 + bss_conf->ssid_len,
+              ptr, probe_rsp_len - (ptr - probe_rsp_data));
+       templ_len += probe_rsp_len - (ptr - probe_rsp_data);
+
+       return wl1271_cmd_template_set(wl,
+                                      CMD_TEMPL_AP_PROBE_RESPONSE,
+                                      probe_rsp_templ,
+                                      templ_len, 0,
+                                      rates);
+}
+
 static int wl1271_bss_erp_info_changed(struct wl1271 *wl,
                                       struct ieee80211_bss_conf *bss_conf,
                                       u32 changed)
@@ -2971,17 +3240,34 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
                        goto out;
                }
 
+               /* remove TIM ie from probe response */
+               wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset);
+
+               /*
+                * remove p2p ie from probe response.
+                * the fw reponds to probe requests that don't include
+                * the p2p ie. probe requests with p2p ie will be passed,
+                * and will be responded by the supplicant (the spec
+                * forbids including the p2p ie when responding to probe
+                * requests that didn't include it).
+                */
+               wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA,
+                                       WLAN_OUI_TYPE_WFA_P2P, ieoffset);
+
                hdr = (struct ieee80211_hdr *) beacon->data;
                hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                                 IEEE80211_STYPE_PROBE_RESP);
-
-               tmpl_id = is_ap ? CMD_TEMPL_AP_PROBE_RESPONSE :
-                                 CMD_TEMPL_PROBE_RESPONSE;
-               ret = wl1271_cmd_template_set(wl,
-                                             tmpl_id,
-                                             beacon->data,
-                                             beacon->len, 0,
-                                             wl1271_tx_min_rate_get(wl));
+               if (is_ap)
+                       ret = wl1271_ap_set_probe_resp_tmpl(wl,
+                                               beacon->data,
+                                               beacon->len,
+                                               wl1271_tx_min_rate_get(wl));
+               else
+                       ret = wl1271_cmd_template_set(wl,
+                                               CMD_TEMPL_PROBE_RESPONSE,
+                                               beacon->data,
+                                               beacon->len, 0,
+                                               wl1271_tx_min_rate_get(wl));
                dev_kfree_skb(beacon);
                if (ret < 0)
                        goto out;
@@ -3023,20 +3309,20 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
        if ((changed & BSS_CHANGED_BEACON_ENABLED)) {
                if (bss_conf->enable_beacon) {
                        if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
-                               ret = wl1271_cmd_start_bss(wl);
+                               ret = wl12xx_cmd_role_start_ap(wl);
                                if (ret < 0)
                                        goto out;
 
-                               set_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
-                               wl1271_debug(DEBUG_AP, "started AP");
-
                                ret = wl1271_ap_init_hwenc(wl);
                                if (ret < 0)
                                        goto out;
+
+                               set_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
+                               wl1271_debug(DEBUG_AP, "started AP");
                        }
                } else {
                        if (test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
-                               ret = wl1271_cmd_stop_bss(wl);
+                               ret = wl12xx_cmd_role_stop_ap(wl);
                                if (ret < 0)
                                        goto out;
 
@@ -3049,6 +3335,18 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
        ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed);
        if (ret < 0)
                goto out;
+
+       /* Handle HT information change */
+       if ((changed & BSS_CHANGED_HT) &&
+           (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
+               ret = wl1271_acx_set_ht_information(wl,
+                                       bss_conf->ht_operation_mode);
+               if (ret < 0) {
+                       wl1271_warning("Set ht information failed %d", ret);
+                       goto out;
+               }
+       }
+
 out:
        return;
 }
@@ -3061,6 +3359,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
 {
        bool do_join = false, set_assoc = false;
        bool is_ibss = (wl->bss_type == BSS_TYPE_IBSS);
+       bool ibss_joined = false;
        u32 sta_rate_set = 0;
        int ret;
        struct ieee80211_sta *sta;
@@ -3074,14 +3373,28 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                        goto out;
        }
 
-       if ((changed & BSS_CHANGED_BEACON_INT)  && is_ibss)
+       if (changed & BSS_CHANGED_IBSS) {
+               if (bss_conf->ibss_joined) {
+                       set_bit(WL1271_FLAG_IBSS_JOINED, &wl->flags);
+                       ibss_joined = true;
+               } else {
+                       if (test_and_clear_bit(WL1271_FLAG_IBSS_JOINED,
+                                              &wl->flags)) {
+                               wl1271_unjoin(wl);
+                               wl12xx_cmd_role_start_dev(wl);
+                               wl12xx_roc(wl, wl->dev_role_id);
+                       }
+               }
+       }
+
+       if ((changed & BSS_CHANGED_BEACON_INT) && ibss_joined)
                do_join = true;
 
        /* Need to update the SSID (for filtering etc) */
-       if ((changed & BSS_CHANGED_BEACON) && is_ibss)
+       if ((changed & BSS_CHANGED_BEACON) && ibss_joined)
                do_join = true;
 
-       if ((changed & BSS_CHANGED_BEACON_ENABLED) && is_ibss) {
+       if ((changed & BSS_CHANGED_BEACON_ENABLED) && ibss_joined) {
                wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
                             bss_conf->enable_beacon ? "enabled" : "disabled");
 
@@ -3126,9 +3439,12 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                }
        }
 
-       rcu_read_lock();
-       sta = ieee80211_find_sta(vif, bss_conf->bssid);
-       if (sta)  {
+       if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) {
+               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)
@@ -3136,38 +3452,9 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                            (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET);
                sta_ht_cap = sta->ht_cap;
                sta_exists = true;
-       }
-       rcu_read_unlock();
 
-       if (sta_exists) {
-               /* handle new association with HT and HT information change */
-               if ((changed & BSS_CHANGED_HT) &&
-                   (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
-                       ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap,
-                                                            true);
-                       if (ret < 0) {
-                               wl1271_warning("Set ht cap true failed %d",
-                                              ret);
-                               goto out;
-                       }
-                       ret = wl1271_acx_set_ht_information(wl,
-                                               bss_conf->ht_operation_mode);
-                       if (ret < 0) {
-                               wl1271_warning("Set ht information 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);
-                       if (ret < 0) {
-                               wl1271_warning("Set ht cap false failed %d",
-                                              ret);
-                               goto out;
-                       }
-               }
+sta_not_found:
+               rcu_read_unlock();
        }
 
        if ((changed & BSS_CHANGED_ASSOC)) {
@@ -3217,25 +3504,14 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                        ret = wl1271_acx_conn_monit_params(wl, true);
                        if (ret < 0)
                                goto out;
-
-                       /* If we want to go in PSM but we're not there yet */
-                       if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) &&
-                           !test_bit(WL1271_FLAG_PSM, &wl->flags)) {
-                               enum wl1271_cmd_ps_mode mode;
-
-                               mode = STATION_POWER_SAVE_MODE;
-                               ret = wl1271_ps_set_mode(wl, mode,
-                                                        wl->basic_rate,
-                                                        true);
-                               if (ret < 0)
-                                       goto out;
-                       }
                } 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);
+                       bool was_ifup =
+                           !!test_and_clear_bit(WL1271_FLAG_STA_STATE_SENT,
+                                                &wl->flags);
                        wl->aid = 0;
 
                        /* free probe-request template */
@@ -3262,8 +3538,32 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
 
                        /* restore the bssid filter and go to dummy bssid */
                        if (was_assoc) {
+                               u32 conf_flags = wl->hw->conf.flags;
+                               /*
+                                * we might have to disable roc, if there was
+                                * no IF_OPER_UP notification.
+                                */
+                               if (!was_ifup) {
+                                       ret = wl12xx_croc(wl, wl->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(wl->dev_role_id, wl->roc_map)) {
+                                       ret = wl12xx_croc(wl, wl->dev_role_id);
+                                       if (ret < 0)
+                                               goto out;
+                               }
+
                                wl1271_unjoin(wl);
-                               wl1271_dummy_join(wl);
+                               if (!(conf_flags & IEEE80211_CONF_IDLE)) {
+                                       wl12xx_cmd_role_start_dev(wl);
+                                       wl12xx_roc(wl, wl->dev_role_id);
+                               }
                        }
                }
        }
@@ -3278,7 +3578,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                                                                         rates);
                        wl->basic_rate = wl1271_tx_min_rate_get(wl);
 
-                       /* by default, use 11b rates */
+                       /* by default, use 11b + OFDM rates */
                        wl->rate_set = CONF_TX_IBSS_DEFAULT_RATES;
                        ret = wl1271_acx_sta_rate_policies(wl);
                        if (ret < 0)
@@ -3324,7 +3624,81 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                        wl1271_warning("cmd join failed %d", ret);
                        goto out;
                }
-               wl1271_check_operstate(wl, ieee80211_get_operstate(vif));
+
+               /* ROC until connected (after EAPOL exchange) */
+               if (!is_ibss) {
+                       ret = wl12xx_roc(wl, wl->role_id);
+                       if (ret < 0)
+                               goto out;
+
+                       wl1271_check_operstate(wl,
+                                              ieee80211_get_operstate(vif));
+               }
+               /*
+                * stop device role if started (we might already be in
+                * STA role). TODO: make it better.
+                */
+               if (wl->dev_role_id != WL12XX_INVALID_ROLE_ID) {
+                       ret = wl12xx_croc(wl, wl->dev_role_id);
+                       if (ret < 0)
+                               goto out;
+
+                       ret = wl12xx_cmd_role_stop_dev(wl);
+                       if (ret < 0)
+                               goto out;
+               }
+
+               /* If we want to go in PSM but we're not there yet */
+               if (test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags) &&
+                   !test_bit(WL1271_FLAG_PSM, &wl->flags)) {
+                       enum wl1271_cmd_ps_mode mode;
+
+                       mode = STATION_POWER_SAVE_MODE;
+                       ret = wl1271_ps_set_mode(wl, mode,
+                                                wl->basic_rate,
+                                                true);
+                       if (ret < 0)
+                               goto out;
+               }
+       }
+
+       /* Handle new association with HT. Do this after join. */
+       if (sta_exists) {
+               if ((changed & BSS_CHANGED_HT) &&
+                   (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
+                       ret = wl1271_acx_set_ht_capabilities(wl,
+                                                            &sta_ht_cap,
+                                                            true,
+                                                            wl->sta_hlid);
+                       if (ret < 0) {
+                               wl1271_warning("Set ht cap true 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,
+                                                            wl->sta_hlid);
+                       if (ret < 0) {
+                               wl1271_warning("Set ht cap false failed %d",
+                                              ret);
+                               goto out;
+                       }
+               }
+       }
+
+       /* Handle HT information change. Done after join. */
+       if ((changed & BSS_CHANGED_HT) &&
+           (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
+               ret = wl1271_acx_set_ht_information(wl,
+                                       bss_conf->ht_operation_mode);
+               if (ret < 0) {
+                       wl1271_warning("Set ht information failed %d", ret);
+                       goto out;
+               }
        }
 
 out:
@@ -3494,31 +3868,31 @@ static int wl1271_allocate_sta(struct wl1271 *wl,
        }
 
        wl_sta = (struct wl1271_station *)sta->drv_priv;
-       __set_bit(id, wl->ap_hlid_map);
+       set_bit(id, wl->ap_hlid_map);
        wl_sta->hlid = WL1271_AP_STA_HLID_START + id;
        *hlid = wl_sta->hlid;
        memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN);
+       wl->active_sta_count++;
        return 0;
 }
 
-static void wl1271_free_sta(struct wl1271 *wl, u8 hlid)
+void wl1271_free_sta(struct wl1271 *wl, u8 hlid)
 {
        int id = hlid - WL1271_AP_STA_HLID_START;
 
-       if (WARN_ON(!test_bit(id, wl->ap_hlid_map)))
+       if (hlid < WL1271_AP_STA_HLID_START)
                return;
 
-       __clear_bit(id, wl->ap_hlid_map);
+       if (!test_bit(id, wl->ap_hlid_map))
+               return;
+
+       clear_bit(id, wl->ap_hlid_map);
        memset(wl->links[hlid].addr, 0, ETH_ALEN);
+       wl->links[hlid].ba_bitmap = 0;
        wl1271_tx_reset_link_queues(wl, hlid);
        __clear_bit(hlid, &wl->ap_ps_map);
        __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
-}
-
-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);
+       wl->active_sta_count--;
 }
 
 static int wl1271_op_sta_add(struct ieee80211_hw *hw,
@@ -3547,7 +3921,15 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out_free_sta;
 
-       ret = wl1271_cmd_add_sta(wl, sta, hlid);
+       ret = wl12xx_cmd_add_peer(wl, sta, hlid);
+       if (ret < 0)
+               goto out_sleep;
+
+       ret = wl12xx_cmd_set_peer_state(wl, hlid);
+       if (ret < 0)
+               goto out_sleep;
+
+       ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true, hlid);
        if (ret < 0)
                goto out_sleep;
 
@@ -3590,7 +3972,7 @@ static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
-       ret = wl1271_cmd_remove_sta(wl, wl_sta->hlid);
+       ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid);
        if (ret < 0)
                goto out_sleep;
 
@@ -3612,6 +3994,14 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
 {
        struct wl1271 *wl = hw->priv;
        int ret;
+       u8 hlid, *ba_bitmap;
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 ampdu action %d tid %d", action,
+                    tid);
+
+       /* sanity check - the fields in FW are only 8bits wide */
+       if (WARN_ON(tid > 0xFF))
+               return -ENOTSUPP;
 
        mutex_lock(&wl->mutex);
 
@@ -3620,6 +4010,20 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
                goto out;
        }
 
+       if (wl->bss_type == BSS_TYPE_STA_BSS) {
+               hlid = wl->sta_hlid;
+               ba_bitmap = &wl->ba_rx_bitmap;
+       } else if (wl->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;
+       }
+
        ret = wl1271_ps_elp_wakeup(wl);
        if (ret < 0)
                goto out;
@@ -3629,20 +4033,46 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
 
        switch (action) {
        case IEEE80211_AMPDU_RX_START:
-               if ((wl->ba_support) && (wl->ba_allowed)) {
-                       ret = wl1271_acx_set_ba_receiver_session(wl, tid, *ssn,
-                                                                true);
-                       if (!ret)
-                               wl->ba_rx_bitmap |= BIT(tid);
-               } else {
+               if (!wl->ba_support || !wl->ba_allowed) {
                        ret = -ENOTSUPP;
+                       break;
+               }
+
+               if (wl->ba_rx_session_count >= RX_BA_MAX_SESSIONS) {
+                       ret = -EBUSY;
+                       wl1271_error("exceeded max RX BA sessions");
+                       break;
+               }
+
+               if (*ba_bitmap & BIT(tid)) {
+                       ret = -EINVAL;
+                       wl1271_error("cannot enable RX BA session on active "
+                                    "tid: %d", tid);
+                       break;
+               }
+
+               ret = wl12xx_acx_set_ba_receiver_session(wl, tid, *ssn, true,
+                                                        hlid);
+               if (!ret) {
+                       *ba_bitmap |= BIT(tid);
+                       wl->ba_rx_session_count++;
                }
                break;
 
        case IEEE80211_AMPDU_RX_STOP:
-               ret = wl1271_acx_set_ba_receiver_session(wl, tid, 0, false);
-               if (!ret)
-                       wl->ba_rx_bitmap &= ~BIT(tid);
+               if (!(*ba_bitmap & BIT(tid))) {
+                       ret = -EINVAL;
+                       wl1271_error("no active RX BA session on tid: %d",
+                                    tid);
+                       break;
+               }
+
+               ret = wl12xx_acx_set_ba_receiver_session(wl, tid, 0, false,
+                                                        hlid);
+               if (!ret) {
+                       *ba_bitmap &= ~BIT(tid);
+                       wl->ba_rx_session_count--;
+               }
                break;
 
        /*
@@ -3784,7 +4214,6 @@ static const u8 wl1271_rate_to_idx_2ghz[] = {
 /* 11n STA capabilities */
 #define HW_RX_HIGHEST_RATE     72
 
-#ifdef CONFIG_WL12XX_HT
 #define WL12XX_HT_CAP { \
        .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | \
               (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), \
@@ -3797,11 +4226,6 @@ static const u8 wl1271_rate_to_idx_2ghz[] = {
                .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \
                }, \
 }
-#else
-#define WL12XX_HT_CAP { \
-       .ht_supported = false, \
-}
-#endif
 
 /* can't be const, mac80211 writes to this */
 static struct ieee80211_supported_band wl1271_band_2ghz = {
@@ -4201,22 +4625,29 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
                IEEE80211_HW_SUPPORTS_CQM_RSSI |
                IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                IEEE80211_HW_SPECTRUM_MGMT |
-               IEEE80211_HW_AP_LINK_PS;
+               IEEE80211_HW_AP_LINK_PS |
+               IEEE80211_HW_AMPDU_AGGREGATION |
+               IEEE80211_HW_TX_AMPDU_SETUP_IN_HW;
 
        wl->hw->wiphy->cipher_suites = cipher_suites;
        wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
 
        wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
-               BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP);
+               BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) |
+               BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO);
        wl->hw->wiphy->max_scan_ssids = 1;
+       wl->hw->wiphy->max_sched_scan_ssids = 16;
+       wl->hw->wiphy->max_match_sets = 16;
        /*
         * Maximum length of elements in scanning probe request templates
         * should be the maximum length possible for a template, without
         * the IEEE80211 header of the template
         */
-       wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
+       wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_DFLT_SIZE -
                        sizeof(struct ieee80211_header);
 
+       wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+
        /* make sure all our channels fit in the scanned_ch bitmask */
        BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
                     ARRAY_SIZE(wl1271_channels_5ghz) >
@@ -4333,12 +4764,24 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
        wl->sched_scanning = false;
        wl->tx_security_seq = 0;
        wl->tx_security_last_seq_lsb = 0;
-
+       wl->tx_spare_blocks = TX_HW_BLOCK_SPARE_DEFAULT;
+       wl->role_id = WL12XX_INVALID_ROLE_ID;
+       wl->system_hlid = WL12XX_SYSTEM_HLID;
+       wl->sta_hlid = WL12XX_INVALID_LINK_ID;
+       wl->dev_role_id = WL12XX_INVALID_ROLE_ID;
+       wl->dev_hlid = WL12XX_INVALID_LINK_ID;
+       wl->session_counter = 0;
+       wl->ap_bcast_hlid = WL12XX_INVALID_LINK_ID;
+       wl->ap_global_hlid = WL12XX_INVALID_LINK_ID;
+       wl->active_sta_count = 0;
        setup_timer(&wl->rx_streaming_timer, wl1271_rx_streaming_timer,
                    (unsigned long) wl);
        wl->fwlog_size = 0;
        init_waitqueue_head(&wl->fwlog_waitq);
 
+       /* The system link is always allocated */
+       __set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
+
        memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
        for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
                wl->tx_frames[i] = NULL;
@@ -4482,6 +4925,9 @@ module_param_named(fwlog, fwlog_param, charp, 0);
 MODULE_PARM_DESC(keymap,
                 "FW logger options: continuous, ondemand, dbgpins or disable");
 
+module_param(bug_on_recovery, bool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
 MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");