]> Pileus Git - ~andy/linux/commitdiff
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac802...
authorJohn W. Linville <linville@tuxdriver.com>
Mon, 13 Jan 2014 19:40:59 +0000 (14:40 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 13 Jan 2014 19:40:59 +0000 (14:40 -0500)
39 files changed:
drivers/net/wireless/adm8211.c
drivers/net/wireless/at76c50x-usb.c
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath9k/htc_drv_init.c
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/carl9170/main.c
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/cw1200/main.c
drivers/net/wireless/libertas/cfg.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mac80211_hwsim.h
drivers/net/wireless/mwl8k.c
drivers/net/wireless/p54/main.c
drivers/net/wireless/rtlwifi/base.c
drivers/net/wireless/ti/wl1251/main.c
drivers/net/wireless/ti/wlcore/main.c
drivers/staging/winbond/wbusb.c
include/linux/ieee80211.h
include/net/cfg80211.h
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/debugfs_netdev.c
net/mac80211/ht.c
net/mac80211/ieee80211_i.h
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh_plink.c
net/mac80211/rx.c
net/mac80211/sta_info.c
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/wpa.c
net/wireless/nl80211.c
net/wireless/scan.c
net/wireless/sme.c
net/wireless/util.c
net/wireless/wext-compat.c

index e888f1893179d439784e9b87f51ab61703df4205..84e57230b31a4d0a8e134ff0c6d82fb4327714d4 100644 (file)
@@ -1865,7 +1865,6 @@ static int adm8211_probe(struct pci_dev *pdev,
        dev->flags = IEEE80211_HW_SIGNAL_UNSPEC;
        dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
 
-       dev->channel_change_time = 1000;
        dev->max_signal = 100;    /* FIXME: find better value */
 
        dev->queues = 1; /* ADM8211C supports more, maybe ADM8211B too */
index 031d4ec647792840ad5238993ac79491eb79b7ec..99b3bfa717d55403b45effb585b0293b46f03ced 100644 (file)
@@ -2112,7 +2112,6 @@ static struct at76_priv *at76_alloc_new_device(struct usb_device *udev)
        priv->pm_period = 0;
 
        /* unit us */
-       priv->hw->channel_change_time = 100000;
 
        return priv;
 }
index 7aa6c4d702d6da4a9796899f0a1f1ea2fed0fdcc..776e364eadcd76c82ed37a64e20601f5bb1926ab 100644 (file)
@@ -4039,7 +4039,6 @@ int ath10k_mac_register(struct ath10k *ar)
 
        ar->hw->vif_data_size = sizeof(struct ath10k_vif);
 
-       ar->hw->channel_change_time = 5000;
        ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
 
        ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
index 6396ad4bce67c8f005ec5373bd214e0d7c30712b..d85c312170bc7bd6c14d4f8f3add807bcc127417 100644 (file)
@@ -2549,7 +2549,6 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops)
        hw->wiphy->available_antennas_rx = 0x3;
 
        hw->extra_tx_headroom = 2;
-       hw->channel_change_time = 5000;
 
        /*
         * Mark the device as detached to avoid processing
index b576c44bb31499a8e33c2f17cf9049f7ef205bdf..f4e1de20d99c02a246b08bfed883acc6418af40e 100644 (file)
@@ -748,7 +748,6 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
                            WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
 
        hw->queues = 4;
-       hw->channel_change_time = 5000;
        hw->max_listen_interval = 1;
 
        hw->vif_data_size = sizeof(struct ath9k_htc_vif);
index f2a17fcf1ae412834bd7c0c0b16471429b007e59..c36de303c8f38d9545ecdb74b575e176ec1b1d25 100644 (file)
@@ -946,7 +946,6 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 
        hw->queues = 4;
        hw->max_rates = 4;
-       hw->channel_change_time = 5000;
        hw->max_listen_interval = 1;
        hw->max_rate_tries = 10;
        hw->sta_data_size = sizeof(struct ath_node);
index 4c3f576c3144befb6173e9ad9bd5b8487db6dc32..4c8cdb097b6599c08816a4e184537d764bc9ef43 100644 (file)
@@ -1967,18 +1967,6 @@ static int carl9170_parse_eeprom(struct ar9170 *ar)
                return -ENOMEM;
        ar->num_channels = chans;
 
-       /*
-        * I measured this, a bandswitch takes roughly
-        * 135 ms and a frequency switch about 80.
-        *
-        * FIXME: measure these values again once EEPROM settings
-        *        are used, that will influence them!
-        */
-       if (bands == 2)
-               ar->hw->channel_change_time = 135 * 1000;
-       else
-               ar->hw->channel_change_time = 80 * 1000;
-
        regulatory->current_rd = le16_to_cpu(ar->eeprom.reg_domain[0]);
 
        /* second part of wiphy init */
index e71ce8c842a22f3a337cf1bbf4a08dc0e3e52b70..925034b80e9cf68f14c8af4fca29b8aafdeda433 100644 (file)
@@ -1071,7 +1071,6 @@ static int ieee_hw_init(struct ieee80211_hw *hw)
        hw->max_rates = 2;      /* Primary rate and 1 fallback rate */
 
        /* channel change time is dependent on chip and band  */
-       hw->channel_change_time = 7 * 1000;
        hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
                                     BIT(NL80211_IFTYPE_AP) |
                                     BIT(NL80211_IFTYPE_ADHOC);
index d1270da4dfeae0ee869de0a9f3d612a9c7ac757f..3e78cc3ccb78390593fc4365661ddd31e10ba166 100644 (file)
@@ -301,7 +301,6 @@ static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr,
 
        hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
 
-       hw->channel_change_time = 1000; /* TODO: find actual value */
        hw->queues = 4;
 
        priv->rts_threshold = -1;
index 116f4aba08d6f629275b3c29a0b98869191e1334..32f75007a825be6c24b6195d35a9f4e31d590961 100644 (file)
@@ -1268,14 +1268,9 @@ static struct cfg80211_scan_request *
 _new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme)
 {
        struct cfg80211_scan_request *creq = NULL;
-       int i, n_channels = 0;
+       int i, n_channels = ieee80211_get_num_supported_channels(wiphy);
        enum ieee80211_band band;
 
-       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
-               if (wiphy->bands[band])
-                       n_channels += wiphy->bands[band]->n_channels;
-       }
-
        creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
                       n_channels * sizeof(void *),
                       GFP_ATOMIC);
index 9c0cc8ded0216de43d09300dbed19cfa9fe2e3b3..dc7f72e3a4e76747d7c01cb4d26d76dbd7002cd1 100644 (file)
@@ -163,6 +163,11 @@ static const struct ieee80211_regdomain hwsim_world_regdom_custom_02 = {
        }
 };
 
+static const struct ieee80211_regdomain *hwsim_world_regdom_custom[] = {
+       &hwsim_world_regdom_custom_01,
+       &hwsim_world_regdom_custom_02,
+};
+
 struct hwsim_vif_priv {
        u32 magic;
        u8 bssid[ETH_ALEN];
@@ -321,8 +326,52 @@ static const struct ieee80211_rate hwsim_rates[] = {
        { .bitrate = 540 }
 };
 
+static const struct ieee80211_iface_limit hwsim_if_limits[] = {
+       { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) },
+       { .max = 2048,  .types = BIT(NL80211_IFTYPE_STATION) |
+                                BIT(NL80211_IFTYPE_P2P_CLIENT) |
+#ifdef CONFIG_MAC80211_MESH
+                                BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+                                BIT(NL80211_IFTYPE_AP) |
+                                BIT(NL80211_IFTYPE_P2P_GO) },
+       { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) },
+};
+
+static const struct ieee80211_iface_limit hwsim_if_dfs_limits[] = {
+       { .max = 8, .types = BIT(NL80211_IFTYPE_AP) },
+};
+
+static const struct ieee80211_iface_combination hwsim_if_comb[] = {
+       {
+               .limits = hwsim_if_limits,
+               .n_limits = ARRAY_SIZE(hwsim_if_limits),
+               .max_interfaces = 2048,
+               .num_different_channels = 1,
+       },
+       {
+               .limits = hwsim_if_dfs_limits,
+               .n_limits = ARRAY_SIZE(hwsim_if_dfs_limits),
+               .max_interfaces = 8,
+               .num_different_channels = 1,
+               .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+                                      BIT(NL80211_CHAN_WIDTH_20) |
+                                      BIT(NL80211_CHAN_WIDTH_40) |
+                                      BIT(NL80211_CHAN_WIDTH_80) |
+                                      BIT(NL80211_CHAN_WIDTH_160),
+       }
+};
+
 static spinlock_t hwsim_radio_lock;
 static struct list_head hwsim_radios;
+static int hwsim_radio_idx;
+
+static struct platform_driver mac80211_hwsim_driver = {
+       .driver = {
+               .name = "mac80211_hwsim",
+               .owner = THIS_MODULE,
+       },
+};
 
 struct mac80211_hwsim_data {
        struct list_head list;
@@ -332,8 +381,10 @@ struct mac80211_hwsim_data {
        struct ieee80211_channel channels_2ghz[ARRAY_SIZE(hwsim_channels_2ghz)];
        struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)];
        struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)];
+       struct ieee80211_iface_combination if_combination;
 
        struct mac_address addresses[2];
+       int channels, idx;
 
        struct ieee80211_channel *tmp_chan;
        struct delayed_work roc_done;
@@ -401,21 +452,179 @@ static struct genl_family hwsim_genl_family = {
 /* MAC80211_HWSIM netlink policy */
 
 static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
-       [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC,
-                                      .len = 6*sizeof(u8) },
-       [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC,
-                                         .len = 6*sizeof(u8) },
+       [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC, .len = ETH_ALEN },
+       [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC, .len = ETH_ALEN },
        [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
                               .len = IEEE80211_MAX_DATA_LEN },
        [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
        [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 },
        [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
        [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC,
-                                .len = IEEE80211_TX_MAX_RATES*sizeof(
-                                       struct hwsim_tx_rate)},
+                                .len = IEEE80211_TX_MAX_RATES *
+                                       sizeof(struct hwsim_tx_rate)},
        [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 },
+       [HWSIM_ATTR_CHANNELS] = { .type = NLA_U32 },
+       [HWSIM_ATTR_RADIO_ID] = { .type = NLA_U32 },
+       [HWSIM_ATTR_REG_HINT_ALPHA2] = { .type = NLA_STRING, .len = 2 },
+       [HWSIM_ATTR_REG_CUSTOM_REG] = { .type = NLA_U32 },
+       [HWSIM_ATTR_REG_STRICT_REG] = { .type = NLA_FLAG },
 };
 
+static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
+                                   struct sk_buff *skb,
+                                   struct ieee80211_channel *chan);
+
+/* sysfs attributes */
+static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
+{
+       struct mac80211_hwsim_data *data = dat;
+       struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
+       struct sk_buff *skb;
+       struct ieee80211_pspoll *pspoll;
+
+       if (!vp->assoc)
+               return;
+
+       wiphy_debug(data->hw->wiphy,
+                   "%s: send PS-Poll to %pM for aid %d\n",
+                   __func__, vp->bssid, vp->aid);
+
+       skb = dev_alloc_skb(sizeof(*pspoll));
+       if (!skb)
+               return;
+       pspoll = (void *) skb_put(skb, sizeof(*pspoll));
+       pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
+                                           IEEE80211_STYPE_PSPOLL |
+                                           IEEE80211_FCTL_PM);
+       pspoll->aid = cpu_to_le16(0xc000 | vp->aid);
+       memcpy(pspoll->bssid, vp->bssid, ETH_ALEN);
+       memcpy(pspoll->ta, mac, ETH_ALEN);
+
+       rcu_read_lock();
+       mac80211_hwsim_tx_frame(data->hw, skb,
+                               rcu_dereference(vif->chanctx_conf)->def.chan);
+       rcu_read_unlock();
+}
+
+static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
+                               struct ieee80211_vif *vif, int ps)
+{
+       struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
+       struct sk_buff *skb;
+       struct ieee80211_hdr *hdr;
+
+       if (!vp->assoc)
+               return;
+
+       wiphy_debug(data->hw->wiphy,
+                   "%s: send data::nullfunc to %pM ps=%d\n",
+                   __func__, vp->bssid, ps);
+
+       skb = dev_alloc_skb(sizeof(*hdr));
+       if (!skb)
+               return;
+       hdr = (void *) skb_put(skb, sizeof(*hdr) - ETH_ALEN);
+       hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+                                        IEEE80211_STYPE_NULLFUNC |
+                                        (ps ? IEEE80211_FCTL_PM : 0));
+       hdr->duration_id = cpu_to_le16(0);
+       memcpy(hdr->addr1, vp->bssid, ETH_ALEN);
+       memcpy(hdr->addr2, mac, ETH_ALEN);
+       memcpy(hdr->addr3, vp->bssid, ETH_ALEN);
+
+       rcu_read_lock();
+       mac80211_hwsim_tx_frame(data->hw, skb,
+                               rcu_dereference(vif->chanctx_conf)->def.chan);
+       rcu_read_unlock();
+}
+
+
+static void hwsim_send_nullfunc_ps(void *dat, u8 *mac,
+                                  struct ieee80211_vif *vif)
+{
+       struct mac80211_hwsim_data *data = dat;
+       hwsim_send_nullfunc(data, mac, vif, 1);
+}
+
+static void hwsim_send_nullfunc_no_ps(void *dat, u8 *mac,
+                                     struct ieee80211_vif *vif)
+{
+       struct mac80211_hwsim_data *data = dat;
+       hwsim_send_nullfunc(data, mac, vif, 0);
+}
+
+static int hwsim_fops_ps_read(void *dat, u64 *val)
+{
+       struct mac80211_hwsim_data *data = dat;
+       *val = data->ps;
+       return 0;
+}
+
+static int hwsim_fops_ps_write(void *dat, u64 val)
+{
+       struct mac80211_hwsim_data *data = dat;
+       enum ps_mode old_ps;
+
+       if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL &&
+           val != PS_MANUAL_POLL)
+               return -EINVAL;
+
+       old_ps = data->ps;
+       data->ps = val;
+
+       if (val == PS_MANUAL_POLL) {
+               ieee80211_iterate_active_interfaces(data->hw,
+                                                   IEEE80211_IFACE_ITER_NORMAL,
+                                                   hwsim_send_ps_poll, data);
+               data->ps_poll_pending = true;
+       } else if (old_ps == PS_DISABLED && val != PS_DISABLED) {
+               ieee80211_iterate_active_interfaces(data->hw,
+                                                   IEEE80211_IFACE_ITER_NORMAL,
+                                                   hwsim_send_nullfunc_ps,
+                                                   data);
+       } else if (old_ps != PS_DISABLED && val == PS_DISABLED) {
+               ieee80211_iterate_active_interfaces(data->hw,
+                                                   IEEE80211_IFACE_ITER_NORMAL,
+                                                   hwsim_send_nullfunc_no_ps,
+                                                   data);
+       }
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write,
+                       "%llu\n");
+
+static int hwsim_write_simulate_radar(void *dat, u64 val)
+{
+       struct mac80211_hwsim_data *data = dat;
+
+       ieee80211_radar_detected(data->hw);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(hwsim_simulate_radar, NULL,
+                       hwsim_write_simulate_radar, "%llu\n");
+
+static int hwsim_fops_group_read(void *dat, u64 *val)
+{
+       struct mac80211_hwsim_data *data = dat;
+       *val = data->group;
+       return 0;
+}
+
+static int hwsim_fops_group_write(void *dat, u64 val)
+{
+       struct mac80211_hwsim_data *data = dat;
+       data->group = val;
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
+                       hwsim_fops_group_read, hwsim_fops_group_write,
+                       "%llx\n");
+
 static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb,
                                        struct net_device *dev)
 {
@@ -639,7 +848,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
        }
 
        if (nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER,
-                   sizeof(struct mac_address), data->addresses[1].addr))
+                   ETH_ALEN, data->addresses[1].addr))
                goto nla_put_failure;
 
        /* We get the skb->data */
@@ -878,7 +1087,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
                return;
        }
 
-       if (channels == 1) {
+       if (data->channels == 1) {
                channel = data->channel;
        } else if (txi->hw_queue == 4) {
                channel = data->tmp_chan;
@@ -906,7 +1115,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
        if (control->sta)
                hwsim_check_sta_magic(control->sta);
 
-       if (rctbl)
+       if (hw->flags & IEEE80211_HW_SUPPORTS_RC_TABLE)
                ieee80211_get_tx_rates(txi->control.vif, control->sta, skb,
                                       txi->control.rates,
                                       ARRAY_SIZE(txi->control.rates));
@@ -1013,7 +1222,7 @@ static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
 {
        u32 _pid = ACCESS_ONCE(wmediumd_portid);
 
-       if (rctbl) {
+       if (hw->flags & IEEE80211_HW_SUPPORTS_RC_TABLE) {
                struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
                ieee80211_get_tx_rates(txi->control.vif, NULL, skb,
                                       txi->control.rates,
@@ -1050,7 +1259,7 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
        if (skb == NULL)
                return;
        info = IEEE80211_SKB_CB(skb);
-       if (rctbl)
+       if (hw->flags & IEEE80211_HW_SUPPORTS_RC_TABLE)
                ieee80211_get_tx_rates(vif, NULL, skb,
                                       info->control.rates,
                                       ARRAY_SIZE(info->control.rates));
@@ -1141,7 +1350,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
 
        data->channel = conf->chandef.chan;
 
-       WARN_ON(data->channel && channels > 1);
+       WARN_ON(data->channel && data->channels > 1);
 
        data->power_level = conf->power_level;
        if (!data->started || !data->beacon_int)
@@ -1388,8 +1597,6 @@ static const struct nla_policy hwsim_testmode_policy[HWSIM_TM_ATTR_MAX + 1] = {
        [HWSIM_TM_ATTR_PS] = { .type = NLA_U32 },
 };
 
-static int hwsim_fops_ps_write(void *dat, u64 val);
-
 static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw,
                                       struct ieee80211_vif *vif,
                                       void *data, int len)
@@ -1700,8 +1907,7 @@ static void mac80211_hwsim_unassign_vif_chanctx(struct ieee80211_hw *hw,
        hwsim_check_chanctx_magic(ctx);
 }
 
-static struct ieee80211_ops mac80211_hwsim_ops =
-{
+static const struct ieee80211_ops mac80211_hwsim_ops = {
        .tx = mac80211_hwsim_tx,
        .start = mac80211_hwsim_start,
        .stop = mac80211_hwsim_stop,
@@ -1726,217 +1932,290 @@ static struct ieee80211_ops mac80211_hwsim_ops =
        .set_tsf = mac80211_hwsim_set_tsf,
 };
 
+static struct ieee80211_ops mac80211_hwsim_mchan_ops;
 
-static void mac80211_hwsim_free(void)
+static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
+                                      const struct ieee80211_regdomain *regd,
+                                      bool reg_strict)
 {
-       struct list_head tmplist, *i, *tmp;
-       struct mac80211_hwsim_data *data, *tmpdata;
-
-       INIT_LIST_HEAD(&tmplist);
+       int err;
+       u8 addr[ETH_ALEN];
+       struct mac80211_hwsim_data *data;
+       struct ieee80211_hw *hw;
+       enum ieee80211_band band;
+       const struct ieee80211_ops *ops = &mac80211_hwsim_ops;
+       int idx;
 
        spin_lock_bh(&hwsim_radio_lock);
-       list_for_each_safe(i, tmp, &hwsim_radios)
-               list_move(i, &tmplist);
+       idx = hwsim_radio_idx++;
        spin_unlock_bh(&hwsim_radio_lock);
 
-       list_for_each_entry_safe(data, tmpdata, &tmplist, list) {
-               debugfs_remove_recursive(data->debugfs);
-               ieee80211_unregister_hw(data->hw);
-               device_release_driver(data->dev);
-               device_unregister(data->dev);
-               ieee80211_free_hw(data->hw);
+       if (channels > 1)
+               ops = &mac80211_hwsim_mchan_ops;
+       hw = ieee80211_alloc_hw(sizeof(*data), ops);
+       if (!hw) {
+               printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw failed\n");
+               err = -ENOMEM;
+               goto failed;
+       }
+       data = hw->priv;
+       data->hw = hw;
+
+       data->dev = device_create(hwsim_class, NULL, 0, hw, "hwsim%d", idx);
+       if (IS_ERR(data->dev)) {
+               printk(KERN_DEBUG
+                      "mac80211_hwsim: device_create failed (%ld)\n",
+                      PTR_ERR(data->dev));
+               err = -ENOMEM;
+               goto failed_drvdata;
+       }
+       data->dev->driver = &mac80211_hwsim_driver.driver;
+       err = device_bind_driver(data->dev);
+       if (err != 0) {
+               printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n",
+                      err);
+               goto failed_hw;
        }
-       class_destroy(hwsim_class);
-}
 
-static struct platform_driver mac80211_hwsim_driver = {
-       .driver = {
-               .name = "mac80211_hwsim",
-               .owner = THIS_MODULE,
-       },
-};
+       skb_queue_head_init(&data->pending);
 
-static const struct net_device_ops hwsim_netdev_ops = {
-       .ndo_start_xmit         = hwsim_mon_xmit,
-       .ndo_change_mtu         = eth_change_mtu,
-       .ndo_set_mac_address    = eth_mac_addr,
-       .ndo_validate_addr      = eth_validate_addr,
-};
+       SET_IEEE80211_DEV(hw, data->dev);
+       memset(addr, 0, ETH_ALEN);
+       addr[0] = 0x02;
+       addr[3] = idx >> 8;
+       addr[4] = idx;
+       memcpy(data->addresses[0].addr, addr, ETH_ALEN);
+       memcpy(data->addresses[1].addr, addr, ETH_ALEN);
+       data->addresses[1].addr[0] |= 0x40;
+       hw->wiphy->n_addresses = 2;
+       hw->wiphy->addresses = data->addresses;
+
+       data->channels = channels;
+       data->idx = idx;
+
+       if (data->channels > 1) {
+               hw->wiphy->max_scan_ssids = 255;
+               hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+               hw->wiphy->max_remain_on_channel_duration = 1000;
+               /* For channels > 1 DFS is not allowed */
+               hw->wiphy->n_iface_combinations = 1;
+               hw->wiphy->iface_combinations = &data->if_combination;
+               data->if_combination = hwsim_if_comb[0];
+               data->if_combination.num_different_channels = data->channels;
+       } else {
+               hw->wiphy->iface_combinations = hwsim_if_comb;
+               hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb);
+       }
 
-static void hwsim_mon_setup(struct net_device *dev)
-{
-       dev->netdev_ops = &hwsim_netdev_ops;
-       dev->destructor = free_netdev;
-       ether_setup(dev);
-       dev->tx_queue_len = 0;
-       dev->type = ARPHRD_IEEE80211_RADIOTAP;
-       memset(dev->dev_addr, 0, ETH_ALEN);
-       dev->dev_addr[0] = 0x12;
-}
+       INIT_DELAYED_WORK(&data->roc_done, hw_roc_done);
+       INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work);
+
+       hw->queues = 5;
+       hw->offchannel_tx_hw_queue = 4;
+       hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+                                    BIT(NL80211_IFTYPE_AP) |
+                                    BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                                    BIT(NL80211_IFTYPE_P2P_GO) |
+                                    BIT(NL80211_IFTYPE_ADHOC) |
+                                    BIT(NL80211_IFTYPE_MESH_POINT) |
+                                    BIT(NL80211_IFTYPE_P2P_DEVICE);
+
+       hw->flags = IEEE80211_HW_MFP_CAPABLE |
+                   IEEE80211_HW_SIGNAL_DBM |
+                   IEEE80211_HW_SUPPORTS_STATIC_SMPS |
+                   IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
+                   IEEE80211_HW_AMPDU_AGGREGATION |
+                   IEEE80211_HW_WANT_MONITOR_VIF |
+                   IEEE80211_HW_QUEUE_CONTROL |
+                   IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
+       if (rctbl)
+               hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE;
+
+       hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
+                           WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+                           WIPHY_FLAG_AP_UAPSD;
+       hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
+
+       /* ask mac80211 to reserve space for magic */
+       hw->vif_data_size = sizeof(struct hwsim_vif_priv);
+       hw->sta_data_size = sizeof(struct hwsim_sta_priv);
+       hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv);
+
+       memcpy(data->channels_2ghz, hwsim_channels_2ghz,
+               sizeof(hwsim_channels_2ghz));
+       memcpy(data->channels_5ghz, hwsim_channels_5ghz,
+               sizeof(hwsim_channels_5ghz));
+       memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates));
+
+       for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
+               struct ieee80211_supported_band *sband = &data->bands[band];
+               switch (band) {
+               case IEEE80211_BAND_2GHZ:
+                       sband->channels = data->channels_2ghz;
+                       sband->n_channels = ARRAY_SIZE(hwsim_channels_2ghz);
+                       sband->bitrates = data->rates;
+                       sband->n_bitrates = ARRAY_SIZE(hwsim_rates);
+                       break;
+               case IEEE80211_BAND_5GHZ:
+                       sband->channels = data->channels_5ghz;
+                       sband->n_channels = ARRAY_SIZE(hwsim_channels_5ghz);
+                       sband->bitrates = data->rates + 4;
+                       sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4;
+                       break;
+               default:
+                       continue;
+               }
 
+               sband->ht_cap.ht_supported = true;
+               sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+                                   IEEE80211_HT_CAP_GRN_FLD |
+                                   IEEE80211_HT_CAP_SGI_40 |
+                                   IEEE80211_HT_CAP_DSSSCCK40;
+               sband->ht_cap.ampdu_factor = 0x3;
+               sband->ht_cap.ampdu_density = 0x6;
+               memset(&sband->ht_cap.mcs, 0,
+                      sizeof(sband->ht_cap.mcs));
+               sband->ht_cap.mcs.rx_mask[0] = 0xff;
+               sband->ht_cap.mcs.rx_mask[1] = 0xff;
+               sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+
+               hw->wiphy->bands[band] = sband;
+
+               sband->vht_cap.vht_supported = true;
+               sband->vht_cap.cap =
+                       IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
+                       IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ |
+                       IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
+                       IEEE80211_VHT_CAP_RXLDPC |
+                       IEEE80211_VHT_CAP_SHORT_GI_80 |
+                       IEEE80211_VHT_CAP_SHORT_GI_160 |
+                       IEEE80211_VHT_CAP_TXSTBC |
+                       IEEE80211_VHT_CAP_RXSTBC_1 |
+                       IEEE80211_VHT_CAP_RXSTBC_2 |
+                       IEEE80211_VHT_CAP_RXSTBC_3 |
+                       IEEE80211_VHT_CAP_RXSTBC_4 |
+                       IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+               sband->vht_cap.vht_mcs.rx_mcs_map =
+                       cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_8 << 0 |
+                                   IEEE80211_VHT_MCS_SUPPORT_0_8 << 2 |
+                                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 |
+                                   IEEE80211_VHT_MCS_SUPPORT_0_8 << 6 |
+                                   IEEE80211_VHT_MCS_SUPPORT_0_8 << 8 |
+                                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 |
+                                   IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 |
+                                   IEEE80211_VHT_MCS_SUPPORT_0_8 << 14);
+               sband->vht_cap.vht_mcs.tx_mcs_map =
+                       sband->vht_cap.vht_mcs.rx_mcs_map;
+       }
 
-static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
-{
-       struct mac80211_hwsim_data *data = dat;
-       struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
-       struct sk_buff *skb;
-       struct ieee80211_pspoll *pspoll;
+       /* By default all radios belong to the first group */
+       data->group = 1;
+       mutex_init(&data->mutex);
 
-       if (!vp->assoc)
-               return;
-
-       wiphy_debug(data->hw->wiphy,
-                   "%s: send PS-Poll to %pM for aid %d\n",
-                   __func__, vp->bssid, vp->aid);
-
-       skb = dev_alloc_skb(sizeof(*pspoll));
-       if (!skb)
-               return;
-       pspoll = (void *) skb_put(skb, sizeof(*pspoll));
-       pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL |
-                                           IEEE80211_STYPE_PSPOLL |
-                                           IEEE80211_FCTL_PM);
-       pspoll->aid = cpu_to_le16(0xc000 | vp->aid);
-       memcpy(pspoll->bssid, vp->bssid, ETH_ALEN);
-       memcpy(pspoll->ta, mac, ETH_ALEN);
-
-       rcu_read_lock();
-       mac80211_hwsim_tx_frame(data->hw, skb,
-                               rcu_dereference(vif->chanctx_conf)->def.chan);
-       rcu_read_unlock();
-}
+       /* Enable frame retransmissions for lossy channels */
+       hw->max_rates = 4;
+       hw->max_rate_tries = 11;
 
-static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
-                               struct ieee80211_vif *vif, int ps)
-{
-       struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
-       struct sk_buff *skb;
-       struct ieee80211_hdr *hdr;
+       if (reg_strict)
+               hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
+       if (regd) {
+               hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
+               wiphy_apply_custom_regulatory(hw->wiphy, regd);
+               /* give the regulatory workqueue a chance to run */
+               schedule_timeout_interruptible(1);
+       }
 
-       if (!vp->assoc)
-               return;
+       err = ieee80211_register_hw(hw);
+       if (err < 0) {
+               printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n",
+                      err);
+               goto failed_hw;
+       }
 
-       wiphy_debug(data->hw->wiphy,
-                   "%s: send data::nullfunc to %pM ps=%d\n",
-                   __func__, vp->bssid, ps);
+       wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr);
 
-       skb = dev_alloc_skb(sizeof(*hdr));
-       if (!skb)
-               return;
-       hdr = (void *) skb_put(skb, sizeof(*hdr) - ETH_ALEN);
-       hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
-                                        IEEE80211_STYPE_NULLFUNC |
-                                        (ps ? IEEE80211_FCTL_PM : 0));
-       hdr->duration_id = cpu_to_le16(0);
-       memcpy(hdr->addr1, vp->bssid, ETH_ALEN);
-       memcpy(hdr->addr2, mac, ETH_ALEN);
-       memcpy(hdr->addr3, vp->bssid, ETH_ALEN);
+       if (reg_alpha2)
+               regulatory_hint(hw->wiphy, reg_alpha2);
 
-       rcu_read_lock();
-       mac80211_hwsim_tx_frame(data->hw, skb,
-                               rcu_dereference(vif->chanctx_conf)->def.chan);
-       rcu_read_unlock();
-}
+       data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir);
+       debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps);
+       debugfs_create_file("group", 0666, data->debugfs, data,
+                           &hwsim_fops_group);
+       if (data->channels == 1)
+               debugfs_create_file("dfs_simulate_radar", 0222,
+                                   data->debugfs,
+                                   data, &hwsim_simulate_radar);
 
+       tasklet_hrtimer_init(&data->beacon_timer,
+                            mac80211_hwsim_beacon,
+                            CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS);
 
-static void hwsim_send_nullfunc_ps(void *dat, u8 *mac,
-                                  struct ieee80211_vif *vif)
-{
-       struct mac80211_hwsim_data *data = dat;
-       hwsim_send_nullfunc(data, mac, vif, 1);
-}
+       spin_lock_bh(&hwsim_radio_lock);
+       list_add_tail(&data->list, &hwsim_radios);
+       spin_unlock_bh(&hwsim_radio_lock);
 
+       return idx;
 
-static void hwsim_send_nullfunc_no_ps(void *dat, u8 *mac,
-                                     struct ieee80211_vif *vif)
-{
-       struct mac80211_hwsim_data *data = dat;
-       hwsim_send_nullfunc(data, mac, vif, 0);
+failed_hw:
+       device_unregister(data->dev);
+failed_drvdata:
+       ieee80211_free_hw(hw);
+failed:
+       return err;
 }
 
-
-static int hwsim_fops_ps_read(void *dat, u64 *val)
+static void mac80211_hwsim_destroy_radio(struct mac80211_hwsim_data *data)
 {
-       struct mac80211_hwsim_data *data = dat;
-       *val = data->ps;
-       return 0;
+       debugfs_remove_recursive(data->debugfs);
+       ieee80211_unregister_hw(data->hw);
+       device_release_driver(data->dev);
+       device_unregister(data->dev);
+       ieee80211_free_hw(data->hw);
 }
 
-static int hwsim_fops_ps_write(void *dat, u64 val)
+static void mac80211_hwsim_free(void)
 {
-       struct mac80211_hwsim_data *data = dat;
-       enum ps_mode old_ps;
-
-       if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL &&
-           val != PS_MANUAL_POLL)
-               return -EINVAL;
-
-       old_ps = data->ps;
-       data->ps = val;
+       struct mac80211_hwsim_data *data;
 
-       if (val == PS_MANUAL_POLL) {
-               ieee80211_iterate_active_interfaces(data->hw,
-                                                   IEEE80211_IFACE_ITER_NORMAL,
-                                                   hwsim_send_ps_poll, data);
-               data->ps_poll_pending = true;
-       } else if (old_ps == PS_DISABLED && val != PS_DISABLED) {
-               ieee80211_iterate_active_interfaces(data->hw,
-                                                   IEEE80211_IFACE_ITER_NORMAL,
-                                                   hwsim_send_nullfunc_ps,
-                                                   data);
-       } else if (old_ps != PS_DISABLED && val == PS_DISABLED) {
-               ieee80211_iterate_active_interfaces(data->hw,
-                                                   IEEE80211_IFACE_ITER_NORMAL,
-                                                   hwsim_send_nullfunc_no_ps,
-                                                   data);
+       spin_lock_bh(&hwsim_radio_lock);
+       while ((data = list_first_entry_or_null(&hwsim_radios,
+                                               struct mac80211_hwsim_data,
+                                               list))) {
+               list_del(&data->list);
+               spin_unlock_bh(&hwsim_radio_lock);
+               mac80211_hwsim_destroy_radio(data);
+               spin_lock_bh(&hwsim_radio_lock);
        }
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write,
-                       "%llu\n");
-
-static int hwsim_write_simulate_radar(void *dat, u64 val)
-{
-       struct mac80211_hwsim_data *data = dat;
-
-       ieee80211_radar_detected(data->hw);
-
-       return 0;
+       spin_unlock_bh(&hwsim_radio_lock);
+       class_destroy(hwsim_class);
 }
 
-DEFINE_SIMPLE_ATTRIBUTE(hwsim_simulate_radar, NULL,
-                       hwsim_write_simulate_radar, "%llu\n");
-
-static int hwsim_fops_group_read(void *dat, u64 *val)
-{
-       struct mac80211_hwsim_data *data = dat;
-       *val = data->group;
-       return 0;
-}
+static const struct net_device_ops hwsim_netdev_ops = {
+       .ndo_start_xmit         = hwsim_mon_xmit,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
 
-static int hwsim_fops_group_write(void *dat, u64 val)
+static void hwsim_mon_setup(struct net_device *dev)
 {
-       struct mac80211_hwsim_data *data = dat;
-       data->group = val;
-       return 0;
+       dev->netdev_ops = &hwsim_netdev_ops;
+       dev->destructor = free_netdev;
+       ether_setup(dev);
+       dev->tx_queue_len = 0;
+       dev->type = ARPHRD_IEEE80211_RADIOTAP;
+       memset(dev->dev_addr, 0, ETH_ALEN);
+       dev->dev_addr[0] = 0x12;
 }
 
-DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
-                       hwsim_fops_group_read, hwsim_fops_group_write,
-                       "%llx\n");
-
-static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
-                            struct mac_address *addr)
+static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(const u8 *addr)
 {
        struct mac80211_hwsim_data *data;
        bool _found = false;
 
        spin_lock_bh(&hwsim_radio_lock);
        list_for_each_entry(data, &hwsim_radios, list) {
-               if (memcmp(data->addresses[1].addr, addr,
-                         sizeof(struct mac_address)) == 0) {
+               if (memcmp(data->addresses[1].addr, addr, ETH_ALEN) == 0) {
                        _found = true;
                        break;
                }
@@ -1959,27 +2238,26 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
        struct hwsim_tx_rate *tx_attempts;
        unsigned long ret_skb_ptr;
        struct sk_buff *skb, *tmp;
-       struct mac_address *src;
+       const u8 *src;
        unsigned int hwsim_flags;
-
        int i;
        bool found = false;
 
+       if (info->snd_portid != wmediumd_portid)
+               return -EINVAL;
+
        if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] ||
-          !info->attrs[HWSIM_ATTR_FLAGS] ||
-          !info->attrs[HWSIM_ATTR_COOKIE] ||
-          !info->attrs[HWSIM_ATTR_TX_INFO])
+           !info->attrs[HWSIM_ATTR_FLAGS] ||
+           !info->attrs[HWSIM_ATTR_COOKIE] ||
+           !info->attrs[HWSIM_ATTR_TX_INFO])
                goto out;
 
-       src = (struct mac_address *)nla_data(
-                                  info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
+       src = (void *)nla_data(info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
        hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
-
        ret_skb_ptr = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]);
 
        data2 = get_hwsim_data_ref_from_addr(src);
-
-       if (data2 == NULL)
+       if (!data2)
                goto out;
 
        /* look for the skb matching the cookie passed back from user */
@@ -2036,38 +2314,37 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
 
        struct mac80211_hwsim_data *data2;
        struct ieee80211_rx_status rx_status;
-       struct mac_address *dst;
+       const u8 *dst;
        int frame_data_len;
-       char *frame_data;
+       void *frame_data;
        struct sk_buff *skb = NULL;
 
+       if (info->snd_portid != wmediumd_portid)
+               return -EINVAL;
+
        if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] ||
            !info->attrs[HWSIM_ATTR_FRAME] ||
            !info->attrs[HWSIM_ATTR_RX_RATE] ||
            !info->attrs[HWSIM_ATTR_SIGNAL])
                goto out;
 
-       dst = (struct mac_address *)nla_data(
-                                  info->attrs[HWSIM_ATTR_ADDR_RECEIVER]);
-
+       dst = (void *)nla_data(info->attrs[HWSIM_ATTR_ADDR_RECEIVER]);
        frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]);
-       frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]);
+       frame_data = (void *)nla_data(info->attrs[HWSIM_ATTR_FRAME]);
 
        /* Allocate new skb here */
        skb = alloc_skb(frame_data_len, GFP_KERNEL);
        if (skb == NULL)
                goto err;
 
-       if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
-               /* Copy the data */
-               memcpy(skb_put(skb, frame_data_len), frame_data,
-                      frame_data_len);
-       } else
+       if (frame_data_len > IEEE80211_MAX_DATA_LEN)
                goto err;
 
-       data2 = get_hwsim_data_ref_from_addr(dst);
+       /* Copy the data */
+       memcpy(skb_put(skb, frame_data_len), frame_data, frame_data_len);
 
-       if (data2 == NULL)
+       data2 = get_hwsim_data_ref_from_addr(dst);
+       if (!data2)
                goto out;
 
        /* check if radio is configured properly */
@@ -2075,7 +2352,7 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
        if (data2->idle || !data2->started)
                goto out;
 
-       /*A frame is received from user space*/
+       /* A frame is received from user space */
        memset(&rx_status, 0, sizeof(rx_status));
        rx_status.freq = data2->channel->center_freq;
        rx_status.band = data2->channel->band;
@@ -2097,8 +2374,24 @@ out:
 static int hwsim_register_received_nl(struct sk_buff *skb_2,
                                      struct genl_info *info)
 {
-       if (info == NULL)
-               goto out;
+       struct mac80211_hwsim_data *data;
+       int chans = 1;
+
+       spin_lock_bh(&hwsim_radio_lock);
+       list_for_each_entry(data, &hwsim_radios, list)
+               chans = max(chans, data->channels);
+       spin_unlock_bh(&hwsim_radio_lock);
+
+       /* In the future we should revise the userspace API and allow it
+        * to set a flag that it does support multi-channel, then we can
+        * let this pass conditionally on the flag.
+        * For current userspace, prohibit it since it won't work right.
+        */
+       if (chans > 1)
+               return -EOPNOTSUPP;
+
+       if (wmediumd_portid)
+               return -EBUSY;
 
        wmediumd_portid = info->snd_portid;
 
@@ -2106,9 +2399,53 @@ static int hwsim_register_received_nl(struct sk_buff *skb_2,
               "switching to wmediumd mode with pid %d\n", info->snd_portid);
 
        return 0;
-out:
-       printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
-       return -EINVAL;
+}
+
+static int hwsim_create_radio_nl(struct sk_buff *msg, struct genl_info *info)
+{
+       unsigned int chans = channels;
+       const char *alpha2 = NULL;
+       const struct ieee80211_regdomain *regd = NULL;
+       bool reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
+
+       if (info->attrs[HWSIM_ATTR_CHANNELS])
+               chans = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
+
+       if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2])
+               alpha2 = nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]);
+
+       if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) {
+               u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]);
+
+               if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom))
+                       return -EINVAL;
+               regd = hwsim_world_regdom_custom[idx];
+       }
+
+       return mac80211_hwsim_create_radio(chans, alpha2, regd, reg_strict);
+}
+
+static int hwsim_destroy_radio_nl(struct sk_buff *msg, struct genl_info *info)
+{
+       struct mac80211_hwsim_data *data;
+       int idx;
+
+       if (!info->attrs[HWSIM_ATTR_RADIO_ID])
+               return -EINVAL;
+       idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
+
+       spin_lock_bh(&hwsim_radio_lock);
+       list_for_each_entry(data, &hwsim_radios, list) {
+               if (data->idx != idx)
+                       continue;
+               list_del(&data->list);
+               spin_unlock_bh(&hwsim_radio_lock);
+               mac80211_hwsim_destroy_radio(data);
+               return 0;
+       }
+       spin_unlock_bh(&hwsim_radio_lock);
+
+       return -ENODEV;
 }
 
 /* Generic Netlink operations array */
@@ -2129,6 +2466,18 @@ static const struct genl_ops hwsim_ops[] = {
                .policy = hwsim_genl_policy,
                .doit = hwsim_tx_info_frame_received_nl,
        },
+       {
+               .cmd = HWSIM_CMD_CREATE_RADIO,
+               .policy = hwsim_genl_policy,
+               .doit = hwsim_create_radio_nl,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = HWSIM_CMD_DESTROY_RADIO,
+               .policy = hwsim_genl_policy,
+               .doit = hwsim_destroy_radio_nl,
+               .flags = GENL_ADMIN_PERM,
+       },
 };
 
 static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
@@ -2157,10 +2506,6 @@ static int hwsim_init_netlink(void)
 {
        int rc;
 
-       /* userspace test API hasn't been adjusted for multi-channel */
-       if (channels > 1)
-               return 0;
-
        printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
 
        rc = genl_register_family_with_ops(&hwsim_genl_family, hwsim_ops);
@@ -2180,94 +2525,36 @@ failure:
 
 static void hwsim_exit_netlink(void)
 {
-       int ret;
-
-       /* userspace test API hasn't been adjusted for multi-channel */
-       if (channels > 1)
-               return;
-
-       printk(KERN_INFO "mac80211_hwsim: closing netlink\n");
        /* unregister the notifier */
        netlink_unregister_notifier(&hwsim_netlink_notifier);
        /* unregister the family */
-       ret = genl_unregister_family(&hwsim_genl_family);
-       if (ret)
-               printk(KERN_DEBUG "mac80211_hwsim: "
-                      "unregister family %i\n", ret);
+       genl_unregister_family(&hwsim_genl_family);
 }
 
-static const struct ieee80211_iface_limit hwsim_if_limits[] = {
-       { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) },
-       { .max = 2048,  .types = BIT(NL80211_IFTYPE_STATION) |
-                                BIT(NL80211_IFTYPE_P2P_CLIENT) |
-#ifdef CONFIG_MAC80211_MESH
-                                BIT(NL80211_IFTYPE_MESH_POINT) |
-#endif
-                                BIT(NL80211_IFTYPE_AP) |
-                                BIT(NL80211_IFTYPE_P2P_GO) },
-       { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) },
-};
-
-static const struct ieee80211_iface_limit hwsim_if_dfs_limits[] = {
-       { .max = 8, .types = BIT(NL80211_IFTYPE_AP) },
-};
-
-static struct ieee80211_iface_combination hwsim_if_comb[] = {
-       {
-               .limits = hwsim_if_limits,
-               .n_limits = ARRAY_SIZE(hwsim_if_limits),
-               .max_interfaces = 2048,
-               .num_different_channels = 1,
-       },
-       {
-               .limits = hwsim_if_dfs_limits,
-               .n_limits = ARRAY_SIZE(hwsim_if_dfs_limits),
-               .max_interfaces = 8,
-               .num_different_channels = 1,
-               .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
-                                      BIT(NL80211_CHAN_WIDTH_20) |
-                                      BIT(NL80211_CHAN_WIDTH_40) |
-                                      BIT(NL80211_CHAN_WIDTH_80) |
-                                      BIT(NL80211_CHAN_WIDTH_160),
-       }
-};
-
 static int __init init_mac80211_hwsim(void)
 {
-       int i, err = 0;
-       u8 addr[ETH_ALEN];
-       struct mac80211_hwsim_data *data;
-       struct ieee80211_hw *hw;
-       enum ieee80211_band band;
+       int i, err;
 
-       if (radios < 1 || radios > 100)
+       if (radios < 0 || radios > 100)
                return -EINVAL;
 
        if (channels < 1)
                return -EINVAL;
 
-       if (channels > 1) {
-               hwsim_if_comb[0].num_different_channels = channels;
-               mac80211_hwsim_ops.hw_scan = mac80211_hwsim_hw_scan;
-               mac80211_hwsim_ops.cancel_hw_scan =
-                       mac80211_hwsim_cancel_hw_scan;
-               mac80211_hwsim_ops.sw_scan_start = NULL;
-               mac80211_hwsim_ops.sw_scan_complete = NULL;
-               mac80211_hwsim_ops.remain_on_channel =
-                       mac80211_hwsim_roc;
-               mac80211_hwsim_ops.cancel_remain_on_channel =
-                       mac80211_hwsim_croc;
-               mac80211_hwsim_ops.add_chanctx =
-                       mac80211_hwsim_add_chanctx;
-               mac80211_hwsim_ops.remove_chanctx =
-                       mac80211_hwsim_remove_chanctx;
-               mac80211_hwsim_ops.change_chanctx =
-                       mac80211_hwsim_change_chanctx;
-               mac80211_hwsim_ops.assign_vif_chanctx =
-                       mac80211_hwsim_assign_vif_chanctx;
-               mac80211_hwsim_ops.unassign_vif_chanctx =
-                       mac80211_hwsim_unassign_vif_chanctx;
-       }
+       mac80211_hwsim_mchan_ops = mac80211_hwsim_ops;
+       mac80211_hwsim_mchan_ops.hw_scan = mac80211_hwsim_hw_scan;
+       mac80211_hwsim_mchan_ops.cancel_hw_scan = mac80211_hwsim_cancel_hw_scan;
+       mac80211_hwsim_mchan_ops.sw_scan_start = NULL;
+       mac80211_hwsim_mchan_ops.sw_scan_complete = NULL;
+       mac80211_hwsim_mchan_ops.remain_on_channel = mac80211_hwsim_roc;
+       mac80211_hwsim_mchan_ops.cancel_remain_on_channel = mac80211_hwsim_croc;
+       mac80211_hwsim_mchan_ops.add_chanctx = mac80211_hwsim_add_chanctx;
+       mac80211_hwsim_mchan_ops.remove_chanctx = mac80211_hwsim_remove_chanctx;
+       mac80211_hwsim_mchan_ops.change_chanctx = mac80211_hwsim_change_chanctx;
+       mac80211_hwsim_mchan_ops.assign_vif_chanctx =
+               mac80211_hwsim_assign_vif_chanctx;
+       mac80211_hwsim_mchan_ops.unassign_vif_chanctx =
+               mac80211_hwsim_unassign_vif_chanctx;
 
        spin_lock_init(&hwsim_radio_lock);
        INIT_LIST_HEAD(&hwsim_radios);
@@ -2279,361 +2566,116 @@ static int __init init_mac80211_hwsim(void)
        hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim");
        if (IS_ERR(hwsim_class)) {
                err = PTR_ERR(hwsim_class);
-               goto failed_unregister_driver;
+               goto out_unregister_driver;
        }
 
-       memset(addr, 0, ETH_ALEN);
-       addr[0] = 0x02;
-
        for (i = 0; i < radios; i++) {
-               printk(KERN_DEBUG "mac80211_hwsim: Initializing radio %d\n",
-                      i);
-               hw = ieee80211_alloc_hw(sizeof(*data), &mac80211_hwsim_ops);
-               if (!hw) {
-                       printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw "
-                              "failed\n");
-                       err = -ENOMEM;
-                       goto failed;
-               }
-               data = hw->priv;
-               data->hw = hw;
-
-               data->dev = device_create(hwsim_class, NULL, 0, hw,
-                                         "hwsim%d", i);
-               if (IS_ERR(data->dev)) {
-                       printk(KERN_DEBUG
-                              "mac80211_hwsim: device_create failed (%ld)\n",
-                              PTR_ERR(data->dev));
-                       err = -ENOMEM;
-                       goto failed_drvdata;
-               }
-               data->dev->driver = &mac80211_hwsim_driver.driver;
-               err = device_bind_driver(data->dev);
-               if (err != 0) {
-                       printk(KERN_DEBUG
-                              "mac80211_hwsim: device_bind_driver failed (%d)\n",
-                              err);
-                       goto failed_hw;
-               }
-
-               skb_queue_head_init(&data->pending);
-
-               SET_IEEE80211_DEV(hw, data->dev);
-               addr[3] = i >> 8;
-               addr[4] = i;
-               memcpy(data->addresses[0].addr, addr, ETH_ALEN);
-               memcpy(data->addresses[1].addr, addr, ETH_ALEN);
-               data->addresses[1].addr[0] |= 0x40;
-               hw->wiphy->n_addresses = 2;
-               hw->wiphy->addresses = data->addresses;
-
-               hw->wiphy->iface_combinations = hwsim_if_comb;
-               hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb);
-
-               if (channels > 1) {
-                       hw->wiphy->max_scan_ssids = 255;
-                       hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
-                       hw->wiphy->max_remain_on_channel_duration = 1000;
-                       /* For channels > 1 DFS is not allowed */
-                       hw->wiphy->n_iface_combinations = 1;
-               }
-
-               INIT_DELAYED_WORK(&data->roc_done, hw_roc_done);
-               INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work);
-
-               hw->channel_change_time = 1;
-               hw->queues = 5;
-               hw->offchannel_tx_hw_queue = 4;
-               hw->wiphy->interface_modes =
-                       BIT(NL80211_IFTYPE_STATION) |
-                       BIT(NL80211_IFTYPE_AP) |
-                       BIT(NL80211_IFTYPE_P2P_CLIENT) |
-                       BIT(NL80211_IFTYPE_P2P_GO) |
-                       BIT(NL80211_IFTYPE_ADHOC) |
-                       BIT(NL80211_IFTYPE_MESH_POINT) |
-                       BIT(NL80211_IFTYPE_P2P_DEVICE);
-
-               hw->flags = IEEE80211_HW_MFP_CAPABLE |
-                           IEEE80211_HW_SIGNAL_DBM |
-                           IEEE80211_HW_SUPPORTS_STATIC_SMPS |
-                           IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
-                           IEEE80211_HW_AMPDU_AGGREGATION |
-                           IEEE80211_HW_WANT_MONITOR_VIF |
-                           IEEE80211_HW_QUEUE_CONTROL |
-                           IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
-               if (rctbl)
-                       hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE;
-
-               hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
-                                   WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
-                                   WIPHY_FLAG_AP_UAPSD;
-               hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
-
-               /* ask mac80211 to reserve space for magic */
-               hw->vif_data_size = sizeof(struct hwsim_vif_priv);
-               hw->sta_data_size = sizeof(struct hwsim_sta_priv);
-               hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv);
-
-               memcpy(data->channels_2ghz, hwsim_channels_2ghz,
-                       sizeof(hwsim_channels_2ghz));
-               memcpy(data->channels_5ghz, hwsim_channels_5ghz,
-                       sizeof(hwsim_channels_5ghz));
-               memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates));
-
-               for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
-                       struct ieee80211_supported_band *sband = &data->bands[band];
-                       switch (band) {
-                       case IEEE80211_BAND_2GHZ:
-                               sband->channels = data->channels_2ghz;
-                               sband->n_channels =
-                                       ARRAY_SIZE(hwsim_channels_2ghz);
-                               sband->bitrates = data->rates;
-                               sband->n_bitrates = ARRAY_SIZE(hwsim_rates);
-                               break;
-                       case IEEE80211_BAND_5GHZ:
-                               sband->channels = data->channels_5ghz;
-                               sband->n_channels =
-                                       ARRAY_SIZE(hwsim_channels_5ghz);
-                               sband->bitrates = data->rates + 4;
-                               sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4;
-                               break;
-                       default:
-                               continue;
-                       }
-
-                       sband->ht_cap.ht_supported = true;
-                       sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
-                               IEEE80211_HT_CAP_GRN_FLD |
-                               IEEE80211_HT_CAP_SGI_40 |
-                               IEEE80211_HT_CAP_DSSSCCK40;
-                       sband->ht_cap.ampdu_factor = 0x3;
-                       sband->ht_cap.ampdu_density = 0x6;
-                       memset(&sband->ht_cap.mcs, 0,
-                              sizeof(sband->ht_cap.mcs));
-                       sband->ht_cap.mcs.rx_mask[0] = 0xff;
-                       sband->ht_cap.mcs.rx_mask[1] = 0xff;
-                       sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
-
-                       hw->wiphy->bands[band] = sband;
-
-                       sband->vht_cap.vht_supported = true;
-                       sband->vht_cap.cap =
-                               IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
-                               IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ |
-                               IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
-                               IEEE80211_VHT_CAP_RXLDPC |
-                               IEEE80211_VHT_CAP_SHORT_GI_80 |
-                               IEEE80211_VHT_CAP_SHORT_GI_160 |
-                               IEEE80211_VHT_CAP_TXSTBC |
-                               IEEE80211_VHT_CAP_RXSTBC_1 |
-                               IEEE80211_VHT_CAP_RXSTBC_2 |
-                               IEEE80211_VHT_CAP_RXSTBC_3 |
-                               IEEE80211_VHT_CAP_RXSTBC_4 |
-                               IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
-                       sband->vht_cap.vht_mcs.rx_mcs_map =
-                               cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_8 << 0 |
-                                           IEEE80211_VHT_MCS_SUPPORT_0_8 << 2 |
-                                           IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 |
-                                           IEEE80211_VHT_MCS_SUPPORT_0_8 << 6 |
-                                           IEEE80211_VHT_MCS_SUPPORT_0_8 << 8 |
-                                           IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 |
-                                           IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 |
-                                           IEEE80211_VHT_MCS_SUPPORT_0_8 << 14);
-                       sband->vht_cap.vht_mcs.tx_mcs_map =
-                               sband->vht_cap.vht_mcs.rx_mcs_map;
-               }
-               /* By default all radios are belonging to the first group */
-               data->group = 1;
-               mutex_init(&data->mutex);
+               const char *reg_alpha2 = NULL;
+               const struct ieee80211_regdomain *regd = NULL;
+               bool reg_strict = false;
 
-               /* Enable frame retransmissions for lossy channels */
-               hw->max_rates = 4;
-               hw->max_rate_tries = 11;
-
-               /* Work to be done prior to ieee80211_register_hw() */
                switch (regtest) {
-               case HWSIM_REGTEST_DISABLED:
-               case HWSIM_REGTEST_DRIVER_REG_FOLLOW:
-               case HWSIM_REGTEST_DRIVER_REG_ALL:
                case HWSIM_REGTEST_DIFF_COUNTRY:
-                       /*
-                        * Nothing to be done for driver regulatory domain
-                        * hints prior to ieee80211_register_hw()
-                        */
-                       break;
-               case HWSIM_REGTEST_WORLD_ROAM:
-                       if (i == 0) {
-                               hw->wiphy->regulatory_flags |=
-                                       REGULATORY_CUSTOM_REG;
-                               wiphy_apply_custom_regulatory(hw->wiphy,
-                                       &hwsim_world_regdom_custom_01);
-                       }
-                       break;
-               case HWSIM_REGTEST_CUSTOM_WORLD:
-                       hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
-                       wiphy_apply_custom_regulatory(hw->wiphy,
-                               &hwsim_world_regdom_custom_01);
-                       break;
-               case HWSIM_REGTEST_CUSTOM_WORLD_2:
-                       if (i == 0) {
-                               hw->wiphy->regulatory_flags |=
-                                       REGULATORY_CUSTOM_REG;
-                               wiphy_apply_custom_regulatory(hw->wiphy,
-                                       &hwsim_world_regdom_custom_01);
-                       } else if (i == 1) {
-                               hw->wiphy->regulatory_flags |=
-                                       REGULATORY_CUSTOM_REG;
-                               wiphy_apply_custom_regulatory(hw->wiphy,
-                                       &hwsim_world_regdom_custom_02);
-                       }
-                       break;
-               case HWSIM_REGTEST_STRICT_ALL:
-                       hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
-                       break;
-               case HWSIM_REGTEST_STRICT_FOLLOW:
-               case HWSIM_REGTEST_STRICT_AND_DRIVER_REG:
-                       if (i == 0)
-                               hw->wiphy->regulatory_flags |=
-                                       REGULATORY_STRICT_REG;
-                       break;
-               case HWSIM_REGTEST_ALL:
-                       if (i == 0) {
-                               hw->wiphy->regulatory_flags |=
-                                       REGULATORY_CUSTOM_REG;
-                               wiphy_apply_custom_regulatory(hw->wiphy,
-                                       &hwsim_world_regdom_custom_01);
-                       } else if (i == 1) {
-                               hw->wiphy->regulatory_flags |=
-                                       REGULATORY_CUSTOM_REG;
-                               wiphy_apply_custom_regulatory(hw->wiphy,
-                                       &hwsim_world_regdom_custom_02);
-                       } else if (i == 4)
-                               hw->wiphy->regulatory_flags |=
-                                       REGULATORY_STRICT_REG;
-                       break;
-               default:
-                       break;
-               }
-
-               /* give the regulatory workqueue a chance to run */
-               if (regtest)
-                       schedule_timeout_interruptible(1);
-               err = ieee80211_register_hw(hw);
-               if (err < 0) {
-                       printk(KERN_DEBUG "mac80211_hwsim: "
-                              "ieee80211_register_hw failed (%d)\n", err);
-                       goto failed_hw;
-               }
-
-               /* Work to be done after to ieee80211_register_hw() */
-               switch (regtest) {
-               case HWSIM_REGTEST_WORLD_ROAM:
-               case HWSIM_REGTEST_DISABLED:
+                       if (i < ARRAY_SIZE(hwsim_alpha2s))
+                               reg_alpha2 = hwsim_alpha2s[i];
                        break;
                case HWSIM_REGTEST_DRIVER_REG_FOLLOW:
                        if (!i)
-                               regulatory_hint(hw->wiphy, hwsim_alpha2s[0]);
+                               reg_alpha2 = hwsim_alpha2s[0];
                        break;
-               case HWSIM_REGTEST_DRIVER_REG_ALL:
                case HWSIM_REGTEST_STRICT_ALL:
-                       regulatory_hint(hw->wiphy, hwsim_alpha2s[0]);
+                       reg_strict = true;
+               case HWSIM_REGTEST_DRIVER_REG_ALL:
+                       reg_alpha2 = hwsim_alpha2s[0];
                        break;
-               case HWSIM_REGTEST_DIFF_COUNTRY:
-                       if (i < ARRAY_SIZE(hwsim_alpha2s))
-                               regulatory_hint(hw->wiphy, hwsim_alpha2s[i]);
+               case HWSIM_REGTEST_WORLD_ROAM:
+                       if (i == 0)
+                               regd = &hwsim_world_regdom_custom_01;
                        break;
                case HWSIM_REGTEST_CUSTOM_WORLD:
+                       regd = &hwsim_world_regdom_custom_01;
+                       break;
                case HWSIM_REGTEST_CUSTOM_WORLD_2:
-                       /*
-                        * Nothing to be done for custom world regulatory
-                        * domains after to ieee80211_register_hw
-                        */
+                       if (i == 0)
+                               regd = &hwsim_world_regdom_custom_01;
+                       else if (i == 1)
+                               regd = &hwsim_world_regdom_custom_02;
                        break;
                case HWSIM_REGTEST_STRICT_FOLLOW:
-                       if (i == 0)
-                               regulatory_hint(hw->wiphy, hwsim_alpha2s[0]);
+                       if (i == 0) {
+                               reg_strict = true;
+                               reg_alpha2 = hwsim_alpha2s[0];
+                       }
                        break;
                case HWSIM_REGTEST_STRICT_AND_DRIVER_REG:
-                       if (i == 0)
-                               regulatory_hint(hw->wiphy, hwsim_alpha2s[0]);
-                       else if (i == 1)
-                               regulatory_hint(hw->wiphy, hwsim_alpha2s[1]);
+                       if (i == 0) {
+                               reg_strict = true;
+                               reg_alpha2 = hwsim_alpha2s[0];
+                       } else if (i == 1) {
+                               reg_alpha2 = hwsim_alpha2s[1];
+                       }
                        break;
                case HWSIM_REGTEST_ALL:
-                       if (i == 2)
-                               regulatory_hint(hw->wiphy, hwsim_alpha2s[0]);
-                       else if (i == 3)
-                               regulatory_hint(hw->wiphy, hwsim_alpha2s[1]);
-                       else if (i == 4)
-                               regulatory_hint(hw->wiphy, hwsim_alpha2s[2]);
+                       switch (i) {
+                       case 0:
+                               regd = &hwsim_world_regdom_custom_01;
+                               break;
+                       case 1:
+                               regd = &hwsim_world_regdom_custom_02;
+                               break;
+                       case 2:
+                               reg_alpha2 = hwsim_alpha2s[0];
+                               break;
+                       case 3:
+                               reg_alpha2 = hwsim_alpha2s[1];
+                               break;
+                       case 4:
+                               reg_strict = true;
+                               reg_alpha2 = hwsim_alpha2s[2];
+                               break;
+                       }
                        break;
                default:
                        break;
                }
 
-               wiphy_debug(hw->wiphy, "hwaddr %pm registered\n",
-                           hw->wiphy->perm_addr);
-
-               data->debugfs = debugfs_create_dir("hwsim",
-                                                  hw->wiphy->debugfsdir);
-               debugfs_create_file("ps", 0666, data->debugfs, data,
-                                   &hwsim_fops_ps);
-               debugfs_create_file("group", 0666, data->debugfs, data,
-                                   &hwsim_fops_group);
-               if (channels == 1)
-                       debugfs_create_file("dfs_simulate_radar", 0222,
-                                           data->debugfs,
-                                           data, &hwsim_simulate_radar);
-
-               tasklet_hrtimer_init(&data->beacon_timer,
-                                    mac80211_hwsim_beacon,
-                                    CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS);
-
-               list_add_tail(&data->list, &hwsim_radios);
+               err = mac80211_hwsim_create_radio(channels, reg_alpha2,
+                                                 regd, reg_strict);
+               if (err < 0)
+                       goto out_free_radios;
        }
 
        hwsim_mon = alloc_netdev(0, "hwsim%d", hwsim_mon_setup);
        if (hwsim_mon == NULL) {
                err = -ENOMEM;
-               goto failed;
+               goto out_free_radios;
        }
 
        rtnl_lock();
-
        err = dev_alloc_name(hwsim_mon, hwsim_mon->name);
-       if (err < 0)
-               goto failed_mon;
-
+       if (err < 0) {
+               rtnl_unlock();
+               goto out_free_radios;
+       }
 
        err = register_netdevice(hwsim_mon);
-       if (err < 0)
-               goto failed_mon;
-
+       if (err < 0) {
+               rtnl_unlock();
+               goto out_free_mon;
+       }
        rtnl_unlock();
 
        err = hwsim_init_netlink();
        if (err < 0)
-               goto failed_nl;
+               goto out_free_mon;
 
        return 0;
 
-failed_nl:
-       printk(KERN_DEBUG "mac_80211_hwsim: failed initializing netlink\n");
-       return err;
-
-failed_mon:
-       rtnl_unlock();
+out_free_mon:
        free_netdev(hwsim_mon);
+out_free_radios:
        mac80211_hwsim_free();
-       return err;
-
-failed_hw:
-       device_unregister(data->dev);
-failed_drvdata:
-       ieee80211_free_hw(hw);
-failed:
-       mac80211_hwsim_free();
-failed_unregister_driver:
+out_unregister_driver:
        platform_driver_unregister(&mac80211_hwsim_driver);
        return err;
 }
index afaad5a443b6b56a4c82acc214bc1862190df7c4..2747cce5a269e46d42d1b3f6f819b59753b8aa8b 100644 (file)
@@ -65,6 +65,9 @@ enum hwsim_tx_control_flags {
  * kernel, uses:
  *     %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS,
  *     %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * @HWSIM_CMD_CREATE_RADIO: create a new radio with the given parameters,
+ *     returns the radio ID (>= 0) or negative on errors
+ * @HWSIM_CMD_DESTROY_RADIO: destroy a radio
  * @__HWSIM_CMD_MAX: enum limit
  */
 enum {
@@ -72,6 +75,8 @@ enum {
        HWSIM_CMD_REGISTER,
        HWSIM_CMD_FRAME,
        HWSIM_CMD_TX_INFO_FRAME,
+       HWSIM_CMD_CREATE_RADIO,
+       HWSIM_CMD_DESTROY_RADIO,
        __HWSIM_CMD_MAX,
 };
 #define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
@@ -94,6 +99,14 @@ enum {
        space
  * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array
  * @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame
+ * @HWSIM_ATTR_CHANNELS: u32 attribute used with the %HWSIM_CMD_CREATE_RADIO
+ *     command giving the number of channels supported by the new radio
+ * @HWSIM_ATTR_RADIO_ID: u32 attribute used with %HWSIM_CMD_DESTROY_RADIO
+ *     only to destroy a radio
+ * @HWSIM_ATTR_REG_HINT_ALPHA2: alpha2 for regulatoro driver hint
+ *     (nla string, length 2)
+ * @HWSIM_ATTR_REG_CUSTOM_REG: custom regulatory domain index (u32 attribute)
+ * @HWSIM_ATTR_REG_STRICT_REG: request REGULATORY_STRICT_REG (flag attribute)
  * @__HWSIM_ATTR_MAX: enum limit
  */
 
@@ -108,6 +121,11 @@ enum {
        HWSIM_ATTR_SIGNAL,
        HWSIM_ATTR_TX_INFO,
        HWSIM_ATTR_COOKIE,
+       HWSIM_ATTR_CHANNELS,
+       HWSIM_ATTR_RADIO_ID,
+       HWSIM_ATTR_REG_HINT_ALPHA2,
+       HWSIM_ATTR_REG_CUSTOM_REG,
+       HWSIM_ATTR_REG_STRICT_REG,
        __HWSIM_ATTR_MAX,
 };
 #define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
index 63dbde5c3713304e538890ba1aa90fef5caec400..4987c3f942ce7dff2bb599feab7bb1aa26d40f50 100644 (file)
@@ -5892,8 +5892,6 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
 
        hw->extra_tx_headroom -= priv->ap_fw ? REDUCED_TX_HEADROOM : 0;
 
-       hw->channel_change_time = 10;
-
        hw->queues = MWL8K_TX_WMM_QUEUES;
 
        /* Set rssi values to dBm */
index 80d93cba51502edc28806202af6c48f8c42485ff..eede90b63f847934a8a0bc45e63695696d88a82d 100644 (file)
@@ -756,7 +756,6 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
                                      BIT(NL80211_IFTYPE_AP) |
                                      BIT(NL80211_IFTYPE_MESH_POINT);
 
-       dev->channel_change_time = 1000;        /* TODO: find actual value */
        priv->beacon_req_id = cpu_to_le32(0);
        priv->tx_stats[P54_QUEUE_BEACON].limit = 1;
        priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1;
index d63a12cc5de8e6af64b3b9fef9650b42bbfcd6e5..93bb384eb0014e17e51d12457159f6d9e08781ff 100644 (file)
@@ -353,7 +353,6 @@ static void _rtl_init_mac80211(struct ieee80211_hw *hw)
 
        /* TODO: Correct this value for our hw */
        /* TODO: define these hard code value */
-       hw->channel_change_time = 100;
        hw->max_listen_interval = 10;
        hw->max_rate_tries = 4;
        /* hw->max_rates = 1; */
index 80f92110a3bfd692ef53f0899cf8551fbc00b973..fa3909a72ad674a466954cf5e5857881b57e5b2d 100644 (file)
@@ -1468,7 +1468,6 @@ int wl1251_init_ieee80211(struct wl1251 *wl)
 
        /* unit us */
        /* FIXME: find a proper value */
-       wl->hw->channel_change_time = 10000;
 
        wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
                IEEE80211_HW_SUPPORTS_PS |
index e9da47cead587704c7060ebd8ab754c1aa7da16b..18a009e593c73bcbd2fe75ab43ed0f6b0306a26c 100644 (file)
@@ -5710,7 +5710,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
 
        /* unit us */
        /* FIXME: find a proper value */
-       wl->hw->channel_change_time = 10000;
        wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval;
 
        wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
index 07891a3e316e6b9e50d0a744f4e7d6a6b854bb40..0d29624416c335887a08da772f9ce2c8b63c85bd 100644 (file)
@@ -788,7 +788,6 @@ static int wb35_probe(struct usb_interface *intf,
        dev->flags = IEEE80211_HW_SIGNAL_UNSPEC;
        dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
 
-       dev->channel_change_time = 1000;
        dev->max_signal = 100;
        dev->queues = 1;
 
index 776cbb80d098d37e90c0bf0e8c37736ab961f338..e526a8cecb70a90533843bc26c90eaa2a7e23e55 100644 (file)
@@ -1857,6 +1857,7 @@ enum ieee80211_key_len {
        WLAN_KEY_LEN_CCMP = 16,
        WLAN_KEY_LEN_TKIP = 32,
        WLAN_KEY_LEN_AES_CMAC = 16,
+       WLAN_KEY_LEN_SMS4 = 32,
 };
 
 #define IEEE80211_WEP_IV_LEN           4
@@ -1902,6 +1903,7 @@ enum ieee80211_tdls_actioncode {
 #define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6)
 
 #define WLAN_EXT_CAPA8_OPMODE_NOTIF    BIT(6)
+#define WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED    BIT(7)
 
 /* TDLS specific payload type in the LLC/SNAP header */
 #define WLAN_TDLS_SNAP_RFTYPE  0x2
index 56c597793d6d380cb90c80c5d17f73fe87a63eb6..b1f84b05c67e99371eef66c6c3adad1a45380f50 100644 (file)
@@ -4640,6 +4640,14 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
  */
 void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp);
 
+/**
+ * ieee80211_get_num_supported_channels - get number of channels device has
+ * @wiphy: the wiphy
+ *
+ * Return: the number of channels supported by the device.
+ */
+unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy);
+
 /* Logging, debugging and troubleshooting/diagnostic helpers. */
 
 /* wiphy_printk helpers, similar to dev_printk */
index f838af816b56cd26909752b650178450c9e47eef..f4ab2fb4d50c445b980e1f6c507ae5a893c1ddfc 100644 (file)
@@ -1616,8 +1616,6 @@ enum ieee80211_hw_flags {
  * @extra_beacon_tailroom: tailroom to reserve in each beacon tx skb.
  *     Can be used by drivers to add extra IEs.
  *
- * @channel_change_time: time (in microseconds) it takes to change channels.
- *
  * @max_signal: Maximum value for signal (rssi) in RX information, used
  *     only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB
  *
@@ -1699,7 +1697,6 @@ struct ieee80211_hw {
        u32 flags;
        unsigned int extra_tx_headroom;
        unsigned int extra_beacon_tailroom;
-       int channel_change_time;
        int vif_data_size;
        int sta_data_size;
        int chanctx_data_size;
@@ -2122,6 +2119,11 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
  * appropriately (only the last frame may have %IEEE80211_TX_STATUS_EOSP)
  * and also take care of the EOSP and MORE_DATA bits in the frame.
  * The driver may also use ieee80211_sta_eosp() in this case.
+ *
+ * Note that if the driver ever buffers frames other than QoS-data
+ * frames, it must take care to never send a non-QoS-data frame as
+ * the last frame in a service period, adding a QoS-nulldata frame
+ * after a non-QoS-data frame if needed.
  */
 
 /**
index 09d2e58a2ba70e50ff6489700565a3755358a3b0..f9ae9b85d4c1bf297f3008e0f0693d6305c69824 100644 (file)
@@ -1035,6 +1035,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
                return err;
        }
 
+       ieee80211_recalc_dtim(local, sdata);
        ieee80211_bss_info_change_notify(sdata, changed);
 
        netif_carrier_on(dev);
@@ -3854,7 +3855,7 @@ static int ieee80211_set_qos_map(struct wiphy *wiphy,
                new_qos_map = NULL;
        }
 
-       old_qos_map = rtnl_dereference(sdata->qos_map);
+       old_qos_map = sdata_dereference(sdata->qos_map, sdata);
        rcu_assign_pointer(sdata->qos_map, new_qos_map);
        if (old_qos_map)
                kfree_rcu(old_qos_map, rcu_head);
index 04b5a14c8a054e33227b893de78912e5ee4a6f80..ebf80f3abd83fe1af18ce94863a0fe1dd090ea19 100644 (file)
@@ -133,7 +133,15 @@ static ssize_t ieee80211_if_fmt_##name(                                    \
                         jiffies_to_msecs(sdata->field));               \
 }
 
-#define __IEEE80211_IF_FILE(name, _write)                              \
+#define _IEEE80211_IF_FILE_OPS(name, _read, _write)                    \
+static const struct file_operations name##_ops = {                     \
+       .read = (_read),                                                \
+       .write = (_write),                                              \
+       .open = simple_open,                                            \
+       .llseek = generic_file_llseek,                                  \
+}
+
+#define _IEEE80211_IF_FILE_R_FN(name)                                  \
 static ssize_t ieee80211_if_read_##name(struct file *file,             \
                                        char __user *userbuf,           \
                                        size_t count, loff_t *ppos)     \
@@ -141,28 +149,34 @@ static ssize_t ieee80211_if_read_##name(struct file *file,                \
        return ieee80211_if_read(file->private_data,                    \
                                 userbuf, count, ppos,                  \
                                 ieee80211_if_fmt_##name);              \
-}                                                                      \
-static const struct file_operations name##_ops = {                     \
-       .read = ieee80211_if_read_##name,                               \
-       .write = (_write),                                              \
-       .open = simple_open,                                            \
-       .llseek = generic_file_llseek,                                  \
 }
 
-#define __IEEE80211_IF_FILE_W(name)                                    \
+#define _IEEE80211_IF_FILE_W_FN(name)                                  \
 static ssize_t ieee80211_if_write_##name(struct file *file,            \
                                         const char __user *userbuf,    \
                                         size_t count, loff_t *ppos)    \
 {                                                                      \
        return ieee80211_if_write(file->private_data, userbuf, count,   \
                                  ppos, ieee80211_if_parse_##name);     \
-}                                                                      \
-__IEEE80211_IF_FILE(name, ieee80211_if_write_##name)
+}
+
+#define IEEE80211_IF_FILE_R(name)                                      \
+       _IEEE80211_IF_FILE_R_FN(name)                                   \
+       _IEEE80211_IF_FILE_OPS(name, ieee80211_if_read_##name, NULL)
+
+#define IEEE80211_IF_FILE_W(name)                                      \
+       _IEEE80211_IF_FILE_W_FN(name)                                   \
+       _IEEE80211_IF_FILE_OPS(name, NULL, ieee80211_if_write_##name)
 
+#define IEEE80211_IF_FILE_RW(name)                                     \
+       _IEEE80211_IF_FILE_R_FN(name)                                   \
+       _IEEE80211_IF_FILE_W_FN(name)                                   \
+       _IEEE80211_IF_FILE_OPS(name, ieee80211_if_read_##name,          \
+                              ieee80211_if_write_##name)
 
 #define IEEE80211_IF_FILE(name, field, format)                         \
-               IEEE80211_IF_FMT_##format(name, field)                  \
-               __IEEE80211_IF_FILE(name, NULL)
+       IEEE80211_IF_FMT_##format(name, field)                          \
+       IEEE80211_IF_FILE_R(name)
 
 /* common attributes */
 IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
@@ -199,7 +213,7 @@ ieee80211_if_fmt_hw_queues(const struct ieee80211_sub_if_data *sdata,
 
        return len;
 }
-__IEEE80211_IF_FILE(hw_queues, NULL);
+IEEE80211_IF_FILE_R(hw_queues);
 
 /* STA attributes */
 IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
@@ -275,14 +289,7 @@ static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata,
 
        return -EINVAL;
 }
-
-__IEEE80211_IF_FILE_W(smps);
-
-static ssize_t ieee80211_if_fmt_tkip_mic_test(
-       const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
-{
-       return -EOPNOTSUPP;
-}
+IEEE80211_IF_FILE_RW(smps);
 
 static ssize_t ieee80211_if_parse_tkip_mic_test(
        struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
@@ -349,8 +356,7 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
 
        return buflen;
 }
-
-__IEEE80211_IF_FILE_W(tkip_mic_test);
+IEEE80211_IF_FILE_W(tkip_mic_test);
 
 static ssize_t ieee80211_if_fmt_uapsd_queues(
        const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
@@ -378,7 +384,7 @@ static ssize_t ieee80211_if_parse_uapsd_queues(
 
        return buflen;
 }
-__IEEE80211_IF_FILE_W(uapsd_queues);
+IEEE80211_IF_FILE_RW(uapsd_queues);
 
 static ssize_t ieee80211_if_fmt_uapsd_max_sp_len(
        const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
@@ -406,7 +412,7 @@ static ssize_t ieee80211_if_parse_uapsd_max_sp_len(
 
        return buflen;
 }
-__IEEE80211_IF_FILE_W(uapsd_max_sp_len);
+IEEE80211_IF_FILE_RW(uapsd_max_sp_len);
 
 /* AP attributes */
 IEEE80211_IF_FILE(num_mcast_sta, u.ap.num_mcast_sta, ATOMIC);
@@ -419,7 +425,7 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast(
        return scnprintf(buf, buflen, "%u\n",
                         skb_queue_len(&sdata->u.ap.ps.bc_buf));
 }
-__IEEE80211_IF_FILE(num_buffered_multicast, NULL);
+IEEE80211_IF_FILE_R(num_buffered_multicast);
 
 /* IBSS attributes */
 static ssize_t ieee80211_if_fmt_tsf(
@@ -468,9 +474,10 @@ static ssize_t ieee80211_if_parse_tsf(
                }
        }
 
+       ieee80211_recalc_dtim(local, sdata);
        return buflen;
 }
-__IEEE80211_IF_FILE_W(tsf);
+IEEE80211_IF_FILE_RW(tsf);
 
 
 /* WDS attributes */
index 9a8be8f69224d0be11fe9b10a0176a2f5a0002f1..fab7b91923e0a8b93313797b53a469234ce83506 100644 (file)
@@ -479,10 +479,9 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,
                         vif->type != NL80211_IFTYPE_AP))
                return;
 
-       if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
-               smps_mode = IEEE80211_SMPS_AUTOMATIC;
-
        if (vif->type == NL80211_IFTYPE_STATION) {
+               if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
+                       smps_mode = IEEE80211_SMPS_AUTOMATIC;
                if (sdata->u.mgd.driver_smps_mode == smps_mode)
                        return;
                sdata->u.mgd.driver_smps_mode = smps_mode;
index 953b9e29454707b39460fb761a5ddba3e9377344..3701930c66493af53461a4f0c1849ad26450e092 100644 (file)
@@ -1800,6 +1800,8 @@ ieee80211_cs_get(struct ieee80211_local *local, u32 cipher,
 int ieee80211_cs_headroom(struct ieee80211_local *local,
                          struct cfg80211_crypto_settings *crypto,
                          enum nl80211_iftype iftype);
+void ieee80211_recalc_dtim(struct ieee80211_local *local,
+                          struct ieee80211_sub_if_data *sdata);
 
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
index 2bd5b552b2f6c41629022876265bdd9a10e8890e..d767cfb9b45f092606cd37288113e82714b75fe4 100644 (file)
@@ -846,17 +846,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                /* TODO: consider VHT for RX chains, hopefully it's the same */
        }
 
-       local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) +
-                                     sizeof(void *) * channels, GFP_KERNEL);
-       if (!local->int_scan_req)
-               return -ENOMEM;
-
-       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
-               if (!local->hw.wiphy->bands[band])
-                       continue;
-               local->int_scan_req->rates[band] = (u32) -1;
-       }
-
        /* if low-level driver supports AP, we also support VLAN */
        if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) {
                hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN);
@@ -880,6 +869,17 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                                return -EINVAL;
        }
 
+       local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) +
+                                     sizeof(void *) * channels, GFP_KERNEL);
+       if (!local->int_scan_req)
+               return -ENOMEM;
+
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+               if (!local->hw.wiphy->bands[band])
+                       continue;
+               local->int_scan_req->rates[band] = (u32) -1;
+       }
+
 #ifndef CONFIG_MAC80211_MESH
        /* mesh depends on Kconfig, but drivers should set it if they want */
        local->hw.wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MESH_POINT);
index 5a74b249ba35d0f47a499b08d4d0f04d75d4a5ec..5b919cab1de0015cd833d04b840c9d7cdc69e96b 100644 (file)
@@ -807,6 +807,7 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
                return -ENOMEM;
        }
 
+       ieee80211_recalc_dtim(local, sdata);
        ieee80211_bss_info_change_notify(sdata, changed);
 
        netif_carrier_on(sdata->dev);
index cf83217103f9c91479cd4f3f1b97a2752af0c897..e8f60aa2e848b16982993f134fc63511186542af 100644 (file)
@@ -437,6 +437,7 @@ __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
        sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
 
        set_sta_flag(sta, WLAN_STA_WME);
+       sta->sta.wme = true;
 
        return sta;
 }
index 5a2afe9583a806d5f9059c390e0cc70ae7799e22..c24ca0d0f4697ea7040c813dbf24d1c32f707329 100644 (file)
@@ -3076,8 +3076,8 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
 
 /* main receive path */
 
-static int prepare_for_handlers(struct ieee80211_rx_data *rx,
-                               struct ieee80211_hdr *hdr)
+static bool prepare_for_handlers(struct ieee80211_rx_data *rx,
+                                struct ieee80211_hdr *hdr)
 {
        struct ieee80211_sub_if_data *sdata = rx->sdata;
        struct sk_buff *skb = rx->skb;
@@ -3088,29 +3088,29 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
        switch (sdata->vif.type) {
        case NL80211_IFTYPE_STATION:
                if (!bssid && !sdata->u.mgd.use_4addr)
-                       return 0;
+                       return false;
                if (!multicast &&
                    !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
                        if (!(sdata->dev->flags & IFF_PROMISC) ||
                            sdata->u.mgd.use_4addr)
-                               return 0;
+                               return false;
                        status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
                }
                break;
        case NL80211_IFTYPE_ADHOC:
                if (!bssid)
-                       return 0;
+                       return false;
                if (ether_addr_equal(sdata->vif.addr, hdr->addr2) ||
                    ether_addr_equal(sdata->u.ibss.bssid, hdr->addr2))
-                       return 0;
+                       return false;
                if (ieee80211_is_beacon(hdr->frame_control)) {
-                       return 1;
+                       return true;
                } else if (!ieee80211_bssid_match(bssid, sdata->u.ibss.bssid)) {
-                       return 0;
+                       return false;
                } else if (!multicast &&
                           !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
                        if (!(sdata->dev->flags & IFF_PROMISC))
-                               return 0;
+                               return false;
                        status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
                } else if (!rx->sta) {
                        int rate_idx;
@@ -3126,7 +3126,7 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
                if (!multicast &&
                    !ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
                        if (!(sdata->dev->flags & IFF_PROMISC))
-                               return 0;
+                               return false;
 
                        status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
                }
@@ -3135,7 +3135,7 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
        case NL80211_IFTYPE_AP:
                if (!bssid) {
                        if (!ether_addr_equal(sdata->vif.addr, hdr->addr1))
-                               return 0;
+                               return false;
                } else if (!ieee80211_bssid_match(bssid, sdata->vif.addr)) {
                        /*
                         * Accept public action frames even when the
@@ -3145,26 +3145,26 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
                         */
                        if (!multicast &&
                            !ether_addr_equal(sdata->vif.addr, hdr->addr1))
-                               return 0;
+                               return false;
                        if (ieee80211_is_public_action(hdr, skb->len))
-                               return 1;
+                               return true;
                        if (!ieee80211_is_beacon(hdr->frame_control))
-                               return 0;
+                               return false;
                        status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
                }
                break;
        case NL80211_IFTYPE_WDS:
                if (bssid || !ieee80211_is_data(hdr->frame_control))
-                       return 0;
+                       return false;
                if (!ether_addr_equal(sdata->u.wds.remote_addr, hdr->addr2))
-                       return 0;
+                       return false;
                break;
        case NL80211_IFTYPE_P2P_DEVICE:
                if (!ieee80211_is_public_action(hdr, skb->len) &&
                    !ieee80211_is_probe_req(hdr->frame_control) &&
                    !ieee80211_is_probe_resp(hdr->frame_control) &&
                    !ieee80211_is_beacon(hdr->frame_control))
-                       return 0;
+                       return false;
                if (!ether_addr_equal(sdata->vif.addr, hdr->addr1) &&
                    !multicast)
                        status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
@@ -3175,7 +3175,7 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
                break;
        }
 
-       return 1;
+       return true;
 }
 
 /*
@@ -3191,13 +3191,11 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
        struct ieee80211_sub_if_data *sdata = rx->sdata;
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_hdr *hdr = (void *)skb->data;
-       int prepares;
 
        rx->skb = skb;
        status->rx_flags |= IEEE80211_RX_RA_MATCH;
-       prepares = prepare_for_handlers(rx, hdr);
 
-       if (!prepares)
+       if (!prepare_for_handlers(rx, hdr))
                return false;
 
        if (!consume) {
index 4576ba0ff2211af31664b10dd40e996e490286df..decd30c1e29053d9a5e3ea56c7ccb66a7407578d 100644 (file)
@@ -300,6 +300,35 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        if (!sta)
                return NULL;
 
+       rcu_read_lock();
+       tx_latency = rcu_dereference(local->tx_latency);
+       /* init stations Tx latency statistics && TID bins */
+       if (tx_latency) {
+               sta->tx_lat = kzalloc(IEEE80211_NUM_TIDS *
+                                     sizeof(struct ieee80211_tx_latency_stat),
+                                     GFP_ATOMIC);
+               if (!sta->tx_lat) {
+                       rcu_read_unlock();
+                       goto free;
+               }
+
+               if (tx_latency->n_ranges) {
+                       for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
+                               /* size of bins is size of the ranges +1 */
+                               sta->tx_lat[i].bin_count =
+                                       tx_latency->n_ranges + 1;
+                               sta->tx_lat[i].bins =
+                                       kcalloc(sta->tx_lat[i].bin_count,
+                                               sizeof(u32), GFP_ATOMIC);
+                               if (!sta->tx_lat[i].bins) {
+                                       rcu_read_unlock();
+                                       goto free;
+                               }
+                       }
+               }
+       }
+       rcu_read_unlock();
+
        spin_lock_init(&sta->lock);
        INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
        INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
@@ -324,10 +353,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++)
                ewma_init(&sta->chain_signal_avg[i], 1024, 8);
 
-       if (sta_prepare_rate_control(local, sta, gfp)) {
-               kfree(sta);
-               return NULL;
-       }
+       if (sta_prepare_rate_control(local, sta, gfp))
+               goto free;
 
        for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
                /*
@@ -371,34 +398,17 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
                }
        }
 
-       rcu_read_lock();
-
-       tx_latency = rcu_dereference(local->tx_latency);
-       /* init stations Tx latency statistics && TID bins */
-       if (tx_latency)
-               sta->tx_lat = kzalloc(IEEE80211_NUM_TIDS *
-                                     sizeof(struct ieee80211_tx_latency_stat),
-                                     GFP_ATOMIC);
-
-       /*
-        * if Tx latency and bins are enabled and the previous allocation
-        * succeeded
-        */
-       if (tx_latency && tx_latency->n_ranges && sta->tx_lat)
-               for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
-                       /* size of bins is size of the ranges +1 */
-                       sta->tx_lat[i].bin_count =
-                               tx_latency->n_ranges + 1;
-                       sta->tx_lat[i].bins  = kcalloc(sta->tx_lat[i].bin_count,
-                                                      sizeof(u32),
-                                                      GFP_ATOMIC);
-               }
-
-       rcu_read_unlock();
-
        sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
-
        return sta;
+
+free:
+       if (sta->tx_lat) {
+               for (i = 0; i < IEEE80211_NUM_TIDS; i++)
+                       kfree(sta->tx_lat[i].bins);
+               kfree(sta->tx_lat);
+       }
+       kfree(sta);
+       return NULL;
 }
 
 static int sta_info_insert_check(struct sta_info *sta)
@@ -1143,7 +1153,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 
 static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
                                         struct sta_info *sta, int tid,
-                                        enum ieee80211_frame_release_type reason)
+                                        enum ieee80211_frame_release_type reason,
+                                        bool call_driver)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_qos_hdr *nullfunc;
@@ -1201,7 +1212,9 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
                       IEEE80211_TX_STATUS_EOSP |
                       IEEE80211_TX_CTL_REQ_TX_STATUS;
 
-       drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false);
+       if (call_driver)
+               drv_allow_buffered_frames(local, sta, BIT(tid), 1,
+                                         reason, false);
 
        skb->dev = sdata->dev;
 
@@ -1217,6 +1230,17 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
        rcu_read_unlock();
 }
 
+static int find_highest_prio_tid(unsigned long tids)
+{
+       /* lower 3 TIDs aren't ordered perfectly */
+       if (tids & 0xF8)
+               return fls(tids) - 1;
+       /* TID 0 is BE just like TID 3 */
+       if (tids & BIT(0))
+               return 0;
+       return fls(tids) - 1;
+}
+
 static void
 ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                                  int n_frames, u8 ignored_acs,
@@ -1224,7 +1248,6 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
-       bool found = false;
        bool more_data = false;
        int ac;
        unsigned long driver_release_tids = 0;
@@ -1235,9 +1258,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
 
        __skb_queue_head_init(&frames);
 
-       /*
-        * Get response frame(s) and more data bit for it.
-        */
+       /* Get response frame(s) and more data bit for the last one. */
        for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
                unsigned long tids;
 
@@ -1246,43 +1267,48 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
 
                tids = ieee80211_tids_for_ac(ac);
 
-               if (!found) {
-                       driver_release_tids = sta->driver_buffered_tids & tids;
-                       if (driver_release_tids) {
-                               found = true;
-                       } else {
-                               struct sk_buff *skb;
-
-                               while (n_frames > 0) {
-                                       skb = skb_dequeue(&sta->tx_filtered[ac]);
-                                       if (!skb) {
-                                               skb = skb_dequeue(
-                                                       &sta->ps_tx_buf[ac]);
-                                               if (skb)
-                                                       local->total_ps_buffered--;
-                                       }
-                                       if (!skb)
-                                               break;
-                                       n_frames--;
-                                       found = true;
-                                       __skb_queue_tail(&frames, skb);
-                               }
-                       }
+               /* if we already have frames from software, then we can't also
+                * release from hardware queues
+                */
+               if (skb_queue_empty(&frames))
+                       driver_release_tids |= sta->driver_buffered_tids & tids;
 
-                       /*
-                        * If the driver has data on more than one TID then
+               if (driver_release_tids) {
+                       /* If the driver has data on more than one TID then
                         * certainly there's more data if we release just a
-                        * single frame now (from a single TID).
+                        * single frame now (from a single TID). This will
+                        * only happen for PS-Poll.
                         */
                        if (reason == IEEE80211_FRAME_RELEASE_PSPOLL &&
                            hweight16(driver_release_tids) > 1) {
                                more_data = true;
                                driver_release_tids =
-                                       BIT(ffs(driver_release_tids) - 1);
+                                       BIT(find_highest_prio_tid(
+                                               driver_release_tids));
                                break;
                        }
+               } else {
+                       struct sk_buff *skb;
+
+                       while (n_frames > 0) {
+                               skb = skb_dequeue(&sta->tx_filtered[ac]);
+                               if (!skb) {
+                                       skb = skb_dequeue(
+                                               &sta->ps_tx_buf[ac]);
+                                       if (skb)
+                                               local->total_ps_buffered--;
+                               }
+                               if (!skb)
+                                       break;
+                               n_frames--;
+                               __skb_queue_tail(&frames, skb);
+                       }
                }
 
+               /* If we have more frames buffered on this AC, then set the
+                * more-data bit and abort the loop since we can't send more
+                * data from other ACs before the buffered frames from this.
+                */
                if (!skb_queue_empty(&sta->tx_filtered[ac]) ||
                    !skb_queue_empty(&sta->ps_tx_buf[ac])) {
                        more_data = true;
@@ -1290,7 +1316,7 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                }
        }
 
-       if (!found) {
+       if (skb_queue_empty(&frames) && !driver_release_tids) {
                int tid;
 
                /*
@@ -1311,15 +1337,13 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                /* This will evaluate to 1, 3, 5 or 7. */
                tid = 7 - ((ffs(~ignored_acs) - 1) << 1);
 
-               ieee80211_send_null_response(sdata, sta, tid, reason);
-               return;
-       }
-
-       if (!driver_release_tids) {
+               ieee80211_send_null_response(sdata, sta, tid, reason, true);
+       } else if (!driver_release_tids) {
                struct sk_buff_head pending;
                struct sk_buff *skb;
                int num = 0;
                u16 tids = 0;
+               bool need_null = false;
 
                skb_queue_head_init(&pending);
 
@@ -1353,22 +1377,57 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                            ieee80211_is_qos_nullfunc(hdr->frame_control))
                                qoshdr = ieee80211_get_qos_ctl(hdr);
 
-                       /* end service period after last frame */
-                       if (skb_queue_empty(&frames)) {
-                               if (reason == IEEE80211_FRAME_RELEASE_UAPSD &&
-                                   qoshdr)
-                                       *qoshdr |= IEEE80211_QOS_CTL_EOSP;
+                       tids |= BIT(skb->priority);
+
+                       __skb_queue_tail(&pending, skb);
+
+                       /* end service period after last frame or add one */
+                       if (!skb_queue_empty(&frames))
+                               continue;
 
+                       if (reason != IEEE80211_FRAME_RELEASE_UAPSD) {
+                               /* for PS-Poll, there's only one frame */
                                info->flags |= IEEE80211_TX_STATUS_EOSP |
                                               IEEE80211_TX_CTL_REQ_TX_STATUS;
+                               break;
                        }
 
-                       if (qoshdr)
-                               tids |= BIT(*qoshdr & IEEE80211_QOS_CTL_TID_MASK);
-                       else
-                               tids |= BIT(0);
+                       /* For uAPSD, things are a bit more complicated. If the
+                        * last frame has a QoS header (i.e. is a QoS-data or
+                        * QoS-nulldata frame) then just set the EOSP bit there
+                        * and be done.
+                        * If the frame doesn't have a QoS header (which means
+                        * it should be a bufferable MMPDU) then we can't set
+                        * the EOSP bit in the QoS header; add a QoS-nulldata
+                        * frame to the list to send it after the MMPDU.
+                        *
+                        * Note that this code is only in the mac80211-release
+                        * code path, we assume that the driver will not buffer
+                        * anything but QoS-data frames, or if it does, will
+                        * create the QoS-nulldata frame by itself if needed.
+                        *
+                        * Cf. 802.11-2012 10.2.1.10 (c).
+                        */
+                       if (qoshdr) {
+                               *qoshdr |= IEEE80211_QOS_CTL_EOSP;
 
-                       __skb_queue_tail(&pending, skb);
+                               info->flags |= IEEE80211_TX_STATUS_EOSP |
+                                              IEEE80211_TX_CTL_REQ_TX_STATUS;
+                       } else {
+                               /* The standard isn't completely clear on this
+                                * as it says the more-data bit should be set
+                                * if there are more BUs. The QoS-Null frame
+                                * we're about to send isn't buffered yet, we
+                                * only create it below, but let's pretend it
+                                * was buffered just in case some clients only
+                                * expect more-data=0 when eosp=1.
+                                */
+                               hdr->frame_control |=
+                                       cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+                               need_null = true;
+                               num++;
+                       }
+                       break;
                }
 
                drv_allow_buffered_frames(local, sta, tids, num,
@@ -1376,17 +1435,22 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
 
                ieee80211_add_pending_skbs(local, &pending);
 
+               if (need_null)
+                       ieee80211_send_null_response(
+                               sdata, sta, find_highest_prio_tid(tids),
+                               reason, false);
+
                sta_info_recalc_tim(sta);
        } else {
                /*
                 * We need to release a frame that is buffered somewhere in the
                 * driver ... it'll have to handle that.
-                * Note that, as per the comment above, it'll also have to see
-                * if there is more than just one frame on the specific TID that
-                * we're releasing from, and it needs to set the more-data bit
-                * accordingly if we tell it that there's no more data. If we do
-                * tell it there's more data, then of course the more-data bit
-                * needs to be set anyway.
+                * Note that the driver also has to check the number of frames
+                * on the TIDs we're releasing from - if there are more than
+                * n_frames it has to set the more-data bit (if we didn't ask
+                * it to set it anyway due to other buffered frames); if there
+                * are fewer than n_frames it has to make sure to adjust that
+                * to allow the service period to end properly.
                 */
                drv_release_buffered_frames(local, sta, driver_release_tids,
                                            n_frames, reason, more_data);
@@ -1394,9 +1458,9 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                /*
                 * Note that we don't recalculate the TIM bit here as it would
                 * most likely have no effect at all unless the driver told us
-                * that the TID became empty before returning here from the
+                * that the TID(s) became empty before returning here from the
                 * release function.
-                * Either way, however, when the driver tells us that the TID
+                * Either way, however, when the driver tells us that the TID(s)
                 * became empty we'll do the TIM recalculation.
                 */
        }
@@ -1485,6 +1549,8 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
        if (WARN_ON(tid >= IEEE80211_NUM_TIDS))
                return;
 
+       trace_api_sta_set_buffered(sta->local, pubsta, tid, buffered);
+
        if (buffered)
                set_bit(tid, &sta->driver_buffered_tids);
        else
index da9366632f378651d14c12d543aa80525d8a6ba8..a0b0aea76525c341711c129519a1c3f89a704bb9 100644 (file)
@@ -1835,6 +1835,33 @@ TRACE_EVENT(api_eosp,
        )
 );
 
+TRACE_EVENT(api_sta_set_buffered,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sta *sta,
+                u8 tid, bool buffered),
+
+       TP_ARGS(local, sta, tid, buffered),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               STA_ENTRY
+               __field(u8, tid)
+               __field(bool, buffered)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               STA_ASSIGN;
+               __entry->tid = tid;
+               __entry->buffered = buffered;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT STA_PR_FMT " tid:%d buffered:%d",
+               LOCAL_PR_ARG, STA_PR_ARG, __entry->tid, __entry->buffered
+       )
+);
+
 /*
  * Tracing for internal functions
  * (which may also be called in response to driver calls)
index 377cf974d97d15b41773b0ea7aed9ae7ebc1edba..9f3cbf14989b2513077479450bc8af6a41fafc45 100644 (file)
@@ -500,6 +500,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
                info->control.jiffies = jiffies;
                info->control.vif = &tx->sdata->vif;
                info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+               info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;
                skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb);
 
                if (!timer_pending(&local->sta_cleanup))
@@ -1073,6 +1074,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,
                        queued = true;
                        info->control.vif = &tx->sdata->vif;
                        info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+                       info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;
                        __skb_queue_tail(&tid_tx->pending, skb);
                        if (skb_queue_len(&tid_tx->pending) > STA_MAX_TX_BUFFER)
                                purge_skb = __skb_dequeue(&tid_tx->pending);
index df00f1978a77574857a9e8cf5c37cad66ce3e487..676dc0967f377f1251a761bf2dff0c35c8e92346 100644 (file)
@@ -2734,3 +2734,44 @@ int ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr,
        return ret;
 }
 EXPORT_SYMBOL(ieee80211_parse_p2p_noa);
+
+void ieee80211_recalc_dtim(struct ieee80211_local *local,
+                          struct ieee80211_sub_if_data *sdata)
+{
+       u64 tsf = drv_get_tsf(local, sdata);
+       u64 dtim_count = 0;
+       u16 beacon_int = sdata->vif.bss_conf.beacon_int * 1024;
+       u8 dtim_period = sdata->vif.bss_conf.dtim_period;
+       struct ps_data *ps;
+       u8 bcns_from_dtim;
+
+       if (tsf == -1ULL || !beacon_int || !dtim_period)
+               return;
+
+       if (sdata->vif.type == NL80211_IFTYPE_AP ||
+           sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+               if (!sdata->bss)
+                       return;
+
+               ps = &sdata->bss->ps;
+       } else if (ieee80211_vif_is_mesh(&sdata->vif)) {
+               ps = &sdata->u.mesh.ps;
+       } else {
+               return;
+       }
+
+       /*
+        * actually finds last dtim_count, mac80211 will update in
+        * __beacon_add_tim().
+        * dtim_count = dtim_period - (tsf / bcn_int) % dtim_period
+        */
+       do_div(tsf, beacon_int);
+       bcns_from_dtim = do_div(tsf, dtim_period);
+       /* just had a DTIM */
+       if (!bcns_from_dtim)
+               dtim_count = 0;
+       else
+               dtim_count = dtim_period - bcns_from_dtim;
+
+       ps->dtim_count = dtim_count;
+}
index 7313d379c0d32aea2c6a152c9756263ff7bf523f..21448d629b152adebff514e5d5a8d37e11a676e6 100644 (file)
@@ -127,7 +127,7 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
                 * APs with pairwise keys should never receive Michael MIC
                 * errors for non-zero keyidx because these are reserved for
                 * group keys and only the AP is sending real multicast
-                * frames in the BSS. (
+                * frames in the BSS.
                 */
                return RX_DROP_UNUSABLE;
        }
index b4f40fe84a0121381d221fd0c18a65043428fc17..d0afd82ebd776276795b8657bb4fc78286a7d458 100644 (file)
@@ -5285,12 +5285,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                        goto unlock;
                }
        } else {
-               enum ieee80211_band band;
-               n_channels = 0;
-
-               for (band = 0; band < IEEE80211_NUM_BANDS; band++)
-                       if (wiphy->bands[band])
-                               n_channels += wiphy->bands[band]->n_channels;
+               n_channels = ieee80211_get_num_supported_channels(wiphy);
        }
 
        if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
@@ -5498,11 +5493,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
                if (!n_channels)
                        return -EINVAL;
        } else {
-               n_channels = 0;
-
-               for (band = 0; band < IEEE80211_NUM_BANDS; band++)
-                       if (wiphy->bands[band])
-                               n_channels += wiphy->bands[band]->n_channels;
+               n_channels = ieee80211_get_num_supported_channels(wiphy);
        }
 
        if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
@@ -6795,6 +6786,55 @@ __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
        return NULL;
 }
 
+struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
+                                          enum nl80211_commands cmd,
+                                          enum nl80211_attrs attr,
+                                          int vendor_event_idx,
+                                          int approxlen, gfp_t gfp)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       const struct nl80211_vendor_cmd_info *info;
+
+       switch (cmd) {
+       case NL80211_CMD_TESTMODE:
+               if (WARN_ON(vendor_event_idx != -1))
+                       return NULL;
+               info = NULL;
+               break;
+       case NL80211_CMD_VENDOR:
+               if (WARN_ON(vendor_event_idx < 0 ||
+                           vendor_event_idx >= wiphy->n_vendor_events))
+                       return NULL;
+               info = &wiphy->vendor_events[vendor_event_idx];
+               break;
+       default:
+               WARN_ON(1);
+               return NULL;
+       }
+
+       return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0,
+                                          cmd, attr, info, gfp);
+}
+EXPORT_SYMBOL(__cfg80211_alloc_event_skb);
+
+void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp)
+{
+       struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
+       void *hdr = ((void **)skb->cb)[1];
+       struct nlattr *data = ((void **)skb->cb)[2];
+       enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE;
+
+       nla_nest_end(skb, data);
+       genlmsg_end(skb, hdr);
+
+       if (data->nla_type == NL80211_ATTR_VENDOR_DATA)
+               mcgrp = NL80211_MCGRP_VENDOR;
+
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0,
+                               mcgrp, gfp);
+}
+EXPORT_SYMBOL(__cfg80211_send_event_skb);
+
 #ifdef CONFIG_NL80211_TESTMODE
 static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
 {
@@ -6921,55 +6961,6 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
        rtnl_unlock();
        return err;
 }
-
-struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
-                                          enum nl80211_commands cmd,
-                                          enum nl80211_attrs attr,
-                                          int vendor_event_idx,
-                                          int approxlen, gfp_t gfp)
-{
-       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
-       const struct nl80211_vendor_cmd_info *info;
-
-       switch (cmd) {
-       case NL80211_CMD_TESTMODE:
-               if (WARN_ON(vendor_event_idx != -1))
-                       return NULL;
-               info = NULL;
-               break;
-       case NL80211_CMD_VENDOR:
-               if (WARN_ON(vendor_event_idx < 0 ||
-                           vendor_event_idx >= wiphy->n_vendor_events))
-                       return NULL;
-               info = &wiphy->vendor_events[vendor_event_idx];
-               break;
-       default:
-               WARN_ON(1);
-               return NULL;
-       }
-
-       return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0,
-                                          cmd, attr, info, gfp);
-}
-EXPORT_SYMBOL(__cfg80211_alloc_event_skb);
-
-void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp)
-{
-       struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
-       void *hdr = ((void **)skb->cb)[1];
-       struct nlattr *data = ((void **)skb->cb)[2];
-       enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE;
-
-       nla_nest_end(skb, data);
-       genlmsg_end(skb, hdr);
-
-       if (data->nla_type == NL80211_ATTR_VENDOR_DATA)
-               mcgrp = NL80211_MCGRP_VENDOR;
-
-       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0,
-                               mcgrp, gfp);
-}
-EXPORT_SYMBOL(__cfg80211_send_event_skb);
 #endif
 
 static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
index a32d52a04c275c6c148e71ab0a31edd9aae3020d..b528e31da2cfc07ccf5826ce02f3014a61b808e4 100644 (file)
@@ -1089,11 +1089,8 @@ int cfg80211_wext_siwscan(struct net_device *dev,
        /* Determine number of channels, needed to allocate creq */
        if (wreq && wreq->num_channels)
                n_channels = wreq->num_channels;
-       else {
-               for (band = 0; band < IEEE80211_NUM_BANDS; band++)
-                       if (wiphy->bands[band])
-                               n_channels += wiphy->bands[band]->n_channels;
-       }
+       else
+               n_channels = ieee80211_get_num_supported_channels(wiphy);
 
        creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
                       n_channels * sizeof(void *),
index 5d6e7bb2fc89a2fc1e4a1034f44beadb76b53e5a..a6350911850890dc40fdcd6326345866ffc342a9 100644 (file)
@@ -70,18 +70,11 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
        if (rdev->scan_req)
                return -EBUSY;
 
-       if (wdev->conn->params.channel) {
+       if (wdev->conn->params.channel)
                n_channels = 1;
-       } else {
-               enum ieee80211_band band;
-               n_channels = 0;
+       else
+               n_channels = ieee80211_get_num_supported_channels(wdev->wiphy);
 
-               for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
-                       if (!wdev->wiphy->bands[band])
-                               continue;
-                       n_channels += wdev->wiphy->bands[band]->n_channels;
-               }
-       }
        request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) +
                          sizeof(request->channels[0]) * n_channels,
                          GFP_KERNEL);
index 5618888853b24ffdac2853509a1528f70ac1661b..d39c37104ae2f125c5def9c943dc65ab8a669f5a 100644 (file)
@@ -879,7 +879,9 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
 
                dev->ieee80211_ptr->use_4addr = false;
                dev->ieee80211_ptr->mesh_id_up_len = 0;
+               wdev_lock(dev->ieee80211_ptr);
                rdev_set_qos_map(rdev, dev, NULL);
+               wdev_unlock(dev->ieee80211_ptr);
 
                switch (otype) {
                case NL80211_IFTYPE_AP:
@@ -1479,6 +1481,19 @@ int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
        return 0;
 }
 
+unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy)
+{
+       enum ieee80211_band band;
+       unsigned int n_channels = 0;
+
+       for (band = 0; band < IEEE80211_NUM_BANDS; band++)
+               if (wiphy->bands[band])
+                       n_channels += wiphy->bands[band]->n_channels;
+
+       return n_channels;
+}
+EXPORT_SYMBOL(ieee80211_get_num_supported_channels);
+
 /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
 /* Ethernet-II snap header (RFC1042 for most EtherTypes) */
 const unsigned char rfc1042_header[] __aligned(2) =
index e7c6e862580dcf64df32b4f499f7ae1c42c67609..5661a54ac7ee4ed1c1865d855e1b2681c67d07cc 100644 (file)
@@ -370,7 +370,7 @@ static int cfg80211_wext_siwretry(struct net_device *dev,
        u8 oshort = wdev->wiphy->retry_short;
        int err;
 
-       if (retry->disabled ||
+       if (retry->disabled || retry->value < 1 || retry->value > 255 ||
            (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
                return -EINVAL;
 
@@ -412,9 +412,9 @@ int cfg80211_wext_giwretry(struct net_device *dev,
                 * First return short value, iwconfig will ask long value
                 * later if needed
                 */
-               retry->flags |= IW_RETRY_LIMIT;
+               retry->flags |= IW_RETRY_LIMIT | IW_RETRY_SHORT;
                retry->value = wdev->wiphy->retry_short;
-               if (wdev->wiphy->retry_long != wdev->wiphy->retry_short)
+               if (wdev->wiphy->retry_long == wdev->wiphy->retry_short)
                        retry->flags |= IW_RETRY_LONG;
 
                return 0;