From 580fcd99299c64daf40293b27a1308b6b8070473 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Mon, 6 Jun 2011 11:20:24 +0200 Subject: cw1200: 11n verification and bugfixing. * MCS rate indexes were wrongly interpreted by the driver as legacy rate offsets in both TX and RX directions. * HT rates have not been marked as MCS rates in rate description table. * Driver should not expose AMPDU aggregation capability to the mac80211 stack, aggrehation is fully controlled by the firmware. Firmware takes care of block ACK negotiation. * Block ACK action frames are filtered by driver: mac80211 layer is not involved into BA dialog. * Block ACK in TX direction is enabled. * Block ACK in SoftAP mode is enabled. * RX'ed frames should not be marked as "aggregated" for the mac80211 stack, it confuses rate control algorithm quite a lot. * CONFIG_CW1200_HT_SUPPORT option is removed: drivers always supports HT. TODO: - Modify minstrel rate policy "distillation" to prioritize higher bitrates. - Verify greenfield mode. Change-Id: I9288a2b99984785ae97d85de98ea79d3a49ea64f Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24480 Reviewed-by: Robert MARKLUND Tested-by: Robert MARKLUND Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25620 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/Kconfig | 9 --- drivers/staging/cw1200/ap.c | 32 +++------- drivers/staging/cw1200/cw1200.h | 4 +- drivers/staging/cw1200/main.c | 37 +++++------- drivers/staging/cw1200/sta.c | 8 +-- drivers/staging/cw1200/txrx.c | 131 +++++++++++++++++++++++++++++++++------- 6 files changed, 138 insertions(+), 83 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig index ed2d23e5d47..8be1b909f6d 100644 --- a/drivers/staging/cw1200/Kconfig +++ b/drivers/staging/cw1200/Kconfig @@ -38,15 +38,6 @@ config CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE If unsure, say N. -config CW1200_HT_SUPPORT - bool "11n support (DEVELOPMENT)" - depends on CW1200 - help - Say Y if you want to enable 11n support in the driver. - Note that 11n support is not 100% verified, status is unknown. - - If unsure, say N. - menu "Driver debug features" depends on CW1200 diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 7fa863c6f20..561dab3f94f 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -427,31 +427,11 @@ int cw1200_ampdu_action(struct ieee80211_hw *hw, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn) { - int ret = 0; - - switch (action) { - /* - * The hw itself takes care of setting up BlockAck mechanisms. - * So, we only have to allow mac80211 to nagotiate a BlockAck - * agreement. Once that is done, the hw will BlockAck incoming - * AMPDUs without further setup. - */ - case IEEE80211_AMPDU_RX_START: - case IEEE80211_AMPDU_RX_STOP: - break; - case IEEE80211_AMPDU_TX_START: - ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - case IEEE80211_AMPDU_TX_STOP: - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - break; - default: - ret = -ENOTSUPP; - } - - return ret; + /* Aggregation is implemented fully in firmware, + * including block ack negotiation. Do not allow + * mac80211 stack to do anything: it interferes with + * the firmware. */ + return -ENOTSUPP; } /* ******************************************************************** */ @@ -606,6 +586,8 @@ static int cw1200_start_ap(struct cw1200_common *priv) if (!ret) ret = WARN_ON(wsm_beacon_transmit(priv, &transmit)); if (!ret) { + WARN_ON(wsm_set_block_ack_policy(priv, + priv->ba_tid_mask, priv->ba_tid_mask)); priv->join_status = CW1200_JOIN_STATUS_AP; cw1200_update_filtering(priv); } diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 0c88bfa48da..cd5061d1097 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -81,7 +81,8 @@ struct cw1200_common { /* calibration, output power limit and rssi<->dBm conversation data */ /* BBP/MAC state */ - struct ieee80211_rate *rates; + struct ieee80211_rate *rates; + struct ieee80211_rate *mcs_rates; u8 mac_addr[ETH_ALEN]; struct ieee80211_channel *channel; u8 bssid[ETH_ALEN]; @@ -107,6 +108,7 @@ struct cw1200_common { bool listening; struct wsm_rx_filter rx_filter; struct wsm_beacon_filter_control bf_control; + u8 ba_tid_mask; /* BH */ atomic_t bh_rx; diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 0a1193b8b41..e22fbf2e22e 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -71,22 +71,22 @@ static struct ieee80211_rate cw1200_rates[] = { RATETAB_ENT(360, 11, 0), RATETAB_ENT(480, 12, 0), RATETAB_ENT(540, 13, 0), -#if defined(CONFIG_CW1200_HT_SUPPORT) - RATETAB_ENT(65, 14, 0), - RATETAB_ENT(130, 15, 0), - RATETAB_ENT(195, 16, 0), - RATETAB_ENT(260, 17, 0), - RATETAB_ENT(390, 18, 0), - RATETAB_ENT(520, 19, 0), - RATETAB_ENT(585, 20, 0), - RATETAB_ENT(650, 21, 0), -#endif /* CONFIG_CW1200_HT_SUPPORT */ + RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS), + RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS), + RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS), + RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS), + RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS), + RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS), + RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS), + RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS), }; #define cw1200_a_rates (cw1200_rates + 4) #define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4) #define cw1200_g_rates (cw1200_rates + 0) #define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates)) +#define cw1200_n_rates (cw1200_rates + 12) +#define cw1200_n_rates_size (ARRAY_SIZE(cw1200_rates) - 12) #define CHAN2G(_channel, _freq, _flags) { \ @@ -151,7 +151,6 @@ static struct ieee80211_supported_band cw1200_band_2ghz = { .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable), .bitrates = cw1200_g_rates, .n_bitrates = cw1200_g_rates_size, -#if defined(CONFIG_CW1200_HT_SUPPORT) .ht_cap = { .cap = IEEE80211_HT_CAP_SM_PS | IEEE80211_HT_CAP_GRN_FLD | @@ -160,8 +159,6 @@ static struct ieee80211_supported_band cw1200_band_2ghz = { IEEE80211_HT_CAP_DELAY_BA | IEEE80211_HT_CAP_MAX_AMSDU, .ht_supported = 1, - /* TODO: It was 4K for cut 1.1 HW, if I remember - * it correctly. Needs to be verified on cut 2 HW. */ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, .mcs = { @@ -170,7 +167,6 @@ static struct ieee80211_supported_band cw1200_band_2ghz = { .tx_params = IEEE80211_HT_MCS_TX_DEFINED, }, }, -#endif /* CONFIG_CW1200_HT_SUPPORT */ }; static struct ieee80211_supported_band cw1200_band_5ghz = { @@ -178,7 +174,6 @@ static struct ieee80211_supported_band cw1200_band_5ghz = { .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable), .bitrates = cw1200_a_rates, .n_bitrates = cw1200_a_rates_size, -#if defined(CONFIG_CW1200_HT_SUPPORT) .ht_cap = { .cap = IEEE80211_HT_CAP_SM_PS | IEEE80211_HT_CAP_GRN_FLD | @@ -187,8 +182,6 @@ static struct ieee80211_supported_band cw1200_band_5ghz = { IEEE80211_HT_CAP_DELAY_BA | IEEE80211_HT_CAP_MAX_AMSDU, .ht_supported = 1, - /* TODO: It was 4K for cut 1.1 HW, if I remember - * it correctly. Needs to be verified on cut 2 HW. */ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, .mcs = { @@ -197,7 +190,6 @@ static struct ieee80211_supported_band cw1200_band_5ghz = { .tx_params = IEEE80211_HT_MCS_TX_DEFINED, }, }, -#endif /* CONFIG_CW1200_HT_SUPPORT */ }; static const struct ieee80211_ops cw1200_ops = { @@ -240,15 +232,18 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) priv->hw = hw; priv->mode = NL80211_IFTYPE_UNSPECIFIED; priv->rates = cw1200_rates; /* TODO: fetch from FW */ + priv->mcs_rates = cw1200_n_rates; + /* Enable block ACK for every TID but voice. */ + priv->ba_tid_mask = 0x3F; hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | /* IEEE80211_HW_SUPPORTS_UAPSD | */ IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | -#if defined(CONFIG_CW1200_HT_SUPPORT) - IEEE80211_HW_AMPDU_AGGREGATION | -#endif + /* Aggregation is fully controlled by firmware. + * Do not need any support from the mac80211 stack */ + /* IEEE80211_HW_AMPDU_AGGREGATION | */ #if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) IEEE80211_HW_SUPPORTS_CQM_BEACON_MISS | IEEE80211_HW_SUPPORTS_CQM_TX_FAIL | diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 60a3c57100b..65df33ac61f 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -972,12 +972,8 @@ void cw1200_join_work(struct work_struct *work) wsm_flush_tx(priv); - /* TX block_ack can use only 3 TX buffers, - * which is /slightly/ :) unefficient => disabled. - * RX block ACK is enabled for everything but voice. - * TODO: Verify video lags, change 0x3F -> 0x0F - * if necessary. */ - WARN_ON(wsm_set_block_ack_policy(priv, 0x00, 0x3F)); + WARN_ON(wsm_set_block_ack_policy(priv, + priv->ba_tid_mask, priv->ba_tid_mask)); #if defined(CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) priv->last_activity_time = jiffies; diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 005654eabe8..16a85b39b99 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -22,6 +22,14 @@ #define tx_policy_printk(...) #endif +static int cw1200_handle_action_rx(struct cw1200_common *priv, + struct sk_buff *skb); +static int cw1200_handle_action_tx(struct cw1200_common *priv, + struct sk_buff *skb); +static const struct ieee80211_rate * +cw1200_get_tx_rate(const struct cw1200_common *priv, + const struct ieee80211_tx_rate *rate); + /* ******************************************************************** */ /* TX queue lock / unlock */ @@ -67,8 +75,7 @@ static void tx_policy_build(const struct cw1200_common *priv, /* [out] */ struct tx_policy *policy, struct ieee80211_tx_rate *rates, size_t count) { - int i; - const struct ieee80211_rate *rates_tbl = priv->rates; + int i, j; unsigned limit = priv->short_frame_max_tx_count; unsigned total = 0; BUG_ON(rates[0].idx < 0); @@ -76,22 +83,34 @@ static void tx_policy_build(const struct cw1200_common *priv, /* minstrel is buggy a little bit, so distille * incoming rates first. */ - for (i = 0; i < count; ++i) { - if (rates[i].idx < 0) - break; - /* minstrel is buggy a little bit. */ - if (i && (rates[i].idx == rates[i - 1].idx)) { - rates[i - 1].count += rates[i].count; + for (i = 1; i < count; ++i) { + if (rates[i].idx < 0) { + count = i; break; } - total += rates[i].count; - if (i && (rates[i].idx > rates[i - 1].idx)) { + if (rates[i].idx > rates[i - 1].idx) { struct ieee80211_tx_rate tmp = rates[i - 1]; rates[i - 1] = rates[i]; rates[i] = tmp; } } - count = i; + + total = rates[0].count; + for (i = 0, j = 1; j < count; ++j) { + if (rates[j].idx == rates[i].idx) { + rates[i].count += rates[j].count; + } else if (rates[j].idx > rates[i].idx) { + break; + } else { + ++i; + if (i != j) + rates[i] = rates[j]; + } + total += rates[j].count; + } + if (i + 1 < count) + count = i + 1; + if (limit < count) limit = count; @@ -102,12 +121,12 @@ static void tx_policy_build(const struct cw1200_common *priv, limit -= rates[i].count; } } - policy->defined = rates_tbl[rates[0].idx].hw_value + 1; + policy->defined = cw1200_get_tx_rate(priv, &rates[0])->hw_value + 1; for (i = 0; i < count; ++i) { register unsigned rateid, off, shift, retries; - rateid = rates_tbl[rates[i].idx].hw_value; + rateid = cw1200_get_tx_rate(priv, &rates[i])->hw_value; off = rateid >> 3; /* eq. rateid / 8 */ shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */ @@ -331,13 +350,26 @@ u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates) return ret; } +static const struct ieee80211_rate * +cw1200_get_tx_rate(const struct cw1200_common *priv, + const struct ieee80211_tx_rate *rate) +{ + if (rate->idx < 0) + return NULL; + if (rate->flags & IEEE80211_TX_RC_MCS) + return &priv->mcs_rates[rate->idx]; + return &priv->hw->wiphy->bands[priv->channel->band]-> + bitrates[rate->idx]; +} + /* NOTE: cw1200_skb_to_wsm executes in atomic context. */ int cw1200_skb_to_wsm(struct cw1200_common *priv, struct sk_buff *skb, struct wsm_tx *wsm) { bool tx_policy_renew = false; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_rate *rate = ieee80211_get_tx_rate(priv->hw, tx_info); + const struct ieee80211_rate *rate = cw1200_get_tx_rate(priv, + &tx_info->control.rates[0]); memset(wsm, 0, sizeof(*wsm)); wsm->hdr.len = __cpu_to_le16(skb->len); @@ -467,6 +499,10 @@ int cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) skb_trim(skb, skb->len - offset); } + if (ieee80211_is_action(hdr->frame_control)) + if (cw1200_handle_action_tx(priv, skb)) + goto drop; + ret = cw1200_queue_put(&priv->tx_queue[queue], priv, skb, link_id); if (!WARN_ON(ret)) @@ -480,12 +516,42 @@ err: /* TODO: Update TX failure counters */ dev_kfree_skb_any(skb); return NETDEV_TX_OK; + +drop: + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +/* ******************************************************************** */ + +static int cw1200_handle_action_rx(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + + /* Filter block ACK negotiation: fully controlled by firmware */ + if (mgmt->u.action.category == WLAN_CATEGORY_BACK) + return 1; + + return 0; +} + +static int cw1200_handle_action_tx(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + + /* Filter block ACK negotiation: fully controlled by firmware */ + if (mgmt->u.action.category == WLAN_CATEGORY_BACK) + return 1; + + return 0; } /* ******************************************************************** */ void cw1200_tx_confirm_cb(struct cw1200_common *priv, - struct wsm_tx_confirm *arg) + struct wsm_tx_confirm *arg) { u8 queue_id = cw1200_queue_get_queue_id(arg->packetID); struct cw1200_queue *queue = &priv->tx_queue[queue_id]; @@ -527,7 +593,9 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, ++tx_count; cw1200_debug_txed(priv); if (arg->flags & WSM_TX_STATUS_AGGREGATION) { - tx->flags |= IEEE80211_TX_STAT_AMPDU; + /* Do not report aggregation to mac80211: + * it confuses minstrel a lot. */ + /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */ cw1200_debug_txed_agg(priv); } } else { @@ -569,6 +637,8 @@ void cw1200_rx_cb(struct cw1200_common *priv, { struct sk_buff *skb = *skb_p; struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); + const struct ieee80211_rate *rate; + __le16 frame_control; hdr->flag = 0; if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { @@ -590,13 +660,29 @@ void cw1200_rx_cb(struct cw1200_common *priv, } } + if (skb->len < sizeof(struct ieee80211_hdr_3addr)) { + wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed.\n"); + return; + } + + frame_control = *(__le16*)skb->data; hdr->mactime = 0; /* Not supported by WSM */ hdr->freq = ieee80211_channel_to_frequency(arg->channelNumber); hdr->band = (hdr->freq >= 5000) ? IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; - hdr->rate_idx = arg->rxedRate; - if (hdr->rate_idx >= 4) /* TODO: Use common convert function. */ - hdr->rate_idx -= 2; + + if (arg->rxedRate >= 4) + rate = &priv->rates[arg->rxedRate - 2]; + else + rate = &priv->rates[arg->rxedRate]; + + if (rate >= priv->mcs_rates) { + hdr->rate_idx = rate - priv->mcs_rates; + hdr->flag |= RX_FLAG_HT; + } else { + hdr->rate_idx = rate - priv->rates; + } + hdr->signal = (s8)arg->rcpiRssi; hdr->antenna = 0; @@ -609,13 +695,16 @@ void cw1200_rx_cb(struct cw1200_common *priv, skb_trim(skb, skb->len - 8 /*MICHAEL_MIC_LEN*/); } } - if (arg->flags & WSM_RX_STATUS_HT) - hdr->flag |= RX_FLAG_HT; cw1200_debug_rxed(priv); if (arg->flags & WSM_RX_STATUS_AGGREGATE) cw1200_debug_rxed_agg(priv); + if (ieee80211_is_action(frame_control) && + (arg->flags & WSM_RX_STATUS_ADDRESS1)) + if (cw1200_handle_action_rx(priv, skb)) + return; + /* Not that we really need _irqsafe variant here, * but it offloads realtime bh thread and improve * system performance. */ -- cgit v1.2.3