nl80211: explicit userspace MPM
[cascardo/linux.git] / net / wireless / nl80211.c
index 83151a5..bdf3983 100644 (file)
@@ -370,6 +370,14 @@ 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, },
+       [NL80211_ATTR_DISABLE_VHT] = { .type = NLA_FLAG },
+       [NL80211_ATTR_VHT_CAPABILITY_MASK] = {
+               .len = NL80211_VHT_CAPABILITY_LEN,
+       },
+       [NL80211_ATTR_MDID] = { .type = NLA_U16 },
+       [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
+                                 .len = IEEE80211_MAX_DATA_LEN },
 };
 
 /* policy for the key attributes */
@@ -539,7 +547,8 @@ static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
 }
 
 static int nl80211_msg_put_channel(struct sk_buff *msg,
-                                  struct ieee80211_channel *chan)
+                                  struct ieee80211_channel *chan,
+                                  bool large)
 {
        if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,
                        chan->center_freq))
@@ -554,9 +563,37 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
        if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
            nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
                goto nla_put_failure;
-       if ((chan->flags & IEEE80211_CHAN_RADAR) &&
-           nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
-               goto nla_put_failure;
+       if (chan->flags & IEEE80211_CHAN_RADAR) {
+               if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
+                       goto nla_put_failure;
+               if (large) {
+                       u32 time;
+
+                       time = elapsed_jiffies_msecs(chan->dfs_state_entered);
+
+                       if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE,
+                                       chan->dfs_state))
+                               goto nla_put_failure;
+                       if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME,
+                                       time))
+                               goto nla_put_failure;
+               }
+       }
+
+       if (large) {
+               if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_80MHZ))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))
+                       goto nla_put_failure;
+       }
 
        if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
                        DBM_TO_MBM(chan->max_power)))
@@ -832,7 +869,8 @@ nla_put_failure:
 }
 
 static int nl80211_put_iface_combinations(struct wiphy *wiphy,
-                                         struct sk_buff *msg)
+                                         struct sk_buff *msg,
+                                         bool large)
 {
        struct nlattr *nl_combis;
        int i, j;
@@ -881,6 +919,10 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy,
                    nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
                                c->max_interfaces))
                        goto nla_put_failure;
+               if (large &&
+                   nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
+                               c->radar_detect_widths))
+                       goto nla_put_failure;
 
                nla_nest_end(msg, nl_combi);
        }
@@ -892,412 +934,611 @@ 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_tcp_caps(struct cfg80211_registered_device *rdev,
+                                       struct sk_buff *msg)
 {
-       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;
+       const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan.tcp;
+       struct nlattr *nl_tcp;
 
-       hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
-       if (!hdr)
-               return -1;
+       if (!tcp)
+               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_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
+       if (!nl_tcp)
+               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 (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
+                       tcp->data_payload_max))
+               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 (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
+                       tcp->data_payload_max))
+               return -ENOBUFS;
 
-       if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
-                      dev->wiphy.max_num_pmkids))
-               goto nla_put_failure;
+       if (tcp->seq && nla_put_flag(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ))
+               return -ENOBUFS;
 
-       if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
-           nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
-               goto nla_put_failure;
+       if (tcp->tok && nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
+                               sizeof(*tcp->tok), tcp->tok))
+               return -ENOBUFS;
 
-       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;
+       if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
+                       tcp->data_interval_max))
+               return -ENOBUFS;
 
-       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 (nla_put_u32(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
+                       tcp->wake_payload_max))
+               return -ENOBUFS;
 
-       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;
-               }
+       nla_nest_end(msg, nl_tcp);
+       return 0;
+}
+
+static int nl80211_send_wowlan(struct sk_buff *msg,
+                              struct cfg80211_registered_device *dev,
+                              bool large)
+{
+       struct nlattr *nl_wowlan;
+
+       if (!dev->wiphy.wowlan.flags && !dev->wiphy.wowlan.n_patterns)
+               return 0;
+
+       nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
+       if (!nl_wowlan)
+               return -ENOBUFS;
+
+       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 (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(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
+                           sizeof(pat), &pat))
+                       return -ENOBUFS;
        }
 
-       if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
-                               dev->wiphy.interface_modes))
-               goto nla_put_failure;
+       if (large && nl80211_send_wowlan_tcp_caps(dev, msg))
+               return -ENOBUFS;
 
-       nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
-       if (!nl_bands)
-               goto nla_put_failure;
+       nla_nest_end(msg, nl_wowlan);
 
-       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
-               if (!dev->wiphy.bands[band])
-                       continue;
+       return 0;
+}
+#endif
 
-               nl_band = nla_nest_start(msg, band);
-               if (!nl_band)
-                       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;
 
-               /* 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;
+       /* 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;
 
-               /* 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;
+       /* 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;
 
-               /* add frequencies */
-               nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
-               if (!nl_freqs)
-                       goto nla_put_failure;
+       /* add bitrates */
+       nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
+       if (!nl_rates)
+               return -ENOBUFS;
 
-               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;
+       for (i = 0; i < sband->n_bitrates; i++) {
+               nl_rate = nla_nest_start(msg, i);
+               if (!nl_rate)
+                       return -ENOBUFS;
 
-                       chan = &dev->wiphy.bands[band]->channels[i];
+               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;
 
-                       if (nl80211_msg_put_channel(msg, chan))
-                               goto nla_put_failure;
+               nla_nest_end(msg, nl_rate);
+       }
 
-                       nla_nest_end(msg, nl_freq);
-               }
+       nla_nest_end(msg, nl_rates);
+
+       return 0;
+}
 
-               nla_nest_end(msg, nl_freqs);
+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;
 
-               /* add bitrates */
-               nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
-               if (!nl_rates)
-                       goto nla_put_failure;
+       if (!mgmt_stypes)
+               return 0;
 
-               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;
+       nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
+       if (!nl_ifs)
+               return -ENOBUFS;
 
-                       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;
+       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_ifs);
+
+       nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
+       if (!nl_ifs)
+               return -ENOBUFS;
 
-                       nla_nest_end(msg, nl_rate);
+       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);
 
-               nla_nest_end(msg, nl_rates);
+       return 0;
+}
 
-               nla_nest_end(msg, nl_band);
+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;
+       u32 features;
+
+       hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_WIPHY);
+       if (!hdr)
+               return -ENOBUFS;
+
+       /* 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];
+
+                       if (!sband)
+                               continue;
 
-               for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
-                       nl_ftypes = nla_nest_start(msg, ift);
-                       if (!nl_ftypes)
+                       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;
+
+                               for (i = *chan_start - 1;
+                                    i < sband->n_channels;
+                                    i++) {
+                                       nl_freq = nla_nest_start(msg, i);
+                                       if (!nl_freq)
+                                               goto nla_put_failure;
+
+                                       chan = &sband->channels[i];
+
+                                       if (nl80211_msg_put_channel(msg, chan,
+                                                                   split))
+                                               goto nla_put_failure;
+
+                                       nla_nest_end(msg, nl_freq);
+                                       if (split)
+                                               break;
+                               }
+                               if (i < sband->n_channels)
+                                       *chan_start = i + 2;
+                               else
+                                       *chan_start = 0;
+                               nla_nest_end(msg, nl_freqs);
+                       }
+
+                       nla_nest_end(msg, nl_band);
+
+                       if (split) {
+                               /* start again here */
+                               if (*chan_start)
+                                       band--;
+                               break;
                        }
-                       nla_nest_end(msg, nl_ftypes);
                }
+               nla_nest_end(msg, nl_bands);
 
-               nla_nest_end(msg, nl_ifs);
+               if (band < IEEE80211_NUM_BANDS)
+                       *band_start = band + 1;
+               else
+                       *band_start = 0;
 
-               nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
-               if (!nl_ifs)
+               /* if bands & channels are done, continue outside */
+               if (*band_start == 0 && *chan_start == 0)
+                       (*split_start)++;
+               if (split)
+                       break;
+       case 4:
+               nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
+               if (!nl_cmds)
                        goto nla_put_failure;
 
-               for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
-                       nl_ftypes = nla_nest_start(msg, ift);
-                       if (!nl_ftypes)
+               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))
                                goto nla_put_failure;
-                       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))
-                                       goto nla_put_failure;
-                               stypes >>= 1;
-                               i++;
-                       }
-                       nla_nest_end(msg, nl_ftypes);
                }
-               nla_nest_end(msg, nl_ifs);
-       }
+               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))
+                               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))
+                               goto nla_put_failure;
+               }
+               CMD(start_p2p_device, START_P2P_DEVICE);
+               CMD(set_mcast_rate, SET_MCAST_RATE);
 
-#ifdef CONFIG_PM
-       if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
-               struct nlattr *nl_wowlan;
+#ifdef CONFIG_NL80211_TESTMODE
+               CMD(testmode_cmd, TESTMODE);
+#endif
 
-               nl_wowlan = nla_nest_start(msg,
-                               NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
-               if (!nl_wowlan)
-                       goto nla_put_failure;
+#undef CMD
 
-               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)))
-                   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(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
-                                   sizeof(pat), &pat))
+               if (dev->ops->connect || dev->ops->auth) {
+                       i++;
+                       if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
                                goto nla_put_failure;
                }
 
-               nla_nest_end(msg, nl_wowlan);
-       }
+               if (dev->ops->disconnect || dev->ops->deauth) {
+                       i++;
+                       if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
+                               goto nla_put_failure;
+               }
+
+               nla_nest_end(msg, nl_cmds);
+               (*split_start)++;
+               if (split)
+                       break;
+       case 5:
+               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.flags & WIPHY_FLAG_OFFCHAN_TX) &&
+                   nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
+                       goto nla_put_failure;
+
+               if (nl80211_send_mgmt_stypes(msg, mgmt_stypes))
+                       goto nla_put_failure;
+               (*split_start)++;
+               if (split)
+                       break;
+       case 6:
+#ifdef CONFIG_PM
+               if (nl80211_send_wowlan(msg, dev, split))
+                       goto nla_put_failure;
+               (*split_start)++;
+               if (split)
+                       break;
+#else
+               (*split_start)++;
 #endif
+       case 7:
+               if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
+                                       dev->wiphy.software_iftypes))
+                       goto nla_put_failure;
 
-       if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
-                               dev->wiphy.software_iftypes))
-               goto nla_put_failure;
+               if (nl80211_put_iface_combinations(&dev->wiphy, msg, split))
+                       goto nla_put_failure;
 
-       if (nl80211_put_iface_combinations(&dev->wiphy, msg))
-               goto nla_put_failure;
+               (*split_start)++;
+               if (split)
+                       break;
+       case 8:
+               if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
+                   nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
+                               dev->wiphy.ap_sme_capa))
+                       goto nla_put_failure;
 
-       if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
-           nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
-                       dev->wiphy.ap_sme_capa))
-               goto nla_put_failure;
+               features = dev->wiphy.features;
+               /*
+                * We can only add the per-channel limit information if the
+                * dump is split, otherwise it makes it too big. Therefore
+                * only advertise it in that case.
+                */
+               if (split)
+                       features |= NL80211_FEATURE_ADVERTISE_CHAN_LIMITS;
+               if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, features))
+                       goto nla_put_failure;
 
-       if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS,
-                       dev->wiphy.features))
-               goto nla_put_failure;
+               if (dev->wiphy.ht_capa_mod_mask &&
+                   nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
+                           sizeof(*dev->wiphy.ht_capa_mod_mask),
+                           dev->wiphy.ht_capa_mod_mask))
+                       goto nla_put_failure;
 
-       if (dev->wiphy.ht_capa_mod_mask &&
-           nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
-                   sizeof(*dev->wiphy.ht_capa_mod_mask),
-                   dev->wiphy.ht_capa_mod_mask))
-               goto nla_put_failure;
+               if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
+                   dev->wiphy.max_acl_mac_addrs &&
+                   nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
+                               dev->wiphy.max_acl_mac_addrs))
+                       goto nla_put_failure;
 
-       if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
-           dev->wiphy.max_acl_mac_addrs &&
-           nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
-                       dev->wiphy.max_acl_mac_addrs))
-               goto nla_put_failure;
+               /*
+                * Any information below this point is only available to
+                * applications that can deal with it being split. This
+                * helps ensure that newly added capabilities don't break
+                * older tools by overrunning their buffers.
+                *
+                * We still increment split_start so that in the split
+                * case we'll continue with more data in the next round,
+                * but break unconditionally so unsplit data stops here.
+                */
+               (*split_start)++;
+               break;
+       case 9:
+               if (dev->wiphy.extended_capabilities &&
+                   (nla_put(msg, NL80211_ATTR_EXT_CAPA,
+                            dev->wiphy.extended_capabilities_len,
+                            dev->wiphy.extended_capabilities) ||
+                    nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
+                            dev->wiphy.extended_capabilities_len,
+                            dev->wiphy.extended_capabilities_mask)))
+                       goto nla_put_failure;
+
+               if (dev->wiphy.vht_capa_mod_mask &&
+                   nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK,
+                           sizeof(*dev->wiphy.vht_capa_mod_mask),
+                           dev->wiphy.vht_capa_mod_mask))
+                       goto nla_put_failure;
 
+               /* done */
+               *split_start = 0;
+               break;
+       }
        return genlmsg_end(msg, hdr);
 
  nla_put_failure:
@@ -1310,39 +1551,80 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
        int idx = 0, ret;
        int start = cb->args[0];
        struct cfg80211_registered_device *dev;
+       s64 filter_wiphy = -1;
+       bool split = false;
+       struct nlattr **tb = nl80211_fam.attrbuf;
+       int res;
 
        mutex_lock(&cfg80211_mutex);
+       res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
+                         tb, nl80211_fam.maxattr, nl80211_policy);
+       if (res == 0) {
+               split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP];
+               if (tb[NL80211_ATTR_WIPHY])
+                       filter_wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+               if (tb[NL80211_ATTR_WDEV])
+                       filter_wiphy = nla_get_u64(tb[NL80211_ATTR_WDEV]) >> 32;
+               if (tb[NL80211_ATTR_IFINDEX]) {
+                       struct net_device *netdev;
+                       int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+
+                       netdev = dev_get_by_index(sock_net(skb->sk), ifidx);
+                       if (!netdev) {
+                               mutex_unlock(&cfg80211_mutex);
+                               return -ENODEV;
+                       }
+                       if (netdev->ieee80211_ptr) {
+                               dev = wiphy_to_dev(
+                                       netdev->ieee80211_ptr->wiphy);
+                               filter_wiphy = dev->wiphy_idx;
+                       }
+                       dev_put(netdev);
+               }
+       }
+
        list_for_each_entry(dev, &cfg80211_rdev_list, list) {
                if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
                        continue;
                if (++idx <= start)
                        continue;
-               ret = nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).portid,
-                                        cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                                        dev);
-               if (ret < 0) {
-                       /*
-                        * If sending the wiphy data didn't fit (ENOBUFS or
-                        * EMSGSIZE returned), this SKB is still empty (so
-                        * it's not too big because another wiphy dataset is
-                        * already in the skb) and we've not tried to adjust
-                        * the dump allocation yet ... then adjust the alloc
-                        * size to be bigger, and return 1 but with the empty
-                        * skb. This results in an empty message being RX'ed
-                        * in userspace, but that is ignored.
-                        *
-                        * We can then retry with the larger buffer.
-                        */
-                       if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
-                           !skb->len &&
-                           cb->min_dump_alloc < 4096) {
-                               cb->min_dump_alloc = 4096;
-                               mutex_unlock(&cfg80211_mutex);
-                               return 1;
+               if (filter_wiphy != -1 && dev->wiphy_idx != filter_wiphy)
+                       continue;
+               /* attempt to fit multiple wiphy data chunks into the skb */
+               do {
+                       ret = nl80211_send_wiphy(dev, skb,
+                                                NETLINK_CB(cb->skb).portid,
+                                                cb->nlh->nlmsg_seq,
+                                                NLM_F_MULTI,
+                                                split, &cb->args[1],
+                                                &cb->args[2],
+                                                &cb->args[3]);
+                       if (ret < 0) {
+                               /*
+                                * If sending the wiphy data didn't fit (ENOBUFS
+                                * or EMSGSIZE returned), this SKB is still
+                                * empty (so it's not too big because another
+                                * wiphy dataset is already in the skb) and
+                                * we've not tried to adjust the dump allocation
+                                * yet ... then adjust the alloc size to be
+                                * bigger, and return 1 but with the empty skb.
+                                * This results in an empty message being RX'ed
+                                * in userspace, but that is ignored.
+                                *
+                                * We can then retry with the larger buffer.
+                                */
+                               if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
+                                   !skb->len &&
+                                   cb->min_dump_alloc < 4096) {
+                                       cb->min_dump_alloc = 4096;
+                                       mutex_unlock(&cfg80211_mutex);
+                                       return 1;
+                               }
+                               idx--;
+                               break;
                        }
-                       idx--;
-                       break;
-               }
+               } while (cb->args[1] > 0);
+               break;
        }
        mutex_unlock(&cfg80211_mutex);
 
@@ -1360,7 +1642,8 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
        if (!msg)
                return -ENOMEM;
 
-       if (nl80211_send_wiphy(msg, info->snd_portid, info->snd_seq, 0, dev) < 0) {
+       if (nl80211_send_wiphy(dev, msg, info->snd_portid, info->snd_seq, 0,
+                              false, NULL, NULL, NULL) < 0) {
                nlmsg_free(msg);
                return -ENOBUFS;
        }
@@ -3768,6 +4051,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
                break;
        case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
                /* ignore uAPSD data */
                params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
 
@@ -4334,6 +4618,7 @@ static const struct nla_policy
        [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
        [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
        [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
+       [NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG },
        [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
                                    .len = IEEE80211_MAX_DATA_LEN },
        [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
@@ -4472,6 +4757,7 @@ do {                                                                          \
 static int nl80211_parse_mesh_setup(struct genl_info *info,
                                     struct mesh_setup *setup)
 {
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
 
        if (!info->attrs[NL80211_ATTR_MESH_SETUP])
@@ -4508,8 +4794,14 @@ static int nl80211_parse_mesh_setup(struct genl_info *info,
                setup->ie = nla_data(ieattr);
                setup->ie_len = nla_len(ieattr);
        }
+       if (tb[NL80211_MESH_SETUP_USERSPACE_MPM] &&
+           !(rdev->wiphy.features & NL80211_FEATURE_USERSPACE_MPM))
+               return -EINVAL;
+       setup->user_mpm = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_MPM]);
        setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
        setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
+       if (setup->is_secure)
+               setup->user_mpm = true;
 
        return 0;
 }
@@ -5704,14 +5996,10 @@ static int nl80211_associate(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 cfg80211_crypto_settings crypto;
        struct ieee80211_channel *chan;
-       const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
-       int err, ssid_len, ie_len = 0;
-       bool use_mfp = false;
-       u32 flags = 0;
-       struct ieee80211_ht_cap *ht_capa = NULL;
-       struct ieee80211_ht_cap *ht_capa_mask = NULL;
+       struct cfg80211_assoc_request req = {};
+       const u8 *bssid, *ssid;
+       int err, ssid_len = 0;
 
        if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
                return -EINVAL;
@@ -5739,41 +6027,58 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
        ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
 
        if (info->attrs[NL80211_ATTR_IE]) {
-               ie = nla_data(info->attrs[NL80211_ATTR_IE]);
-               ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+               req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
        }
 
        if (info->attrs[NL80211_ATTR_USE_MFP]) {
                enum nl80211_mfp mfp =
                        nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
                if (mfp == NL80211_MFP_REQUIRED)
-                       use_mfp = true;
+                       req.use_mfp = true;
                else if (mfp != NL80211_MFP_NO)
                        return -EINVAL;
        }
 
        if (info->attrs[NL80211_ATTR_PREV_BSSID])
-               prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
+               req.prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
 
        if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
-               flags |= ASSOC_REQ_DISABLE_HT;
+               req.flags |= ASSOC_REQ_DISABLE_HT;
 
        if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
-               ht_capa_mask =
-                       nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]);
+               memcpy(&req.ht_capa_mask,
+                      nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
+                      sizeof(req.ht_capa_mask));
 
        if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
-               if (!ht_capa_mask)
+               if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+                       return -EINVAL;
+               memcpy(&req.ht_capa,
+                      nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
+                      sizeof(req.ht_capa));
+       }
+
+       if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
+               req.flags |= ASSOC_REQ_DISABLE_VHT;
+
+       if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
+               memcpy(&req.vht_capa_mask,
+                      nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
+                      sizeof(req.vht_capa_mask));
+
+       if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
+               if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
                        return -EINVAL;
-               ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
+               memcpy(&req.vht_capa,
+                      nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
+                      sizeof(req.vht_capa));
        }
 
-       err = nl80211_crypto_settings(rdev, info, &crypto, 1);
+       err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
        if (!err)
-               err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
-                                         ssid, ssid_len, ie, ie_len, use_mfp,
-                                         &crypto, flags, ht_capa,
-                                         ht_capa_mask);
+               err = cfg80211_mlme_assoc(rdev, dev, chan, bssid,
+                                         ssid, ssid_len, &req);
 
        return err;
 }
@@ -6353,6 +6658,24 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
                       sizeof(connect.ht_capa));
        }
 
+       if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
+               connect.flags |= ASSOC_REQ_DISABLE_VHT;
+
+       if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
+               memcpy(&connect.vht_capa_mask,
+                      nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
+                      sizeof(connect.vht_capa_mask));
+
+       if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
+               if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) {
+                       kfree(connkeys);
+                       return -EINVAL;
+               }
+               memcpy(&connect.vht_capa,
+                      nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
+                      sizeof(connect.vht_capa));
+       }
+
        err = cfg80211_connect(rdev, dev, &connect, connkeys);
        if (err)
                kfree(connkeys);
@@ -7325,7 +7648,8 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
                return -EINVAL;
 
        if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
-                       rdev->wiphy.wowlan.tcp->data_interval_max)
+                       rdev->wiphy.wowlan.tcp->data_interval_max ||
+           nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0)
                return -EINVAL;
 
        wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
@@ -7821,6 +8145,54 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
        return 0;
 }
 
+static int nl80211_get_protocol_features(struct sk_buff *skb,
+                                        struct genl_info *info)
+{
+       void *hdr;
+       struct sk_buff *msg;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+                            NL80211_CMD_GET_PROTOCOL_FEATURES);
+       if (!hdr)
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, NL80211_ATTR_PROTOCOL_FEATURES,
+                       NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return genlmsg_reply(msg, info);
+
+ nla_put_failure:
+       kfree_skb(msg);
+       return -ENOBUFS;
+}
+
+static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct cfg80211_update_ft_ies_params ft_params;
+       struct net_device *dev = info->user_ptr[1];
+
+       if (!rdev->ops->update_ft_ies)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[NL80211_ATTR_MDID] ||
+           !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       memset(&ft_params, 0, sizeof(ft_params));
+       ft_params.md = nla_get_u16(info->attrs[NL80211_ATTR_MDID]);
+       ft_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+       ft_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+
+       return rdev_update_ft_ies(rdev, dev, &ft_params);
+}
+
 #define NL80211_FLAG_NEED_WIPHY                0x01
 #define NL80211_FLAG_NEED_NETDEV       0x02
 #define NL80211_FLAG_NEED_RTNL         0x04
@@ -8497,6 +8869,19 @@ static struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
+               .doit = nl80211_get_protocol_features,
+               .policy = nl80211_policy,
+       },
+       {
+               .cmd = NL80211_CMD_UPDATE_FT_IES,
+               .doit = nl80211_update_ft_ies,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -8524,7 +8909,8 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
        if (!msg)
                return;
 
-       if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
+       if (nl80211_send_wiphy(rdev, msg, 0, 0, 0,
+                              false, NULL, NULL, NULL) < 0) {
                nlmsg_free(msg);
                return;
        }
@@ -8848,21 +9234,31 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
                                NL80211_CMD_DISASSOCIATE, gfp);
 }
 
-void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
-                               struct net_device *netdev, const u8 *buf,
-                               size_t len, gfp_t gfp)
+void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
+                                size_t len)
 {
-       nl80211_send_mlme_event(rdev, netdev, buf, len,
-                               NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_send_unprot_deauth(dev);
+       nl80211_send_mlme_event(rdev, dev, buf, len,
+                               NL80211_CMD_UNPROT_DEAUTHENTICATE, GFP_ATOMIC);
 }
+EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
 
-void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
-                                 struct net_device *netdev, const u8 *buf,
-                                 size_t len, gfp_t gfp)
+void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
+                                  size_t len)
 {
-       nl80211_send_mlme_event(rdev, netdev, buf, len,
-                               NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_send_unprot_disassoc(dev);
+       nl80211_send_mlme_event(rdev, dev, buf, len,
+                               NL80211_CMD_UNPROT_DISASSOCIATE, GFP_ATOMIC);
 }
+EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
 
 static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
                                      struct net_device *netdev, int cmd,
@@ -9065,14 +9461,19 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
-               struct net_device *netdev,
-               const u8 *macaddr, const u8* ie, u8 ie_len,
-               gfp_t gfp)
+void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
+                                       const u8* ie, u8 ie_len, gfp_t gfp)
 {
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
        struct sk_buff *msg;
        void *hdr;
 
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT))
+               return;
+
+       trace_cfg80211_notify_new_peer_candidate(dev, addr);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9084,8 +9485,8 @@ void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
        }
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
-           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr) ||
+           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)))
                goto nla_put_failure;
@@ -9100,6 +9501,7 @@ void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate);
 
 void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
                                 struct net_device *netdev, const u8 *addr,
@@ -9168,7 +9570,7 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
        nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
        if (!nl_freq)
                goto nla_put_failure;
-       if (nl80211_msg_put_channel(msg, channel_before))
+       if (nl80211_msg_put_channel(msg, channel_before, false))
                goto nla_put_failure;
        nla_nest_end(msg, nl_freq);
 
@@ -9176,7 +9578,7 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
        nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
        if (!nl_freq)
                goto nla_put_failure;
-       if (nl80211_msg_put_channel(msg, channel_after))
+       if (nl80211_msg_put_channel(msg, channel_after, false))
                goto nla_put_failure;
        nla_nest_end(msg, nl_freq);
 
@@ -9238,31 +9640,42 @@ static void nl80211_send_remain_on_chan_event(
        nlmsg_free(msg);
 }
 
-void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
-                                   struct wireless_dev *wdev, u64 cookie,
-                                   struct ieee80211_channel *chan,
-                                   unsigned int duration, gfp_t gfp)
+void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
+                              struct ieee80211_channel *chan,
+                              unsigned int duration, gfp_t gfp)
 {
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);
        nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
                                          rdev, wdev, cookie, chan,
                                          duration, gfp);
 }
+EXPORT_SYMBOL(cfg80211_ready_on_channel);
 
-void nl80211_send_remain_on_channel_cancel(
-       struct cfg80211_registered_device *rdev,
-       struct wireless_dev *wdev,
-       u64 cookie, struct ieee80211_channel *chan, gfp_t gfp)
+void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
+                                       struct ieee80211_channel *chan,
+                                       gfp_t gfp)
 {
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);
        nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
                                          rdev, wdev, cookie, chan, 0, gfp);
 }
+EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
 
-void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
-                           struct net_device *dev, const u8 *mac_addr,
-                           struct station_info *sinfo, gfp_t gfp)
+void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
+                     struct station_info *sinfo, gfp_t gfp)
 {
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
 
+       trace_cfg80211_new_sta(dev, mac_addr, sinfo);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9276,14 +9689,17 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
        genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
                                nl80211_mlme_mcgrp.id, gfp);
 }
+EXPORT_SYMBOL(cfg80211_new_sta);
 
-void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
-                               struct net_device *dev, const u8 *mac_addr,
-                               gfp_t gfp)
+void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
 {
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        void *hdr;
 
+       trace_cfg80211_del_sta(dev, mac_addr);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9308,12 +9724,14 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_del_sta);
 
-void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
-                                   struct net_device *dev, const u8 *mac_addr,
-                                   enum nl80211_connect_failed_reason reason,
-                                   gfp_t gfp)
+void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
+                         enum nl80211_connect_failed_reason reason,
+                         gfp_t gfp)
 {
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        void *hdr;
 
@@ -9342,6 +9760,7 @@ void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_conn_failed);
 
 static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
                                       const u8 *addr, gfp_t gfp)
@@ -9386,19 +9805,47 @@ static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
        return true;
 }
 
-bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp)
+bool cfg80211_rx_spurious_frame(struct net_device *dev,
+                               const u8 *addr, gfp_t gfp)
 {
-       return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
-                                         addr, gfp);
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       bool ret;
+
+       trace_cfg80211_rx_spurious_frame(dev, addr);
+
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO)) {
+               trace_cfg80211_return_bool(false);
+               return false;
+       }
+       ret = __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
+                                        addr, gfp);
+       trace_cfg80211_return_bool(ret);
+       return ret;
 }
+EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
 
-bool nl80211_unexpected_4addr_frame(struct net_device *dev,
-                                   const u8 *addr, gfp_t gfp)
+bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
+                                       const u8 *addr, gfp_t gfp)
 {
-       return __nl80211_unexpected_frame(dev,
-                                         NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
-                                         addr, gfp);
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       bool ret;
+
+       trace_cfg80211_rx_unexpected_4addr_frame(dev, addr);
+
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO &&
+                   wdev->iftype != NL80211_IFTYPE_AP_VLAN)) {
+               trace_cfg80211_return_bool(false);
+               return false;
+       }
+       ret = __nl80211_unexpected_frame(dev,
+                                        NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
+                                        addr, gfp);
+       trace_cfg80211_return_bool(ret);
+       return ret;
 }
+EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
 
 int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
                      struct wireless_dev *wdev, u32 nlportid,
@@ -9438,15 +9885,17 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
        return -ENOBUFS;
 }
 
-void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
-                                struct wireless_dev *wdev, u64 cookie,
-                                const u8 *buf, size_t len, bool ack,
-                                gfp_t gfp)
+void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
+                            const u8 *buf, size_t len, bool ack, gfp_t gfp)
 {
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct net_device *netdev = wdev->netdev;
        struct sk_buff *msg;
        void *hdr;
 
+       trace_cfg80211_mgmt_tx_status(wdev, cookie, ack);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9474,17 +9923,21 @@ void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
 
-void
-nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
-                            struct net_device *netdev,
-                            enum nl80211_cqm_rssi_threshold_event rssi_event,
-                            gfp_t gfp)
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
+                             enum nl80211_cqm_rssi_threshold_event rssi_event,
+                             gfp_t gfp)
 {
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        struct nlattr *pinfoattr;
        void *hdr;
 
+       trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9496,7 +9949,7 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
        }
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
                goto nla_put_failure;
 
        pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
@@ -9519,10 +9972,11 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
 
-void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
-                             struct net_device *netdev, const u8 *bssid,
-                             const u8 *replay_ctr, gfp_t gfp)
+static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
+                                    struct net_device *netdev, const u8 *bssid,
+                                    const u8 *replay_ctr, gfp_t gfp)
 {
        struct sk_buff *msg;
        struct nlattr *rekey_attr;
@@ -9564,9 +10018,22 @@ void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
-                                   struct net_device *netdev, int index,
-                                   const u8 *bssid, bool preauth, gfp_t gfp)
+void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
+                              const u8 *replay_ctr, gfp_t gfp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_gtk_rekey_notify(dev, bssid);
+       nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
+}
+EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);
+
+static void
+nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
+                              struct net_device *netdev, int index,
+                              const u8 *bssid, bool preauth, gfp_t gfp)
 {
        struct sk_buff *msg;
        struct nlattr *attr;
@@ -9609,9 +10076,22 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
-                             struct net_device *netdev,
-                             struct cfg80211_chan_def *chandef, gfp_t gfp)
+void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
+                                    const u8 *bssid, bool preauth, gfp_t gfp)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth);
+       nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
+}
+EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
+
+static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
+                                    struct net_device *netdev,
+                                    struct cfg80211_chan_def *chandef,
+                                    gfp_t gfp)
 {
        struct sk_buff *msg;
        void *hdr;
@@ -9643,11 +10123,36 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void
-nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
-                           struct net_device *netdev, const u8 *peer,
-                           u32 num_packets, u32 rate, u32 intvl, gfp_t gfp)
+void cfg80211_ch_switch_notify(struct net_device *dev,
+                              struct cfg80211_chan_def *chandef)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       trace_cfg80211_ch_switch_notify(dev, chandef);
+
+       wdev_lock(wdev);
+
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO))
+               goto out;
+
+       wdev->channel = chandef->chan;
+       nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
+out:
+       wdev_unlock(wdev);
+       return;
+}
+EXPORT_SYMBOL(cfg80211_ch_switch_notify);
+
+void cfg80211_cqm_txe_notify(struct net_device *dev,
+                            const u8 *peer, u32 num_packets,
+                            u32 rate, u32 intvl, gfp_t gfp)
 {
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        struct nlattr *pinfoattr;
        void *hdr;
@@ -9663,7 +10168,7 @@ nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
        }
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
                goto nla_put_failure;
 
@@ -9692,6 +10197,7 @@ nl80211_send_cqm_txe_notify(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
 
 void
 nl80211_radar_notify(struct cfg80211_registered_device *rdev,
@@ -9744,15 +10250,18 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
-void
-nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
-                               struct net_device *netdev, const u8 *peer,
-                               u32 num_packets, gfp_t gfp)
+void cfg80211_cqm_pktloss_notify(struct net_device *dev,
+                                const u8 *peer, u32 num_packets, gfp_t gfp)
 {
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
        struct sk_buff *msg;
        struct nlattr *pinfoattr;
        void *hdr;
 
+       trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
+
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
        if (!msg)
                return;
@@ -9764,7 +10273,7 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
        }
 
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
-           nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
            nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
                goto nla_put_failure;
 
@@ -9787,6 +10296,7 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
        genlmsg_cancel(msg, hdr);
        nlmsg_free(msg);
 }
+EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
 
 void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
                           u64 cookie, bool acked, gfp_t gfp)
@@ -10073,6 +10583,50 @@ static struct notifier_block nl80211_netlink_notifier = {
        .notifier_call = nl80211_netlink_notify,
 };
 
+void cfg80211_ft_event(struct net_device *netdev,
+                      struct cfg80211_ft_event_params *ft_event)
+{
+       struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct sk_buff *msg;
+       void *hdr;
+       int err;
+
+       trace_cfg80211_ft_event(wiphy, netdev, ft_event);
+
+       if (!ft_event->target_ap)
+               return;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FT_EVENT);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+       nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, ft_event->target_ap);
+       if (ft_event->ies)
+               nla_put(msg, NL80211_ATTR_IE, ft_event->ies_len, ft_event->ies);
+       if (ft_event->ric_ies)
+               nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
+                       ft_event->ric_ies);
+
+       err = genlmsg_end(msg, hdr);
+       if (err < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, GFP_KERNEL);
+}
+EXPORT_SYMBOL(cfg80211_ft_event);
+
 /* initialisation/exit functions */
 
 int nl80211_init(void)