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: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2012-07-27 19:48:26 +02:00
parent 6fb47de9cf
commit e826117142

View 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;
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);
hsd->hw = hw;
INIT_DELAYED_WORK(&hsd->w, hw_scan_done);
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;