Commit 7e7c8926 authored by Ben Greear's avatar Ben Greear Committed by John W. Linville

wireless: Support ht-capabilities over-rides.

This allows users to disable features such as HT, HT40,
and to modify the MCS, AMPDU, and AMSDU settings for
drivers that support it.

The MCS, AMPDU, and AMSDU features that may be disabled are
are reported in the phy-info netlink message as a mask.

Attemping to disable features that are not supported will
take no affect, but will not return errors.  This is to aid
backwards compatibility in user-space apps that may not be
clever enough to deal with parsing the the capabilities mask.

This patch only enables the infrastructure.  An additional
patch will enable the feature in mac80211.
Signed-off-by: default avatarBen Greear <greearb@candelatech.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent dd76986b
...@@ -1169,6 +1169,17 @@ enum nl80211_commands { ...@@ -1169,6 +1169,17 @@ enum nl80211_commands {
* @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire * @NL80211_ATTR_PROBE_RESP: Probe Response template data. Contains the entire
* probe-response frame. The DA field in the 802.11 header is zero-ed out, * probe-response frame. The DA field in the 802.11 header is zero-ed out,
* to be filled by the FW. * to be filled by the FW.
* @NL80211_ATTR_DISABLE_HT: Force HT capable interfaces to disable
* this feature. Currently, only supported in mac80211 drivers.
* @NL80211_ATTR_HT_CAPABILITY_MASK: Specify which bits of the
* ATTR_HT_CAPABILITY to which attention should be paid.
* Currently, only mac80211 NICs support this feature.
* The values that may be configured are:
* MCS rates, MAX-AMSDU, HT-20-40 and HT_CAP_SGI_40
* AMPDU density and AMPDU factor.
* All values are treated as suggestions and may be ignored
* by the driver as required. The actual values may be seen in
* the station debugfs ht_caps file.
* *
* @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country * @NL80211_ATTR_DFS_REGION: region for regulatory rules which this country
* abides to when initiating radiation on DFS channels. A country maps * abides to when initiating radiation on DFS channels. A country maps
...@@ -1414,6 +1425,9 @@ enum nl80211_attrs { ...@@ -1414,6 +1425,9 @@ enum nl80211_attrs {
NL80211_ATTR_DFS_REGION, NL80211_ATTR_DFS_REGION,
NL80211_ATTR_DISABLE_HT,
NL80211_ATTR_HT_CAPABILITY_MASK,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
......
...@@ -1043,6 +1043,15 @@ struct cfg80211_auth_request { ...@@ -1043,6 +1043,15 @@ struct cfg80211_auth_request {
bool local_state_change; bool local_state_change;
}; };
/**
* enum cfg80211_assoc_req_flags - Over-ride default behaviour in association.
*
* @ASSOC_REQ_DISABLE_HT: Disable HT (802.11n)
*/
enum cfg80211_assoc_req_flags {
ASSOC_REQ_DISABLE_HT = BIT(0),
};
/** /**
* struct cfg80211_assoc_request - (Re)Association request data * struct cfg80211_assoc_request - (Re)Association request data
* *
...@@ -1054,6 +1063,10 @@ struct cfg80211_auth_request { ...@@ -1054,6 +1063,10 @@ struct cfg80211_auth_request {
* @use_mfp: Use management frame protection (IEEE 802.11w) in this association * @use_mfp: Use management frame protection (IEEE 802.11w) in this association
* @crypto: crypto settings * @crypto: crypto settings
* @prev_bssid: previous BSSID, if not %NULL use reassociate frame * @prev_bssid: previous BSSID, if not %NULL use reassociate frame
* @flags: See &enum cfg80211_assoc_req_flags
* @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask
* will be used in ht_capa. Un-supported values will be ignored.
* @ht_capa_mask: The bits of ht_capa which are to be used.
*/ */
struct cfg80211_assoc_request { struct cfg80211_assoc_request {
struct cfg80211_bss *bss; struct cfg80211_bss *bss;
...@@ -1061,6 +1074,9 @@ struct cfg80211_assoc_request { ...@@ -1061,6 +1074,9 @@ struct cfg80211_assoc_request {
size_t ie_len; size_t ie_len;
struct cfg80211_crypto_settings crypto; struct cfg80211_crypto_settings crypto;
bool use_mfp; bool use_mfp;
u32 flags;
struct ieee80211_ht_cap ht_capa;
struct ieee80211_ht_cap ht_capa_mask;
}; };
/** /**
...@@ -1159,6 +1175,10 @@ struct cfg80211_ibss_params { ...@@ -1159,6 +1175,10 @@ struct cfg80211_ibss_params {
* @key_len: length of WEP key for shared key authentication * @key_len: length of WEP key for shared key authentication
* @key_idx: index of WEP key for shared key authentication * @key_idx: index of WEP key for shared key authentication
* @key: WEP key for shared key authentication * @key: WEP key for shared key authentication
* @flags: See &enum cfg80211_assoc_req_flags
* @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask
* will be used in ht_capa. Un-supported values will be ignored.
* @ht_capa_mask: The bits of ht_capa which are to be used.
*/ */
struct cfg80211_connect_params { struct cfg80211_connect_params {
struct ieee80211_channel *channel; struct ieee80211_channel *channel;
...@@ -1172,6 +1192,9 @@ struct cfg80211_connect_params { ...@@ -1172,6 +1192,9 @@ struct cfg80211_connect_params {
struct cfg80211_crypto_settings crypto; struct cfg80211_crypto_settings crypto;
const u8 *key; const u8 *key;
u8 key_len, key_idx; u8 key_len, key_idx;
u32 flags;
struct ieee80211_ht_cap ht_capa;
struct ieee80211_ht_cap ht_capa_mask;
}; };
/** /**
...@@ -1938,6 +1961,8 @@ struct wiphy_wowlan_support { ...@@ -1938,6 +1961,8 @@ struct wiphy_wowlan_support {
* @wowlan: WoWLAN support information * @wowlan: WoWLAN support information
* *
* @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features. * @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features.
* @ht_capa_mod_mask: Specify what ht_cap values can be over-ridden.
* If null, then none can be over-ridden.
*/ */
struct wiphy { struct wiphy {
/* assign these fields before you register the wiphy */ /* assign these fields before you register the wiphy */
...@@ -2027,6 +2052,8 @@ struct wiphy { ...@@ -2027,6 +2052,8 @@ struct wiphy {
/* dir in debugfs: ieee80211/<wiphyname> */ /* dir in debugfs: ieee80211/<wiphyname> */
struct dentry *debugfsdir; struct dentry *debugfsdir;
const struct ieee80211_ht_cap *ht_capa_mod_mask;
#ifdef CONFIG_NET_NS #ifdef CONFIG_NET_NS
/* the network namespace this phy lives in currently */ /* the network namespace this phy lives in currently */
struct net *_net; struct net *_net;
......
...@@ -341,13 +341,17 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, ...@@ -341,13 +341,17 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
const u8 *bssid, const u8 *prev_bssid, const u8 *bssid, const u8 *prev_bssid,
const u8 *ssid, int ssid_len, const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len, bool use_mfp, const u8 *ie, int ie_len, bool use_mfp,
struct cfg80211_crypto_settings *crypt); struct cfg80211_crypto_settings *crypt,
u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
struct ieee80211_ht_cap *ht_capa_mask);
int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev, struct ieee80211_channel *chan, struct net_device *dev, struct ieee80211_channel *chan,
const u8 *bssid, const u8 *prev_bssid, const u8 *bssid, const u8 *prev_bssid,
const u8 *ssid, int ssid_len, const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len, bool use_mfp, const u8 *ie, int ie_len, bool use_mfp,
struct cfg80211_crypto_settings *crypt); struct cfg80211_crypto_settings *crypt,
u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
struct ieee80211_ht_cap *ht_capa_mask);
int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
struct net_device *dev, const u8 *bssid, struct net_device *dev, const u8 *bssid,
const u8 *ie, int ie_len, u16 reason, const u8 *ie, int ie_len, u16 reason,
...@@ -379,6 +383,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, ...@@ -379,6 +383,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
bool channel_type_valid, unsigned int wait, bool channel_type_valid, unsigned int wait,
const u8 *buf, size_t len, bool no_cck, const u8 *buf, size_t len, bool no_cck,
bool dont_wait_for_ack, u64 *cookie); bool dont_wait_for_ack, u64 *cookie);
void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
const struct ieee80211_ht_cap *ht_capa_mask);
/* SME */ /* SME */
int __cfg80211_connect(struct cfg80211_registered_device *rdev, int __cfg80211_connect(struct cfg80211_registered_device *rdev,
......
...@@ -501,13 +501,32 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, ...@@ -501,13 +501,32 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
return err; return err;
} }
/* Do a logical ht_capa &= ht_capa_mask. */
void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
const struct ieee80211_ht_cap *ht_capa_mask)
{
int i;
u8 *p1, *p2;
if (!ht_capa_mask) {
memset(ht_capa, 0, sizeof(*ht_capa));
return;
}
p1 = (u8*)(ht_capa);
p2 = (u8*)(ht_capa_mask);
for (i = 0; i<sizeof(*ht_capa); i++)
p1[i] &= p2[i];
}
int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev, struct net_device *dev,
struct ieee80211_channel *chan, struct ieee80211_channel *chan,
const u8 *bssid, const u8 *prev_bssid, const u8 *bssid, const u8 *prev_bssid,
const u8 *ssid, int ssid_len, const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len, bool use_mfp, const u8 *ie, int ie_len, bool use_mfp,
struct cfg80211_crypto_settings *crypt) struct cfg80211_crypto_settings *crypt,
u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
struct ieee80211_ht_cap *ht_capa_mask)
{ {
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_assoc_request req; struct cfg80211_assoc_request req;
...@@ -537,6 +556,15 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, ...@@ -537,6 +556,15 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
memcpy(&req.crypto, crypt, sizeof(req.crypto)); memcpy(&req.crypto, crypt, sizeof(req.crypto));
req.use_mfp = use_mfp; req.use_mfp = use_mfp;
req.prev_bssid = prev_bssid; req.prev_bssid = prev_bssid;
req.flags = assoc_flags;
if (ht_capa)
memcpy(&req.ht_capa, ht_capa, sizeof(req.ht_capa));
if (ht_capa_mask)
memcpy(&req.ht_capa_mask, ht_capa_mask,
sizeof(req.ht_capa_mask));
cfg80211_oper_and_ht_capa(&req.ht_capa_mask,
rdev->wiphy.ht_capa_mod_mask);
req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
if (!req.bss) { if (!req.bss) {
...@@ -574,14 +602,17 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, ...@@ -574,14 +602,17 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
const u8 *bssid, const u8 *prev_bssid, const u8 *bssid, const u8 *prev_bssid,
const u8 *ssid, int ssid_len, const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len, bool use_mfp, const u8 *ie, int ie_len, bool use_mfp,
struct cfg80211_crypto_settings *crypt) struct cfg80211_crypto_settings *crypt,
u32 assoc_flags, struct ieee80211_ht_cap *ht_capa,
struct ieee80211_ht_cap *ht_capa_mask)
{ {
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
int err; int err;
wdev_lock(wdev); wdev_lock(wdev);
err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
ssid, ssid_len, ie, ie_len, use_mfp, crypt); ssid, ssid_len, ie, ie_len, use_mfp, crypt,
assoc_flags, ht_capa, ht_capa_mask);
wdev_unlock(wdev); wdev_unlock(wdev);
return err; return err;
......
...@@ -200,6 +200,10 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { ...@@ -200,6 +200,10 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY, [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN }, .len = IEEE80211_MAX_DATA_LEN },
[NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 }, [NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 },
[NL80211_ATTR_DISABLE_HT] = { .type = NLA_FLAG },
[NL80211_ATTR_HT_CAPABILITY_MASK] = {
.len = NL80211_HT_CAPABILITY_LEN
},
}; };
/* policy for the key attributes */ /* policy for the key attributes */
...@@ -1032,6 +1036,11 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, ...@@ -1032,6 +1036,11 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
NLA_PUT_U32(msg, NL80211_ATTR_FEATURE_FLAGS, dev->wiphy.features); NLA_PUT_U32(msg, NL80211_ATTR_FEATURE_FLAGS, dev->wiphy.features);
if (dev->wiphy.ht_capa_mod_mask)
NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
sizeof(*dev->wiphy.ht_capa_mod_mask),
dev->wiphy.ht_capa_mod_mask);
return genlmsg_end(msg, hdr); return genlmsg_end(msg, hdr);
nla_put_failure: nla_put_failure:
...@@ -4413,6 +4422,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) ...@@ -4413,6 +4422,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
int err, ssid_len, ie_len = 0; int err, ssid_len, ie_len = 0;
bool use_mfp = false; bool use_mfp = false;
u32 flags = 0;
struct ieee80211_ht_cap *ht_capa = NULL;
struct ieee80211_ht_cap *ht_capa_mask = NULL;
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
return -EINVAL; return -EINVAL;
...@@ -4456,11 +4468,25 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) ...@@ -4456,11 +4468,25 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_PREV_BSSID]) if (info->attrs[NL80211_ATTR_PREV_BSSID])
prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]); prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
flags |= ASSOC_REQ_DISABLE_HT;
if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
ht_capa_mask =
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]);
if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
if (!ht_capa_mask)
return -EINVAL;
ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
}
err = nl80211_crypto_settings(rdev, info, &crypto, 1); err = nl80211_crypto_settings(rdev, info, &crypto, 1);
if (!err) if (!err)
err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
ssid, ssid_len, ie, ie_len, use_mfp, ssid, ssid_len, ie, ie_len, use_mfp,
&crypto); &crypto, flags, ht_capa,
ht_capa_mask);
return err; return err;
} }
...@@ -4950,6 +4976,22 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) ...@@ -4950,6 +4976,22 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
return PTR_ERR(connkeys); return PTR_ERR(connkeys);
} }
if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
connect.flags |= ASSOC_REQ_DISABLE_HT;
if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
memcpy(&connect.ht_capa_mask,
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
sizeof(connect.ht_capa_mask));
if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
return -EINVAL;
memcpy(&connect.ht_capa,
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
sizeof(connect.ht_capa));
}
err = cfg80211_connect(rdev, dev, &connect, connkeys); err = cfg80211_connect(rdev, dev, &connect, connkeys);
if (err) if (err)
kfree(connkeys); kfree(connkeys);
......
...@@ -189,7 +189,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) ...@@ -189,7 +189,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
prev_bssid, prev_bssid,
params->ssid, params->ssid_len, params->ssid, params->ssid_len,
params->ie, params->ie_len, params->ie, params->ie_len,
false, &params->crypto); false, &params->crypto,
params->flags, &params->ht_capa,
&params->ht_capa_mask);
if (err) if (err)
__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
NULL, 0, NULL, 0,
...@@ -773,6 +775,9 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, ...@@ -773,6 +775,9 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
wdev->connect_keys = NULL; wdev->connect_keys = NULL;
} }
cfg80211_oper_and_ht_capa(&connect->ht_capa_mask,
rdev->wiphy.ht_capa_mod_mask);
if (connkeys && connkeys->def >= 0) { if (connkeys && connkeys->def >= 0) {
int idx; int idx;
u32 cipher; u32 cipher;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment