mac80211: rewrite HT handling
The HT handling has the following deficiencies, which I've
(partially) fixed:
* it always uses the AP info even if there is no AP,
hence has no chance of working as an AP
* it pretends to be HW config, but really is per-BSS
* channel sanity checking is left to the drivers
* it generally lets the driver control too much
HT enabling is still wrong with this patch if you have more than
one virtual STA mode interface, but that never happens currently.
Once WDS, IBSS or AP/VLAN gets HT capabilities, it will also be
wrong, see the comment in ieee80211_enable_ht().
Additionally, this fixes a number of bugs:
* mac80211: ieee80211_set_disassoc doesn't notify the driver any
more since the refactoring
* iwl-agn-rs: always uses the HT capabilities from the wrong stuff
mac80211 gives it rather than the actual peer STA
* ath9k: a number of bugs resulting from the broken HT API
I'm not entirely happy with putting the HT capabilities into
struct ieee80211_sta as restricted to our own HT TX capabilities,
but I see no cleaner solution for now.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/drivers/net/wireless/ath9k/core.h b/drivers/net/wireless/ath9k/core.h
index cb3e61e..59d835b 100644
--- a/drivers/net/wireless/ath9k/core.h
+++ b/drivers/net/wireless/ath9k/core.h
@@ -380,7 +380,6 @@
int ath_rx_tasklet(struct ath_softc *sc, int flush);
int ath_rx_input(struct ath_softc *sc,
struct ath_node *node,
- int is_ampdu,
struct sk_buff *skb,
struct ath_recv_status *rx_status,
enum ATH_RX_TYPE *status);
@@ -650,6 +649,9 @@
u8 an_smmode; /* SM Power save mode */
u8 an_flags;
u8 an_addr[ETH_ALEN];
+
+ u16 maxampdu;
+ u8 mpdudensity;
};
void ath_tx_resume_tid(struct ath_softc *sc,
@@ -919,8 +921,6 @@
struct ath_ht_info {
enum ath9k_ht_macmode tx_chan_width;
- u16 maxampdu;
- u8 mpdudensity;
u8 ext_chan_offset;
};
diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c
index 41cd114..7555c34 100644
--- a/drivers/net/wireless/ath9k/main.c
+++ b/drivers/net/wireless/ath9k/main.c
@@ -330,25 +330,15 @@
{
struct ath_ht_info *ht_info = &sc->sc_ht_info;
- if (bss_conf->assoc_ht) {
- ht_info->ext_chan_offset =
- bss_conf->ht_bss_conf->bss_cap &
- IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
+ if (sc->hw->conf.ht.enabled) {
+ ht_info->ext_chan_offset = bss_conf->ht.secondary_channel_offset;
- if (!(bss_conf->ht_cap->cap &
- IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
- (bss_conf->ht_bss_conf->bss_cap &
- IEEE80211_HT_PARAM_CHAN_WIDTH_ANY))
+ if (bss_conf->ht.width_40_ok)
ht_info->tx_chan_width = ATH9K_HT_MACMODE_2040;
else
ht_info->tx_chan_width = ATH9K_HT_MACMODE_20;
ath9k_hw_set11nmac2040(sc->sc_ah, ht_info->tx_chan_width);
- ht_info->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR +
- bss_conf->ht_cap->ampdu_factor);
- ht_info->mpdudensity =
- parse_mpdudensity(bss_conf->ht_cap->ampdu_density);
-
}
}
@@ -390,7 +380,7 @@
sc->sc_halstats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER;
/* Update chainmask */
- ath_update_chainmask(sc, bss_conf->assoc_ht);
+ ath_update_chainmask(sc, hw->conf.ht.enabled);
DPRINTF(sc, ATH_DBG_CONFIG,
"%s: bssid %pM aid 0x%x\n",
@@ -408,7 +398,7 @@
return;
}
- if (hw->conf.ht_cap.ht_supported)
+ if (hw->conf.ht.enabled)
sc->sc_ah->ah_channels[pos].chanmode =
ath_get_extchanmode(sc, curchan);
else
@@ -531,7 +521,6 @@
if (an) {
ath_rx_input(sc, an,
- hw->conf.ht_cap.ht_supported,
skb, status, &st);
}
if (!an || (st != ATH_RX_CONSUMED))
@@ -1241,6 +1230,9 @@
__func__,
curchan->center_freq);
+ /* Update chainmask */
+ ath_update_chainmask(sc, conf->ht.enabled);
+
pos = ath_get_channel(sc, curchan);
if (pos == -1) {
DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__);
@@ -1251,7 +1243,7 @@
(curchan->band == IEEE80211_BAND_2GHZ) ?
CHANNEL_G : CHANNEL_A;
- if (sc->sc_curaid && hw->conf.ht_cap.ht_supported)
+ if (sc->sc_curaid && hw->conf.ht.enabled)
sc->sc_ah->ah_channels[pos].chanmode =
ath_get_extchanmode(sc, curchan);
@@ -1434,6 +1426,14 @@
} else {
ath_node_get(sc, sta->addr);
}
+
+ /* XXX: Is this right? Can the capabilities change? */
+ an = ath_node_find(sc, sta->addr);
+ an->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR +
+ sta->ht_cap.ampdu_factor);
+ an->mpdudensity =
+ parse_mpdudensity(sta->ht_cap.ampdu_density);
+
spin_unlock_irqrestore(&sc->node_lock, flags);
break;
case STA_NOTIFY_REMOVE:
@@ -1552,9 +1552,8 @@
}
if (changed & BSS_CHANGED_HT) {
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed HT %d\n",
- __func__,
- bss_conf->assoc_ht);
+ DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed HT\n",
+ __func__);
ath9k_ht_conf(sc, bss_conf);
}
diff --git a/drivers/net/wireless/ath9k/rc.c b/drivers/net/wireless/ath9k/rc.c
index ee2dbce..9b25260 100644
--- a/drivers/net/wireless/ath9k/rc.c
+++ b/drivers/net/wireless/ath9k/rc.c
@@ -1838,7 +1838,7 @@
struct ath_softc *sc = hw->priv;
u32 capflag = 0;
- if (hw->conf.ht_cap.ht_supported) {
+ if (hw->conf.ht.enabled) {
capflag |= ATH_RC_HT_FLAG | ATH_RC_DS_FLAG;
if (sc->sc_ht_info.tx_chan_width == ATH9K_HT_MACMODE_2040)
capflag |= ATH_RC_CW40_FLAG;
@@ -1979,7 +1979,7 @@
/* Check if aggregation has to be enabled for this tid */
- if (hw->conf.ht_cap.ht_supported) {
+ if (hw->conf.ht.enabled) {
if (ieee80211_is_data_qos(fc)) {
qc = ieee80211_get_qos_ctl(hdr);
tid = qc[0] & 0xf;
@@ -2026,9 +2026,9 @@
DPRINTF(sc, ATH_DBG_RATE, "%s\n", __func__);
ath_setup_rates(sc, sband, sta, ath_rc_priv);
- if (sc->hw->conf.flags & IEEE80211_CONF_SUPPORT_HT_MODE) {
+ if (sc->hw->conf.ht.enabled) {
for (i = 0; i < 77; i++) {
- if (sc->hw->conf.ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
+ if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
ath_rc_priv->neg_ht_rates.rs_rates[j++] = i;
if (j == ATH_RATE_MAX)
break;
diff --git a/drivers/net/wireless/ath9k/recv.c b/drivers/net/wireless/ath9k/recv.c
index 010fcdf..8283228 100644
--- a/drivers/net/wireless/ath9k/recv.c
+++ b/drivers/net/wireless/ath9k/recv.c
@@ -720,12 +720,11 @@
int ath_rx_input(struct ath_softc *sc,
struct ath_node *an,
- int is_ampdu,
struct sk_buff *skb,
struct ath_recv_status *rx_status,
enum ATH_RX_TYPE *status)
{
- if (is_ampdu && (sc->sc_flags & SC_OP_RXAGGR)) {
+ if (sc->sc_flags & SC_OP_RXAGGR) {
*status = ATH_RX_CONSUMED;
return ath_ampdu_input(sc, an, skb, rx_status);
} else {
diff --git a/drivers/net/wireless/ath9k/xmit.c b/drivers/net/wireless/ath9k/xmit.c
index 3770fbe..ba818cc 100644
--- a/drivers/net/wireless/ath9k/xmit.c
+++ b/drivers/net/wireless/ath9k/xmit.c
@@ -300,7 +300,8 @@
if (ieee80211_is_data(fc) && !txctl->use_minrate) {
/* Enable HT only for DATA frames and not for EAPOL */
- txctl->ht = (hw->conf.ht_cap.ht_supported &&
+ /* XXX why AMPDU only?? */
+ txctl->ht = (hw->conf.ht.enabled &&
(tx_info->flags & IEEE80211_TX_CTL_AMPDU));
if (is_multicast_ether_addr(hdr->addr1)) {
@@ -1450,7 +1451,8 @@
*/
static u32 ath_lookup_rate(struct ath_softc *sc,
- struct ath_buf *bf)
+ struct ath_buf *bf,
+ struct ath_atx_tid *tid)
{
const struct ath9k_rate_table *rt = sc->sc_currates;
struct sk_buff *skb;
@@ -1504,7 +1506,7 @@
* The IE, however can hold upto 65536, which shows up here
* as zero. Ignore 65536 since we are constrained by hw.
*/
- maxampdu = sc->sc_ht_info.maxampdu;
+ maxampdu = tid->an->maxampdu;
if (maxampdu)
aggr_limit = min(aggr_limit, maxampdu);
@@ -1518,6 +1520,7 @@
*/
static int ath_compute_num_delims(struct ath_softc *sc,
+ struct ath_atx_tid *tid,
struct ath_buf *bf,
u16 frmlen)
{
@@ -1545,7 +1548,7 @@
* required minimum length for subframe. Take into account
* whether high rate is 20 or 40Mhz and half or full GI.
*/
- mpdudensity = sc->sc_ht_info.mpdudensity;
+ mpdudensity = tid->an->mpdudensity;
/*
* If there is no mpdu density restriction, no further calculation
@@ -1619,7 +1622,7 @@
}
if (!rl) {
- aggr_limit = ath_lookup_rate(sc, bf);
+ aggr_limit = ath_lookup_rate(sc, bf, tid);
rl = 1;
/*
* Is rate dual stream
@@ -1657,7 +1660,7 @@
* Get the delimiters needed to meet the MPDU
* density for this node.
*/
- ndelim = ath_compute_num_delims(sc, bf_first, bf->bf_frmlen);
+ ndelim = ath_compute_num_delims(sc, tid, bf_first, bf->bf_frmlen);
bpad = PADBYTES(al_delta) + (ndelim << 2);
@@ -2629,7 +2632,7 @@
struct ath_atx_ac *ac;
int tidno, acno;
- sc->sc_ht_info.maxampdu = ATH_AMPDU_LIMIT_DEFAULT;
+ an->maxampdu = ATH_AMPDU_LIMIT_DEFAULT;
/*
* Init per tid tx state