diff options
Diffstat (limited to 'drivers/net/wireless/ti/wlcore/main.c')
-rw-r--r-- | drivers/net/wireless/ti/wlcore/main.c | 200 |
1 files changed, 104 insertions, 96 deletions
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index b46b3116cc5..ed88d391348 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -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); @@ -803,7 +800,7 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen) size_t len; /* Make sure we have enough room */ - len = min(maxlen, (size_t)(PAGE_SIZE - wl->fwlog_size)); + len = min_t(size_t, maxlen, PAGE_SIZE - wl->fwlog_size); /* Fill the FW log file, consumed by the sysfs fwlog entry */ memcpy(wl->fwlog + wl->fwlog_size, memblock, len); @@ -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)) { @@ -3649,8 +3668,8 @@ out: return ret; } -static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static int wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); @@ -3672,6 +3691,8 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); + + return 0; } static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) @@ -4298,6 +4319,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 +4679,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 +4782,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 +5707,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; @@ -5733,7 +5739,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_AP_LINK_PS | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_TX_AMPDU_SETUP_IN_HW | - IEEE80211_HW_QUEUE_CONTROL; + IEEE80211_HW_QUEUE_CONTROL | + IEEE80211_HW_CHANCTX_STA_CSA; wl->hw->wiphy->cipher_suites = cipher_suites; wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); @@ -5821,10 +5828,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 +5849,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 +5870,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 +6018,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); |