Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[cascardo/linux.git] / drivers / net / wireless / mac80211_hwsim.c
index 59caa10..69d4c31 100644 (file)
@@ -57,6 +57,117 @@ static bool rctbl = false;
 module_param(rctbl, bool, 0444);
 MODULE_PARM_DESC(rctbl, "Handle rate control table");
 
+/**
+ * enum hwsim_regtest - the type of regulatory tests we offer
+ *
+ * These are the different values you can use for the regtest
+ * module parameter. This is useful to help test world roaming
+ * and the driver regulatory_hint() call and combinations of these.
+ * If you want to do specific alpha2 regulatory domain tests simply
+ * use the userspace regulatory request as that will be respected as
+ * well without the need of this module parameter. This is designed
+ * only for testing the driver regulatory request, world roaming
+ * and all possible combinations.
+ *
+ * @HWSIM_REGTEST_DISABLED: No regulatory tests are performed,
+ *     this is the default value.
+ * @HWSIM_REGTEST_DRIVER_REG_FOLLOW: Used for testing the driver regulatory
+ *     hint, only one driver regulatory hint will be sent as such the
+ *     secondary radios are expected to follow.
+ * @HWSIM_REGTEST_DRIVER_REG_ALL: Used for testing the driver regulatory
+ *     request with all radios reporting the same regulatory domain.
+ * @HWSIM_REGTEST_DIFF_COUNTRY: Used for testing the drivers calling
+ *     different regulatory domains requests. Expected behaviour is for
+ *     an intersection to occur but each device will still use their
+ *     respective regulatory requested domains. Subsequent radios will
+ *     use the resulting intersection.
+ * @HWSIM_REGTEST_WORLD_ROAM: Used for testing the world roaming. We accomplish
+ *     this by using a custom beacon-capable regulatory domain for the first
+ *     radio. All other device world roam.
+ * @HWSIM_REGTEST_CUSTOM_WORLD: Used for testing the custom world regulatory
+ *     domain requests. All radios will adhere to this custom world regulatory
+ *     domain.
+ * @HWSIM_REGTEST_CUSTOM_WORLD_2: Used for testing 2 custom world regulatory
+ *     domain requests. The first radio will adhere to the first custom world
+ *     regulatory domain, the second one to the second custom world regulatory
+ *     domain. All other devices will world roam.
+ * @HWSIM_REGTEST_STRICT_FOLLOW_: Used for testing strict regulatory domain
+ *     settings, only the first radio will send a regulatory domain request
+ *     and use strict settings. The rest of the radios are expected to follow.
+ * @HWSIM_REGTEST_STRICT_ALL: Used for testing strict regulatory domain
+ *     settings. All radios will adhere to this.
+ * @HWSIM_REGTEST_STRICT_AND_DRIVER_REG: Used for testing strict regulatory
+ *     domain settings, combined with secondary driver regulatory domain
+ *     settings. The first radio will get a strict regulatory domain setting
+ *     using the first driver regulatory request and the second radio will use
+ *     non-strict settings using the second driver regulatory request. All
+ *     other devices should follow the intersection created between the
+ *     first two.
+ * @HWSIM_REGTEST_ALL: Used for testing every possible mix. You will need
+ *     at least 6 radios for a complete test. We will test in this order:
+ *     1 - driver custom world regulatory domain
+ *     2 - second custom world regulatory domain
+ *     3 - first driver regulatory domain request
+ *     4 - second driver regulatory domain request
+ *     5 - strict regulatory domain settings using the third driver regulatory
+ *         domain request
+ *     6 and on - should follow the intersection of the 3rd, 4rth and 5th radio
+ *                regulatory requests.
+ */
+enum hwsim_regtest {
+       HWSIM_REGTEST_DISABLED = 0,
+       HWSIM_REGTEST_DRIVER_REG_FOLLOW = 1,
+       HWSIM_REGTEST_DRIVER_REG_ALL = 2,
+       HWSIM_REGTEST_DIFF_COUNTRY = 3,
+       HWSIM_REGTEST_WORLD_ROAM = 4,
+       HWSIM_REGTEST_CUSTOM_WORLD = 5,
+       HWSIM_REGTEST_CUSTOM_WORLD_2 = 6,
+       HWSIM_REGTEST_STRICT_FOLLOW = 7,
+       HWSIM_REGTEST_STRICT_ALL = 8,
+       HWSIM_REGTEST_STRICT_AND_DRIVER_REG = 9,
+       HWSIM_REGTEST_ALL = 10,
+};
+
+/* Set to one of the HWSIM_REGTEST_* values above */
+static int regtest = HWSIM_REGTEST_DISABLED;
+module_param(regtest, int, 0444);
+MODULE_PARM_DESC(regtest, "The type of regulatory test we want to run");
+
+static const char *hwsim_alpha2s[] = {
+       "FI",
+       "AL",
+       "US",
+       "DE",
+       "JP",
+       "AL",
+};
+
+static const struct ieee80211_regdomain hwsim_world_regdom_custom_01 = {
+       .n_reg_rules = 4,
+       .alpha2 =  "99",
+       .reg_rules = {
+               REG_RULE(2412-10, 2462+10, 40, 0, 20, 0),
+               REG_RULE(2484-10, 2484+10, 40, 0, 20, 0),
+               REG_RULE(5150-10, 5240+10, 40, 0, 30, 0),
+               REG_RULE(5745-10, 5825+10, 40, 0, 30, 0),
+       }
+};
+
+static const struct ieee80211_regdomain hwsim_world_regdom_custom_02 = {
+       .n_reg_rules = 2,
+       .alpha2 =  "99",
+       .reg_rules = {
+               REG_RULE(2412-10, 2462+10, 40, 0, 20, 0),
+               REG_RULE(5725-10, 5850+10, 40, 0, 30,
+                        NL80211_RRF_NO_IR),
+       }
+};
+
+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];
@@ -215,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;
@@ -229,7 +384,7 @@ struct mac80211_hwsim_data {
        struct ieee80211_iface_combination if_combination;
 
        struct mac_address addresses[2];
-       int channels;
+       int channels, idx;
 
        struct ieee80211_channel *tmp_chan;
        struct delayed_work roc_done;
@@ -297,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)
 {
@@ -535,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 */
@@ -1284,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)
@@ -1623,220 +1934,288 @@ static const struct ieee80211_ops mac80211_hwsim_ops = {
 
 static struct ieee80211_ops mac80211_hwsim_mchan_ops;
 
-static void mac80211_hwsim_destroy_radio(struct mac80211_hwsim_data *data)
-{
-       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 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)
 {
+       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);
-       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);
-       }
+       idx = hwsim_radio_idx++;
        spin_unlock_bh(&hwsim_radio_lock);
-       class_destroy(hwsim_class);
-}
 
-static struct platform_driver mac80211_hwsim_driver = {
-       .driver = {
-               .name = "mac80211_hwsim",
-               .owner = THIS_MODULE,
-       },
-};
+       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;
 
-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 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;
-}
+       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;
+       }
 
+       skb_queue_head_init(&data->pending);
 
-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;
+       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;
 
-       if (!vp->assoc)
-               return;
+       data->channels = channels;
+       data->idx = idx;
 
-       wiphy_debug(data->hw->wiphy,
-                   "%s: send PS-Poll to %pM for aid %d\n",
-                   __func__, vp->bssid, vp->aid);
+       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);
+       }
 
-       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);
+       INIT_DELAYED_WORK(&data->roc_done, hw_roc_done);
+       INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work);
 
-       rcu_read_lock();
-       mac80211_hwsim_tx_frame(data->hw, skb,
-                               rcu_dereference(vif->chanctx_conf)->def.chan);
-       rcu_read_unlock();
-}
+       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);
 
-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;
+       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;
 
-       if (!vp->assoc)
-               return;
+       hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
+                           WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+                           WIPHY_FLAG_AP_UAPSD;
+       hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
 
-       wiphy_debug(data->hw->wiphy,
-                   "%s: send data::nullfunc to %pM ps=%d\n",
-                   __func__, vp->bssid, ps);
+       /* 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);
 
-       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);
+       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));
 
-       rcu_read_lock();
-       mac80211_hwsim_tx_frame(data->hw, skb,
-                               rcu_dereference(vif->chanctx_conf)->def.chan);
-       rcu_read_unlock();
-}
+       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;
 
-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);
-}
+               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_nullfunc_no_ps(void *dat, u8 *mac,
-                                     struct ieee80211_vif *vif)
-{
-       struct mac80211_hwsim_data *data = dat;
-       hwsim_send_nullfunc(data, mac, vif, 0);
-}
+       /* By default all radios belong to the first group */
+       data->group = 1;
+       mutex_init(&data->mutex);
 
+       /* Enable frame retransmissions for lossy channels */
+       hw->max_rates = 4;
+       hw->max_rate_tries = 11;
 
-static int hwsim_fops_ps_read(void *dat, u64 *val)
-{
-       struct mac80211_hwsim_data *data = dat;
-       *val = data->ps;
-       return 0;
-}
+       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);
+       }
 
-static int hwsim_fops_ps_write(void *dat, u64 val)
-{
-       struct mac80211_hwsim_data *data = dat;
-       enum ps_mode old_ps;
+       err = ieee80211_register_hw(hw);
+       if (err < 0) {
+               printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n",
+                      err);
+               goto failed_hw;
+       }
 
-       if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL &&
-           val != PS_MANUAL_POLL)
-               return -EINVAL;
+       wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr);
 
-       old_ps = data->ps;
-       data->ps = val;
+       if (reg_alpha2)
+               regulatory_hint(hw->wiphy, reg_alpha2);
 
-       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;
-}
+       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);
 
-DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write,
-                       "%llu\n");
+       tasklet_hrtimer_init(&data->beacon_timer,
+                            mac80211_hwsim_beacon,
+                            CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS);
 
-static int hwsim_write_simulate_radar(void *dat, u64 val)
-{
-       struct mac80211_hwsim_data *data = dat;
+       spin_lock_bh(&hwsim_radio_lock);
+       list_add_tail(&data->list, &hwsim_radios);
+       spin_unlock_bh(&hwsim_radio_lock);
 
-       ieee80211_radar_detected(data->hw);
+       return idx;
 
-       return 0;
+failed_hw:
+       device_unregister(data->dev);
+failed_drvdata:
+       ieee80211_free_hw(hw);
+failed:
+       return err;
 }
 
-DEFINE_SIMPLE_ATTRIBUTE(hwsim_simulate_radar, NULL,
-                       hwsim_write_simulate_radar, "%llu\n");
-
-static int hwsim_fops_group_read(void *dat, u64 *val)
+static void mac80211_hwsim_destroy_radio(struct mac80211_hwsim_data *data)
 {
-       struct mac80211_hwsim_data *data = dat;
-       *val = data->group;
-       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_group_write(void *dat, u64 val)
+static void mac80211_hwsim_free(void)
 {
-       struct mac80211_hwsim_data *data = dat;
-       data->group = val;
-       return 0;
+       struct mac80211_hwsim_data *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);
+       }
+       spin_unlock_bh(&hwsim_radio_lock);
+       class_destroy(hwsim_class);
 }
 
-DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
-                       hwsim_fops_group_read, hwsim_fops_group_write,
-                       "%llx\n");
+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 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;
+}
 
-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;
                }
@@ -1859,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 */
@@ -1918,7 +2296,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
           (hwsim_flags & HWSIM_TX_STAT_ACK)) {
                if (skb->len >= 16) {
                        hdr = (struct ieee80211_hdr *) skb->data;
-                       mac80211_hwsim_monitor_ack(txi->rate_driver_data[0],
+                       mac80211_hwsim_monitor_ack(data2->channel,
                                                   hdr->addr2);
                }
                txi->flags |= IEEE80211_TX_STAT_ACK;
@@ -1936,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 */
@@ -1975,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;
@@ -1997,350 +2374,168 @@ out:
 static int hwsim_register_received_nl(struct sk_buff *skb_2,
                                      struct genl_info *info)
 {
-       if (info == NULL)
-               goto out;
-
-       wmediumd_portid = info->snd_portid;
-
-       printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, "
-              "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;
-}
-
-/* Generic Netlink operations array */
-static const struct genl_ops hwsim_ops[] = {
-       {
-               .cmd = HWSIM_CMD_REGISTER,
-               .policy = hwsim_genl_policy,
-               .doit = hwsim_register_received_nl,
-               .flags = GENL_ADMIN_PERM,
-       },
-       {
-               .cmd = HWSIM_CMD_FRAME,
-               .policy = hwsim_genl_policy,
-               .doit = hwsim_cloned_frame_received_nl,
-       },
-       {
-               .cmd = HWSIM_CMD_TX_INFO_FRAME,
-               .policy = hwsim_genl_policy,
-               .doit = hwsim_tx_info_frame_received_nl,
-       },
-};
-
-static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
-                                        unsigned long state,
-                                        void *_notify)
-{
-       struct netlink_notify *notify = _notify;
-
-       if (state != NETLINK_URELEASE)
-               return NOTIFY_DONE;
-
-       if (notify->portid == wmediumd_portid) {
-               printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
-                      " socket, switching to perfect channel medium\n");
-               wmediumd_portid = 0;
-       }
-       return NOTIFY_DONE;
-
-}
-
-static struct notifier_block hwsim_netlink_notifier = {
-       .notifier_call = mac80211_hwsim_netlink_notify,
-};
-
-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);
-       if (rc)
-               goto failure;
-
-       rc = netlink_register_notifier(&hwsim_netlink_notifier);
-       if (rc)
-               goto failure;
-
-       return 0;
-
-failure:
-       printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
-       return -EINVAL;
-}
-
-static void hwsim_exit_netlink(void)
-{
-       /* userspace test API hasn't been adjusted for multi-channel */
-       if (channels > 1)
-               return;
-
-       /* unregister the notifier */
-       netlink_unregister_notifier(&hwsim_netlink_notifier);
-       /* unregister the family */
-       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 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 int __init mac80211_hwsim_create_radio(int idx)
-{
-       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;
-
-       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;
+       int chans = 1;
 
-       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;
-       }
+       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);
 
-       skb_queue_head_init(&data->pending);
+       /* 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;
 
-       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;
+       if (wmediumd_portid)
+               return -EBUSY;
 
-       data->channels = channels;
+       wmediumd_portid = info->snd_portid;
 
-       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);
-       }
+       printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, "
+              "switching to wmediumd mode with pid %d\n", info->snd_portid);
 
-       INIT_DELAYED_WORK(&data->roc_done, hw_roc_done);
-       INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work);
+       return 0;
+}
 
-       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);
+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];
 
-       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;
+       if (info->attrs[HWSIM_ATTR_CHANNELS])
+               chans = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
 
-       hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
-                           WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
-                           WIPHY_FLAG_AP_UAPSD;
-       hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
+       if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2])
+               alpha2 = nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]);
 
-       /* 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);
+       if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) {
+               u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]);
 
-       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));
+               if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom))
+                       return -EINVAL;
+               regd = hwsim_world_regdom_custom[idx];
+       }
 
-       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;
-               }
+       return mac80211_hwsim_create_radio(chans, alpha2, regd, reg_strict);
+}
 
-               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;
+static int hwsim_destroy_radio_nl(struct sk_buff *msg, struct genl_info *info)
+{
+       struct mac80211_hwsim_data *data;
+       int idx;
 
-               hw->wiphy->bands[band] = sband;
+       if (!info->attrs[HWSIM_ATTR_RADIO_ID])
+               return -EINVAL;
+       idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
 
-               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;
+       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);
 
-       /* By default all radios belong to the first group */
-       data->group = 1;
-       mutex_init(&data->mutex);
+       return -ENODEV;
+}
 
-       /* Enable frame retransmissions for lossy channels */
-       hw->max_rates = 4;
-       hw->max_rate_tries = 11;
+/* Generic Netlink operations array */
+static const struct genl_ops hwsim_ops[] = {
+       {
+               .cmd = HWSIM_CMD_REGISTER,
+               .policy = hwsim_genl_policy,
+               .doit = hwsim_register_received_nl,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = HWSIM_CMD_FRAME,
+               .policy = hwsim_genl_policy,
+               .doit = hwsim_cloned_frame_received_nl,
+       },
+       {
+               .cmd = HWSIM_CMD_TX_INFO_FRAME,
+               .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,
+       },
+};
 
-       err = ieee80211_register_hw(hw);
-       if (err < 0) {
-               printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n",
-                      err);
-               goto failed_hw;
+static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
+                                        unsigned long state,
+                                        void *_notify)
+{
+       struct netlink_notify *notify = _notify;
+
+       if (state != NETLINK_URELEASE)
+               return NOTIFY_DONE;
+
+       if (notify->portid == wmediumd_portid) {
+               printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
+                      " socket, switching to perfect channel medium\n");
+               wmediumd_portid = 0;
        }
+       return NOTIFY_DONE;
 
-       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 (data->channels == 1)
-               debugfs_create_file("dfs_simulate_radar", 0222,
-                                   data->debugfs,
-                                   data, &hwsim_simulate_radar);
+static struct notifier_block hwsim_netlink_notifier = {
+       .notifier_call = mac80211_hwsim_netlink_notify,
+};
 
-       tasklet_hrtimer_init(&data->beacon_timer,
-                            mac80211_hwsim_beacon,
-                            CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS);
+static int hwsim_init_netlink(void)
+{
+       int rc;
 
-       spin_lock_bh(&hwsim_radio_lock);
-       list_add_tail(&data->list, &hwsim_radios);
-       spin_unlock_bh(&hwsim_radio_lock);
+       printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
+
+       rc = genl_register_family_with_ops(&hwsim_genl_family, hwsim_ops);
+       if (rc)
+               goto failure;
+
+       rc = netlink_register_notifier(&hwsim_netlink_notifier);
+       if (rc)
+               goto failure;
 
        return 0;
 
-failed_hw:
-       device_unregister(data->dev);
-failed_drvdata:
-       ieee80211_free_hw(hw);
-failed:
-       return err;
+failure:
+       printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
+       return -EINVAL;
+}
+
+static void hwsim_exit_netlink(void)
+{
+       /* unregister the notifier */
+       netlink_unregister_notifier(&hwsim_netlink_notifier);
+       /* unregister the family */
+       genl_unregister_family(&hwsim_genl_family);
 }
 
 static int __init init_mac80211_hwsim(void)
 {
        int i, err;
 
-       if (radios < 1 || radios > 100)
+       if (radios < 0 || radios > 100)
                return -EINVAL;
 
        if (channels < 1)
@@ -2375,8 +2570,78 @@ static int __init init_mac80211_hwsim(void)
        }
 
        for (i = 0; i < radios; i++) {
-               err = mac80211_hwsim_create_radio(i);
-               if (err)
+               const char *reg_alpha2 = NULL;
+               const struct ieee80211_regdomain *regd = NULL;
+               bool reg_strict = false;
+
+               switch (regtest) {
+               case HWSIM_REGTEST_DIFF_COUNTRY:
+                       if (i < ARRAY_SIZE(hwsim_alpha2s))
+                               reg_alpha2 = hwsim_alpha2s[i];
+                       break;
+               case HWSIM_REGTEST_DRIVER_REG_FOLLOW:
+                       if (!i)
+                               reg_alpha2 = hwsim_alpha2s[0];
+                       break;
+               case HWSIM_REGTEST_STRICT_ALL:
+                       reg_strict = true;
+               case HWSIM_REGTEST_DRIVER_REG_ALL:
+                       reg_alpha2 = hwsim_alpha2s[0];
+                       break;
+               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:
+                       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) {
+                               reg_strict = true;
+                               reg_alpha2 = hwsim_alpha2s[0];
+                       }
+                       break;
+               case HWSIM_REGTEST_STRICT_AND_DRIVER_REG:
+                       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:
+                       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;
+               }
+
+               err = mac80211_hwsim_create_radio(channels, reg_alpha2,
+                                                 regd, reg_strict);
+               if (err < 0)
                        goto out_free_radios;
        }