Skip to content

Commit

Permalink
wireless: Support ht-capabilities over-rides.
Browse files Browse the repository at this point in the history
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: Ben Greear <greearb@candelatech.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Ben Greear authored and John W. Linville committed Nov 21, 2011
1 parent dd76986 commit 7e7c892
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 7 deletions.
14 changes: 14 additions & 0 deletions include/linux/nl80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,17 @@ enum nl80211_commands {
* @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,
* 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
* abides to when initiating radiation on DFS channels. A country maps
Expand Down Expand Up @@ -1414,6 +1425,9 @@ enum nl80211_attrs {

NL80211_ATTR_DFS_REGION,

NL80211_ATTR_DISABLE_HT,
NL80211_ATTR_HT_CAPABILITY_MASK,

/* add attributes here, update the policy in nl80211.c */

__NL80211_ATTR_AFTER_LAST,
Expand Down
27 changes: 27 additions & 0 deletions include/net/cfg80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,15 @@ struct cfg80211_auth_request {
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
*
Expand All @@ -1054,13 +1063,20 @@ struct cfg80211_auth_request {
* @use_mfp: Use management frame protection (IEEE 802.11w) in this association
* @crypto: crypto settings
* @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_bss *bss;
const u8 *ie, *prev_bssid;
size_t ie_len;
struct cfg80211_crypto_settings crypto;
bool use_mfp;
u32 flags;
struct ieee80211_ht_cap ht_capa;
struct ieee80211_ht_cap ht_capa_mask;
};

/**
Expand Down Expand Up @@ -1159,6 +1175,10 @@ struct cfg80211_ibss_params {
* @key_len: length of WEP key for shared key authentication
* @key_idx: index of 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 ieee80211_channel *channel;
Expand All @@ -1172,6 +1192,9 @@ struct cfg80211_connect_params {
struct cfg80211_crypto_settings crypto;
const u8 *key;
u8 key_len, key_idx;
u32 flags;
struct ieee80211_ht_cap ht_capa;
struct ieee80211_ht_cap ht_capa_mask;
};

/**
Expand Down Expand Up @@ -1938,6 +1961,8 @@ struct wiphy_wowlan_support {
* @wowlan: WoWLAN support information
*
* @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 {
/* assign these fields before you register the wiphy */
Expand Down Expand Up @@ -2027,6 +2052,8 @@ struct wiphy {
/* dir in debugfs: ieee80211/<wiphyname> */
struct dentry *debugfsdir;

const struct ieee80211_ht_cap *ht_capa_mod_mask;

#ifdef CONFIG_NET_NS
/* the network namespace this phy lives in currently */
struct net *_net;
Expand Down
10 changes: 8 additions & 2 deletions net/wireless/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -341,13 +341,17 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
const u8 *bssid, const u8 *prev_bssid,
const u8 *ssid, int ssid_len,
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,
struct net_device *dev, struct ieee80211_channel *chan,
const u8 *bssid, const u8 *prev_bssid,
const u8 *ssid, int ssid_len,
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,
struct net_device *dev, const u8 *bssid,
const u8 *ie, int ie_len, u16 reason,
Expand Down Expand Up @@ -379,6 +383,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
bool channel_type_valid, unsigned int wait,
const u8 *buf, size_t len, bool no_cck,
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 */
int __cfg80211_connect(struct cfg80211_registered_device *rdev,
Expand Down
37 changes: 34 additions & 3 deletions net/wireless/mlme.c
Original file line number Diff line number Diff line change
Expand Up @@ -501,13 +501,32 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
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,
struct net_device *dev,
struct ieee80211_channel *chan,
const u8 *bssid, const u8 *prev_bssid,
const u8 *ssid, int ssid_len,
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 cfg80211_assoc_request req;
Expand Down Expand Up @@ -537,6 +556,15 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
memcpy(&req.crypto, crypt, sizeof(req.crypto));
req.use_mfp = use_mfp;
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,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
if (!req.bss) {
Expand Down Expand Up @@ -574,14 +602,17 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
const u8 *bssid, const u8 *prev_bssid,
const u8 *ssid, int ssid_len,
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;
int err;

wdev_lock(wdev);
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);

return err;
Expand Down
44 changes: 43 additions & 1 deletion net/wireless/nl80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
[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 */
Expand Down Expand Up @@ -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);

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);

nla_put_failure:
Expand Down Expand Up @@ -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;
int err, ssid_len, ie_len = 0;
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]))
return -EINVAL;
Expand Down Expand Up @@ -4456,11 +4468,25 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
if (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);
if (!err)
err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
ssid, ssid_len, ie, ie_len, use_mfp,
&crypto);
&crypto, flags, ht_capa,
ht_capa_mask);

return err;
}
Expand Down Expand Up @@ -4950,6 +4976,22 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
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);
if (err)
kfree(connkeys);
Expand Down
7 changes: 6 additions & 1 deletion net/wireless/sme.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,9 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
prev_bssid,
params->ssid, params->ssid_len,
params->ie, params->ie_len,
false, &params->crypto);
false, &params->crypto,
params->flags, &params->ht_capa,
&params->ht_capa_mask);
if (err)
__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
NULL, 0,
Expand Down Expand Up @@ -773,6 +775,9 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
wdev->connect_keys = NULL;
}

cfg80211_oper_and_ht_capa(&connect->ht_capa_mask,
rdev->wiphy.ht_capa_mod_mask);

if (connkeys && connkeys->def >= 0) {
int idx;
u32 cipher;
Expand Down

0 comments on commit 7e7c892

Please sign in to comment.