2010-11-10 12:23:55

by Vivek Natarajan

[permalink] [raw]
Subject: [RFC 1/5] mac80211: Add support for transmit beam forming.

Enable beamforming if the driver and the AP are capable of sending
and receiving beam-formed frames.

Signed-off-by: Vivek Natarajan <[email protected]>
---
include/linux/ieee80211.h | 41 ++++++++++++++++++++++++++++++++++++++++-
include/net/cfg80211.h | 6 ++++++
include/net/mac80211.h | 9 +++++++--
net/mac80211/cfg.c | 7 +++++++
net/mac80211/ht.c | 18 ++++++++++++++++++
net/mac80211/ieee80211_i.h | 1 +
net/mac80211/mlme.c | 19 +++++++++++++++++++
net/mac80211/rx.c | 10 ++++++++++
net/mac80211/sta_info.c | 2 ++
net/mac80211/sta_info.h | 6 ++++++
net/mac80211/status.c | 19 +++++++++++++++++++
net/mac80211/tx.c | 27 ++++++++++++++++++++++++---
net/mac80211/work.c | 1 +
13 files changed, 160 insertions(+), 6 deletions(-)

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index ed5a03c..ba92b73 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -120,6 +120,8 @@
#define IEEE80211_QOS_CTL_TID_MASK 0x000F
#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007

+#define IEEE80211_QOS_HTC_LEN 4
+
/* U-APSD queue for WMM IEs sent by AP */
#define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7)

@@ -140,6 +142,9 @@

#define IEEE80211_HT_CTL_LEN 4

+#define IEEE80211_HTC2_CSI_NONCOMP_BF 0x00800000
+#define IEEE80211_HTC2_CSI_COMP_BF 0x00c00000
+
struct ieee80211_hdr {
__le16 frame_control;
__le16 duration_id;
@@ -169,6 +174,17 @@ struct ieee80211_qos_hdr {
__le16 qos_ctrl;
} __attribute__ ((packed));

+struct ieee80211_qos_htc_hdr {
+ __le16 frame_control;
+ __le16 duration_id;
+ u8 addr1[6];
+ u8 addr2[6];
+ u8 addr3[6];
+ __le16 seq_ctrl;
+ __le16 qos_ctrl;
+ __le32 htc;
+} __attribute__ ((packed));
+
/**
* ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set
* @fc: frame control bytes in little-endian byteorder
@@ -827,6 +843,29 @@ struct ieee80211_mcs_info {
u8 reserved[3];
} __attribute__((packed));

+struct ieee80211_txbf_caps {
+ u32 implicit_rx_capable:1,
+ rx_staggered_sounding:1,
+ tx_staggered_sounding:1,
+ rx_ndp_capable:1,
+ tx_ndp_capable:1,
+ implicit_txbf_capable:1,
+ calibration:2,
+ explicit_csi_txbf_capable:1,
+ explicit_noncomp_steering:1,
+ explicit_comp_steering:1,
+ explicit_csi_feedback:2,
+ explicit_noncomp_bf:2,
+ explicit_comp_bf:2,
+ minimal_grouping:2,
+ csi_bfer_antennas:2,
+ noncomp_bfer_antennas:2,
+ comp_bfer_antennas:2,
+ csi_max_rows_bfer:2,
+ channel_estimation_cap:2,
+ reserved:3;
+};
+
/* 802.11n HT capability MSC set */
#define IEEE80211_HT_MCS_RX_HIGHEST_MASK 0x3ff
#define IEEE80211_HT_MCS_TX_DEFINED 0x01
@@ -862,7 +901,7 @@ struct ieee80211_ht_cap {
struct ieee80211_mcs_info mcs;

__le16 extended_ht_cap_info;
- __le32 tx_BF_cap_info;
+ struct ieee80211_txbf_caps tx_BF_cap_info;
u8 antenna_selection_info;
} __attribute__ ((packed));

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index e5702f5..23b120a 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -210,6 +210,12 @@ struct ieee80211_sta_ht_cap {
u8 ampdu_factor;
u8 ampdu_density;
struct ieee80211_mcs_info mcs;
+ struct ieee80211_txbf_caps txbf;
+ bool explicit_compbf;
+ bool explicit_noncompbf;
+ bool implicit_bf;
+ bool staggered_sounding;
+ u8 channel_estimation_cap;
};

/**
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 9fdf982..0818c58 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -321,6 +321,8 @@ struct ieee80211_bss_conf {
* @IEEE80211_TX_CTL_LDPC: tells the driver to use LDPC for this frame
* @IEEE80211_TX_CTL_STBC: Enables Space-Time Block Coding (STBC) for this
* frame and selects the maximum number of streams that it can use.
+ * @IEEE80211_TX_CTL_TXBF_UPDATE: Channel information needs to be updated
+ * for beamforming of Tx frames.
*
* Note: If you have to add new flags to the enumeration, then don't
* forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary.
@@ -349,6 +351,8 @@ enum mac80211_tx_control_flags {
IEEE80211_TX_INTFL_NL80211_FRAME_TX = BIT(21),
IEEE80211_TX_CTL_LDPC = BIT(22),
IEEE80211_TX_CTL_STBC = BIT(23) | BIT(24),
+ IEEE80211_TX_CTL_TXBF_UPDATE = BIT(25),
+ IEEE80211_TX_CTL_STAG_SOUND = BIT(26),
};

#define IEEE80211_TX_CTL_STBC_SHIFT 23
@@ -364,7 +368,7 @@ enum mac80211_tx_control_flags {
IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_STAT_AMPDU_NO_BACK | \
IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_PSPOLL_RESPONSE | \
IEEE80211_TX_CTL_MORE_FRAMES | IEEE80211_TX_CTL_LDPC | \
- IEEE80211_TX_CTL_STBC)
+ IEEE80211_TX_CTL_STBC | IEEE80211_TX_CTL_TXBF_UPDATE)

/**
* enum mac80211_rate_control_flags - per-rate flags set by the
@@ -900,7 +904,8 @@ struct ieee80211_sta {
u8 addr[ETH_ALEN];
u16 aid;
struct ieee80211_sta_ht_cap ht_cap;
-
+ bool txbf;
+ u8 channel_estimation_cap;
/* must be last */
u8 drv_priv[0] __attribute__((__aligned__(sizeof(void *))));
};
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 18bd0e5..926cc8d 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -698,6 +698,13 @@ static void sta_apply_parameters(struct ieee80211_local *local,
params->ht_capa,
&sta->sta.ht_cap);

+ if (sta->sta.ht_cap.explicit_compbf ||
+ sta->sta.ht_cap.explicit_noncompbf ||
+ sta->sta.ht_cap.implicit_bf) {
+ sta->sta.txbf = true;
+ sta->bf_update_cv = true;
+ }
+
if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) {
switch (params->plink_action) {
case PLINK_ACTION_OPEN:
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 75d679d..5223ea7 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -24,6 +24,7 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
{
u8 ampdu_info, tx_mcs_set_cap;
int i, max_tx_streams;
+ struct ieee80211_txbf_caps bfee, bfmr;

BUG_ON(!ht_cap);

@@ -99,6 +100,23 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
/* handle MCS rate 32 too */
if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
ht_cap->mcs.rx_mask[32/8] |= 1;
+
+ bfee = ht_cap_ie->tx_BF_cap_info;
+ bfmr = sband->ht_cap.txbf;
+
+ if (bfmr.explicit_comp_steering && (bfee.explicit_comp_bf != 0))
+ ht_cap->explicit_compbf = true;
+
+ if (bfmr.explicit_noncomp_steering && (bfee.explicit_noncomp_bf != 0))
+ ht_cap->explicit_noncompbf = true;
+
+ if (bfmr.implicit_txbf_capable && bfee.implicit_rx_capable)
+ ht_cap->implicit_bf = true;
+
+ if (bfmr.tx_staggered_sounding && bfee.rx_staggered_sounding)
+ ht_cap->staggered_sounding = true;
+
+ ht_cap->channel_estimation_cap = bfee.channel_estimation_cap;
}

void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index b80c386..e0eecbf 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1202,6 +1202,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid);
void ieee80211_ba_session_work(struct work_struct *work);
void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid);
void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid);
+void ieee80211_txbf_cv_work(struct work_struct *work);

/* Spectrum management */
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index a3a9421..0ee85f9 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -63,6 +63,8 @@
#define TMR_RUNNING_TIMER 0
#define TMR_RUNNING_CHANSW 1

+#define TXBF_CV_TIMER 1000
+
/*
* All cfg80211 functions have to be called outside a locked
* section so that they can acquire a lock themselves... This
@@ -1237,6 +1239,17 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
}


+void ieee80211_txbf_cv_work(struct work_struct *work)
+{
+ struct sta_info *sta =
+ container_of(work, struct sta_info, txbf_cv_work.work);
+ struct ieee80211_local *local = sta->local;
+
+ sta->bf_update_cv = true;
+ ieee80211_queue_delayed_work(&local->hw,
+ &sta->txbf_cv_work, TXBF_CV_TIMER);
+}
+
static bool ieee80211_assoc_success(struct ieee80211_work *wk,
struct ieee80211_mgmt *mgmt, size_t len)
{
@@ -1343,6 +1356,12 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,

ap_ht_cap_flags = sta->sta.ht_cap.cap;

+ if (sta->sta.ht_cap.explicit_compbf ||
+ sta->sta.ht_cap.explicit_noncompbf ||
+ sta->sta.ht_cap.implicit_bf) {
+ sta->sta.txbf = true;
+ sta->bf_update_cv = true;
+ }
rate_control_rate_init(sta);

if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 902b03e..a012fb6 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1455,6 +1455,16 @@ ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx)
if (!ieee80211_is_data_qos(hdr->frame_control))
return RX_CONTINUE;

+ /* Qos frame with Order bit set indicates an HTC frame */
+ if (ieee80211_has_order(hdr->frame_control)) {
+ memmove(data + IEEE80211_QOS_HTC_LEN, data,
+ ieee80211_hdrlen(hdr->frame_control) -
+ IEEE80211_QOS_HTC_LEN);
+ hdr = (struct ieee80211_hdr *)skb_pull(rx->skb,
+ IEEE80211_QOS_HTC_LEN);
+ hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_ORDER);
+ }
+
/* remove the qos control field, update frame type and meta-data */
memmove(data + IEEE80211_QOS_CTL_LEN, data,
ieee80211_hdrlen(hdr->frame_control) - IEEE80211_QOS_CTL_LEN);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 6d8f897..829398e 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -235,6 +235,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
spin_lock_init(&sta->flaglock);
INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
+ INIT_DELAYED_WORK(&sta->txbf_cv_work, ieee80211_txbf_cv_work);
mutex_init(&sta->ampdu_mlme.mtx);

memcpy(sta->sta.addr, addr, ETH_ALEN);
@@ -691,6 +692,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
wiphy_debug(local->hw.wiphy, "Removed STA %pM\n", sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
cancel_work_sync(&sta->drv_unblock_wk);
+ cancel_delayed_work_sync(&sta->txbf_cv_work);

rate_control_remove_sta_debugfs(sta);
ieee80211_sta_debugfs_remove(sta);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 9265aca..61631e3 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -312,6 +312,12 @@ struct sta_info {
struct sta_ampdu_mlme ampdu_mlme;
u8 timer_to_tid[STA_TID_NUM];

+ bool txbf;
+ bool bf_update_cv;
+ bool bf_sound_pending;
+ bool allow_cv_update;
+ struct delayed_work txbf_cv_work;
+
#ifdef CONFIG_MAC80211_MESH
/*
* Mesh peer link attributes
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 3153c19..b0447ca 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -209,6 +209,25 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
return;
}

+ if (ieee80211_has_order(fc)) {
+ if ((info->flags & IEEE80211_TX_STAT_ACK) &&
+ (sta->bf_sound_pending)) {
+ sta->bf_sound_pending = false;
+ ieee80211_queue_delayed_work(&local->hw,
+ &sta->txbf_cv_work, 1000);
+ } else
+ sta->bf_update_cv = true;
+ }
+
+
+ if ((info->flags & IEEE80211_TX_CTL_TXBF_UPDATE) &&
+ !(sta->bf_sound_pending)) {
+ if (sta->sta.ht_cap.explicit_compbf ||
+ sta->sta.ht_cap.explicit_noncompbf ||
+ sta->sta.ht_cap.implicit_bf)
+ sta->bf_update_cv = true;
+ }
+
if ((local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) &&
(rates_idx != -1))
sta->last_tx_rate = info->status.rates[rates_idx];
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 96c5943..5900cf2 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1888,6 +1888,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
if ((sta_flags & WLAN_STA_WME) && local->hw.queues >= 4) {
fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
hdrlen += 2;
+ if (sta->bf_update_cv)
+ hdrlen += 4;
}

/*
@@ -1973,9 +1975,28 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,

if (ieee80211_is_data_qos(fc)) {
__le16 *qos_control;
-
- qos_control = (__le16*) skb_push(skb, 2);
- memcpy(skb_push(skb, hdrlen - 2), &hdr, hdrlen - 2);
+ __le32 *htc;
+
+ if (sta->bf_update_cv) {
+ hdr.frame_control |= cpu_to_le16(IEEE80211_FCTL_ORDER);
+ htc = (__le32 *) skb_push(skb, 4);
+ sta->bf_sound_pending = true;
+ *htc = 0;
+ sta->bf_update_cv = false;
+
+ if (sta->sta.ht_cap.explicit_compbf)
+ *htc |= IEEE80211_HTC2_CSI_COMP_BF;
+ else if (sta->sta.ht_cap.explicit_noncompbf)
+ *htc |= IEEE80211_HTC2_CSI_NONCOMP_BF;
+
+ ieee80211_queue_delayed_work(&local->hw,
+ &sta->txbf_cv_work, 1000);
+ qos_control = (__le16 *) skb_push(skb, 2);
+ memcpy(skb_push(skb, hdrlen - 6), &hdr, hdrlen - 6);
+ } else {
+ qos_control = (__le16 *) skb_push(skb, 2);
+ memcpy(skb_push(skb, hdrlen - 2), &hdr, hdrlen - 2);
+ };
/*
* Maybe we could actually set some fields here, for now just
* initialise to zero to indicate no special operation.
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index ae344d1..7676567 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -190,6 +190,7 @@ static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie,

/* extended capabilities */
pos += sizeof(__le16);
+ memcpy(pos, &sband->ht_cap.txbf, sizeof(sband->ht_cap.txbf));

/* BF capabilities */
pos += sizeof(__le32);
--
1.6.3.3



2010-11-10 12:24:01

by Vivek Natarajan

[permalink] [raw]
Subject: [RFC 2/5] ath: Add a keycache entry if beamforming is enabled.

Keycache entry for a sta has the details of the negotiated
beamforming parameters which is to be used by the hardware
while trasmitting to that specific sta.

Signed-off-by: Vivek Natarajan <[email protected]>
---
drivers/net/wireless/ath/ath.h | 2 ++
drivers/net/wireless/ath/key.c | 29 +++++++++++++++++++++++++++++
2 files changed, 31 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index 501050c..214a25f 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -177,5 +177,7 @@ int ath_key_config(struct ath_common *common,
bool ath_hw_keyreset(struct ath_common *common, u16 entry);
void ath_hw_cycle_counters_update(struct ath_common *common);
int32_t ath_hw_get_listen_time(struct ath_common *common);
+int ath_txbf_key_config(struct ath_common *common,
+ struct ieee80211_sta *sta);

#endif /* ATH_H */
diff --git a/drivers/net/wireless/ath/key.c b/drivers/net/wireless/ath/key.c
index 62e3dac..11f562e 100644
--- a/drivers/net/wireless/ath/key.c
+++ b/drivers/net/wireless/ath/key.c
@@ -115,6 +115,7 @@ static bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry,
void *ah = common->ah;
u32 key0, key1, key2, key3, key4;
u32 keyType;
+ u32 txbf = 0;

if (entry >= common->keymax) {
ath_print(common, ATH_DBG_FATAL,
@@ -199,6 +200,9 @@ static bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry,
REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4);
REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType);

+ REG_WRITE(ah, AR_KEYTABLE_TYPE(entry),
+ keyType | txbf);
+
/* Write MAC address for the entry */
(void) ath_hw_keysetmac(common, entry, mac);

@@ -435,6 +439,31 @@ static int ath_reserve_key_cache_slot(struct ath_common *common,
return -1;
}

+int ath_txbf_key_config(struct ath_common *common, struct ieee80211_sta *sta)
+{
+ int idx, ret;
+ struct ath_keyval hk;
+
+ if (WARN_ON(!sta))
+ return -EOPNOTSUPP;
+
+ memset(&hk, 0, sizeof(hk));
+ hk.kv_type = ATH_CIPHER_CLR;
+
+ idx = ath_reserve_key_cache_slot(common, 0);
+
+ if (idx < 0)
+ return -ENOSPC;
+
+ ret = ath_hw_set_keycache_entry(common, idx, &hk, sta->addr);
+
+ if (!ret)
+ return -EIO;
+
+ return idx;
+}
+EXPORT_SYMBOL(ath_txbf_key_config);
+
/*
* Configure encryption in the HW.
*/
--
1.6.3.3


2010-11-10 12:47:33

by Felix Fietkau

[permalink] [raw]
Subject: Re: [RFC 2/5] ath: Add a keycache entry if beamforming is enabled.

On 2010-11-10 1:23 PM, Vivek Natarajan wrote:
> Keycache entry for a sta has the details of the negotiated
> beamforming parameters which is to be used by the hardware
> while trasmitting to that specific sta.
>
> Signed-off-by: Vivek Natarajan <[email protected]>
> ---
> drivers/net/wireless/ath/ath.h | 2 ++
> drivers/net/wireless/ath/key.c | 29 +++++++++++++++++++++++++++++
> 2 files changed, 31 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/key.c b/drivers/net/wireless/ath/key.c
> index 62e3dac..11f562e 100644
> --- a/drivers/net/wireless/ath/key.c
> +++ b/drivers/net/wireless/ath/key.c
> @@ -115,6 +115,7 @@ static bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry,
> void *ah = common->ah;
> u32 key0, key1, key2, key3, key4;
> u32 keyType;
> + u32 txbf = 0;
>
> if (entry >= common->keymax) {
> ath_print(common, ATH_DBG_FATAL,
> @@ -199,6 +200,9 @@ static bool ath_hw_set_keycache_entry(struct ath_common *common, u16 entry,
> REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4);
> REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType);
>
> + REG_WRITE(ah, AR_KEYTABLE_TYPE(entry),
> + keyType | txbf);
> +
> /* Write MAC address for the entry */
> (void) ath_hw_keysetmac(common, entry, mac);
>
What's the point of this change, if txbf is always zero?
And why duplicate the REG_WRITE?

- Felix

2010-11-10 13:26:04

by Felix Fietkau

[permalink] [raw]
Subject: Re: [RFC 5/5] ath9k_hw: Add support for Tx beamforming.

On 2010-11-10 1:23 PM, Vivek Natarajan wrote:
> Initialize Tx beamforming capabilities, related registers and set
> descriptors for sounding frames.
>
> Signed-off-by: Vivek Natarajan <[email protected]>
> ---
> drivers/net/wireless/ath/ath9k/Makefile | 3 +-
> drivers/net/wireless/ath/ath9k/ar9003_mac.c | 80 ++++++-
> drivers/net/wireless/ath/ath9k/ar9003_mac.h | 22 ++
> drivers/net/wireless/ath/ath9k/ar9003_phy.h | 11 +
> drivers/net/wireless/ath/ath9k/ar9003_txbf.c | 359 ++++++++++++++++++++++++++
> drivers/net/wireless/ath/ath9k/ar9003_txbf.h | 87 +++++++
> drivers/net/wireless/ath/ath9k/hw.c | 3 +
> drivers/net/wireless/ath/ath9k/hw.h | 56 ++++
> drivers/net/wireless/ath/ath9k/mac.h | 48 ++++-
> drivers/net/wireless/ath/ath9k/reg.h | 119 +++++++++-
> 10 files changed, 782 insertions(+), 6 deletions(-)
> create mode 100644 drivers/net/wireless/ath/ath9k/ar9003_txbf.c
> create mode 100644 drivers/net/wireless/ath/ath9k/ar9003_txbf.h
>
> diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
> index 3394dfe..a1fada3 100644
> --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h
> +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
> @@ -101,6 +101,8 @@
> */
> #define AR_PHY_TIMING2_USE_FORCE_PPM 0x00001000
> #define AR_PHY_TIMING2_FORCE_PPM_VAL 0x00000fff
> +#define AR_PHY_TIMING2_HT_Fine_Timing_EN 0x80000000
> +
> #define AR_PHY_TIMING3_DSC_MAN 0xFFFE0000
> #define AR_PHY_TIMING3_DSC_MAN_S 17
> #define AR_PHY_TIMING3_DSC_EXP 0x0001E000
> @@ -188,6 +190,12 @@
> #define AR_PHY_RADAR_DC_PWR_THRESH_S 15
> #define AR_PHY_RADAR_LB_DC_CAP 0x7f800000
> #define AR_PHY_RADAR_LB_DC_CAP_S 23
> +#define AR_PHY_PERCHAIN_CSD_chn1_2chains 0x0000001f
> +#define AR_PHY_PERCHAIN_CSD_chn1_2chains_S 0
> +#define AR_PHY_PERCHAIN_CSD_chn1_3chains 0x000003e0
> +#define AR_PHY_PERCHAIN_CSD_chn1_3chains_S 5
> +#define AR_PHY_PERCHAIN_CSD_chn2_3chains 0x00007c00
> +#define AR_PHY_PERCHAIN_CSD_chn2_3chains_S 10
> #define AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW (0x3f << 6)
> #define AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW_S 6
> #define AR_PHY_FIND_SIG_LOW_FIRPWR (0x7f << 12)
Lowercase/Uppercase consistency?

> diff --git a/drivers/net/wireless/ath/ath9k/ar9003_txbf.h b/drivers/net/wireless/ath/ath9k/ar9003_txbf.h
> new file mode 100644
> index 0000000..1b6f5f8
> --- /dev/null
> +++ b/drivers/net/wireless/ath/ath9k/ar9003_txbf.h
> @@ -0,0 +1,87 @@
> +/*
> + * Copyright (c) 2008-2010, Atheros Communications Inc.
> + *
> + * Permission to use, copy, modify, and/or distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#ifndef _ATH_AR9000_TxBF_CAL_H_
> +#define _ATH_AR9300_TxBF_CAL_H_
> +
> +#define debugKa 0
> +#define debugRC 0
> +#define debugWinRC 0
???

> +
> +#define NUM_OF_BW 2 /* two bandwidth used for mapping:20M,40M */
> +#define NUM_OF_Ng 3 /* three group define used for mapping :Ng=0,Ng=1,Ng=2*/
> +
> +#define MAX_BITS_PER_SYMBOL 8
> +#define NUM_OF_CHAINMASK (1 << AR9300_MAX_CHAINS)

> +#define BITS_PER_BYTE 8
What's this for?

> +#define BITS_PER_COMPLEX_SYMBOL (2 * BITS_PER_SYMBOL)
> +#define BITS_PER_SYMBOL 10
> +
> +#define MAX_STREAMS 3
> +#define MAX_PILOTS 6
> +#define EVM_MIN -128
> +
> +#define Tone_40M 114
> +#define Tone_20M 56
> +#define NUM_ITER 8
> +
> +#define Nb_phin (10-1)
> +#define Nb_coridc (12-1)
> +#define Nb_sin (8-1)
> +#define Nb_ph 5
> +#define Nb_psi 4
> +#define NUM_ITER_V 6
> +
> +#define Nb_MHINV 13
> +#define EVM_TH 10
> +#define rc_max 6000
> +#define rc_min 100
> +
> +#define BW_40M 1
> +#define BW_20M_low 2
> +#define BW_20M_up 3
What are those for?

> diff --git a/drivers/net/wireless/ath/ath9k/ar9003_txbf.c b/drivers/net/wireless/ath/ath9k/ar9003_txbf.c
> new file mode 100644
> index 0000000..94a5133
> --- /dev/null
> +++ b/drivers/net/wireless/ath/ath9k/ar9003_txbf.c
> @@ -0,0 +1,359 @@
> +/*
> + * Copyright (c) 2008-2010, Atheros Communications Inc.
> + *
> + * Permission to use, copy, modify, and/or distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +#include "hw.h"
> +#include "ar9003_txbf.h"
> +#include "ar9003_phy.h"
> +#include "ar9003_mac.h"
> +/* number of carrier mappings under different bandwidth and grouping, ex:
> + * bw = 1 (40M), Ng=0 (no group), number of carrier = 114*/
> +static u8 const Ns[NUM_OF_BW][NUM_OF_Ng] = {
> + {56, 30, 16},
> + {114, 58, 30}
> +};
> +static u8 const Valid_bits[MAX_BITS_PER_SYMBOL] = {
> + 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff
> +};
Unused, also rather useless, there are kernel functions for that sort of
thing.

> diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
> index 8bebc3e..670a5ca 100644
> --- a/drivers/net/wireless/ath/ath9k/hw.h
> +++ b/drivers/net/wireless/ath/ath9k/hw.h
> @@ -238,6 +238,29 @@ struct ath9k_ops_config {
> u16 spurchans[AR_EEPROM_MODAL_SPURS][2];
> u8 max_txtrig_level;
> u16 ani_poll_interval; /* ANI poll interval in ms */
> + u8 ath_hw_cvtimeout;
> + u8 ath_hw_txbf_ctl;
> +#define TxBFCtl_ImBF 0x01
> +#define TxBFCtl_ImBF_S 0
> +#define TxBFCtl_Non_ExBF 0x02
> +#define TxBFCtl_Non_ExBF_S 1
> +#define TxBFCtl_Comp_ExBF 0x04
> +#define TxBFCtl_Comp_ExBF_S 2
> +#define TxBFCtl_ImBF_FB 0x08
> +#define TxBFCtl_ImBF_FB_S 3
> +#define TxBFCtl_Non_ExBF_Immediately_Rpt 0x10
> +#define TxBFCtl_Non_ExBF_Immediately_Rpt_S 4
> +#define TxBFCtl_Comp_ExBF_Immediately_Rpt 0x20
> +#define TxBFCtl_Comp_ExBF_Immediately_Rpt_S 5
> +
> +#define TxBFCtl_Non_ExBF_delay_Rpt 0x40
> +#define TxBFCtl_Non_ExBF_delay_Rpt_S 6
> +#define TxBFCtl_Comp_ExBF_delay_Rpt 0x80
> +#define TxBFCtl_Comp_ExBF_delay_Rpt_S 7
Why the _S defines? Also, why not just use BIT() instead of the raw hex
values.

> @@ -603,6 +626,28 @@ struct ath_nf_limits {
> s16 nominal;
> };
>
> +struct hal_txbf_caps {
> + u8 channel_estimation_cap;
> + u8 csi_max_rows_bfer;
> + u8 comp_bfer_antennas;
> + u8 noncomp_bfer_antennas;
> + u8 csi_bfer_antennas;
> + u8 minimal_grouping;
> + u8 explicit_comp_bf;
> + u8 explicit_noncomp_bf;
> + u8 explicit_csi_feedback;
> + u8 explicit_comp_steering;
> + u8 explicit_noncomp_steering;
> + u8 explicit_csi_txbf_capable;
> + u8 calibration;
> + u8 implicit_txbf_capable;
> + u8 tx_ndp_capable;
> + u8 rx_ndp_capable;
> + u8 tx_staggered_sounding;
> + u8 rx_staggered_sounding;
> + u8 implicit_rx_capable;
> +};
Maybe rename this to ath9k_hw_txbf_caps?

> @@ -955,6 +1002,15 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning);
> void ath9k_hw_proc_mib_event(struct ath_hw *ah);
> void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan);
>
> +extern void ar9003_init_txbf(struct ath_hw *ah);
> +extern void ar9003_set_11n_txbf_sounding(struct ath_hw *ah,
> + void *ds, struct ath9k_11n_rate_series series[],
> + u8 cec, u16 opt);
> +extern void ar9003_fill_txbf_capabilities(struct ath_hw *ah);
> +extern struct ieee80211_txbf_caps
> + ar9003_get_txbf_capabilities(struct ath_hw *ah);
> +extern bool ar9300_read_key_cache_mac(struct ath_hw *ah, u16 entry,
> + u8 *mac);
> #define ATH_PCIE_CAP_LINK_CTRL 0x70
> #define ATH_PCIE_CAP_LINK_L0S 1
> #define ATH_PCIE_CAP_LINK_L1 2
> diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
> index 22907e2..7110506 100644
> --- a/drivers/net/wireless/ath/ath9k/mac.h
> +++ b/drivers/net/wireless/ath/ath9k/mac.h
> @@ -89,7 +89,7 @@
> #define ATH9K_TX_DATA_UNDERRUN 0x08
> #define ATH9K_TX_DELIM_UNDERRUN 0x10
> #define ATH9K_TX_SW_FILTERED 0x80
> -
> +#define ATH9K_TX_BF_ERR 0xa0
> /* 64 bytes */
> #define MIN_TX_FIFO_THRESHOLD 0x1
>
> @@ -124,6 +124,12 @@ struct ath_tx_status {
> u32 evm0;
> u32 evm1;
> u32 evm2;
> + u8 ts_txbfstatus; /* Tx bf status */
> +#define AR_BW_Mismatch 0x1
> +#define AR_Stream_Miss 0x2
> +#define AR_CV_Missed 0x4
> +#define AR_Dest_Miss 0x8
> +#define AR_Expired 0x10
> };
>
> struct ath_rx_status {
> @@ -151,6 +157,11 @@ struct ath_rx_status {
> u32 evm2;
> u32 evm3;
> u32 evm4;
> + u8 rx_hw_upload_data:1,
> + rx_not_sounding:1,
> + rx_Ness:2,
> + rx_hw_upload_data_valid:1,
> + rx_hw_upload_data_type:2;
> };
>
> struct ath_htc_rx_status {
> @@ -263,6 +274,15 @@ struct ath_desc {
> #define ATH9K_TXDESC_VMF 0x0100
> #define ATH9K_TXDESC_FRAG_IS_ON 0x0200
> #define ATH9K_TXDESC_LOWRXCHAIN 0x0400
> +#define ATH9K_TXDESC_TXBF 0x0800 /*for txbf*/
> +#define ATH9K_TXDESC_TXBF_SOUND 0x1000 /* for sounding settings*/
> +#define ATH9K_TXDESC_TXBF_SOUND_S 11
It's just one bit, it doesn't need a _S

> diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
> index fa05b71..3d9914e 100644
> --- a/drivers/net/wireless/ath/ath9k/reg.h
> +++ b/drivers/net/wireless/ath/ath9k/reg.h

> @@ -1816,5 +1849,89 @@ enum {
> #define AR_PHY_AGC_CONTROL_CLC_SUCCESS 0x00080000 /* carrier leak calibration done */
> #define AR_PHY_AGC_CONTROL_YCOK_MAX 0x000003c0
> #define AR_PHY_AGC_CONTROL_YCOK_MAX_S 6
> -
> +#define AR_TXBF_DBG 0x10000
> +
> +#define AR_TXBF 0x10004
> +#define AR_TXBF_CB_TX 0x00000003
> +#define AR_TXBF_CB_TX_S 0
> +#define AR_TXBF_PSI_1_PHI_3 0
> +#define AR_TXBF_PSI_2_PHI_4 1
> +#define AR_TXBF_PSI_3_PHI_5 2
> +#define AR_TXBF_PSI_4_PHI_6 3
> +
> +#define AR_TXBF_NB_TX 0x0000000C
> +#define AR_TXBF_NB_TX_S 2
> +#define AR_TXBF_NUMBEROFBIT_4 0
> +#define AR_TXBF_NUMBEROFBIT_2 1
> +#define AR_TXBF_NUMBEROFBIT_6 2
> +#define AR_TXBF_NUMBEROFBIT_8 3
> +
> +#define AR_TXBF_NG_RPT_TX 0x00000030
> +#define AR_TXBF_NG_RPT_TX_S 4
> +#define AR_TXBF_No_GROUP 0
> +#define AR_TXBF_TWO_GROUP 1
> +#define AR_TXBF_FOUR_GROUP 2
> +
> +#define AR_TXBF_NG_CVCACHE 0x000000C0
> +#define AR_TXBF_NG_CVCACHE_S 6
> +#define AR_TXBF_FOUR_CLIENTS 0
> +#define AR_TXBF_EIGHT_CLIENTS 1
> +#define AR_TXBF_SIXTEEN_CLIENTS 2
> +
> +#define AR_TXBF_TXCV_BFWEIGHT_METHOD 0x00000600
> +#define AR_TXBF_TXCV_BFWEIGHT_METHOD_S 9
> +#define AR_TXBF_NO_WEIGHTING 0
> +#define AR_TXBF_MAX_POWER 1
> +#define AR_TXBF_KEEP_RATIO 2
> +
> +#define AR_TXBF_RLR_EN 0x00000800
> +#define AR_TXBF_RC_20_U_DONE 0x00001000
> +#define AR_TXBF_RC_20_L_DONE 0x00002000
> +#define AR_TXBF_RC_40_DONE 0x00004000
> +#define AR_TXBF_FORCE_UPDATE_V2BB 0x00008000
> +
> +#define AR_TXBF_TIMER 0x10008
> +#define AR_TXBF_TIMER_TIMEOUT 0x000000FF
> +#define AR_TXBF_TIMER_TIMEOUT_S 0
> +#define AR_TXBF_TIMER_ATIMEOUT 0x0000FF00
> +#define AR_TXBF_TIMER_ATIMEOUT_S 8
> +
> +/* for SVD cache update */
> +#define AR_TXBF_SW 0x1000c
> +#define AR_LRU_ACK 0x00000001
> +#define AR_LRU_ADDR 0x000003FE
> +#define AR_LRU_ADDR_S 1
> +#define AR_LRU_EN 0x00000400
> +#define AR_LRU_EN_S 11
> +#define AR_DEST_IDX 0x0007f000
> +#define AR_DEST_IDX_S 12
> +#define AR_LRU_WR_ACK 0x00080000
> +#define AR_LRU_WR_ACK_S 19
> +#define AR_LRU_RD_ACK 0x00100000
> +#define AR_LRU_RD_ACK_S 20
> +
> +#define AR_RC0_0 0x11000
> +#define AR_RC0(_idx) (AR_RC0_0+(_idx))
> +#define AR_RC1_0 0x11200
> +#define AR_RC1(_idx) (AR_RC1_0+(_idx))
> +
> +#define AR_CVCACHE_0 0x12400
> +#define AR_CVCACHE(_idx) (AR_CVCACHE_0+(_idx))
> +#define AR_CVCACHE_Ng_IDX 0x0000C000
> +#define AR_CVCACHE_Ng_IDX_S 14
> +#define AR_CVCACHE_BW40 0x00010000
> +#define AR_CVCACHE_BW40_S 16
> +#define AR_CVCACHE_IMPLICIT 0x00020000
> +#define AR_CVCACHE_IMPLICIT_S 17
_S unnecessary here as well

- Felix

2010-11-10 12:24:21

by Vivek Natarajan

[permalink] [raw]
Subject: [RFC 5/5] ath9k_hw: Add support for Tx beamforming.

Initialize Tx beamforming capabilities, related registers and set
descriptors for sounding frames.

Signed-off-by: Vivek Natarajan <[email protected]>
---
drivers/net/wireless/ath/ath9k/Makefile | 3 +-
drivers/net/wireless/ath/ath9k/ar9003_mac.c | 80 ++++++-
drivers/net/wireless/ath/ath9k/ar9003_mac.h | 22 ++
drivers/net/wireless/ath/ath9k/ar9003_phy.h | 11 +
drivers/net/wireless/ath/ath9k/ar9003_txbf.c | 359 ++++++++++++++++++++++++++
drivers/net/wireless/ath/ath9k/ar9003_txbf.h | 87 +++++++
drivers/net/wireless/ath/ath9k/hw.c | 3 +
drivers/net/wireless/ath/ath9k/hw.h | 56 ++++
drivers/net/wireless/ath/ath9k/mac.h | 48 ++++-
drivers/net/wireless/ath/ath9k/reg.h | 119 +++++++++-
10 files changed, 782 insertions(+), 6 deletions(-)
create mode 100644 drivers/net/wireless/ath/ath9k/ar9003_txbf.c
create mode 100644 drivers/net/wireless/ath/ath9k/ar9003_txbf.h

diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index aca0162..3f4a6e6 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -33,7 +33,8 @@ ath9k_hw-y:= \
ar9002_mac.o \
ar9003_mac.o \
ar9003_eeprom.o \
- ar9003_paprd.o
+ ar9003_paprd.o \
+ ar9003_txbf.o

obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o

diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
index 10c812e..66795c9 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
@@ -15,6 +15,7 @@
*/
#include "hw.h"
#include "ar9003_mac.h"
+#include "ar9003_phy.h"

static void ar9003_hw_rx_enable(struct ath_hw *hw)
{
@@ -302,6 +303,22 @@ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds,
ts->ts_longretry = MS(status, AR_DataFailCnt);
ts->ts_virtcol = MS(status, AR_VirtRetryCnt);

+ ts->ts_txbfstatus = 0;
+ if (ads->status8 & AR_TxBF_BW_Mismatch)
+ ts->ts_txbfstatus |= AR_BW_Mismatch;
+
+ if (ads->status8 & AR_TxBF_Stream_Miss)
+ ts->ts_txbfstatus |= AR_Stream_Miss;
+
+ if (ads->status8 & AR_TxBF_Dest_Miss)
+ ts->ts_txbfstatus |= AR_Dest_Miss;
+
+ if (ads->status8 & AR_TxBF_Expired)
+ ts->ts_txbfstatus |= AR_Expired;
+
+ if (ts->ts_txbfstatus)
+ ts->ts_flags |= ATH9K_TX_BF_ERR;
+
status = ACCESS_ONCE(ads->status7);
ts->ts_rssi = MS(status, AR_TxRSSICombined);
ts->ts_rssi_ext0 = MS(status, AR_TxRSSIAnt10);
@@ -360,7 +377,7 @@ static void ar9003_hw_set11n_ratescenario(struct ath_hw *ah, void *ds,
{
struct ar9003_txc *ads = (struct ar9003_txc *) ds;
struct ar9003_txc *last_ads = (struct ar9003_txc *) lastds;
- u_int32_t ctl11;
+ u32 ctl11, steerflag, tmp;

if (flags & (ATH9K_TXDESC_RTSENA | ATH9K_TXDESC_CTSENA)) {
ctl11 = ads->ctl11;
@@ -378,6 +395,48 @@ static void ar9003_hw_set11n_ratescenario(struct ath_hw *ah, void *ds,
ads->ctl11 = (ads->ctl11 & ~(AR_RTSEnable | AR_CTSEnable));
}

+ if (flags & (ATH9K_TXDESC_TXBF_SOUND | ATH9K_TXDESC_TXBF_STAG_SOUND)) {
+ steerflag = set11n_txbf_flags(series, 0)
+ | set11n_txbf_flags(series, 1)
+ | set11n_txbf_flags(series, 2)
+ | set11n_txbf_flags(series, 3);
+
+ if (steerflag != 0) {
+ if (ads->ctl17 & AR_LDPC) {
+ if (not_two_stream_rate(series[0].Rate)) {
+ steerflag = set11n_txbf_flags(series, 0);
+ if (not_two_stream_rate(series[1].Rate))
+ steerflag |=
+ set11n_txbf_flags(series, 1);
+
+ if (not_two_stream_rate(series[2].Rate))
+ steerflag |=
+ set11n_txbf_flags(series, 2);
+
+ if (not_two_stream_rate(series[3].Rate))
+ steerflag |=
+ set11n_txbf_flags(series, 3);
+
+ } else
+ ads->ctl17 &= ~(AR_LDPC);
+ }
+ ads->ctl11 |= steerflag;
+ }
+ }
+ if (flags & ATH9K_TXDESC_CAL) {
+ tmp = REG_READ(ah, AR_PHY_PERCHAIN_CSD);
+ tmp |= SM(8, AR_PHY_PERCHAIN_CSD_chn1_2chains);
+ tmp |= SM(4, AR_PHY_PERCHAIN_CSD_chn1_3chains);
+ tmp |= SM(8, AR_PHY_PERCHAIN_CSD_chn2_3chains);
+ REG_WRITE(ah, AR_PHY_PERCHAIN_CSD, tmp);
+ } else {
+ tmp = REG_READ(ah, AR_PHY_PERCHAIN_CSD);
+ tmp |= SM(2, AR_PHY_PERCHAIN_CSD_chn1_2chains);
+ tmp |= SM(2, AR_PHY_PERCHAIN_CSD_chn1_3chains);
+ tmp |= SM(4, AR_PHY_PERCHAIN_CSD_chn2_3chains);
+ REG_WRITE(ah, AR_PHY_PERCHAIN_CSD, tmp);
+ }
+
ads->ctl13 = set11nTries(series, 0)
| set11nTries(series, 1)
| set11nTries(series, 2)
@@ -401,7 +460,16 @@ static void ar9003_hw_set11n_ratescenario(struct ath_hw *ah, void *ds,
| set11nRateFlags(series, 2)
| set11nRateFlags(series, 3)
| SM(rtsctsRate, AR_RTSCTSRate);
- ads->ctl19 = AR_Not_Sounding;
+
+ if (flags & (ATH9K_TXDESC_TXBF_SOUND | ATH9K_TXDESC_TXBF_STAG_SOUND)) {
+ ar9003_set_11n_txbf_sounding(ah, ds, series,
+ MS(flags, ATH9K_TXDESC_CEC),
+ (ATH9K_TXDESC_TXBF_SOUND |
+ ATH9K_TXDESC_TXBF_STAG_SOUND));
+ } else {
+ /* set not sounding for normal frame */
+ ads->ctl19 = AR_Not_Sounding;
+ }

last_ads->ctl13 = ads->ctl13;
last_ads->ctl14 = ads->ctl14;
@@ -517,6 +585,7 @@ void ath9k_hw_addrxbuf_edma(struct ath_hw *ah, u32 rxdp,
}
EXPORT_SYMBOL(ath9k_hw_addrxbuf_edma);

+
int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
void *buf_addr)
{
@@ -566,6 +635,13 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
rxs->rs_flags = (rxsp->status4 & AR_GI) ? ATH9K_RX_GI : 0;
rxs->rs_flags |= (rxsp->status4 & AR_2040) ? ATH9K_RX_2040 : 0;

+ rxs->rx_hw_upload_data = (rxsp->status2 & AR_HwUploadData) ? 1 : 0;
+ rxs->rx_not_sounding = (rxsp->status4 & AR_RxNotSounding) ? 1 : 0;
+ rxs->rx_Ness = MS(rxsp->status4, AR_RxNess);
+ rxs->rx_hw_upload_data_valid = (rxsp->status4 & AR_HwUploadDataValid)
+ ? 1 : 0;
+ rxs->rx_hw_upload_data_type = MS(rxsp->status11, AR_HwUploadDataType);
+
rxs->evm0 = rxsp->status6;
rxs->evm1 = rxsp->status7;
rxs->evm2 = rxsp->status8;
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.h b/drivers/net/wireless/ath/ath9k/ar9003_mac.h
index 45cc7e8..dd547aa 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.h
@@ -36,6 +36,17 @@
#define AR_LowRxChain 0x00004000

#define AR_Not_Sounding 0x20000000
+#define AR_NESS 0xc0000000
+#define AR_NESS_S 30
+
+#define AR_NESS1 0xc0000000
+#define AR_NESS1_S 30
+
+#define AR_NESS2 0xc0000000
+#define AR_NESS2_S 30
+
+#define AR_NESS3 0xc0000000
+#define AR_NESS3_S 30

/* ctl 12 */
#define AR_PAPRDChainMask 0x00000e00
@@ -51,7 +62,18 @@
#define MAP_ISR_S2_BB_WATCHDOG 6

#define AR9003TXC_CONST(_ds) ((const struct ar9003_txc *) _ds)
+#define set11n_txbf_flags(_series, _index) \
+ ((_series)[_index].RateFlags & ATH9K_RATESERIES_TXBF ? \
+ AR_TxBf##_index : 0)
+
+#define not_two_stream_rate(_rate) (((_rate) > 0x8f) || ((_rate) < 0x88))

+#define set11n_txbf_LDPC(_series) \
+ (((not_two_stream_rate((_series)[0].Rate) && \
+ (not_two_stream_rate((_series)[1].Rate) || (!(_series)[1].Tries)) \
+ && (not_two_stream_rate((_series)[2].Rate) || (!(_series)[2].Tries)) \
+ && (not_two_stream_rate((_series)[3].Rate) || (!(_series)[3].Tries)))) \
+ ? AR_LDPC : 0)
struct ar9003_rxs {
u32 ds_info;
u32 status1;
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
index 3394dfe..a1fada3 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
@@ -101,6 +101,8 @@
*/
#define AR_PHY_TIMING2_USE_FORCE_PPM 0x00001000
#define AR_PHY_TIMING2_FORCE_PPM_VAL 0x00000fff
+#define AR_PHY_TIMING2_HT_Fine_Timing_EN 0x80000000
+
#define AR_PHY_TIMING3_DSC_MAN 0xFFFE0000
#define AR_PHY_TIMING3_DSC_MAN_S 17
#define AR_PHY_TIMING3_DSC_EXP 0x0001E000
@@ -188,6 +190,12 @@
#define AR_PHY_RADAR_DC_PWR_THRESH_S 15
#define AR_PHY_RADAR_LB_DC_CAP 0x7f800000
#define AR_PHY_RADAR_LB_DC_CAP_S 23
+#define AR_PHY_PERCHAIN_CSD_chn1_2chains 0x0000001f
+#define AR_PHY_PERCHAIN_CSD_chn1_2chains_S 0
+#define AR_PHY_PERCHAIN_CSD_chn1_3chains 0x000003e0
+#define AR_PHY_PERCHAIN_CSD_chn1_3chains_S 5
+#define AR_PHY_PERCHAIN_CSD_chn2_3chains 0x00007c00
+#define AR_PHY_PERCHAIN_CSD_chn2_3chains_S 10
#define AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW (0x3f << 6)
#define AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW_S 6
#define AR_PHY_FIND_SIG_LOW_FIRPWR (0x7f << 12)
@@ -432,6 +440,9 @@
#define AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_A 0x3FF
#define AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_A_S 0

+#define AR_PHY_ENABLE_FLT_SVD 0x00001000
+#define AR_PHY_ENABLE_FLT_SVD_S 12
+
#define AR_PHY_TEST (AR_SM_BASE + 0x160)

#define AR_PHY_TEST_BBB_OBS_SEL 0x780000
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_txbf.c b/drivers/net/wireless/ath/ath9k/ar9003_txbf.c
new file mode 100644
index 0000000..94a5133
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/ar9003_txbf.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2008-2010, Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "hw.h"
+#include "ar9003_txbf.h"
+#include "ar9003_phy.h"
+#include "ar9003_mac.h"
+/* number of carrier mappings under different bandwidth and grouping, ex:
+ * bw = 1 (40M), Ng=0 (no group), number of carrier = 114*/
+static u8 const Ns[NUM_OF_BW][NUM_OF_Ng] = {
+ {56, 30, 16},
+ {114, 58, 30}
+};
+static u8 const Valid_bits[MAX_BITS_PER_SYMBOL] = {
+ 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff
+};
+static u8 Num_bits_on[NUM_OF_CHAINMASK] = {
+ 0 /* 000 */ ,
+ 1 /* 001 */ ,
+ 1 /* 010 */ ,
+ 2 /* 011 */ ,
+ 1 /* 100 */ ,
+ 2 /* 101 */ ,
+ 2 /* 110 */ ,
+ 3 /* 111 */
+};
+
+u8 ar9003_get_ntx(struct ath_hw *ah)
+{
+ return Num_bits_on[ah->txchainmask];
+}
+
+u8 ar9003_get_nrx(struct ath_hw *ah)
+{
+ return Num_bits_on[ah->rxchainmask];
+}
+
+void ar9003_set_hw_cv_timeout(struct ath_hw *ah, bool opt)
+{
+ /* if true use H/W settings to update cv timeout values */
+ if (opt)
+ ah->ah_txbf_hw_cvtimeout = ah->config.ath_hw_cvtimeout;
+
+ REG_WRITE(ah, AR_TXBF_TIMER,
+ (ah->ah_txbf_hw_cvtimeout << AR_TXBF_TIMER_ATIMEOUT_S) |
+ (ah->ah_txbf_hw_cvtimeout << AR_TXBF_TIMER_TIMEOUT_S));
+}
+
+void ar9003_init_txbf(struct ath_hw *ah)
+{
+ u32 tmp;
+ u8 txbf_ctl;
+
+ /* set default settings for TxBF */
+ REG_WRITE(ah, AR_TXBF,
+ /* size of codebook entry set to psi 3bits, phi 6bits */
+ (AR_TXBF_PSI_4_PHI_6 << AR_TXBF_CB_TX_S) |
+
+ /* set Number of bit_to 8 bits */
+ (AR_TXBF_NUMBEROFBIT_8 << AR_TXBF_NB_TX_S) |
+
+ /* NG_RPT_TX set t0 No_GROUP */
+ (AR_TXBF_No_GROUP << AR_TXBF_NG_RPT_TX_S) |
+
+ /* TXBF_NG_CVCACHE set to 16 clients */
+ (AR_TXBF_SIXTEEN_CLIENTS << AR_TXBF_NG_CVCACHE_S) |
+
+ /* set weighting method to max power */
+ (SM(AR_TXBF_MAX_POWER, AR_TXBF_TXCV_BFWEIGHT_METHOD)));
+ /* when ah_txbf_hw_cvtimeout = 0, use setting values */
+ if (ah->ah_txbf_hw_cvtimeout == 0)
+ ar9003_set_hw_cv_timeout(ah, true);
+ else
+ ar9003_set_hw_cv_timeout(ah, false);
+
+ /*
+ * Set CEC to 2 stream for self_gen.
+ * Set spacing to 8 us.
+ * Initial selfgen Minimum MPDU.
+ */
+ tmp = REG_READ(ah, AR_SELFGEN);
+ tmp |= SM(AR_SELFGEN_CEC_TWO_SPACETIMESTREAM, AR_CEC);
+ tmp |= SM(AR_SELFGEN_MMSS_EIGHT_us, AR_MMSS);
+ REG_WRITE(ah, AR_SELFGEN, tmp);
+
+ /* set inital basic rate for all rate */
+ REG_WRITE(ah, AR_BASIC_SET, ALL_RATE);
+
+ /* enable HT fine timing */
+ tmp = REG_READ(ah, AR_PHY_TIMING2);
+ tmp |= AR_PHY_TIMING2_HT_Fine_Timing_EN;
+ REG_WRITE(ah, AR_PHY_TIMING2, tmp);
+
+ /* enable description decouple */
+ tmp = REG_READ(ah, AR_PCU_MISC_MODE2);
+ tmp |= AR_DECOUPLE_DECRYPTION;
+ REG_WRITE(ah, AR_PCU_MISC_MODE2, tmp);
+
+ /* enable restart */
+ tmp = REG_READ(ah, AR_PHY_RESTART);
+ tmp |= AR_PHY_RESTART_ENA;
+ REG_WRITE(ah, AR_PHY_RESTART, tmp);
+
+ /* enable flt SVD */
+ tmp = REG_READ(ah, AR_PHY_SEARCH_START_DELAY);
+
+ tmp |= AR_PHY_ENABLE_FLT_SVD;
+ REG_WRITE(ah, AR_PHY_SEARCH_START_DELAY, tmp);
+ txbf_ctl = ah->config.ath_hw_txbf_ctl;
+
+ tmp = REG_READ(ah, AR_H_XFER_TIMEOUT);
+ tmp |= AR_EXBF_IMMDIATE_RESP;
+ tmp &= ~(AR_EXBF_NOACK_NO_RPT);
+
+ /* enable immediate report */
+ REG_WRITE(ah, AR_H_XFER_TIMEOUT, tmp);
+}
+
+int ar9003_get_ness(struct ath_hw *ah, u8 code_rate, u8 cec)
+{
+ u8 ndltf = 0, ness = 0, ntx;
+
+ ntx = ar9003_get_ntx(ah);
+
+ /* cec+1 remote cap's for channel estimation in stream. */
+ /* limit by remote's cap */
+ if (ntx > (cec + 2))
+ ntx = cec + 2;
+
+ if (code_rate < MIN_TWO_STREAM_RATE)
+ ndltf = 1;
+ else if (code_rate < MIN_THREE_STREAM_RATE)
+ ndltf = 2;
+ else
+ ndltf = 4;
+
+ /* NESS is used for setting neltf and NTX =<NDLTF + NELTF, NDLTF
+ * is 2^(stream-1), if NTX < NDLTF, NESS=0, other NESS = NTX-NDLTF */
+ if (code_rate >= MIN_HT_RATE) { /* HT rate */
+ if (ntx > ndltf)
+ ness = ntx - ndltf;
+ }
+
+ return ness;
+}
+
+/*
+ * function: ar9003_set_11n_txbf_sounding
+ * purpose: Set sounding frame
+ * inputs:
+ * series: rate series of sounding frame
+ * cec: Channel Estimation Capability . Extract from subfields
+ * of the Transmit Beamforming Capabilities field of remote.
+ * opt: control flag of current frame
+ */
+void
+ar9003_set_11n_txbf_sounding(struct ath_hw *ah,
+ void *ds,
+ struct ath9k_11n_rate_series series[],
+ u8 cec, u16 opt)
+{
+ struct ar9003_txc *ads = (struct ar9003_txc *) ds;
+ u8 ness = 0, ness1 = 0, ness2 = 0, ness3 = 0;
+
+ ads->ctl19 &= (~AR_Not_Sounding); /*set sounding frame */
+ if (opt & ATH9K_TXDESC_TXBF_STAG_SOUND) {
+ ness = ar9003_get_ness(ah, series[0].Rate, cec);
+ ness1 = ar9003_get_ness(ah, series[1].Rate, cec);
+ ness2 = ar9003_get_ness(ah, series[2].Rate, cec);
+ ness3 = ar9003_get_ness(ah, series[3].Rate, cec);
+ }
+
+ ads->ctl19 |= SM(ness, AR_NESS);
+ ads->ctl20 |= SM(ness1, AR_NESS1);
+ ads->ctl21 |= SM(ness2, AR_NESS2);
+ ads->ctl22 |= SM(ness3, AR_NESS3);
+
+ /* disable other series that don't support sounding at legacy rate */
+ if (series[1].Rate < MIN_HT_RATE) {
+ ads->ctl13 &=
+ ~(AR_XmitDataTries1 | AR_XmitDataTries2 |
+ AR_XmitDataTries3);
+ } else if (series[2].Rate < MIN_HT_RATE)
+ ads->ctl13 &= ~(AR_XmitDataTries2 | AR_XmitDataTries3);
+ else if (series[3].Rate < MIN_HT_RATE)
+ ads->ctl13 &= ~(AR_XmitDataTries3);
+}
+
+/* search CV cache address according to key index */
+static u16 ar9003_txbf_lru_search(struct ath_hw *ah, u8 key_idx)
+{
+ u32 tmp = 0;
+ u16 cv_cache_idx = 0;
+
+ /* make sure LRU search is initially disabled */
+ REG_WRITE(ah, AR_TXBF_SW, 0);
+ tmp = (SM(key_idx, AR_DEST_IDX) | AR_LRU_EN);
+
+ /* enable LRU search */
+ REG_WRITE(ah, AR_TXBF_SW, tmp);
+
+ /* wait for LRU search to finish */
+ do {
+ tmp = REG_READ(ah, AR_TXBF_SW);
+ } while ((tmp & AR_LRU_ACK) != 1);
+ cv_cache_idx = MS(tmp, AR_LRU_ADDR);
+
+ /* disable LRU search */
+ REG_WRITE(ah, AR_TXBF_SW, 0);
+ return cv_cache_idx;
+}
+
+void ar9003_fill_txbf_capabilities(struct ath_hw *ah)
+{
+ struct ieee80211_txbf_caps *txbf = &ah->txbf_caps;
+ u32 val;
+ u8 txbf_ctl;
+
+ memset(txbf, 0, sizeof(struct ieee80211_txbf_caps));
+
+ /* CEC for osprey is always 1 (2 stream) */
+ txbf->channel_estimation_cap = 1;
+
+ /* For calibration, always 2 (3 stream) for osprey */
+ txbf->csi_max_rows_bfer = 2;
+
+ /*
+ * Compressed Steering Number of Beamformer Antennas Supported is
+ * limited by local's antenna
+ */
+ txbf->comp_bfer_antennas = ar9003_get_ntx(ah) - 1;
+
+ /*
+ * Compressed Steering Number of Beamformer Antennas Supported
+ * is limited by local's antenna
+ */
+ txbf->noncomp_bfer_antennas = ar9003_get_ntx(ah) - 1;
+
+ /* NOT SUPPORT CSI */
+ txbf->csi_bfer_antennas = 0;
+
+ /* 1, 2, 4 group is supported */
+ txbf->minimal_grouping = ALL_GROUP;
+
+ /* Explicit compressed Beamforming Feedback Capable */
+ txbf->explicit_comp_bf |= Delay_Rpt;
+
+ /*TxBFCtl_Comp_ExBF_Immediately_Rpt)*/
+ txbf->explicit_comp_bf |= Immediately_Rpt;
+
+ /* Explicit non-Compressed Beamforming Feedback Capable */
+ txbf->explicit_noncomp_bf |= Delay_Rpt;
+
+ txbf->explicit_noncomp_bf |= Immediately_Rpt;
+
+ /* not support csi feekback */
+ txbf->explicit_csi_feedback = 0;
+
+ /* Explicit compressed Steering Capable from settings */
+ txbf->explicit_comp_steering = 1;
+
+ /* Explicit Non-compressed Steering Capable from settings */
+ txbf->explicit_noncomp_steering = 1;
+
+ /* not support CSI */
+ txbf->explicit_csi_txbf_capable = false;
+
+ /* not support imbf and calibration */
+ txbf->calibration = No_CALIBRATION;
+ txbf->implicit_txbf_capable = false;
+ txbf->implicit_rx_capable = false;
+
+ /* not support NDP */
+ txbf->tx_ndp_capable = false;
+ txbf->rx_ndp_capable = false;
+
+ /* support stagger sounding. */
+ txbf->tx_staggered_sounding = true;
+ txbf->rx_staggered_sounding = true;
+
+ /* set immediately or delay report to H/W */
+ val = REG_READ(ah, AR_H_XFER_TIMEOUT);
+ txbf_ctl = ah->config.ath_hw_txbf_ctl;
+
+ val |= AR_EXBF_IMMDIATE_RESP;
+ val &= ~(AR_EXBF_NOACK_NO_RPT);
+ /* enable immediately report */
+ REG_WRITE(ah, AR_H_XFER_TIMEOUT, val);
+}
+EXPORT_SYMBOL(ar9003_fill_txbf_capabilities);
+
+struct ieee80211_txbf_caps ar9003_get_txbf_capabilities(struct ath_hw *ah)
+{
+ return ah->txbf_caps;
+}
+EXPORT_SYMBOL(ar9003_get_txbf_capabilities);
+
+/*
+ * ar9003_txbf_set_key is used to set TXBF related field in key cache.
+ */
+void ar9003_txbf_set_key(struct ath_hw *ah,
+ u16 entry,
+ u8 rx_staggered_sounding,
+ u8 channel_estimation_cap, u8 mmss)
+{
+ u32 tmp, txbf;
+
+ /* 1 for 2 stream, 0 for 1 stream, should add 1 for H/W */
+ channel_estimation_cap += 1;
+ txbf = (SM(rx_staggered_sounding, AR_KEYTABLE_STAGGED) |
+ SM(channel_estimation_cap, AR_KEYTABLE_CEC) |
+ SM(mmss, AR_KEYTABLE_MMSS));
+ tmp = REG_READ(ah, AR_KEYTABLE_TYPE(entry));
+ if (txbf !=
+ (tmp & (AR_KEYTABLE_STAGGED | AR_KEYTABLE_CEC | AR_KEYTABLE_MMSS)))
+ /* update key cache for txbf */
+ REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), tmp | txbf);
+}
+
+u32 ar9003_read_cv_cache(struct ath_hw *ah, u32 addr)
+{
+ u32 tmp, value;
+
+ REG_WRITE(ah, addr, AR_CVCACHE_RD_EN);
+
+ do {
+ tmp = REG_READ(ah, AR_TXBF_SW);
+ } while ((tmp & AR_LRU_RD_ACK) == 0);
+ value = REG_READ(ah, addr);
+ tmp &= ~(AR_LRU_RD_ACK);
+ REG_WRITE(ah, AR_TXBF_SW, tmp);
+ return value & AR_CVCACHE_DATA;
+}
+
+void ar9003_txbf_get_cv_cache_nr(struct ath_hw *ah, u16 key_idx, u8 * nr)
+{
+ u32 idx, value;
+ u8 nr_idx;
+
+ /* get current CV cache addess offset from key index */
+ idx = ar9003_txbf_lru_search(ah, key_idx);
+
+ /* read the cvcache header */
+ value = ar9003_read_cv_cache(ah, AR_CVCACHE(idx));
+ nr_idx = MS(value, AR_CVCACHE_Nr_IDX);
+ *nr = nr_idx + 1;
+}
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_txbf.h b/drivers/net/wireless/ath/ath9k/ar9003_txbf.h
new file mode 100644
index 0000000..1b6f5f8
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/ar9003_txbf.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2008-2010, Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _ATH_AR9000_TxBF_CAL_H_
+#define _ATH_AR9300_TxBF_CAL_H_
+
+#define debugKa 0
+#define debugRC 0
+#define debugWinRC 0
+
+#define NUM_OF_BW 2 /* two bandwidth used for mapping:20M,40M */
+#define NUM_OF_Ng 3 /* three group define used for mapping :Ng=0,Ng=1,Ng=2*/
+
+#define MAX_BITS_PER_SYMBOL 8
+
+#define NUM_OF_CHAINMASK (1 << AR9300_MAX_CHAINS)
+
+#define BITS_PER_BYTE 8
+#define BITS_PER_COMPLEX_SYMBOL (2 * BITS_PER_SYMBOL)
+#define BITS_PER_SYMBOL 10
+
+#define MAX_STREAMS 3
+#define MAX_PILOTS 6
+#define EVM_MIN -128
+
+#define Tone_40M 114
+#define Tone_20M 56
+#define NUM_ITER 8
+
+#define Nb_phin (10-1)
+#define Nb_coridc (12-1)
+#define Nb_sin (8-1)
+#define Nb_ph 5
+#define Nb_psi 4
+#define NUM_ITER_V 6
+
+#define Nb_MHINV 13
+#define EVM_TH 10
+#define rc_max 6000
+#define rc_min 100
+
+#define BW_40M 1
+#define BW_20M_low 2
+#define BW_20M_up 3
+
+
+/* MIMO control field related bit definiton */
+
+#define AR_Nc_idx 0x0003 /* Nc Index*/
+#define AR_Nc_idx_S 0
+#define AR_Nr_idx 0x000c /* Nr Index*/
+#define AR_Nr_idx_S 2
+#define AR_bandwith 0x0010 /* MIMO Control Channel Width*/
+#define AR_bandwith_S 4
+#define AR_Ng 0x0060 /* Grouping*/
+#define AR_Ng_S 5
+#define AR_Nb 0x0180 /* Coeffiecient Size*/
+#define AR_Nb_S 7
+#define AR_CI 0x0600 /* Codebook Information*/
+#define AR_CI_S 9
+#define CAL_GAIN 0
+#define Smooth 1
+
+/* constant for TXBF IE */
+#define ALL_GROUP 3
+#define No_CALIBRATION 0
+#define INIT_RESP_CAL 3
+
+/* constant for rate code */
+#define MIN_THREE_STREAM_RATE 0x90
+#define MIN_TWO_STREAM_RATE 0x88
+#define MIN_HT_RATE 0x80
+
+#endif
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 5fb1bf3..9597cc3 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -1425,6 +1425,9 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
if (!ath9k_hw_init_cal(ah, chan))
return -EIO;

+ if (AR_SREV_9300_20_OR_LATER(ah))
+ ar9003_init_txbf(ah);
+
ENABLE_REGWRITE_BUFFER(ah);

ath9k_hw_restore_chainmask(ah);
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 8bebc3e..670a5ca 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -238,6 +238,29 @@ struct ath9k_ops_config {
u16 spurchans[AR_EEPROM_MODAL_SPURS][2];
u8 max_txtrig_level;
u16 ani_poll_interval; /* ANI poll interval in ms */
+ u8 ath_hw_cvtimeout;
+ u8 ath_hw_txbf_ctl;
+#define TxBFCtl_ImBF 0x01
+#define TxBFCtl_ImBF_S 0
+#define TxBFCtl_Non_ExBF 0x02
+#define TxBFCtl_Non_ExBF_S 1
+#define TxBFCtl_Comp_ExBF 0x04
+#define TxBFCtl_Comp_ExBF_S 2
+#define TxBFCtl_ImBF_FB 0x08
+#define TxBFCtl_ImBF_FB_S 3
+#define TxBFCtl_Non_ExBF_Immediately_Rpt 0x10
+#define TxBFCtl_Non_ExBF_Immediately_Rpt_S 4
+#define TxBFCtl_Comp_ExBF_Immediately_Rpt 0x20
+#define TxBFCtl_Comp_ExBF_Immediately_Rpt_S 5
+
+#define TxBFCtl_Non_ExBF_delay_Rpt 0x40
+#define TxBFCtl_Non_ExBF_delay_Rpt_S 6
+#define TxBFCtl_Comp_ExBF_delay_Rpt 0x80
+#define TxBFCtl_Comp_ExBF_delay_Rpt_S 7
+
+#define Delay_Rpt 1
+#define Immediately_Rpt 2
+
};

enum ath9k_int {
@@ -603,6 +626,28 @@ struct ath_nf_limits {
s16 nominal;
};

+struct hal_txbf_caps {
+ u8 channel_estimation_cap;
+ u8 csi_max_rows_bfer;
+ u8 comp_bfer_antennas;
+ u8 noncomp_bfer_antennas;
+ u8 csi_bfer_antennas;
+ u8 minimal_grouping;
+ u8 explicit_comp_bf;
+ u8 explicit_noncomp_bf;
+ u8 explicit_csi_feedback;
+ u8 explicit_comp_steering;
+ u8 explicit_noncomp_steering;
+ u8 explicit_csi_txbf_capable;
+ u8 calibration;
+ u8 implicit_txbf_capable;
+ u8 tx_ndp_capable;
+ u8 rx_ndp_capable;
+ u8 tx_staggered_sounding;
+ u8 rx_staggered_sounding;
+ u8 implicit_rx_capable;
+};
+
struct ath_hw {
struct ieee80211_hw *hw;
struct ath_common common;
@@ -736,10 +781,12 @@ struct ath_hw {
/* Bluetooth coexistance */
struct ath_btcoex_hw btcoex_hw;

+ struct ieee80211_txbf_caps txbf_caps;
u32 intr_txqs;
u8 txchainmask;
u8 rxchainmask;

+ bool ah_txbf_hw_cvtimeout;
u32 originalGain[22];
int initPDADC;
int PDADCdelta;
@@ -955,6 +1002,15 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning);
void ath9k_hw_proc_mib_event(struct ath_hw *ah);
void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan);

+extern void ar9003_init_txbf(struct ath_hw *ah);
+extern void ar9003_set_11n_txbf_sounding(struct ath_hw *ah,
+ void *ds, struct ath9k_11n_rate_series series[],
+ u8 cec, u16 opt);
+extern void ar9003_fill_txbf_capabilities(struct ath_hw *ah);
+extern struct ieee80211_txbf_caps
+ ar9003_get_txbf_capabilities(struct ath_hw *ah);
+extern bool ar9300_read_key_cache_mac(struct ath_hw *ah, u16 entry,
+ u8 *mac);
#define ATH_PCIE_CAP_LINK_CTRL 0x70
#define ATH_PCIE_CAP_LINK_L0S 1
#define ATH_PCIE_CAP_LINK_L1 2
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index 22907e2..7110506 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -89,7 +89,7 @@
#define ATH9K_TX_DATA_UNDERRUN 0x08
#define ATH9K_TX_DELIM_UNDERRUN 0x10
#define ATH9K_TX_SW_FILTERED 0x80
-
+#define ATH9K_TX_BF_ERR 0xa0
/* 64 bytes */
#define MIN_TX_FIFO_THRESHOLD 0x1

@@ -124,6 +124,12 @@ struct ath_tx_status {
u32 evm0;
u32 evm1;
u32 evm2;
+ u8 ts_txbfstatus; /* Tx bf status */
+#define AR_BW_Mismatch 0x1
+#define AR_Stream_Miss 0x2
+#define AR_CV_Missed 0x4
+#define AR_Dest_Miss 0x8
+#define AR_Expired 0x10
};

struct ath_rx_status {
@@ -151,6 +157,11 @@ struct ath_rx_status {
u32 evm2;
u32 evm3;
u32 evm4;
+ u8 rx_hw_upload_data:1,
+ rx_not_sounding:1,
+ rx_Ness:2,
+ rx_hw_upload_data_valid:1,
+ rx_hw_upload_data_type:2;
};

struct ath_htc_rx_status {
@@ -263,6 +274,15 @@ struct ath_desc {
#define ATH9K_TXDESC_VMF 0x0100
#define ATH9K_TXDESC_FRAG_IS_ON 0x0200
#define ATH9K_TXDESC_LOWRXCHAIN 0x0400
+#define ATH9K_TXDESC_TXBF 0x0800 /*for txbf*/
+#define ATH9K_TXDESC_TXBF_SOUND 0x1000 /* for sounding settings*/
+#define ATH9K_TXDESC_TXBF_SOUND_S 11
+#define ATH9K_TXDESC_SOUND 0x1 /* enable sounding */
+#define ATH9K_TXDESC_TXBF_STAG_SOUND 0x2000 /* enable sounding */
+#define ATH9K_TXDESC_TRQ 0x3 /* request sounding */
+#define ATH9K_TXDESC_CAL 0x2000 /* calibration frame */
+#define ATH9K_TXDESC_CEC 0xC000 /* channel calibration capability*/
+#define ATH9K_TXDESC_CEC_S 14
#define ATH9K_TXDESC_LDPC 0x00010000

#define ATH9K_RXDESC_INTREQ 0x0020
@@ -352,7 +372,12 @@ struct ar5416_desc {
#define AR_RTSEnable 0x00400000
#define AR_VEOL 0x00800000
#define AR_ClrDestMask 0x01000000
-#define AR_TxCtlRsvd01 0x1e000000
+#define AR_TxBf0 0x02000000
+#define AR_TxBf1 0x04000000
+#define AR_TxBf2 0x08000000
+#define AR_TxBf3 0x10000000
+
+#define AR_TxBfSteered 0x1e000000
#define AR_TxIntrReq 0x20000000
#define AR_DestIdxValid 0x40000000
#define AR_CTSEnable 0x80000000
@@ -495,8 +520,14 @@ struct ar5416_desc {
#define AR_TxStatusRsvd80 0x0001e000
#define AR_TxOpExceeded 0x00020000
#define AR_TxStatusRsvd81 0x001c0000
+#define AR_TXBFStatus 0x001c0000
+#define AR_TXBFStatus_S 18
+#define AR_TxBF_BW_Mismatch 0x00040000
+#define AR_TxBF_Stream_Miss 0x00080000
#define AR_FinalTxIdx 0x00600000
#define AR_FinalTxIdx_S 21
+#define AR_TxBF_Dest_Miss 0x00800000
+#define AR_TxBF_Expired 0x01000000
#define AR_TxStatusRsvd82 0x01800000
#define AR_PowerMgmt 0x02000000
#define AR_TxStatusRsvd83 0xfc000000
@@ -521,6 +552,8 @@ struct ar5416_desc {
#define AR_RxMore 0x00001000
#define AR_NumDelim 0x003fc000
#define AR_NumDelim_S 14
+#define AR_HwUploadData 0x00400000
+#define AR_HwUploadData_S 22
#define AR_RxStatusRsvd10 0xff800000

#define AR_RcvTimestamp ds_rxstatus2
@@ -529,6 +562,12 @@ struct ar5416_desc {
#define AR_2040 0x00000002
#define AR_Parallel40 0x00000004
#define AR_Parallel40_S 2
+#define AR_RxStbc 0x00000008
+#define AR_RxNotSounding 0x00000010
+#define AR_RxNess 0x00000060
+#define AR_RxNess_S 5
+#define AR_HwUploadDataValid 0x00000080
+#define AR_HwUploadDataValid_S 7
#define AR_RxStatusRsvd30 0x000000f8
#define AR_RxAntenna 0xffffff00
#define AR_RxAntenna_S 8
@@ -563,6 +602,10 @@ struct ar5416_desc {
#define AR_RxAggr 0x00020000
#define AR_PostDelimCRCErr 0x00040000
#define AR_RxStatusRsvd71 0x3ff80000
+#define AR_HwUploadDataType 0x06000000
+#define AR_HwUploadDataType_S 25
+#define AR_HiRxChain 0x10000000
+#define AR_RxFirstAggr 0x20000000
#define AR_DecryptBusyErr 0x40000000
#define AR_KeyMiss 0x80000000

@@ -648,6 +691,7 @@ enum ath9k_rx_filter {
#define ATH9K_RATESERIES_2040 0x0002
#define ATH9K_RATESERIES_HALFGI 0x0004
#define ATH9K_RATESERIES_STBC 0x0008
+#define ATH9K_RATESERIES_TXBF 0x0010 /* use TxBF for series */

struct ath9k_11n_rate_series {
u32 Tries;
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index fa05b71..3d9914e 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -1499,6 +1499,9 @@ enum {
#define AR_FCS_FAIL 0x8094
#define AR_BEACON_CNT 0x8098

+#define AR_BASIC_SET 0x80a0
+#define ALL_RATE 0xff
+
#define AR_SLEEP1 0x80d4
#define AR_SLEEP1_ASSUME_DTIM 0x00080000
#define AR_SLEEP1_CAB_TIMEOUT 0xFFE00000
@@ -1508,6 +1511,24 @@ enum {
#define AR_SLEEP2_BEACON_TIMEOUT 0xFFE00000
#define AR_SLEEP2_BEACON_TIMEOUT_S 21

+/*MAC_PCU_SELF_GEN_DEFAULT*/
+#define AR_SELFGEN 0x80dc
+#define AR_MMSS 0x00000007
+#define AR_MMSS_S 0
+#define AR_SELFGEN_MMSS_NO RESTRICTION 0
+#define AR_SELFGEN_MMSS_ONEOVER4_us 1
+#define AR_SELFGEN_MMSS_ONEOVER2_us 2
+#define AR_SELFGEN_MMSS_ONE_us 3
+#define AR_SELFGEN_MMSS_TWO_us 4
+#define AR_SELFGEN_MMSS_FOUR_us 5
+#define AR_SELFGEN_MMSS_EIGHT_us 6
+#define AR_SELFGEN_MMSS_SIXTEEN_us 7
+
+#define AR_CEC 0x00000018
+#define AR_CEC_S 3
+#define AR_SELFGEN_CEC_ONE_SPACETIMESTREAM 1
+#define AR_SELFGEN_CEC_TWO_SPACETIMESTREAM 2
+
#define AR_TPC 0x80e8
#define AR_TPC_ACK 0x0000003f
#define AR_TPC_ACK_S 0x00
@@ -1716,6 +1737,9 @@ enum {
#define AR_2040_MODE 0x8318
#define AR_2040_JOINED_RX_CLEAR 0x00000001

+#define AR_H_XFER_TIMEOUT 0x831c
+#define AR_EXBF_IMMDIATE_RESP 0x00000040
+#define AR_EXBF_NOACK_NO_RPT 0x00000100

#define AR_EXTRCCNT 0x8328

@@ -1739,6 +1763,7 @@ enum {
#define AR_PCU_MISC_MODE2_ENABLE_AGGWEP 0x00020000
#define AR_PCU_MISC_MODE2_HWWAR1 0x00100000
#define AR_PCU_MISC_MODE2_HWWAR2 0x02000000
+#define AR_DECOUPLE_DECRYPTION 0x08000000
#define AR_PCU_MISC_MODE2_RESERVED2 0xFFFE0000

#define AR_MAC_PCU_ASYNC_FIFO_REG3 0x8358
@@ -1776,6 +1801,14 @@ enum {
#define AR_KEYTABLE_TYPE_CCM 0x00000006
#define AR_KEYTABLE_TYPE_CLR 0x00000007
#define AR_KEYTABLE_ANT 0x00000008
+
+#define AR_KEYTABLE_MMSS 0x00001c00 /* remote's MMSS*/
+#define AR_KEYTABLE_MMSS_S 10
+#define AR_KEYTABLE_CEC 0x00006000 /* remote's CEC*/
+#define AR_KEYTABLE_CEC_S 13
+#define AR_KEYTABLE_STAGGED 0x00010000 /* remote's stagged sounding*/
+#define AR_KEYTABLE_STAGGED_S 16
+
#define AR_KEYTABLE_VALID 0x00008000
#define AR_KEYTABLE_KEY0(_n) (AR_KEYTABLE(_n) + 0)
#define AR_KEYTABLE_KEY1(_n) (AR_KEYTABLE(_n) + 4)
@@ -1816,5 +1849,89 @@ enum {
#define AR_PHY_AGC_CONTROL_CLC_SUCCESS 0x00080000 /* carrier leak calibration done */
#define AR_PHY_AGC_CONTROL_YCOK_MAX 0x000003c0
#define AR_PHY_AGC_CONTROL_YCOK_MAX_S 6
-
+#define AR_TXBF_DBG 0x10000
+
+#define AR_TXBF 0x10004
+#define AR_TXBF_CB_TX 0x00000003
+#define AR_TXBF_CB_TX_S 0
+#define AR_TXBF_PSI_1_PHI_3 0
+#define AR_TXBF_PSI_2_PHI_4 1
+#define AR_TXBF_PSI_3_PHI_5 2
+#define AR_TXBF_PSI_4_PHI_6 3
+
+#define AR_TXBF_NB_TX 0x0000000C
+#define AR_TXBF_NB_TX_S 2
+#define AR_TXBF_NUMBEROFBIT_4 0
+#define AR_TXBF_NUMBEROFBIT_2 1
+#define AR_TXBF_NUMBEROFBIT_6 2
+#define AR_TXBF_NUMBEROFBIT_8 3
+
+#define AR_TXBF_NG_RPT_TX 0x00000030
+#define AR_TXBF_NG_RPT_TX_S 4
+#define AR_TXBF_No_GROUP 0
+#define AR_TXBF_TWO_GROUP 1
+#define AR_TXBF_FOUR_GROUP 2
+
+#define AR_TXBF_NG_CVCACHE 0x000000C0
+#define AR_TXBF_NG_CVCACHE_S 6
+#define AR_TXBF_FOUR_CLIENTS 0
+#define AR_TXBF_EIGHT_CLIENTS 1
+#define AR_TXBF_SIXTEEN_CLIENTS 2
+
+#define AR_TXBF_TXCV_BFWEIGHT_METHOD 0x00000600
+#define AR_TXBF_TXCV_BFWEIGHT_METHOD_S 9
+#define AR_TXBF_NO_WEIGHTING 0
+#define AR_TXBF_MAX_POWER 1
+#define AR_TXBF_KEEP_RATIO 2
+
+#define AR_TXBF_RLR_EN 0x00000800
+#define AR_TXBF_RC_20_U_DONE 0x00001000
+#define AR_TXBF_RC_20_L_DONE 0x00002000
+#define AR_TXBF_RC_40_DONE 0x00004000
+#define AR_TXBF_FORCE_UPDATE_V2BB 0x00008000
+
+#define AR_TXBF_TIMER 0x10008
+#define AR_TXBF_TIMER_TIMEOUT 0x000000FF
+#define AR_TXBF_TIMER_TIMEOUT_S 0
+#define AR_TXBF_TIMER_ATIMEOUT 0x0000FF00
+#define AR_TXBF_TIMER_ATIMEOUT_S 8
+
+/* for SVD cache update */
+#define AR_TXBF_SW 0x1000c
+#define AR_LRU_ACK 0x00000001
+#define AR_LRU_ADDR 0x000003FE
+#define AR_LRU_ADDR_S 1
+#define AR_LRU_EN 0x00000400
+#define AR_LRU_EN_S 11
+#define AR_DEST_IDX 0x0007f000
+#define AR_DEST_IDX_S 12
+#define AR_LRU_WR_ACK 0x00080000
+#define AR_LRU_WR_ACK_S 19
+#define AR_LRU_RD_ACK 0x00100000
+#define AR_LRU_RD_ACK_S 20
+
+#define AR_RC0_0 0x11000
+#define AR_RC0(_idx) (AR_RC0_0+(_idx))
+#define AR_RC1_0 0x11200
+#define AR_RC1(_idx) (AR_RC1_0+(_idx))
+
+#define AR_CVCACHE_0 0x12400
+#define AR_CVCACHE(_idx) (AR_CVCACHE_0+(_idx))
+#define AR_CVCACHE_Ng_IDX 0x0000C000
+#define AR_CVCACHE_Ng_IDX_S 14
+#define AR_CVCACHE_BW40 0x00010000
+#define AR_CVCACHE_BW40_S 16
+#define AR_CVCACHE_IMPLICIT 0x00020000
+#define AR_CVCACHE_IMPLICIT_S 17
+#define AR_CVCACHE_DEST_IDX 0x01FC0000
+#define AR_CVCACHE_DEST_IDX_S 18
+#define AR_CVCACHE_Nc_IDX 0x06000000
+#define AR_CVCACHE_Nc_IDX_S 25
+#define AR_CVCACHE_Nr_IDX 0x18000000
+#define AR_CVCACHE_Nr_IDX_S 27
+#define AR_CVCACHE_EXPIRED 0x20000000
+#define AR_CVCACHE_EXPIRED_S 29
+#define AR_CVCACHE_WRITE 0x80000000
+#define AR_CVCACHE_RD_EN 0x40000000
+#define AR_CVCACHE_DATA 0x3fffffff
#endif
--
1.6.3.3


2010-11-10 17:26:16

by Luis R. Rodriguez

[permalink] [raw]
Subject: Re: [RFC 5/5] ath9k_hw: Add support for Tx beamforming.

On Wed, Nov 10, 2010 at 4:23 AM, Vivek Natarajan <[email protected]> wrote:
> Initialize Tx beamforming capabilities, related registers and set
> descriptors for sounding frames.
>
> Signed-off-by: Vivek Natarajan <[email protected]>

Hardware patch for ath9k_hw should go first otherwise would you not
get a compile issue?

Luis

2010-11-10 12:24:14

by Vivek Natarajan

[permalink] [raw]
Subject: [RFC 4/5] ath9k: Add support for Tx beamforming feature.

Beamforming is enabled with the latest hardware if the module parameter
is set.

Signed-off-by: Vivek Natarajan <[email protected]>
---
drivers/net/wireless/ath/ath9k/ath9k.h | 2 +
drivers/net/wireless/ath/ath9k/init.c | 9 +
drivers/net/wireless/ath/ath9k/main.c | 22 ++
drivers/net/wireless/ath/ath9k/rc.c | 453 ++++++++++++++++++++------------
drivers/net/wireless/ath/ath9k/rc.h | 90 +++++++-
drivers/net/wireless/ath/ath9k/xmit.c | 43 +++-
6 files changed, 454 insertions(+), 165 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 8a2b04e..5216999 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -270,6 +270,8 @@ struct ath_node {
struct ath_atx_ac ac[WME_NUM_AC];
u16 maxampdu;
u8 mpdudensity;
+ bool txbf;
+ u8 key_idx;
};

#define AGGR_CLEANUP BIT(1)
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index f14fe53..531efe7 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -38,6 +38,10 @@ int led_blink;
module_param_named(blink, led_blink, int, 0444);
MODULE_PARM_DESC(blink, "Enable LED blink on activity");

+int txbf;
+module_param_named(txbf, txbf, int, 0444);
+MODULE_PARM_DESC(blink, "Enable TxBF");
+
/* We use the hw_value as an index into our private channel structure */

#define CHAN2G(_freq, _idx) { \
@@ -239,6 +243,11 @@ static void setup_ht_cap(struct ath_softc *sc,
ht_info->mcs.rx_mask[i] = 0xff;

ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
+
+ if (txbf && AR_SREV_9300_20_OR_LATER(ah)) {
+ ar9003_fill_txbf_capabilities(sc->sc_ah);
+ ht_info->txbf = ar9003_get_txbf_capabilities(sc->sc_ah);
+ }
}

static int ath9k_reg_notifier(struct wiphy *wiphy,
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 4b93a72..bea2568 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -553,8 +553,10 @@ void ath_update_chainmask(struct ath_softc *sc, int is_ht)
static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
{
struct ath_node *an;
+ struct ieee80211_sta_ht_cap *ht_cap;

an = (struct ath_node *)sta->drv_priv;
+ ht_cap = &sta->ht_cap;

if (sc->sc_flags & SC_OP_TXAGGR) {
ath_tx_node_init(sc, an);
@@ -562,6 +564,15 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
sta->ht_cap.ampdu_factor);
an->mpdudensity = parse_mpdudensity(sta->ht_cap.ampdu_density);
}
+
+ if (ht_cap->explicit_compbf ||
+ ht_cap->explicit_noncompbf ||
+ ht_cap->implicit_bf) {
+ an->txbf = true;
+ an->key_idx = ath_txbf_key_config(ath9k_hw_common(sc->sc_ah),
+ sta);
+ set_bit(an->key_idx, ath9k_hw_common(sc->sc_ah)->keymap);
+ }
}

static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
@@ -1858,6 +1869,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_node *an = (struct ath_node *)sta->drv_priv;
int ret = 0;

if (modparam_nohwcrypt)
@@ -1869,6 +1881,16 @@ static int ath9k_set_key(struct ieee80211_hw *hw,

switch (cmd) {
case SET_KEY:
+ if (sta && sta->txbf &&
+ (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
+ if (key->cipher == WLAN_CIPHER_SUITE_CCMP)
+ key->keyidx = an->key_idx;
+ else {
+ ath_hw_keyreset(common, an->key_idx);
+ clear_bit(an->key_idx, common->keymap);
+ }
+ }
+
ret = ath_key_config(common, vif, sta, key);
if (ret >= 0) {
key->hw_key_idx = ret;
diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
index 85c8e93..0b9c405 100644
--- a/drivers/net/wireless/ath/ath9k/rc.c
+++ b/drivers/net/wireless/ath/ath9k/rc.c
@@ -23,141 +23,141 @@ static const struct ath_rate_table ar5416_11na_ratetable = {
68,
8, /* MCS start */
{
- [0] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 6000,
+ [0] = { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 6000,
5400, 0, 12, 0, 0, 0, 0 }, /* 6 Mb */
- [1] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 9000,
+ [1] = { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 9000,
7800, 1, 18, 0, 1, 1, 1 }, /* 9 Mb */
- [2] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000,
+ [2] = { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 12000,
10000, 2, 24, 2, 2, 2, 2 }, /* 12 Mb */
- [3] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000,
+ [3] = { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 18000,
13900, 3, 36, 2, 3, 3, 3 }, /* 18 Mb */
- [4] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000,
+ [4] = { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 24000,
17300, 4, 48, 4, 4, 4, 4 }, /* 24 Mb */
- [5] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000,
+ [5] = { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 36000,
23000, 5, 72, 4, 5, 5, 5 }, /* 36 Mb */
- [6] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000,
+ [6] = { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 48000,
27400, 6, 96, 4, 6, 6, 6 }, /* 48 Mb */
- [7] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000,
+ [7] = { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 54000,
29300, 7, 108, 4, 7, 7, 7 }, /* 54 Mb */
- [8] = { RC_HT_SDT_2040, WLAN_RC_PHY_HT_20_SS, 6500,
+ [8] = { RC_HT_SDT_2040, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_20_SS, 6500,
6400, 0, 0, 0, 38, 8, 38 }, /* 6.5 Mb */
- [9] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 13000,
+ [9] = { RC_HT_SDT_20, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_20_SS, 13000,
12700, 1, 1, 2, 39, 9, 39 }, /* 13 Mb */
- [10] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 19500,
+ [10] = { RC_HT_SDT_20, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_20_SS, 19500,
18800, 2, 2, 2, 40, 10, 40 }, /* 19.5 Mb */
- [11] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 26000,
+ [11] = { RC_HT_SD_20, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_20_SS, 26000,
25000, 3, 3, 4, 41, 11, 41 }, /* 26 Mb */
- [12] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 39000,
+ [12] = { RC_HT_SD_20, TRUE_N2_T_N1_T_S, WLAN_RC_PHY_HT_20_SS, 39000,
36700, 4, 4, 4, 42, 12, 42 }, /* 39 Mb */
- [13] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 52000,
+ [13] = { RC_HT_S_20, TRUE_N2_F_N1_S, WLAN_RC_PHY_HT_20_SS, 52000,
48100, 5, 5, 4, 43, 13, 43 }, /* 52 Mb */
- [14] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 58500,
+ [14] = { RC_HT_S_20, TRUE_N2_T_N1_S, WLAN_RC_PHY_HT_20_SS, 58500,
53500, 6, 6, 4, 44, 14, 44 }, /* 58.5 Mb */
- [15] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 65000,
+ [15] = { RC_HT_S_20, TRUE_N2_F_N1_S, WLAN_RC_PHY_HT_20_SS, 65000,
59000, 7, 7, 4, 45, 16, 46 }, /* 65 Mb */
- [16] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS_HGI, 72200,
+ [16] = { RC_HT_S_20, TRUE_N2_F_N1_S, WLAN_RC_PHY_HT_20_SS_HGI, 72200,
65400, 7, 7, 4, 45, 16, 46 }, /* 75 Mb */
- [17] = { RC_INVALID, WLAN_RC_PHY_HT_20_DS, 13000,
+ [17] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_20_DS, 13000,
12700, 8, 8, 0, 47, 17, 47 }, /* 13 Mb */
- [18] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 26000,
+ [18] = { RC_HT_T_20, FALSE, WLAN_RC_PHY_HT_20_DS, 26000,
24800, 9, 9, 2, 48, 18, 48 }, /* 26 Mb */
- [19] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 39000,
+ [19] = { RC_HT_T_20, TRUE_N2_D_N1_D, WLAN_RC_PHY_HT_20_DS, 39000,
36600, 10, 10, 2, 49, 19, 49 }, /* 39 Mb */
- [20] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 52000,
+ [20] = { RC_HT_DT_20, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_20_DS, 52000,
48100, 11, 11, 4, 50, 20, 50 }, /* 52 Mb */
- [21] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 78000,
+ [21] = { RC_HT_DT_20, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_20_DS, 78000,
69500, 12, 12, 4, 51, 21, 51 }, /* 78 Mb */
- [22] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 104000,
+ [22] = { RC_HT_DT_20, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_20_DS, 104000,
89500, 13, 13, 4, 52, 22, 52 }, /* 104 Mb */
- [23] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 117000,
+ [23] = { RC_HT_DT_20, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_20_DS, 117000,
98900, 14, 14, 4, 53, 23, 53 }, /* 117 Mb */
- [24] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 130000,
+ [24] = { RC_HT_DT_20, TRUE_N2_ALL_N1_T, WLAN_RC_PHY_HT_20_DS, 130000,
108300, 15, 15, 4, 54, 25, 55 }, /* 130 Mb */
- [25] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS_HGI, 144400,
+ [25] = { RC_HT_DT_20, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_20_DS_HGI, 144400,
120000, 15, 15, 4, 54, 25, 55 }, /* 144.4 Mb */
- [26] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 19500,
+ [26] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_20_TS, 19500,
17400, 16, 16, 0, 56, 26, 56 }, /* 19.5 Mb */
- [27] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 39000,
+ [27] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_20_TS, 39000,
35100, 17, 17, 2, 57, 27, 57 }, /* 39 Mb */
- [28] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 58500,
+ [28] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_20_TS, 58500,
52600, 18, 18, 2, 58, 28, 58 }, /* 58.5 Mb */
- [29] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 78000,
+ [29] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_20_TS, 78000,
70400, 19, 19, 4, 59, 29, 59 }, /* 78 Mb */
- [30] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 117000,
+ [30] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_20_TS, 117000,
104900, 20, 20, 4, 60, 31, 61 }, /* 117 Mb */
- [31] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS_HGI, 130000,
+ [31] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_20_TS_HGI, 130000,
115800, 20, 20, 4, 60, 31, 61 }, /* 130 Mb*/
- [32] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 156000,
+ [32] = { RC_HT_T_20, FALSE, WLAN_RC_PHY_HT_20_TS, 156000,
137200, 21, 21, 4, 62, 33, 63 }, /* 156 Mb */
- [33] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 173300,
+ [33] = { RC_HT_T_20, TRUE_N2_T_N1_T, WLAN_RC_PHY_HT_20_TS_HGI, 173300,
151100, 21, 21, 4, 62, 33, 63 }, /* 173.3 Mb */
- [34] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 175500,
+ [34] = { RC_HT_T_20, FALSE, WLAN_RC_PHY_HT_20_TS, 175500,
152800, 22, 22, 4, 64, 35, 65 }, /* 175.5 Mb */
- [35] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 195000,
+ [35] = { RC_HT_T_20, TRUE_N2_T_N1_T, WLAN_RC_PHY_HT_20_TS_HGI, 195000,
168400, 22, 22, 4, 64, 35, 65 }, /* 195 Mb*/
- [36] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 195000,
+ [36] = { RC_HT_T_20, FALSE, WLAN_RC_PHY_HT_20_TS, 195000,
168400, 23, 23, 4, 66, 37, 67 }, /* 195 Mb */
- [37] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 216700,
+ [37] = { RC_HT_T_20, TRUE_N2_T_N1_T, WLAN_RC_PHY_HT_20_TS_HGI, 216700,
185000, 23, 23, 4, 66, 37, 67 }, /* 216.7 Mb */
- [38] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 13500,
+ [38] = { RC_HT_SDT_40, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_40_SS, 13500,
13200, 0, 0, 0, 38, 38, 38 }, /* 13.5 Mb*/
- [39] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 27500,
+ [39] = { RC_HT_SDT_40, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_40_SS, 27500,
25900, 1, 1, 2, 39, 39, 39 }, /* 27.0 Mb*/
- [40] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 40500,
+ [40] = { RC_HT_SDT_40, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_40_SS, 40500,
38600, 2, 2, 2, 40, 40, 40 }, /* 40.5 Mb*/
- [41] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 54000,
+ [41] = { RC_HT_SD_40, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_40_SS, 54000,
49800, 3, 3, 4, 41, 41, 41 }, /* 54 Mb */
- [42] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 81500,
+ [42] = { RC_HT_SD_40, TRUE_N2_T_N1_T_S, WLAN_RC_PHY_HT_40_SS, 81500,
72200, 4, 4, 4, 42, 42, 42 }, /* 81 Mb */
- [43] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 108000,
+ [43] = { RC_HT_S_40, TRUE_N2_F_N1_S, WLAN_RC_PHY_HT_40_SS, 108000,
92900, 5, 5, 4, 43, 43, 43 }, /* 108 Mb */
- [44] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 121500,
+ [44] = { RC_HT_S_40, TRUE_N2_T_N1_S, WLAN_RC_PHY_HT_40_SS, 121500,
102700, 6, 6, 4, 44, 44, 44 }, /* 121.5 Mb*/
- [45] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 135000,
+ [45] = { RC_HT_S_40, TRUE_N2_F_N1_S, WLAN_RC_PHY_HT_40_SS, 135000,
112000, 7, 7, 4, 45, 46, 46 }, /* 135 Mb */
- [46] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000,
+ [46] = { RC_HT_S_40, TRUE_N2_F_N1_S, WLAN_RC_PHY_HT_40_SS_HGI, 150000,
122000, 7, 7, 4, 45, 46, 46 }, /* 150 Mb */
- [47] = { RC_INVALID, WLAN_RC_PHY_HT_40_DS, 27000,
+ [47] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_40_DS, 27000,
25800, 8, 8, 0, 47, 47, 47 }, /* 27 Mb */
- [48] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 54000,
+ [48] = { RC_HT_T_40, FALSE, WLAN_RC_PHY_HT_40_DS, 54000,
49800, 9, 9, 2, 48, 48, 48 }, /* 54 Mb */
- [49] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 81000,
+ [49] = { RC_HT_T_40, TRUE_N2_D_N1_D, WLAN_RC_PHY_HT_40_DS, 81000,
71900, 10, 10, 2, 49, 49, 49 }, /* 81 Mb */
- [50] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 108000,
+ [50] = { RC_HT_DT_40, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_40_DS, 108000,
92500, 11, 11, 4, 50, 50, 50 }, /* 108 Mb */
- [51] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 162000,
+ [51] = { RC_HT_DT_40, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_40_DS, 162000,
130300, 12, 12, 4, 51, 51, 51 }, /* 162 Mb */
- [52] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 216000,
+ [52] = { RC_HT_DT_40, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_40_DS, 216000,
162800, 13, 13, 4, 52, 52, 52 }, /* 216 Mb */
- [53] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 243000,
+ [53] = { RC_HT_DT_40, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_40_DS, 243000,
178200, 14, 14, 4, 53, 53, 53 }, /* 243 Mb */
- [54] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 270000,
+ [54] = { RC_HT_DT_40, TRUE_N2_ALL_N1_T, WLAN_RC_PHY_HT_40_DS, 270000,
192100, 15, 15, 4, 54, 55, 55 }, /* 270 Mb */
- [55] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS_HGI, 300000,
+ [55] = { RC_HT_DT_40, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_40_DS_HGI, 300000,
207000, 15, 15, 4, 54, 55, 55 }, /* 300 Mb */
- [56] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 40500,
+ [56] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_40_TS, 40500,
36100, 16, 16, 0, 56, 56, 56 }, /* 40.5 Mb */
- [57] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 81000,
+ [57] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_40_TS, 81000,
72900, 17, 17, 2, 57, 57, 57 }, /* 81 Mb */
- [58] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 121500,
+ [58] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_40_TS, 121500,
108300, 18, 18, 2, 58, 58, 58 }, /* 121.5 Mb */
- [59] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 162000,
+ [59] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_40_TS, 162000,
142000, 19, 19, 4, 59, 59, 59 }, /* 162 Mb */
- [60] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 243000,
+ [60] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_40_TS, 243000,
205100, 20, 20, 4, 60, 61, 61 }, /* 243 Mb */
- [61] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS_HGI, 270000,
+ [61] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_40_TS_HGI, 270000,
224700, 20, 20, 4, 60, 61, 61 }, /* 270 Mb */
- [62] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 324000,
+ [62] = { RC_HT_T_40, FALSE, WLAN_RC_PHY_HT_40_TS, 324000,
263100, 21, 21, 4, 62, 63, 63 }, /* 324 Mb */
- [63] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 360000,
+ [63] = { RC_HT_T_40, TRUE_N2_T_N1_T, WLAN_RC_PHY_HT_40_TS_HGI, 360000,
288000, 21, 21, 4, 62, 63, 63 }, /* 360 Mb */
- [64] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 364500,
+ [64] = { RC_HT_T_40, FALSE, WLAN_RC_PHY_HT_40_TS, 364500,
290700, 22, 22, 4, 64, 65, 65 }, /* 364.5 Mb */
- [65] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 405000,
+ [65] = { RC_HT_T_40, TRUE_N2_T_N1_T, WLAN_RC_PHY_HT_40_TS_HGI, 405000,
317200, 22, 22, 4, 64, 65, 65 }, /* 405 Mb */
- [66] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 405000,
+ [66] = { RC_HT_T_40, FALSE, WLAN_RC_PHY_HT_40_TS, 405000,
317200, 23, 23, 4, 66, 67, 67 }, /* 405 Mb */
- [67] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 450000,
+ [67] = { RC_HT_T_40, TRUE_N2_T_N1_T, WLAN_RC_PHY_HT_40_TS_HGI, 450000,
346400, 23, 23, 4, 66, 67, 67 }, /* 450 Mb */
},
50, /* probe interval */
@@ -171,149 +171,149 @@ static const struct ath_rate_table ar5416_11ng_ratetable = {
72,
12, /* MCS start */
{
- [0] = { RC_ALL, WLAN_RC_PHY_CCK, 1000,
+ [0] = { RC_ALL, FALSE, WLAN_RC_PHY_CCK, 1000,
900, 0, 2, 0, 0, 0, 0 }, /* 1 Mb */
- [1] = { RC_ALL, WLAN_RC_PHY_CCK, 2000,
+ [1] = { RC_ALL, FALSE, WLAN_RC_PHY_CCK, 2000,
1900, 1, 4, 1, 1, 1, 1 }, /* 2 Mb */
- [2] = { RC_ALL, WLAN_RC_PHY_CCK, 5500,
+ [2] = { RC_ALL, FALSE, WLAN_RC_PHY_CCK, 5500,
4900, 2, 11, 2, 2, 2, 2 }, /* 5.5 Mb */
- [3] = { RC_ALL, WLAN_RC_PHY_CCK, 11000,
+ [3] = { RC_ALL, FALSE, WLAN_RC_PHY_CCK, 11000,
8100, 3, 22, 3, 3, 3, 3 }, /* 11 Mb */
- [4] = { RC_INVALID, WLAN_RC_PHY_OFDM, 6000,
+ [4] = { RC_INVALID, FALSE, WLAN_RC_PHY_OFDM, 6000,
5400, 4, 12, 4, 4, 4, 4 }, /* 6 Mb */
- [5] = { RC_INVALID, WLAN_RC_PHY_OFDM, 9000,
+ [5] = { RC_INVALID, FALSE, WLAN_RC_PHY_OFDM, 9000,
7800, 5, 18, 4, 5, 5, 5 }, /* 9 Mb */
- [6] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000,
+ [6] = { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 12000,
10100, 6, 24, 6, 6, 6, 6 }, /* 12 Mb */
- [7] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000,
+ [7] = { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 18000,
14100, 7, 36, 6, 7, 7, 7 }, /* 18 Mb */
- [8] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000,
+ [8] = { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 24000,
17700, 8, 48, 8, 8, 8, 8 }, /* 24 Mb */
- [9] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000,
+ [9] = { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 36000,
23700, 9, 72, 8, 9, 9, 9 }, /* 36 Mb */
- [10] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000,
+ [10] = { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 48000,
27400, 10, 96, 8, 10, 10, 10 }, /* 48 Mb */
- [11] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000,
+ [11] = { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 54000,
30900, 11, 108, 8, 11, 11, 11 }, /* 54 Mb */
- [12] = { RC_INVALID, WLAN_RC_PHY_HT_20_SS, 6500,
+ [12] = { RC_INVALID, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_20_SS, 6500,
6400, 0, 0, 4, 42, 12, 42 }, /* 6.5 Mb */
- [13] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 13000,
+ [13] = { RC_HT_SDT_20, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_20_SS, 13000,
12700, 1, 1, 6, 43, 13, 43 }, /* 13 Mb */
- [14] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 19500,
+ [14] = { RC_HT_SDT_20, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_20_SS, 19500,
18800, 2, 2, 6, 44, 14, 44 }, /* 19.5 Mb*/
- [15] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 26000,
+ [15] = { RC_HT_SD_20, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_20_SS, 26000,
25000, 3, 3, 8, 45, 15, 45 }, /* 26 Mb */
- [16] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 39000,
+ [16] = { RC_HT_SD_20, TRUE_N2_T_N1_ALL, WLAN_RC_PHY_HT_20_SS, 39000,
36700, 4, 4, 8, 46, 16, 46 }, /* 39 Mb */
- [17] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 52000,
+ [17] = { RC_HT_S_20, TRUE_N2_F_N1_D_S, WLAN_RC_PHY_HT_20_SS, 52000,
48100, 5, 5, 8, 47, 17, 47 }, /* 52 Mb */
- [18] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 58500,
+ [18] = { RC_HT_S_20, TRUE_N2_T_N1_S, WLAN_RC_PHY_HT_20_SS, 58500,
53500, 6, 6, 8, 48, 18, 48 }, /* 58.5 Mb */
- [19] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 65000,
+ [19] = { RC_HT_S_20, TRUE_N2_F_N1_S, WLAN_RC_PHY_HT_20_SS, 65000,
59000, 7, 7, 8, 49, 20, 50 }, /* 65 Mb */
- [20] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS_HGI, 72200,
+ [20] = { RC_HT_S_20, TRUE_N2_F_N1_S, WLAN_RC_PHY_HT_20_SS_HGI, 72200,
65400, 7, 7, 8, 49, 20, 50 }, /* 65 Mb*/
- [21] = { RC_INVALID, WLAN_RC_PHY_HT_20_DS, 13000,
+ [21] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_20_DS, 13000,
12700, 8, 8, 4, 51, 21, 51 }, /* 13 Mb */
- [22] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 26000,
+ [22] = { RC_HT_T_20, FALSE, WLAN_RC_PHY_HT_20_DS, 26000,
24800, 9, 9, 6, 52, 22, 52 }, /* 26 Mb */
- [23] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 39000,
+ [23] = { RC_HT_T_20, TRUE_N2_D_N1_F, WLAN_RC_PHY_HT_20_DS, 39000,
36600, 10, 10, 6, 53, 23, 53 }, /* 39 Mb */
- [24] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 52000,
+ [24] = { RC_HT_DT_20, TRUE_N2_ALL_N1_T, WLAN_RC_PHY_HT_20_DS, 52000,
48100, 11, 11, 8, 54, 24, 54 }, /* 52 Mb */
- [25] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 78000,
+ [25] = { RC_HT_DT_20, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_20_DS, 78000,
69500, 12, 12, 8, 55, 25, 55 }, /* 78 Mb */
- [26] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 104000,
+ [26] = { RC_HT_DT_20, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_20_DS, 104000,
89500, 13, 13, 8, 56, 26, 56 }, /* 104 Mb */
- [27] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 117000,
+ [27] = { RC_HT_DT_20, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_20_DS, 117000,
98900, 14, 14, 8, 57, 27, 57 }, /* 117 Mb */
- [28] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 130000,
+ [28] = { RC_HT_DT_20, TRUE_N2_ALL_N1_T, WLAN_RC_PHY_HT_20_DS, 130000,
108300, 15, 15, 8, 58, 29, 59 }, /* 130 Mb */
- [29] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS_HGI, 144400,
+ [29] = { RC_HT_DT_20, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_20_DS_HGI, 144400,
120000, 15, 15, 8, 58, 29, 59 }, /* 144.4 Mb */
- [30] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 19500,
+ [30] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_20_TS, 19500,
17400, 16, 16, 4, 60, 30, 60 }, /* 19.5 Mb */
- [31] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 39000,
+ [31] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_20_TS, 39000,
35100, 17, 17, 6, 61, 31, 61 }, /* 39 Mb */
- [32] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 58500,
+ [32] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_20_TS, 58500,
52600, 18, 18, 6, 62, 32, 62 }, /* 58.5 Mb */
- [33] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 78000,
+ [33] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_20_TS, 78000,
70400, 19, 19, 8, 63, 33, 63 }, /* 78 Mb */
- [34] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 117000,
+ [34] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_20_TS, 117000,
104900, 20, 20, 8, 64, 35, 65 }, /* 117 Mb */
- [35] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS_HGI, 130000,
+ [35] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_20_TS_HGI, 130000,
115800, 20, 20, 8, 64, 35, 65 }, /* 130 Mb */
- [36] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 156000,
+ [36] = { RC_HT_T_20, FALSE, WLAN_RC_PHY_HT_20_TS, 156000,
137200, 21, 21, 8, 66, 37, 67 }, /* 156 Mb */
- [37] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 173300,
+ [37] = { RC_HT_T_20, TRUE_N2_T_N1_T, WLAN_RC_PHY_HT_20_TS_HGI, 173300,
151100, 21, 21, 8, 66, 37, 67 }, /* 173.3 Mb */
- [38] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 175500,
+ [38] = { RC_HT_T_20, FALSE, WLAN_RC_PHY_HT_20_TS, 175500,
152800, 22, 22, 8, 68, 39, 69 }, /* 175.5 Mb */
- [39] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 195000,
+ [39] = { RC_HT_T_20, TRUE_N2_T_N1_T, WLAN_RC_PHY_HT_20_TS_HGI, 195000,
168400, 22, 22, 8, 68, 39, 69 }, /* 195 Mb */
- [40] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 195000,
+ [40] = { RC_HT_T_20, FALSE, WLAN_RC_PHY_HT_20_TS, 195000,
168400, 23, 23, 8, 70, 41, 71 }, /* 195 Mb */
- [41] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 216700,
+ [41] = { RC_HT_T_20, TRUE_N2_T_N1_T, WLAN_RC_PHY_HT_20_TS_HGI, 216700,
185000, 23, 23, 8, 70, 41, 71 }, /* 216.7 Mb */
- [42] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 13500,
+ [42] = { RC_HT_SDT_40, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_40_SS, 13500,
13200, 0, 0, 8, 42, 42, 42 }, /* 13.5 Mb */
- [43] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 27500,
+ [43] = { RC_HT_SDT_40, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_40_SS, 27500,
25900, 1, 1, 8, 43, 43, 43 }, /* 27.0 Mb */
- [44] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 40500,
+ [44] = { RC_HT_SDT_40, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_40_SS, 40500,
38600, 2, 2, 8, 44, 44, 44 }, /* 40.5 Mb */
- [45] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 54000,
+ [45] = { RC_HT_SD_40, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_40_SS, 54000,
49800, 3, 3, 8, 45, 45, 45 }, /* 54 Mb */
- [46] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 81500,
+ [46] = { RC_HT_SD_40, TRUE_N_1_2_ALL, WLAN_RC_PHY_HT_40_SS, 81500,
72200, 4, 4, 8, 46, 46, 46 }, /* 81 Mb */
- [47] = { RC_HT_S_40 , WLAN_RC_PHY_HT_40_SS, 108000,
+ [47] = { RC_HT_S_40, TRUE_N2_F_N1_D_S, WLAN_RC_PHY_HT_40_SS, 108000,
92900, 5, 5, 8, 47, 47, 47 }, /* 108 Mb */
- [48] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 121500,
+ [48] = { RC_HT_S_40, TRUE_N2_T_N1_S, WLAN_RC_PHY_HT_40_SS, 121500,
102700, 6, 6, 8, 48, 48, 48 }, /* 121.5 Mb */
- [49] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 135000,
+ [49] = { RC_HT_S_40, TRUE_N2_F_N1_S, WLAN_RC_PHY_HT_40_SS, 135000,
112000, 7, 7, 8, 49, 50, 50 }, /* 135 Mb */
- [50] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000,
+ [50] = { RC_HT_S_40, TRUE_N2_F_N1_S, WLAN_RC_PHY_HT_40_SS_HGI, 150000,
122000, 7, 7, 8, 49, 50, 50 }, /* 150 Mb */
- [51] = { RC_INVALID, WLAN_RC_PHY_HT_40_DS, 27000,
+ [51] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_40_DS, 27000,
25800, 8, 8, 8, 51, 51, 51 }, /* 27 Mb */
- [52] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 54000,
+ [52] = { RC_HT_T_40, FALSE, WLAN_RC_PHY_HT_40_DS, 54000,
49800, 9, 9, 8, 52, 52, 52 }, /* 54 Mb */
- [53] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 81000,
+ [53] = { RC_HT_T_40, FALSE, WLAN_RC_PHY_HT_40_DS, 81000,
71900, 10, 10, 8, 53, 53, 53 }, /* 81 Mb */
- [54] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 108000,
+ [54] = { RC_HT_DT_40, TRUE_N2_ALL_N1_T, WLAN_RC_PHY_HT_40_DS, 108000,
92500, 11, 11, 8, 54, 54, 54 }, /* 108 Mb */
- [55] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 162000,
+ [55] = { RC_HT_DT_40, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_40_DS, 162000,
130300, 12, 12, 8, 55, 55, 55 }, /* 162 Mb */
- [56] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 216000,
+ [56] = { RC_HT_DT_40, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_40_DS, 216000,
162800, 13, 13, 8, 56, 56, 56 }, /* 216 Mb */
- [57] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 243000,
+ [57] = { RC_HT_DT_40, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_40_DS, 243000,
178200, 14, 14, 8, 57, 57, 57 }, /* 243 Mb */
- [58] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 270000,
+ [58] = { RC_HT_DT_40, TRUE_N2_ALL_N1_T, WLAN_RC_PHY_HT_40_DS, 270000,
192100, 15, 15, 8, 58, 59, 59 }, /* 270 Mb */
- [59] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS_HGI, 300000,
+ [59] = { RC_HT_DT_40, TRUE_N2_ALL_N1_T_D, WLAN_RC_PHY_HT_40_DS_HGI, 300000,
207000, 15, 15, 8, 58, 59, 59 }, /* 300 Mb */
- [60] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 40500,
+ [60] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_40_TS, 40500,
36100, 16, 16, 8, 60, 60, 60 }, /* 40.5 Mb */
- [61] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 81000,
+ [61] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_40_TS, 81000,
72900, 17, 17, 8, 61, 61, 61 }, /* 81 Mb */
- [62] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 121500,
+ [62] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_40_TS, 121500,
108300, 18, 18, 8, 62, 62, 62 }, /* 121.5 Mb */
- [63] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 162000,
+ [63] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_40_TS, 162000,
142000, 19, 19, 8, 63, 63, 63 }, /* 162 Mb */
- [64] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 243000,
+ [64] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_40_TS, 243000,
205100, 20, 20, 8, 64, 65, 65 }, /* 243 Mb */
- [65] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS_HGI, 270000,
- 224700, 20, 20, 8, 64, 65, 65 }, /* 270 Mb */
- [66] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 324000,
+ [65] = { RC_INVALID, FALSE, WLAN_RC_PHY_HT_40_TS_HGI, 270000,
+ 224700, 20, 20, 8, 64, 65, 65 }, /* 170 Mb */
+ [66] = { RC_HT_T_40, FALSE, WLAN_RC_PHY_HT_40_TS, 324000,
263100, 21, 21, 8, 66, 67, 67 }, /* 324 Mb */
- [67] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 360000,
+ [67] = { RC_HT_T_40, TRUE_N2_T_N1_T, WLAN_RC_PHY_HT_40_TS_HGI, 360000,
288000, 21, 21, 8, 66, 67, 67 }, /* 360 Mb */
- [68] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 364500,
+ [68] = { RC_HT_T_40, FALSE, WLAN_RC_PHY_HT_40_TS, 364500,
290700, 22, 22, 8, 68, 69, 69 }, /* 364.5 Mb */
- [69] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 405000,
+ [69] = { RC_HT_T_40, TRUE_N2_T_N1_T, WLAN_RC_PHY_HT_40_TS_HGI, 405000,
317200, 22, 22, 8, 68, 69, 69 }, /* 405 Mb */
- [70] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 405000,
+ [70] = { RC_HT_T_40, FALSE, WLAN_RC_PHY_HT_40_TS, 405000,
317200, 23, 23, 8, 70, 71, 71 }, /* 405 Mb */
- [71] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 450000,
+ [71] = { RC_HT_T_40, TRUE_N2_T_N1_T, WLAN_RC_PHY_HT_40_TS_HGI, 450000,
346400, 23, 23, 8, 70, 71, 71 }, /* 450 Mb */
},
50, /* probe interval */
@@ -324,21 +324,21 @@ static const struct ath_rate_table ar5416_11a_ratetable = {
8,
0,
{
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
5400, 0, 12, 0},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
7800, 1, 18, 0},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
10000, 2, 24, 2},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
13900, 3, 36, 2},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
17300, 4, 48, 4},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
23000, 5, 72, 4},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
27400, 6, 96, 4},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
29300, 7, 108, 4},
},
50, /* probe interval */
@@ -349,29 +349,29 @@ static const struct ath_rate_table ar5416_11g_ratetable = {
12,
0,
{
- { RC_L_SDT, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */
900, 0, 2, 0},
- { RC_L_SDT, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */
1900, 1, 4, 1},
- { RC_L_SDT, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */
4900, 2, 11, 2},
- { RC_L_SDT, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */
8100, 3, 22, 3},
- { RC_INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
+ { RC_INVALID, FALSE, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */
5400, 4, 12, 4},
- { RC_INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
+ { RC_INVALID, FALSE, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */
7800, 5, 18, 4},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */
10000, 6, 24, 6},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */
13900, 7, 36, 6},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */
17300, 8, 48, 8},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */
23000, 9, 72, 8},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */
27400, 10, 96, 8},
- { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
+ { RC_L_SDT, FALSE, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */
29300, 11, 108, 8},
},
50, /* probe interval */
@@ -541,6 +541,72 @@ static u8 ath_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv,
return hi;
}

+static bool ath_valid_txbf_rate(struct ath_rate_priv *ath_rc_priv,
+ const struct ath_rate_table *rate_table,
+ struct ath_rateset *rateset,
+ u32 capflag, u8 i, u8 j)
+{
+ u32 phy = rate_table->info[j].phy;
+ u16 rate_flags = rate_table->info[j].rate_flags;
+ u8 rate = rateset->rs_rates[i];
+
+ if (ath_rc_priv->usedNss == NSS_DIM_2) {
+ if (RC_TS_ONLY(rate_table->info[rate].rate_flags)) {
+ if (!(rate_table->info[j].tx_bf & TRUE_TS_N2))
+ return true;
+ if (capflag & WLAN_RC_40_FLAG) {
+ if ((phy < WLAN_RC_PHY_HT_40_SS) ||
+ ((phy > WLAN_RC_PHY_HT_40_TS) &&
+ (phy < WLAN_RC_PHY_HT_40_SS_HGI)))
+ return true;
+ }
+ } else if (RC_DS_ONLY(rate_table->info[rate].rate_flags)) {
+ if (!(rate_table->info[j].tx_bf & TRUE_DS_N2))
+ return true;
+
+ if (capflag & WLAN_RC_40_FLAG) {
+ if ((phy < WLAN_RC_PHY_HT_40_SS) ||
+ (phy > WLAN_RC_PHY_HT_40_TS))
+ return true;
+ }
+ }
+ } else if (ath_rc_priv->usedNss == NSS_DIM_1) {
+ if (RC_TS_ONLY(rate_table->info[rate].rate_flags)) {
+ if (!(rate_table->info[j].tx_bf & TRUE_TS_N1))
+ return true;
+
+ if (capflag & WLAN_RC_40_FLAG) {
+ if ((phy < WLAN_RC_PHY_HT_40_SS) ||
+ ((phy > WLAN_RC_PHY_HT_40_TS) &&
+ (phy < WLAN_RC_PHY_HT_40_SS_HGI)))
+ return true;
+ }
+ } else if (RC_DS_ONLY(rate_table->info[rate].rate_flags)) {
+ if (!(rate_table->info[j].tx_bf & TRUE_DS_N1))
+ return true;
+
+ if (capflag & WLAN_RC_40_FLAG) {
+ if ((phy < WLAN_RC_PHY_HT_40_SS) ||
+ ((phy > WLAN_RC_PHY_HT_40_TS) &&
+ (phy < WLAN_RC_PHY_HT_40_SS_HGI)))
+ return true;
+ }
+ } else if (RC_SS_ONLY(rate_table->info[rate].rate_flags)) {
+ if (!(rate_table->info[j].tx_bf & TRUE_SS_N1))
+ return true;
+
+ if (capflag & WLAN_RC_40_FLAG) {
+ if ((phy < WLAN_RC_PHY_HT_40_SS) ||
+ (phy > WLAN_RC_PHY_HT_40_TS))
+ return true;
+ }
+ }
+ } else {
+ if (!WLAN_RC_PHY_HT_VALID(rate_flags, capflag))
+ return true;
+ }
+ return false;
+}
static u8 ath_rc_setvalid_htrates(struct ath_rate_priv *ath_rc_priv,
const struct ath_rate_table *rate_table,
u8 *mcs_set, u32 capflag)
@@ -557,6 +623,19 @@ static u8 ath_rc_setvalid_htrates(struct ath_rate_priv *ath_rc_priv,
u8 rate = rateset->rs_rates[i];
u8 dot11rate = rate_table->info[j].dot11rate;

+ if (capflag & WLAN_RC_TxBF_FLAG) {
+ if ((rate != dot11rate) || !WLAN_RC_PHY_HT(phy))
+ continue;
+ else {
+ if (ath_valid_txbf_rate(ath_rc_priv,
+ rate_table,
+ rateset,
+ capflag,
+ i, j))
+ continue;
+ }
+ }
+
if ((rate != dot11rate) || !WLAN_RC_PHY_HT(phy) ||
!(rate_flags & WLAN_RC_CAP_STREAM(capflag)) ||
!WLAN_RC_PHY_HT_VALID(rate_flags, capflag))
@@ -749,6 +828,14 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
if (rate_control_send_low(sta, priv_sta, txrc))
return;

+#define MS(_v, _f) (((_v) & _f) >> _f##_S)
+ ath_rc_priv->txbf_sounding = 0;
+ if (ieee80211_has_order(fc)) {
+ ath_rc_priv->txbf_sounding = 1;
+ if (sta->ht_cap.staggered_sounding)
+ tx_info->flags |= IEEE80211_TX_CTL_STAG_SOUND;
+ }
+
/*
* For Multi Rate Retry we use a different number of
* retry attempt counts. This ends up looking like this:
@@ -764,17 +851,26 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
rate_table = ath_rc_priv->rate_table;
rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table, &is_probe);

+ if (ath_rc_priv->txbf_sounding) {
+ if (rix > 9)
+ rix -= 2;
+ if (rix == 9)
+ rix = 8;
+ }
+
/*
* If we're in HT mode and both us and our peer supports LDPC.
* We don't need to check our own device's capabilities as our own
* ht capabilities would have already been intersected with our peer's.
*/
if (conf_is_ht(&sc->hw->conf) &&
- (sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
+ (sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING) &&
+ !(ath_rc_priv->txbf))
tx_info->flags |= IEEE80211_TX_CTL_LDPC;

if (conf_is_ht(&sc->hw->conf) &&
- (sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC))
+ (sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC) &&
+ !(ath_rc_priv->txbf))
tx_info->flags |= (1 << IEEE80211_TX_CTL_STBC_SHIFT);

if (is_probe) {
@@ -807,6 +903,16 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
/* All other rates in the series have RTS enabled */
ath_rc_rate_set_series(rate_table, &rates[i], txrc,
try_per_rate, rix, 1);
+
+ if (ath_rc_priv->txbf && ath_rc_priv->txbf_sounding) {
+ if (rates[i].flags & IEEE80211_TX_RC_MCS)
+ rates[i].flags &= ~IEEE80211_TX_RC_SHORT_GI;
+ else {
+ /* Remove sounding */
+ hdr->frame_control &=
+ ~cpu_to_le16(IEEE80211_FCTL_ORDER);
+ }
+ }
}

/*
@@ -1163,6 +1269,9 @@ static void ath_rc_tx_status(struct ath_softc *sc,
!(ath_rc_priv->ht_cap & WLAN_RC_40_FLAG))
return;

+ if (ath_rc_priv->txbf_sounding)
+ return;
+
rix = ath_rc_get_rateindex(rate_table, &rates[i]);
ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries, long_retry);
}
@@ -1189,6 +1298,10 @@ struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc,
}
}

+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
static void ath_rc_init(struct ath_softc *sc,
struct ath_rate_priv *ath_rc_priv,
struct ieee80211_supported_band *sband,
@@ -1218,6 +1331,15 @@ static void ath_rc_init(struct ath_softc *sc,
ath_rc_priv->valid_phy_ratecnt[i] = 0;
}

+ ath_rc_priv->usedNss = 0;
+ if (ath_rc_priv->txbf) {
+ ath_rc_priv->usedNss = MIN(MIN((ar5416_get_ntxchains(
+ sc->sc_ah->txchainmask) - 1),
+ TX_STREAM_USED_NUM(ath_rc_priv)),
+ sta->ht_cap.channel_estimation_cap
+ + 1);
+ }
+
if (!rateset->rs_nrates) {
/* No working rate, just initialize valid rates */
hi = ath_rc_init_validrates(ath_rc_priv, rate_table,
@@ -1279,6 +1401,8 @@ static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta,
caps |= WLAN_RC_40_FLAG;
if (is_sgi)
caps |= WLAN_RC_SGI_FLAG;
+ if (sta->txbf)
+ caps |= WLAN_RC_TxBF_FLAG;
}

return caps;
@@ -1437,6 +1561,9 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband,
rate_table = ath_choose_rate_table(sc, sband->band,
sta->ht_cap.ht_supported);

+ if (sta->txbf)
+ ath_rc_priv->txbf = 1;
+
ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta, is_cw40, is_sgi);
ath_rc_init(sc, priv_sta, sband, sta, rate_table);
}
diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h
index 2f46a22..ec2fe4e 100644
--- a/drivers/net/wireless/ath/ath9k/rc.h
+++ b/drivers/net/wireless/ath/ath9k/rc.h
@@ -36,11 +36,80 @@ struct ath_softc;
#define RC_HT_20 0x0010
#define RC_HT_40 0x0020

+/* flag for TxBF */
+/*
+ * N2;N1 mean Nss(number of spatial streams) of Beamforming
+ * Nss=2 : dualStream & tripleStream mode (DS & TS)
+ * Nss=1 : singleStream ,dualStream mode & tripleStream mode (SS, DS & TS)
+ *
+ * TRUE_AS_NB
+ * A: which stream mode supported (TS, DS or SS)
+ * B: under which Nss number
+*/
+#define FALSE 0
+#define TRUE_TS_N2 (0x01)
+#define TRUE_DS_N2 (0x02)
+#define TRUE_TS_DS_N2 (TRUE_TS_N2|TRUE_DS_N2)
+#define TRUE_TS_N1 (0x04)
+#define TRUE_DS_N1 (0x08)
+#define TRUE_SS_N1 (0x10)
+#define TRUE_TS_SS_N1 (TRUE_TS_N1|TRUE_SS_N1)
+#define TRUE_DS_SS_N1 (TRUE_DS_N1|TRUE_SS_N1)
+#define TRUE_TS_DS_N1 (TRUE_TS_N1|TRUE_DS_N1)
+#define TRUE_ALL_N1 (TRUE_TS_N1|TRUE_DS_N1|TRUE_SS_N1)
+
+/*
+ * TRUE_N2_A_N1_B
+ * A: support under Nss=2
+ * B: support under Nss=1
+ * ALL : rate supported for all configuration of its Nss
+ * S,D,T : rate supported for single, dual ,triple stream of its Nss
+ * F : rate not supported
+ *
+ * (Nss2 | Nss1)
+ */
+#define TRUE_N_1_2_ALL (TRUE_TS_DS_N2|TRUE_ALL_N1)
+#define FALSE_N_ALL (FALSE|FALSE)
+#define TRUE_N2_ALL_N1_F (TRUE_TS_DS_N2|FALSE)
+#define TRUE_N2_T_N1_F (TRUE_TS_N2|FALSE)
+#define TRUE_N2_D_N1_F (TRUE_DS_N2|FALSE)
+#define TRUE_N2_ALL_N1_T (TRUE_TS_DS_N2|TRUE_TS_N1)
+#define TRUE_N2_T_N1_T (TRUE_TS_N2|TRUE_TS_N1)
+#define TRUE_N2_D_N1_T (TRUE_DS_N2|TRUE_TS_N1)
+#define TRUE_N2_F_N1_T (FALSE|TRUE_TS_N1)
+#define TRUE_N2_ALL_N1_D (TRUE_TS_DS_N2|TRUE_DS_N1)
+#define TRUE_N2_T_N1_D (TRUE_TS_N2|TRUE_DS_N1)
+#define TRUE_N2_D_N1_D (TRUE_DS_N2|TRUE_DS_N1)
+#define TRUE_N2_F_N1_D (FALSE|TRUE_DS_N1)
+#define TRUE_N2_ALL_N1_S (TRUE_TS_DS_N2 | TRUE_SS_N1)
+#define TRUE_N2_T_N1_S (TRUE_TS_N2 | TRUE_SS_N1)
+#define TRUE_N2_D_N1_S (TRUE_DS_N2 | TRUE_SS_N1)
+#define TRUE_N2_F_N1_S (FALSE | TRUE_SS_N1)
+#define TRUE_N2_ALL_N1_T_S (TRUE_TS_DS_N2 | TRUE_TS_SS_N1)
+#define TRUE_N2_T_N1_T_S (TRUE_TS_N2 | TRUE_TS_SS_N1)
+#define TRUE_N2_D_N1_T_S (TRUE_DS_N2 | TRUE_TS_SS_N1)
+#define TRUE_N2_F_N1_T_S (FALSE | TRUE_TS_SS_N1)
+#define TRUE_N2_ALL_N1_D_S (TRUE_TS_DS_N2 | TRUE_DS_SS_N1)
+#define TRUE_N2_T_N1_D_S (TRUE_TS_N2 | TRUE_DS_SS_N1)
+#define TRUE_N2_D_N1_D_S (TRUE_DS_N2 | TRUE_DS_SS_N1)
+#define TRUE_N2_F_N1_D_S (FALSE | TRUE_DS_SS_N1)
+#define TRUE_N2_ALL_N1_T_D (TRUE_TS_DS_N2 | TRUE_TS_DS_N1)
+#define TRUE_N2_T_N1_T_D (TRUE_TS_N2 | TRUE_TS_DS_N1)
+#define TRUE_N2_D_N1_T_D (TRUE_DS_N2 | TRUE_TS_DS_N1)
+#define TRUE_N2_F_N1_T_D (FALSE | TRUE_TS_DS_N1)
+#define TRUE_N2_T_N1_ALL (TRUE_TS_N2 | TRUE_ALL_N1)
+#define TRUE_N2_D_N1_ALL (TRUE_DS_N2 | TRUE_ALL_N1)
+#define TRUE_N2_F_N1_ALL (FALSE | TRUE_ALL_N1)
+
+
+
#define RC_STREAM_MASK 0xe
#define RC_DS_OR_LATER(f) ((((f) & RC_STREAM_MASK) == RC_DS) || \
(((f) & RC_STREAM_MASK) == (RC_DS | RC_TS)))
#define RC_TS_ONLY(f) (((f) & RC_STREAM_MASK) == RC_TS)
#define RC_SS_OR_LEGACY(f) ((f) & (RC_SS | RC_LEGACY))
+#define RC_DS_ONLY(f) (((f) & RC_STREAM_MASK) == RC_DS)
+#define RC_SS_ONLY(f) (((f) & RC_STREAM_MASK) == RC_SS)

#define RC_HT_2040 (RC_HT_20 | RC_HT_40)
#define RC_ALL_STREAM (RC_SS | RC_DS | RC_TS)
@@ -66,6 +135,9 @@ struct ath_softc;

#define RC_ALL (RC_LEGACY | RC_HT_2040 | RC_ALL_STREAM)

+#define WLAN_RC_TxBF_FLAG (0x400)
+#define WLAN_RC_CEC_FLAG (0x1800)
+#define WLAN_RC_CEC_FLAG_S 11
enum {
WLAN_RC_PHY_OFDM,
WLAN_RC_PHY_CCK,
@@ -132,7 +204,12 @@ enum {
#define WLAN_RC_40_FLAG (0x04)
#define WLAN_RC_SGI_FLAG (0x08)
#define WLAN_RC_HT_FLAG (0x10)
-
+#define ATH_RC_TXBF_FLAG 0x800
+#define ATH_RC_CEC_FLAG 0x3000
+#define ATH_RC_CEC_FLAG_S 12
+#define ATH_RC_SOUNDING_FLAG 0x4000
+#define VALID_TXBF_RATE(_rate, _nss) ((_rate >= 8) && \
+ (_rate < ((_nss == 2) ? 24 : 16)))
/**
* struct ath_rate_table - Rate Control table
* @rate_cnt: total number of rates for the given wireless mode
@@ -157,6 +234,7 @@ struct ath_rate_table {
int mcs_start;
struct {
u16 rate_flags;
+ u8 tx_bf;
u8 phy;
u32 ratekbps;
u32 user_ratekbps;
@@ -216,6 +294,9 @@ struct ath_rate_priv {
u32 probe_interval;
u32 prev_data_rix;
u32 tx_triglevel_max;
+ u8 usedNss;
+ u8 txbf:1, /* transmit beamforming capable */
+ txbf_sounding:1; /* sounding indicator */
struct ath_rateset neg_rates;
struct ath_rateset neg_ht_rates;
struct ath_rate_softc *asc;
@@ -249,5 +330,12 @@ static inline void ath_rate_control_unregister(void)
{
}
#endif
+#define NSS_DIM_2 0x2
+#define NSS_DIM_1 0x1
+
+#define TX_STREAM_USED_NUM(_pSib) (3*(_pSib->ht_cap & (WLAN_RC_TS_FLAG)) + \
+ 2*(_pSib->ht_cap & (WLAN_RC_DS_FLAG)) + 1)
+#define OPPOSITE_CEC_NUM(_capflag) \
+ (((_capflag & WLAN_RC_CEC_FLAG) >> WLAN_RC_CEC_FLAG_S) + 1)

#endif /* RC_H */
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 2bc422e..73e3723 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -668,18 +668,32 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
struct list_head *bf_q)
{
#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
+#define MS(_v, _f) (((_v) & _f) >> _f##_S)
struct ath_buf *bf, *bf_first, *bf_prev = NULL;
int rl = 0, nframes = 0, ndelim, prev_al = 0;
u16 aggr_limit = 0, al = 0, bpad = 0,
al_delta, h_baw = tid->baw_size / 2;
enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
struct ieee80211_tx_info *tx_info;
+ u8 is_prev_sounding = 0;

bf_first = list_first_entry(&tid->buf_q, struct ath_buf, list);

do {
bf = list_first_entry(&tid->buf_q, struct ath_buf, list);

+ /* send the sounding frame as a single frame */
+ if (bf->bf_flags &
+ (ATH9K_TXDESC_TXBF_SOUND | ATH9K_TXDESC_TXBF_STAG_SOUND)) {
+ if (nframes != 0)
+ break;
+ else
+ is_prev_sounding = 1;
+ }
+
+ if ((nframes == 1) & (is_prev_sounding))
+ break;
+
/* do not step over block-ack window */
if (!BAW_WITHIN(tid->seq_start, tid->baw_size, bf->bf_seqno)) {
status = ATH_AGGR_BAW_CLOSED;
@@ -1532,6 +1546,25 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf)
is_40 = !!(rates[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH);
is_sp = !!(rates[i].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE);

+ if (ieee80211_has_order(hdr->frame_control)) {
+ struct ieee80211_qos_htc_hdr *qos_hdr;
+ qos_hdr = (struct ieee80211_qos_htc_hdr *)skb->data;
+
+ if (ieee80211_has_order(hdr->frame_control)) {
+ if ((qos_hdr->htc & cpu_to_le32(
+ IEEE80211_HTC2_CSI_COMP_BF |
+ IEEE80211_HTC2_CSI_NONCOMP_BF))) {
+ if (tx_info->flags &
+ IEEE80211_TX_CTL_STAG_SOUND)
+ flags |=
+ ATH9K_TXDESC_TXBF_STAG_SOUND;
+ else
+ flags |=
+ ATH9K_TXDESC_TXBF_SOUND;
+ }
+ }
+ }
+
if (rates[i].flags & IEEE80211_TX_RC_MCS) {
/* MCS rates */
series[i].Rate = rix | 0x80;
@@ -1588,6 +1621,7 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf,
struct ath_softc *sc = aphy->sc;
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ath_node *an = NULL;
int hdrlen;
__le16 fc;
int padpos, padsize;
@@ -1634,7 +1668,11 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf,
bf->bf_frmlen += tx_info->control.hw_key->icv_len;
bf->bf_keyix = tx_info->control.hw_key->hw_key_idx;
} else {
- bf->bf_keyix = ATH9K_TXKEYIX_INVALID;
+ if (tx_info->control.sta) {
+ an = (struct ath_node *)tx_info->control.sta->drv_priv;
+ bf->bf_keyix = an->key_idx;
+ } else
+ bf->bf_keyix = ATH9K_TXKEYIX_INVALID;
}

if (ieee80211_is_data_qos(fc) && bf_isht(bf) &&
@@ -1998,6 +2036,9 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts,
tx_info->status.ampdu_ack_len = bf->bf_nframes - nbad;
}

+ if (ts->ts_flags & ATH9K_TX_BF_ERR)
+ tx_info->flags |= IEEE80211_TX_CTL_TXBF_UPDATE;
+
if ((ts->ts_status & ATH9K_TXERR_FILT) == 0 &&
(bf->bf_flags & ATH9K_TXDESC_NOACK) == 0 && update_rc) {
if (ieee80211_is_data(hdr->frame_control)) {
--
1.6.3.3


2010-11-10 16:21:34

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC 1/5] mac80211: Add support for transmit beam forming.

On Wed, 2010-11-10 at 17:53 +0530, Vivek Natarajan wrote:

> +struct ieee80211_txbf_caps {
> + u32 implicit_rx_capable:1,
> + rx_staggered_sounding:1,
> + tx_staggered_sounding:1,
> + rx_ndp_capable:1,
> + tx_ndp_capable:1,
> + implicit_txbf_capable:1,
> + calibration:2,
> + explicit_csi_txbf_capable:1,
> + explicit_noncomp_steering:1,
> + explicit_comp_steering:1,
> + explicit_csi_feedback:2,
> + explicit_noncomp_bf:2,
> + explicit_comp_bf:2,
> + minimal_grouping:2,
> + csi_bfer_antennas:2,
> + noncomp_bfer_antennas:2,
> + comp_bfer_antennas:2,
> + csi_max_rows_bfer:2,
> + channel_estimation_cap:2,
> + reserved:3;
> +};

No way, never use bitfields.

> @@ -862,7 +901,7 @@ struct ieee80211_ht_cap {
> struct ieee80211_mcs_info mcs;
>
> __le16 extended_ht_cap_info;
> - __le32 tx_BF_cap_info;
> + struct ieee80211_txbf_caps tx_BF_cap_info;
> u8 antenna_selection_info;
> } __attribute__ ((packed));

keep __le32 and add #defines for the bits.

> @@ -321,6 +321,8 @@ struct ieee80211_bss_conf {
> * @IEEE80211_TX_CTL_LDPC: tells the driver to use LDPC for this frame
> * @IEEE80211_TX_CTL_STBC: Enables Space-Time Block Coding (STBC) for this
> * frame and selects the maximum number of streams that it can use.
> + * @IEEE80211_TX_CTL_TXBF_UPDATE: Channel information needs to be updated
> + * for beamforming of Tx frames.
> *
> * Note: If you have to add new flags to the enumeration, then don't
> * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary.
> @@ -349,6 +351,8 @@ enum mac80211_tx_control_flags {
> IEEE80211_TX_INTFL_NL80211_FRAME_TX = BIT(21),
> IEEE80211_TX_CTL_LDPC = BIT(22),
> IEEE80211_TX_CTL_STBC = BIT(23) | BIT(24),
> + IEEE80211_TX_CTL_TXBF_UPDATE = BIT(25),
> + IEEE80211_TX_CTL_STAG_SOUND = BIT(26),

Missing docs for the second entry.

> #define IEEE80211_TX_CTL_STBC_SHIFT 23
> @@ -364,7 +368,7 @@ enum mac80211_tx_control_flags {
> IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_STAT_AMPDU_NO_BACK | \
> IEEE80211_TX_CTL_RATE_CTRL_PROBE | IEEE80211_TX_CTL_PSPOLL_RESPONSE | \
> IEEE80211_TX_CTL_MORE_FRAMES | IEEE80211_TX_CTL_LDPC | \
> - IEEE80211_TX_CTL_STBC)
> + IEEE80211_TX_CTL_STBC | IEEE80211_TX_CTL_TXBF_UPDATE)

Therefore I cannot evaluate this change.

> @@ -900,7 +904,8 @@ struct ieee80211_sta {
> u8 addr[ETH_ALEN];
> u16 aid;
> struct ieee80211_sta_ht_cap ht_cap;
> -
> + bool txbf;

not sure I like names that short -- docs missing too

> @@ -698,6 +698,13 @@ static void sta_apply_parameters(struct ieee80211_local *local,
> params->ht_capa,
> &sta->sta.ht_cap);
>
> + if (sta->sta.ht_cap.explicit_compbf ||
> + sta->sta.ht_cap.explicit_noncompbf ||
> + sta->sta.ht_cap.implicit_bf) {
> + sta->sta.txbf = true;
> + sta->bf_update_cv = true;
> + }

I wonder at what point we should move the capability handling from
hostapd to the kernel ...

> @@ -99,6 +100,23 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
> /* handle MCS rate 32 too */
> if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
> ht_cap->mcs.rx_mask[32/8] |= 1;
> +
> + bfee = ht_cap_ie->tx_BF_cap_info;
> + bfmr = sband->ht_cap.txbf;

Nice variable names... what?

> + if (bfmr.explicit_comp_steering && (bfee.explicit_comp_bf != 0))
> + ht_cap->explicit_compbf = true;
> +
> + if (bfmr.explicit_noncomp_steering && (bfee.explicit_noncomp_bf != 0))
> + ht_cap->explicit_noncompbf = true;
> +
> + if (bfmr.implicit_txbf_capable && bfee.implicit_rx_capable)
> + ht_cap->implicit_bf = true;

xx = a && b;

instead of the if?

> --- a/net/mac80211/mlme.c
> +++ b/net/mac80211/mlme.c
> @@ -63,6 +63,8 @@
> #define TMR_RUNNING_TIMER 0
> #define TMR_RUNNING_CHANSW 1
>
> +#define TXBF_CV_TIMER 1000

Err ... missing units.


> +void ieee80211_txbf_cv_work(struct work_struct *work)
> +{
> + struct sta_info *sta =
> + container_of(work, struct sta_info, txbf_cv_work.work);
> + struct ieee80211_local *local = sta->local;
> +
> + sta->bf_update_cv = true;
> + ieee80211_queue_delayed_work(&local->hw,
> + &sta->txbf_cv_work, TXBF_CV_TIMER);
> +}

self arming -- how does it get cancelled properly?

also, this is part of a sta_info entry, so why is it in mlme.c??

> --- a/net/mac80211/rx.c
> +++ b/net/mac80211/rx.c
> @@ -1455,6 +1455,16 @@ ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx)
> if (!ieee80211_is_data_qos(hdr->frame_control))
> return RX_CONTINUE;
>
> + /* Qos frame with Order bit set indicates an HTC frame */
> + if (ieee80211_has_order(hdr->frame_control)) {
> + memmove(data + IEEE80211_QOS_HTC_LEN, data,
> + ieee80211_hdrlen(hdr->frame_control) -
> + IEEE80211_QOS_HTC_LEN);
> + hdr = (struct ieee80211_hdr *)skb_pull(rx->skb,
> + IEEE80211_QOS_HTC_LEN);
> + hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_ORDER);
> + }
> +
> /* remove the qos control field, update frame type and meta-data */
> memmove(data + IEEE80211_QOS_CTL_LEN, data,
> ieee80211_hdrlen(hdr->frame_control) - IEEE80211_QOS_CTL_LEN);
> diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
> index 6d8f897..829398e 100644
> --- a/net/mac80211/sta_info.c
> +++ b/net/mac80211/sta_info.c
> @@ -235,6 +235,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
> spin_lock_init(&sta->flaglock);
> INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
> INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
> + INIT_DELAYED_WORK(&sta->txbf_cv_work, ieee80211_txbf_cv_work);
> mutex_init(&sta->ampdu_mlme.mtx);
>
> memcpy(sta->sta.addr, addr, ETH_ALEN);
> @@ -691,6 +692,7 @@ static int __must_check __sta_info_destroy(struct sta_info *sta)
> wiphy_debug(local->hw.wiphy, "Removed STA %pM\n", sta->sta.addr);
> #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
> cancel_work_sync(&sta->drv_unblock_wk);
> + cancel_delayed_work_sync(&sta->txbf_cv_work);
>
> rate_control_remove_sta_debugfs(sta);
> ieee80211_sta_debugfs_remove(sta);
> diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
> index 9265aca..61631e3 100644
> --- a/net/mac80211/sta_info.h
> +++ b/net/mac80211/sta_info.h
> @@ -312,6 +312,12 @@ struct sta_info {
> struct sta_ampdu_mlme ampdu_mlme;
> u8 timer_to_tid[STA_TID_NUM];
>
> + bool txbf;
> + bool bf_update_cv;
> + bool bf_sound_pending;
> + bool allow_cv_update;
> + struct delayed_work txbf_cv_work;
> +
> #ifdef CONFIG_MAC80211_MESH
> /*
> * Mesh peer link attributes
> diff --git a/net/mac80211/status.c b/net/mac80211/status.c
> index 3153c19..b0447ca 100644
> --- a/net/mac80211/status.c
> +++ b/net/mac80211/status.c
> @@ -209,6 +209,25 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
> return;
> }
>
> + if (ieee80211_has_order(fc)) {
> + if ((info->flags & IEEE80211_TX_STAT_ACK) &&
> + (sta->bf_sound_pending)) {
> + sta->bf_sound_pending = false;
> + ieee80211_queue_delayed_work(&local->hw,
> + &sta->txbf_cv_work, 1000);

1000 what?

> + } else
> + sta->bf_update_cv = true;
> + }
> +
> +
> + if ((info->flags & IEEE80211_TX_CTL_TXBF_UPDATE) &&
> + !(sta->bf_sound_pending)) {
> + if (sta->sta.ht_cap.explicit_compbf ||
> + sta->sta.ht_cap.explicit_noncompbf ||
> + sta->sta.ht_cap.implicit_bf)
> + sta->bf_update_cv = true;
> + }
> +
> if ((local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) &&
> (rates_idx != -1))
> sta->last_tx_rate = info->status.rates[rates_idx];
> diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
> index 96c5943..5900cf2 100644
> --- a/net/mac80211/tx.c
> +++ b/net/mac80211/tx.c
> @@ -1888,6 +1888,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
> if ((sta_flags & WLAN_STA_WME) && local->hw.queues >= 4) {
> fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
> hdrlen += 2;
> + if (sta->bf_update_cv)
> + hdrlen += 4;

4?

> @@ -1973,9 +1975,28 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
>
> if (ieee80211_is_data_qos(fc)) {
> __le16 *qos_control;
> -
> - qos_control = (__le16*) skb_push(skb, 2);
> - memcpy(skb_push(skb, hdrlen - 2), &hdr, hdrlen - 2);
> + __le32 *htc;
> +
> + if (sta->bf_update_cv) {

It seems to me that this variable access is racy between the hdrlen += 4
and checking it again here since there's no locking on it.

> + hdr.frame_control |= cpu_to_le16(IEEE80211_FCTL_ORDER);
> + htc = (__le32 *) skb_push(skb, 4);
> + sta->bf_sound_pending = true;
> + *htc = 0;
> + sta->bf_update_cv = false;
> +
> + if (sta->sta.ht_cap.explicit_compbf)
> + *htc |= IEEE80211_HTC2_CSI_COMP_BF;

no cpu_to_le32? have you run sparse on this?

> + qos_control = (__le16 *) skb_push(skb, 2);
> + memcpy(skb_push(skb, hdrlen - 6), &hdr, hdrlen - 6);
> + } else {
> + qos_control = (__le16 *) skb_push(skb, 2);
> + memcpy(skb_push(skb, hdrlen - 2), &hdr, hdrlen - 2);
> + };

}; ???

also why not move this out of the if()?

johannes


2010-11-10 12:44:10

by Felix Fietkau

[permalink] [raw]
Subject: Re: [RFC 1/5] mac80211: Add support for transmit beam forming.

On 2010-11-10 1:23 PM, Vivek Natarajan wrote:
> Enable beamforming if the driver and the AP are capable of sending
> and receiving beam-formed frames.
>
> Signed-off-by: Vivek Natarajan <[email protected]>
> ---
> include/linux/ieee80211.h | 41 ++++++++++++++++++++++++++++++++++++++++-
> include/net/cfg80211.h | 6 ++++++
> include/net/mac80211.h | 9 +++++++--
> net/mac80211/cfg.c | 7 +++++++
> net/mac80211/ht.c | 18 ++++++++++++++++++
> net/mac80211/ieee80211_i.h | 1 +
> net/mac80211/mlme.c | 19 +++++++++++++++++++
> net/mac80211/rx.c | 10 ++++++++++
> net/mac80211/sta_info.c | 2 ++
> net/mac80211/sta_info.h | 6 ++++++
> net/mac80211/status.c | 19 +++++++++++++++++++
> net/mac80211/tx.c | 27 ++++++++++++++++++++++++---
> net/mac80211/work.c | 1 +
> 13 files changed, 160 insertions(+), 6 deletions(-)
>
> diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
> index ed5a03c..ba92b73 100644
> --- a/include/linux/ieee80211.h
> +++ b/include/linux/ieee80211.h
[...]
> /**
> * ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set
> * @fc: frame control bytes in little-endian byteorder
> @@ -827,6 +843,29 @@ struct ieee80211_mcs_info {
> u8 reserved[3];
> } __attribute__((packed));
>
> +struct ieee80211_txbf_caps {
> + u32 implicit_rx_capable:1,
> + rx_staggered_sounding:1,
> + tx_staggered_sounding:1,
> + rx_ndp_capable:1,
> + tx_ndp_capable:1,
> + implicit_txbf_capable:1,
> + calibration:2,
> + explicit_csi_txbf_capable:1,
> + explicit_noncomp_steering:1,
> + explicit_comp_steering:1,
> + explicit_csi_feedback:2,
> + explicit_noncomp_bf:2,
> + explicit_comp_bf:2,
> + minimal_grouping:2,
> + csi_bfer_antennas:2,
> + noncomp_bfer_antennas:2,
> + comp_bfer_antennas:2,
> + csi_max_rows_bfer:2,
> + channel_estimation_cap:2,
> + reserved:3;
> +};
> +
> /* 802.11n HT capability MSC set */
> #define IEEE80211_HT_MCS_RX_HIGHEST_MASK 0x3ff
> #define IEEE80211_HT_MCS_TX_DEFINED 0x01
> @@ -862,7 +901,7 @@ struct ieee80211_ht_cap {
> struct ieee80211_mcs_info mcs;
>
> __le16 extended_ht_cap_info;
> - __le32 tx_BF_cap_info;
> + struct ieee80211_txbf_caps tx_BF_cap_info;
> u8 antenna_selection_info;
> } __attribute__ ((packed));
What about big endian? I think it's better to just use defines with the
proper masks - bitfields make handling endian differences harder.

- Felix

2010-11-10 12:24:07

by Vivek Natarajan

[permalink] [raw]
Subject: [RFC 3/5] ath9k_common: Add HTC field length if order bit is set.

Signed-off-by: Vivek Natarajan <[email protected]>
---
drivers/net/wireless/ath/ath9k/common.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
index 48b07c3..2e56200 100644
--- a/drivers/net/wireless/ath/ath9k/common.c
+++ b/drivers/net/wireless/ath/ath9k/common.c
@@ -36,6 +36,8 @@ int ath9k_cmn_padpos(__le16 frame_control)
if (ieee80211_is_data_qos(frame_control)) {
padpos += IEEE80211_QOS_CTL_LEN;
}
+ if (ieee80211_has_order(frame_control))
+ padpos += IEEE80211_QOS_HTC_LEN;

return padpos;
}
--
1.6.3.3


2010-11-10 13:06:26

by Felix Fietkau

[permalink] [raw]
Subject: Re: [RFC 4/5] ath9k: Add support for Tx beamforming feature.

On 2010-11-10 1:23 PM, Vivek Natarajan wrote:
> Beamforming is enabled with the latest hardware if the module parameter
> is set.
>
> Signed-off-by: Vivek Natarajan <[email protected]>
> ---
> drivers/net/wireless/ath/ath9k/ath9k.h | 2 +
> drivers/net/wireless/ath/ath9k/init.c | 9 +
> drivers/net/wireless/ath/ath9k/main.c | 22 ++
> drivers/net/wireless/ath/ath9k/rc.c | 453 ++++++++++++++++++++------------
> drivers/net/wireless/ath/ath9k/rc.h | 90 +++++++-
> drivers/net/wireless/ath/ath9k/xmit.c | 43 +++-
> 6 files changed, 454 insertions(+), 165 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
> index 8a2b04e..5216999 100644
> --- a/drivers/net/wireless/ath/ath9k/ath9k.h
> +++ b/drivers/net/wireless/ath/ath9k/ath9k.h
> @@ -270,6 +270,8 @@ struct ath_node {
> struct ath_atx_ac ac[WME_NUM_AC];
> u16 maxampdu;
> u8 mpdudensity;
> + bool txbf;
> + u8 key_idx;
> };
>
> #define AGGR_CLEANUP BIT(1)
> diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
> index f14fe53..531efe7 100644
> --- a/drivers/net/wireless/ath/ath9k/init.c
> +++ b/drivers/net/wireless/ath/ath9k/init.c
> @@ -38,6 +38,10 @@ int led_blink;
> module_param_named(blink, led_blink, int, 0444);
> MODULE_PARM_DESC(blink, "Enable LED blink on activity");
>
> +int txbf;
> +module_param_named(txbf, txbf, int, 0444);
> +MODULE_PARM_DESC(blink, "Enable TxBF");
> +
I don't think a module parameter is a good idea here. If you want to
make it possible to disable txbf, just add a debugfs entry.

> diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c
> index 85c8e93..0b9c405 100644
> --- a/drivers/net/wireless/ath/ath9k/rc.c
> +++ b/drivers/net/wireless/ath/ath9k/rc.c
> @@ -749,6 +828,14 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
> if (rate_control_send_low(sta, priv_sta, txrc))
> return;
>
> +#define MS(_v, _f) (((_v) & _f) >> _f##_S)
That one's already defined in hw.h

> @@ -807,6 +903,16 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
> /* All other rates in the series have RTS enabled */
> ath_rc_rate_set_series(rate_table, &rates[i], txrc,
> try_per_rate, rix, 1);
> +
> + if (ath_rc_priv->txbf && ath_rc_priv->txbf_sounding) {
> + if (rates[i].flags & IEEE80211_TX_RC_MCS)
> + rates[i].flags &= ~IEEE80211_TX_RC_SHORT_GI;
> + else {
> + /* Remove sounding */
> + hdr->frame_control &=
> + ~cpu_to_le16(IEEE80211_FCTL_ORDER);
Shouldn't mac80211 take care of ensuring hdr->frame_control sanity?
Doing this in the rate control module seems a little messy to me.

> @@ -1189,6 +1298,10 @@ struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc,
> }
> }
>
> +#ifndef MIN
> +#define MIN(a, b) (((a) < (b)) ? (a) : (b))
> +#endif
There's a Linux kernel macro for exactly the same purpose.

> diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
> index 2bc422e..73e3723 100644
> --- a/drivers/net/wireless/ath/ath9k/xmit.c
> +++ b/drivers/net/wireless/ath/ath9k/xmit.c
> @@ -668,18 +668,32 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
> struct list_head *bf_q)
> {
> #define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
> +#define MS(_v, _f) (((_v) & _f) >> _f##_S)
Again, already defined in hw.h

> struct ath_buf *bf, *bf_first, *bf_prev = NULL;
> int rl = 0, nframes = 0, ndelim, prev_al = 0;
> u16 aggr_limit = 0, al = 0, bpad = 0,
> al_delta, h_baw = tid->baw_size / 2;
> enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
> struct ieee80211_tx_info *tx_info;
> + u8 is_prev_sounding = 0;
>
> bf_first = list_first_entry(&tid->buf_q, struct ath_buf, list);
>
> do {
> bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
>
> + /* send the sounding frame as a single frame */
> + if (bf->bf_flags &
> + (ATH9K_TXDESC_TXBF_SOUND | ATH9K_TXDESC_TXBF_STAG_SOUND)) {
> + if (nframes != 0)
> + break;
> + else
> + is_prev_sounding = 1;
> + }
> +
> + if ((nframes == 1) & (is_prev_sounding))
> + break;
> +
How about adding this check in a similar way as the
IEEE80211_TX_CTL_RATE_CTRL_PROBE flag check, instead of adding the
is_prev_sounding variable. That way you won't introduce the same bug
that rate control probing had before I fixed it.

- Felix