@@ -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));
@@ -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;
};
/**
@@ -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 *))));
};
@@ -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:
@@ -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)
@@ -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,
@@ -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)
@@ -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);
@@ -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);
@@ -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
@@ -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];
@@ -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.
@@ -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);