mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 05:41:55 +00:00
This time we have too many changes to list, highlights:
* virt_wifi - wireless control simulation on top of another network interface * hwsim configurability to test capabilities similar to real hardware * various mesh improvements * various radiotap vendor data fixes in mac80211 * finally the nl_set_extack_cookie_u64() we talked about previously, used for * peer measurement APIs, right now only with FTM (flight time measurement) for location * made nl80211 radio/interface announcements more complete * various new HE (802.11ax) things: updates, TWT support, ... -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEH1e1rEeCd0AIMq6MB8qZga/fl8QFAlwaCwkACgkQB8qZga/f l8S7mA/+I1CJmGC7Pvy+SBFkzoY5zEjjzgZYL6sGo16qMs89NPcURSe5j+uCsDP3 nKEjsvhQMYDfGNLTJJfWbDpGwm9LnKp69AFITlvfzmP6Sm36QMZr7oIC4abi8cW4 osaO3qfdaNoZ//x72jgjrFhUAnphvT2BsRVMNEjz7sXcDd7Jm9NnpRhV8zgXFvLF dS2Ng51LM/BLMz5jQpyJUDZeeL/iBYybCecyckmVqzXPh1icIZETSqZXiN4ngv2A 6p9BSGNtP6wmjnbkvZz5RDq76VhTPZWsTgTpVb45Wf1k2fm1rB96UgpqvfQtjTgB +7Zx2WRpMXM5OjGkwaEs8nawFmt7MHCGnhLPLWPCbXc685fhp3OFShysMJdYS/GZ IIRJ7+IchAQX1yluftB+NkQM9sBDjyseMBwxHRYkj/rQVhoLY1sT+ke7lkuV10o6 DQqfpUTZAsIz7zkuscn7hkNdI/Rjub6BZjbrs1Jt9zSt9WQUBao23XudOI0j5JDa ErnfC5PISXMQWik5B9M1Zhq3H9qCI2Swh19lMmtxtSDQ9yrLrJkEJ5SA+aHoxNHj wSxBc3XXSW47qPXGX/D5DNnbOcOrE7kVZuD8YqRsy8VedyjIgEw7oQ21flAD4FC4 R4TgbNkqpfZQsU29gaMkDkYXnfQDB/G9FOk6ARGxjBPjT55Hz0E= =EpyK -----END PGP SIGNATURE----- Merge tag 'mac80211-next-for-davem-2018-12-19' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next Johannes Berg says: ==================== This time we have too many changes to list, highlights: * virt_wifi - wireless control simulation on top of another network interface * hwsim configurability to test capabilities similar to real hardware * various mesh improvements * various radiotap vendor data fixes in mac80211 * finally the nl_set_extack_cookie_u64() we talked about previously, used for * peer measurement APIs, right now only with FTM (flight time measurement) for location * made nl80211 radio/interface announcements more complete * various new HE (802.11ax) things: updates, TWT support, ... ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
5a862f86b8
@ -114,4 +114,11 @@ config USB_NET_RNDIS_WLAN
|
||||
|
||||
If you choose to build a module, it'll be called rndis_wlan.
|
||||
|
||||
config VIRT_WIFI
|
||||
tristate "Wifi wrapper for ethernet drivers"
|
||||
depends on CFG80211
|
||||
---help---
|
||||
This option adds support for ethernet connections to appear as if they
|
||||
are wifi connections through a special rtnetlink device.
|
||||
|
||||
endif # WLAN
|
||||
|
@ -27,3 +27,5 @@ obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o
|
||||
obj-$(CONFIG_USB_NET_RNDIS_WLAN) += rndis_wlan.o
|
||||
|
||||
obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o
|
||||
|
||||
obj-$(CONFIG_VIRT_WIFI) += virt_wifi.o
|
||||
|
@ -374,6 +374,20 @@ static const struct ieee80211_rate hwsim_rates[] = {
|
||||
{ .bitrate = 540 }
|
||||
};
|
||||
|
||||
static const u32 hwsim_ciphers[] = {
|
||||
WLAN_CIPHER_SUITE_WEP40,
|
||||
WLAN_CIPHER_SUITE_WEP104,
|
||||
WLAN_CIPHER_SUITE_TKIP,
|
||||
WLAN_CIPHER_SUITE_CCMP,
|
||||
WLAN_CIPHER_SUITE_CCMP_256,
|
||||
WLAN_CIPHER_SUITE_GCMP,
|
||||
WLAN_CIPHER_SUITE_GCMP_256,
|
||||
WLAN_CIPHER_SUITE_AES_CMAC,
|
||||
WLAN_CIPHER_SUITE_BIP_CMAC_256,
|
||||
WLAN_CIPHER_SUITE_BIP_GMAC_128,
|
||||
WLAN_CIPHER_SUITE_BIP_GMAC_256,
|
||||
};
|
||||
|
||||
#define OUI_QCA 0x001374
|
||||
#define QCA_NL80211_SUBCMD_TEST 1
|
||||
enum qca_nl80211_vendor_subcmds {
|
||||
@ -451,48 +465,6 @@ static const struct nl80211_vendor_cmd_info mac80211_hwsim_vendor_events[] = {
|
||||
{ .vendor_id = OUI_QCA, .subcmd = 1 },
|
||||
};
|
||||
|
||||
static const struct ieee80211_iface_limit hwsim_if_limits[] = {
|
||||
{ .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) },
|
||||
{ .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) |
|
||||
BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
BIT(NL80211_IFTYPE_MESH_POINT) |
|
||||
#endif
|
||||
BIT(NL80211_IFTYPE_AP) |
|
||||
BIT(NL80211_IFTYPE_P2P_GO) },
|
||||
/* must be last, see hwsim_if_comb */
|
||||
{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }
|
||||
};
|
||||
|
||||
static const struct ieee80211_iface_combination hwsim_if_comb[] = {
|
||||
{
|
||||
.limits = hwsim_if_limits,
|
||||
/* remove the last entry which is P2P_DEVICE */
|
||||
.n_limits = ARRAY_SIZE(hwsim_if_limits) - 1,
|
||||
.max_interfaces = 2048,
|
||||
.num_different_channels = 1,
|
||||
.radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
|
||||
BIT(NL80211_CHAN_WIDTH_20) |
|
||||
BIT(NL80211_CHAN_WIDTH_40) |
|
||||
BIT(NL80211_CHAN_WIDTH_80) |
|
||||
BIT(NL80211_CHAN_WIDTH_160),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct ieee80211_iface_combination hwsim_if_comb_p2p_dev[] = {
|
||||
{
|
||||
.limits = hwsim_if_limits,
|
||||
.n_limits = ARRAY_SIZE(hwsim_if_limits),
|
||||
.max_interfaces = 2048,
|
||||
.num_different_channels = 1,
|
||||
.radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
|
||||
BIT(NL80211_CHAN_WIDTH_20) |
|
||||
BIT(NL80211_CHAN_WIDTH_40) |
|
||||
BIT(NL80211_CHAN_WIDTH_80) |
|
||||
BIT(NL80211_CHAN_WIDTH_160),
|
||||
},
|
||||
};
|
||||
|
||||
static spinlock_t hwsim_radio_lock;
|
||||
static LIST_HEAD(hwsim_radios);
|
||||
static struct rhashtable hwsim_radios_rht;
|
||||
@ -515,6 +487,10 @@ struct mac80211_hwsim_data {
|
||||
struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)];
|
||||
struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)];
|
||||
struct ieee80211_iface_combination if_combination;
|
||||
struct ieee80211_iface_limit if_limits[3];
|
||||
int n_if_limits;
|
||||
|
||||
u32 ciphers[ARRAY_SIZE(hwsim_ciphers)];
|
||||
|
||||
struct mac_address addresses[2];
|
||||
int channels, idx;
|
||||
@ -642,6 +618,8 @@ static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
|
||||
[HWSIM_ATTR_NO_VIF] = { .type = NLA_FLAG },
|
||||
[HWSIM_ATTR_FREQ] = { .type = NLA_U32 },
|
||||
[HWSIM_ATTR_PERM_ADDR] = { .type = NLA_UNSPEC, .len = ETH_ALEN },
|
||||
[HWSIM_ATTR_IFTYPE_SUPPORT] = { .type = NLA_U32 },
|
||||
[HWSIM_ATTR_CIPHER_SUPPORT] = { .type = NLA_BINARY },
|
||||
};
|
||||
|
||||
static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
|
||||
@ -2414,6 +2392,9 @@ struct hwsim_new_radio_params {
|
||||
const char *hwname;
|
||||
bool no_vif;
|
||||
const u8 *perm_addr;
|
||||
u32 iftypes;
|
||||
u32 *ciphers;
|
||||
u8 n_ciphers;
|
||||
};
|
||||
|
||||
static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
|
||||
@ -2630,6 +2611,27 @@ static void mac80211_hswim_he_capab(struct ieee80211_supported_band *sband)
|
||||
sband->n_iftype_data = 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
#define HWSIM_MESH_BIT BIT(NL80211_IFTYPE_MESH_POINT)
|
||||
#else
|
||||
#define HWSIM_MESH_BIT 0
|
||||
#endif
|
||||
|
||||
#define HWSIM_DEFAULT_IF_LIMIT \
|
||||
(BIT(NL80211_IFTYPE_STATION) | \
|
||||
BIT(NL80211_IFTYPE_P2P_CLIENT) | \
|
||||
BIT(NL80211_IFTYPE_AP) | \
|
||||
BIT(NL80211_IFTYPE_P2P_GO) | \
|
||||
HWSIM_MESH_BIT)
|
||||
|
||||
#define HWSIM_IFTYPE_SUPPORT_MASK \
|
||||
(BIT(NL80211_IFTYPE_STATION) | \
|
||||
BIT(NL80211_IFTYPE_AP) | \
|
||||
BIT(NL80211_IFTYPE_P2P_CLIENT) | \
|
||||
BIT(NL80211_IFTYPE_P2P_GO) | \
|
||||
BIT(NL80211_IFTYPE_ADHOC) | \
|
||||
BIT(NL80211_IFTYPE_MESH_POINT))
|
||||
|
||||
static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||
struct hwsim_new_radio_params *param)
|
||||
{
|
||||
@ -2641,6 +2643,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||
const struct ieee80211_ops *ops = &mac80211_hwsim_ops;
|
||||
struct net *net;
|
||||
int idx;
|
||||
int n_limits = 0;
|
||||
|
||||
if (WARN_ON(param->channels > 1 && !param->use_chanctx))
|
||||
return -EINVAL;
|
||||
@ -2716,26 +2719,60 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||
if (info)
|
||||
data->portid = info->snd_portid;
|
||||
|
||||
/* setup interface limits, only on interface types we support */
|
||||
if (param->iftypes & BIT(NL80211_IFTYPE_ADHOC)) {
|
||||
data->if_limits[n_limits].max = 1;
|
||||
data->if_limits[n_limits].types = BIT(NL80211_IFTYPE_ADHOC);
|
||||
n_limits++;
|
||||
}
|
||||
|
||||
if (param->iftypes & HWSIM_DEFAULT_IF_LIMIT) {
|
||||
data->if_limits[n_limits].max = 2048;
|
||||
/*
|
||||
* For this case, we may only support a subset of
|
||||
* HWSIM_DEFAULT_IF_LIMIT, therefore we only want to add the
|
||||
* bits that both param->iftype & HWSIM_DEFAULT_IF_LIMIT have.
|
||||
*/
|
||||
data->if_limits[n_limits].types =
|
||||
HWSIM_DEFAULT_IF_LIMIT & param->iftypes;
|
||||
n_limits++;
|
||||
}
|
||||
|
||||
if (param->iftypes & BIT(NL80211_IFTYPE_P2P_DEVICE)) {
|
||||
data->if_limits[n_limits].max = 1;
|
||||
data->if_limits[n_limits].types =
|
||||
BIT(NL80211_IFTYPE_P2P_DEVICE);
|
||||
n_limits++;
|
||||
}
|
||||
|
||||
if (data->use_chanctx) {
|
||||
hw->wiphy->max_scan_ssids = 255;
|
||||
hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
|
||||
hw->wiphy->max_remain_on_channel_duration = 1000;
|
||||
hw->wiphy->iface_combinations = &data->if_combination;
|
||||
if (param->p2p_device)
|
||||
data->if_combination = hwsim_if_comb_p2p_dev[0];
|
||||
else
|
||||
data->if_combination = hwsim_if_comb[0];
|
||||
hw->wiphy->n_iface_combinations = 1;
|
||||
/* For channels > 1 DFS is not allowed */
|
||||
data->if_combination.radar_detect_widths = 0;
|
||||
data->if_combination.num_different_channels = data->channels;
|
||||
} else if (param->p2p_device) {
|
||||
hw->wiphy->iface_combinations = hwsim_if_comb_p2p_dev;
|
||||
hw->wiphy->n_iface_combinations =
|
||||
ARRAY_SIZE(hwsim_if_comb_p2p_dev);
|
||||
} else {
|
||||
hw->wiphy->iface_combinations = hwsim_if_comb;
|
||||
hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb);
|
||||
data->if_combination.num_different_channels = 1;
|
||||
data->if_combination.radar_detect_widths =
|
||||
BIT(NL80211_CHAN_WIDTH_20_NOHT) |
|
||||
BIT(NL80211_CHAN_WIDTH_20) |
|
||||
BIT(NL80211_CHAN_WIDTH_40) |
|
||||
BIT(NL80211_CHAN_WIDTH_80) |
|
||||
BIT(NL80211_CHAN_WIDTH_160);
|
||||
}
|
||||
|
||||
data->if_combination.n_limits = n_limits;
|
||||
data->if_combination.max_interfaces = 2048;
|
||||
data->if_combination.limits = data->if_limits;
|
||||
|
||||
hw->wiphy->iface_combinations = &data->if_combination;
|
||||
hw->wiphy->n_iface_combinations = 1;
|
||||
|
||||
if (param->ciphers) {
|
||||
memcpy(data->ciphers, param->ciphers,
|
||||
param->n_ciphers * sizeof(u32));
|
||||
hw->wiphy->cipher_suites = data->ciphers;
|
||||
hw->wiphy->n_cipher_suites = param->n_ciphers;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&data->roc_start, hw_roc_start);
|
||||
@ -2744,15 +2781,6 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||
|
||||
hw->queues = 5;
|
||||
hw->offchannel_tx_hw_queue = 4;
|
||||
hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
|
||||
BIT(NL80211_IFTYPE_AP) |
|
||||
BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
||||
BIT(NL80211_IFTYPE_P2P_GO) |
|
||||
BIT(NL80211_IFTYPE_ADHOC) |
|
||||
BIT(NL80211_IFTYPE_MESH_POINT);
|
||||
|
||||
if (param->p2p_device)
|
||||
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
|
||||
|
||||
ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
|
||||
ieee80211_hw_set(hw, CHANCTX_STA_CSA);
|
||||
@ -2778,6 +2806,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||
NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
|
||||
wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
|
||||
|
||||
hw->wiphy->interface_modes = param->iftypes;
|
||||
|
||||
/* ask mac80211 to reserve space for magic */
|
||||
hw->vif_data_size = sizeof(struct hwsim_vif_priv);
|
||||
hw->sta_data_size = sizeof(struct hwsim_sta_priv);
|
||||
@ -3293,6 +3323,29 @@ static int hwsim_register_received_nl(struct sk_buff *skb_2,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ensures ciphers only include ciphers listed in 'hwsim_ciphers' array */
|
||||
static bool hwsim_known_ciphers(const u32 *ciphers, int n_ciphers)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n_ciphers; i++) {
|
||||
int j;
|
||||
int found = 0;
|
||||
|
||||
for (j = 0; j < ARRAY_SIZE(hwsim_ciphers); j++) {
|
||||
if (ciphers[i] == hwsim_ciphers[j]) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
||||
{
|
||||
struct hwsim_new_radio_params param = { 0 };
|
||||
@ -3321,15 +3374,6 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
||||
if (info->attrs[HWSIM_ATTR_NO_VIF])
|
||||
param.no_vif = true;
|
||||
|
||||
if (info->attrs[HWSIM_ATTR_RADIO_NAME]) {
|
||||
hwname = kasprintf(GFP_KERNEL, "%.*s",
|
||||
nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
|
||||
(char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]));
|
||||
if (!hwname)
|
||||
return -ENOMEM;
|
||||
param.hwname = hwname;
|
||||
}
|
||||
|
||||
if (info->attrs[HWSIM_ATTR_USE_CHANCTX])
|
||||
param.use_chanctx = true;
|
||||
else
|
||||
@ -3342,10 +3386,8 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
||||
if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) {
|
||||
u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]);
|
||||
|
||||
if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom)) {
|
||||
kfree(hwname);
|
||||
if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
idx = array_index_nospec(idx,
|
||||
ARRAY_SIZE(hwsim_world_regdom_custom));
|
||||
@ -3358,14 +3400,72 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
||||
GENL_SET_ERR_MSG(info,"MAC is no valid source addr");
|
||||
NL_SET_BAD_ATTR(info->extack,
|
||||
info->attrs[HWSIM_ATTR_PERM_ADDR]);
|
||||
kfree(hwname);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
param.perm_addr = nla_data(info->attrs[HWSIM_ATTR_PERM_ADDR]);
|
||||
}
|
||||
|
||||
if (info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT]) {
|
||||
param.iftypes =
|
||||
nla_get_u32(info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT]);
|
||||
|
||||
if (param.iftypes & ~HWSIM_IFTYPE_SUPPORT_MASK) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
info->attrs[HWSIM_ATTR_IFTYPE_SUPPORT],
|
||||
"cannot support more iftypes than kernel");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK;
|
||||
}
|
||||
|
||||
/* ensure both flag and iftype support is honored */
|
||||
if (param.p2p_device ||
|
||||
param.iftypes & BIT(NL80211_IFTYPE_P2P_DEVICE)) {
|
||||
param.iftypes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
|
||||
param.p2p_device = true;
|
||||
}
|
||||
|
||||
if (info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]) {
|
||||
u32 len = nla_len(info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]);
|
||||
|
||||
param.ciphers =
|
||||
nla_data(info->attrs[HWSIM_ATTR_CIPHER_SUPPORT]);
|
||||
|
||||
if (len % sizeof(u32)) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
info->attrs[HWSIM_ATTR_CIPHER_SUPPORT],
|
||||
"bad cipher list length");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
param.n_ciphers = len / sizeof(u32);
|
||||
|
||||
if (param.n_ciphers > ARRAY_SIZE(hwsim_ciphers)) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
info->attrs[HWSIM_ATTR_CIPHER_SUPPORT],
|
||||
"too many ciphers specified");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!hwsim_known_ciphers(param.ciphers, param.n_ciphers)) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
info->attrs[HWSIM_ATTR_CIPHER_SUPPORT],
|
||||
"unsupported ciphers specified");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (info->attrs[HWSIM_ATTR_RADIO_NAME]) {
|
||||
hwname = kasprintf(GFP_KERNEL, "%.*s",
|
||||
nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
|
||||
(char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]));
|
||||
if (!hwname)
|
||||
return -ENOMEM;
|
||||
param.hwname = hwname;
|
||||
}
|
||||
|
||||
ret = mac80211_hwsim_new_radio(info, ¶m);
|
||||
kfree(hwname);
|
||||
return ret;
|
||||
@ -3785,6 +3885,7 @@ static int __init init_mac80211_hwsim(void)
|
||||
|
||||
param.p2p_device = support_p2p_device;
|
||||
param.use_chanctx = channels > 1;
|
||||
param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK;
|
||||
|
||||
err = mac80211_hwsim_new_radio(NULL, ¶m);
|
||||
if (err < 0)
|
||||
|
@ -132,6 +132,8 @@ enum {
|
||||
* @HWSIM_ATTR_TX_INFO_FLAGS: additional flags for corresponding
|
||||
* rates of %HWSIM_ATTR_TX_INFO
|
||||
* @HWSIM_ATTR_PERM_ADDR: permanent mac address of new radio
|
||||
* @HWSIM_ATTR_IFTYPE_SUPPORT: u32 attribute of supported interface types bits
|
||||
* @HWSIM_ATTR_CIPHER_SUPPORT: u32 array of supported cipher types
|
||||
* @__HWSIM_ATTR_MAX: enum limit
|
||||
*/
|
||||
|
||||
@ -160,6 +162,8 @@ enum {
|
||||
HWSIM_ATTR_PAD,
|
||||
HWSIM_ATTR_TX_INFO_FLAGS,
|
||||
HWSIM_ATTR_PERM_ADDR,
|
||||
HWSIM_ATTR_IFTYPE_SUPPORT,
|
||||
HWSIM_ATTR_CIPHER_SUPPORT,
|
||||
__HWSIM_ATTR_MAX,
|
||||
};
|
||||
#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
|
||||
|
632
drivers/net/wireless/virt_wifi.c
Normal file
632
drivers/net/wireless/virt_wifi.c
Normal file
@ -0,0 +1,632 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* drivers/net/wireless/virt_wifi.c
|
||||
*
|
||||
* A fake implementation of cfg80211_ops that can be tacked on to an ethernet
|
||||
* net_device to make it appear as a wireless connection.
|
||||
*
|
||||
* Copyright (C) 2018 Google, Inc.
|
||||
*
|
||||
* Author: schuffelen@google.com
|
||||
*/
|
||||
|
||||
#include <net/cfg80211.h>
|
||||
#include <net/rtnetlink.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <net/cfg80211.h>
|
||||
#include <net/rtnetlink.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
static struct wiphy *common_wiphy;
|
||||
|
||||
struct virt_wifi_wiphy_priv {
|
||||
struct delayed_work scan_result;
|
||||
struct cfg80211_scan_request *scan_request;
|
||||
bool being_deleted;
|
||||
};
|
||||
|
||||
static struct ieee80211_channel channel_2ghz = {
|
||||
.band = NL80211_BAND_2GHZ,
|
||||
.center_freq = 2432,
|
||||
.hw_value = 2432,
|
||||
.max_power = 20,
|
||||
};
|
||||
|
||||
static struct ieee80211_rate bitrates_2ghz[] = {
|
||||
{ .bitrate = 10 },
|
||||
{ .bitrate = 20 },
|
||||
{ .bitrate = 55 },
|
||||
{ .bitrate = 110 },
|
||||
{ .bitrate = 60 },
|
||||
{ .bitrate = 120 },
|
||||
{ .bitrate = 240 },
|
||||
};
|
||||
|
||||
static struct ieee80211_supported_band band_2ghz = {
|
||||
.channels = &channel_2ghz,
|
||||
.bitrates = bitrates_2ghz,
|
||||
.band = NL80211_BAND_2GHZ,
|
||||
.n_channels = 1,
|
||||
.n_bitrates = ARRAY_SIZE(bitrates_2ghz),
|
||||
.ht_cap = {
|
||||
.ht_supported = true,
|
||||
.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
|
||||
IEEE80211_HT_CAP_GRN_FLD |
|
||||
IEEE80211_HT_CAP_SGI_20 |
|
||||
IEEE80211_HT_CAP_SGI_40 |
|
||||
IEEE80211_HT_CAP_DSSSCCK40,
|
||||
.ampdu_factor = 0x3,
|
||||
.ampdu_density = 0x6,
|
||||
.mcs = {
|
||||
.rx_mask = {0xff, 0xff},
|
||||
.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct ieee80211_channel channel_5ghz = {
|
||||
.band = NL80211_BAND_5GHZ,
|
||||
.center_freq = 5240,
|
||||
.hw_value = 5240,
|
||||
.max_power = 20,
|
||||
};
|
||||
|
||||
static struct ieee80211_rate bitrates_5ghz[] = {
|
||||
{ .bitrate = 60 },
|
||||
{ .bitrate = 120 },
|
||||
{ .bitrate = 240 },
|
||||
};
|
||||
|
||||
#define RX_MCS_MAP (IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
|
||||
IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \
|
||||
IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \
|
||||
IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \
|
||||
IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \
|
||||
IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \
|
||||
IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \
|
||||
IEEE80211_VHT_MCS_SUPPORT_0_9 << 14)
|
||||
|
||||
#define TX_MCS_MAP (IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
|
||||
IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \
|
||||
IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \
|
||||
IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \
|
||||
IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \
|
||||
IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \
|
||||
IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \
|
||||
IEEE80211_VHT_MCS_SUPPORT_0_9 << 14)
|
||||
|
||||
static struct ieee80211_supported_band band_5ghz = {
|
||||
.channels = &channel_5ghz,
|
||||
.bitrates = bitrates_5ghz,
|
||||
.band = NL80211_BAND_5GHZ,
|
||||
.n_channels = 1,
|
||||
.n_bitrates = ARRAY_SIZE(bitrates_5ghz),
|
||||
.ht_cap = {
|
||||
.ht_supported = true,
|
||||
.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
|
||||
IEEE80211_HT_CAP_GRN_FLD |
|
||||
IEEE80211_HT_CAP_SGI_20 |
|
||||
IEEE80211_HT_CAP_SGI_40 |
|
||||
IEEE80211_HT_CAP_DSSSCCK40,
|
||||
.ampdu_factor = 0x3,
|
||||
.ampdu_density = 0x6,
|
||||
.mcs = {
|
||||
.rx_mask = {0xff, 0xff},
|
||||
.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
|
||||
},
|
||||
},
|
||||
.vht_cap = {
|
||||
.vht_supported = true,
|
||||
.cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
|
||||
IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ |
|
||||
IEEE80211_VHT_CAP_RXLDPC |
|
||||
IEEE80211_VHT_CAP_SHORT_GI_80 |
|
||||
IEEE80211_VHT_CAP_SHORT_GI_160 |
|
||||
IEEE80211_VHT_CAP_TXSTBC |
|
||||
IEEE80211_VHT_CAP_RXSTBC_1 |
|
||||
IEEE80211_VHT_CAP_RXSTBC_2 |
|
||||
IEEE80211_VHT_CAP_RXSTBC_3 |
|
||||
IEEE80211_VHT_CAP_RXSTBC_4 |
|
||||
IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
|
||||
.vht_mcs = {
|
||||
.rx_mcs_map = cpu_to_le16(RX_MCS_MAP),
|
||||
.tx_mcs_map = cpu_to_le16(TX_MCS_MAP),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/* Assigned at module init. Guaranteed locally-administered and unicast. */
|
||||
static u8 fake_router_bssid[ETH_ALEN] __ro_after_init = {};
|
||||
|
||||
/* Called with the rtnl lock held. */
|
||||
static int virt_wifi_scan(struct wiphy *wiphy,
|
||||
struct cfg80211_scan_request *request)
|
||||
{
|
||||
struct virt_wifi_wiphy_priv *priv = wiphy_priv(wiphy);
|
||||
|
||||
wiphy_debug(wiphy, "scan\n");
|
||||
|
||||
if (priv->scan_request || priv->being_deleted)
|
||||
return -EBUSY;
|
||||
|
||||
priv->scan_request = request;
|
||||
schedule_delayed_work(&priv->scan_result, HZ * 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Acquires and releases the rdev BSS lock. */
|
||||
static void virt_wifi_scan_result(struct work_struct *work)
|
||||
{
|
||||
struct {
|
||||
u8 tag;
|
||||
u8 len;
|
||||
u8 ssid[8];
|
||||
} __packed ssid = {
|
||||
.tag = WLAN_EID_SSID, .len = 8, .ssid = "VirtWifi",
|
||||
};
|
||||
struct cfg80211_bss *informed_bss;
|
||||
struct virt_wifi_wiphy_priv *priv =
|
||||
container_of(work, struct virt_wifi_wiphy_priv,
|
||||
scan_result.work);
|
||||
struct wiphy *wiphy = priv_to_wiphy(priv);
|
||||
struct cfg80211_scan_info scan_info = { .aborted = false };
|
||||
|
||||
informed_bss = cfg80211_inform_bss(wiphy, &channel_5ghz,
|
||||
CFG80211_BSS_FTYPE_PRESP,
|
||||
fake_router_bssid,
|
||||
ktime_get_boot_ns(),
|
||||
WLAN_CAPABILITY_ESS, 0,
|
||||
(void *)&ssid, sizeof(ssid),
|
||||
DBM_TO_MBM(-50), GFP_KERNEL);
|
||||
cfg80211_put_bss(wiphy, informed_bss);
|
||||
|
||||
/* Schedules work which acquires and releases the rtnl lock. */
|
||||
cfg80211_scan_done(priv->scan_request, &scan_info);
|
||||
priv->scan_request = NULL;
|
||||
}
|
||||
|
||||
/* May acquire and release the rdev BSS lock. */
|
||||
static void virt_wifi_cancel_scan(struct wiphy *wiphy)
|
||||
{
|
||||
struct virt_wifi_wiphy_priv *priv = wiphy_priv(wiphy);
|
||||
|
||||
cancel_delayed_work_sync(&priv->scan_result);
|
||||
/* Clean up dangling callbacks if necessary. */
|
||||
if (priv->scan_request) {
|
||||
struct cfg80211_scan_info scan_info = { .aborted = true };
|
||||
/* Schedules work which acquires and releases the rtnl lock. */
|
||||
cfg80211_scan_done(priv->scan_request, &scan_info);
|
||||
priv->scan_request = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct virt_wifi_netdev_priv {
|
||||
struct delayed_work connect;
|
||||
struct net_device *lowerdev;
|
||||
struct net_device *upperdev;
|
||||
u32 tx_packets;
|
||||
u32 tx_failed;
|
||||
u8 connect_requested_bss[ETH_ALEN];
|
||||
bool is_up;
|
||||
bool is_connected;
|
||||
bool being_deleted;
|
||||
};
|
||||
|
||||
/* Called with the rtnl lock held. */
|
||||
static int virt_wifi_connect(struct wiphy *wiphy, struct net_device *netdev,
|
||||
struct cfg80211_connect_params *sme)
|
||||
{
|
||||
struct virt_wifi_netdev_priv *priv = netdev_priv(netdev);
|
||||
bool could_schedule;
|
||||
|
||||
if (priv->being_deleted || !priv->is_up)
|
||||
return -EBUSY;
|
||||
|
||||
could_schedule = schedule_delayed_work(&priv->connect, HZ * 2);
|
||||
if (!could_schedule)
|
||||
return -EBUSY;
|
||||
|
||||
if (sme->bssid)
|
||||
ether_addr_copy(priv->connect_requested_bss, sme->bssid);
|
||||
else
|
||||
eth_zero_addr(priv->connect_requested_bss);
|
||||
|
||||
wiphy_debug(wiphy, "connect\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Acquires and releases the rdev event lock. */
|
||||
static void virt_wifi_connect_complete(struct work_struct *work)
|
||||
{
|
||||
struct virt_wifi_netdev_priv *priv =
|
||||
container_of(work, struct virt_wifi_netdev_priv, connect.work);
|
||||
u8 *requested_bss = priv->connect_requested_bss;
|
||||
bool has_addr = !is_zero_ether_addr(requested_bss);
|
||||
bool right_addr = ether_addr_equal(requested_bss, fake_router_bssid);
|
||||
u16 status = WLAN_STATUS_SUCCESS;
|
||||
|
||||
if (!priv->is_up || (has_addr && !right_addr))
|
||||
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||
else
|
||||
priv->is_connected = true;
|
||||
|
||||
/* Schedules an event that acquires the rtnl lock. */
|
||||
cfg80211_connect_result(priv->upperdev, requested_bss, NULL, 0, NULL, 0,
|
||||
status, GFP_KERNEL);
|
||||
netif_carrier_on(priv->upperdev);
|
||||
}
|
||||
|
||||
/* May acquire and release the rdev event lock. */
|
||||
static void virt_wifi_cancel_connect(struct net_device *netdev)
|
||||
{
|
||||
struct virt_wifi_netdev_priv *priv = netdev_priv(netdev);
|
||||
|
||||
/* If there is work pending, clean up dangling callbacks. */
|
||||
if (cancel_delayed_work_sync(&priv->connect)) {
|
||||
/* Schedules an event that acquires the rtnl lock. */
|
||||
cfg80211_connect_result(priv->upperdev,
|
||||
priv->connect_requested_bss, NULL, 0,
|
||||
NULL, 0,
|
||||
WLAN_STATUS_UNSPECIFIED_FAILURE,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called with the rtnl lock held. Acquires the rdev event lock. */
|
||||
static int virt_wifi_disconnect(struct wiphy *wiphy, struct net_device *netdev,
|
||||
u16 reason_code)
|
||||
{
|
||||
struct virt_wifi_netdev_priv *priv = netdev_priv(netdev);
|
||||
|
||||
if (priv->being_deleted)
|
||||
return -EBUSY;
|
||||
|
||||
wiphy_debug(wiphy, "disconnect\n");
|
||||
virt_wifi_cancel_connect(netdev);
|
||||
|
||||
cfg80211_disconnected(netdev, reason_code, NULL, 0, true, GFP_KERNEL);
|
||||
priv->is_connected = false;
|
||||
netif_carrier_off(netdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called with the rtnl lock held. */
|
||||
static int virt_wifi_get_station(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *mac, struct station_info *sinfo)
|
||||
{
|
||||
struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
|
||||
|
||||
wiphy_debug(wiphy, "get_station\n");
|
||||
|
||||
if (!priv->is_connected || !ether_addr_equal(mac, fake_router_bssid))
|
||||
return -ENOENT;
|
||||
|
||||
sinfo->filled = BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |
|
||||
BIT_ULL(NL80211_STA_INFO_TX_FAILED) |
|
||||
BIT_ULL(NL80211_STA_INFO_SIGNAL) |
|
||||
BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
|
||||
sinfo->tx_packets = priv->tx_packets;
|
||||
sinfo->tx_failed = priv->tx_failed;
|
||||
/* For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_ */
|
||||
sinfo->signal = -50;
|
||||
sinfo->txrate = (struct rate_info) {
|
||||
.legacy = 10, /* units are 100kbit/s */
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called with the rtnl lock held. */
|
||||
static int virt_wifi_dump_station(struct wiphy *wiphy, struct net_device *dev,
|
||||
int idx, u8 *mac, struct station_info *sinfo)
|
||||
{
|
||||
struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
|
||||
|
||||
wiphy_debug(wiphy, "dump_station\n");
|
||||
|
||||
if (idx != 0 || !priv->is_connected)
|
||||
return -ENOENT;
|
||||
|
||||
ether_addr_copy(mac, fake_router_bssid);
|
||||
return virt_wifi_get_station(wiphy, dev, fake_router_bssid, sinfo);
|
||||
}
|
||||
|
||||
static const struct cfg80211_ops virt_wifi_cfg80211_ops = {
|
||||
.scan = virt_wifi_scan,
|
||||
|
||||
.connect = virt_wifi_connect,
|
||||
.disconnect = virt_wifi_disconnect,
|
||||
|
||||
.get_station = virt_wifi_get_station,
|
||||
.dump_station = virt_wifi_dump_station,
|
||||
};
|
||||
|
||||
/* Acquires and releases the rtnl lock. */
|
||||
static struct wiphy *virt_wifi_make_wiphy(void)
|
||||
{
|
||||
struct wiphy *wiphy;
|
||||
struct virt_wifi_wiphy_priv *priv;
|
||||
int err;
|
||||
|
||||
wiphy = wiphy_new(&virt_wifi_cfg80211_ops, sizeof(*priv));
|
||||
|
||||
if (!wiphy)
|
||||
return NULL;
|
||||
|
||||
wiphy->max_scan_ssids = 4;
|
||||
wiphy->max_scan_ie_len = 1000;
|
||||
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
|
||||
|
||||
wiphy->bands[NL80211_BAND_2GHZ] = &band_2ghz;
|
||||
wiphy->bands[NL80211_BAND_5GHZ] = &band_5ghz;
|
||||
wiphy->bands[NL80211_BAND_60GHZ] = NULL;
|
||||
|
||||
wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED;
|
||||
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
|
||||
|
||||
priv = wiphy_priv(wiphy);
|
||||
priv->being_deleted = false;
|
||||
priv->scan_request = NULL;
|
||||
INIT_DELAYED_WORK(&priv->scan_result, virt_wifi_scan_result);
|
||||
|
||||
err = wiphy_register(wiphy);
|
||||
if (err < 0) {
|
||||
wiphy_free(wiphy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return wiphy;
|
||||
}
|
||||
|
||||
/* Acquires and releases the rtnl lock. */
|
||||
static void virt_wifi_destroy_wiphy(struct wiphy *wiphy)
|
||||
{
|
||||
struct virt_wifi_wiphy_priv *priv;
|
||||
|
||||
WARN(!wiphy, "%s called with null wiphy", __func__);
|
||||
if (!wiphy)
|
||||
return;
|
||||
|
||||
priv = wiphy_priv(wiphy);
|
||||
priv->being_deleted = true;
|
||||
virt_wifi_cancel_scan(wiphy);
|
||||
|
||||
if (wiphy->registered)
|
||||
wiphy_unregister(wiphy);
|
||||
wiphy_free(wiphy);
|
||||
}
|
||||
|
||||
/* Enters and exits a RCU-bh critical section. */
|
||||
static netdev_tx_t virt_wifi_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
|
||||
|
||||
priv->tx_packets++;
|
||||
if (!priv->is_connected) {
|
||||
priv->tx_failed++;
|
||||
return NET_XMIT_DROP;
|
||||
}
|
||||
|
||||
skb->dev = priv->lowerdev;
|
||||
return dev_queue_xmit(skb);
|
||||
}
|
||||
|
||||
/* Called with rtnl lock held. */
|
||||
static int virt_wifi_net_device_open(struct net_device *dev)
|
||||
{
|
||||
struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
|
||||
|
||||
priv->is_up = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called with rtnl lock held. */
|
||||
static int virt_wifi_net_device_stop(struct net_device *dev)
|
||||
{
|
||||
struct virt_wifi_netdev_priv *n_priv = netdev_priv(dev);
|
||||
struct virt_wifi_wiphy_priv *w_priv;
|
||||
|
||||
n_priv->is_up = false;
|
||||
|
||||
if (!dev->ieee80211_ptr)
|
||||
return 0;
|
||||
w_priv = wiphy_priv(dev->ieee80211_ptr->wiphy);
|
||||
|
||||
virt_wifi_cancel_scan(dev->ieee80211_ptr->wiphy);
|
||||
virt_wifi_cancel_connect(dev);
|
||||
netif_carrier_off(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct net_device_ops virt_wifi_ops = {
|
||||
.ndo_start_xmit = virt_wifi_start_xmit,
|
||||
.ndo_open = virt_wifi_net_device_open,
|
||||
.ndo_stop = virt_wifi_net_device_stop,
|
||||
};
|
||||
|
||||
/* Invoked as part of rtnl lock release. */
|
||||
static void virt_wifi_net_device_destructor(struct net_device *dev)
|
||||
{
|
||||
/* Delayed past dellink to allow nl80211 to react to the device being
|
||||
* deleted.
|
||||
*/
|
||||
kfree(dev->ieee80211_ptr);
|
||||
dev->ieee80211_ptr = NULL;
|
||||
free_netdev(dev);
|
||||
}
|
||||
|
||||
/* No lock interaction. */
|
||||
static void virt_wifi_setup(struct net_device *dev)
|
||||
{
|
||||
ether_setup(dev);
|
||||
dev->netdev_ops = &virt_wifi_ops;
|
||||
dev->priv_destructor = virt_wifi_net_device_destructor;
|
||||
}
|
||||
|
||||
/* Called in a RCU read critical section from netif_receive_skb */
|
||||
static rx_handler_result_t virt_wifi_rx_handler(struct sk_buff **pskb)
|
||||
{
|
||||
struct sk_buff *skb = *pskb;
|
||||
struct virt_wifi_netdev_priv *priv =
|
||||
rcu_dereference(skb->dev->rx_handler_data);
|
||||
|
||||
if (!priv->is_connected)
|
||||
return RX_HANDLER_PASS;
|
||||
|
||||
/* GFP_ATOMIC because this is a packet interrupt handler. */
|
||||
skb = skb_share_check(skb, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
dev_err(&priv->upperdev->dev, "can't skb_share_check\n");
|
||||
return RX_HANDLER_CONSUMED;
|
||||
}
|
||||
|
||||
*pskb = skb;
|
||||
skb->dev = priv->upperdev;
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
return RX_HANDLER_ANOTHER;
|
||||
}
|
||||
|
||||
/* Called with rtnl lock held. */
|
||||
static int virt_wifi_newlink(struct net *src_net, struct net_device *dev,
|
||||
struct nlattr *tb[], struct nlattr *data[],
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
if (!tb[IFLA_LINK])
|
||||
return -EINVAL;
|
||||
|
||||
netif_carrier_off(dev);
|
||||
|
||||
priv->upperdev = dev;
|
||||
priv->lowerdev = __dev_get_by_index(src_net,
|
||||
nla_get_u32(tb[IFLA_LINK]));
|
||||
|
||||
if (!priv->lowerdev)
|
||||
return -ENODEV;
|
||||
if (!tb[IFLA_MTU])
|
||||
dev->mtu = priv->lowerdev->mtu;
|
||||
else if (dev->mtu > priv->lowerdev->mtu)
|
||||
return -EINVAL;
|
||||
|
||||
err = netdev_rx_handler_register(priv->lowerdev, virt_wifi_rx_handler,
|
||||
priv);
|
||||
if (err) {
|
||||
dev_err(&priv->lowerdev->dev,
|
||||
"can't netdev_rx_handler_register: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
eth_hw_addr_inherit(dev, priv->lowerdev);
|
||||
netif_stacked_transfer_operstate(priv->lowerdev, dev);
|
||||
|
||||
SET_NETDEV_DEV(dev, &priv->lowerdev->dev);
|
||||
dev->ieee80211_ptr = kzalloc(sizeof(*dev->ieee80211_ptr), GFP_KERNEL);
|
||||
|
||||
if (!dev->ieee80211_ptr)
|
||||
goto remove_handler;
|
||||
|
||||
dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION;
|
||||
dev->ieee80211_ptr->wiphy = common_wiphy;
|
||||
|
||||
err = register_netdevice(dev);
|
||||
if (err) {
|
||||
dev_err(&priv->lowerdev->dev, "can't register_netdevice: %d\n",
|
||||
err);
|
||||
goto free_wireless_dev;
|
||||
}
|
||||
|
||||
err = netdev_upper_dev_link(priv->lowerdev, dev, extack);
|
||||
if (err) {
|
||||
dev_err(&priv->lowerdev->dev, "can't netdev_upper_dev_link: %d\n",
|
||||
err);
|
||||
goto unregister_netdev;
|
||||
}
|
||||
|
||||
priv->being_deleted = false;
|
||||
priv->is_connected = false;
|
||||
priv->is_up = false;
|
||||
INIT_DELAYED_WORK(&priv->connect, virt_wifi_connect_complete);
|
||||
|
||||
return 0;
|
||||
unregister_netdev:
|
||||
unregister_netdevice(dev);
|
||||
free_wireless_dev:
|
||||
kfree(dev->ieee80211_ptr);
|
||||
dev->ieee80211_ptr = NULL;
|
||||
remove_handler:
|
||||
netdev_rx_handler_unregister(priv->lowerdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Called with rtnl lock held. */
|
||||
static void virt_wifi_dellink(struct net_device *dev,
|
||||
struct list_head *head)
|
||||
{
|
||||
struct virt_wifi_netdev_priv *priv = netdev_priv(dev);
|
||||
|
||||
if (dev->ieee80211_ptr)
|
||||
virt_wifi_cancel_scan(dev->ieee80211_ptr->wiphy);
|
||||
|
||||
priv->being_deleted = true;
|
||||
virt_wifi_cancel_connect(dev);
|
||||
netif_carrier_off(dev);
|
||||
|
||||
netdev_rx_handler_unregister(priv->lowerdev);
|
||||
netdev_upper_dev_unlink(priv->lowerdev, dev);
|
||||
|
||||
unregister_netdevice_queue(dev, head);
|
||||
|
||||
/* Deleting the wiphy is handled in the module destructor. */
|
||||
}
|
||||
|
||||
static struct rtnl_link_ops virt_wifi_link_ops = {
|
||||
.kind = "virt_wifi",
|
||||
.setup = virt_wifi_setup,
|
||||
.newlink = virt_wifi_newlink,
|
||||
.dellink = virt_wifi_dellink,
|
||||
.priv_size = sizeof(struct virt_wifi_netdev_priv),
|
||||
};
|
||||
|
||||
/* Acquires and releases the rtnl lock. */
|
||||
static int __init virt_wifi_init_module(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Guaranteed to be locallly-administered and not multicast. */
|
||||
eth_random_addr(fake_router_bssid);
|
||||
|
||||
common_wiphy = virt_wifi_make_wiphy();
|
||||
if (!common_wiphy)
|
||||
return -ENOMEM;
|
||||
|
||||
err = rtnl_link_register(&virt_wifi_link_ops);
|
||||
if (err)
|
||||
virt_wifi_destroy_wiphy(common_wiphy);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Acquires and releases the rtnl lock. */
|
||||
static void __exit virt_wifi_cleanup_module(void)
|
||||
{
|
||||
/* Will delete any devices that depend on the wiphy. */
|
||||
rtnl_link_unregister(&virt_wifi_link_ops);
|
||||
virt_wifi_destroy_wiphy(common_wiphy);
|
||||
}
|
||||
|
||||
module_init(virt_wifi_init_module);
|
||||
module_exit(virt_wifi_cleanup_module);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Cody Schuffelen <schuffelen@google.com>");
|
||||
MODULE_DESCRIPTION("Driver for a wireless wrapper of ethernet devices");
|
||||
MODULE_ALIAS_RTNL_LINK("virt_wifi");
|
@ -812,6 +812,8 @@ enum mesh_config_capab_flags {
|
||||
IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL = 0x40,
|
||||
};
|
||||
|
||||
#define IEEE80211_MESHCONF_FORM_CONNECTED_TO_GATE 0x1
|
||||
|
||||
/**
|
||||
* mesh channel switch parameters element's flag indicator
|
||||
*
|
||||
@ -1617,7 +1619,7 @@ struct ieee80211_he_mcs_nss_supp {
|
||||
* struct ieee80211_he_operation - HE capabilities element
|
||||
*
|
||||
* This structure is the "HE operation element" fields as
|
||||
* described in P802.11ax_D2.0 section 9.4.2.238
|
||||
* described in P802.11ax_D3.0 section 9.4.2.238
|
||||
*/
|
||||
struct ieee80211_he_operation {
|
||||
__le32 he_oper_params;
|
||||
@ -2009,17 +2011,17 @@ ieee80211_he_ppe_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
|
||||
}
|
||||
|
||||
/* HE Operation defines */
|
||||
#define IEEE80211_HE_OPERATION_BSS_COLOR_MASK 0x0000003f
|
||||
#define IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK 0x000001c0
|
||||
#define IEEE80211_HE_OPERATION_DFLT_PE_DURATION_OFFSET 6
|
||||
#define IEEE80211_HE_OPERATION_TWT_REQUIRED 0x00000200
|
||||
#define IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK 0x000ffc00
|
||||
#define IEEE80211_HE_OPERATION_RTS_THRESHOLD_OFFSET 10
|
||||
#define IEEE80211_HE_OPERATION_PARTIAL_BSS_COLOR 0x00100000
|
||||
#define IEEE80211_HE_OPERATION_VHT_OPER_INFO 0x00200000
|
||||
#define IEEE80211_HE_OPERATION_MULTI_BSSID_AP 0x10000000
|
||||
#define IEEE80211_HE_OPERATION_TX_BSSID_INDICATOR 0x20000000
|
||||
#define IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED 0x40000000
|
||||
#define IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK 0x00000003
|
||||
#define IEEE80211_HE_OPERATION_TWT_REQUIRED 0x00000008
|
||||
#define IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK 0x00003ff0
|
||||
#define IEEE80211_HE_OPERATION_RTS_THRESHOLD_OFFSET 4
|
||||
#define IEEE80211_HE_OPERATION_VHT_OPER_INFO 0x00004000
|
||||
#define IEEE80211_HE_OPERATION_CO_LOCATED_BSS 0x00008000
|
||||
#define IEEE80211_HE_OPERATION_ER_SU_DISABLE 0x00010000
|
||||
#define IEEE80211_HE_OPERATION_BSS_COLOR_MASK 0x3f000000
|
||||
#define IEEE80211_HE_OPERATION_BSS_COLOR_OFFSET 24
|
||||
#define IEEE80211_HE_OPERATION_PARTIAL_BSS_COLOR 0x40000000
|
||||
#define IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED 0x80000000
|
||||
|
||||
/*
|
||||
* ieee80211_he_oper_size - calculate 802.11ax HE Operations IE size
|
||||
@ -2044,7 +2046,7 @@ ieee80211_he_oper_size(const u8 *he_oper_ie)
|
||||
he_oper_params = le32_to_cpu(he_oper->he_oper_params);
|
||||
if (he_oper_params & IEEE80211_HE_OPERATION_VHT_OPER_INFO)
|
||||
oper_len += 3;
|
||||
if (he_oper_params & IEEE80211_HE_OPERATION_MULTI_BSSID_AP)
|
||||
if (he_oper_params & IEEE80211_HE_OPERATION_CO_LOCATED_BSS)
|
||||
oper_len++;
|
||||
|
||||
/* Add the first byte (extension ID) to the total length */
|
||||
@ -2685,6 +2687,10 @@ enum ieee80211_tdls_actioncode {
|
||||
*/
|
||||
#define WLAN_EXT_CAPA9_FTM_INITIATOR BIT(7)
|
||||
|
||||
/* Defines support for TWT Requester and TWT Responder */
|
||||
#define WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT BIT(5)
|
||||
#define WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT BIT(6)
|
||||
|
||||
/* TDLS specific payload type in the LLC/SNAP header */
|
||||
#define WLAN_TDLS_SNAP_RFTYPE 0x2
|
||||
|
||||
|
@ -110,6 +110,15 @@ struct netlink_ext_ack {
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static inline void nl_set_extack_cookie_u64(struct netlink_ext_ack *extack,
|
||||
u64 cookie)
|
||||
{
|
||||
u64 __cookie = cookie;
|
||||
|
||||
memcpy(extack->cookie, &__cookie, sizeof(__cookie));
|
||||
extack->cookie_len = sizeof(__cookie);
|
||||
}
|
||||
|
||||
extern void netlink_kernel_release(struct sock *sk);
|
||||
extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups);
|
||||
extern int netlink_change_ngroups(struct sock *sk, unsigned int groups);
|
||||
|
@ -777,8 +777,10 @@ struct cfg80211_crypto_settings {
|
||||
* @probe_resp: probe response template (AP mode only)
|
||||
* @ftm_responder: enable FTM responder functionality; -1 for no change
|
||||
* (which also implies no change in LCI/civic location data)
|
||||
* @lci: LCI subelement content
|
||||
* @civicloc: Civic location subelement content
|
||||
* @lci: Measurement Report element content, starting with Measurement Token
|
||||
* (measurement type 8)
|
||||
* @civicloc: Measurement Report element content, starting with Measurement
|
||||
* Token (measurement type 11)
|
||||
* @lci_len: LCI data length
|
||||
* @civicloc_len: Civic location data length
|
||||
*/
|
||||
@ -1296,6 +1298,7 @@ struct cfg80211_tid_stats {
|
||||
* @rx_beacon: number of beacons received from this peer
|
||||
* @rx_beacon_signal_avg: signal strength average (in dBm) for beacons received
|
||||
* from this peer
|
||||
* @connected_to_gate: true if mesh STA has a path to mesh gate
|
||||
* @rx_duration: aggregate PPDU duration(usecs) for all the frames from a peer
|
||||
* @pertid: per-TID statistics, see &struct cfg80211_tid_stats, using the last
|
||||
* (IEEE80211_NUM_TIDS) index for MSDUs not encapsulated in QoS-MPDUs.
|
||||
@ -1350,6 +1353,8 @@ struct station_info {
|
||||
u64 rx_beacon;
|
||||
u64 rx_duration;
|
||||
u8 rx_beacon_signal_avg;
|
||||
u8 connected_to_gate;
|
||||
|
||||
struct cfg80211_tid_stats *pertid;
|
||||
s8 ack_signal;
|
||||
s8 avg_ack_signal;
|
||||
@ -1559,6 +1564,10 @@ struct bss_parameters {
|
||||
* @plink_timeout: If no tx activity is seen from a STA we've established
|
||||
* peering with for longer than this time (in seconds), then remove it
|
||||
* from the STA's list of peers. Default is 30 minutes.
|
||||
* @dot11MeshConnectedToMeshGate: if set to true, advertise that this STA is
|
||||
* connected to a mesh gate in mesh formation info. If false, the
|
||||
* value in mesh formation is determined by the presence of root paths
|
||||
* in the mesh path table
|
||||
*/
|
||||
struct mesh_config {
|
||||
u16 dot11MeshRetryTimeout;
|
||||
@ -1578,6 +1587,7 @@ struct mesh_config {
|
||||
u16 dot11MeshHWMPperrMinInterval;
|
||||
u16 dot11MeshHWMPnetDiameterTraversalTime;
|
||||
u8 dot11MeshHWMPRootMode;
|
||||
bool dot11MeshConnectedToMeshGate;
|
||||
u16 dot11MeshHWMPRannInterval;
|
||||
bool dot11MeshGateAnnouncementProtocol;
|
||||
bool dot11MeshForwarding;
|
||||
@ -2815,7 +2825,7 @@ struct cfg80211_external_auth_params {
|
||||
};
|
||||
|
||||
/**
|
||||
* cfg80211_ftm_responder_stats - FTM responder statistics
|
||||
* struct cfg80211_ftm_responder_stats - FTM responder statistics
|
||||
*
|
||||
* @filled: bitflag of flags using the bits of &enum nl80211_ftm_stats to
|
||||
* indicate the relevant values in this struct for them
|
||||
@ -2848,6 +2858,190 @@ struct cfg80211_ftm_responder_stats {
|
||||
u32 out_of_window_triggers_num;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_pmsr_ftm_result - FTM result
|
||||
* @failure_reason: if this measurement failed (PMSR status is
|
||||
* %NL80211_PMSR_STATUS_FAILURE), this gives a more precise
|
||||
* reason than just "failure"
|
||||
* @burst_index: if reporting partial results, this is the index
|
||||
* in [0 .. num_bursts-1] of the burst that's being reported
|
||||
* @num_ftmr_attempts: number of FTM request frames transmitted
|
||||
* @num_ftmr_successes: number of FTM request frames acked
|
||||
* @busy_retry_time: if failure_reason is %NL80211_PMSR_FTM_FAILURE_PEER_BUSY,
|
||||
* fill this to indicate in how many seconds a retry is deemed possible
|
||||
* by the responder
|
||||
* @num_bursts_exp: actual number of bursts exponent negotiated
|
||||
* @burst_duration: actual burst duration negotiated
|
||||
* @ftms_per_burst: actual FTMs per burst negotiated
|
||||
* @lci_len: length of LCI information (if present)
|
||||
* @civicloc_len: length of civic location information (if present)
|
||||
* @lci: LCI data (may be %NULL)
|
||||
* @civicloc: civic location data (may be %NULL)
|
||||
* @rssi_avg: average RSSI over FTM action frames reported
|
||||
* @rssi_spread: spread of the RSSI over FTM action frames reported
|
||||
* @tx_rate: bitrate for transmitted FTM action frame response
|
||||
* @rx_rate: bitrate of received FTM action frame
|
||||
* @rtt_avg: average of RTTs measured (must have either this or @dist_avg)
|
||||
* @rtt_variance: variance of RTTs measured (note that standard deviation is
|
||||
* the square root of the variance)
|
||||
* @rtt_spread: spread of the RTTs measured
|
||||
* @dist_avg: average of distances (mm) measured
|
||||
* (must have either this or @rtt_avg)
|
||||
* @dist_variance: variance of distances measured (see also @rtt_variance)
|
||||
* @dist_spread: spread of distances measured (see also @rtt_spread)
|
||||
* @num_ftmr_attempts_valid: @num_ftmr_attempts is valid
|
||||
* @num_ftmr_successes_valid: @num_ftmr_successes is valid
|
||||
* @rssi_avg_valid: @rssi_avg is valid
|
||||
* @rssi_spread_valid: @rssi_spread is valid
|
||||
* @tx_rate_valid: @tx_rate is valid
|
||||
* @rx_rate_valid: @rx_rate is valid
|
||||
* @rtt_avg_valid: @rtt_avg is valid
|
||||
* @rtt_variance_valid: @rtt_variance is valid
|
||||
* @rtt_spread_valid: @rtt_spread is valid
|
||||
* @dist_avg_valid: @dist_avg is valid
|
||||
* @dist_variance_valid: @dist_variance is valid
|
||||
* @dist_spread_valid: @dist_spread is valid
|
||||
*/
|
||||
struct cfg80211_pmsr_ftm_result {
|
||||
const u8 *lci;
|
||||
const u8 *civicloc;
|
||||
unsigned int lci_len;
|
||||
unsigned int civicloc_len;
|
||||
enum nl80211_peer_measurement_ftm_failure_reasons failure_reason;
|
||||
u32 num_ftmr_attempts, num_ftmr_successes;
|
||||
s16 burst_index;
|
||||
u8 busy_retry_time;
|
||||
u8 num_bursts_exp;
|
||||
u8 burst_duration;
|
||||
u8 ftms_per_burst;
|
||||
s32 rssi_avg;
|
||||
s32 rssi_spread;
|
||||
struct rate_info tx_rate, rx_rate;
|
||||
s64 rtt_avg;
|
||||
s64 rtt_variance;
|
||||
s64 rtt_spread;
|
||||
s64 dist_avg;
|
||||
s64 dist_variance;
|
||||
s64 dist_spread;
|
||||
|
||||
u16 num_ftmr_attempts_valid:1,
|
||||
num_ftmr_successes_valid:1,
|
||||
rssi_avg_valid:1,
|
||||
rssi_spread_valid:1,
|
||||
tx_rate_valid:1,
|
||||
rx_rate_valid:1,
|
||||
rtt_avg_valid:1,
|
||||
rtt_variance_valid:1,
|
||||
rtt_spread_valid:1,
|
||||
dist_avg_valid:1,
|
||||
dist_variance_valid:1,
|
||||
dist_spread_valid:1;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_pmsr_result - peer measurement result
|
||||
* @addr: address of the peer
|
||||
* @host_time: host time (use ktime_get_boottime() adjust to the time when the
|
||||
* measurement was made)
|
||||
* @ap_tsf: AP's TSF at measurement time
|
||||
* @status: status of the measurement
|
||||
* @final: if reporting partial results, mark this as the last one; if not
|
||||
* reporting partial results always set this flag
|
||||
* @ap_tsf_valid: indicates the @ap_tsf value is valid
|
||||
* @type: type of the measurement reported, note that we only support reporting
|
||||
* one type at a time, but you can report multiple results separately and
|
||||
* they're all aggregated for userspace.
|
||||
*/
|
||||
struct cfg80211_pmsr_result {
|
||||
u64 host_time, ap_tsf;
|
||||
enum nl80211_peer_measurement_status status;
|
||||
|
||||
u8 addr[ETH_ALEN];
|
||||
|
||||
u8 final:1,
|
||||
ap_tsf_valid:1;
|
||||
|
||||
enum nl80211_peer_measurement_type type;
|
||||
|
||||
union {
|
||||
struct cfg80211_pmsr_ftm_result ftm;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_pmsr_ftm_request_peer - FTM request data
|
||||
* @requested: indicates FTM is requested
|
||||
* @preamble: frame preamble to use
|
||||
* @burst_period: burst period to use
|
||||
* @asap: indicates to use ASAP mode
|
||||
* @num_bursts_exp: number of bursts exponent
|
||||
* @burst_duration: burst duration
|
||||
* @ftms_per_burst: number of FTMs per burst
|
||||
* @ftmr_retries: number of retries for FTM request
|
||||
* @request_lci: request LCI information
|
||||
* @request_civicloc: request civic location information
|
||||
*
|
||||
* See also nl80211 for the respective attribute documentation.
|
||||
*/
|
||||
struct cfg80211_pmsr_ftm_request_peer {
|
||||
enum nl80211_preamble preamble;
|
||||
u16 burst_period;
|
||||
u8 requested:1,
|
||||
asap:1,
|
||||
request_lci:1,
|
||||
request_civicloc:1;
|
||||
u8 num_bursts_exp;
|
||||
u8 burst_duration;
|
||||
u8 ftms_per_burst;
|
||||
u8 ftmr_retries;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_pmsr_request_peer - peer data for a peer measurement request
|
||||
* @addr: MAC address
|
||||
* @chandef: channel to use
|
||||
* @report_ap_tsf: report the associated AP's TSF
|
||||
* @ftm: FTM data, see &struct cfg80211_pmsr_ftm_request_peer
|
||||
*/
|
||||
struct cfg80211_pmsr_request_peer {
|
||||
u8 addr[ETH_ALEN];
|
||||
struct cfg80211_chan_def chandef;
|
||||
u8 report_ap_tsf:1;
|
||||
struct cfg80211_pmsr_ftm_request_peer ftm;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_pmsr_request - peer measurement request
|
||||
* @cookie: cookie, set by cfg80211
|
||||
* @nl_portid: netlink portid - used by cfg80211
|
||||
* @drv_data: driver data for this request, if required for aborting,
|
||||
* not otherwise freed or anything by cfg80211
|
||||
* @mac_addr: MAC address used for (randomised) request
|
||||
* @mac_addr_mask: MAC address mask used for randomisation, bits that
|
||||
* are 0 in the mask should be randomised, bits that are 1 should
|
||||
* be taken from the @mac_addr
|
||||
* @list: used by cfg80211 to hold on to the request
|
||||
* @timeout: timeout (in milliseconds) for the whole operation, if
|
||||
* zero it means there's no timeout
|
||||
* @n_peers: number of peers to do measurements with
|
||||
* @peers: per-peer measurement request data
|
||||
*/
|
||||
struct cfg80211_pmsr_request {
|
||||
u64 cookie;
|
||||
void *drv_data;
|
||||
u32 n_peers;
|
||||
u32 nl_portid;
|
||||
|
||||
u32 timeout;
|
||||
|
||||
u8 mac_addr[ETH_ALEN] __aligned(2);
|
||||
u8 mac_addr_mask[ETH_ALEN] __aligned(2);
|
||||
|
||||
struct list_head list;
|
||||
|
||||
struct cfg80211_pmsr_request_peer peers[];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_ops - backend description for wireless configuration
|
||||
*
|
||||
@ -3183,6 +3377,8 @@ struct cfg80211_ftm_responder_stats {
|
||||
*
|
||||
* @get_ftm_responder_stats: Retrieve FTM responder statistics, if available.
|
||||
* Statistics should be cumulative, currently no way to reset is provided.
|
||||
* @start_pmsr: start peer measurement (e.g. FTM)
|
||||
* @abort_pmsr: abort peer measurement
|
||||
*/
|
||||
struct cfg80211_ops {
|
||||
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
|
||||
@ -3492,6 +3688,11 @@ struct cfg80211_ops {
|
||||
int (*get_ftm_responder_stats)(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_ftm_responder_stats *ftm_stats);
|
||||
|
||||
int (*start_pmsr)(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
struct cfg80211_pmsr_request *request);
|
||||
void (*abort_pmsr)(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
struct cfg80211_pmsr_request *request);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -3863,6 +4064,42 @@ struct wiphy_iftype_ext_capab {
|
||||
u8 extended_capabilities_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_pmsr_capabilities - cfg80211 peer measurement capabilities
|
||||
* @max_peers: maximum number of peers in a single measurement
|
||||
* @report_ap_tsf: can report assoc AP's TSF for radio resource measurement
|
||||
* @randomize_mac_addr: can randomize MAC address for measurement
|
||||
* @ftm.supported: FTM measurement is supported
|
||||
* @ftm.asap: ASAP-mode is supported
|
||||
* @ftm.non_asap: non-ASAP-mode is supported
|
||||
* @ftm.request_lci: can request LCI data
|
||||
* @ftm.request_civicloc: can request civic location data
|
||||
* @ftm.preambles: bitmap of preambles supported (&enum nl80211_preamble)
|
||||
* @ftm.bandwidths: bitmap of bandwidths supported (&enum nl80211_chan_width)
|
||||
* @ftm.max_bursts_exponent: maximum burst exponent supported
|
||||
* (set to -1 if not limited; note that setting this will necessarily
|
||||
* forbid using the value 15 to let the responder pick)
|
||||
* @ftm.max_ftms_per_burst: maximum FTMs per burst supported (set to 0 if
|
||||
* not limited)
|
||||
*/
|
||||
struct cfg80211_pmsr_capabilities {
|
||||
unsigned int max_peers;
|
||||
u8 report_ap_tsf:1,
|
||||
randomize_mac_addr:1;
|
||||
|
||||
struct {
|
||||
u32 preambles;
|
||||
u32 bandwidths;
|
||||
s8 max_bursts_exponent;
|
||||
u8 max_ftms_per_burst;
|
||||
u8 supported:1,
|
||||
asap:1,
|
||||
non_asap:1,
|
||||
request_lci:1,
|
||||
request_civicloc:1;
|
||||
} ftm;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct wiphy - wireless hardware description
|
||||
* @reg_notifier: the driver's regulatory notification callback,
|
||||
@ -4027,6 +4264,8 @@ struct wiphy_iftype_ext_capab {
|
||||
* @txq_limit: configuration of internal TX queue frame limit
|
||||
* @txq_memory_limit: configuration internal TX queue memory limit
|
||||
* @txq_quantum: configuration of internal TX queue scheduler quantum
|
||||
*
|
||||
* @pmsr_capa: peer measurement capabilities
|
||||
*/
|
||||
struct wiphy {
|
||||
/* assign these fields before you register the wiphy */
|
||||
@ -4163,6 +4402,8 @@ struct wiphy {
|
||||
u32 txq_memory_limit;
|
||||
u32 txq_quantum;
|
||||
|
||||
const struct cfg80211_pmsr_capabilities *pmsr_capa;
|
||||
|
||||
char priv[0] __aligned(NETDEV_ALIGN);
|
||||
};
|
||||
|
||||
@ -4365,6 +4606,9 @@ struct cfg80211_cqm_config;
|
||||
* @owner_nlportid: (private) owner socket port ID
|
||||
* @nl_owner_dead: (private) owner socket went away
|
||||
* @cqm_config: (private) nl80211 RSSI monitor state
|
||||
* @pmsr_list: (private) peer measurement requests
|
||||
* @pmsr_lock: (private) peer measurements requests/results lock
|
||||
* @pmsr_free_wk: (private) peer measurements cleanup work
|
||||
*/
|
||||
struct wireless_dev {
|
||||
struct wiphy *wiphy;
|
||||
@ -4436,6 +4680,10 @@ struct wireless_dev {
|
||||
#endif
|
||||
|
||||
struct cfg80211_cqm_config *cqm_config;
|
||||
|
||||
struct list_head pmsr_list;
|
||||
spinlock_t pmsr_lock;
|
||||
struct work_struct pmsr_free_wk;
|
||||
};
|
||||
|
||||
static inline u8 *wdev_address(struct wireless_dev *wdev)
|
||||
@ -5328,7 +5576,8 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
|
||||
* cfg80211 then sends a notification to userspace.
|
||||
*/
|
||||
void cfg80211_notify_new_peer_candidate(struct net_device *dev,
|
||||
const u8 *macaddr, const u8 *ie, u8 ie_len, gfp_t gfp);
|
||||
const u8 *macaddr, const u8 *ie, u8 ie_len,
|
||||
int sig_dbm, gfp_t gfp);
|
||||
|
||||
/**
|
||||
* DOC: RFkill integration
|
||||
@ -6630,6 +6879,31 @@ int cfg80211_external_auth_request(struct net_device *netdev,
|
||||
struct cfg80211_external_auth_params *params,
|
||||
gfp_t gfp);
|
||||
|
||||
/**
|
||||
* cfg80211_pmsr_report - report peer measurement result data
|
||||
* @wdev: the wireless device reporting the measurement
|
||||
* @req: the original measurement request
|
||||
* @result: the result data
|
||||
* @gfp: allocation flags
|
||||
*/
|
||||
void cfg80211_pmsr_report(struct wireless_dev *wdev,
|
||||
struct cfg80211_pmsr_request *req,
|
||||
struct cfg80211_pmsr_result *result,
|
||||
gfp_t gfp);
|
||||
|
||||
/**
|
||||
* cfg80211_pmsr_complete - report peer measurement completed
|
||||
* @wdev: the wireless device reporting the measurement
|
||||
* @req: the original measurement request
|
||||
* @gfp: allocation flags
|
||||
*
|
||||
* Report that the entire measurement completed, after this
|
||||
* the request pointer will no longer be valid.
|
||||
*/
|
||||
void cfg80211_pmsr_complete(struct wireless_dev *wdev,
|
||||
struct cfg80211_pmsr_request *req,
|
||||
gfp_t gfp);
|
||||
|
||||
/* Logging, debugging and troubleshooting/diagnostic helpers. */
|
||||
|
||||
/* wiphy_printk helpers, similar to dev_printk */
|
||||
|
@ -467,7 +467,7 @@ struct ieee80211_mu_group_data {
|
||||
};
|
||||
|
||||
/**
|
||||
* ieee80211_ftm_responder_params - FTM responder parameters
|
||||
* struct ieee80211_ftm_responder_params - FTM responder parameters
|
||||
*
|
||||
* @lci: LCI subelement content
|
||||
* @civicloc: CIVIC location subelement content
|
||||
@ -496,6 +496,8 @@ struct ieee80211_ftm_responder_params {
|
||||
* @uora_ocw_range: UORA element's OCW Range field
|
||||
* @frame_time_rts_th: HE duration RTS threshold, in units of 32us
|
||||
* @he_support: does this BSS support HE
|
||||
* @twt_requester: does this BSS support TWT requester (relevant for managed
|
||||
* mode only, set if the AP advertises TWT responder role)
|
||||
* @assoc: association status
|
||||
* @ibss_joined: indicates whether this station is part of an IBSS
|
||||
* or not
|
||||
@ -594,6 +596,7 @@ struct ieee80211_bss_conf {
|
||||
u8 uora_ocw_range;
|
||||
u16 frame_time_rts_th;
|
||||
bool he_support;
|
||||
bool twt_requester;
|
||||
/* association related data */
|
||||
bool assoc, ibss_joined;
|
||||
bool ibss_creator;
|
||||
@ -3239,6 +3242,11 @@ enum ieee80211_reconfig_type {
|
||||
* When the scan finishes, ieee80211_scan_completed() must be called;
|
||||
* note that it also must be called when the scan cannot finish due to
|
||||
* any error unless this callback returned a negative error code.
|
||||
* This callback is also allowed to return the special return value 1,
|
||||
* this indicates that hardware scan isn't desirable right now and a
|
||||
* software scan should be done instead. A driver wishing to use this
|
||||
* capability must ensure its (hardware) scan capabilities aren't
|
||||
* advertised as more capable than mac80211's software scan is.
|
||||
* The callback can sleep.
|
||||
*
|
||||
* @cancel_hw_scan: Ask the low-level tp cancel the active hw scan.
|
||||
@ -3623,6 +3631,9 @@ enum ieee80211_reconfig_type {
|
||||
* skb is always a real frame, head may or may not be an A-MSDU.
|
||||
* @get_ftm_responder_stats: Retrieve FTM responder statistics, if available.
|
||||
* Statistics should be cumulative, currently no way to reset is provided.
|
||||
*
|
||||
* @start_pmsr: start peer measurement (e.g. FTM) (this call can sleep)
|
||||
* @abort_pmsr: abort peer measurement (this call can sleep)
|
||||
*/
|
||||
struct ieee80211_ops {
|
||||
void (*tx)(struct ieee80211_hw *hw,
|
||||
@ -3911,6 +3922,10 @@ struct ieee80211_ops {
|
||||
int (*get_ftm_responder_stats)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_ftm_responder_stats *ftm_stats);
|
||||
int (*start_pmsr)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct cfg80211_pmsr_request *request);
|
||||
void (*abort_pmsr)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct cfg80211_pmsr_request *request);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -6091,6 +6106,14 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid);
|
||||
* @txq: pointer obtained from station or virtual interface
|
||||
*
|
||||
* Returns the skb if successful, %NULL if no frame was available.
|
||||
*
|
||||
* Note that this must be called in an rcu_read_lock() critical section,
|
||||
* which can only be released after the SKB was handled. Some pointers in
|
||||
* skb->cb, e.g. the key pointer, are protected by by RCU and thus the
|
||||
* critical section must persist not just for the duration of this call
|
||||
* but for the duration of the frame handling.
|
||||
* However, also note that while in the wake_tx_queue() method,
|
||||
* rcu_read_lock() is already held.
|
||||
*/
|
||||
struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
|
||||
struct ieee80211_txq *txq);
|
||||
|
@ -1036,6 +1036,35 @@
|
||||
* @NL80211_CMD_GET_FTM_RESPONDER_STATS: Retrieve FTM responder statistics, in
|
||||
* the %NL80211_ATTR_FTM_RESPONDER_STATS attribute.
|
||||
*
|
||||
* @NL80211_CMD_PEER_MEASUREMENT_START: start a (set of) peer measurement(s)
|
||||
* with the given parameters, which are encapsulated in the nested
|
||||
* %NL80211_ATTR_PEER_MEASUREMENTS attribute. Optionally, MAC address
|
||||
* randomization may be enabled and configured by specifying the
|
||||
* %NL80211_ATTR_MAC and %NL80211_ATTR_MAC_MASK attributes.
|
||||
* If a timeout is requested, use the %NL80211_ATTR_TIMEOUT attribute.
|
||||
* A u64 cookie for further %NL80211_ATTR_COOKIE use is is returned in
|
||||
* the netlink extended ack message.
|
||||
*
|
||||
* To cancel a measurement, close the socket that requested it.
|
||||
*
|
||||
* Measurement results are reported to the socket that requested the
|
||||
* measurement using @NL80211_CMD_PEER_MEASUREMENT_RESULT when they
|
||||
* become available, so applications must ensure a large enough socket
|
||||
* buffer size.
|
||||
*
|
||||
* Depending on driver support it may or may not be possible to start
|
||||
* multiple concurrent measurements.
|
||||
* @NL80211_CMD_PEER_MEASUREMENT_RESULT: This command number is used for the
|
||||
* result notification from the driver to the requesting socket.
|
||||
* @NL80211_CMD_PEER_MEASUREMENT_COMPLETE: Notification only, indicating that
|
||||
* the measurement completed, using the measurement cookie
|
||||
* (%NL80211_ATTR_COOKIE).
|
||||
*
|
||||
* @NL80211_CMD_NOTIFY_RADAR: Notify the kernel that a radar signal was
|
||||
* detected and reported by a neighboring device on the channel
|
||||
* indicated by %NL80211_ATTR_WIPHY_FREQ and other attributes
|
||||
* determining the width and type.
|
||||
*
|
||||
* @NL80211_CMD_MAX: highest used command number
|
||||
* @__NL80211_CMD_AFTER_LAST: internal use
|
||||
*/
|
||||
@ -1250,6 +1279,12 @@ enum nl80211_commands {
|
||||
|
||||
NL80211_CMD_GET_FTM_RESPONDER_STATS,
|
||||
|
||||
NL80211_CMD_PEER_MEASUREMENT_START,
|
||||
NL80211_CMD_PEER_MEASUREMENT_RESULT,
|
||||
NL80211_CMD_PEER_MEASUREMENT_COMPLETE,
|
||||
|
||||
NL80211_CMD_NOTIFY_RADAR,
|
||||
|
||||
/* add new commands above here */
|
||||
|
||||
/* used to define NL80211_CMD_MAX below */
|
||||
@ -1706,7 +1741,7 @@ enum nl80211_commands {
|
||||
* the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID
|
||||
* is included in the probe request, but the match attributes
|
||||
* will never let it go through), -EINVAL may be returned.
|
||||
* If ommited, no filtering is done.
|
||||
* If omitted, no filtering is done.
|
||||
*
|
||||
* @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported
|
||||
* interface combinations. In each nested item, it contains attributes
|
||||
@ -1811,7 +1846,7 @@ enum nl80211_commands {
|
||||
*
|
||||
* @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be
|
||||
* used by the drivers which has MLME in firmware and does not have support
|
||||
* to report per station tx/rx activity to free up the staion entry from
|
||||
* to report per station tx/rx activity to free up the station entry from
|
||||
* the list. This needs to be used when the driver advertises the
|
||||
* capability to timeout the stations.
|
||||
*
|
||||
@ -2172,7 +2207,7 @@ enum nl80211_commands {
|
||||
*
|
||||
* @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in
|
||||
* the specified band is to be adjusted before doing
|
||||
* %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparision to figure out
|
||||
* %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparison to figure out
|
||||
* better BSSs. The attribute value is a packed structure
|
||||
* value as specified by &struct nl80211_bss_select_rssi_adjust.
|
||||
*
|
||||
@ -2254,6 +2289,16 @@ enum nl80211_commands {
|
||||
* @NL80211_ATTR_FTM_RESPONDER_STATS: Nested attribute with FTM responder
|
||||
* statistics, see &enum nl80211_ftm_responder_stats.
|
||||
*
|
||||
* @NL80211_ATTR_TIMEOUT: Timeout for the given operation in milliseconds (u32),
|
||||
* if the attribute is not given no timeout is requested. Note that 0 is an
|
||||
* invalid value.
|
||||
*
|
||||
* @NL80211_ATTR_PEER_MEASUREMENTS: peer measurements request (and result)
|
||||
* data, uses nested attributes specified in
|
||||
* &enum nl80211_peer_measurement_attrs.
|
||||
* This is also used for capability advertisement in the wiphy information,
|
||||
* with the appropriate sub-attributes.
|
||||
*
|
||||
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
|
||||
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||||
* @__NL80211_ATTR_AFTER_LAST: internal use
|
||||
@ -2699,6 +2744,10 @@ enum nl80211_attrs {
|
||||
|
||||
NL80211_ATTR_FTM_RESPONDER_STATS,
|
||||
|
||||
NL80211_ATTR_TIMEOUT,
|
||||
|
||||
NL80211_ATTR_PEER_MEASUREMENTS,
|
||||
|
||||
/* add attributes here, update the policy in nl80211.c */
|
||||
|
||||
__NL80211_ATTR_AFTER_LAST,
|
||||
@ -3074,6 +3123,8 @@ enum nl80211_sta_bss_param {
|
||||
* with an FCS error (u32, from this station). This count may not include
|
||||
* some packets with an FCS error due to TA corruption. Hence this counter
|
||||
* might not be fully accurate.
|
||||
* @NL80211_STA_INFO_CONNECTED_TO_GATE: set to true if STA has a path to a
|
||||
* mesh gate (u8, 0 or 1)
|
||||
* @__NL80211_STA_INFO_AFTER_LAST: internal
|
||||
* @NL80211_STA_INFO_MAX: highest possible station info attribute
|
||||
*/
|
||||
@ -3116,6 +3167,7 @@ enum nl80211_sta_info {
|
||||
NL80211_STA_INFO_ACK_SIGNAL_AVG,
|
||||
NL80211_STA_INFO_RX_MPDUS,
|
||||
NL80211_STA_INFO_FCS_ERROR_COUNT,
|
||||
NL80211_STA_INFO_CONNECTED_TO_GATE,
|
||||
|
||||
/* keep last */
|
||||
__NL80211_STA_INFO_AFTER_LAST,
|
||||
@ -3895,6 +3947,11 @@ enum nl80211_mesh_power_mode {
|
||||
* remove it from the STA's list of peers. You may set this to 0 to disable
|
||||
* the removal of the STA. Default is 30 minutes.
|
||||
*
|
||||
* @NL80211_MESHCONF_CONNECTED_TO_GATE: If set to true then this mesh STA
|
||||
* will advertise that it is connected to a gate in the mesh formation
|
||||
* field. If left unset then the mesh formation field will only
|
||||
* advertise such if there is an active root mesh path.
|
||||
*
|
||||
* @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
|
||||
*/
|
||||
enum nl80211_meshconf_params {
|
||||
@ -3927,6 +3984,7 @@ enum nl80211_meshconf_params {
|
||||
NL80211_MESHCONF_POWER_MODE,
|
||||
NL80211_MESHCONF_AWAKE_WINDOW,
|
||||
NL80211_MESHCONF_PLINK_TIMEOUT,
|
||||
NL80211_MESHCONF_CONNECTED_TO_GATE,
|
||||
|
||||
/* keep last */
|
||||
__NL80211_MESHCONF_ATTR_AFTER_LAST,
|
||||
@ -4859,7 +4917,7 @@ enum nl80211_iface_limit_attrs {
|
||||
* numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4
|
||||
* => allows a STA plus three P2P interfaces
|
||||
*
|
||||
* The list of these four possiblities could completely be contained
|
||||
* The list of these four possibilities could completely be contained
|
||||
* within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate
|
||||
* that any of these groups must match.
|
||||
*
|
||||
@ -4889,7 +4947,7 @@ enum nl80211_if_combination_attrs {
|
||||
* enum nl80211_plink_state - state of a mesh peer link finite state machine
|
||||
*
|
||||
* @NL80211_PLINK_LISTEN: initial state, considered the implicit
|
||||
* state of non existant mesh peer links
|
||||
* state of non existent mesh peer links
|
||||
* @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to
|
||||
* this mesh peer
|
||||
* @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received
|
||||
@ -5381,7 +5439,7 @@ enum nl80211_timeout_reason {
|
||||
* request parameters IE in the probe request
|
||||
* @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses
|
||||
* @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at
|
||||
* rate of at least 5.5M. In case non OCE AP is dicovered in the channel,
|
||||
* rate of at least 5.5M. In case non OCE AP is discovered in the channel,
|
||||
* only the first probe req in the channel will be sent in high rate.
|
||||
* @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request
|
||||
* tx deferral (dot11FILSProbeDelay shall be set to 15ms)
|
||||
@ -5842,9 +5900,11 @@ enum nl80211_external_auth_action {
|
||||
* @__NL80211_FTM_RESP_ATTR_INVALID: Invalid
|
||||
* @NL80211_FTM_RESP_ATTR_ENABLED: FTM responder is enabled
|
||||
* @NL80211_FTM_RESP_ATTR_LCI: The content of Measurement Report Element
|
||||
* (9.4.2.22 in 802.11-2016) with type 8 - LCI (9.4.2.22.10)
|
||||
* (9.4.2.22 in 802.11-2016) with type 8 - LCI (9.4.2.22.10),
|
||||
* i.e. starting with the measurement token
|
||||
* @NL80211_FTM_RESP_ATTR_CIVIC: The content of Measurement Report Element
|
||||
* (9.4.2.22 in 802.11-2016) with type 11 - Civic (Section 9.4.2.22.13)
|
||||
* (9.4.2.22 in 802.11-2016) with type 11 - Civic (Section 9.4.2.22.13),
|
||||
* i.e. starting with the measurement token
|
||||
* @__NL80211_FTM_RESP_ATTR_LAST: Internal
|
||||
* @NL80211_FTM_RESP_ATTR_MAX: highest FTM responder attribute.
|
||||
*/
|
||||
@ -5906,4 +5966,386 @@ enum nl80211_ftm_responder_stats {
|
||||
NL80211_FTM_STATS_MAX = __NL80211_FTM_STATS_AFTER_LAST - 1
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl80211_preamble - frame preamble types
|
||||
* @NL80211_PREAMBLE_LEGACY: legacy (HR/DSSS, OFDM, ERP PHY) preamble
|
||||
* @NL80211_PREAMBLE_HT: HT preamble
|
||||
* @NL80211_PREAMBLE_VHT: VHT preamble
|
||||
* @NL80211_PREAMBLE_DMG: DMG preamble
|
||||
*/
|
||||
enum nl80211_preamble {
|
||||
NL80211_PREAMBLE_LEGACY,
|
||||
NL80211_PREAMBLE_HT,
|
||||
NL80211_PREAMBLE_VHT,
|
||||
NL80211_PREAMBLE_DMG,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl80211_peer_measurement_type - peer measurement types
|
||||
* @NL80211_PMSR_TYPE_INVALID: invalid/unused, needed as we use
|
||||
* these numbers also for attributes
|
||||
*
|
||||
* @NL80211_PMSR_TYPE_FTM: flight time measurement
|
||||
*
|
||||
* @NUM_NL80211_PMSR_TYPES: internal
|
||||
* @NL80211_PMSR_TYPE_MAX: highest type number
|
||||
*/
|
||||
enum nl80211_peer_measurement_type {
|
||||
NL80211_PMSR_TYPE_INVALID,
|
||||
|
||||
NL80211_PMSR_TYPE_FTM,
|
||||
|
||||
NUM_NL80211_PMSR_TYPES,
|
||||
NL80211_PMSR_TYPE_MAX = NUM_NL80211_PMSR_TYPES - 1
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl80211_peer_measurement_status - peer measurement status
|
||||
* @NL80211_PMSR_STATUS_SUCCESS: measurement completed successfully
|
||||
* @NL80211_PMSR_STATUS_REFUSED: measurement was locally refused
|
||||
* @NL80211_PMSR_STATUS_TIMEOUT: measurement timed out
|
||||
* @NL80211_PMSR_STATUS_FAILURE: measurement failed, a type-dependent
|
||||
* reason may be available in the response data
|
||||
*/
|
||||
enum nl80211_peer_measurement_status {
|
||||
NL80211_PMSR_STATUS_SUCCESS,
|
||||
NL80211_PMSR_STATUS_REFUSED,
|
||||
NL80211_PMSR_STATUS_TIMEOUT,
|
||||
NL80211_PMSR_STATUS_FAILURE,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl80211_peer_measurement_req - peer measurement request attributes
|
||||
* @__NL80211_PMSR_REQ_ATTR_INVALID: invalid
|
||||
*
|
||||
* @NL80211_PMSR_REQ_ATTR_DATA: This is a nested attribute with measurement
|
||||
* type-specific request data inside. The attributes used are from the
|
||||
* enums named nl80211_peer_measurement_<type>_req.
|
||||
* @NL80211_PMSR_REQ_ATTR_GET_AP_TSF: include AP TSF timestamp, if supported
|
||||
* (flag attribute)
|
||||
*
|
||||
* @NUM_NL80211_PMSR_REQ_ATTRS: internal
|
||||
* @NL80211_PMSR_REQ_ATTR_MAX: highest attribute number
|
||||
*/
|
||||
enum nl80211_peer_measurement_req {
|
||||
__NL80211_PMSR_REQ_ATTR_INVALID,
|
||||
|
||||
NL80211_PMSR_REQ_ATTR_DATA,
|
||||
NL80211_PMSR_REQ_ATTR_GET_AP_TSF,
|
||||
|
||||
/* keep last */
|
||||
NUM_NL80211_PMSR_REQ_ATTRS,
|
||||
NL80211_PMSR_REQ_ATTR_MAX = NUM_NL80211_PMSR_REQ_ATTRS - 1
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl80211_peer_measurement_resp - peer measurement response attributes
|
||||
* @__NL80211_PMSR_RESP_ATTR_INVALID: invalid
|
||||
*
|
||||
* @NL80211_PMSR_RESP_ATTR_DATA: This is a nested attribute with measurement
|
||||
* type-specific results inside. The attributes used are from the enums
|
||||
* named nl80211_peer_measurement_<type>_resp.
|
||||
* @NL80211_PMSR_RESP_ATTR_STATUS: u32 value with the measurement status
|
||||
* (using values from &enum nl80211_peer_measurement_status.)
|
||||
* @NL80211_PMSR_RESP_ATTR_HOST_TIME: host time (%CLOCK_BOOTTIME) when the
|
||||
* result was measured; this value is not expected to be accurate to
|
||||
* more than 20ms. (u64, nanoseconds)
|
||||
* @NL80211_PMSR_RESP_ATTR_AP_TSF: TSF of the AP that the interface
|
||||
* doing the measurement is connected to when the result was measured.
|
||||
* This shall be accurately reported if supported and requested
|
||||
* (u64, usec)
|
||||
* @NL80211_PMSR_RESP_ATTR_FINAL: If results are sent to the host partially
|
||||
* (*e.g. with FTM per-burst data) this flag will be cleared on all but
|
||||
* the last result; if all results are combined it's set on the single
|
||||
* result.
|
||||
* @NL80211_PMSR_RESP_ATTR_PAD: padding for 64-bit attributes, ignore
|
||||
*
|
||||
* @NUM_NL80211_PMSR_RESP_ATTRS: internal
|
||||
* @NL80211_PMSR_RESP_ATTR_MAX: highest attribute number
|
||||
*/
|
||||
enum nl80211_peer_measurement_resp {
|
||||
__NL80211_PMSR_RESP_ATTR_INVALID,
|
||||
|
||||
NL80211_PMSR_RESP_ATTR_DATA,
|
||||
NL80211_PMSR_RESP_ATTR_STATUS,
|
||||
NL80211_PMSR_RESP_ATTR_HOST_TIME,
|
||||
NL80211_PMSR_RESP_ATTR_AP_TSF,
|
||||
NL80211_PMSR_RESP_ATTR_FINAL,
|
||||
NL80211_PMSR_RESP_ATTR_PAD,
|
||||
|
||||
/* keep last */
|
||||
NUM_NL80211_PMSR_RESP_ATTRS,
|
||||
NL80211_PMSR_RESP_ATTR_MAX = NUM_NL80211_PMSR_RESP_ATTRS - 1
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl80211_peer_measurement_peer_attrs - peer attributes for measurement
|
||||
* @__NL80211_PMSR_PEER_ATTR_INVALID: invalid
|
||||
*
|
||||
* @NL80211_PMSR_PEER_ATTR_ADDR: peer's MAC address
|
||||
* @NL80211_PMSR_PEER_ATTR_CHAN: channel definition, nested, using top-level
|
||||
* attributes like %NL80211_ATTR_WIPHY_FREQ etc.
|
||||
* @NL80211_PMSR_PEER_ATTR_REQ: This is a nested attribute indexed by
|
||||
* measurement type, with attributes from the
|
||||
* &enum nl80211_peer_measurement_req inside.
|
||||
* @NL80211_PMSR_PEER_ATTR_RESP: This is a nested attribute indexed by
|
||||
* measurement type, with attributes from the
|
||||
* &enum nl80211_peer_measurement_resp inside.
|
||||
*
|
||||
* @NUM_NL80211_PMSR_PEER_ATTRS: internal
|
||||
* @NL80211_PMSR_PEER_ATTR_MAX: highest attribute number
|
||||
*/
|
||||
enum nl80211_peer_measurement_peer_attrs {
|
||||
__NL80211_PMSR_PEER_ATTR_INVALID,
|
||||
|
||||
NL80211_PMSR_PEER_ATTR_ADDR,
|
||||
NL80211_PMSR_PEER_ATTR_CHAN,
|
||||
NL80211_PMSR_PEER_ATTR_REQ,
|
||||
NL80211_PMSR_PEER_ATTR_RESP,
|
||||
|
||||
/* keep last */
|
||||
NUM_NL80211_PMSR_PEER_ATTRS,
|
||||
NL80211_PMSR_PEER_ATTR_MAX = NUM_NL80211_PMSR_PEER_ATTRS - 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl80211_peer_measurement_attrs - peer measurement attributes
|
||||
* @__NL80211_PMSR_ATTR_INVALID: invalid
|
||||
*
|
||||
* @NL80211_PMSR_ATTR_MAX_PEERS: u32 attribute used for capability
|
||||
* advertisement only, indicates the maximum number of peers
|
||||
* measurements can be done with in a single request
|
||||
* @NL80211_PMSR_ATTR_REPORT_AP_TSF: flag attribute in capability
|
||||
* indicating that the connected AP's TSF can be reported in
|
||||
* measurement results
|
||||
* @NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR: flag attribute in capability
|
||||
* indicating that MAC address randomization is supported.
|
||||
* @NL80211_PMSR_ATTR_TYPE_CAPA: capabilities reported by the device,
|
||||
* this contains a nesting indexed by measurement type, and
|
||||
* type-specific capabilities inside, which are from the enums
|
||||
* named nl80211_peer_measurement_<type>_capa.
|
||||
* @NL80211_PMSR_ATTR_PEERS: nested attribute, the nesting index is
|
||||
* meaningless, just a list of peers to measure with, with the
|
||||
* sub-attributes taken from
|
||||
* &enum nl80211_peer_measurement_peer_attrs.
|
||||
*
|
||||
* @NUM_NL80211_PMSR_ATTR: internal
|
||||
* @NL80211_PMSR_ATTR_MAX: highest attribute number
|
||||
*/
|
||||
enum nl80211_peer_measurement_attrs {
|
||||
__NL80211_PMSR_ATTR_INVALID,
|
||||
|
||||
NL80211_PMSR_ATTR_MAX_PEERS,
|
||||
NL80211_PMSR_ATTR_REPORT_AP_TSF,
|
||||
NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR,
|
||||
NL80211_PMSR_ATTR_TYPE_CAPA,
|
||||
NL80211_PMSR_ATTR_PEERS,
|
||||
|
||||
/* keep last */
|
||||
NUM_NL80211_PMSR_ATTR,
|
||||
NL80211_PMSR_ATTR_MAX = NUM_NL80211_PMSR_ATTR - 1
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl80211_peer_measurement_ftm_capa - FTM capabilities
|
||||
* @__NL80211_PMSR_FTM_CAPA_ATTR_INVALID: invalid
|
||||
*
|
||||
* @NL80211_PMSR_FTM_CAPA_ATTR_ASAP: flag attribute indicating ASAP mode
|
||||
* is supported
|
||||
* @NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP: flag attribute indicating non-ASAP
|
||||
* mode is supported
|
||||
* @NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI: flag attribute indicating if LCI
|
||||
* data can be requested during the measurement
|
||||
* @NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC: flag attribute indicating if civic
|
||||
* location data can be requested during the measurement
|
||||
* @NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES: u32 bitmap attribute of bits
|
||||
* from &enum nl80211_preamble.
|
||||
* @NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS: bitmap of values from
|
||||
* &enum nl80211_chan_width indicating the supported channel
|
||||
* bandwidths for FTM. Note that a higher channel bandwidth may be
|
||||
* configured to allow for other measurements types with different
|
||||
* bandwidth requirement in the same measurement.
|
||||
* @NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT: u32 attribute indicating
|
||||
* the maximum bursts exponent that can be used (if not present anything
|
||||
* is valid)
|
||||
* @NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST: u32 attribute indicating
|
||||
* the maximum FTMs per burst (if not present anything is valid)
|
||||
*
|
||||
* @NUM_NL80211_PMSR_FTM_CAPA_ATTR: internal
|
||||
* @NL80211_PMSR_FTM_CAPA_ATTR_MAX: highest attribute number
|
||||
*/
|
||||
enum nl80211_peer_measurement_ftm_capa {
|
||||
__NL80211_PMSR_FTM_CAPA_ATTR_INVALID,
|
||||
|
||||
NL80211_PMSR_FTM_CAPA_ATTR_ASAP,
|
||||
NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP,
|
||||
NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI,
|
||||
NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC,
|
||||
NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES,
|
||||
NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS,
|
||||
NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT,
|
||||
NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST,
|
||||
|
||||
/* keep last */
|
||||
NUM_NL80211_PMSR_FTM_CAPA_ATTR,
|
||||
NL80211_PMSR_FTM_CAPA_ATTR_MAX = NUM_NL80211_PMSR_FTM_CAPA_ATTR - 1
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl80211_peer_measurement_ftm_req - FTM request attributes
|
||||
* @__NL80211_PMSR_FTM_REQ_ATTR_INVALID: invalid
|
||||
*
|
||||
* @NL80211_PMSR_FTM_REQ_ATTR_ASAP: ASAP mode requested (flag)
|
||||
* @NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE: preamble type (see
|
||||
* &enum nl80211_preamble), optional for DMG (u32)
|
||||
* @NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP: number of bursts exponent as in
|
||||
* 802.11-2016 9.4.2.168 "Fine Timing Measurement Parameters element"
|
||||
* (u8, 0-15, optional with default 15 i.e. "no preference")
|
||||
* @NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD: interval between bursts in units
|
||||
* of 100ms (u16, optional with default 0)
|
||||
* @NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION: burst duration, as in 802.11-2016
|
||||
* Table 9-257 "Burst Duration field encoding" (u8, 0-15, optional with
|
||||
* default 15 i.e. "no preference")
|
||||
* @NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST: number of successful FTM frames
|
||||
* requested per burst
|
||||
* (u8, 0-31, optional with default 0 i.e. "no preference")
|
||||
* @NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES: number of FTMR frame retries
|
||||
* (u8, default 3)
|
||||
* @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI: request LCI data (flag)
|
||||
* @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC: request civic location data
|
||||
* (flag)
|
||||
*
|
||||
* @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal
|
||||
* @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number
|
||||
*/
|
||||
enum nl80211_peer_measurement_ftm_req {
|
||||
__NL80211_PMSR_FTM_REQ_ATTR_INVALID,
|
||||
|
||||
NL80211_PMSR_FTM_REQ_ATTR_ASAP,
|
||||
NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE,
|
||||
NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP,
|
||||
NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD,
|
||||
NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION,
|
||||
NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST,
|
||||
NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES,
|
||||
NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI,
|
||||
NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC,
|
||||
|
||||
/* keep last */
|
||||
NUM_NL80211_PMSR_FTM_REQ_ATTR,
|
||||
NL80211_PMSR_FTM_REQ_ATTR_MAX = NUM_NL80211_PMSR_FTM_REQ_ATTR - 1
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl80211_peer_measurement_ftm_failure_reasons - FTM failure reasons
|
||||
* @NL80211_PMSR_FTM_FAILURE_UNSPECIFIED: unspecified failure, not used
|
||||
* @NL80211_PMSR_FTM_FAILURE_NO_RESPONSE: no response from the FTM responder
|
||||
* @NL80211_PMSR_FTM_FAILURE_REJECTED: FTM responder rejected measurement
|
||||
* @NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL: we already know the peer is
|
||||
* on a different channel, so can't measure (if we didn't know, we'd
|
||||
* try and get no response)
|
||||
* @NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE: peer can't actually do FTM
|
||||
* @NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP: invalid T1/T4 timestamps
|
||||
* received
|
||||
* @NL80211_PMSR_FTM_FAILURE_PEER_BUSY: peer reports busy, you may retry
|
||||
* later (see %NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME)
|
||||
* @NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS: parameters were changed
|
||||
* by the peer and are no longer supported
|
||||
*/
|
||||
enum nl80211_peer_measurement_ftm_failure_reasons {
|
||||
NL80211_PMSR_FTM_FAILURE_UNSPECIFIED,
|
||||
NL80211_PMSR_FTM_FAILURE_NO_RESPONSE,
|
||||
NL80211_PMSR_FTM_FAILURE_REJECTED,
|
||||
NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL,
|
||||
NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE,
|
||||
NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP,
|
||||
NL80211_PMSR_FTM_FAILURE_PEER_BUSY,
|
||||
NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl80211_peer_measurement_ftm_resp - FTM response attributes
|
||||
* @__NL80211_PMSR_FTM_RESP_ATTR_INVALID: invalid
|
||||
*
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON: FTM-specific failure reason
|
||||
* (u32, optional)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX: optional, if bursts are reported
|
||||
* as separate results then it will be the burst index 0...(N-1) and
|
||||
* the top level will indicate partial results (u32)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS: number of FTM Request frames
|
||||
* transmitted (u32, optional)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES: number of FTM Request frames
|
||||
* that were acknowleged (u32, optional)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME: retry time received from the
|
||||
* busy peer (u32, seconds)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP: actual number of bursts exponent
|
||||
* used by the responder (similar to request, u8)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION: actual burst duration used by
|
||||
* the responder (similar to request, u8)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST: actual FTMs per burst used
|
||||
* by the responder (similar to request, u8)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG: average RSSI across all FTM action
|
||||
* frames (optional, s32, 1/2 dBm)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD: RSSI spread across all FTM action
|
||||
* frames (optional, s32, 1/2 dBm)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_TX_RATE: bitrate we used for the response to the
|
||||
* FTM action frame (optional, nested, using &enum nl80211_rate_info
|
||||
* attributes)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_RX_RATE: bitrate the responder used for the FTM
|
||||
* action frame (optional, nested, using &enum nl80211_rate_info attrs)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG: average RTT (s64, picoseconds, optional
|
||||
* but one of RTT/DIST must be present)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE: RTT variance (u64, ps^2, note that
|
||||
* standard deviation is the square root of variance, optional)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD: RTT spread (u64, picoseconds,
|
||||
* optional)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG: average distance (s64, mm, optional
|
||||
* but one of RTT/DIST must be present)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE: distance variance (u64, mm^2, note
|
||||
* that standard deviation is the square root of variance, optional)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD: distance spread (u64, mm, optional)
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_LCI: LCI data from peer (binary, optional);
|
||||
* this is the contents of the Measurement Report Element (802.11-2016
|
||||
* 9.4.2.22.1) starting with the Measurement Token, with Measurement
|
||||
* Type 8.
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC: civic location data from peer
|
||||
* (binary, optional);
|
||||
* this is the contents of the Measurement Report Element (802.11-2016
|
||||
* 9.4.2.22.1) starting with the Measurement Token, with Measurement
|
||||
* Type 11.
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_PAD: ignore, for u64/s64 padding only
|
||||
*
|
||||
* @NUM_NL80211_PMSR_FTM_RESP_ATTR: internal
|
||||
* @NL80211_PMSR_FTM_RESP_ATTR_MAX: highest attribute number
|
||||
*/
|
||||
enum nl80211_peer_measurement_ftm_resp {
|
||||
__NL80211_PMSR_FTM_RESP_ATTR_INVALID,
|
||||
|
||||
NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_TX_RATE,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_RX_RATE,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_LCI,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_PAD,
|
||||
|
||||
/* keep last */
|
||||
NUM_NL80211_PMSR_FTM_RESP_ATTR,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_MAX = NUM_NL80211_PMSR_FTM_RESP_ATTR - 1
|
||||
};
|
||||
|
||||
#endif /* __LINUX_NL80211_H */
|
||||
|
@ -57,14 +57,13 @@ comment "Some wireless drivers require a rate control algorithm"
|
||||
depends on MAC80211 && MAC80211_HAS_RC=n
|
||||
|
||||
config MAC80211_MESH
|
||||
bool "Enable mac80211 mesh networking (pre-802.11s) support"
|
||||
bool "Enable mac80211 mesh networking support"
|
||||
depends on MAC80211
|
||||
---help---
|
||||
This options enables support of Draft 802.11s mesh networking.
|
||||
The implementation is based on Draft 2.08 of the Mesh Networking
|
||||
amendment. However, no compliance with that draft is claimed or even
|
||||
possible, as drafts leave a number of identifiers to be defined after
|
||||
ratification. For more information visit http://o11s.org/.
|
||||
Select this option to enable 802.11 mesh operation in mac80211
|
||||
drivers that support it. 802.11 mesh connects multiple stations
|
||||
over (possibly multi-hop) wireless links to form a single logical
|
||||
LAN.
|
||||
|
||||
config MAC80211_LEDS
|
||||
bool "Enable LED triggers"
|
||||
|
@ -800,8 +800,8 @@ static int ieee80211_set_ftm_responder_params(
|
||||
u8 *pos;
|
||||
int len;
|
||||
|
||||
if ((!lci || !lci_len) && (!civicloc || !civicloc_len))
|
||||
return 1;
|
||||
if (!lci_len && !civicloc_len)
|
||||
return 0;
|
||||
|
||||
bss_conf = &sdata->vif.bss_conf;
|
||||
old = bss_conf->ftmr_params;
|
||||
@ -2028,6 +2028,9 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy,
|
||||
nconf->dot11MeshAwakeWindowDuration;
|
||||
if (_chg_mesh_attr(NL80211_MESHCONF_PLINK_TIMEOUT, mask))
|
||||
conf->plink_timeout = nconf->plink_timeout;
|
||||
if (_chg_mesh_attr(NL80211_MESHCONF_CONNECTED_TO_GATE, mask))
|
||||
conf->dot11MeshConnectedToMeshGate =
|
||||
nconf->dot11MeshConnectedToMeshGate;
|
||||
ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);
|
||||
return 0;
|
||||
}
|
||||
@ -3850,6 +3853,26 @@ ieee80211_get_ftm_responder_stats(struct wiphy *wiphy,
|
||||
return drv_get_ftm_responder_stats(local, sdata, ftm_stats);
|
||||
}
|
||||
|
||||
static int
|
||||
ieee80211_start_pmsr(struct wiphy *wiphy, struct wireless_dev *dev,
|
||||
struct cfg80211_pmsr_request *request)
|
||||
{
|
||||
struct ieee80211_local *local = wiphy_priv(wiphy);
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(dev);
|
||||
|
||||
return drv_start_pmsr(local, sdata, request);
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_abort_pmsr(struct wiphy *wiphy, struct wireless_dev *dev,
|
||||
struct cfg80211_pmsr_request *request)
|
||||
{
|
||||
struct ieee80211_local *local = wiphy_priv(wiphy);
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(dev);
|
||||
|
||||
return drv_abort_pmsr(local, sdata, request);
|
||||
}
|
||||
|
||||
const struct cfg80211_ops mac80211_config_ops = {
|
||||
.add_virtual_intf = ieee80211_add_iface,
|
||||
.del_virtual_intf = ieee80211_del_iface,
|
||||
@ -3945,4 +3968,6 @@ const struct cfg80211_ops mac80211_config_ops = {
|
||||
.tx_control_port = ieee80211_tx_control_port,
|
||||
.get_txq_stats = ieee80211_get_txq_stats,
|
||||
.get_ftm_responder_stats = ieee80211_get_ftm_responder_stats,
|
||||
.start_pmsr = ieee80211_start_pmsr,
|
||||
.abort_pmsr = ieee80211_abort_pmsr,
|
||||
};
|
||||
|
@ -641,6 +641,8 @@ IEEE80211_IF_FILE(dot11MeshHWMPconfirmationInterval,
|
||||
IEEE80211_IF_FILE(power_mode, u.mesh.mshcfg.power_mode, DEC);
|
||||
IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration,
|
||||
u.mesh.mshcfg.dot11MeshAwakeWindowDuration, DEC);
|
||||
IEEE80211_IF_FILE(dot11MeshConnectedToMeshGate,
|
||||
u.mesh.mshcfg.dot11MeshConnectedToMeshGate, DEC);
|
||||
#endif
|
||||
|
||||
#define DEBUGFS_ADD_MODE(name, mode) \
|
||||
@ -762,6 +764,7 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
|
||||
MESHPARAMS_ADD(dot11MeshHWMPconfirmationInterval);
|
||||
MESHPARAMS_ADD(power_mode);
|
||||
MESHPARAMS_ADD(dot11MeshAwakeWindowDuration);
|
||||
MESHPARAMS_ADD(dot11MeshConnectedToMeshGate);
|
||||
#undef MESHPARAMS_ADD
|
||||
}
|
||||
#endif
|
||||
|
@ -795,22 +795,22 @@ static ssize_t sta_he_capa_read(struct file *file, char __user *userbuf,
|
||||
|
||||
#define PRINT_NSS_SUPP(f, n) \
|
||||
do { \
|
||||
int i; \
|
||||
int _i; \
|
||||
u16 v = le16_to_cpu(nss->f); \
|
||||
p += scnprintf(p, buf_sz + buf - p, n ": %#.4x\n", v); \
|
||||
for (i = 0; i < 8; i += 2) { \
|
||||
switch ((v >> i) & 0x3) { \
|
||||
for (_i = 0; _i < 8; _i += 2) { \
|
||||
switch ((v >> _i) & 0x3) { \
|
||||
case 0: \
|
||||
PRINT(n "-%d-SUPPORT-0-7", i / 2); \
|
||||
PRINT(n "-%d-SUPPORT-0-7", _i / 2); \
|
||||
break; \
|
||||
case 1: \
|
||||
PRINT(n "-%d-SUPPORT-0-9", i / 2); \
|
||||
PRINT(n "-%d-SUPPORT-0-9", _i / 2); \
|
||||
break; \
|
||||
case 2: \
|
||||
PRINT(n "-%d-SUPPORT-0-11", i / 2); \
|
||||
PRINT(n "-%d-SUPPORT-0-11", _i / 2); \
|
||||
break; \
|
||||
case 3: \
|
||||
PRINT(n "-%d-NOT-SUPPORTED", i / 2); \
|
||||
PRINT(n "-%d-NOT-SUPPORTED", _i / 2); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
|
@ -1199,6 +1199,40 @@ drv_get_ftm_responder_stats(struct ieee80211_local *local,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int drv_start_pmsr(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_pmsr_request *request)
|
||||
{
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
might_sleep();
|
||||
if (!check_sdata_in_driver(sdata))
|
||||
return -EIO;
|
||||
|
||||
trace_drv_start_pmsr(local, sdata);
|
||||
|
||||
if (local->ops->start_pmsr)
|
||||
ret = local->ops->start_pmsr(&local->hw, &sdata->vif, request);
|
||||
trace_drv_return_int(local, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void drv_abort_pmsr(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_pmsr_request *request)
|
||||
{
|
||||
trace_drv_abort_pmsr(local, sdata);
|
||||
|
||||
might_sleep();
|
||||
if (!check_sdata_in_driver(sdata))
|
||||
return;
|
||||
|
||||
if (local->ops->abort_pmsr)
|
||||
local->ops->abort_pmsr(&local->hw, &sdata->vif, request);
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
static inline int drv_start_nan(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_nan_conf *conf)
|
||||
|
@ -500,6 +500,7 @@ struct ieee80211_if_managed {
|
||||
unsigned int uapsd_max_sp_len;
|
||||
|
||||
int wmm_last_param_set;
|
||||
int mu_edca_last_param_set;
|
||||
|
||||
u8 use_4addr;
|
||||
|
||||
|
@ -1801,7 +1801,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
|
||||
}
|
||||
|
||||
ieee80211_assign_perm_addr(local, ndev->perm_addr, type);
|
||||
if (params && is_valid_ether_addr(params->macaddr))
|
||||
if (is_valid_ether_addr(params->macaddr))
|
||||
memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN);
|
||||
else
|
||||
memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
|
||||
@ -1870,11 +1870,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
|
||||
ieee80211_setup_sdata(sdata, type);
|
||||
|
||||
if (ndev) {
|
||||
if (params) {
|
||||
ndev->ieee80211_ptr->use_4addr = params->use_4addr;
|
||||
if (type == NL80211_IFTYPE_STATION)
|
||||
sdata->u.mgd.use_4addr = params->use_4addr;
|
||||
}
|
||||
ndev->ieee80211_ptr->use_4addr = params->use_4addr;
|
||||
if (type == NL80211_IFTYPE_STATION)
|
||||
sdata->u.mgd.use_4addr = params->use_4addr;
|
||||
|
||||
ndev->features |= local->hw.netdev_features;
|
||||
|
||||
|
@ -1221,8 +1221,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||
/* add one default STA interface if supported */
|
||||
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
|
||||
!ieee80211_hw_check(hw, NO_AUTO_VIF)) {
|
||||
struct vif_params params = {0};
|
||||
|
||||
result = ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL,
|
||||
NL80211_IFTYPE_STATION, NULL);
|
||||
NL80211_IFTYPE_STATION, ¶ms);
|
||||
if (result)
|
||||
wiphy_warn(local->hw.wiphy,
|
||||
"Failed to add default virtual iface\n");
|
||||
|
@ -254,6 +254,9 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
u8 *pos, neighbors;
|
||||
u8 meshconf_len = sizeof(struct ieee80211_meshconf_ie);
|
||||
bool is_connected_to_gate = ifmsh->num_gates > 0 ||
|
||||
ifmsh->mshcfg.dot11MeshGateAnnouncementProtocol ||
|
||||
ifmsh->mshcfg.dot11MeshConnectedToMeshGate;
|
||||
|
||||
if (skb_tailroom(skb) < 2 + meshconf_len)
|
||||
return -ENOMEM;
|
||||
@ -278,7 +281,7 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
|
||||
/* Mesh Formation Info - number of neighbors */
|
||||
neighbors = atomic_read(&ifmsh->estab_plinks);
|
||||
neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS);
|
||||
*pos++ = neighbors << 1;
|
||||
*pos++ = (neighbors << 1) | is_connected_to_gate;
|
||||
/* Mesh capability */
|
||||
*pos = 0x00;
|
||||
*pos |= ifmsh->mshcfg.dot11MeshForwarding ?
|
||||
@ -1191,7 +1194,8 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
|
||||
if (!sdata->u.mesh.user_mpm ||
|
||||
sdata->u.mesh.mshcfg.rssi_threshold == 0 ||
|
||||
sdata->u.mesh.mshcfg.rssi_threshold < rx_status->signal)
|
||||
mesh_neighbour_update(sdata, mgmt->sa, &elems);
|
||||
mesh_neighbour_update(sdata, mgmt->sa, &elems,
|
||||
rx_status);
|
||||
}
|
||||
|
||||
if (ifmsh->sync_ops)
|
||||
|
@ -273,7 +273,8 @@ int mesh_gate_num(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
/* Mesh plinks */
|
||||
void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
|
||||
u8 *hw_addr, struct ieee802_11_elems *ie);
|
||||
u8 *hw_addr, struct ieee802_11_elems *ie,
|
||||
struct ieee80211_rx_status *rx_status);
|
||||
bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie);
|
||||
u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata);
|
||||
void mesh_plink_timer(struct timer_list *t);
|
||||
|
@ -513,7 +513,8 @@ __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
|
||||
|
||||
static struct sta_info *
|
||||
mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,
|
||||
struct ieee802_11_elems *elems)
|
||||
struct ieee802_11_elems *elems,
|
||||
struct ieee80211_rx_status *rx_status)
|
||||
{
|
||||
struct sta_info *sta = NULL;
|
||||
|
||||
@ -521,11 +522,17 @@ mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,
|
||||
if (sdata->u.mesh.user_mpm ||
|
||||
sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) {
|
||||
if (mesh_peer_accepts_plinks(elems) &&
|
||||
mesh_plink_availables(sdata))
|
||||
mesh_plink_availables(sdata)) {
|
||||
int sig = 0;
|
||||
|
||||
if (ieee80211_hw_check(&sdata->local->hw, SIGNAL_DBM))
|
||||
sig = rx_status->signal;
|
||||
|
||||
cfg80211_notify_new_peer_candidate(sdata->dev, addr,
|
||||
elems->ie_start,
|
||||
elems->total_len,
|
||||
GFP_KERNEL);
|
||||
sig, GFP_KERNEL);
|
||||
}
|
||||
} else
|
||||
sta = __mesh_sta_info_alloc(sdata, addr);
|
||||
|
||||
@ -538,13 +545,15 @@ mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,
|
||||
* @sdata: local meshif
|
||||
* @addr: peer's address
|
||||
* @elems: IEs from beacon or mesh peering frame.
|
||||
* @rx_status: rx status for the frame for signal reporting
|
||||
*
|
||||
* Return existing or newly allocated sta_info under RCU read lock.
|
||||
* (re)initialize with given IEs.
|
||||
*/
|
||||
static struct sta_info *
|
||||
mesh_sta_info_get(struct ieee80211_sub_if_data *sdata,
|
||||
u8 *addr, struct ieee802_11_elems *elems) __acquires(RCU)
|
||||
u8 *addr, struct ieee802_11_elems *elems,
|
||||
struct ieee80211_rx_status *rx_status) __acquires(RCU)
|
||||
{
|
||||
struct sta_info *sta = NULL;
|
||||
|
||||
@ -555,7 +564,7 @@ mesh_sta_info_get(struct ieee80211_sub_if_data *sdata,
|
||||
} else {
|
||||
rcu_read_unlock();
|
||||
/* can't run atomic */
|
||||
sta = mesh_sta_info_alloc(sdata, addr, elems);
|
||||
sta = mesh_sta_info_alloc(sdata, addr, elems, rx_status);
|
||||
if (!sta) {
|
||||
rcu_read_lock();
|
||||
return NULL;
|
||||
@ -576,20 +585,25 @@ mesh_sta_info_get(struct ieee80211_sub_if_data *sdata,
|
||||
* @sdata: local meshif
|
||||
* @addr: peer's address
|
||||
* @elems: IEs from beacon or mesh peering frame
|
||||
* @rx_status: rx status for the frame for signal reporting
|
||||
*
|
||||
* Initiates peering if appropriate.
|
||||
*/
|
||||
void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
|
||||
u8 *hw_addr,
|
||||
struct ieee802_11_elems *elems)
|
||||
struct ieee802_11_elems *elems,
|
||||
struct ieee80211_rx_status *rx_status)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
u32 changed = 0;
|
||||
|
||||
sta = mesh_sta_info_get(sdata, hw_addr, elems);
|
||||
sta = mesh_sta_info_get(sdata, hw_addr, elems, rx_status);
|
||||
if (!sta)
|
||||
goto out;
|
||||
|
||||
sta->mesh->connected_to_gate = elems->mesh_config->meshconf_form &
|
||||
IEEE80211_MESHCONF_FORM_CONNECTED_TO_GATE;
|
||||
|
||||
if (mesh_peer_accepts_plinks(elems) &&
|
||||
sta->mesh->plink_state == NL80211_PLINK_LISTEN &&
|
||||
sdata->u.mesh.accepting_plinks &&
|
||||
@ -1069,7 +1083,8 @@ out:
|
||||
static void
|
||||
mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
struct ieee802_11_elems *elems)
|
||||
struct ieee802_11_elems *elems,
|
||||
struct ieee80211_rx_status *rx_status)
|
||||
{
|
||||
|
||||
struct sta_info *sta;
|
||||
@ -1134,7 +1149,7 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
|
||||
if (event == OPN_ACPT) {
|
||||
rcu_read_unlock();
|
||||
/* allocate sta entry if necessary and update info */
|
||||
sta = mesh_sta_info_get(sdata, mgmt->sa, elems);
|
||||
sta = mesh_sta_info_get(sdata, mgmt->sa, elems, rx_status);
|
||||
if (!sta) {
|
||||
mpl_dbg(sdata, "Mesh plink: failed to init peer!\n");
|
||||
goto unlock_rcu;
|
||||
@ -1200,5 +1215,5 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
|
||||
return;
|
||||
}
|
||||
ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems);
|
||||
mesh_process_plink_frame(sdata, mgmt, &elems);
|
||||
mesh_process_plink_frame(sdata, mgmt, &elems, rx_status);
|
||||
}
|
||||
|
@ -916,6 +916,15 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|
||||
ieee80211_add_vht_ie(sdata, skb, sband,
|
||||
&assoc_data->ap_vht_cap);
|
||||
|
||||
/*
|
||||
* If AP doesn't support HT, mark HE as disabled.
|
||||
* If on the 5GHz band, make sure it supports VHT.
|
||||
*/
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_HT ||
|
||||
(sband->band == NL80211_BAND_5GHZ &&
|
||||
ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
|
||||
ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
|
||||
|
||||
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
|
||||
ieee80211_add_he_ie(sdata, skb, sband);
|
||||
|
||||
@ -1869,7 +1878,7 @@ ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
||||
struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS];
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
size_t left;
|
||||
int count, ac;
|
||||
int count, mu_edca_count, ac;
|
||||
const u8 *pos;
|
||||
u8 uapsd_queues = 0;
|
||||
|
||||
@ -1889,9 +1898,16 @@ ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
||||
uapsd_queues = ifmgd->uapsd_queues;
|
||||
|
||||
count = wmm_param[6] & 0x0f;
|
||||
if (count == ifmgd->wmm_last_param_set)
|
||||
/* -1 is the initial value of ifmgd->mu_edca_last_param_set.
|
||||
* if mu_edca was preset before and now it disappeared tell
|
||||
* the driver about it.
|
||||
*/
|
||||
mu_edca_count = mu_edca ? mu_edca->mu_qos_info & 0x0f : -1;
|
||||
if (count == ifmgd->wmm_last_param_set &&
|
||||
mu_edca_count == ifmgd->mu_edca_last_param_set)
|
||||
return false;
|
||||
ifmgd->wmm_last_param_set = count;
|
||||
ifmgd->mu_edca_last_param_set = mu_edca_count;
|
||||
|
||||
pos = wmm_param + 8;
|
||||
left = wmm_param_len - 8;
|
||||
@ -3062,6 +3078,19 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
|
||||
}
|
||||
}
|
||||
|
||||
static bool ieee80211_twt_req_supported(const struct sta_info *sta,
|
||||
const struct ieee802_11_elems *elems)
|
||||
{
|
||||
if (elems->ext_capab_len < 10)
|
||||
return false;
|
||||
|
||||
if (!(elems->ext_capab[9] & WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT))
|
||||
return false;
|
||||
|
||||
return sta->sta.he_cap.he_cap_elem.mac_cap_info[0] &
|
||||
IEEE80211_HE_MAC_CAP0_TWT_RES;
|
||||
}
|
||||
|
||||
static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_bss *cbss,
|
||||
struct ieee80211_mgmt *mgmt, size_t len)
|
||||
@ -3215,16 +3244,6 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If AP doesn't support HT, or it doesn't have HE mandatory IEs, mark
|
||||
* HE as disabled. If on the 5GHz band, make sure it supports VHT.
|
||||
*/
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_HT ||
|
||||
(sband->band == NL80211_BAND_5GHZ &&
|
||||
ifmgd->flags & IEEE80211_STA_DISABLE_VHT) ||
|
||||
(!elems.he_cap && !elems.he_operation))
|
||||
ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
|
||||
|
||||
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) &&
|
||||
(!elems.he_cap || !elems.he_operation)) {
|
||||
mutex_unlock(&sdata->local->sta_mtx);
|
||||
@ -3251,8 +3270,11 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
||||
sta);
|
||||
|
||||
bss_conf->he_support = sta->sta.he_cap.has_he;
|
||||
bss_conf->twt_requester =
|
||||
ieee80211_twt_req_supported(sta, &elems);
|
||||
} else {
|
||||
bss_conf->he_support = false;
|
||||
bss_conf->twt_requester = false;
|
||||
}
|
||||
|
||||
if (bss_conf->he_support) {
|
||||
@ -3337,6 +3359,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
||||
* 4-bit value.
|
||||
*/
|
||||
ifmgd->wmm_last_param_set = -1;
|
||||
ifmgd->mu_edca_last_param_set = -1;
|
||||
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
|
||||
ieee80211_set_wmm_default(sdata, false, false);
|
||||
@ -4660,8 +4683,10 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
}
|
||||
|
||||
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) &&
|
||||
ieee80211_get_he_sta_cap(sband)) {
|
||||
if (!ieee80211_get_he_sta_cap(sband))
|
||||
ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
|
||||
|
||||
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE)) {
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
const u8 *he_oper_ie;
|
||||
|
||||
|
@ -143,6 +143,9 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
|
||||
/* allocate extra bitmaps */
|
||||
if (status->chains)
|
||||
len += 4 * hweight8(status->chains);
|
||||
/* vendor presence bitmap */
|
||||
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)
|
||||
len += 4;
|
||||
|
||||
if (ieee80211_have_rx_timestamp(status)) {
|
||||
len = ALIGN(len, 8);
|
||||
@ -207,8 +210,6 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
|
||||
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
|
||||
struct ieee80211_vendor_radiotap *rtap = (void *)skb->data;
|
||||
|
||||
/* vendor presence bitmap */
|
||||
len += 4;
|
||||
/* alignment for fixed 6-byte vendor data header */
|
||||
len = ALIGN(len, 2);
|
||||
/* vendor data header */
|
||||
@ -753,6 +754,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
|
||||
struct ieee80211_sub_if_data *monitor_sdata =
|
||||
rcu_dereference(local->monitor_sdata);
|
||||
bool only_monitor = false;
|
||||
unsigned int min_head_len;
|
||||
|
||||
if (status->flag & RX_FLAG_RADIOTAP_HE)
|
||||
rtap_space += sizeof(struct ieee80211_radiotap_he);
|
||||
@ -760,12 +762,18 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
|
||||
if (status->flag & RX_FLAG_RADIOTAP_HE_MU)
|
||||
rtap_space += sizeof(struct ieee80211_radiotap_he_mu);
|
||||
|
||||
if (status->flag & RX_FLAG_RADIOTAP_LSIG)
|
||||
rtap_space += sizeof(struct ieee80211_radiotap_lsig);
|
||||
|
||||
if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) {
|
||||
struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data;
|
||||
struct ieee80211_vendor_radiotap *rtap =
|
||||
(void *)(origskb->data + rtap_space);
|
||||
|
||||
rtap_space += sizeof(*rtap) + rtap->len + rtap->pad;
|
||||
}
|
||||
|
||||
min_head_len = rtap_space;
|
||||
|
||||
/*
|
||||
* First, we may need to make a copy of the skb because
|
||||
* (1) we need to modify it for radiotap (if not present), and
|
||||
@ -775,18 +783,23 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
|
||||
* the SKB because it has a bad FCS/PLCP checksum.
|
||||
*/
|
||||
|
||||
if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) {
|
||||
if (unlikely(origskb->len <= FCS_LEN)) {
|
||||
/* driver bug */
|
||||
WARN_ON(1);
|
||||
dev_kfree_skb(origskb);
|
||||
return NULL;
|
||||
if (!(status->flag & RX_FLAG_NO_PSDU)) {
|
||||
if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) {
|
||||
if (unlikely(origskb->len <= FCS_LEN + rtap_space)) {
|
||||
/* driver bug */
|
||||
WARN_ON(1);
|
||||
dev_kfree_skb(origskb);
|
||||
return NULL;
|
||||
}
|
||||
present_fcs_len = FCS_LEN;
|
||||
}
|
||||
present_fcs_len = FCS_LEN;
|
||||
|
||||
/* also consider the hdr->frame_control */
|
||||
min_head_len += 2;
|
||||
}
|
||||
|
||||
/* ensure hdr->frame_control and vendor radiotap data are in skb head */
|
||||
if (!pskb_may_pull(origskb, 2 + rtap_space)) {
|
||||
/* ensure that the expected data elements are in skb head */
|
||||
if (!pskb_may_pull(origskb, min_head_len)) {
|
||||
dev_kfree_skb(origskb);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -356,7 +356,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
|
||||
static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
bool hw_scan = local->ops->hw_scan;
|
||||
bool hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
|
||||
bool was_scanning = local->scanning;
|
||||
struct cfg80211_scan_request *scan_req;
|
||||
struct ieee80211_sub_if_data *scan_sdata;
|
||||
@ -606,6 +606,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_scan_request *req)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
bool hw_scan = local->ops->hw_scan;
|
||||
int rc;
|
||||
|
||||
lockdep_assert_held(&local->mtx);
|
||||
@ -620,7 +621,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (local->ops->hw_scan) {
|
||||
again:
|
||||
if (hw_scan) {
|
||||
u8 *ies;
|
||||
|
||||
local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len;
|
||||
@ -679,7 +681,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
||||
else
|
||||
memcpy(local->scan_addr, sdata->vif.addr, ETH_ALEN);
|
||||
|
||||
if (local->ops->hw_scan) {
|
||||
if (hw_scan) {
|
||||
__set_bit(SCAN_HW_SCANNING, &local->scanning);
|
||||
} else if ((req->n_channels == 1) &&
|
||||
(req->channels[0] == local->_oper_chandef.chan)) {
|
||||
@ -722,7 +724,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
ieee80211_recalc_idle(local);
|
||||
|
||||
if (local->ops->hw_scan) {
|
||||
if (hw_scan) {
|
||||
WARN_ON(!ieee80211_prep_hw_scan(local));
|
||||
rc = drv_hw_scan(local, sdata, local->hw_scan_req);
|
||||
} else {
|
||||
@ -740,6 +742,18 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
||||
RCU_INIT_POINTER(local->scan_sdata, NULL);
|
||||
}
|
||||
|
||||
if (hw_scan && rc == 1) {
|
||||
/*
|
||||
* we can't fall back to software for P2P-GO
|
||||
* as it must update NoA etc.
|
||||
*/
|
||||
if (ieee80211_vif_type_p2p(&sdata->vif) ==
|
||||
NL80211_IFTYPE_P2P_GO)
|
||||
return -EOPNOTSUPP;
|
||||
hw_scan = false;
|
||||
goto again;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -2253,11 +2253,8 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
|
||||
}
|
||||
|
||||
if (tidstats && !cfg80211_sinfo_alloc_tid_stats(sinfo, GFP_KERNEL)) {
|
||||
for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
|
||||
struct cfg80211_tid_stats *tidstats = &sinfo->pertid[i];
|
||||
|
||||
sta_set_tidstats(sta, tidstats, i);
|
||||
}
|
||||
for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++)
|
||||
sta_set_tidstats(sta, &sinfo->pertid[i], i);
|
||||
}
|
||||
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
@ -2267,7 +2264,8 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
|
||||
BIT_ULL(NL80211_STA_INFO_PLINK_STATE) |
|
||||
BIT_ULL(NL80211_STA_INFO_LOCAL_PM) |
|
||||
BIT_ULL(NL80211_STA_INFO_PEER_PM) |
|
||||
BIT_ULL(NL80211_STA_INFO_NONPEER_PM);
|
||||
BIT_ULL(NL80211_STA_INFO_NONPEER_PM) |
|
||||
BIT_ULL(NL80211_STA_INFO_CONNECTED_TO_GATE);
|
||||
|
||||
sinfo->llid = sta->mesh->llid;
|
||||
sinfo->plid = sta->mesh->plid;
|
||||
@ -2279,6 +2277,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
|
||||
sinfo->local_pm = sta->mesh->local_pm;
|
||||
sinfo->peer_pm = sta->mesh->peer_pm;
|
||||
sinfo->nonpeer_pm = sta->mesh->nonpeer_pm;
|
||||
sinfo->connected_to_gate = sta->mesh->connected_to_gate;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -364,6 +364,7 @@ DECLARE_EWMA(mesh_fail_avg, 20, 8)
|
||||
* @nonpeer_pm: STA power save mode towards non-peer neighbors
|
||||
* @processed_beacon: set to true after peer rates and capabilities are
|
||||
* processed
|
||||
* @connected_to_gate: true if mesh STA has a path to a mesh gate
|
||||
* @fail_avg: moving percentage of failed MSDUs
|
||||
*/
|
||||
struct mesh_sta {
|
||||
@ -381,6 +382,7 @@ struct mesh_sta {
|
||||
u8 plink_retries;
|
||||
|
||||
bool processed_beacon;
|
||||
bool connected_to_gate;
|
||||
|
||||
enum nl80211_plink_state plink_state;
|
||||
u32 plink_timeout;
|
||||
|
@ -1052,10 +1052,10 @@ TRACE_EVENT(drv_ampdu_action,
|
||||
);
|
||||
|
||||
TRACE_EVENT(drv_get_survey,
|
||||
TP_PROTO(struct ieee80211_local *local, int idx,
|
||||
TP_PROTO(struct ieee80211_local *local, int _idx,
|
||||
struct survey_info *survey),
|
||||
|
||||
TP_ARGS(local, idx, survey),
|
||||
TP_ARGS(local, _idx, survey),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
@ -1064,7 +1064,7 @@ TRACE_EVENT(drv_get_survey,
|
||||
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
__entry->idx = idx;
|
||||
__entry->idx = _idx;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
@ -1882,6 +1882,18 @@ TRACE_EVENT(drv_del_nan_func,
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(local_sdata_evt, drv_start_pmsr,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata),
|
||||
TP_ARGS(local, sdata)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(local_sdata_evt, drv_abort_pmsr,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata),
|
||||
TP_ARGS(local, sdata)
|
||||
);
|
||||
|
||||
/*
|
||||
* Tracing for API calls that drivers call.
|
||||
*/
|
||||
|
@ -3218,6 +3218,9 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
|
||||
if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
|
||||
return false;
|
||||
|
||||
if (skb_is_gso(skb))
|
||||
return false;
|
||||
|
||||
if (!txq)
|
||||
return false;
|
||||
|
||||
@ -3242,7 +3245,7 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
|
||||
tin = &txqi->tin;
|
||||
flow = fq_flow_classify(fq, tin, skb, fq_flow_get_default_func);
|
||||
head = skb_peek_tail(&flow->queue);
|
||||
if (!head)
|
||||
if (!head || skb_is_gso(head))
|
||||
goto out;
|
||||
|
||||
orig_len = head->len;
|
||||
@ -3583,7 +3586,7 @@ begin:
|
||||
skb_queue_splice_tail(&tx.skbs, &txqi->frags);
|
||||
}
|
||||
|
||||
if (skb && skb_has_frag_list(skb) &&
|
||||
if (skb_has_frag_list(skb) &&
|
||||
!ieee80211_hw_check(&local->hw, TX_FRAG_LIST)) {
|
||||
if (skb_linearize(skb)) {
|
||||
ieee80211_free_txskb(&local->hw, skb);
|
||||
@ -4579,7 +4582,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
|
||||
IEEE80211_STYPE_NULLFUNC |
|
||||
IEEE80211_FCTL_TODS);
|
||||
if (qos) {
|
||||
__le16 qos = cpu_to_le16(7);
|
||||
__le16 qoshdr = cpu_to_le16(7);
|
||||
|
||||
BUILD_BUG_ON((IEEE80211_STYPE_QOS_NULLFUNC |
|
||||
IEEE80211_STYPE_NULLFUNC) !=
|
||||
@ -4588,7 +4591,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
|
||||
cpu_to_le16(IEEE80211_STYPE_QOS_NULLFUNC);
|
||||
skb->priority = 7;
|
||||
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
|
||||
skb_put_data(skb, &qos, sizeof(qos));
|
||||
skb_put_data(skb, &qoshdr, sizeof(qoshdr));
|
||||
}
|
||||
|
||||
memcpy(nullfunc->addr1, ifmgd->bssid, ETH_ALEN);
|
||||
|
@ -299,16 +299,16 @@ out:
|
||||
spin_unlock_bh(&fq->lock);
|
||||
}
|
||||
|
||||
void ieee80211_wake_txqs(unsigned long data)
|
||||
static void
|
||||
__releases(&local->queue_stop_reason_lock)
|
||||
__acquires(&local->queue_stop_reason_lock)
|
||||
_ieee80211_wake_txqs(struct ieee80211_local *local, unsigned long *flags)
|
||||
{
|
||||
struct ieee80211_local *local = (struct ieee80211_local *)data;
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
int n_acs = IEEE80211_NUM_ACS;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
rcu_read_lock();
|
||||
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
||||
|
||||
if (local->hw.queues < IEEE80211_NUM_ACS)
|
||||
n_acs = 1;
|
||||
@ -317,7 +317,7 @@ void ieee80211_wake_txqs(unsigned long data)
|
||||
if (local->queue_stop_reasons[i])
|
||||
continue;
|
||||
|
||||
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||||
spin_unlock_irqrestore(&local->queue_stop_reason_lock, *flags);
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
int ac;
|
||||
|
||||
@ -329,13 +329,22 @@ void ieee80211_wake_txqs(unsigned long data)
|
||||
__ieee80211_wake_txqs(sdata, ac);
|
||||
}
|
||||
}
|
||||
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
||||
spin_lock_irqsave(&local->queue_stop_reason_lock, *flags);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
void ieee80211_wake_txqs(unsigned long data)
|
||||
{
|
||||
struct ieee80211_local *local = (struct ieee80211_local *)data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
||||
_ieee80211_wake_txqs(local, &flags);
|
||||
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||||
}
|
||||
|
||||
void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
@ -371,7 +380,8 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
|
||||
|
||||
static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
|
||||
enum queue_stop_reason reason,
|
||||
bool refcounted)
|
||||
bool refcounted,
|
||||
unsigned long *flags)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
|
||||
@ -405,8 +415,19 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
|
||||
} else
|
||||
tasklet_schedule(&local->tx_pending_tasklet);
|
||||
|
||||
if (local->ops->wake_tx_queue)
|
||||
tasklet_schedule(&local->wake_txqs_tasklet);
|
||||
/*
|
||||
* Calling _ieee80211_wake_txqs here can be a problem because it may
|
||||
* release queue_stop_reason_lock which has been taken by
|
||||
* __ieee80211_wake_queue's caller. It is certainly not very nice to
|
||||
* release someone's lock, but it is fine because all the callers of
|
||||
* __ieee80211_wake_queue call it right before releasing the lock.
|
||||
*/
|
||||
if (local->ops->wake_tx_queue) {
|
||||
if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER)
|
||||
tasklet_schedule(&local->wake_txqs_tasklet);
|
||||
else
|
||||
_ieee80211_wake_txqs(local, flags);
|
||||
}
|
||||
}
|
||||
|
||||
void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
|
||||
@ -417,7 +438,7 @@ void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
||||
__ieee80211_wake_queue(hw, queue, reason, refcounted);
|
||||
__ieee80211_wake_queue(hw, queue, reason, refcounted, &flags);
|
||||
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||||
}
|
||||
|
||||
@ -514,7 +535,7 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local,
|
||||
false);
|
||||
__skb_queue_tail(&local->pending[queue], skb);
|
||||
__ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
|
||||
false);
|
||||
false, &flags);
|
||||
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||||
}
|
||||
|
||||
@ -547,7 +568,7 @@ void ieee80211_add_pending_skbs(struct ieee80211_local *local,
|
||||
for (i = 0; i < hw->queues; i++)
|
||||
__ieee80211_wake_queue(hw, i,
|
||||
IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
|
||||
false);
|
||||
false, &flags);
|
||||
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||||
}
|
||||
|
||||
@ -605,7 +626,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
|
||||
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
||||
|
||||
for_each_set_bit(i, &queues, hw->queues)
|
||||
__ieee80211_wake_queue(hw, i, reason, refcounted);
|
||||
__ieee80211_wake_queue(hw, i, reason, refcounted, &flags);
|
||||
|
||||
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||||
}
|
||||
@ -1202,6 +1223,8 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
|
||||
if (pos[0] == WLAN_EID_EXT_HE_MU_EDCA &&
|
||||
elen >= (sizeof(*elems->mu_edca_param_set) + 1)) {
|
||||
elems->mu_edca_param_set = (void *)&pos[1];
|
||||
if (calc_crc)
|
||||
crc = crc32_be(crc, pos - 2, elen + 2);
|
||||
} else if (pos[0] == WLAN_EID_EXT_HE_CAPABILITY) {
|
||||
elems->he_cap = (void *)&pos[1];
|
||||
elems->he_cap_len = elen - 1;
|
||||
|
@ -16,7 +16,6 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -12,6 +12,7 @@ obj-$(CONFIG_WEXT_PRIV) += wext-priv.o
|
||||
|
||||
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
|
||||
cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o
|
||||
cfg80211-y += pmsr.o
|
||||
cfg80211-$(CONFIG_OF) += of.o
|
||||
cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
|
||||
cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
|
||||
|
@ -6,6 +6,7 @@
|
||||
*
|
||||
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright 2018 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
@ -747,6 +748,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
if (!ht_cap->ht_supported)
|
||||
return false;
|
||||
/* fall through */
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
prohibited_flags |= IEEE80211_CHAN_NO_20MHZ;
|
||||
width = 20;
|
||||
@ -769,6 +771,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
|
||||
cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
|
||||
if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
|
||||
return false;
|
||||
/* fall through */
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
if (!vht_cap->vht_supported)
|
||||
return false;
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
@ -190,11 +191,25 @@ int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
|
||||
return err;
|
||||
}
|
||||
|
||||
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
|
||||
if (!wdev->netdev)
|
||||
continue;
|
||||
nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE);
|
||||
}
|
||||
nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
|
||||
|
||||
wiphy_net_set(&rdev->wiphy, net);
|
||||
|
||||
err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev));
|
||||
WARN_ON(err);
|
||||
|
||||
nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
|
||||
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
|
||||
if (!wdev->netdev)
|
||||
continue;
|
||||
nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -664,6 +679,34 @@ int wiphy_register(struct wiphy *wiphy)
|
||||
return -EINVAL;
|
||||
#endif
|
||||
|
||||
if (WARN_ON(wiphy->pmsr_capa && !wiphy->pmsr_capa->ftm.supported))
|
||||
return -EINVAL;
|
||||
|
||||
if (wiphy->pmsr_capa && wiphy->pmsr_capa->ftm.supported) {
|
||||
if (WARN_ON(!wiphy->pmsr_capa->ftm.asap &&
|
||||
!wiphy->pmsr_capa->ftm.non_asap))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(!wiphy->pmsr_capa->ftm.preambles ||
|
||||
!wiphy->pmsr_capa->ftm.bandwidths))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(wiphy->pmsr_capa->ftm.preambles &
|
||||
~(BIT(NL80211_PREAMBLE_LEGACY) |
|
||||
BIT(NL80211_PREAMBLE_HT) |
|
||||
BIT(NL80211_PREAMBLE_VHT) |
|
||||
BIT(NL80211_PREAMBLE_DMG))))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(wiphy->pmsr_capa->ftm.bandwidths &
|
||||
~(BIT(NL80211_CHAN_WIDTH_20_NOHT) |
|
||||
BIT(NL80211_CHAN_WIDTH_20) |
|
||||
BIT(NL80211_CHAN_WIDTH_40) |
|
||||
BIT(NL80211_CHAN_WIDTH_80) |
|
||||
BIT(NL80211_CHAN_WIDTH_80P80) |
|
||||
BIT(NL80211_CHAN_WIDTH_160) |
|
||||
BIT(NL80211_CHAN_WIDTH_5) |
|
||||
BIT(NL80211_CHAN_WIDTH_10))))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* if a wiphy has unsupported modes for regulatory channel enforcement,
|
||||
* opt-out of enforcement checking
|
||||
@ -1087,6 +1130,8 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
|
||||
ASSERT_RTNL();
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
cfg80211_pmsr_wdev_down(wdev);
|
||||
|
||||
switch (wdev->iftype) {
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
__cfg80211_leave_ibss(rdev, dev, true);
|
||||
@ -1174,6 +1219,9 @@ void cfg80211_init_wdev(struct cfg80211_registered_device *rdev,
|
||||
spin_lock_init(&wdev->event_lock);
|
||||
INIT_LIST_HEAD(&wdev->mgmt_registrations);
|
||||
spin_lock_init(&wdev->mgmt_registrations_lock);
|
||||
INIT_LIST_HEAD(&wdev->pmsr_list);
|
||||
spin_lock_init(&wdev->pmsr_lock);
|
||||
INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
|
||||
|
||||
/*
|
||||
* We get here also when the interface changes network namespaces,
|
||||
|
@ -3,6 +3,7 @@
|
||||
* Wireless configuration interface internals.
|
||||
*
|
||||
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
*/
|
||||
#ifndef __NET_WIRELESS_CORE_H
|
||||
#define __NET_WIRELESS_CORE_H
|
||||
@ -530,4 +531,8 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
|
||||
|
||||
void cfg80211_cqm_config_free(struct wireless_dev *wdev);
|
||||
|
||||
void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid);
|
||||
void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev);
|
||||
void cfg80211_pmsr_free_wk(struct work_struct *work);
|
||||
|
||||
#endif /* __NET_WIRELESS_CORE_H */
|
||||
|
@ -240,7 +240,63 @@ nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = {
|
||||
.len = U8_MAX },
|
||||
};
|
||||
|
||||
static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
||||
static const struct nla_policy
|
||||
nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = {
|
||||
[NL80211_PMSR_FTM_REQ_ATTR_ASAP] = { .type = NLA_FLAG },
|
||||
[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] = { .type = NLA_U32 },
|
||||
[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP] =
|
||||
NLA_POLICY_MAX(NLA_U8, 15),
|
||||
[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD] = { .type = NLA_U16 },
|
||||
[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION] =
|
||||
NLA_POLICY_MAX(NLA_U8, 15),
|
||||
[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST] =
|
||||
NLA_POLICY_MAX(NLA_U8, 15),
|
||||
[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES] = { .type = NLA_U8 },
|
||||
[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI] = { .type = NLA_FLAG },
|
||||
[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC] = { .type = NLA_FLAG },
|
||||
};
|
||||
|
||||
static const struct nla_policy
|
||||
nl80211_pmsr_req_data_policy[NL80211_PMSR_TYPE_MAX + 1] = {
|
||||
[NL80211_PMSR_TYPE_FTM] =
|
||||
NLA_POLICY_NESTED(NL80211_PMSR_FTM_REQ_ATTR_MAX,
|
||||
nl80211_pmsr_ftm_req_attr_policy),
|
||||
};
|
||||
|
||||
static const struct nla_policy
|
||||
nl80211_pmsr_req_attr_policy[NL80211_PMSR_REQ_ATTR_MAX + 1] = {
|
||||
[NL80211_PMSR_REQ_ATTR_DATA] =
|
||||
NLA_POLICY_NESTED(NL80211_PMSR_TYPE_MAX,
|
||||
nl80211_pmsr_req_data_policy),
|
||||
[NL80211_PMSR_REQ_ATTR_GET_AP_TSF] = { .type = NLA_FLAG },
|
||||
};
|
||||
|
||||
static const struct nla_policy
|
||||
nl80211_psmr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = {
|
||||
[NL80211_PMSR_PEER_ATTR_ADDR] = NLA_POLICY_ETH_ADDR,
|
||||
/*
|
||||
* we could specify this again to be the top-level policy,
|
||||
* but that would open us up to recursion problems ...
|
||||
*/
|
||||
[NL80211_PMSR_PEER_ATTR_CHAN] = { .type = NLA_NESTED },
|
||||
[NL80211_PMSR_PEER_ATTR_REQ] =
|
||||
NLA_POLICY_NESTED(NL80211_PMSR_REQ_ATTR_MAX,
|
||||
nl80211_pmsr_req_attr_policy),
|
||||
[NL80211_PMSR_PEER_ATTR_RESP] = { .type = NLA_REJECT },
|
||||
};
|
||||
|
||||
static const struct nla_policy
|
||||
nl80211_pmsr_attr_policy[NL80211_PMSR_ATTR_MAX + 1] = {
|
||||
[NL80211_PMSR_ATTR_MAX_PEERS] = { .type = NLA_REJECT },
|
||||
[NL80211_PMSR_ATTR_REPORT_AP_TSF] = { .type = NLA_REJECT },
|
||||
[NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR] = { .type = NLA_REJECT },
|
||||
[NL80211_PMSR_ATTR_TYPE_CAPA] = { .type = NLA_REJECT },
|
||||
[NL80211_PMSR_ATTR_PEERS] =
|
||||
NLA_POLICY_NESTED_ARRAY(NL80211_PMSR_PEER_ATTR_MAX,
|
||||
nl80211_psmr_peer_attr_policy),
|
||||
};
|
||||
|
||||
const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
||||
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
|
||||
.len = 20-1 },
|
||||
@ -497,6 +553,10 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
||||
.type = NLA_NESTED,
|
||||
.validation_data = nl80211_ftm_responder_policy,
|
||||
},
|
||||
[NL80211_ATTR_TIMEOUT] = NLA_POLICY_MIN(NLA_U32, 1),
|
||||
[NL80211_ATTR_PEER_MEASUREMENTS] =
|
||||
NLA_POLICY_NESTED(NL80211_PMSR_FTM_REQ_ATTR_MAX,
|
||||
nl80211_pmsr_attr_policy),
|
||||
};
|
||||
|
||||
/* policy for the key attributes */
|
||||
@ -637,9 +697,9 @@ nl80211_packet_pattern_policy[MAX_NL80211_PKTPAT + 1] = {
|
||||
[NL80211_PKTPAT_OFFSET] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
static int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
|
||||
struct cfg80211_registered_device **rdev,
|
||||
struct wireless_dev **wdev)
|
||||
int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
|
||||
struct cfg80211_registered_device **rdev,
|
||||
struct wireless_dev **wdev)
|
||||
{
|
||||
int err;
|
||||
|
||||
@ -684,8 +744,8 @@ static int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
|
||||
}
|
||||
|
||||
/* message building helper */
|
||||
static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
|
||||
int flags, u8 cmd)
|
||||
void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
|
||||
int flags, u8 cmd)
|
||||
{
|
||||
/* since there is no private header just add the generic one */
|
||||
return genlmsg_put(skb, portid, seq, &nl80211_fam, flags, cmd);
|
||||
@ -1615,6 +1675,91 @@ static int nl80211_add_commands_unsplit(struct cfg80211_registered_device *rdev,
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
static int
|
||||
nl80211_send_pmsr_ftm_capa(const struct cfg80211_pmsr_capabilities *cap,
|
||||
struct sk_buff *msg)
|
||||
{
|
||||
struct nlattr *ftm;
|
||||
|
||||
if (!cap->ftm.supported)
|
||||
return 0;
|
||||
|
||||
ftm = nla_nest_start(msg, NL80211_PMSR_TYPE_FTM);
|
||||
if (!ftm)
|
||||
return -ENOBUFS;
|
||||
|
||||
if (cap->ftm.asap && nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_ASAP))
|
||||
return -ENOBUFS;
|
||||
if (cap->ftm.non_asap &&
|
||||
nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP))
|
||||
return -ENOBUFS;
|
||||
if (cap->ftm.request_lci &&
|
||||
nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI))
|
||||
return -ENOBUFS;
|
||||
if (cap->ftm.request_civicloc &&
|
||||
nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC))
|
||||
return -ENOBUFS;
|
||||
if (nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES,
|
||||
cap->ftm.preambles))
|
||||
return -ENOBUFS;
|
||||
if (nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS,
|
||||
cap->ftm.bandwidths))
|
||||
return -ENOBUFS;
|
||||
if (cap->ftm.max_bursts_exponent >= 0 &&
|
||||
nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT,
|
||||
cap->ftm.max_bursts_exponent))
|
||||
return -ENOBUFS;
|
||||
if (cap->ftm.max_ftms_per_burst &&
|
||||
nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST,
|
||||
cap->ftm.max_ftms_per_burst))
|
||||
return -ENOBUFS;
|
||||
|
||||
nla_nest_end(msg, ftm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_send_pmsr_capa(struct cfg80211_registered_device *rdev,
|
||||
struct sk_buff *msg)
|
||||
{
|
||||
const struct cfg80211_pmsr_capabilities *cap = rdev->wiphy.pmsr_capa;
|
||||
struct nlattr *pmsr, *caps;
|
||||
|
||||
if (!cap)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* we don't need to clean up anything here since the caller
|
||||
* will genlmsg_cancel() if we fail
|
||||
*/
|
||||
|
||||
pmsr = nla_nest_start(msg, NL80211_ATTR_PEER_MEASUREMENTS);
|
||||
if (!pmsr)
|
||||
return -ENOBUFS;
|
||||
|
||||
if (nla_put_u32(msg, NL80211_PMSR_ATTR_MAX_PEERS, cap->max_peers))
|
||||
return -ENOBUFS;
|
||||
|
||||
if (cap->report_ap_tsf &&
|
||||
nla_put_flag(msg, NL80211_PMSR_ATTR_REPORT_AP_TSF))
|
||||
return -ENOBUFS;
|
||||
|
||||
if (cap->randomize_mac_addr &&
|
||||
nla_put_flag(msg, NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR))
|
||||
return -ENOBUFS;
|
||||
|
||||
caps = nla_nest_start(msg, NL80211_PMSR_ATTR_TYPE_CAPA);
|
||||
if (!caps)
|
||||
return -ENOBUFS;
|
||||
|
||||
if (nl80211_send_pmsr_ftm_capa(cap, msg))
|
||||
return -ENOBUFS;
|
||||
|
||||
nla_nest_end(msg, caps);
|
||||
nla_nest_end(msg, pmsr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct nl80211_dump_wiphy_state {
|
||||
s64 filter_wiphy;
|
||||
long start;
|
||||
@ -1706,6 +1851,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
||||
state->split_start++;
|
||||
if (state->split)
|
||||
break;
|
||||
/* fall through */
|
||||
case 1:
|
||||
if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
|
||||
sizeof(u32) * rdev->wiphy.n_cipher_suites,
|
||||
@ -1752,6 +1898,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
||||
state->split_start++;
|
||||
if (state->split)
|
||||
break;
|
||||
/* fall through */
|
||||
case 2:
|
||||
if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
|
||||
rdev->wiphy.interface_modes))
|
||||
@ -1759,6 +1906,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
||||
state->split_start++;
|
||||
if (state->split)
|
||||
break;
|
||||
/* fall through */
|
||||
case 3:
|
||||
nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
|
||||
if (!nl_bands)
|
||||
@ -1784,6 +1932,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
||||
state->chan_start++;
|
||||
if (state->split)
|
||||
break;
|
||||
/* fall through */
|
||||
default:
|
||||
/* add frequencies */
|
||||
nl_freqs = nla_nest_start(
|
||||
@ -1837,6 +1986,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
||||
state->split_start++;
|
||||
if (state->split)
|
||||
break;
|
||||
/* fall through */
|
||||
case 4:
|
||||
nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
|
||||
if (!nl_cmds)
|
||||
@ -1863,6 +2013,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
||||
state->split_start++;
|
||||
if (state->split)
|
||||
break;
|
||||
/* fall through */
|
||||
case 5:
|
||||
if (rdev->ops->remain_on_channel &&
|
||||
(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
|
||||
@ -1880,6 +2031,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
||||
state->split_start++;
|
||||
if (state->split)
|
||||
break;
|
||||
/* fall through */
|
||||
case 6:
|
||||
#ifdef CONFIG_PM
|
||||
if (nl80211_send_wowlan(msg, rdev, state->split))
|
||||
@ -1890,6 +2042,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
||||
#else
|
||||
state->split_start++;
|
||||
#endif
|
||||
/* fall through */
|
||||
case 7:
|
||||
if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
|
||||
rdev->wiphy.software_iftypes))
|
||||
@ -1902,6 +2055,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
||||
state->split_start++;
|
||||
if (state->split)
|
||||
break;
|
||||
/* fall through */
|
||||
case 8:
|
||||
if ((rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
|
||||
nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
|
||||
@ -2118,6 +2272,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
||||
state->split_start++;
|
||||
break;
|
||||
case 14:
|
||||
if (nl80211_send_pmsr_capa(rdev, msg))
|
||||
goto nla_put_failure;
|
||||
|
||||
/* done */
|
||||
state->split_start = 0;
|
||||
break;
|
||||
@ -2318,9 +2478,9 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
|
||||
wdev->iftype == NL80211_IFTYPE_P2P_GO;
|
||||
}
|
||||
|
||||
static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
|
||||
struct genl_info *info,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
|
||||
struct genl_info *info,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct netlink_ext_ack *extack = info->extack;
|
||||
struct nlattr **attrs = info->attrs;
|
||||
@ -2794,12 +2954,6 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u64 wdev_id(struct wireless_dev *wdev)
|
||||
{
|
||||
return (u64)wdev->identifier |
|
||||
((u64)wiphy_to_rdev(wdev->wiphy)->wiphy_idx << 32);
|
||||
}
|
||||
|
||||
static int nl80211_send_chandef(struct sk_buff *msg,
|
||||
const struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
@ -2832,14 +2986,15 @@ static int nl80211_send_chandef(struct sk_buff *msg,
|
||||
|
||||
static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
|
||||
struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev, bool removal)
|
||||
struct wireless_dev *wdev,
|
||||
enum nl80211_commands cmd)
|
||||
{
|
||||
struct net_device *dev = wdev->netdev;
|
||||
u8 cmd = NL80211_CMD_NEW_INTERFACE;
|
||||
void *hdr;
|
||||
|
||||
if (removal)
|
||||
cmd = NL80211_CMD_DEL_INTERFACE;
|
||||
WARN_ON(cmd != NL80211_CMD_NEW_INTERFACE &&
|
||||
cmd != NL80211_CMD_DEL_INTERFACE &&
|
||||
cmd != NL80211_CMD_SET_INTERFACE);
|
||||
|
||||
hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
|
||||
if (!hdr)
|
||||
@ -2987,7 +3142,8 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
|
||||
}
|
||||
if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
||||
rdev, wdev, false) < 0) {
|
||||
rdev, wdev,
|
||||
NL80211_CMD_NEW_INTERFACE) < 0) {
|
||||
goto out;
|
||||
}
|
||||
if_idx++;
|
||||
@ -3017,7 +3173,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
return -ENOMEM;
|
||||
|
||||
if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
|
||||
rdev, wdev, false) < 0) {
|
||||
rdev, wdev, NL80211_CMD_NEW_INTERFACE) < 0) {
|
||||
nlmsg_free(msg);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
@ -3207,6 +3363,12 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!err && params.use_4addr != -1)
|
||||
dev->ieee80211_ptr->use_4addr = params.use_4addr;
|
||||
|
||||
if (change && !err) {
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
|
||||
nl80211_notify_iface(rdev, wdev, NL80211_CMD_SET_INTERFACE);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -3298,7 +3460,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
}
|
||||
|
||||
if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
|
||||
rdev, wdev, false) < 0) {
|
||||
rdev, wdev, NL80211_CMD_NEW_INTERFACE) < 0) {
|
||||
nlmsg_free(msg);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
@ -4521,8 +4683,7 @@ static int parse_station_flags(struct genl_info *info,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
|
||||
int attr)
|
||||
bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr)
|
||||
{
|
||||
struct nlattr *rate;
|
||||
u32 bitrate;
|
||||
@ -4731,6 +4892,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
|
||||
PUT_SINFO(LOCAL_PM, local_pm, u32);
|
||||
PUT_SINFO(PEER_PM, peer_pm, u32);
|
||||
PUT_SINFO(NONPEER_PM, nonpeer_pm, u32);
|
||||
PUT_SINFO(CONNECTED_TO_GATE, connected_to_gate, u8);
|
||||
|
||||
if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) {
|
||||
bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
|
||||
@ -6122,7 +6284,9 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
|
||||
nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW,
|
||||
cur_params.dot11MeshAwakeWindowDuration) ||
|
||||
nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
|
||||
cur_params.plink_timeout))
|
||||
cur_params.plink_timeout) ||
|
||||
nla_put_u8(msg, NL80211_MESHCONF_CONNECTED_TO_GATE,
|
||||
cur_params.dot11MeshConnectedToMeshGate))
|
||||
goto nla_put_failure;
|
||||
nla_nest_end(msg, pinfoattr);
|
||||
genlmsg_end(msg, hdr);
|
||||
@ -6179,6 +6343,7 @@ nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
|
||||
NL80211_MESH_POWER_MAX),
|
||||
[NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },
|
||||
[NL80211_MESHCONF_PLINK_TIMEOUT] = { .type = NLA_U32 },
|
||||
[NL80211_MESHCONF_CONNECTED_TO_GATE] = NLA_POLICY_RANGE(NLA_U8, 0, 1),
|
||||
};
|
||||
|
||||
static const struct nla_policy
|
||||
@ -6290,6 +6455,9 @@ do { \
|
||||
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, mask,
|
||||
NL80211_MESHCONF_RSSI_THRESHOLD,
|
||||
nla_get_s32);
|
||||
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConnectedToMeshGate, mask,
|
||||
NL80211_MESHCONF_CONNECTED_TO_GATE,
|
||||
nla_get_u8);
|
||||
/*
|
||||
* Check HT operation mode based on
|
||||
* IEEE 802.11-2016 9.4.2.57 HT Operation element.
|
||||
@ -6855,8 +7023,8 @@ static int parse_bss_select(struct nlattr *nla, struct wiphy *wiphy,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_parse_random_mac(struct nlattr **attrs,
|
||||
u8 *mac_addr, u8 *mac_addr_mask)
|
||||
int nl80211_parse_random_mac(struct nlattr **attrs,
|
||||
u8 *mac_addr, u8 *mac_addr_mask)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -7822,6 +7990,60 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nl80211_notify_radar_detection(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct wiphy *wiphy = wdev->wiphy;
|
||||
struct cfg80211_chan_def chandef;
|
||||
enum nl80211_dfs_regions dfs_region;
|
||||
int err;
|
||||
|
||||
dfs_region = reg_get_dfs_region(wiphy);
|
||||
if (dfs_region == NL80211_DFS_UNSET) {
|
||||
GENL_SET_ERR_MSG(info,
|
||||
"DFS Region is not set. Unexpected Radar indication");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = nl80211_parse_chandef(rdev, info, &chandef);
|
||||
if (err) {
|
||||
GENL_SET_ERR_MSG(info, "Unable to extract chandef info");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = cfg80211_chandef_dfs_required(wiphy, &chandef, wdev->iftype);
|
||||
if (err < 0) {
|
||||
GENL_SET_ERR_MSG(info, "chandef is invalid");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (err == 0) {
|
||||
GENL_SET_ERR_MSG(info,
|
||||
"Unexpected Radar indication for chandef/iftype");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Do not process this notification if radar is already detected
|
||||
* by kernel on this channel, and return success.
|
||||
*/
|
||||
if (chandef.chan->dfs_state == NL80211_DFS_UNAVAILABLE)
|
||||
return 0;
|
||||
|
||||
cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_UNAVAILABLE);
|
||||
|
||||
cfg80211_sched_dfs_chan_update(rdev);
|
||||
|
||||
memcpy(&rdev->radar_chandef, &chandef, sizeof(chandef));
|
||||
|
||||
/* Propagate this notification to other radios as well */
|
||||
queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
@ -13899,6 +14121,22 @@ static const struct genl_ops nl80211_ops[] = {
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_PEER_MEASUREMENT_START,
|
||||
.doit = nl80211_pmsr_start,
|
||||
.policy = nl80211_policy,
|
||||
.flags = GENL_UNS_ADMIN_PERM,
|
||||
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_NOTIFY_RADAR,
|
||||
.doit = nl80211_notify_radar_detection,
|
||||
.policy = nl80211_policy,
|
||||
.flags = GENL_UNS_ADMIN_PERM,
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_family nl80211_fam __ro_after_init = {
|
||||
@ -13946,15 +14184,11 @@ void nl80211_notify_iface(struct cfg80211_registered_device *rdev,
|
||||
{
|
||||
struct sk_buff *msg;
|
||||
|
||||
WARN_ON(cmd != NL80211_CMD_NEW_INTERFACE &&
|
||||
cmd != NL80211_CMD_DEL_INTERFACE);
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
if (nl80211_send_iface(msg, 0, 0, 0, rdev, wdev,
|
||||
cmd == NL80211_CMD_DEL_INTERFACE) < 0) {
|
||||
if (nl80211_send_iface(msg, 0, 0, 0, rdev, wdev, cmd) < 0) {
|
||||
nlmsg_free(msg);
|
||||
return;
|
||||
}
|
||||
@ -14573,7 +14807,8 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
|
||||
}
|
||||
|
||||
void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
|
||||
const u8* ie, u8 ie_len, gfp_t gfp)
|
||||
const u8 *ie, u8 ie_len,
|
||||
int sig_dbm, gfp_t gfp)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
@ -14599,7 +14834,9 @@ void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
|
||||
nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
||||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
|
||||
(ie_len && ie &&
|
||||
nla_put(msg, NL80211_ATTR_IE, ie_len , ie)))
|
||||
nla_put(msg, NL80211_ATTR_IE, ie_len, ie)) ||
|
||||
(sig_dbm &&
|
||||
nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)))
|
||||
goto nla_put_failure;
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
@ -15882,6 +16119,8 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
|
||||
} else if (wdev->conn_owner_nlportid == notify->portid) {
|
||||
schedule_work(&wdev->disconnect_wk);
|
||||
}
|
||||
|
||||
cfg80211_release_pmsr(wdev, notify->portid);
|
||||
}
|
||||
|
||||
spin_lock_bh(&rdev->beacon_registrations_lock);
|
||||
|
@ -1,4 +1,8 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Portions of this file
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
*/
|
||||
#ifndef __NET_WIRELESS_NL80211_H
|
||||
#define __NET_WIRELESS_NL80211_H
|
||||
|
||||
@ -6,6 +10,30 @@
|
||||
|
||||
int nl80211_init(void);
|
||||
void nl80211_exit(void);
|
||||
|
||||
extern const struct nla_policy nl80211_policy[NUM_NL80211_ATTR];
|
||||
|
||||
void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
|
||||
int flags, u8 cmd);
|
||||
bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
|
||||
int attr);
|
||||
|
||||
static inline u64 wdev_id(struct wireless_dev *wdev)
|
||||
{
|
||||
return (u64)wdev->identifier |
|
||||
((u64)wiphy_to_rdev(wdev->wiphy)->wiphy_idx << 32);
|
||||
}
|
||||
|
||||
int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
|
||||
struct cfg80211_registered_device **rdev,
|
||||
struct wireless_dev **wdev);
|
||||
|
||||
int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
|
||||
struct genl_info *info,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
int nl80211_parse_random_mac(struct nlattr **attrs,
|
||||
u8 *mac_addr, u8 *mac_addr_mask);
|
||||
|
||||
void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev,
|
||||
enum nl80211_commands cmd);
|
||||
void nl80211_notify_iface(struct cfg80211_registered_device *rdev,
|
||||
@ -95,4 +123,8 @@ void nl80211_send_ap_stopped(struct wireless_dev *wdev);
|
||||
|
||||
void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);
|
||||
|
||||
/* peer measurement */
|
||||
int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info);
|
||||
int nl80211_pmsr_dump_results(struct sk_buff *skb, struct netlink_callback *cb);
|
||||
|
||||
#endif /* __NET_WIRELESS_NL80211_H */
|
||||
|
590
net/wireless/pmsr.c
Normal file
590
net/wireless/pmsr.c
Normal file
@ -0,0 +1,590 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation
|
||||
*/
|
||||
#ifndef __PMSR_H
|
||||
#define __PMSR_H
|
||||
#include <net/cfg80211.h>
|
||||
#include "core.h"
|
||||
#include "nl80211.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
|
||||
struct nlattr *ftmreq,
|
||||
struct cfg80211_pmsr_request_peer *out,
|
||||
struct genl_info *info)
|
||||
{
|
||||
const struct cfg80211_pmsr_capabilities *capa = rdev->wiphy.pmsr_capa;
|
||||
struct nlattr *tb[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1];
|
||||
u32 preamble = NL80211_PREAMBLE_DMG; /* only optional in DMG */
|
||||
|
||||
/* validate existing data */
|
||||
if (!(rdev->wiphy.pmsr_capa->ftm.bandwidths & BIT(out->chandef.width))) {
|
||||
NL_SET_ERR_MSG(info->extack, "FTM: unsupported bandwidth");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* no validation needed - was already done via nested policy */
|
||||
nla_parse_nested(tb, NL80211_PMSR_FTM_REQ_ATTR_MAX, ftmreq, NULL, NULL);
|
||||
|
||||
if (tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE])
|
||||
preamble = nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]);
|
||||
|
||||
/* set up values - struct is 0-initialized */
|
||||
out->ftm.requested = true;
|
||||
|
||||
switch (out->chandef.chan->band) {
|
||||
case NL80211_BAND_60GHZ:
|
||||
/* optional */
|
||||
break;
|
||||
default:
|
||||
if (!tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]) {
|
||||
NL_SET_ERR_MSG(info->extack,
|
||||
"FTM: must specify preamble");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(capa->ftm.preambles & BIT(preamble))) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE],
|
||||
"FTM: invalid preamble");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
out->ftm.preamble = preamble;
|
||||
|
||||
out->ftm.burst_period = 0;
|
||||
if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD])
|
||||
out->ftm.burst_period =
|
||||
nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]);
|
||||
|
||||
out->ftm.asap = !!tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP];
|
||||
if (out->ftm.asap && !capa->ftm.asap) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP],
|
||||
"FTM: ASAP mode not supported");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!out->ftm.asap && !capa->ftm.non_asap) {
|
||||
NL_SET_ERR_MSG(info->extack,
|
||||
"FTM: non-ASAP mode not supported");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
out->ftm.num_bursts_exp = 0;
|
||||
if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP])
|
||||
out->ftm.num_bursts_exp =
|
||||
nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]);
|
||||
|
||||
if (capa->ftm.max_bursts_exponent >= 0 &&
|
||||
out->ftm.num_bursts_exp > capa->ftm.max_bursts_exponent) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP],
|
||||
"FTM: max NUM_BURSTS_EXP must be set lower than the device limit");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
out->ftm.burst_duration = 15;
|
||||
if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION])
|
||||
out->ftm.burst_duration =
|
||||
nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]);
|
||||
|
||||
out->ftm.ftms_per_burst = 0;
|
||||
if (tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST])
|
||||
out->ftm.ftms_per_burst =
|
||||
nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]);
|
||||
|
||||
if (capa->ftm.max_ftms_per_burst &&
|
||||
(out->ftm.ftms_per_burst > capa->ftm.max_ftms_per_burst ||
|
||||
out->ftm.ftms_per_burst == 0)) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST],
|
||||
"FTM: FTMs per burst must be set lower than the device limit but non-zero");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
out->ftm.ftmr_retries = 3;
|
||||
if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES])
|
||||
out->ftm.ftmr_retries =
|
||||
nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]);
|
||||
|
||||
out->ftm.request_lci = !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI];
|
||||
if (out->ftm.request_lci && !capa->ftm.request_lci) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI],
|
||||
"FTM: LCI request not supported");
|
||||
}
|
||||
|
||||
out->ftm.request_civicloc =
|
||||
!!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC];
|
||||
if (out->ftm.request_civicloc && !capa->ftm.request_civicloc) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC],
|
||||
"FTM: civic location request not supported");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
|
||||
struct nlattr *peer,
|
||||
struct cfg80211_pmsr_request_peer *out,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1];
|
||||
struct nlattr *req[NL80211_PMSR_REQ_ATTR_MAX + 1];
|
||||
struct nlattr *treq;
|
||||
int err, rem;
|
||||
|
||||
/* no validation needed - was already done via nested policy */
|
||||
nla_parse_nested(tb, NL80211_PMSR_PEER_ATTR_MAX, peer, NULL, NULL);
|
||||
|
||||
if (!tb[NL80211_PMSR_PEER_ATTR_ADDR] ||
|
||||
!tb[NL80211_PMSR_PEER_ATTR_CHAN] ||
|
||||
!tb[NL80211_PMSR_PEER_ATTR_REQ]) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack, peer,
|
||||
"insufficient peer data");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(out->addr, nla_data(tb[NL80211_PMSR_PEER_ATTR_ADDR]), ETH_ALEN);
|
||||
|
||||
/* reuse info->attrs */
|
||||
memset(info->attrs, 0, sizeof(*info->attrs) * (NL80211_ATTR_MAX + 1));
|
||||
/* need to validate here, we don't want to have validation recursion */
|
||||
err = nla_parse_nested(info->attrs, NL80211_ATTR_MAX,
|
||||
tb[NL80211_PMSR_PEER_ATTR_CHAN],
|
||||
nl80211_policy, info->extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = nl80211_parse_chandef(rdev, info, &out->chandef);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* no validation needed - was already done via nested policy */
|
||||
nla_parse_nested(req, NL80211_PMSR_REQ_ATTR_MAX,
|
||||
tb[NL80211_PMSR_PEER_ATTR_REQ],
|
||||
NULL, NULL);
|
||||
|
||||
if (!req[NL80211_PMSR_REQ_ATTR_DATA]) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
tb[NL80211_PMSR_PEER_ATTR_REQ],
|
||||
"missing request type/data");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF])
|
||||
out->report_ap_tsf = true;
|
||||
|
||||
if (out->report_ap_tsf && !rdev->wiphy.pmsr_capa->report_ap_tsf) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF],
|
||||
"reporting AP TSF is not supported");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
nla_for_each_nested(treq, req[NL80211_PMSR_REQ_ATTR_DATA], rem) {
|
||||
switch (nla_type(treq)) {
|
||||
case NL80211_PMSR_TYPE_FTM:
|
||||
err = pmsr_parse_ftm(rdev, treq, out, info);
|
||||
break;
|
||||
default:
|
||||
NL_SET_ERR_MSG_ATTR(info->extack, treq,
|
||||
"unsupported measurement type");
|
||||
err = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct nlattr *reqattr = info->attrs[NL80211_ATTR_PEER_MEASUREMENTS];
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct wireless_dev *wdev = info->user_ptr[1];
|
||||
struct cfg80211_pmsr_request *req;
|
||||
struct nlattr *peers, *peer;
|
||||
int count, rem, err, idx;
|
||||
|
||||
if (!rdev->wiphy.pmsr_capa)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!reqattr)
|
||||
return -EINVAL;
|
||||
|
||||
peers = nla_find(nla_data(reqattr), nla_len(reqattr),
|
||||
NL80211_PMSR_ATTR_PEERS);
|
||||
if (!peers)
|
||||
return -EINVAL;
|
||||
|
||||
count = 0;
|
||||
nla_for_each_nested(peer, peers, rem) {
|
||||
count++;
|
||||
|
||||
if (count > rdev->wiphy.pmsr_capa->max_peers) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack, peer,
|
||||
"Too many peers used");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
req = kzalloc(struct_size(req, peers, count), GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
if (info->attrs[NL80211_ATTR_TIMEOUT])
|
||||
req->timeout = nla_get_u32(info->attrs[NL80211_ATTR_TIMEOUT]);
|
||||
|
||||
if (info->attrs[NL80211_ATTR_MAC]) {
|
||||
if (!rdev->wiphy.pmsr_capa->randomize_mac_addr) {
|
||||
NL_SET_ERR_MSG_ATTR(info->extack,
|
||||
info->attrs[NL80211_ATTR_MAC],
|
||||
"device cannot randomize MAC address");
|
||||
err = -EINVAL;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
err = nl80211_parse_random_mac(info->attrs, req->mac_addr,
|
||||
req->mac_addr_mask);
|
||||
if (err)
|
||||
goto out_err;
|
||||
} else {
|
||||
memcpy(req->mac_addr, nla_data(info->attrs[NL80211_ATTR_MAC]),
|
||||
ETH_ALEN);
|
||||
memset(req->mac_addr_mask, 0xff, ETH_ALEN);
|
||||
}
|
||||
|
||||
idx = 0;
|
||||
nla_for_each_nested(peer, peers, rem) {
|
||||
/* NB: this reuses info->attrs, but we no longer need it */
|
||||
err = pmsr_parse_peer(rdev, peer, &req->peers[idx], info);
|
||||
if (err)
|
||||
goto out_err;
|
||||
idx++;
|
||||
}
|
||||
|
||||
req->n_peers = count;
|
||||
req->cookie = cfg80211_assign_cookie(rdev);
|
||||
|
||||
err = rdev_start_pmsr(rdev, wdev, req);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
list_add_tail(&req->list, &wdev->pmsr_list);
|
||||
|
||||
nl_set_extack_cookie_u64(info->extack, req->cookie);
|
||||
return 0;
|
||||
out_err:
|
||||
kfree(req);
|
||||
return err;
|
||||
}
|
||||
|
||||
void cfg80211_pmsr_complete(struct wireless_dev *wdev,
|
||||
struct cfg80211_pmsr_request *req,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct sk_buff *msg;
|
||||
void *hdr;
|
||||
|
||||
trace_cfg80211_pmsr_complete(wdev->wiphy, wdev, req->cookie);
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
||||
if (!msg)
|
||||
goto free_request;
|
||||
|
||||
hdr = nl80211hdr_put(msg, 0, 0, 0,
|
||||
NL80211_CMD_PEER_MEASUREMENT_COMPLETE);
|
||||
if (!hdr)
|
||||
goto free_msg;
|
||||
|
||||
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
||||
nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
|
||||
NL80211_ATTR_PAD))
|
||||
goto free_msg;
|
||||
|
||||
if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie,
|
||||
NL80211_ATTR_PAD))
|
||||
goto free_msg;
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid);
|
||||
goto free_request;
|
||||
free_msg:
|
||||
nlmsg_free(msg);
|
||||
free_request:
|
||||
spin_lock_bh(&wdev->pmsr_lock);
|
||||
list_del(&req->list);
|
||||
spin_unlock_bh(&wdev->pmsr_lock);
|
||||
kfree(req);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cfg80211_pmsr_complete);
|
||||
|
||||
static int nl80211_pmsr_send_ftm_res(struct sk_buff *msg,
|
||||
struct cfg80211_pmsr_result *res)
|
||||
{
|
||||
if (res->status == NL80211_PMSR_STATUS_FAILURE) {
|
||||
if (nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON,
|
||||
res->ftm.failure_reason))
|
||||
goto error;
|
||||
|
||||
if (res->ftm.failure_reason ==
|
||||
NL80211_PMSR_FTM_FAILURE_PEER_BUSY &&
|
||||
res->ftm.busy_retry_time &&
|
||||
nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME,
|
||||
res->ftm.busy_retry_time))
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PUT(tp, attr, val) \
|
||||
do { \
|
||||
if (nla_put_##tp(msg, \
|
||||
NL80211_PMSR_FTM_RESP_ATTR_##attr, \
|
||||
res->ftm.val)) \
|
||||
goto error; \
|
||||
} while (0)
|
||||
|
||||
#define PUTOPT(tp, attr, val) \
|
||||
do { \
|
||||
if (res->ftm.val##_valid) \
|
||||
PUT(tp, attr, val); \
|
||||
} while (0)
|
||||
|
||||
#define PUT_U64(attr, val) \
|
||||
do { \
|
||||
if (nla_put_u64_64bit(msg, \
|
||||
NL80211_PMSR_FTM_RESP_ATTR_##attr,\
|
||||
res->ftm.val, \
|
||||
NL80211_PMSR_FTM_RESP_ATTR_PAD)) \
|
||||
goto error; \
|
||||
} while (0)
|
||||
|
||||
#define PUTOPT_U64(attr, val) \
|
||||
do { \
|
||||
if (res->ftm.val##_valid) \
|
||||
PUT_U64(attr, val); \
|
||||
} while (0)
|
||||
|
||||
if (res->ftm.burst_index >= 0)
|
||||
PUT(u32, BURST_INDEX, burst_index);
|
||||
PUTOPT(u32, NUM_FTMR_ATTEMPTS, num_ftmr_attempts);
|
||||
PUTOPT(u32, NUM_FTMR_SUCCESSES, num_ftmr_successes);
|
||||
PUT(u8, NUM_BURSTS_EXP, num_bursts_exp);
|
||||
PUT(u8, BURST_DURATION, burst_duration);
|
||||
PUT(u8, FTMS_PER_BURST, ftms_per_burst);
|
||||
PUTOPT(s32, RSSI_AVG, rssi_avg);
|
||||
PUTOPT(s32, RSSI_SPREAD, rssi_spread);
|
||||
if (res->ftm.tx_rate_valid &&
|
||||
!nl80211_put_sta_rate(msg, &res->ftm.tx_rate,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_TX_RATE))
|
||||
goto error;
|
||||
if (res->ftm.rx_rate_valid &&
|
||||
!nl80211_put_sta_rate(msg, &res->ftm.rx_rate,
|
||||
NL80211_PMSR_FTM_RESP_ATTR_RX_RATE))
|
||||
goto error;
|
||||
PUTOPT_U64(RTT_AVG, rtt_avg);
|
||||
PUTOPT_U64(RTT_VARIANCE, rtt_variance);
|
||||
PUTOPT_U64(RTT_SPREAD, rtt_spread);
|
||||
PUTOPT_U64(DIST_AVG, dist_avg);
|
||||
PUTOPT_U64(DIST_VARIANCE, dist_variance);
|
||||
PUTOPT_U64(DIST_SPREAD, dist_spread);
|
||||
if (res->ftm.lci && res->ftm.lci_len &&
|
||||
nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_LCI,
|
||||
res->ftm.lci_len, res->ftm.lci))
|
||||
goto error;
|
||||
if (res->ftm.civicloc && res->ftm.civicloc_len &&
|
||||
nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC,
|
||||
res->ftm.civicloc_len, res->ftm.civicloc))
|
||||
goto error;
|
||||
#undef PUT
|
||||
#undef PUTOPT
|
||||
#undef PUT_U64
|
||||
#undef PUTOPT_U64
|
||||
|
||||
return 0;
|
||||
error:
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
static int nl80211_pmsr_send_result(struct sk_buff *msg,
|
||||
struct cfg80211_pmsr_result *res)
|
||||
{
|
||||
struct nlattr *pmsr, *peers, *peer, *resp, *data, *typedata;
|
||||
|
||||
pmsr = nla_nest_start(msg, NL80211_ATTR_PEER_MEASUREMENTS);
|
||||
if (!pmsr)
|
||||
goto error;
|
||||
|
||||
peers = nla_nest_start(msg, NL80211_PMSR_ATTR_PEERS);
|
||||
if (!peers)
|
||||
goto error;
|
||||
|
||||
peer = nla_nest_start(msg, 1);
|
||||
if (!peer)
|
||||
goto error;
|
||||
|
||||
if (nla_put(msg, NL80211_PMSR_PEER_ATTR_ADDR, ETH_ALEN, res->addr))
|
||||
goto error;
|
||||
|
||||
resp = nla_nest_start(msg, NL80211_PMSR_PEER_ATTR_RESP);
|
||||
if (!resp)
|
||||
goto error;
|
||||
|
||||
if (nla_put_u32(msg, NL80211_PMSR_RESP_ATTR_STATUS, res->status) ||
|
||||
nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_HOST_TIME,
|
||||
res->host_time, NL80211_PMSR_RESP_ATTR_PAD))
|
||||
goto error;
|
||||
|
||||
if (res->ap_tsf_valid &&
|
||||
nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_AP_TSF,
|
||||
res->host_time, NL80211_PMSR_RESP_ATTR_PAD))
|
||||
goto error;
|
||||
|
||||
if (res->final && nla_put_flag(msg, NL80211_PMSR_RESP_ATTR_FINAL))
|
||||
goto error;
|
||||
|
||||
data = nla_nest_start(msg, NL80211_PMSR_RESP_ATTR_DATA);
|
||||
if (!data)
|
||||
goto error;
|
||||
|
||||
typedata = nla_nest_start(msg, res->type);
|
||||
if (!typedata)
|
||||
goto error;
|
||||
|
||||
switch (res->type) {
|
||||
case NL80211_PMSR_TYPE_FTM:
|
||||
if (nl80211_pmsr_send_ftm_res(msg, res))
|
||||
goto error;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
nla_nest_end(msg, typedata);
|
||||
nla_nest_end(msg, data);
|
||||
nla_nest_end(msg, resp);
|
||||
nla_nest_end(msg, peer);
|
||||
nla_nest_end(msg, peers);
|
||||
nla_nest_end(msg, pmsr);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
void cfg80211_pmsr_report(struct wireless_dev *wdev,
|
||||
struct cfg80211_pmsr_request *req,
|
||||
struct cfg80211_pmsr_result *result,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct sk_buff *msg;
|
||||
void *hdr;
|
||||
int err;
|
||||
|
||||
trace_cfg80211_pmsr_report(wdev->wiphy, wdev, req->cookie,
|
||||
result->addr);
|
||||
|
||||
/*
|
||||
* Currently, only variable items are LCI and civic location,
|
||||
* both of which are reasonably short so we don't need to
|
||||
* worry about them here for the allocation.
|
||||
*/
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PEER_MEASUREMENT_RESULT);
|
||||
if (!hdr)
|
||||
goto free;
|
||||
|
||||
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
||||
nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
|
||||
NL80211_ATTR_PAD))
|
||||
goto free;
|
||||
|
||||
if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie,
|
||||
NL80211_ATTR_PAD))
|
||||
goto free;
|
||||
|
||||
err = nl80211_pmsr_send_result(msg, result);
|
||||
if (err) {
|
||||
pr_err_ratelimited("peer measurement result: message didn't fit!");
|
||||
goto free;
|
||||
}
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid);
|
||||
return;
|
||||
free:
|
||||
nlmsg_free(msg);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cfg80211_pmsr_report);
|
||||
|
||||
void cfg80211_pmsr_free_wk(struct work_struct *work)
|
||||
{
|
||||
struct wireless_dev *wdev = container_of(work, struct wireless_dev,
|
||||
pmsr_free_wk);
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct cfg80211_pmsr_request *req, *tmp;
|
||||
LIST_HEAD(free_list);
|
||||
|
||||
spin_lock_bh(&wdev->pmsr_lock);
|
||||
list_for_each_entry_safe(req, tmp, &wdev->pmsr_list, list) {
|
||||
if (req->nl_portid)
|
||||
continue;
|
||||
list_move_tail(&req->list, &free_list);
|
||||
}
|
||||
spin_unlock_bh(&wdev->pmsr_lock);
|
||||
|
||||
list_for_each_entry_safe(req, tmp, &free_list, list) {
|
||||
wdev_lock(wdev);
|
||||
rdev_abort_pmsr(rdev, wdev, req);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
kfree(req);
|
||||
}
|
||||
}
|
||||
|
||||
void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev)
|
||||
{
|
||||
struct cfg80211_pmsr_request *req;
|
||||
bool found = false;
|
||||
|
||||
spin_lock_bh(&wdev->pmsr_lock);
|
||||
list_for_each_entry(req, &wdev->pmsr_list, list) {
|
||||
found = true;
|
||||
req->nl_portid = 0;
|
||||
}
|
||||
spin_unlock_bh(&wdev->pmsr_lock);
|
||||
|
||||
if (found)
|
||||
schedule_work(&wdev->pmsr_free_wk);
|
||||
flush_work(&wdev->pmsr_free_wk);
|
||||
WARN_ON(!list_empty(&wdev->pmsr_list));
|
||||
}
|
||||
|
||||
void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid)
|
||||
{
|
||||
struct cfg80211_pmsr_request *req;
|
||||
|
||||
spin_lock_bh(&wdev->pmsr_lock);
|
||||
list_for_each_entry(req, &wdev->pmsr_list, list) {
|
||||
if (req->nl_portid == portid) {
|
||||
req->nl_portid = 0;
|
||||
schedule_work(&wdev->pmsr_free_wk);
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&wdev->pmsr_lock);
|
||||
}
|
||||
|
||||
#endif /* __PMSR_H */
|
@ -1247,4 +1247,29 @@ rdev_get_ftm_responder_stats(struct cfg80211_registered_device *rdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_start_pmsr(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
struct cfg80211_pmsr_request *request)
|
||||
{
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
trace_rdev_start_pmsr(&rdev->wiphy, wdev, request->cookie);
|
||||
if (rdev->ops->start_pmsr)
|
||||
ret = rdev->ops->start_pmsr(&rdev->wiphy, wdev, request);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void
|
||||
rdev_abort_pmsr(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
struct cfg80211_pmsr_request *request)
|
||||
{
|
||||
trace_rdev_abort_pmsr(&rdev->wiphy, wdev, request->cookie);
|
||||
if (rdev->ops->abort_pmsr)
|
||||
rdev->ops->abort_pmsr(&rdev->wiphy, wdev, request);
|
||||
trace_rdev_return_void(&rdev->wiphy);
|
||||
}
|
||||
|
||||
#endif /* __CFG80211_RDEV_OPS */
|
||||
|
@ -1183,7 +1183,7 @@ cfg80211_inform_bss_data(struct wiphy *wiphy,
|
||||
switch (ftype) {
|
||||
case CFG80211_BSS_FTYPE_BEACON:
|
||||
ies->from_beacon = true;
|
||||
/* fall through to assign */
|
||||
/* fall through */
|
||||
case CFG80211_BSS_FTYPE_UNKNOWN:
|
||||
rcu_assign_pointer(tmp.pub.beacon_ies, ies);
|
||||
break;
|
||||
|
@ -361,6 +361,24 @@ DECLARE_EVENT_CLASS(wiphy_wdev_evt,
|
||||
TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(wiphy_wdev_cookie_evt,
|
||||
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie),
|
||||
TP_ARGS(wiphy, wdev, cookie),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
WDEV_ENTRY
|
||||
__field(u64, cookie)
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
WDEV_ASSIGN;
|
||||
__entry->cookie = cookie;
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie: %lld",
|
||||
WIPHY_PR_ARG, WDEV_PR_ARG,
|
||||
(unsigned long long)__entry->cookie)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wiphy_wdev_evt, rdev_return_wdev,
|
||||
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
|
||||
TP_ARGS(wiphy, wdev)
|
||||
@ -770,9 +788,9 @@ DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_set_wds_peer,
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_dump_station,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int _idx,
|
||||
u8 *mac),
|
||||
TP_ARGS(wiphy, netdev, idx, mac),
|
||||
TP_ARGS(wiphy, netdev, _idx, mac),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
NETDEV_ENTRY
|
||||
@ -783,7 +801,7 @@ TRACE_EVENT(rdev_dump_station,
|
||||
WIPHY_ASSIGN;
|
||||
NETDEV_ASSIGN;
|
||||
MAC_ASSIGN(sta_mac, mac);
|
||||
__entry->idx = idx;
|
||||
__entry->idx = _idx;
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT ", idx: %d",
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac),
|
||||
@ -847,9 +865,9 @@ DEFINE_EVENT(mpath_evt, rdev_get_mpath,
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_dump_mpath,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int _idx,
|
||||
u8 *dst, u8 *next_hop),
|
||||
TP_ARGS(wiphy, netdev, idx, dst, next_hop),
|
||||
TP_ARGS(wiphy, netdev, _idx, dst, next_hop),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
NETDEV_ENTRY
|
||||
@ -862,7 +880,7 @@ TRACE_EVENT(rdev_dump_mpath,
|
||||
NETDEV_ASSIGN;
|
||||
MAC_ASSIGN(dst, dst);
|
||||
MAC_ASSIGN(next_hop, next_hop);
|
||||
__entry->idx = idx;
|
||||
__entry->idx = _idx;
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: "
|
||||
MAC_PR_FMT ", next hop: " MAC_PR_FMT,
|
||||
@ -892,9 +910,9 @@ TRACE_EVENT(rdev_get_mpp,
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_dump_mpp,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int _idx,
|
||||
u8 *dst, u8 *mpp),
|
||||
TP_ARGS(wiphy, netdev, idx, mpp, dst),
|
||||
TP_ARGS(wiphy, netdev, _idx, mpp, dst),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
NETDEV_ENTRY
|
||||
@ -907,7 +925,7 @@ TRACE_EVENT(rdev_dump_mpp,
|
||||
NETDEV_ASSIGN;
|
||||
MAC_ASSIGN(dst, dst);
|
||||
MAC_ASSIGN(mpp, mpp);
|
||||
__entry->idx = idx;
|
||||
__entry->idx = _idx;
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: "
|
||||
MAC_PR_FMT ", mpp: " MAC_PR_FMT,
|
||||
@ -1673,8 +1691,8 @@ TRACE_EVENT(rdev_tdls_mgmt,
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_dump_survey,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx),
|
||||
TP_ARGS(wiphy, netdev, idx),
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int _idx),
|
||||
TP_ARGS(wiphy, netdev, _idx),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
NETDEV_ENTRY
|
||||
@ -1683,7 +1701,7 @@ TRACE_EVENT(rdev_dump_survey,
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
NETDEV_ASSIGN;
|
||||
__entry->idx = idx;
|
||||
__entry->idx = _idx;
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d",
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx)
|
||||
@ -2502,6 +2520,16 @@ TRACE_EVENT(rdev_get_ftm_responder_stats,
|
||||
__entry->out_of_window)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wiphy_wdev_cookie_evt, rdev_start_pmsr,
|
||||
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie),
|
||||
TP_ARGS(wiphy, wdev, cookie)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wiphy_wdev_cookie_evt, rdev_abort_pmsr,
|
||||
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie),
|
||||
TP_ARGS(wiphy, wdev, cookie)
|
||||
);
|
||||
|
||||
/*************************************************************
|
||||
* cfg80211 exported functions traces *
|
||||
*************************************************************/
|
||||
@ -3294,6 +3322,46 @@ TRACE_EVENT(cfg80211_stop_iface,
|
||||
TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT,
|
||||
WIPHY_PR_ARG, WDEV_PR_ARG)
|
||||
);
|
||||
|
||||
TRACE_EVENT(cfg80211_pmsr_report,
|
||||
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
u64 cookie, const u8 *addr),
|
||||
TP_ARGS(wiphy, wdev, cookie, addr),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
WDEV_ENTRY
|
||||
__field(u64, cookie)
|
||||
MAC_ENTRY(addr)
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
WDEV_ASSIGN;
|
||||
__entry->cookie = cookie;
|
||||
MAC_ASSIGN(addr, addr);
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie:%lld, " MAC_PR_FMT,
|
||||
WIPHY_PR_ARG, WDEV_PR_ARG,
|
||||
(unsigned long long)__entry->cookie,
|
||||
MAC_PR_ARG(addr))
|
||||
);
|
||||
|
||||
TRACE_EVENT(cfg80211_pmsr_complete,
|
||||
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie),
|
||||
TP_ARGS(wiphy, wdev, cookie),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
WDEV_ENTRY
|
||||
__field(u64, cookie)
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
WDEV_ASSIGN;
|
||||
__entry->cookie = cookie;
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie:%lld",
|
||||
WIPHY_PR_ARG, WDEV_PR_ARG,
|
||||
(unsigned long long)__entry->cookie)
|
||||
);
|
||||
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
|
@ -2015,33 +2015,32 @@ int ieee80211_get_vht_max_nss(struct ieee80211_vht_cap *cap,
|
||||
case IEEE80211_VHT_CHANWIDTH_160MHZ:
|
||||
if (supp_width == 0 &&
|
||||
(ext_nss_bw == 1 || ext_nss_bw == 2))
|
||||
return DIV_ROUND_UP(max_vht_nss, 2);
|
||||
return max_vht_nss / 2;
|
||||
if (supp_width == 0 &&
|
||||
ext_nss_bw == 3)
|
||||
return DIV_ROUND_UP(3 * max_vht_nss, 4);
|
||||
return (3 * max_vht_nss) / 4;
|
||||
if (supp_width == 1 &&
|
||||
ext_nss_bw == 3)
|
||||
return 2 * max_vht_nss;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
|
||||
if (supp_width == 0 &&
|
||||
(ext_nss_bw == 1 || ext_nss_bw == 2))
|
||||
if (supp_width == 0 && ext_nss_bw == 1)
|
||||
return 0; /* not possible */
|
||||
if (supp_width == 0 &&
|
||||
ext_nss_bw == 2)
|
||||
return DIV_ROUND_UP(max_vht_nss, 2);
|
||||
return max_vht_nss / 2;
|
||||
if (supp_width == 0 &&
|
||||
ext_nss_bw == 3)
|
||||
return DIV_ROUND_UP(3 * max_vht_nss, 4);
|
||||
return (3 * max_vht_nss) / 4;
|
||||
if (supp_width == 1 &&
|
||||
ext_nss_bw == 0)
|
||||
return 0; /* not possible */
|
||||
if (supp_width == 1 &&
|
||||
ext_nss_bw == 1)
|
||||
return DIV_ROUND_UP(max_vht_nss, 2);
|
||||
return max_vht_nss / 2;
|
||||
if (supp_width == 1 &&
|
||||
ext_nss_bw == 2)
|
||||
return DIV_ROUND_UP(3 * max_vht_nss, 4);
|
||||
return (3 * max_vht_nss) / 4;
|
||||
break;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user