ath10k: implement sta_rc_update()
authorMichal Kazior <michal.kazior@tieto.com>
Fri, 14 Feb 2014 13:49:48 +0000 (14:49 +0100)
committerKalle Valo <kvalo@qca.qualcomm.com>
Sat, 15 Feb 2014 06:49:02 +0000 (08:49 +0200)
This allows dynamic changes of bandwidth/nss/smps,
e.g. via ht/vht operation mode change
notification.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/wmi.h

index fae53f909550ae383ad44c127de1942c8703bfdf..1fc26fe057e80bd760d0ef9b6a132679353b41be 100644 (file)
@@ -228,6 +228,18 @@ struct ath10k_peer {
        struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
 };
 
+struct ath10k_sta {
+       struct ath10k_vif *arvif;
+
+       /* the following are protected by ar->data_lock */
+       u32 changed; /* IEEE80211_RC_* */
+       u32 bw;
+       u32 nss;
+       u32 smps;
+
+       struct work_struct update_wk;
+};
+
 #define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ)
 
 struct ath10k_vif {
index c2aaecb4c25bfeb46a2776639d24e32be3ea4583..e17f5d732b5abf099eeaa590bca0be746aed073e 100644 (file)
@@ -3104,6 +3104,69 @@ exit:
        return ret;
 }
 
+static void ath10k_sta_rc_update_wk(struct work_struct *wk)
+{
+       struct ath10k *ar;
+       struct ath10k_vif *arvif;
+       struct ath10k_sta *arsta;
+       struct ieee80211_sta *sta;
+       u32 changed, bw, nss, smps;
+       int err;
+
+       arsta = container_of(wk, struct ath10k_sta, update_wk);
+       sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv);
+       arvif = arsta->arvif;
+       ar = arvif->ar;
+
+       spin_lock_bh(&ar->data_lock);
+
+       changed = arsta->changed;
+       arsta->changed = 0;
+
+       bw = arsta->bw;
+       nss = arsta->nss;
+       smps = arsta->smps;
+
+       spin_unlock_bh(&ar->data_lock);
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (changed & IEEE80211_RC_BW_CHANGED) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM peer bw %d\n",
+                          sta->addr, bw);
+
+               err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+                                               WMI_PEER_CHAN_WIDTH, bw);
+               if (err)
+                       ath10k_warn("failed to update STA %pM peer bw %d: %d\n",
+                                   sta->addr, bw, err);
+       }
+
+       if (changed & IEEE80211_RC_NSS_CHANGED) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM nss %d\n",
+                          sta->addr, nss);
+
+               err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+                                               WMI_PEER_NSS, nss);
+               if (err)
+                       ath10k_warn("failed to update STA %pM nss %d: %d\n",
+                                   sta->addr, nss, err);
+       }
+
+       if (changed & IEEE80211_RC_SMPS_CHANGED) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM smps %d\n",
+                          sta->addr, smps);
+
+               err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+                                               WMI_PEER_SMPS_STATE, smps);
+               if (err)
+                       ath10k_warn("failed to update STA %pM smps %d: %d\n",
+                                   sta->addr, smps, err);
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
 static int ath10k_sta_state(struct ieee80211_hw *hw,
                            struct ieee80211_vif *vif,
                            struct ieee80211_sta *sta,
@@ -3112,9 +3175,15 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
 {
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
        int max_num_peers;
        int ret = 0;
 
+       /* cancel must be done outside the mutex to avoid deadlock */
+       if ((old_state == IEEE80211_STA_NONE &&
+            new_state == IEEE80211_STA_NOTEXIST))
+               cancel_work_sync(&arsta->update_wk);
+
        mutex_lock(&ar->conf_mutex);
 
        if (old_state == IEEE80211_STA_NOTEXIST &&
@@ -3139,6 +3208,10 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                           "mac vdev %d peer create %pM (new sta) num_peers %d\n",
                           arvif->vdev_id, sta->addr, ar->num_peers);
 
+               memset(arsta, 0, sizeof(*arsta));
+               arsta->arvif = arvif;
+               INIT_WORK(&arsta->update_wk, ath10k_sta_rc_update_wk);
+
                ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
                if (ret)
                        ath10k_warn("Failed to add peer %pM for vdev %d when adding a new sta: %i\n",
@@ -3905,6 +3978,88 @@ static void ath10k_channel_switch_beacon(struct ieee80211_hw *hw,
        return;
 }
 
+static void ath10k_sta_rc_update(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif,
+                                struct ieee80211_sta *sta,
+                                u32 changed)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+       u32 bw, smps;
+
+       spin_lock_bh(&ar->data_lock);
+
+       ath10k_dbg(ATH10K_DBG_MAC,
+                  "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n",
+                  sta->addr, changed, sta->bandwidth, sta->rx_nss,
+                  sta->smps_mode);
+
+       if (changed & IEEE80211_RC_BW_CHANGED) {
+               bw = WMI_PEER_CHWIDTH_20MHZ;
+
+               switch (sta->bandwidth) {
+               case IEEE80211_STA_RX_BW_20:
+                       bw = WMI_PEER_CHWIDTH_20MHZ;
+                       break;
+               case IEEE80211_STA_RX_BW_40:
+                       bw = WMI_PEER_CHWIDTH_40MHZ;
+                       break;
+               case IEEE80211_STA_RX_BW_80:
+                       bw = WMI_PEER_CHWIDTH_80MHZ;
+                       break;
+               case IEEE80211_STA_RX_BW_160:
+                       ath10k_warn("mac sta rc update for %pM: invalid bw %d\n",
+                                   sta->addr, sta->bandwidth);
+                       bw = WMI_PEER_CHWIDTH_20MHZ;
+                       break;
+               }
+
+               arsta->bw = bw;
+       }
+
+       if (changed & IEEE80211_RC_NSS_CHANGED)
+               arsta->nss = sta->rx_nss;
+
+       if (changed & IEEE80211_RC_SMPS_CHANGED) {
+               smps = WMI_PEER_SMPS_PS_NONE;
+
+               switch (sta->smps_mode) {
+               case IEEE80211_SMPS_AUTOMATIC:
+               case IEEE80211_SMPS_OFF:
+                       smps = WMI_PEER_SMPS_PS_NONE;
+                       break;
+               case IEEE80211_SMPS_STATIC:
+                       smps = WMI_PEER_SMPS_STATIC;
+                       break;
+               case IEEE80211_SMPS_DYNAMIC:
+                       smps = WMI_PEER_SMPS_DYNAMIC;
+                       break;
+               case IEEE80211_SMPS_NUM_MODES:
+                       ath10k_warn("mac sta rc update for %pM: invalid smps: %d\n",
+                                   sta->addr, sta->smps_mode);
+                       smps = WMI_PEER_SMPS_PS_NONE;
+                       break;
+               }
+
+               arsta->smps = smps;
+       }
+
+       if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
+               /* FIXME: Not implemented. Probably the only way to do it would
+                * be to re-assoc the peer. */
+               changed &= ~IEEE80211_RC_SUPP_RATES_CHANGED;
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac sta rc update for %pM: changing supported rates not implemented\n",
+                          sta->addr);
+       }
+
+       arsta->changed |= changed;
+
+       spin_unlock_bh(&ar->data_lock);
+
+       ieee80211_queue_work(hw, &arsta->update_wk);
+}
+
 static const struct ieee80211_ops ath10k_ops = {
        .tx                             = ath10k_tx,
        .start                          = ath10k_start,
@@ -3929,6 +4084,7 @@ static const struct ieee80211_ops ath10k_ops = {
        .get_survey                     = ath10k_get_survey,
        .set_bitrate_mask               = ath10k_set_bitrate_mask,
        .channel_switch_beacon          = ath10k_channel_switch_beacon,
+       .sta_rc_update                  = ath10k_sta_rc_update,
 #ifdef CONFIG_PM
        .suspend                        = ath10k_suspend,
        .resume                         = ath10k_resume,
@@ -4304,6 +4460,7 @@ int ath10k_mac_register(struct ath10k *ar)
        ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN;
 
        ar->hw->vif_data_size = sizeof(struct ath10k_vif);
+       ar->hw->sta_data_size = sizeof(struct ath10k_sta);
 
        ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
 
index fc1093a51ab13268170b7408887703770f9b381a..4fcc96aa9513b89a45e11fd9c2d8da79414204c9 100644 (file)
@@ -3876,6 +3876,12 @@ enum wmi_peer_smps_state {
        WMI_PEER_SMPS_DYNAMIC = 0x2
 };
 
+enum wmi_peer_chwidth {
+       WMI_PEER_CHWIDTH_20MHZ = 0,
+       WMI_PEER_CHWIDTH_40MHZ = 1,
+       WMI_PEER_CHWIDTH_80MHZ = 2,
+};
+
 enum wmi_peer_param {
        WMI_PEER_SMPS_STATE = 0x1, /* see %wmi_peer_smps_state */
        WMI_PEER_AMPDU      = 0x2,