linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC 1/5] mac80211: Add support for transmit beam forming.
@ 2010-11-10 12:23 Vivek Natarajan
  2010-11-10 12:23 ` [RFC 2/5] ath: Add a keycache entry if beamforming is enabled Vivek Natarajan
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Vivek Natarajan @ 2010-11-10 12:23 UTC (permalink / raw)
  To: linux-wireless

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

Signed-off-by: Vivek Natarajan <vnatarajan@atheros.com>
---
 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


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC 2/5] ath: Add a keycache entry if beamforming is enabled.
  2010-11-10 12:23 [RFC 1/5] mac80211: Add support for transmit beam forming Vivek Natarajan
@ 2010-11-10 12:23 ` Vivek Natarajan
  2010-11-10 12:23   ` [RFC 3/5] ath9k_common: Add HTC field length if order bit is set Vivek Natarajan
  2010-11-10 12:47   ` [RFC 2/5] ath: Add a keycache entry if beamforming is enabled Felix Fietkau
  2010-11-10 12:44 ` [RFC 1/5] mac80211: Add support for transmit beam forming Felix Fietkau
  2010-11-10 16:22 ` Johannes Berg
  2 siblings, 2 replies; 11+ messages in thread
From: Vivek Natarajan @ 2010-11-10 12:23 UTC (permalink / raw)
  To: linux-wireless

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 <vnatarajan@atheros.com>
---
 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


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC 3/5] ath9k_common: Add HTC field length if order bit is set.
  2010-11-10 12:23 ` [RFC 2/5] ath: Add a keycache entry if beamforming is enabled Vivek Natarajan
@ 2010-11-10 12:23   ` Vivek Natarajan
  2010-11-10 12:23     ` [RFC 4/5] ath9k: Add support for Tx beamforming feature Vivek Natarajan
  2010-11-10 12:47   ` [RFC 2/5] ath: Add a keycache entry if beamforming is enabled Felix Fietkau
  1 sibling, 1 reply; 11+ messages in thread
From: Vivek Natarajan @ 2010-11-10 12:23 UTC (permalink / raw)
  To: linux-wireless

Signed-off-by: Vivek Natarajan <vnatarajan@atheros.com>
---
 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


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC 4/5] ath9k: Add support for Tx beamforming feature.
  2010-11-10 12:23   ` [RFC 3/5] ath9k_common: Add HTC field length if order bit is set Vivek Natarajan
@ 2010-11-10 12:23     ` Vivek Natarajan
  2010-11-10 12:23       ` [RFC 5/5] ath9k_hw: Add support for Tx beamforming Vivek Natarajan
  2010-11-10 13:06       ` [RFC 4/5] ath9k: Add support for Tx beamforming feature Felix Fietkau
  0 siblings, 2 replies; 11+ messages in thread
From: Vivek Natarajan @ 2010-11-10 12:23 UTC (permalink / raw)
  To: linux-wireless

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

Signed-off-by: Vivek Natarajan <vnatarajan@atheros.com>
---
 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


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [RFC 5/5] ath9k_hw: Add support for Tx beamforming.
  2010-11-10 12:23     ` [RFC 4/5] ath9k: Add support for Tx beamforming feature Vivek Natarajan
@ 2010-11-10 12:23       ` Vivek Natarajan
  2010-11-10 13:26         ` Felix Fietkau
  2010-11-10 17:25         ` Luis R. Rodriguez
  2010-11-10 13:06       ` [RFC 4/5] ath9k: Add support for Tx beamforming feature Felix Fietkau
  1 sibling, 2 replies; 11+ messages in thread
From: Vivek Natarajan @ 2010-11-10 12:23 UTC (permalink / raw)
  To: linux-wireless

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

Signed-off-by: Vivek Natarajan <vnatarajan@atheros.com>
---
 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


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [RFC 1/5] mac80211: Add support for transmit beam forming.
  2010-11-10 12:23 [RFC 1/5] mac80211: Add support for transmit beam forming Vivek Natarajan
  2010-11-10 12:23 ` [RFC 2/5] ath: Add a keycache entry if beamforming is enabled Vivek Natarajan
@ 2010-11-10 12:44 ` Felix Fietkau
  2010-11-10 16:22 ` Johannes Berg
  2 siblings, 0 replies; 11+ messages in thread
From: Felix Fietkau @ 2010-11-10 12:44 UTC (permalink / raw)
  To: Vivek Natarajan; +Cc: linux-wireless

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 <vnatarajan@atheros.com>
> ---
>  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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC 2/5] ath: Add a keycache entry if beamforming is enabled.
  2010-11-10 12:23 ` [RFC 2/5] ath: Add a keycache entry if beamforming is enabled Vivek Natarajan
  2010-11-10 12:23   ` [RFC 3/5] ath9k_common: Add HTC field length if order bit is set Vivek Natarajan
@ 2010-11-10 12:47   ` Felix Fietkau
  1 sibling, 0 replies; 11+ messages in thread
From: Felix Fietkau @ 2010-11-10 12:47 UTC (permalink / raw)
  To: Vivek Natarajan; +Cc: linux-wireless

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 <vnatarajan@atheros.com>
> ---
>  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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC 4/5] ath9k: Add support for Tx beamforming feature.
  2010-11-10 12:23     ` [RFC 4/5] ath9k: Add support for Tx beamforming feature Vivek Natarajan
  2010-11-10 12:23       ` [RFC 5/5] ath9k_hw: Add support for Tx beamforming Vivek Natarajan
@ 2010-11-10 13:06       ` Felix Fietkau
  1 sibling, 0 replies; 11+ messages in thread
From: Felix Fietkau @ 2010-11-10 13:06 UTC (permalink / raw)
  To: Vivek Natarajan; +Cc: linux-wireless

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 <vnatarajan@atheros.com>
> ---
>  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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC 5/5] ath9k_hw: Add support for Tx beamforming.
  2010-11-10 12:23       ` [RFC 5/5] ath9k_hw: Add support for Tx beamforming Vivek Natarajan
@ 2010-11-10 13:26         ` Felix Fietkau
  2010-11-10 17:25         ` Luis R. Rodriguez
  1 sibling, 0 replies; 11+ messages in thread
From: Felix Fietkau @ 2010-11-10 13:26 UTC (permalink / raw)
  To: Vivek Natarajan; +Cc: linux-wireless

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 <vnatarajan@atheros.com>
> ---
>  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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC 1/5] mac80211: Add support for transmit beam forming.
  2010-11-10 12:23 [RFC 1/5] mac80211: Add support for transmit beam forming Vivek Natarajan
  2010-11-10 12:23 ` [RFC 2/5] ath: Add a keycache entry if beamforming is enabled Vivek Natarajan
  2010-11-10 12:44 ` [RFC 1/5] mac80211: Add support for transmit beam forming Felix Fietkau
@ 2010-11-10 16:22 ` Johannes Berg
  2 siblings, 0 replies; 11+ messages in thread
From: Johannes Berg @ 2010-11-10 16:22 UTC (permalink / raw)
  To: Vivek Natarajan; +Cc: linux-wireless

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


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC 5/5] ath9k_hw: Add support for Tx beamforming.
  2010-11-10 12:23       ` [RFC 5/5] ath9k_hw: Add support for Tx beamforming Vivek Natarajan
  2010-11-10 13:26         ` Felix Fietkau
@ 2010-11-10 17:25         ` Luis R. Rodriguez
  1 sibling, 0 replies; 11+ messages in thread
From: Luis R. Rodriguez @ 2010-11-10 17:25 UTC (permalink / raw)
  To: Vivek Natarajan; +Cc: linux-wireless

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

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

  Luis

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2010-11-10 17:26 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-11-10 12:23 [RFC 1/5] mac80211: Add support for transmit beam forming Vivek Natarajan
2010-11-10 12:23 ` [RFC 2/5] ath: Add a keycache entry if beamforming is enabled Vivek Natarajan
2010-11-10 12:23   ` [RFC 3/5] ath9k_common: Add HTC field length if order bit is set Vivek Natarajan
2010-11-10 12:23     ` [RFC 4/5] ath9k: Add support for Tx beamforming feature Vivek Natarajan
2010-11-10 12:23       ` [RFC 5/5] ath9k_hw: Add support for Tx beamforming Vivek Natarajan
2010-11-10 13:26         ` Felix Fietkau
2010-11-10 17:25         ` Luis R. Rodriguez
2010-11-10 13:06       ` [RFC 4/5] ath9k: Add support for Tx beamforming feature Felix Fietkau
2010-11-10 12:47   ` [RFC 2/5] ath: Add a keycache entry if beamforming is enabled Felix Fietkau
2010-11-10 12:44 ` [RFC 1/5] mac80211: Add support for transmit beam forming Felix Fietkau
2010-11-10 16:22 ` Johannes Berg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).