]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/net/wireless/ath/wcn36xx/main.c
wcn36xx: Delete BSS before idling link
[karo-tx-linux.git] / drivers / net / wireless / ath / wcn36xx / main.c
index 7c169abdbafee95ccfe8f461e670fbb8ab5ca5eb..d71a9a25f5e70cd52893cd0a0e2fc3119ca90df0 100644 (file)
@@ -201,7 +201,21 @@ static const char * const wcn36xx_caps_names[] = {
        "BCN_FILTER",                   /* 19 */
        "RTT",                          /* 20 */
        "RATECTRL",                     /* 21 */
-       "WOW"                           /* 22 */
+       "WOW",                          /* 22 */
+       "WLAN_ROAM_SCAN_OFFLOAD",       /* 23 */
+       "SPECULATIVE_PS_POLL",          /* 24 */
+       "SCAN_SCH",                     /* 25 */
+       "IBSS_HEARTBEAT_OFFLOAD",       /* 26 */
+       "WLAN_SCAN_OFFLOAD",            /* 27 */
+       "WLAN_PERIODIC_TX_PTRN",        /* 28 */
+       "ADVANCE_TDLS",                 /* 29 */
+       "BATCH_SCAN",                   /* 30 */
+       "FW_IN_TX_PATH",                /* 31 */
+       "EXTENDED_NSOFFLOAD_SLOT",      /* 32 */
+       "CH_SWITCH_V1",                 /* 33 */
+       "HT40_OBSS_SCAN",               /* 34 */
+       "UPDATE_CHANNEL_LIST",          /* 35 */
+
 };
 
 static const char *wcn36xx_get_cap_name(enum place_holder_in_cap_bitmap x)
@@ -221,17 +235,6 @@ static void wcn36xx_feat_caps_info(struct wcn36xx *wcn)
        }
 }
 
-static void wcn36xx_detect_chip_version(struct wcn36xx *wcn)
-{
-       if (get_feat_caps(wcn->fw_feat_caps, DOT11AC)) {
-               wcn36xx_info("Chip is 3680\n");
-               wcn->chip_version = WCN36XX_CHIP_3680;
-       } else {
-               wcn36xx_info("Chip is 3660\n");
-               wcn->chip_version = WCN36XX_CHIP_3660;
-       }
-}
-
 static int wcn36xx_start(struct ieee80211_hw *hw)
 {
        struct wcn36xx *wcn = hw->priv;
@@ -286,7 +289,7 @@ static int wcn36xx_start(struct ieee80211_hw *hw)
                        wcn36xx_feat_caps_info(wcn);
        }
 
-       wcn36xx_detect_chip_version(wcn);
+       wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_ENABLE_MC_ADDR_LIST, 1);
 
        /* DMA channel initialization */
        ret = wcn36xx_dxe_init(wcn);
@@ -346,9 +349,7 @@ static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed)
                wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n",
                            ch);
                list_for_each_entry(tmp, &wcn->vif_list, list) {
-                       vif = container_of((void *)tmp,
-                                          struct ieee80211_vif,
-                                          drv_priv);
+                       vif = wcn36xx_priv_to_vif(tmp);
                        wcn36xx_smd_switch_channel(wcn, vif, ch);
                }
        }
@@ -356,15 +357,59 @@ static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed)
        return 0;
 }
 
-#define WCN36XX_SUPPORTED_FILTERS (0)
+#define WCN36XX_SUPPORTED_FILTERS (FIF_ALLMULTI)
 
 static void wcn36xx_configure_filter(struct ieee80211_hw *hw,
                                     unsigned int changed,
                                     unsigned int *total, u64 multicast)
 {
+       struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp;
+       struct wcn36xx *wcn = hw->priv;
+       struct wcn36xx_vif *tmp;
+       struct ieee80211_vif *vif = NULL;
+
        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n");
 
        *total &= WCN36XX_SUPPORTED_FILTERS;
+
+       fp = (void *)(unsigned long)multicast;
+       list_for_each_entry(tmp, &wcn->vif_list, list) {
+               vif = wcn36xx_priv_to_vif(tmp);
+
+               /* FW handles MC filtering only when connected as STA */
+               if (*total & FIF_ALLMULTI)
+                       wcn36xx_smd_set_mc_list(wcn, vif, NULL);
+               else if (NL80211_IFTYPE_STATION == vif->type && tmp->sta_assoc)
+                       wcn36xx_smd_set_mc_list(wcn, vif, fp);
+       }
+       kfree(fp);
+}
+
+static u64 wcn36xx_prepare_multicast(struct ieee80211_hw *hw,
+                                    struct netdev_hw_addr_list *mc_list)
+{
+       struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp;
+       struct netdev_hw_addr *ha;
+
+       wcn36xx_dbg(WCN36XX_DBG_MAC, "mac prepare multicast list\n");
+       fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
+       if (!fp) {
+               wcn36xx_err("Out of memory setting filters.\n");
+               return 0;
+       }
+
+       fp->mc_addr_count = 0;
+       /* update multicast filtering parameters */
+       if (netdev_hw_addr_list_count(mc_list) <=
+           WCN36XX_HAL_MAX_NUM_MULTICAST_ADDRESS) {
+               netdev_hw_addr_list_for_each(ha, mc_list) {
+                       memcpy(fp->mc_addr[fp->mc_addr_count],
+                                       ha->addr, ETH_ALEN);
+                       fp->mc_addr_count++;
+               }
+       }
+
+       return (u64)(unsigned long)fp;
 }
 
 static void wcn36xx_tx(struct ieee80211_hw *hw,
@@ -375,7 +420,7 @@ static void wcn36xx_tx(struct ieee80211_hw *hw,
        struct wcn36xx_sta *sta_priv = NULL;
 
        if (control->sta)
-               sta_priv = (struct wcn36xx_sta *)control->sta->drv_priv;
+               sta_priv = wcn36xx_sta_to_priv(control->sta);
 
        if (wcn36xx_start_tx(wcn, sta_priv, skb))
                ieee80211_free_txskb(wcn->hw, skb);
@@ -387,8 +432,8 @@ static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                           struct ieee80211_key_conf *key_conf)
 {
        struct wcn36xx *wcn = hw->priv;
-       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
-       struct wcn36xx_sta *sta_priv = vif_priv->sta;
+       struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
+       struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
        int ret = 0;
        u8 key[WLAN_MAX_KEY_LEN];
 
@@ -473,6 +518,7 @@ static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                break;
        case DISABLE_KEY:
                if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) {
+                       vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE;
                        wcn36xx_smd_remove_bsskey(wcn,
                                vif_priv->encrypt_type,
                                key_conf->keyidx);
@@ -520,7 +566,7 @@ static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta,
 {
        int i, size;
        u16 *rates_table;
-       struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv;
+       struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
        u32 rates = sta->supp_rates[band];
 
        memset(&sta_priv->supported_rates, 0,
@@ -590,7 +636,7 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
        struct sk_buff *skb = NULL;
        u16 tim_off, tim_len;
        enum wcn36xx_hal_link_state link_state;
-       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
+       struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
 
        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%08x\n",
                    vif, changed);
@@ -620,7 +666,7 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
 
                if (!is_zero_ether_addr(bss_conf->bssid)) {
                        vif_priv->is_joining = true;
-                       vif_priv->bss_index = 0xff;
+                       vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
                        wcn36xx_smd_join(wcn, bss_conf->bssid,
                                         vif->addr, WCN36XX_HW_CHANNEL(wcn));
                        wcn36xx_smd_config_bss(wcn, vif, NULL,
@@ -628,6 +674,7 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
                } else {
                        vif_priv->is_joining = false;
                        wcn36xx_smd_delete_bss(wcn, vif);
+                       vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE;
                }
        }
 
@@ -655,6 +702,7 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
                                     vif->addr,
                                     bss_conf->aid);
 
+                       vif_priv->sta_assoc = true;
                        rcu_read_lock();
                        sta = ieee80211_find_sta(vif, bss_conf->bssid);
                        if (!sta) {
@@ -663,7 +711,7 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
                                rcu_read_unlock();
                                goto out;
                        }
-                       sta_priv = (struct wcn36xx_sta *)sta->drv_priv;
+                       sta_priv = wcn36xx_sta_to_priv(sta);
 
                        wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
 
@@ -686,6 +734,7 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
                                    bss_conf->bssid,
                                    vif->addr,
                                    bss_conf->aid);
+                       vif_priv->sta_assoc = false;
                        wcn36xx_smd_set_link_st(wcn,
                                                bss_conf->bssid,
                                                vif->addr,
@@ -713,7 +762,7 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
 
                if (bss_conf->enable_beacon) {
                        vif_priv->dtim_period = bss_conf->dtim_period;
-                       vif_priv->bss_index = 0xff;
+                       vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
                        wcn36xx_smd_config_bss(wcn, vif, NULL,
                                               vif->addr, false);
                        skb = ieee80211_beacon_get_tim(hw, vif, &tim_off,
@@ -734,9 +783,9 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
                        wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
                                                link_state);
                } else {
+                       wcn36xx_smd_delete_bss(wcn, vif);
                        wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
                                                WCN36XX_HAL_LINK_IDLE_STATE);
-                       wcn36xx_smd_delete_bss(wcn, vif);
                }
        }
 out:
@@ -757,7 +806,7 @@ static void wcn36xx_remove_interface(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif)
 {
        struct wcn36xx *wcn = hw->priv;
-       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
+       struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif);
 
        list_del(&vif_priv->list);
@@ -768,7 +817,7 @@ static int wcn36xx_add_interface(struct ieee80211_hw *hw,
                                 struct ieee80211_vif *vif)
 {
        struct wcn36xx *wcn = hw->priv;
-       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
+       struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
 
        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac add interface vif %p type %d\n",
                    vif, vif->type);
@@ -792,13 +841,12 @@ static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                           struct ieee80211_sta *sta)
 {
        struct wcn36xx *wcn = hw->priv;
-       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
-       struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv;
+       struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
+       struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n",
                    vif, sta->addr);
 
        spin_lock_init(&sta_priv->ampdu_lock);
-       vif_priv->sta = sta_priv;
        sta_priv->vif = vif_priv;
        /*
         * For STA mode HW will be configured on BSS_CHANGED_ASSOC because
@@ -817,14 +865,12 @@ static int wcn36xx_sta_remove(struct ieee80211_hw *hw,
                              struct ieee80211_sta *sta)
 {
        struct wcn36xx *wcn = hw->priv;
-       struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
-       struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv;
+       struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
 
        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n",
                    vif, sta->addr, sta_priv->sta_index);
 
        wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index);
-       vif_priv->sta = NULL;
        sta_priv->vif = NULL;
        return 0;
 }
@@ -862,13 +908,11 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw,
                    u8 buf_size, bool amsdu)
 {
        struct wcn36xx *wcn = hw->priv;
-       struct wcn36xx_sta *sta_priv = NULL;
+       struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
 
        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n",
                    action, tid);
 
-       sta_priv = (struct wcn36xx_sta *)sta->drv_priv;
-
        switch (action) {
        case IEEE80211_AMPDU_RX_START:
                sta_priv->tid = tid;
@@ -921,6 +965,7 @@ static const struct ieee80211_ops wcn36xx_ops = {
        .resume                 = wcn36xx_resume,
 #endif
        .config                 = wcn36xx_config,
+       .prepare_multicast      = wcn36xx_prepare_multicast,
        .configure_filter       = wcn36xx_configure_filter,
        .tx                     = wcn36xx_tx,
        .set_key                = wcn36xx_set_key,
@@ -951,6 +996,10 @@ static int wcn36xx_init_ieee80211(struct wcn36xx *wcn)
        ieee80211_hw_set(wcn->hw, SIGNAL_DBM);
        ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL);
 
+       /* 3620 powersaving currently unstable */
+       if (wcn->chip_version == WCN36XX_CHIP_3620)
+               __clear_bit(IEEE80211_HW_SUPPORTS_PS, wcn->hw->flags);
+
        wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
                BIT(NL80211_IFTYPE_AP) |
                BIT(NL80211_IFTYPE_ADHOC) |
@@ -964,6 +1013,9 @@ static int wcn36xx_init_ieee80211(struct wcn36xx *wcn)
 
        wcn->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
 
+       /* TODO: Figure out why this is necessary */
+       wcn->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
 #ifdef CONFIG_PM
        wcn->hw->wiphy->wowlan = &wowlan_support;
 #endif
@@ -1036,11 +1088,25 @@ static int wcn36xx_probe(struct platform_device *pdev)
        wcn = hw->priv;
        wcn->hw = hw;
        wcn->dev = &pdev->dev;
-       wcn->ctrl_ops = pdev->dev.platform_data;
+       wcn->dev->dma_mask = kzalloc(sizeof(*wcn->dev->dma_mask), GFP_KERNEL);
+       if (!wcn->dev->dma_mask) {
+               ret = -ENOMEM;
+               goto dma_mask_err;
+       }
+       dma_set_mask_and_coherent(wcn->dev, DMA_BIT_MASK(32));
+       wcn->wcn36xx_data = pdev->dev.platform_data;
+       wcn->ctrl_ops = &wcn->wcn36xx_data->ctrl_ops;
+       wcn->wcn36xx_data->wcn = wcn;
+       if (!wcn->ctrl_ops->get_chip_type) {
+               dev_err(&pdev->dev, "Missing ops->get_chip_type\n");
+               ret = -EINVAL;
+               goto out_wq;
+       }
+       wcn->chip_version = wcn->ctrl_ops->get_chip_type(wcn);
 
        mutex_init(&wcn->hal_mutex);
 
-       if (!wcn->ctrl_ops->get_hw_mac(addr)) {
+       if (!wcn->ctrl_ops->get_hw_mac(wcn, addr)) {
                wcn36xx_info("mac address: %pM\n", addr);
                SET_IEEE80211_PERM_ADDR(wcn->hw, addr);
        }
@@ -1059,6 +1125,8 @@ static int wcn36xx_probe(struct platform_device *pdev)
 out_unmap:
        iounmap(wcn->mmio);
 out_wq:
+       kfree(wcn->dev->dma_mask);
+dma_mask_err:
        ieee80211_free_hw(hw);
 out_err:
        return ret;