@@ -2582,6 +2582,7 @@ struct cfg80211_auth_request {
* userspace if this flag is set. Only applicable for cfg80211_connect()
* request (connect callback).
* @ASSOC_REQ_DISABLE_HE: Disable HE
+ * @ASSOC_REQ_DISABLE_TWT: Disable TWT
*/
enum cfg80211_assoc_req_flags {
ASSOC_REQ_DISABLE_HT = BIT(0),
@@ -2589,6 +2590,7 @@ enum cfg80211_assoc_req_flags {
ASSOC_REQ_USE_RRM = BIT(2),
CONNECT_REQ_EXTERNAL_AUTH_SUPPORT = BIT(3),
ASSOC_REQ_DISABLE_HE = BIT(4),
+ ASSOC_REQ_DISABLE_TWT = BIT(5),
};
/**
@@ -3046,6 +3046,7 @@ enum nl80211_attrs {
NL80211_ATTR_SAR_SPEC,
NL80211_ATTR_DISABLE_HE,
+ NL80211_ATTR_DISABLE_TWT,
/* add attributes here, update the policy in nl80211.c */
@@ -371,6 +371,7 @@ enum ieee80211_sta_flags {
IEEE80211_STA_DISABLE_WMM = BIT(14),
IEEE80211_STA_ENABLE_RRM = BIT(15),
IEEE80211_STA_DISABLE_HE = BIT(16),
+ IEEE80211_STA_DISABLE_TWT = BIT(17),
};
struct ieee80211_mgd_auth_data {
@@ -2247,7 +2248,7 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
const struct cfg80211_chan_def *chandef);
u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype);
-u8 *ieee80211_ie_build_he_cap(u8 *pos,
+u8 *ieee80211_ie_build_he_cap(struct ieee80211_sub_if_data *sdata, u8 *pos,
const struct ieee80211_sta_he_cap *he_cap,
u8 *end);
void ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata,
@@ -580,7 +580,7 @@ int mesh_add_he_cap_ie(struct ieee80211_sub_if_data *sdata,
return -ENOMEM;
pos = skb_put(skb, ie_len);
- ieee80211_ie_build_he_cap(pos, he_cap, pos + ie_len);
+ ieee80211_ie_build_he_cap(sdata, pos, he_cap, pos + ie_len);
return 0;
}
@@ -703,7 +703,7 @@ static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata,
ieee80211_he_ppe_size(he_cap->ppe_thres[0],
he_cap->he_cap_elem.phy_cap_info);
pos = skb_put(skb, he_cap_size);
- ieee80211_ie_build_he_cap(pos, he_cap, pos + he_cap_size);
+ ieee80211_ie_build_he_cap(sdata, pos, he_cap, pos + he_cap_size);
ieee80211_ie_build_he_6ghz_cap(sdata, skb);
}
@@ -918,6 +918,16 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
ext_capa && ext_capa->datalen >= 3)
ext_capa->data[2] |= WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT;
+ /* Apply overrides as needed. */
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_TWT) {
+ if (ext_capa) {
+ if (ext_capa && ext_capa->datalen > 10) {
+ ext_capa->data[9] &= ~(WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT);
+ ext_capa->data[9] &= ~(WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT);
+ }
+ }
+ }
+
/* if present, add any custom IEs that go before HT */
if (assoc_data->ie_len) {
static const u8 before_ht[] = {
@@ -3306,15 +3316,21 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
}
}
-static bool ieee80211_twt_req_supported(const struct sta_info *sta,
+static bool ieee80211_twt_req_supported(struct ieee80211_sub_if_data *sdata,
+ const struct sta_info *sta,
const struct ieee802_11_elems *elems)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
if (elems->ext_capab_len < 10)
return false;
if (!(elems->ext_capab[9] & WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT))
return false;
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_TWT)
+ return false;
+
return sta->sta.he_cap.he_cap_elem.mac_cap_info[0] &
IEEE80211_HE_MAC_CAP0_TWT_RES;
}
@@ -3323,7 +3339,7 @@ static int ieee80211_recalc_twt_req(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee802_11_elems *elems)
{
- bool twt = ieee80211_twt_req_supported(sta, elems);
+ bool twt = ieee80211_twt_req_supported(sdata, sta, elems);
if (sdata->vif.bss_conf.twt_requester != twt) {
sdata->vif.bss_conf.twt_requester = twt;
@@ -5799,6 +5815,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
if (req->flags & ASSOC_REQ_DISABLE_HE)
ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
+ if (req->flags & ASSOC_REQ_DISABLE_TWT)
+ ifmgd->flags |= IEEE80211_STA_DISABLE_TWT;
+
err = ieee80211_prep_connection(sdata, req->bss, true, override);
if (err)
goto err_clear;
@@ -2088,7 +2088,7 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata,
he_cap = ieee80211_get_he_sta_cap(sband);
if (he_cap) {
- pos = ieee80211_ie_build_he_cap(pos, he_cap, end);
+ pos = ieee80211_ie_build_he_cap(sdata, pos, he_cap, end);
if (!pos)
goto out_err;
@@ -3039,13 +3039,14 @@ u8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype)
he_cap->he_cap_elem.phy_cap_info);
}
-u8 *ieee80211_ie_build_he_cap(u8 *pos,
+u8 *ieee80211_ie_build_he_cap(struct ieee80211_sub_if_data *sdata, u8 *pos,
const struct ieee80211_sta_he_cap *he_cap,
u8 *end)
{
u8 n;
u8 ie_len;
u8 *orig_pos = pos;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
/* Make sure we have place for the IE */
/*
@@ -3070,6 +3071,15 @@ u8 *ieee80211_ie_build_he_cap(u8 *pos,
/* Fixed data */
memcpy(pos, &he_cap->he_cap_elem, sizeof(he_cap->he_cap_elem));
+
+ /* Apply overrides as needed. */
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_TWT) {
+ struct ieee80211_he_cap_elem *hec;
+ hec = (struct ieee80211_he_cap_elem *)(pos);
+ hec->mac_cap_info[0] &= ~(IEEE80211_HE_MAC_CAP0_TWT_REQ);
+ hec->mac_cap_info[0] &= ~(IEEE80211_HE_MAC_CAP0_TWT_RES);
+ }
+
pos += sizeof(he_cap->he_cap_elem);
memcpy(pos, &he_cap->he_mcs_nss_supp, n);
@@ -733,6 +733,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_RECONNECT_REQUESTED] = { .type = NLA_REJECT },
[NL80211_ATTR_SAR_SPEC] = NLA_POLICY_NESTED(sar_policy),
[NL80211_ATTR_DISABLE_HE] = { .type = NLA_FLAG },
+ [NL80211_ATTR_DISABLE_TWT] = { .type = NLA_FLAG },
};
/* policy for the key attributes */
@@ -10125,6 +10126,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HE]))
req.flags |= ASSOC_REQ_DISABLE_HE;
+ if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_TWT]))
+ req.flags |= ASSOC_REQ_DISABLE_TWT;
+
if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
memcpy(&req.vht_capa_mask,
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
@@ -10937,6 +10941,9 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HE]))
connect.flags |= ASSOC_REQ_DISABLE_HE;
+ if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_TWT]))
+ connect.flags |= ASSOC_REQ_DISABLE_TWT;
+
if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
memcpy(&connect.vht_capa_mask,
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),