wl12xx: AP mode - support hidden SSID
[pandora-kernel.git] / drivers / net / wireless / wl12xx / main.c
index e280d9d..02b5c00 100644 (file)
@@ -236,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,
        },
@@ -267,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,
@@ -359,9 +358,23 @@ static struct conf_drv_settings default_conf = {
                        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);
@@ -755,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) {
@@ -795,18 +826,20 @@ 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 wl12xx_fw_status(struct wl1271 *wl,
                             struct wl12xx_fw_status *status)
@@ -814,6 +847,7 @@ static void wl12xx_fw_status(struct wl1271 *wl,
        struct timespec ts;
        u32 old_tx_blk_count = wl->tx_blocks_available;
        int avail, freed_blocks;
+       int i;
 
        wl1271_raw_read(wl, FW_STATUS_ADDR, status, sizeof(*status), false);
 
@@ -824,8 +858,24 @@ static void wl12xx_fw_status(struct wl1271 *wl,
                     status->drv_rx_counter,
                     status->tx_results_counter);
 
-       freed_blocks = le32_to_cpu(status->total_released_blks) -
-                      wl->tx_blocks_freed;
+       for (i = 0; i < NUM_TX_QUEUES; 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_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;
@@ -848,11 +898,8 @@ static void wl12xx_fw_status(struct wl1271 *wl,
                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) {
-#if 0
-               wl1271_irq_update_links_status(wl, status);
-#endif
-       }
+       if (wl->bss_type == BSS_TYPE_AP_BSS)
+               wl12xx_irq_update_links_status(wl, status);
 
        /* update the host-chipset time offset */
        getnstimeofday(&ts);
@@ -1184,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
@@ -1193,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);
 
@@ -1456,6 +1502,21 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *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]++;
 
        /*
@@ -1468,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!
@@ -1485,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);
@@ -1765,10 +1825,16 @@ static u8 wl12xx_get_role_type(struct wl1271 *wl)
 {
        switch (wl->bss_type) {
        case BSS_TYPE_AP_BSS:
-               return WL1271_ROLE_AP;
+               if (wl->p2p)
+                       return WL1271_ROLE_P2P_GO;
+               else
+                       return WL1271_ROLE_AP;
 
        case BSS_TYPE_STA_BSS:
-               return WL1271_ROLE_STA;
+               if (wl->p2p)
+                       return WL1271_ROLE_P2P_CL;
+               else
+                       return WL1271_ROLE_STA;
 
        case BSS_TYPE_IBSS:
                return WL1271_ROLE_IBSS;
@@ -1790,7 +1856,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
        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) {
@@ -1810,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;
@@ -1819,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;
@@ -1934,7 +2006,7 @@ out:
 static void __wl1271_op_remove_interface(struct wl1271 *wl,
                                         bool reset_tx_queues)
 {
-       int ret;
+       int ret, i;
 
        wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
 
@@ -2014,6 +2086,7 @@ deinit:
        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;
@@ -2027,6 +2100,7 @@ deinit:
        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;
@@ -2037,6 +2111,7 @@ deinit:
        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);
@@ -2050,6 +2125,11 @@ deinit:
 
        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);
 
        kfree(wl->fw_status);
@@ -2157,10 +2237,14 @@ 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)
@@ -2271,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;
 
@@ -2533,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;
@@ -2606,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)) {
@@ -2620,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,
@@ -2964,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)
@@ -3046,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;
@@ -3102,12 +3313,12 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *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)) {
@@ -3124,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;
 }
@@ -3281,19 +3504,6 @@ sta_not_found:
                        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 =
@@ -3368,7 +3578,7 @@ sta_not_found:
                                                                         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)
@@ -3437,14 +3647,29 @@ sta_not_found:
                        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 only after join. */
+       /* 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);
+                       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);
@@ -3453,8 +3678,10 @@ sta_not_found:
                }
                /* handle new association without HT and disassociation */
                else if (changed & BSS_CHANGED_ASSOC) {
-                       ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap,
-                                                            false);
+                       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);
@@ -3463,8 +3690,8 @@ sta_not_found:
                }
        }
 
-       /* Handle HT information change. Only after join. */
-       if (sta_exists && (changed & BSS_CHANGED_HT) &&
+       /* 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);
@@ -3641,32 +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,
@@ -3703,6 +3929,10 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out_sleep;
 
+       ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true, hlid);
+       if (ret < 0)
+               goto out_sleep;
+
 out_sleep:
        wl1271_ps_elp_sleep(wl);
 
@@ -3984,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), \
@@ -3997,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 = {
@@ -4401,14 +4625,19 @@ 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
@@ -4417,6 +4646,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
        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) >
@@ -4533,6 +4764,7 @@ 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;
@@ -4541,6 +4773,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
        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;
@@ -4692,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>");