summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>2011-06-06 11:20:24 +0200
committerPhilippe LANGLAIS <philippe.langlais@stericsson.com>2011-06-22 11:31:24 +0200
commit580fcd99299c64daf40293b27a1308b6b8070473 (patch)
tree1de3e3749f5e81bcaa9e9656550d7737d83f80e9
parent010113999d1c0db44b9bdd44b325914dc4d14645 (diff)
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 <dmitry.tarnyagin@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24480 Reviewed-by: Robert MARKLUND <robert.marklund@stericsson.com> Tested-by: Robert MARKLUND <robert.marklund@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25620 Reviewed-by: Philippe LANGLAIS <philippe.langlais@stericsson.com>
-rw-r--r--drivers/staging/cw1200/Kconfig9
-rw-r--r--drivers/staging/cw1200/ap.c32
-rw-r--r--drivers/staging/cw1200/cw1200.h4
-rw-r--r--drivers/staging/cw1200/main.c37
-rw-r--r--drivers/staging/cw1200/sta.c8
-rw-r--r--drivers/staging/cw1200/txrx.c131
6 files changed, 138 insertions, 83 deletions
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. */