|
@@ -19,6 +19,7 @@
|
|
|
#include "wmm.h"
|
|
|
#include "11n.h"
|
|
|
#include "11n_rxreorder.h"
|
|
|
+#include "11ac.h"
|
|
|
|
|
|
#define TDLS_REQ_FIX_LEN 6
|
|
|
#define TDLS_RESP_FIX_LEN 8
|
|
@@ -151,7 +152,156 @@ mwifiex_tdls_append_rates_ie(struct mwifiex_private *priv,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void mwifiex_tdls_add_ext_capab(struct sk_buff *skb)
|
|
|
+static void mwifiex_tdls_add_aid(struct mwifiex_private *priv,
|
|
|
+ struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct ieee_types_assoc_rsp *assoc_rsp;
|
|
|
+ u8 *pos;
|
|
|
+
|
|
|
+ assoc_rsp = (struct ieee_types_assoc_rsp *)&priv->assoc_rsp_buf;
|
|
|
+ pos = (void *)skb_put(skb, 4);
|
|
|
+ *pos++ = WLAN_EID_AID;
|
|
|
+ *pos++ = 2;
|
|
|
+ *pos++ = le16_to_cpu(assoc_rsp->a_id);
|
|
|
+
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+static int mwifiex_tdls_add_vht_capab(struct mwifiex_private *priv,
|
|
|
+ struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct ieee80211_vht_cap vht_cap;
|
|
|
+ u8 *pos;
|
|
|
+
|
|
|
+ pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
|
|
|
+ *pos++ = WLAN_EID_VHT_CAPABILITY;
|
|
|
+ *pos++ = sizeof(struct ieee80211_vht_cap);
|
|
|
+
|
|
|
+ memset(&vht_cap, 0, sizeof(struct ieee80211_vht_cap));
|
|
|
+
|
|
|
+ mwifiex_fill_vht_cap_tlv(priv, &vht_cap, priv->curr_bss_params.band);
|
|
|
+ memcpy(pos, &vht_cap, sizeof(struct ieee80211_ht_cap));
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv,
|
|
|
+ u8 *mac, struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct mwifiex_bssdescriptor *bss_desc;
|
|
|
+ struct ieee80211_vht_operation *vht_oper;
|
|
|
+ struct ieee80211_vht_cap *vht_cap, *ap_vht_cap = NULL;
|
|
|
+ struct mwifiex_sta_node *sta_ptr;
|
|
|
+ struct mwifiex_adapter *adapter = priv->adapter;
|
|
|
+ u8 supp_chwd_set, peer_supp_chwd_set;
|
|
|
+ u8 *pos, ap_supp_chwd_set, chan_bw;
|
|
|
+ u16 mcs_map_user, mcs_map_resp, mcs_map_result;
|
|
|
+ u16 mcs_user, mcs_resp, nss;
|
|
|
+ u32 usr_vht_cap_info;
|
|
|
+
|
|
|
+ bss_desc = &priv->curr_bss_params.bss_descriptor;
|
|
|
+
|
|
|
+ sta_ptr = mwifiex_get_sta_entry(priv, mac);
|
|
|
+ if (unlikely(!sta_ptr)) {
|
|
|
+ dev_warn(adapter->dev, "TDLS peer station not found in list\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!mwifiex_is_bss_in_11ac_mode(priv)) {
|
|
|
+ if (sta_ptr->tdls_cap.extcap.ext_capab[7] &
|
|
|
+ WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) {
|
|
|
+ dev_dbg(adapter->dev,
|
|
|
+ "TDLS peer doesn't support wider bandwitdh\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ ap_vht_cap = bss_desc->bcn_vht_cap;
|
|
|
+ }
|
|
|
+
|
|
|
+ pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_operation) + 2);
|
|
|
+ *pos++ = WLAN_EID_VHT_OPERATION;
|
|
|
+ *pos++ = sizeof(struct ieee80211_vht_operation);
|
|
|
+ vht_oper = (struct ieee80211_vht_operation *)pos;
|
|
|
+
|
|
|
+ if (bss_desc->bss_band & BAND_A)
|
|
|
+ usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a;
|
|
|
+ else
|
|
|
+ usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg;
|
|
|
+
|
|
|
+ /* find the minmum bandwith between AP/TDLS peers */
|
|
|
+ vht_cap = &sta_ptr->tdls_cap.vhtcap;
|
|
|
+ supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info);
|
|
|
+ peer_supp_chwd_set =
|
|
|
+ GET_VHTCAP_CHWDSET(le32_to_cpu(vht_cap->vht_cap_info));
|
|
|
+ supp_chwd_set = min_t(u8, supp_chwd_set, peer_supp_chwd_set);
|
|
|
+
|
|
|
+ /* We need check AP's bandwidth when TDLS_WIDER_BANDWIDTH is off */
|
|
|
+
|
|
|
+ if (ap_vht_cap && sta_ptr->tdls_cap.extcap.ext_capab[7] &
|
|
|
+ WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) {
|
|
|
+ ap_supp_chwd_set =
|
|
|
+ GET_VHTCAP_CHWDSET(le32_to_cpu(ap_vht_cap->vht_cap_info));
|
|
|
+ supp_chwd_set = min_t(u8, supp_chwd_set, ap_supp_chwd_set);
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (supp_chwd_set) {
|
|
|
+ case IEEE80211_VHT_CHANWIDTH_80MHZ:
|
|
|
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
|
|
|
+ break;
|
|
|
+ case IEEE80211_VHT_CHANWIDTH_160MHZ:
|
|
|
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ;
|
|
|
+ break;
|
|
|
+ case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
|
|
|
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support);
|
|
|
+ mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map);
|
|
|
+ mcs_map_result = 0;
|
|
|
+
|
|
|
+ for (nss = 1; nss <= 8; nss++) {
|
|
|
+ mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
|
|
|
+ mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
|
|
|
+
|
|
|
+ if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) ||
|
|
|
+ (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED))
|
|
|
+ SET_VHTNSSMCS(mcs_map_result, nss,
|
|
|
+ IEEE80211_VHT_MCS_NOT_SUPPORTED);
|
|
|
+ else
|
|
|
+ SET_VHTNSSMCS(mcs_map_result, nss,
|
|
|
+ min_t(u16, mcs_user, mcs_resp));
|
|
|
+ }
|
|
|
+
|
|
|
+ vht_oper->basic_mcs_set = cpu_to_le16(mcs_map_result);
|
|
|
+
|
|
|
+ switch (vht_oper->chan_width) {
|
|
|
+ case IEEE80211_VHT_CHANWIDTH_80MHZ:
|
|
|
+ chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ;
|
|
|
+ break;
|
|
|
+ case IEEE80211_VHT_CHANWIDTH_160MHZ:
|
|
|
+ chan_bw = IEEE80211_VHT_CHANWIDTH_160MHZ;
|
|
|
+ break;
|
|
|
+ case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
|
|
|
+ chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ chan_bw = IEEE80211_VHT_CHANWIDTH_USE_HT;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ vht_oper->center_freq_seg1_idx =
|
|
|
+ mwifiex_get_center_freq_index(priv, BAND_AAC,
|
|
|
+ bss_desc->channel,
|
|
|
+ chan_bw);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void mwifiex_tdls_add_ext_capab(struct mwifiex_private *priv,
|
|
|
+ struct sk_buff *skb)
|
|
|
{
|
|
|
struct ieee_types_extcap *extcap;
|
|
|
|
|
@@ -160,6 +310,9 @@ static void mwifiex_tdls_add_ext_capab(struct sk_buff *skb)
|
|
|
extcap->ieee_hdr.len = 8;
|
|
|
memset(extcap->ext_capab, 0, 8);
|
|
|
extcap->ext_capab[4] |= WLAN_EXT_CAPA5_TDLS_ENABLED;
|
|
|
+
|
|
|
+ if (priv->adapter->is_hw_11ac_capable)
|
|
|
+ extcap->ext_capab[7] |= WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED;
|
|
|
}
|
|
|
|
|
|
static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb)
|
|
@@ -213,7 +366,16 @@ static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- mwifiex_tdls_add_ext_capab(skb);
|
|
|
+ if (priv->adapter->is_hw_11ac_capable) {
|
|
|
+ ret = mwifiex_tdls_add_vht_capab(priv, skb);
|
|
|
+ if (ret) {
|
|
|
+ dev_kfree_skb_any(skb);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ mwifiex_tdls_add_aid(priv, skb);
|
|
|
+ }
|
|
|
+
|
|
|
+ mwifiex_tdls_add_ext_capab(priv, skb);
|
|
|
mwifiex_tdls_add_qos_capab(skb);
|
|
|
break;
|
|
|
|
|
@@ -241,7 +403,16 @@ static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- mwifiex_tdls_add_ext_capab(skb);
|
|
|
+ if (priv->adapter->is_hw_11ac_capable) {
|
|
|
+ ret = mwifiex_tdls_add_vht_capab(priv, skb);
|
|
|
+ if (ret) {
|
|
|
+ dev_kfree_skb_any(skb);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ mwifiex_tdls_add_aid(priv, skb);
|
|
|
+ }
|
|
|
+
|
|
|
+ mwifiex_tdls_add_ext_capab(priv, skb);
|
|
|
mwifiex_tdls_add_qos_capab(skb);
|
|
|
break;
|
|
|
|
|
@@ -251,6 +422,13 @@ static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv,
|
|
|
skb_put(skb, sizeof(tf->u.setup_cfm));
|
|
|
tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
|
|
|
tf->u.setup_cfm.dialog_token = dialog_token;
|
|
|
+ if (priv->adapter->is_hw_11ac_capable) {
|
|
|
+ ret = mwifiex_tdls_add_vht_oper(priv, peer, skb);
|
|
|
+ if (ret) {
|
|
|
+ dev_kfree_skb_any(skb);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
break;
|
|
|
|
|
|
case WLAN_TDLS_TEARDOWN:
|
|
@@ -313,6 +491,11 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv,
|
|
|
sizeof(struct ieee80211_tdls_lnkie) +
|
|
|
extra_ies_len;
|
|
|
|
|
|
+ if (priv->adapter->is_hw_11ac_capable)
|
|
|
+ skb_len += sizeof(struct ieee_types_vht_cap) +
|
|
|
+ sizeof(struct ieee_types_vht_oper) +
|
|
|
+ sizeof(struct ieee_types_aid);
|
|
|
+
|
|
|
skb = dev_alloc_skb(skb_len);
|
|
|
if (!skb) {
|
|
|
dev_err(priv->adapter->dev,
|
|
@@ -435,7 +618,16 @@ mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, u8 *peer,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- mwifiex_tdls_add_ext_capab(skb);
|
|
|
+ if (priv->adapter->is_hw_11ac_capable) {
|
|
|
+ ret = mwifiex_tdls_add_vht_capab(priv, skb);
|
|
|
+ if (ret) {
|
|
|
+ dev_kfree_skb_any(skb);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ mwifiex_tdls_add_aid(priv, skb);
|
|
|
+ }
|
|
|
+
|
|
|
+ mwifiex_tdls_add_ext_capab(priv, skb);
|
|
|
mwifiex_tdls_add_qos_capab(skb);
|
|
|
break;
|
|
|
default:
|
|
@@ -472,6 +664,11 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv,
|
|
|
3 + /* Qos Info */
|
|
|
ETH_ALEN; /* Address4 */
|
|
|
|
|
|
+ if (priv->adapter->is_hw_11ac_capable)
|
|
|
+ skb_len += sizeof(struct ieee_types_vht_cap) +
|
|
|
+ sizeof(struct ieee_types_vht_oper) +
|
|
|
+ sizeof(struct ieee_types_aid);
|
|
|
+
|
|
|
skb = dev_alloc_skb(skb_len);
|
|
|
if (!skb) {
|
|
|
dev_err(priv->adapter->dev,
|
|
@@ -626,6 +823,22 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv,
|
|
|
case WLAN_EID_QOS_CAPA:
|
|
|
sta_ptr->tdls_cap.qos_info = pos[2];
|
|
|
break;
|
|
|
+ case WLAN_EID_VHT_OPERATION:
|
|
|
+ if (priv->adapter->is_hw_11ac_capable)
|
|
|
+ memcpy(&sta_ptr->tdls_cap.vhtoper, pos,
|
|
|
+ sizeof(struct ieee80211_vht_operation));
|
|
|
+ break;
|
|
|
+ case WLAN_EID_VHT_CAPABILITY:
|
|
|
+ if (priv->adapter->is_hw_11ac_capable) {
|
|
|
+ memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos,
|
|
|
+ sizeof(struct ieee80211_vht_cap));
|
|
|
+ sta_ptr->is_11ac_enabled = 1;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case WLAN_EID_AID:
|
|
|
+ if (priv->adapter->is_hw_11ac_capable)
|
|
|
+ sta_ptr->tdls_cap.aid =
|
|
|
+ le16_to_cpu(*(__le16 *)(pos + 2));
|
|
|
default:
|
|
|
break;
|
|
|
}
|