ath10k: implement sta_rc_update()

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>
This commit is contained in:
Michal Kazior 2014-02-14 14:49:48 +01:00 committed by Kalle Valo
parent 90046f509d
commit 9797febc4c
3 changed files with 175 additions and 0 deletions

View File

@ -228,6 +228,18 @@ struct ath10k_peer {
struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; 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) #define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ)
struct ath10k_vif { struct ath10k_vif {

View File

@ -3104,6 +3104,69 @@ exit:
return ret; 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, static int ath10k_sta_state(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct ieee80211_sta *sta,
@ -3112,9 +3175,15 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
{ {
struct ath10k *ar = hw->priv; struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
int max_num_peers; int max_num_peers;
int ret = 0; 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); mutex_lock(&ar->conf_mutex);
if (old_state == IEEE80211_STA_NOTEXIST && 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", "mac vdev %d peer create %pM (new sta) num_peers %d\n",
arvif->vdev_id, sta->addr, ar->num_peers); 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); ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
if (ret) if (ret)
ath10k_warn("Failed to add peer %pM for vdev %d when adding a new sta: %i\n", 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; 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 = { static const struct ieee80211_ops ath10k_ops = {
.tx = ath10k_tx, .tx = ath10k_tx,
.start = ath10k_start, .start = ath10k_start,
@ -3929,6 +4084,7 @@ static const struct ieee80211_ops ath10k_ops = {
.get_survey = ath10k_get_survey, .get_survey = ath10k_get_survey,
.set_bitrate_mask = ath10k_set_bitrate_mask, .set_bitrate_mask = ath10k_set_bitrate_mask,
.channel_switch_beacon = ath10k_channel_switch_beacon, .channel_switch_beacon = ath10k_channel_switch_beacon,
.sta_rc_update = ath10k_sta_rc_update,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.suspend = ath10k_suspend, .suspend = ath10k_suspend,
.resume = ath10k_resume, .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->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN;
ar->hw->vif_data_size = sizeof(struct ath10k_vif); 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; ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;

View File

@ -3876,6 +3876,12 @@ enum wmi_peer_smps_state {
WMI_PEER_SMPS_DYNAMIC = 0x2 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 { enum wmi_peer_param {
WMI_PEER_SMPS_STATE = 0x1, /* see %wmi_peer_smps_state */ WMI_PEER_SMPS_STATE = 0x1, /* see %wmi_peer_smps_state */
WMI_PEER_AMPDU = 0x2, WMI_PEER_AMPDU = 0x2,