mac80211_hwsim: allow using channel contexts
authorJohannes Berg <johannes.berg@intel.com>
Fri, 27 Jul 2012 17:48:26 +0000 (19:48 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 30 Oct 2012 08:11:33 +0000 (09:11 +0100)
To use mac80211_hwsim for testing channel contexts it
has to support them, and for that it has to support
hw scan and hw-remain-on-channel.

Since it's pure software, the off-channel activities
are really not off-channel but listening and sending
on a second channel. Also, the multi-channel isn't
really doing TDM, it's just on both channels at the
same time.

For testing purposes, you can specify the number of
concurrent channels with a module parameter, it is
set to one by default. When set to two or more, the
userspace API for wmediumd is disabled as it has no
provisions for multi-channel yet.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
drivers/net/wireless/mac80211_hwsim.c

index 429ca3215fdbf3e0d48f82e3c1eb69381938fd46..96e91dd17b34c4dad9d6454c2a8914b2ed2281b1 100644 (file)
@@ -44,9 +44,9 @@ static int radios = 2;
 module_param(radios, int, 0444);
 MODULE_PARM_DESC(radios, "Number of simulated radios");
 
-static bool fake_hw_scan;
-module_param(fake_hw_scan, bool, 0444);
-MODULE_PARM_DESC(fake_hw_scan, "Install fake (no-op) hw-scan handler");
+static int channels = 1;
+module_param(channels, int, 0444);
+MODULE_PARM_DESC(channels, "Number of concurrent channels");
 
 /**
  * enum hwsim_regtest - the type of regulatory tests we offer
@@ -166,7 +166,9 @@ struct hwsim_vif_priv {
 static inline void hwsim_check_magic(struct ieee80211_vif *vif)
 {
        struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
-       WARN_ON(vp->magic != HWSIM_VIF_MAGIC);
+       WARN(vp->magic != HWSIM_VIF_MAGIC,
+            "Invalid VIF (%p) magic %#x, %pM, %d/%d\n",
+            vif, vp->magic, vif->addr, vif->type, vif->p2p);
 }
 
 static inline void hwsim_set_magic(struct ieee80211_vif *vif)
@@ -185,7 +187,7 @@ struct hwsim_sta_priv {
        u32 magic;
 };
 
-#define HWSIM_STA_MAGIC        0x6d537748
+#define HWSIM_STA_MAGIC        0x6d537749
 
 static inline void hwsim_check_sta_magic(struct ieee80211_sta *sta)
 {
@@ -205,6 +207,30 @@ static inline void hwsim_clear_sta_magic(struct ieee80211_sta *sta)
        sp->magic = 0;
 }
 
+struct hwsim_chanctx_priv {
+       u32 magic;
+};
+
+#define HWSIM_CHANCTX_MAGIC 0x6d53774a
+
+static inline void hwsim_check_chanctx_magic(struct ieee80211_chanctx_conf *c)
+{
+       struct hwsim_chanctx_priv *cp = (void *)c->drv_priv;
+       WARN_ON(cp->magic != HWSIM_CHANCTX_MAGIC);
+}
+
+static inline void hwsim_set_chanctx_magic(struct ieee80211_chanctx_conf *c)
+{
+       struct hwsim_chanctx_priv *cp = (void *)c->drv_priv;
+       cp->magic = HWSIM_CHANCTX_MAGIC;
+}
+
+static inline void hwsim_clear_chanctx_magic(struct ieee80211_chanctx_conf *c)
+{
+       struct hwsim_chanctx_priv *cp = (void *)c->drv_priv;
+       cp->magic = 0;
+}
+
 static struct class *hwsim_class;
 
 static struct net_device *hwsim_mon; /* global monitor netdev */
@@ -299,6 +325,13 @@ struct mac80211_hwsim_data {
 
        struct mac_address addresses[2];
 
+       struct ieee80211_channel *tmp_chan;
+       struct delayed_work roc_done;
+       struct delayed_work hw_scan;
+       struct cfg80211_scan_request *hw_scan_request;
+       struct ieee80211_vif *hw_scan_vif;
+       int scan_chan_idx;
+
        struct ieee80211_channel *channel;
        unsigned long beacon_int; /* in jiffies unit */
        unsigned int rx_filter;
@@ -396,7 +429,8 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw,
 }
 
 static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
-                                     struct sk_buff *tx_skb)
+                                     struct sk_buff *tx_skb,
+                                     struct ieee80211_channel *chan)
 {
        struct mac80211_hwsim_data *data = hw->priv;
        struct sk_buff *skb;
@@ -423,7 +457,7 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
        hdr->rt_tsft = __mac80211_hwsim_get_tsf(data);
        hdr->rt_flags = 0;
        hdr->rt_rate = txrate->bitrate / 5;
-       hdr->rt_channel = cpu_to_le16(data->channel->center_freq);
+       hdr->rt_channel = cpu_to_le16(chan->center_freq);
        flags = IEEE80211_CHAN_2GHZ;
        if (txrate->flags & IEEE80211_RATE_ERP_G)
                flags |= IEEE80211_CHAN_OFDM;
@@ -441,9 +475,9 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
 }
 
 
-static void mac80211_hwsim_monitor_ack(struct ieee80211_hw *hw, const u8 *addr)
+static void mac80211_hwsim_monitor_ack(struct ieee80211_channel *chan,
+                                      const u8 *addr)
 {
-       struct mac80211_hwsim_data *data = hw->priv;
        struct sk_buff *skb;
        struct hwsim_radiotap_hdr *hdr;
        u16 flags;
@@ -464,7 +498,7 @@ static void mac80211_hwsim_monitor_ack(struct ieee80211_hw *hw, const u8 *addr)
                                          (1 << IEEE80211_RADIOTAP_CHANNEL));
        hdr->rt_flags = 0;
        hdr->rt_rate = 0;
-       hdr->rt_channel = cpu_to_le16(data->channel->center_freq);
+       hdr->rt_channel = cpu_to_le16(chan->center_freq);
        flags = IEEE80211_CHAN_2GHZ;
        hdr->rt_chbitmask = cpu_to_le16(flags);
 
@@ -556,12 +590,6 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
        int i;
        struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES];
 
-       if (data->idle) {
-               wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
-               dev_kfree_skb(my_skb);
-               return;
-       }
-
        if (data->ps != PS_DISABLED)
                hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
        /* If the queue contains MAX_QUEUE skb's drop some */
@@ -629,8 +657,38 @@ nla_put_failure:
        printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
 }
 
+static bool hwsim_chans_compat(struct ieee80211_channel *c1,
+                              struct ieee80211_channel *c2)
+{
+       if (!c1 || !c2)
+               return false;
+
+       return c1->center_freq == c2->center_freq;
+}
+
+struct tx_iter_data {
+       struct ieee80211_channel *channel;
+       bool receive;
+};
+
+static void mac80211_hwsim_tx_iter(void *_data, u8 *addr,
+                                  struct ieee80211_vif *vif)
+{
+       struct tx_iter_data *data = _data;
+
+       if (!vif->chanctx_conf)
+               return;
+
+       if (!hwsim_chans_compat(data->channel,
+                               rcu_dereference(vif->chanctx_conf)->channel))
+               return;
+
+       data->receive = true;
+}
+
 static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
-                                         struct sk_buff *skb)
+                                         struct sk_buff *skb,
+                                         struct ieee80211_channel *chan)
 {
        struct mac80211_hwsim_data *data = hw->priv, *data2;
        bool ack = false;
@@ -639,15 +697,10 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
        struct ieee80211_rx_status rx_status;
        struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info);
 
-       if (data->idle) {
-               wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
-               return false;
-       }
-
        memset(&rx_status, 0, sizeof(rx_status));
        rx_status.flag |= RX_FLAG_MACTIME_MPDU;
-       rx_status.freq = data->channel->center_freq;
-       rx_status.band = data->channel->band;
+       rx_status.freq = chan->center_freq;
+       rx_status.band = chan->band;
        rx_status.rate_idx = info->control.rates[0].idx;
        if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS)
                rx_status.flag |= RX_FLAG_HT;
@@ -673,16 +726,30 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
        list_for_each_entry(data2, &hwsim_radios, list) {
                struct sk_buff *nskb;
                struct ieee80211_mgmt *mgmt;
+               struct tx_iter_data tx_iter_data = {
+                       .receive = false,
+                       .channel = chan,
+               };
 
                if (data == data2)
                        continue;
 
-               if (data2->idle || !data2->started ||
-                   !hwsim_ps_rx_ok(data2, skb) || !data2->channel ||
-                   data->channel->center_freq != data2->channel->center_freq ||
-                   !(data->group & data2->group))
+               if (!data2->started || (data2->idle && !data2->tmp_chan) ||
+                   !hwsim_ps_rx_ok(data2, skb))
                        continue;
 
+               if (!(data->group & data2->group))
+                       continue;
+
+               if (!hwsim_chans_compat(chan, data2->tmp_chan) &&
+                   !hwsim_chans_compat(chan, data2->channel)) {
+                       ieee80211_iterate_active_interfaces_atomic(
+                               data2->hw, mac80211_hwsim_tx_iter,
+                               &tx_iter_data);
+                       if (!tx_iter_data.receive)
+                               continue;
+               }
+
                nskb = skb_copy(skb, GFP_ATOMIC);
                if (nskb == NULL)
                        continue;
@@ -713,18 +780,51 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
                              struct ieee80211_tx_control *control,
                              struct sk_buff *skb)
 {
+       struct mac80211_hwsim_data *data = hw->priv;
+       struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_channel *channel;
        bool ack;
-       struct ieee80211_tx_info *txi;
        u32 _portid;
 
-       mac80211_hwsim_monitor_rx(hw, skb);
-
-       if (skb->len < 10) {
+       if (WARN_ON(skb->len < 10)) {
                /* Should not happen; just a sanity check for addr1 use */
                dev_kfree_skb(skb);
                return;
        }
 
+       if (channels == 1) {
+               channel = data->channel;
+       } else if (txi->hw_queue == 4) {
+               channel = data->tmp_chan;
+       } else {
+               chanctx_conf = rcu_dereference(txi->control.vif->chanctx_conf);
+               if (chanctx_conf)
+                       channel = chanctx_conf->channel;
+               else
+                       channel = NULL;
+       }
+
+       if (WARN(!channel, "TX w/o channel - queue = %d\n", txi->hw_queue)) {
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       if (data->idle && !data->tmp_chan) {
+               wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
+               dev_kfree_skb(skb);
+               return;
+       }
+
+       if (txi->control.vif)
+               hwsim_check_magic(txi->control.vif);
+       if (control->sta)
+               hwsim_check_sta_magic(control->sta);
+
+       txi->rate_driver_data[0] = channel;
+
+       mac80211_hwsim_monitor_rx(hw, skb, channel);
+
        /* wmediumd mode check */
        _portid = ACCESS_ONCE(wmediumd_portid);
 
@@ -732,15 +832,13 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
                return mac80211_hwsim_tx_frame_nl(hw, skb, _portid);
 
        /* NO wmediumd detected, perfect medium simulation */
-       ack = mac80211_hwsim_tx_frame_no_nl(hw, skb);
+       ack = mac80211_hwsim_tx_frame_no_nl(hw, skb, channel);
 
        if (ack && skb->len >= 16) {
                struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-               mac80211_hwsim_monitor_ack(hw, hdr->addr2);
+               mac80211_hwsim_monitor_ack(channel, hdr->addr2);
        }
 
-       txi = IEEE80211_SKB_CB(skb);
-
        ieee80211_tx_info_clear_status(txi);
 
        /* frame was transmitted at most favorable rate at first attempt */
@@ -778,6 +876,13 @@ static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw,
                    __func__, ieee80211_vif_type_p2p(vif),
                    vif->addr);
        hwsim_set_magic(vif);
+
+       vif->cab_queue = 0;
+       vif->hw_queue[IEEE80211_AC_VO] = 0;
+       vif->hw_queue[IEEE80211_AC_VI] = 1;
+       vif->hw_queue[IEEE80211_AC_BE] = 2;
+       vif->hw_queue[IEEE80211_AC_BK] = 3;
+
        return 0;
 }
 
@@ -807,14 +912,26 @@ static void mac80211_hwsim_remove_interface(
        hwsim_clear_magic(vif);
 }
 
+static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
+                                   struct sk_buff *skb,
+                                   struct ieee80211_channel *chan)
+{
+       u32 _pid = ACCESS_ONCE(wmediumd_portid);
+
+       mac80211_hwsim_monitor_rx(hw, skb, chan);
+
+       if (_pid)
+               return mac80211_hwsim_tx_frame_nl(hw, skb, _pid);
+
+       mac80211_hwsim_tx_frame_no_nl(hw, skb, chan);
+       dev_kfree_skb(skb);
+}
 
 static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
                                     struct ieee80211_vif *vif)
 {
        struct ieee80211_hw *hw = arg;
        struct sk_buff *skb;
-       struct ieee80211_tx_info *info;
-       u32 _portid;
 
        hwsim_check_magic(vif);
 
@@ -826,18 +943,9 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
        skb = ieee80211_beacon_get(hw, vif);
        if (skb == NULL)
                return;
-       info = IEEE80211_SKB_CB(skb);
-
-       mac80211_hwsim_monitor_rx(hw, skb);
-
-       /* wmediumd mode check */
-       _portid = ACCESS_ONCE(wmediumd_portid);
-
-       if (_portid)
-               return mac80211_hwsim_tx_frame_nl(hw, skb, _portid);
 
-       mac80211_hwsim_tx_frame_no_nl(hw, skb);
-       dev_kfree_skb(skb);
+       mac80211_hwsim_tx_frame(hw, skb,
+                               rcu_dereference(vif->chanctx_conf)->channel);
 }
 
 
@@ -877,7 +985,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
        wiphy_debug(hw->wiphy,
                    "%s (freq=%d/%s idle=%d ps=%d smps=%s)\n",
                    __func__,
-                   conf->channel->center_freq,
+                   conf->channel ? conf->channel->center_freq : 0,
                    hwsim_chantypes[conf->channel_type],
                    !!(conf->flags & IEEE80211_CONF_IDLE),
                    !!(conf->flags & IEEE80211_CONF_PS),
@@ -886,6 +994,9 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
        data->idle = !!(conf->flags & IEEE80211_CONF_IDLE);
 
        data->channel = conf->channel;
+
+       WARN_ON(data->channel && channels > 1);
+
        data->power_level = conf->power_level;
        if (!data->started || !data->beacon_int)
                del_timer(&data->beacon_timer);
@@ -1166,45 +1277,102 @@ static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop)
        /* Not implemented, queues only on kernel side */
 }
 
-struct hw_scan_done {
-       struct delayed_work w;
-       struct ieee80211_hw *hw;
-};
-
-static void hw_scan_done(struct work_struct *work)
+static void hw_scan_work(struct work_struct *work)
 {
-       struct hw_scan_done *hsd =
-               container_of(work, struct hw_scan_done, w.work);
+       struct mac80211_hwsim_data *hwsim =
+               container_of(work, struct mac80211_hwsim_data, hw_scan.work);
+       struct cfg80211_scan_request *req = hwsim->hw_scan_request;
+       int dwell, i;
 
-       ieee80211_scan_completed(hsd->hw, false);
-       kfree(hsd);
+       mutex_lock(&hwsim->mutex);
+       if (hwsim->scan_chan_idx >= req->n_channels) {
+               wiphy_debug(hwsim->hw->wiphy, "hw scan complete\n");
+               ieee80211_scan_completed(hwsim->hw, false);
+               hwsim->hw_scan_request = NULL;
+               hwsim->hw_scan_vif = NULL;
+               hwsim->tmp_chan = NULL;
+               mutex_unlock(&hwsim->mutex);
+               return;
+       }
+
+       wiphy_debug(hwsim->hw->wiphy, "hw scan %d MHz\n",
+                   req->channels[hwsim->scan_chan_idx]->center_freq);
+
+       hwsim->tmp_chan = req->channels[hwsim->scan_chan_idx];
+       if (hwsim->tmp_chan->flags & IEEE80211_CHAN_PASSIVE_SCAN ||
+           !req->n_ssids) {
+               dwell = 120;
+       } else {
+               dwell = 30;
+               /* send probes */
+               for (i = 0; i < req->n_ssids; i++) {
+                       struct sk_buff *probe;
+
+                       probe = ieee80211_probereq_get(hwsim->hw,
+                                                      hwsim->hw_scan_vif,
+                                                      req->ssids[i].ssid,
+                                                      req->ssids[i].ssid_len,
+                                                      req->ie, req->ie_len);
+                       if (!probe)
+                               continue;
+                       local_bh_disable();
+                       mac80211_hwsim_tx_frame(hwsim->hw, probe,
+                                               hwsim->tmp_chan);
+                       local_bh_enable();
+               }
+       }
+       ieee80211_queue_delayed_work(hwsim->hw, &hwsim->hw_scan,
+                                    msecs_to_jiffies(dwell));
+       hwsim->scan_chan_idx++;
+       mutex_unlock(&hwsim->mutex);
 }
 
 static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif,
                                  struct cfg80211_scan_request *req)
 {
-       struct hw_scan_done *hsd = kzalloc(sizeof(*hsd), GFP_KERNEL);
+       struct mac80211_hwsim_data *hwsim = hw->priv;
        int i;
 
-       if (!hsd)
-               return -ENOMEM;
-
-       hsd->hw = hw;
-       INIT_DELAYED_WORK(&hsd->w, hw_scan_done);
+       mutex_lock(&hwsim->mutex);
+       if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) {
+               mutex_unlock(&hwsim->mutex);
+               return -EBUSY;
+       }
+       hwsim->hw_scan_request = req;
+       hwsim->hw_scan_vif = vif;
+       hwsim->scan_chan_idx = 0;
+       mutex_unlock(&hwsim->mutex);
 
-       printk(KERN_DEBUG "hwsim hw_scan request\n");
+       wiphy_debug(hw->wiphy, "hwsim hw_scan request\n");
        for (i = 0; i < req->n_channels; i++)
                printk(KERN_DEBUG "hwsim hw_scan freq %d\n",
                        req->channels[i]->center_freq);
        print_hex_dump(KERN_DEBUG, "scan IEs: ", DUMP_PREFIX_OFFSET,
                        16, 1, req->ie, req->ie_len, 1);
 
-       ieee80211_queue_delayed_work(hw, &hsd->w, 2 * HZ);
+       ieee80211_queue_delayed_work(hwsim->hw, &hwsim->hw_scan, 0);
 
        return 0;
 }
 
+static void mac80211_hwsim_cancel_hw_scan(struct ieee80211_hw *hw,
+                                         struct ieee80211_vif *vif)
+{
+       struct mac80211_hwsim_data *hwsim = hw->priv;
+
+       wiphy_debug(hw->wiphy, "hwsim cancel_hw_scan\n");
+
+       cancel_delayed_work_sync(&hwsim->hw_scan);
+
+       mutex_lock(&hwsim->mutex);
+       ieee80211_scan_completed(hwsim->hw, true);
+       hwsim->tmp_chan = NULL;
+       hwsim->hw_scan_request = NULL;
+       hwsim->hw_scan_vif = NULL;
+       mutex_unlock(&hwsim->mutex);
+}
+
 static void mac80211_hwsim_sw_scan(struct ieee80211_hw *hw)
 {
        struct mac80211_hwsim_data *hwsim = hw->priv;
@@ -1235,6 +1403,105 @@ static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw)
        mutex_unlock(&hwsim->mutex);
 }
 
+static void hw_roc_done(struct work_struct *work)
+{
+       struct mac80211_hwsim_data *hwsim =
+               container_of(work, struct mac80211_hwsim_data, roc_done.work);
+
+       mutex_lock(&hwsim->mutex);
+       ieee80211_remain_on_channel_expired(hwsim->hw);
+       hwsim->tmp_chan = NULL;
+       mutex_unlock(&hwsim->mutex);
+
+       wiphy_debug(hwsim->hw->wiphy, "hwsim ROC expired\n");
+}
+
+static int mac80211_hwsim_roc(struct ieee80211_hw *hw,
+                             struct ieee80211_channel *chan,
+                             enum nl80211_channel_type channel_type,
+                             int duration)
+{
+       struct mac80211_hwsim_data *hwsim = hw->priv;
+
+       mutex_lock(&hwsim->mutex);
+       if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) {
+               mutex_unlock(&hwsim->mutex);
+               return -EBUSY;
+       }
+
+       hwsim->tmp_chan = chan;
+       mutex_unlock(&hwsim->mutex);
+
+       wiphy_debug(hw->wiphy, "hwsim ROC (%d MHz, %d ms)\n",
+                   chan->center_freq, duration);
+
+       ieee80211_ready_on_channel(hw);
+
+       ieee80211_queue_delayed_work(hw, &hwsim->roc_done,
+                                    msecs_to_jiffies(duration));
+       return 0;
+}
+
+static int mac80211_hwsim_croc(struct ieee80211_hw *hw)
+{
+       struct mac80211_hwsim_data *hwsim = hw->priv;
+
+       cancel_delayed_work_sync(&hwsim->roc_done);
+
+       mutex_lock(&hwsim->mutex);
+       hwsim->tmp_chan = NULL;
+       mutex_unlock(&hwsim->mutex);
+
+       wiphy_debug(hw->wiphy, "hwsim ROC canceled\n");
+
+       return 0;
+}
+
+static int mac80211_hwsim_add_chanctx(struct ieee80211_hw *hw,
+                                     struct ieee80211_chanctx_conf *ctx)
+{
+       hwsim_set_chanctx_magic(ctx);
+       wiphy_debug(hw->wiphy, "add channel context %d MHz/%d\n",
+                   ctx->channel->center_freq, ctx->channel_type);
+       return 0;
+}
+
+static void mac80211_hwsim_remove_chanctx(struct ieee80211_hw *hw,
+                                         struct ieee80211_chanctx_conf *ctx)
+{
+       wiphy_debug(hw->wiphy, "remove channel context %d MHz/%d\n",
+                   ctx->channel->center_freq, ctx->channel_type);
+       hwsim_check_chanctx_magic(ctx);
+       hwsim_clear_chanctx_magic(ctx);
+}
+
+static void mac80211_hwsim_change_chanctx(struct ieee80211_hw *hw,
+                                         struct ieee80211_chanctx_conf *ctx,
+                                         u32 changed)
+{
+       hwsim_check_chanctx_magic(ctx);
+       wiphy_debug(hw->wiphy, "change channel context %#x (%d MHz/%d)\n",
+                   changed, ctx->channel->center_freq, ctx->channel_type);
+}
+
+static int mac80211_hwsim_assign_vif_chanctx(struct ieee80211_hw *hw,
+                                            struct ieee80211_vif *vif,
+                                            struct ieee80211_chanctx_conf *ctx)
+{
+       hwsim_check_magic(vif);
+       hwsim_check_chanctx_magic(ctx);
+
+       return 0;
+}
+
+static void mac80211_hwsim_unassign_vif_chanctx(struct ieee80211_hw *hw,
+                                               struct ieee80211_vif *vif,
+                                               struct ieee80211_chanctx_conf *ctx)
+{
+       hwsim_check_magic(vif);
+       hwsim_check_chanctx_magic(ctx);
+}
+
 static struct ieee80211_ops mac80211_hwsim_ops =
 {
        .tx = mac80211_hwsim_tx,
@@ -1315,7 +1582,6 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
        struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
        struct sk_buff *skb;
        struct ieee80211_pspoll *pspoll;
-       u32 _portid;
 
        if (!vp->assoc)
                return;
@@ -1335,25 +1601,18 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
        memcpy(pspoll->bssid, vp->bssid, ETH_ALEN);
        memcpy(pspoll->ta, mac, ETH_ALEN);
 
-       /* wmediumd mode check */
-       _portid = ACCESS_ONCE(wmediumd_portid);
-
-       if (_portid)
-               return mac80211_hwsim_tx_frame_nl(data->hw, skb, _portid);
-
-       if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb))
-               printk(KERN_DEBUG "%s: PS-poll frame not ack'ed\n", __func__);
-       dev_kfree_skb(skb);
+       rcu_read_lock();
+       mac80211_hwsim_tx_frame(data->hw, skb,
+                               rcu_dereference(vif->chanctx_conf)->channel);
+       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;
-       u32 _portid;
 
        if (!vp->assoc)
                return;
@@ -1374,15 +1633,10 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
        memcpy(hdr->addr2, mac, ETH_ALEN);
        memcpy(hdr->addr3, vp->bssid, ETH_ALEN);
 
-       /* wmediumd mode check */
-       _portid = ACCESS_ONCE(wmediumd_portid);
-
-       if (_portid)
-               return mac80211_hwsim_tx_frame_nl(data->hw, skb, _portid);
-
-       if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb))
-               printk(KERN_DEBUG "%s: nullfunc frame not ack'ed\n", __func__);
-       dev_kfree_skb(skb);
+       rcu_read_lock();
+       mac80211_hwsim_tx_frame(data->hw, skb,
+                               rcu_dereference(vif->chanctx_conf)->channel);
+       rcu_read_unlock();
 }
 
 
@@ -1551,7 +1805,8 @@ 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(data2->hw, hdr->addr2);
+                       mac80211_hwsim_monitor_ack(txi->rate_driver_data[0],
+                                                  hdr->addr2);
                }
                txi->flags |= IEEE80211_TX_STAT_ACK;
        }
@@ -1566,7 +1821,7 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
                                          struct genl_info *info)
 {
 
-       struct mac80211_hwsim_data  *data2;
+       struct mac80211_hwsim_data *data2;
        struct ieee80211_rx_status rx_status;
        struct mac_address *dst;
        int frame_data_len;
@@ -1574,9 +1829,9 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
        struct sk_buff *skb = NULL;
 
        if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] ||
-          !info->attrs[HWSIM_ATTR_FRAME] ||
-          !info->attrs[HWSIM_ATTR_RX_RATE] ||
-          !info->attrs[HWSIM_ATTR_SIGNAL])
+           !info->attrs[HWSIM_ATTR_FRAME] ||
+           !info->attrs[HWSIM_ATTR_RX_RATE] ||
+           !info->attrs[HWSIM_ATTR_SIGNAL])
                goto out;
 
        dst = (struct mac_address *)nla_data(
@@ -1604,7 +1859,7 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
 
        /* check if radio is configured properly */
 
-       if (data2->idle || !data2->started || !data2->channel)
+       if (data2->idle || !data2->started)
                goto out;
 
        /*A frame is received from user space*/
@@ -1688,6 +1943,11 @@ static struct notifier_block hwsim_netlink_notifier = {
 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,
@@ -1710,6 +1970,10 @@ 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);
@@ -1732,7 +1996,7 @@ static const struct ieee80211_iface_limit hwsim_if_limits[] = {
        { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) },
 };
 
-static const struct ieee80211_iface_combination hwsim_if_comb = {
+static struct ieee80211_iface_combination hwsim_if_comb = {
        .limits = hwsim_if_limits,
        .n_limits = ARRAY_SIZE(hwsim_if_limits),
        .max_interfaces = 2048,
@@ -1750,10 +2014,30 @@ static int __init init_mac80211_hwsim(void)
        if (radios < 1 || radios > 100)
                return -EINVAL;
 
-       if (fake_hw_scan) {
+       if (channels < 1)
+               return -EINVAL;
+
+       if (channels > 1) {
+               hwsim_if_comb.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;
        }
 
        spin_lock_init(&hwsim_radio_lock);
@@ -1803,13 +2087,18 @@ static int __init init_mac80211_hwsim(void)
                hw->wiphy->iface_combinations = &hwsim_if_comb;
                hw->wiphy->n_iface_combinations = 1;
 
-               if (fake_hw_scan) {
+               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;
                }
 
+               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 = 4;
+               hw->queues = 5;
+               hw->offchannel_tx_hw_queue = 4;
                hw->wiphy->interface_modes =
                        BIT(NL80211_IFTYPE_STATION) |
                        BIT(NL80211_IFTYPE_AP) |
@@ -1824,7 +2113,8 @@ static int __init init_mac80211_hwsim(void)
                            IEEE80211_HW_SUPPORTS_STATIC_SMPS |
                            IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
                            IEEE80211_HW_AMPDU_AGGREGATION |
-                           IEEE80211_HW_WANT_MONITOR_VIF;
+                           IEEE80211_HW_WANT_MONITOR_VIF |
+                           IEEE80211_HW_QUEUE_CONTROL;
 
                hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
                                    WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;