Skip to content

Commit

Permalink
ath9k: Handle multiple keys while setting tx filters
Browse files Browse the repository at this point in the history
The keycache index is used to abort transmission for given station
when it goes to sleep state. But the commit "ath9k_hw: Abort transmission
for sleeping station" is not handling multi-key station. Fix that.

Cc: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Rajkumar Manoharan authored and John W. Linville committed May 22, 2014
1 parent 0986949 commit 4bbf441
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 11 deletions.
1 change: 1 addition & 0 deletions drivers/net/wireless/ath/ath9k/ath9k.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ struct ath_node {
#ifdef CONFIG_ATH9K_STATION_STATISTICS
struct ath_rx_rate_stats rx_rate_stats;
#endif
u8 key_idx[4];
};

struct ath_tx_control {
Expand Down
55 changes: 44 additions & 11 deletions drivers/net/wireless/ath/ath9k/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
an->sc = sc;
an->sta = sta;
an->vif = vif;
memset(&an->key_idx, 0, sizeof(an->key_idx));

ath_tx_node_init(sc, an);
}
Expand Down Expand Up @@ -1461,8 +1462,10 @@ static int ath9k_sta_add(struct ieee80211_hw *hw,
return 0;

key = ath_key_config(common, vif, sta, &ps_key);
if (key > 0)
if (key > 0) {
an->ps_key = key;
an->key_idx[0] = key;
}

return 0;
}
Expand All @@ -1480,6 +1483,7 @@ static void ath9k_del_ps_key(struct ath_softc *sc,

ath_key_delete(common, &ps_key);
an->ps_key = 0;
an->key_idx[0] = 0;
}

static int ath9k_sta_remove(struct ieee80211_hw *hw,
Expand All @@ -1494,6 +1498,19 @@ static int ath9k_sta_remove(struct ieee80211_hw *hw,
return 0;
}

static void ath9k_sta_set_tx_filter(struct ath_hw *ah,
struct ath_node *an,
bool set)
{
int i;

for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) {
if (!an->key_idx[i])
continue;
ath9k_hw_set_tx_filter(ah, an->key_idx[i], set);
}
}

static void ath9k_sta_notify(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
enum sta_notify_cmd cmd,
Expand All @@ -1506,12 +1523,10 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw,
case STA_NOTIFY_SLEEP:
an->sleeping = true;
ath_tx_aggr_sleep(sta, sc, an);
if (an->ps_key > 0)
ath9k_hw_set_tx_filter(sc->sc_ah, an->ps_key, true);
ath9k_sta_set_tx_filter(sc->sc_ah, an, true);
break;
case STA_NOTIFY_AWAKE:
if (an->ps_key > 0)
ath9k_hw_set_tx_filter(sc->sc_ah, an->ps_key, false);
ath9k_sta_set_tx_filter(sc->sc_ah, an, false);
an->sleeping = false;
ath_tx_aggr_wakeup(sc, an);
break;
Expand Down Expand Up @@ -1567,7 +1582,8 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
{
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
int ret = 0;
struct ath_node *an = NULL;
int ret = 0, i;

if (ath9k_modparam_nohwcrypt)
return -ENOSPC;
Expand All @@ -1589,16 +1605,17 @@ static int ath9k_set_key(struct ieee80211_hw *hw,

mutex_lock(&sc->mutex);
ath9k_ps_wakeup(sc);
ath_dbg(common, CONFIG, "Set HW Key\n");
ath_dbg(common, CONFIG, "Set HW Key %d\n", cmd);
if (sta)
an = (struct ath_node *)sta->drv_priv;

switch (cmd) {
case SET_KEY:
if (sta)
ath9k_del_ps_key(sc, vif, sta);

key->hw_key_idx = 0;
ret = ath_key_config(common, vif, sta, key);
if (sta && (ret > 0))
((struct ath_node *)sta->drv_priv)->ps_key = ret;
if (ret >= 0) {
key->hw_key_idx = ret;
/* push IV and Michael MIC generation to stack */
Expand All @@ -1610,11 +1627,27 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
ret = 0;
}
if (an && key->hw_key_idx) {
for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) {
if (an->key_idx[i])
continue;
an->key_idx[i] = key->hw_key_idx;
break;
}
WARN_ON(i == ARRAY_SIZE(an->key_idx));
}
break;
case DISABLE_KEY:
ath_key_delete(common, key);
if (sta)
((struct ath_node *)sta->drv_priv)->ps_key = 0;
if (an) {
for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) {
if (an->key_idx[i] != key->hw_key_idx)
continue;
an->key_idx[i] = 0;
break;
}
}
key->hw_key_idx = 0;
break;
default:
ret = -EINVAL;
Expand Down

0 comments on commit 4bbf441

Please sign in to comment.