Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e8261171 authored by Johannes Berg's avatar Johannes Berg
Browse files

mac80211_hwsim: allow using channel contexts



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: default avatarJohannes Berg <johannes.berg@intel.com>
parent 6fb47de9
Loading
Loading
Loading
Loading
+390 −100
Original line number Diff line number Diff line
@@ -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 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
	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;
	}
@@ -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;