wlcore: enable beacon filtering only after receiving a beacon
[deliverable/linux.git] / drivers / net / wireless / ti / wlcore / main.c
index b46b3116cc55c1cf129534af6ecf788996ae0cc0..82d546155f451b3906b82f714f87ef1ff134f094 100644 (file)
@@ -345,24 +345,24 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
         * Start high-level PS if the STA is asleep with enough blocks in FW.
         * Make an exception if this is the only connected link. In this
         * case FW-memory congestion is less of a problem.
-        * Note that a single connected STA means 3 active links, since we must
-        * account for the global and broadcast AP links. The "fw_ps" check
-        * assures us the third link is a STA connected to the AP. Otherwise
-        * the FW would not set the PSM bit.
+        * Note that a single connected STA means 2*ap_count + 1 active links,
+        * since we must account for the global and broadcast AP links
+        * for each AP. The "fw_ps" check assures us the other link is a STA
+        * connected to the AP. Otherwise the FW would not set the PSM bit.
         */
-       else if (wl->active_link_count > 3 && fw_ps &&
+       else if (wl->active_link_count > (wl->ap_count*2 + 1) && fw_ps &&
                 tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
                wl12xx_ps_link_start(wl, wlvif, hlid, true);
 }
 
 static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                                           struct wl12xx_vif *wlvif,
-                                          struct wl_fw_status_2 *status)
+                                          struct wl_fw_status *status)
 {
        u32 cur_fw_ps_map;
        u8 hlid;
 
-       cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
+       cur_fw_ps_map = status->link_ps_bitmap;
        if (wl->ap_fw_ps_map != cur_fw_ps_map) {
                wl1271_debug(DEBUG_PSM,
                             "link ps prev 0x%x cur 0x%x changed 0x%x",
@@ -372,77 +372,73 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                wl->ap_fw_ps_map = cur_fw_ps_map;
        }
 
-       for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS)
+       for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, wl->num_links)
                wl12xx_irq_ps_regulate_link(wl, wlvif, hlid,
                                            wl->links[hlid].allocated_pkts);
 }
 
-static int wlcore_fw_status(struct wl1271 *wl,
-                           struct wl_fw_status_1 *status_1,
-                           struct wl_fw_status_2 *status_2)
+static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status *status)
 {
        struct wl12xx_vif *wlvif;
        struct timespec ts;
        u32 old_tx_blk_count = wl->tx_blocks_available;
        int avail, freed_blocks;
        int i;
-       size_t status_len;
        int ret;
        struct wl1271_link *lnk;
 
-       status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
-               sizeof(*status_2) + wl->fw_status_priv_len;
-
-       ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1,
-                                  status_len, false);
+       ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR,
+                                  wl->raw_fw_status,
+                                  wl->fw_status_len, false);
        if (ret < 0)
                return ret;
 
+       wlcore_hw_convert_fw_status(wl, wl->raw_fw_status, wl->fw_status);
+
        wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
                     "drv_rx_counter = %d, tx_results_counter = %d)",
-                    status_1->intr,
-                    status_1->fw_rx_counter,
-                    status_1->drv_rx_counter,
-                    status_1->tx_results_counter);
+                    status->intr,
+                    status->fw_rx_counter,
+                    status->drv_rx_counter,
+                    status->tx_results_counter);
 
        for (i = 0; i < NUM_TX_QUEUES; i++) {
                /* prevent wrap-around in freed-packets counter */
                wl->tx_allocated_pkts[i] -=
-                               (status_2->counters.tx_released_pkts[i] -
+                               (status->counters.tx_released_pkts[i] -
                                wl->tx_pkts_freed[i]) & 0xff;
 
-               wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i];
+               wl->tx_pkts_freed[i] = status->counters.tx_released_pkts[i];
        }
 
 
-       for_each_set_bit(i, wl->links_map, WL12XX_MAX_LINKS) {
+       for_each_set_bit(i, wl->links_map, wl->num_links) {
                u8 diff;
                lnk = &wl->links[i];
 
                /* prevent wrap-around in freed-packets counter */
-               diff = (status_2->counters.tx_lnk_free_pkts[i] -
+               diff = (status->counters.tx_lnk_free_pkts[i] -
                       lnk->prev_freed_pkts) & 0xff;
 
                if (diff == 0)
                        continue;
 
                lnk->allocated_pkts -= diff;
-               lnk->prev_freed_pkts = status_2->counters.tx_lnk_free_pkts[i];
+               lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[i];
 
                /* accumulate the prev_freed_pkts counter */
                lnk->total_freed_pkts += diff;
        }
 
        /* prevent wrap-around in total blocks counter */
-       if (likely(wl->tx_blocks_freed <=
-                  le32_to_cpu(status_2->total_released_blks)))
-               freed_blocks = le32_to_cpu(status_2->total_released_blks) -
+       if (likely(wl->tx_blocks_freed <= status->total_released_blks))
+               freed_blocks = status->total_released_blks -
                               wl->tx_blocks_freed;
        else
                freed_blocks = 0x100000000LL - wl->tx_blocks_freed +
-                              le32_to_cpu(status_2->total_released_blks);
+                              status->total_released_blks;
 
-       wl->tx_blocks_freed = le32_to_cpu(status_2->total_released_blks);
+       wl->tx_blocks_freed = status->total_released_blks;
 
        wl->tx_allocated_blocks -= freed_blocks;
 
@@ -458,7 +454,7 @@ static int wlcore_fw_status(struct wl1271 *wl,
                        cancel_delayed_work(&wl->tx_watchdog_work);
        }
 
-       avail = le32_to_cpu(status_2->tx_total) - wl->tx_allocated_blocks;
+       avail = status->tx_total - wl->tx_allocated_blocks;
 
        /*
         * The FW might change the total number of TX memblocks before
@@ -477,15 +473,15 @@ static int wlcore_fw_status(struct wl1271 *wl,
 
        /* for AP update num of allocated TX blocks per link and ps status */
        wl12xx_for_each_wlvif_ap(wl, wlvif) {
-               wl12xx_irq_update_links_status(wl, wlvif, status_2);
+               wl12xx_irq_update_links_status(wl, wlvif, status);
        }
 
        /* update the host-chipset time offset */
        getnstimeofday(&ts);
        wl->time_offset = (timespec_to_ns(&ts) >> 10) -
-               (s64)le32_to_cpu(status_2->fw_localtime);
+               (s64)(status->fw_localtime);
 
-       wl->fw_fast_lnk_map = le32_to_cpu(status_2->link_fast_bitmap);
+       wl->fw_fast_lnk_map = status->link_fast_bitmap;
 
        return 0;
 }
@@ -549,13 +545,13 @@ static int wlcore_irq_locked(struct wl1271 *wl)
                clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
                smp_mb__after_clear_bit();
 
-               ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
+               ret = wlcore_fw_status(wl, wl->fw_status);
                if (ret < 0)
                        goto out;
 
                wlcore_hw_tx_immediate_compl(wl);
 
-               intr = le32_to_cpu(wl->fw_status_1->intr);
+               intr = wl->fw_status->intr;
                intr &= WLCORE_ALL_INTR_MASK;
                if (!intr) {
                        done = true;
@@ -584,7 +580,7 @@ static int wlcore_irq_locked(struct wl1271 *wl)
                if (likely(intr & WL1271_ACX_INTR_DATA)) {
                        wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
 
-                       ret = wlcore_rx(wl, wl->fw_status_1);
+                       ret = wlcore_rx(wl, wl->fw_status);
                        if (ret < 0)
                                goto out;
 
@@ -786,10 +782,11 @@ out:
 
 void wl12xx_queue_recovery_work(struct wl1271 *wl)
 {
-       WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
-
        /* Avoid a recursive recovery */
        if (wl->state == WLCORE_STATE_ON) {
+               WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY,
+                                 &wl->flags));
+
                wl->state = WLCORE_STATE_RESTARTING;
                set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
                wl1271_ps_elp_wakeup(wl);
@@ -843,11 +840,11 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
                wl12xx_cmd_stop_fwlog(wl);
 
        /* Read the first memory block address */
-       ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
+       ret = wlcore_fw_status(wl, wl->fw_status);
        if (ret < 0)
                goto out;
 
-       addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
+       addr = wl->fw_status->log_start_addr;
        if (!addr)
                goto out;
 
@@ -990,23 +987,23 @@ static int wlcore_fw_wakeup(struct wl1271 *wl)
 
 static int wl1271_setup(struct wl1271 *wl)
 {
-       wl->fw_status_1 = kzalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
-                                 sizeof(*wl->fw_status_2) +
-                                 wl->fw_status_priv_len, GFP_KERNEL);
-       if (!wl->fw_status_1)
-               return -ENOMEM;
+       wl->raw_fw_status = kzalloc(wl->fw_status_len, GFP_KERNEL);
+       if (!wl->raw_fw_status)
+               goto err;
 
-       wl->fw_status_2 = (struct wl_fw_status_2 *)
-                               (((u8 *) wl->fw_status_1) +
-                               WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc));
+       wl->fw_status = kzalloc(sizeof(*wl->fw_status), GFP_KERNEL);
+       if (!wl->fw_status)
+               goto err;
 
        wl->tx_res_if = kzalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
-       if (!wl->tx_res_if) {
-               kfree(wl->fw_status_1);
-               return -ENOMEM;
-       }
+       if (!wl->tx_res_if)
+               goto err;
 
        return 0;
+err:
+       kfree(wl->fw_status);
+       kfree(wl->raw_fw_status);
+       return -ENOMEM;
 }
 
 static int wl12xx_set_power_on(struct wl1271 *wl)
@@ -1767,6 +1764,12 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
        flush_work(&wl->tx_work);
        flush_delayed_work(&wl->elp_work);
 
+       /*
+        * Cancel the watchdog even if above tx_flush failed. We will detect
+        * it on resume anyway.
+        */
+       cancel_delayed_work(&wl->tx_watchdog_work);
+
        return 0;
 }
 
@@ -1824,6 +1827,13 @@ static int wl1271_op_resume(struct ieee80211_hw *hw)
 
 out:
        wl->wow_enabled = false;
+
+       /*
+        * Set a flag to re-init the watchdog on the first Tx after resume.
+        * That way we avoid possible conditions where Tx-complete interrupts
+        * fail to arrive and we perform a spurious recovery.
+        */
+       set_bit(WL1271_FLAG_REINIT_TX_WDOG, &wl->flags);
        mutex_unlock(&wl->mutex);
 
        return 0;
@@ -1914,6 +1924,7 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
        memset(wl->links_map, 0, sizeof(wl->links_map));
        memset(wl->roc_map, 0, sizeof(wl->roc_map));
        memset(wl->session_ids, 0, sizeof(wl->session_ids));
+       memset(wl->rx_filter_enabled, 0, sizeof(wl->rx_filter_enabled));
        wl->active_sta_count = 0;
        wl->active_link_count = 0;
 
@@ -1938,9 +1949,10 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
 
        wl1271_debugfs_reset(wl);
 
-       kfree(wl->fw_status_1);
-       wl->fw_status_1 = NULL;
-       wl->fw_status_2 = NULL;
+       kfree(wl->raw_fw_status);
+       wl->raw_fw_status = NULL;
+       kfree(wl->fw_status);
+       wl->fw_status = NULL;
        kfree(wl->tx_res_if);
        wl->tx_res_if = NULL;
        kfree(wl->target_mem_map);
@@ -2571,10 +2583,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
                ieee80211_scan_completed(wl->hw, true);
        }
 
-       if (wl->sched_vif == wlvif) {
-               ieee80211_sched_scan_stopped(wl->hw);
+       if (wl->sched_vif == wlvif)
                wl->sched_vif = NULL;
-       }
 
        if (wl->roc_vif == vif) {
                wl->roc_vif = NULL;
@@ -2931,6 +2941,11 @@ static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
                if (ret < 0)
                        return ret;
+
+               /* disable beacon filtering */
+               ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
+               if (ret < 0)
+                       return ret;
        }
 
        if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
@@ -3463,6 +3478,10 @@ static void wl1271_op_set_default_key_idx(struct ieee80211_hw *hw,
        wl1271_debug(DEBUG_MAC80211, "mac80211 set default key idx %d",
                     key_idx);
 
+       /* we don't handle unsetting of default key */
+       if (key_idx == -1)
+               return;
+
        mutex_lock(&wl->mutex);
 
        if (unlikely(wl->state != WLCORE_STATE_ON)) {
@@ -4298,6 +4317,13 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                }
        }
 
+       if ((changed & BSS_CHANGED_BEACON_INFO) && bss_conf->dtim_period) {
+               /* enable beacon filtering */
+               ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
+               if (ret < 0)
+                       goto out;
+       }
+
        ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
        if (ret < 0)
                goto out;
@@ -4651,7 +4677,7 @@ static int wl1271_allocate_sta(struct wl1271 *wl,
        int ret;
 
 
-       if (wl->active_sta_count >= AP_MAX_STATIONS) {
+       if (wl->active_sta_count >= wl->max_ap_stations) {
                wl1271_warning("could not allocate HLID - too much stations");
                return -EBUSY;
        }
@@ -4754,7 +4780,7 @@ static int wl12xx_sta_remove(struct wl1271 *wl,
        if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map)))
                return -EINVAL;
 
-       ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid);
+       ret = wl12xx_cmd_remove_peer(wl, wlvif, wl_sta->hlid);
        if (ret < 0)
                return ret;
 
@@ -5679,28 +5705,6 @@ static void wl1271_unregister_hw(struct wl1271 *wl)
 
 }
 
-static const struct ieee80211_iface_limit wlcore_iface_limits[] = {
-       {
-               .max = 3,
-               .types = BIT(NL80211_IFTYPE_STATION),
-       },
-       {
-               .max = 1,
-               .types = BIT(NL80211_IFTYPE_AP) |
-                        BIT(NL80211_IFTYPE_P2P_GO) |
-                        BIT(NL80211_IFTYPE_P2P_CLIENT),
-       },
-};
-
-static struct ieee80211_iface_combination
-wlcore_iface_combinations[] = {
-       {
-         .max_interfaces = 3,
-         .limits = wlcore_iface_limits,
-         .n_limits = ARRAY_SIZE(wlcore_iface_limits),
-       },
-};
-
 static int wl1271_init_ieee80211(struct wl1271 *wl)
 {
        int i;
@@ -5821,10 +5825,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
                NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
 
        /* allowed interface combinations */
-       wlcore_iface_combinations[0].num_different_channels = wl->num_channels;
-       wl->hw->wiphy->iface_combinations = wlcore_iface_combinations;
-       wl->hw->wiphy->n_iface_combinations =
-               ARRAY_SIZE(wlcore_iface_combinations);
+       wl->hw->wiphy->iface_combinations = wl->iface_combinations;
+       wl->hw->wiphy->n_iface_combinations = wl->n_iface_combinations;
 
        SET_IEEE80211_DEV(wl->hw, wl->dev);
 
@@ -5844,8 +5846,6 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
        int i, j, ret;
        unsigned int order;
 
-       BUILD_BUG_ON(AP_MAX_STATIONS > WL12XX_MAX_LINKS);
-
        hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
        if (!hw) {
                wl1271_error("could not alloc ieee80211_hw");
@@ -5867,8 +5867,12 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
 
        wl->hw = hw;
 
+       /*
+        * wl->num_links is not configured yet, so just use WLCORE_MAX_LINKS.
+        * we don't allocate any additional resource here, so that's fine.
+        */
        for (i = 0; i < NUM_TX_QUEUES; i++)
-               for (j = 0; j < WL12XX_MAX_LINKS; j++)
+               for (j = 0; j < WLCORE_MAX_LINKS; j++)
                        skb_queue_head_init(&wl->links[j].tx_queue[i]);
 
        skb_queue_head_init(&wl->deferred_rx_queue);
@@ -6011,7 +6015,8 @@ int wlcore_free_hw(struct wl1271 *wl)
        kfree(wl->nvs);
        wl->nvs = NULL;
 
-       kfree(wl->fw_status_1);
+       kfree(wl->raw_fw_status);
+       kfree(wl->fw_status);
        kfree(wl->tx_res_if);
        destroy_workqueue(wl->freezable_wq);
 
This page took 0.047054 seconds and 5 git commands to generate.