Commit 3713b4e3 authored by Johannes Berg's avatar Johannes Berg
Browse files

nl80211: allow splitting wiphy information in dumps



The per-wiphy information is getting large, to the point
where with more than the typical number of channels it's
too large and overflows, and userspace can't get any of
the information at all.

To address this (in a way that doesn't require making all
messages bigger) allow userspace to specify that it can
deal with wiphy information split across multiple parts
of the dump, and if it can split up the data. This also
splits up each channel separately so an arbitrary number
of channels can be supported.

Additionally, since GET_WIPHY has the same problem, add
support for filtering the wiphy dump and get information
for a single wiphy only, this allows userspace apps to
use dump in this case to retrieve all data from a single
device.

As userspace needs to know if all this this is supported,
add a global nl80211 feature set and include a bit for
this behaviour in it.

Cc: Dennis H Jensen <dennis.h.jensen@siemens.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 191922cd
......@@ -625,6 +625,10 @@
* %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the
* event.
*
* @NL80211_CMD_GET_PROTOCOL_FEATURES: Get global nl80211 protocol features,
* i.e. features for the nl80211 protocol rather than device features.
* Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap.
*
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
......@@ -779,6 +783,8 @@ enum nl80211_commands {
NL80211_CMD_RADAR_DETECT,
NL80211_CMD_GET_PROTOCOL_FEATURES,
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
......@@ -1383,6 +1389,13 @@ enum nl80211_commands {
* advertised to the driver, e.g., to enable TDLS off channel operations
* and PU-APSD.
*
* @NL80211_ATTR_PROTOCOL_FEATURES: global nl80211 feature flags, see
* &enum nl80211_protocol_features, the attribute is a u32.
*
* @NL80211_ATTR_SPLIT_WIPHY_DUMP: flag attribute, userspace supports
* receiving the data for a single wiphy split across multiple
* messages, given with wiphy dump message
*
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
......@@ -1669,6 +1682,9 @@ enum nl80211_attrs {
NL80211_ATTR_STA_CAPABILITY,
NL80211_ATTR_STA_EXT_CAPABILITY,
NL80211_ATTR_PROTOCOL_FEATURES,
NL80211_ATTR_SPLIT_WIPHY_DUMP,
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
......@@ -3619,4 +3635,16 @@ enum nl80211_dfs_state {
NL80211_DFS_AVAILABLE,
};
/**
* enum enum nl80211_protocol_features - nl80211 protocol features
* @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting
* wiphy dumps (if requested by the application with the attribute
* %NL80211_ATTR_SPLIT_WIPHY_DUMP. Also supported is filtering the
* wiphy dump by %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFINDEX or
* %NL80211_ATTR_WDEV.
*/
enum nl80211_protocol_features {
NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP = 1 << 0,
};
#endif /* __LINUX_NL80211_H */
......@@ -370,6 +370,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
[NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 },
[NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, },
[NL80211_ATTR_SPLIT_WIPHY_DUMP] = { .type = NLA_FLAG, },
};
/* policy for the key attributes */
......@@ -892,412 +893,545 @@ nla_put_failure:
return -ENOBUFS;
}
static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flags,
struct cfg80211_registered_device *dev)
#ifdef CONFIG_PM
static int nl80211_send_wowlan(struct sk_buff *msg,
struct cfg80211_registered_device *dev)
{
void *hdr;
struct nlattr *nl_bands, *nl_band;
struct nlattr *nl_freqs, *nl_freq;
struct nlattr *nl_rates, *nl_rate;
struct nlattr *nl_cmds;
enum ieee80211_band band;
struct ieee80211_channel *chan;
struct ieee80211_rate *rate;
int i;
const struct ieee80211_txrx_stypes *mgmt_stypes =
dev->wiphy.mgmt_stypes;
struct nlattr *nl_wowlan;
hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
if (!hdr)
return -1;
if (!dev->wiphy.wowlan.flags && !dev->wiphy.wowlan.n_patterns)
return 0;
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
nla_put_string(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)) ||
nla_put_u32(msg, NL80211_ATTR_GENERATION,
cfg80211_rdev_list_generation) ||
nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
dev->wiphy.retry_short) ||
nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
dev->wiphy.retry_long) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
dev->wiphy.frag_threshold) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
dev->wiphy.rts_threshold) ||
nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
dev->wiphy.coverage_class) ||
nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
dev->wiphy.max_scan_ssids) ||
nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
dev->wiphy.max_sched_scan_ssids) ||
nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
dev->wiphy.max_scan_ie_len) ||
nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
dev->wiphy.max_sched_scan_ie_len) ||
nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
dev->wiphy.max_match_sets))
goto nla_put_failure;
nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
if (!nl_wowlan)
return -ENOBUFS;
if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
goto nla_put_failure;
if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
goto nla_put_failure;
if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
goto nla_put_failure;
if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
goto nla_put_failure;
if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
goto nla_put_failure;
if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
goto nla_put_failure;
if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
return -ENOBUFS;
if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
sizeof(u32) * dev->wiphy.n_cipher_suites,
dev->wiphy.cipher_suites))
goto nla_put_failure;
if (dev->wiphy.wowlan.n_patterns) {
struct nl80211_wowlan_pattern_support pat = {
.max_patterns = dev->wiphy.wowlan.n_patterns,
.min_pattern_len = dev->wiphy.wowlan.pattern_min_len,
.max_pattern_len = dev->wiphy.wowlan.pattern_max_len,
.max_pkt_offset = dev->wiphy.wowlan.max_pkt_offset,
};
if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
dev->wiphy.max_num_pmkids))
goto nla_put_failure;
if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
sizeof(pat), &pat))
return -ENOBUFS;
}
if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
goto nla_put_failure;
nla_nest_end(msg, nl_wowlan);
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
dev->wiphy.available_antennas_tx) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
dev->wiphy.available_antennas_rx))
goto nla_put_failure;
return 0;
}
#endif
if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
dev->wiphy.probe_resp_offload))
goto nla_put_failure;
static int nl80211_send_band_rateinfo(struct sk_buff *msg,
struct ieee80211_supported_band *sband)
{
struct nlattr *nl_rates, *nl_rate;
struct ieee80211_rate *rate;
int i;
if ((dev->wiphy.available_antennas_tx ||
dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
u32 tx_ant = 0, rx_ant = 0;
int res;
res = rdev_get_antenna(dev, &tx_ant, &rx_ant);
if (!res) {
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX,
tx_ant) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX,
rx_ant))
goto nla_put_failure;
}
}
/* add HT info */
if (sband->ht_cap.ht_supported &&
(nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
sizeof(sband->ht_cap.mcs),
&sband->ht_cap.mcs) ||
nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
sband->ht_cap.cap) ||
nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
sband->ht_cap.ampdu_factor) ||
nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
sband->ht_cap.ampdu_density)))
return -ENOBUFS;
if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
dev->wiphy.interface_modes))
goto nla_put_failure;
/* add VHT info */
if (sband->vht_cap.vht_supported &&
(nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET,
sizeof(sband->vht_cap.vht_mcs),
&sband->vht_cap.vht_mcs) ||
nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA,
sband->vht_cap.cap)))
return -ENOBUFS;
nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
if (!nl_bands)
goto nla_put_failure;
/* add bitrates */
nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
if (!nl_rates)
return -ENOBUFS;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!dev->wiphy.bands[band])
continue;
for (i = 0; i < sband->n_bitrates; i++) {
nl_rate = nla_nest_start(msg, i);
if (!nl_rate)
return -ENOBUFS;
nl_band = nla_nest_start(msg, band);
if (!nl_band)
goto nla_put_failure;
rate = &sband->bitrates[i];
if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE,
rate->bitrate))
return -ENOBUFS;
if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
nla_put_flag(msg,
NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE))
return -ENOBUFS;
/* add HT info */
if (dev->wiphy.bands[band]->ht_cap.ht_supported &&
(nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
&dev->wiphy.bands[band]->ht_cap.mcs) ||
nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
dev->wiphy.bands[band]->ht_cap.cap) ||
nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
dev->wiphy.bands[band]->ht_cap.ampdu_factor) ||
nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
dev->wiphy.bands[band]->ht_cap.ampdu_density)))
goto nla_put_failure;
nla_nest_end(msg, nl_rate);
}
/* add VHT info */
if (dev->wiphy.bands[band]->vht_cap.vht_supported &&
(nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET,
sizeof(dev->wiphy.bands[band]->vht_cap.vht_mcs),
&dev->wiphy.bands[band]->vht_cap.vht_mcs) ||
nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA,
dev->wiphy.bands[band]->vht_cap.cap)))
goto nla_put_failure;
nla_nest_end(msg, nl_rates);
/* add frequencies */
nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
if (!nl_freqs)
goto nla_put_failure;
return 0;
}
for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
nl_freq = nla_nest_start(msg, i);
if (!nl_freq)
goto nla_put_failure;
static int
nl80211_send_mgmt_stypes(struct sk_buff *msg,
const struct ieee80211_txrx_stypes *mgmt_stypes)
{
u16 stypes;
struct nlattr *nl_ftypes, *nl_ifs;
enum nl80211_iftype ift;
int i;
chan = &dev->wiphy.bands[band]->channels[i];
if (!mgmt_stypes)
return 0;
if (nl80211_msg_put_channel(msg, chan))
goto nla_put_failure;
nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
if (!nl_ifs)
return -ENOBUFS;
nla_nest_end(msg, nl_freq);
for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
nl_ftypes = nla_nest_start(msg, ift);
if (!nl_ftypes)
return -ENOBUFS;
i = 0;
stypes = mgmt_stypes[ift].tx;
while (stypes) {
if ((stypes & 1) &&
nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
(i << 4) | IEEE80211_FTYPE_MGMT))
return -ENOBUFS;
stypes >>= 1;
i++;
}
nla_nest_end(msg, nl_ftypes);
}
nla_nest_end(msg, nl_freqs);
nla_nest_end(msg, nl_ifs);
/* add bitrates */
nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
if (!nl_rates)
goto nla_put_failure;
nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
if (!nl_ifs)
return -ENOBUFS;
for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
nl_rate = nla_nest_start(msg, i);
if (!nl_rate)
goto nla_put_failure;
for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
nl_ftypes = nla_nest_start(msg, ift);
if (!nl_ftypes)
return -ENOBUFS;
i = 0;
stypes = mgmt_stypes[ift].rx;
while (stypes) {
if ((stypes & 1) &&
nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
(i << 4) | IEEE80211_FTYPE_MGMT))
return -ENOBUFS;
stypes >>= 1;
i++;
}
nla_nest_end(msg, nl_ftypes);
}
nla_nest_end(msg, nl_ifs);
rate = &dev->wiphy.bands[band]->bitrates[i];
if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE,
rate->bitrate))
goto nla_put_failure;
if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
nla_put_flag(msg,
NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE))
goto nla_put_failure;
return 0;
}
nla_nest_end(msg, nl_rate);
}
static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
struct sk_buff *msg, u32 portid, u32 seq,
int flags, bool split, long *split_start,
long *band_start, long *chan_start)
{
void *hdr;
struct nlattr *nl_bands, *nl_band;
struct nlattr *nl_freqs, *nl_freq;
struct nlattr *nl_cmds;
enum ieee80211_band band;
struct ieee80211_channel *chan;
int i;
const struct ieee80211_txrx_stypes *mgmt_stypes =
dev->wiphy.mgmt_stypes;
long start = 0, start_chan = 0, start_band = 0;
nla_nest_end(msg, nl_rates);
hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
if (!hdr)
return -ENOBUFS;
nla_nest_end(msg, nl_band);
/* allow always using the variables */
if (!split) {
split_start = &start;
band_start = &start_band;
chan_start = &start_chan;
}
nla_nest_end(msg, nl_bands);
nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
if (!nl_cmds)
goto nla_put_failure;
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
nla_put_string(msg, NL80211_ATTR_WIPHY_NAME,
wiphy_name(&dev->wiphy)) ||
nla_put_u32(msg, NL80211_ATTR_GENERATION,
cfg80211_rdev_list_generation))
goto nla_put_failure;
switch (*split_start) {
case 0:
if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
dev->wiphy.retry_short) ||
nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
dev->wiphy.retry_long) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
dev->wiphy.frag_threshold) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
dev->wiphy.rts_threshold) ||
nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
dev->wiphy.coverage_class) ||
nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
dev->wiphy.max_scan_ssids) ||
nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
dev->wiphy.max_sched_scan_ssids) ||
nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
dev->wiphy.max_scan_ie_len) ||
nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
dev->wiphy.max_sched_scan_ie_len) ||
nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
dev->wiphy.max_match_sets))
goto nla_put_failure;
i = 0;
#define CMD(op, n) \
do { \
if (dev->ops->op) { \
i++; \
if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
goto nla_put_failure; \
} \
} while (0)
CMD(add_virtual_intf, NEW_INTERFACE);
CMD(change_virtual_intf, SET_INTERFACE);
CMD(add_key, NEW_KEY);
CMD(start_ap, START_AP);
CMD(add_station, NEW_STATION);
CMD(add_mpath, NEW_MPATH);
CMD(update_mesh_config, SET_MESH_CONFIG);
CMD(change_bss, SET_BSS);
CMD(auth, AUTHENTICATE);
CMD(assoc, ASSOCIATE);
CMD(deauth, DEAUTHENTICATE);
CMD(disassoc, DISASSOCIATE);
CMD(join_ibss, JOIN_IBSS);
CMD(join_mesh, JOIN_MESH);
CMD(set_pmksa, SET_PMKSA);
CMD(del_pmksa, DEL_PMKSA);
CMD(flush_pmksa, FLUSH_PMKSA);
if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
CMD(remain_on_channel, REMAIN_ON_CHANNEL);
CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
CMD(mgmt_tx, FRAME);
CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
i++;
if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
goto nla_put_failure;
}
if (dev->ops->set_monitor_channel || dev->ops->start_ap ||
dev->ops->join_mesh) {
i++;
if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
goto nla_put_failure;
}
CMD(set_wds_peer, SET_WDS_PEER);
if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
CMD(tdls_mgmt, TDLS_MGMT);
CMD(tdls_oper, TDLS_OPER);
}
if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
CMD(sched_scan_start, START_SCHED_SCAN);
CMD(probe_client, PROBE_CLIENT);
CMD(set_noack_map, SET_NOACK_MAP);
if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
i++;
if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
goto nla_put_failure;
if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
goto nla_put_failure;
if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
goto nla_put_failure;
if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
goto nla_put_failure;
}
CMD(start_p2p_device, START_P2P_DEVICE);
CMD(set_mcast_rate, SET_MCAST_RATE);
#ifdef CONFIG_NL80211_TESTMODE
CMD(testmode_cmd, TESTMODE);
#endif
(*split_start)++;
if (split)
break;
case 1:
if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
sizeof(u32) * dev->wiphy.n_cipher_suites,
dev->wiphy.cipher_suites))
goto nla_put_failure;
#undef CMD
if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
dev->wiphy.max_num_pmkids))
goto nla_put_failure;
if (dev->ops->connect || dev->ops->auth) {
i++;
if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
goto nla_put_failure;
}
if (dev->ops->disconnect || dev->ops->deauth) {
i++;
if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
dev->wiphy.available_antennas_tx) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
dev->wiphy.available_antennas_rx))
goto nla_put_failure;
}
nla_nest_end(msg, nl_cmds);
if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
dev->wiphy.probe_resp_offload))
goto nla_put_failure;
if (dev->ops->remain_on_channel &&
(dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
nla_put_u32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
dev->wiphy.max_remain_on_channel_duration))
goto nla_put_failure;
if ((dev->wiphy.available_antennas_tx ||
dev->wiphy.available_antennas_rx) &&
dev->ops->get_antenna) {
u32 tx_ant = 0, rx_ant = 0;
int res;
res = rdev_get_antenna(dev, &tx_ant, &rx_ant);
if (!res) {
if (nla_put_u32(msg,
NL80211_ATTR_WIPHY_ANTENNA_TX,
tx_ant) ||
nla_put_u32(msg,
NL80211_ATTR_WIPHY_ANTENNA_RX,
rx_ant))
goto nla_put_failure;
}
}
if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
goto nla_put_failure;
(*split_start)++;
if (split)
break;
case 2:
if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
dev->wiphy.interface_modes))
goto nla_put_failure;
(*split_start)++;
if (split)
break;
case 3:
nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
if (!nl_bands)
goto nla_put_failure;
if (mgmt_stypes) {
u16 stypes;
struct nlattr *nl_ftypes, *nl_ifs;
enum nl80211_iftype ift;
for (band = *band_start; band < IEEE80211_NUM_BANDS; band++) {
struct ieee80211_supported_band *sband;
nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
if (!nl_ifs)
goto nla_put_failure;
sband = dev->wiphy.bands[band];
for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
nl_ftypes = nla_nest_start(msg, ift);
if (!nl_ftypes)
if (!sband)
continue;
nl_band = nla_nest_start(msg, band);
if (!nl_band)
goto nla_put_failure;
i = 0;
stypes = mgmt_stypes[ift].tx;
while (stypes) {
if ((stypes & 1) &&
nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
(i << 4) | IEEE80211_FTYPE_MGMT))
switch (*chan_start) {
case 0:
if (nl80211_send_band_rateinfo(msg, sband))
goto nla_put_failure;
stypes >>= 1;
i++;
(*chan_start)++;
if (split)
break;
default:
/* add frequencies */
nl_freqs = nla_nest_start(
msg, NL80211_BAND_ATTR_FREQS);
if (!nl_freqs)
goto nla_put_failure;