summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/mac80211_hwsim.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/mac80211_hwsim.c')
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c1213
1 files changed, 646 insertions, 567 deletions
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index a1b32ee9594a..69d4c3179d04 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -159,10 +159,15 @@ static const struct ieee80211_regdomain hwsim_world_regdom_custom_02 = {
.reg_rules = {
REG_RULE(2412-10, 2462+10, 40, 0, 20, 0),
REG_RULE(5725-10, 5850+10, 40, 0, 30,
- NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS),
+ 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];
@@ -321,8 +326,52 @@ static const struct ieee80211_rate hwsim_rates[] = {
{ .bitrate = 540 }
};
+static const struct ieee80211_iface_limit hwsim_if_limits[] = {
+ { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) },
+ { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+#ifdef CONFIG_MAC80211_MESH
+ BIT(NL80211_IFTYPE_MESH_POINT) |
+#endif
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_GO) },
+ { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) },
+};
+
+static const struct ieee80211_iface_limit hwsim_if_dfs_limits[] = {
+ { .max = 8, .types = BIT(NL80211_IFTYPE_AP) },
+};
+
+static const struct ieee80211_iface_combination hwsim_if_comb[] = {
+ {
+ .limits = hwsim_if_limits,
+ .n_limits = ARRAY_SIZE(hwsim_if_limits),
+ .max_interfaces = 2048,
+ .num_different_channels = 1,
+ },
+ {
+ .limits = hwsim_if_dfs_limits,
+ .n_limits = ARRAY_SIZE(hwsim_if_dfs_limits),
+ .max_interfaces = 8,
+ .num_different_channels = 1,
+ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40) |
+ BIT(NL80211_CHAN_WIDTH_80) |
+ BIT(NL80211_CHAN_WIDTH_160),
+ }
+};
+
static spinlock_t hwsim_radio_lock;
static struct list_head hwsim_radios;
+static int hwsim_radio_idx;
+
+static struct platform_driver mac80211_hwsim_driver = {
+ .driver = {
+ .name = "mac80211_hwsim",
+ .owner = THIS_MODULE,
+ },
+};
struct mac80211_hwsim_data {
struct list_head list;
@@ -332,8 +381,10 @@ struct mac80211_hwsim_data {
struct ieee80211_channel channels_2ghz[ARRAY_SIZE(hwsim_channels_2ghz)];
struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)];
struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)];
+ struct ieee80211_iface_combination if_combination;
struct mac_address addresses[2];
+ int channels, idx;
struct ieee80211_channel *tmp_chan;
struct delayed_work roc_done;
@@ -353,7 +404,6 @@ struct mac80211_hwsim_data {
} ps;
bool ps_poll_pending;
struct dentry *debugfs;
- struct dentry *debugfs_ps;
struct sk_buff_head pending; /* packets pending */
/*
@@ -362,7 +412,6 @@ struct mac80211_hwsim_data {
* radio can be in more then one group.
*/
u64 group;
- struct dentry *debugfs_group;
int power_level;
@@ -403,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)
{
@@ -641,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 */
@@ -880,7 +1087,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
return;
}
- if (channels == 1) {
+ if (data->channels == 1) {
channel = data->channel;
} else if (txi->hw_queue == 4) {
channel = data->tmp_chan;
@@ -908,7 +1115,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
if (control->sta)
hwsim_check_sta_magic(control->sta);
- if (rctbl)
+ if (hw->flags & IEEE80211_HW_SUPPORTS_RC_TABLE)
ieee80211_get_tx_rates(txi->control.vif, control->sta, skb,
txi->control.rates,
ARRAY_SIZE(txi->control.rates));
@@ -1015,7 +1222,7 @@ static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
{
u32 _pid = ACCESS_ONCE(wmediumd_portid);
- if (rctbl) {
+ if (hw->flags & IEEE80211_HW_SUPPORTS_RC_TABLE) {
struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
ieee80211_get_tx_rates(txi->control.vif, NULL, skb,
txi->control.rates,
@@ -1052,7 +1259,7 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
if (skb == NULL)
return;
info = IEEE80211_SKB_CB(skb);
- if (rctbl)
+ if (hw->flags & IEEE80211_HW_SUPPORTS_RC_TABLE)
ieee80211_get_tx_rates(vif, NULL, skb,
info->control.rates,
ARRAY_SIZE(info->control.rates));
@@ -1143,7 +1350,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
data->channel = conf->chandef.chan;
- WARN_ON(data->channel && channels > 1);
+ WARN_ON(data->channel && data->channels > 1);
data->power_level = conf->power_level;
if (!data->started || !data->beacon_int)
@@ -1390,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)
@@ -1493,7 +1698,7 @@ static void hw_scan_work(struct work_struct *work)
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 ||
+ if (hwsim->tmp_chan->flags & IEEE80211_CHAN_NO_IR ||
!req->n_ssids) {
dwell = 120;
} else {
@@ -1702,8 +1907,7 @@ static void mac80211_hwsim_unassign_vif_chanctx(struct ieee80211_hw *hw,
hwsim_check_chanctx_magic(ctx);
}
-static struct ieee80211_ops mac80211_hwsim_ops =
-{
+static const struct ieee80211_ops mac80211_hwsim_ops = {
.tx = mac80211_hwsim_tx,
.start = mac80211_hwsim_start,
.stop = mac80211_hwsim_stop,
@@ -1728,208 +1932,290 @@ static struct ieee80211_ops mac80211_hwsim_ops =
.set_tsf = mac80211_hwsim_set_tsf,
};
+static struct ieee80211_ops mac80211_hwsim_mchan_ops;
-static void mac80211_hwsim_free(void)
+static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
+ const struct ieee80211_regdomain *regd,
+ bool reg_strict)
{
- struct list_head tmplist, *i, *tmp;
- struct mac80211_hwsim_data *data, *tmpdata;
-
- INIT_LIST_HEAD(&tmplist);
+ int err;
+ u8 addr[ETH_ALEN];
+ struct mac80211_hwsim_data *data;
+ struct ieee80211_hw *hw;
+ enum ieee80211_band band;
+ const struct ieee80211_ops *ops = &mac80211_hwsim_ops;
+ int idx;
spin_lock_bh(&hwsim_radio_lock);
- list_for_each_safe(i, tmp, &hwsim_radios)
- list_move(i, &tmplist);
+ idx = hwsim_radio_idx++;
spin_unlock_bh(&hwsim_radio_lock);
- list_for_each_entry_safe(data, tmpdata, &tmplist, list) {
- debugfs_remove(data->debugfs_group);
- debugfs_remove(data->debugfs_ps);
- debugfs_remove(data->debugfs);
- ieee80211_unregister_hw(data->hw);
- device_release_driver(data->dev);
- device_unregister(data->dev);
- ieee80211_free_hw(data->hw);
+ if (channels > 1)
+ ops = &mac80211_hwsim_mchan_ops;
+ hw = ieee80211_alloc_hw(sizeof(*data), ops);
+ if (!hw) {
+ printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw failed\n");
+ err = -ENOMEM;
+ goto failed;
+ }
+ data = hw->priv;
+ data->hw = hw;
+
+ data->dev = device_create(hwsim_class, NULL, 0, hw, "hwsim%d", idx);
+ if (IS_ERR(data->dev)) {
+ printk(KERN_DEBUG
+ "mac80211_hwsim: device_create failed (%ld)\n",
+ PTR_ERR(data->dev));
+ err = -ENOMEM;
+ goto failed_drvdata;
+ }
+ data->dev->driver = &mac80211_hwsim_driver.driver;
+ err = device_bind_driver(data->dev);
+ if (err != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n",
+ err);
+ goto failed_hw;
}
- class_destroy(hwsim_class);
-}
-
-static struct platform_driver mac80211_hwsim_driver = {
- .driver = {
- .name = "mac80211_hwsim",
- .owner = THIS_MODULE,
- },
-};
-
-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;
-}
+ 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;
+
+ data->channels = channels;
+ data->idx = idx;
+
+ if (data->channels > 1) {
+ hw->wiphy->max_scan_ssids = 255;
+ hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+ hw->wiphy->max_remain_on_channel_duration = 1000;
+ /* For channels > 1 DFS is not allowed */
+ hw->wiphy->n_iface_combinations = 1;
+ hw->wiphy->iface_combinations = &data->if_combination;
+ data->if_combination = hwsim_if_comb[0];
+ data->if_combination.num_different_channels = data->channels;
+ } else {
+ hw->wiphy->iface_combinations = hwsim_if_comb;
+ hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb);
+ }
- if (!vp->assoc)
- return;
+ INIT_DELAYED_WORK(&data->roc_done, hw_roc_done);
+ INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work);
+
+ hw->queues = 5;
+ hw->offchannel_tx_hw_queue = 4;
+ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_MESH_POINT) |
+ BIT(NL80211_IFTYPE_P2P_DEVICE);
+
+ hw->flags = IEEE80211_HW_MFP_CAPABLE |
+ IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_SUPPORTS_STATIC_SMPS |
+ IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
+ IEEE80211_HW_AMPDU_AGGREGATION |
+ IEEE80211_HW_WANT_MONITOR_VIF |
+ IEEE80211_HW_QUEUE_CONTROL |
+ IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
+ if (rctbl)
+ hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE;
+
+ hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+ WIPHY_FLAG_AP_UAPSD;
+ hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
+
+ /* ask mac80211 to reserve space for magic */
+ hw->vif_data_size = sizeof(struct hwsim_vif_priv);
+ hw->sta_data_size = sizeof(struct hwsim_sta_priv);
+ hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv);
+
+ memcpy(data->channels_2ghz, hwsim_channels_2ghz,
+ sizeof(hwsim_channels_2ghz));
+ memcpy(data->channels_5ghz, hwsim_channels_5ghz,
+ sizeof(hwsim_channels_5ghz));
+ memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates));
+
+ for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
+ struct ieee80211_supported_band *sband = &data->bands[band];
+ switch (band) {
+ case IEEE80211_BAND_2GHZ:
+ sband->channels = data->channels_2ghz;
+ sband->n_channels = ARRAY_SIZE(hwsim_channels_2ghz);
+ sband->bitrates = data->rates;
+ sband->n_bitrates = ARRAY_SIZE(hwsim_rates);
+ break;
+ case IEEE80211_BAND_5GHZ:
+ sband->channels = data->channels_5ghz;
+ sband->n_channels = ARRAY_SIZE(hwsim_channels_5ghz);
+ sband->bitrates = data->rates + 4;
+ sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4;
+ break;
+ default:
+ continue;
+ }
- wiphy_debug(data->hw->wiphy,
- "%s: send PS-Poll to %pM for aid %d\n",
- __func__, vp->bssid, vp->aid);
+ sband->ht_cap.ht_supported = true;
+ sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_GRN_FLD |
+ IEEE80211_HT_CAP_SGI_40 |
+ IEEE80211_HT_CAP_DSSSCCK40;
+ sband->ht_cap.ampdu_factor = 0x3;
+ sband->ht_cap.ampdu_density = 0x6;
+ memset(&sband->ht_cap.mcs, 0,
+ sizeof(sband->ht_cap.mcs));
+ sband->ht_cap.mcs.rx_mask[0] = 0xff;
+ sband->ht_cap.mcs.rx_mask[1] = 0xff;
+ sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+
+ hw->wiphy->bands[band] = sband;
+
+ sband->vht_cap.vht_supported = true;
+ sband->vht_cap.cap =
+ IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
+ IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ |
+ IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
+ IEEE80211_VHT_CAP_RXLDPC |
+ IEEE80211_VHT_CAP_SHORT_GI_80 |
+ IEEE80211_VHT_CAP_SHORT_GI_160 |
+ IEEE80211_VHT_CAP_TXSTBC |
+ IEEE80211_VHT_CAP_RXSTBC_1 |
+ IEEE80211_VHT_CAP_RXSTBC_2 |
+ IEEE80211_VHT_CAP_RXSTBC_3 |
+ IEEE80211_VHT_CAP_RXSTBC_4 |
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+ sband->vht_cap.vht_mcs.rx_mcs_map =
+ cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_8 << 0 |
+ IEEE80211_VHT_MCS_SUPPORT_0_8 << 2 |
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 |
+ IEEE80211_VHT_MCS_SUPPORT_0_8 << 6 |
+ IEEE80211_VHT_MCS_SUPPORT_0_8 << 8 |
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 |
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 |
+ IEEE80211_VHT_MCS_SUPPORT_0_8 << 14);
+ sband->vht_cap.vht_mcs.tx_mcs_map =
+ sband->vht_cap.vht_mcs.rx_mcs_map;
+ }
- 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);
+ /* By default all radios belong to the first group */
+ data->group = 1;
+ mutex_init(&data->mutex);
- rcu_read_lock();
- mac80211_hwsim_tx_frame(data->hw, skb,
- rcu_dereference(vif->chanctx_conf)->def.chan);
- rcu_read_unlock();
-}
+ /* Enable frame retransmissions for lossy channels */
+ hw->max_rates = 4;
+ hw->max_rate_tries = 11;
-static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
- struct ieee80211_vif *vif, int ps)
-{
- struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
- struct sk_buff *skb;
- struct ieee80211_hdr *hdr;
+ if (reg_strict)
+ hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
+ if (regd) {
+ hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
+ wiphy_apply_custom_regulatory(hw->wiphy, regd);
+ /* give the regulatory workqueue a chance to run */
+ schedule_timeout_interruptible(1);
+ }
- if (!vp->assoc)
- return;
+ err = ieee80211_register_hw(hw);
+ if (err < 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n",
+ err);
+ goto failed_hw;
+ }
- wiphy_debug(data->hw->wiphy,
- "%s: send data::nullfunc to %pM ps=%d\n",
- __func__, vp->bssid, ps);
+ wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr);
- skb = dev_alloc_skb(sizeof(*hdr));
- if (!skb)
- return;
- hdr = (void *) skb_put(skb, sizeof(*hdr) - ETH_ALEN);
- hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
- IEEE80211_STYPE_NULLFUNC |
- (ps ? IEEE80211_FCTL_PM : 0));
- hdr->duration_id = cpu_to_le16(0);
- memcpy(hdr->addr1, vp->bssid, ETH_ALEN);
- memcpy(hdr->addr2, mac, ETH_ALEN);
- memcpy(hdr->addr3, vp->bssid, ETH_ALEN);
+ if (reg_alpha2)
+ regulatory_hint(hw->wiphy, reg_alpha2);
- rcu_read_lock();
- mac80211_hwsim_tx_frame(data->hw, skb,
- rcu_dereference(vif->chanctx_conf)->def.chan);
- rcu_read_unlock();
-}
+ data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir);
+ debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps);
+ debugfs_create_file("group", 0666, data->debugfs, data,
+ &hwsim_fops_group);
+ if (data->channels == 1)
+ debugfs_create_file("dfs_simulate_radar", 0222,
+ data->debugfs,
+ data, &hwsim_simulate_radar);
+ tasklet_hrtimer_init(&data->beacon_timer,
+ mac80211_hwsim_beacon,
+ CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS);
-static void hwsim_send_nullfunc_ps(void *dat, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct mac80211_hwsim_data *data = dat;
- hwsim_send_nullfunc(data, mac, vif, 1);
-}
+ spin_lock_bh(&hwsim_radio_lock);
+ list_add_tail(&data->list, &hwsim_radios);
+ spin_unlock_bh(&hwsim_radio_lock);
+ return idx;
-static void hwsim_send_nullfunc_no_ps(void *dat, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct mac80211_hwsim_data *data = dat;
- hwsim_send_nullfunc(data, mac, vif, 0);
+failed_hw:
+ device_unregister(data->dev);
+failed_drvdata:
+ ieee80211_free_hw(hw);
+failed:
+ return err;
}
-
-static int hwsim_fops_ps_read(void *dat, u64 *val)
+static void mac80211_hwsim_destroy_radio(struct mac80211_hwsim_data *data)
{
- struct mac80211_hwsim_data *data = dat;
- *val = data->ps;
- return 0;
+ debugfs_remove_recursive(data->debugfs);
+ ieee80211_unregister_hw(data->hw);
+ device_release_driver(data->dev);
+ device_unregister(data->dev);
+ ieee80211_free_hw(data->hw);
}
-static int hwsim_fops_ps_write(void *dat, u64 val)
+static void mac80211_hwsim_free(void)
{
- struct mac80211_hwsim_data *data = dat;
- enum ps_mode old_ps;
-
- if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL &&
- val != PS_MANUAL_POLL)
- return -EINVAL;
-
- old_ps = data->ps;
- data->ps = val;
+ struct mac80211_hwsim_data *data;
- if (val == PS_MANUAL_POLL) {
- ieee80211_iterate_active_interfaces(data->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- hwsim_send_ps_poll, data);
- data->ps_poll_pending = true;
- } else if (old_ps == PS_DISABLED && val != PS_DISABLED) {
- ieee80211_iterate_active_interfaces(data->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- hwsim_send_nullfunc_ps,
- data);
- } else if (old_ps != PS_DISABLED && val == PS_DISABLED) {
- ieee80211_iterate_active_interfaces(data->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- hwsim_send_nullfunc_no_ps,
- data);
+ spin_lock_bh(&hwsim_radio_lock);
+ while ((data = list_first_entry_or_null(&hwsim_radios,
+ struct mac80211_hwsim_data,
+ list))) {
+ list_del(&data->list);
+ spin_unlock_bh(&hwsim_radio_lock);
+ mac80211_hwsim_destroy_radio(data);
+ spin_lock_bh(&hwsim_radio_lock);
}
-
- return 0;
+ spin_unlock_bh(&hwsim_radio_lock);
+ class_destroy(hwsim_class);
}
-DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write,
- "%llu\n");
-
-
-static int hwsim_fops_group_read(void *dat, u64 *val)
-{
- struct mac80211_hwsim_data *data = dat;
- *val = data->group;
- return 0;
-}
+static const struct net_device_ops hwsim_netdev_ops = {
+ .ndo_start_xmit = hwsim_mon_xmit,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
-static int hwsim_fops_group_write(void *dat, u64 val)
+static void hwsim_mon_setup(struct net_device *dev)
{
- struct mac80211_hwsim_data *data = dat;
- data->group = val;
- return 0;
+ dev->netdev_ops = &hwsim_netdev_ops;
+ dev->destructor = free_netdev;
+ ether_setup(dev);
+ dev->tx_queue_len = 0;
+ dev->type = ARPHRD_IEEE80211_RADIOTAP;
+ memset(dev->dev_addr, 0, ETH_ALEN);
+ dev->dev_addr[0] = 0x12;
}
-DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
- hwsim_fops_group_read, hwsim_fops_group_write,
- "%llx\n");
-
-static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
- struct mac_address *addr)
+static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(const u8 *addr)
{
struct mac80211_hwsim_data *data;
bool _found = false;
spin_lock_bh(&hwsim_radio_lock);
list_for_each_entry(data, &hwsim_radios, list) {
- if (memcmp(data->addresses[1].addr, addr,
- sizeof(struct mac_address)) == 0) {
+ if (memcmp(data->addresses[1].addr, addr, ETH_ALEN) == 0) {
_found = true;
break;
}
@@ -1952,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 */
@@ -2029,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 */
@@ -2068,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;
@@ -2090,8 +2374,24 @@ out:
static int hwsim_register_received_nl(struct sk_buff *skb_2,
struct genl_info *info)
{
- if (info == NULL)
- goto out;
+ struct mac80211_hwsim_data *data;
+ int chans = 1;
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry(data, &hwsim_radios, list)
+ chans = max(chans, data->channels);
+ spin_unlock_bh(&hwsim_radio_lock);
+
+ /* In the future we should revise the userspace API and allow it
+ * to set a flag that it does support multi-channel, then we can
+ * let this pass conditionally on the flag.
+ * For current userspace, prohibit it since it won't work right.
+ */
+ if (chans > 1)
+ return -EOPNOTSUPP;
+
+ if (wmediumd_portid)
+ return -EBUSY;
wmediumd_portid = info->snd_portid;
@@ -2099,9 +2399,53 @@ static int hwsim_register_received_nl(struct sk_buff *skb_2,
"switching to wmediumd mode with pid %d\n", info->snd_portid);
return 0;
-out:
- printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
- return -EINVAL;
+}
+
+static int hwsim_create_radio_nl(struct sk_buff *msg, struct genl_info *info)
+{
+ unsigned int chans = channels;
+ const char *alpha2 = NULL;
+ const struct ieee80211_regdomain *regd = NULL;
+ bool reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
+
+ if (info->attrs[HWSIM_ATTR_CHANNELS])
+ chans = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
+
+ if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2])
+ alpha2 = nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]);
+
+ if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) {
+ u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]);
+
+ if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom))
+ return -EINVAL;
+ regd = hwsim_world_regdom_custom[idx];
+ }
+
+ return mac80211_hwsim_create_radio(chans, alpha2, regd, reg_strict);
+}
+
+static int hwsim_destroy_radio_nl(struct sk_buff *msg, struct genl_info *info)
+{
+ struct mac80211_hwsim_data *data;
+ int idx;
+
+ if (!info->attrs[HWSIM_ATTR_RADIO_ID])
+ return -EINVAL;
+ idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry(data, &hwsim_radios, list) {
+ if (data->idx != idx)
+ continue;
+ list_del(&data->list);
+ spin_unlock_bh(&hwsim_radio_lock);
+ mac80211_hwsim_destroy_radio(data);
+ return 0;
+ }
+ spin_unlock_bh(&hwsim_radio_lock);
+
+ return -ENODEV;
}
/* Generic Netlink operations array */
@@ -2122,6 +2466,18 @@ static const struct genl_ops hwsim_ops[] = {
.policy = hwsim_genl_policy,
.doit = hwsim_tx_info_frame_received_nl,
},
+ {
+ .cmd = HWSIM_CMD_CREATE_RADIO,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_create_radio_nl,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = HWSIM_CMD_DESTROY_RADIO,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_destroy_radio_nl,
+ .flags = GENL_ADMIN_PERM,
+ },
};
static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
@@ -2150,10 +2506,6 @@ static int hwsim_init_netlink(void)
{
int rc;
- /* userspace test API hasn't been adjusted for multi-channel */
- if (channels > 1)
- return 0;
-
printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
rc = genl_register_family_with_ops(&hwsim_genl_family, hwsim_ops);
@@ -2173,77 +2525,36 @@ failure:
static void hwsim_exit_netlink(void)
{
- int ret;
-
- /* userspace test API hasn't been adjusted for multi-channel */
- if (channels > 1)
- return;
-
- printk(KERN_INFO "mac80211_hwsim: closing netlink\n");
/* unregister the notifier */
netlink_unregister_notifier(&hwsim_netlink_notifier);
/* unregister the family */
- ret = genl_unregister_family(&hwsim_genl_family);
- if (ret)
- printk(KERN_DEBUG "mac80211_hwsim: "
- "unregister family %i\n", ret);
+ genl_unregister_family(&hwsim_genl_family);
}
-static const struct ieee80211_iface_limit hwsim_if_limits[] = {
- { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) },
- { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_P2P_CLIENT) |
-#ifdef CONFIG_MAC80211_MESH
- BIT(NL80211_IFTYPE_MESH_POINT) |
-#endif
- BIT(NL80211_IFTYPE_AP) |
- BIT(NL80211_IFTYPE_P2P_GO) },
- { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) },
-};
-
-static 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,
-};
-
static int __init init_mac80211_hwsim(void)
{
- int i, err = 0;
- u8 addr[ETH_ALEN];
- struct mac80211_hwsim_data *data;
- struct ieee80211_hw *hw;
- enum ieee80211_band band;
+ int i, err;
- if (radios < 1 || radios > 100)
+ if (radios < 0 || radios > 100)
return -EINVAL;
if (channels < 1)
return -EINVAL;
- if (channels > 1) {
- hwsim_if_comb.num_different_channels = channels;
- mac80211_hwsim_ops.hw_scan = mac80211_hwsim_hw_scan;
- mac80211_hwsim_ops.cancel_hw_scan =
- mac80211_hwsim_cancel_hw_scan;
- mac80211_hwsim_ops.sw_scan_start = NULL;
- mac80211_hwsim_ops.sw_scan_complete = NULL;
- mac80211_hwsim_ops.remain_on_channel =
- mac80211_hwsim_roc;
- mac80211_hwsim_ops.cancel_remain_on_channel =
- mac80211_hwsim_croc;
- mac80211_hwsim_ops.add_chanctx =
- mac80211_hwsim_add_chanctx;
- mac80211_hwsim_ops.remove_chanctx =
- mac80211_hwsim_remove_chanctx;
- mac80211_hwsim_ops.change_chanctx =
- mac80211_hwsim_change_chanctx;
- mac80211_hwsim_ops.assign_vif_chanctx =
- mac80211_hwsim_assign_vif_chanctx;
- mac80211_hwsim_ops.unassign_vif_chanctx =
- mac80211_hwsim_unassign_vif_chanctx;
- }
+ mac80211_hwsim_mchan_ops = mac80211_hwsim_ops;
+ mac80211_hwsim_mchan_ops.hw_scan = mac80211_hwsim_hw_scan;
+ mac80211_hwsim_mchan_ops.cancel_hw_scan = mac80211_hwsim_cancel_hw_scan;
+ mac80211_hwsim_mchan_ops.sw_scan_start = NULL;
+ mac80211_hwsim_mchan_ops.sw_scan_complete = NULL;
+ mac80211_hwsim_mchan_ops.remain_on_channel = mac80211_hwsim_roc;
+ mac80211_hwsim_mchan_ops.cancel_remain_on_channel = mac80211_hwsim_croc;
+ mac80211_hwsim_mchan_ops.add_chanctx = mac80211_hwsim_add_chanctx;
+ mac80211_hwsim_mchan_ops.remove_chanctx = mac80211_hwsim_remove_chanctx;
+ mac80211_hwsim_mchan_ops.change_chanctx = mac80211_hwsim_change_chanctx;
+ mac80211_hwsim_mchan_ops.assign_vif_chanctx =
+ mac80211_hwsim_assign_vif_chanctx;
+ mac80211_hwsim_mchan_ops.unassign_vif_chanctx =
+ mac80211_hwsim_unassign_vif_chanctx;
spin_lock_init(&hwsim_radio_lock);
INIT_LIST_HEAD(&hwsim_radios);
@@ -2255,348 +2566,116 @@ static int __init init_mac80211_hwsim(void)
hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim");
if (IS_ERR(hwsim_class)) {
err = PTR_ERR(hwsim_class);
- goto failed_unregister_driver;
+ goto out_unregister_driver;
}
- memset(addr, 0, ETH_ALEN);
- addr[0] = 0x02;
-
for (i = 0; i < radios; i++) {
- printk(KERN_DEBUG "mac80211_hwsim: Initializing radio %d\n",
- i);
- hw = ieee80211_alloc_hw(sizeof(*data), &mac80211_hwsim_ops);
- if (!hw) {
- printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw "
- "failed\n");
- err = -ENOMEM;
- goto failed;
- }
- data = hw->priv;
- data->hw = hw;
-
- data->dev = device_create(hwsim_class, NULL, 0, hw,
- "hwsim%d", i);
- if (IS_ERR(data->dev)) {
- printk(KERN_DEBUG
- "mac80211_hwsim: device_create failed (%ld)\n",
- PTR_ERR(data->dev));
- err = -ENOMEM;
- goto failed_drvdata;
- }
- data->dev->driver = &mac80211_hwsim_driver.driver;
- err = device_bind_driver(data->dev);
- if (err != 0) {
- printk(KERN_DEBUG
- "mac80211_hwsim: device_bind_driver failed (%d)\n",
- err);
- goto failed_hw;
- }
-
- skb_queue_head_init(&data->pending);
+ const char *reg_alpha2 = NULL;
+ const struct ieee80211_regdomain *regd = NULL;
+ bool reg_strict = false;
- SET_IEEE80211_DEV(hw, data->dev);
- addr[3] = i >> 8;
- addr[4] = i;
- memcpy(data->addresses[0].addr, addr, ETH_ALEN);
- memcpy(data->addresses[1].addr, addr, ETH_ALEN);
- data->addresses[1].addr[0] |= 0x40;
- hw->wiphy->n_addresses = 2;
- hw->wiphy->addresses = data->addresses;
-
- hw->wiphy->iface_combinations = &hwsim_if_comb;
- hw->wiphy->n_iface_combinations = 1;
-
- 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 = 5;
- hw->offchannel_tx_hw_queue = 4;
- hw->wiphy->interface_modes =
- BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_AP) |
- BIT(NL80211_IFTYPE_P2P_CLIENT) |
- BIT(NL80211_IFTYPE_P2P_GO) |
- BIT(NL80211_IFTYPE_ADHOC) |
- BIT(NL80211_IFTYPE_MESH_POINT) |
- BIT(NL80211_IFTYPE_P2P_DEVICE);
-
- hw->flags = IEEE80211_HW_MFP_CAPABLE |
- IEEE80211_HW_SIGNAL_DBM |
- IEEE80211_HW_SUPPORTS_STATIC_SMPS |
- IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
- IEEE80211_HW_AMPDU_AGGREGATION |
- IEEE80211_HW_WANT_MONITOR_VIF |
- IEEE80211_HW_QUEUE_CONTROL;
- if (rctbl)
- hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE;
-
- hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
- WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
- WIPHY_FLAG_AP_UAPSD;
- hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
-
- /* ask mac80211 to reserve space for magic */
- hw->vif_data_size = sizeof(struct hwsim_vif_priv);
- hw->sta_data_size = sizeof(struct hwsim_sta_priv);
- hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv);
-
- memcpy(data->channels_2ghz, hwsim_channels_2ghz,
- sizeof(hwsim_channels_2ghz));
- memcpy(data->channels_5ghz, hwsim_channels_5ghz,
- sizeof(hwsim_channels_5ghz));
- memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates));
-
- for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
- struct ieee80211_supported_band *sband = &data->bands[band];
- switch (band) {
- case IEEE80211_BAND_2GHZ:
- sband->channels = data->channels_2ghz;
- sband->n_channels =
- ARRAY_SIZE(hwsim_channels_2ghz);
- sband->bitrates = data->rates;
- sband->n_bitrates = ARRAY_SIZE(hwsim_rates);
- break;
- case IEEE80211_BAND_5GHZ:
- sband->channels = data->channels_5ghz;
- sband->n_channels =
- ARRAY_SIZE(hwsim_channels_5ghz);
- sband->bitrates = data->rates + 4;
- sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4;
- break;
- default:
- continue;
- }
-
- sband->ht_cap.ht_supported = true;
- sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
- IEEE80211_HT_CAP_GRN_FLD |
- IEEE80211_HT_CAP_SGI_40 |
- IEEE80211_HT_CAP_DSSSCCK40;
- sband->ht_cap.ampdu_factor = 0x3;
- sband->ht_cap.ampdu_density = 0x6;
- memset(&sband->ht_cap.mcs, 0,
- sizeof(sband->ht_cap.mcs));
- sband->ht_cap.mcs.rx_mask[0] = 0xff;
- sband->ht_cap.mcs.rx_mask[1] = 0xff;
- sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
-
- hw->wiphy->bands[band] = sband;
-
- sband->vht_cap.vht_supported = true;
- sband->vht_cap.cap =
- IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
- IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ |
- IEEE80211_VHT_CAP_RXLDPC |
- IEEE80211_VHT_CAP_SHORT_GI_80 |
- IEEE80211_VHT_CAP_SHORT_GI_160 |
- IEEE80211_VHT_CAP_TXSTBC |
- IEEE80211_VHT_CAP_RXSTBC_1 |
- IEEE80211_VHT_CAP_RXSTBC_2 |
- IEEE80211_VHT_CAP_RXSTBC_3 |
- IEEE80211_VHT_CAP_RXSTBC_4 |
- IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
- sband->vht_cap.vht_mcs.rx_mcs_map =
- cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_8 << 0 |
- IEEE80211_VHT_MCS_SUPPORT_0_8 << 2 |
- IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 |
- IEEE80211_VHT_MCS_SUPPORT_0_8 << 6 |
- IEEE80211_VHT_MCS_SUPPORT_0_8 << 8 |
- IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 |
- IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 |
- IEEE80211_VHT_MCS_SUPPORT_0_8 << 14);
- sband->vht_cap.vht_mcs.tx_mcs_map =
- sband->vht_cap.vht_mcs.rx_mcs_map;
- }
- /* By default all radios are belonging to the first group */
- data->group = 1;
- mutex_init(&data->mutex);
-
- /* Enable frame retransmissions for lossy channels */
- hw->max_rates = 4;
- hw->max_rate_tries = 11;
-
- /* Work to be done prior to ieee80211_register_hw() */
switch (regtest) {
- case HWSIM_REGTEST_DISABLED:
- case HWSIM_REGTEST_DRIVER_REG_FOLLOW:
- case HWSIM_REGTEST_DRIVER_REG_ALL:
case HWSIM_REGTEST_DIFF_COUNTRY:
- /*
- * Nothing to be done for driver regulatory domain
- * hints prior to ieee80211_register_hw()
- */
- break;
- case HWSIM_REGTEST_WORLD_ROAM:
- if (i == 0) {
- hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
- wiphy_apply_custom_regulatory(hw->wiphy,
- &hwsim_world_regdom_custom_01);
- }
- break;
- case HWSIM_REGTEST_CUSTOM_WORLD:
- hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
- wiphy_apply_custom_regulatory(hw->wiphy,
- &hwsim_world_regdom_custom_01);
- break;
- case HWSIM_REGTEST_CUSTOM_WORLD_2:
- if (i == 0) {
- hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
- wiphy_apply_custom_regulatory(hw->wiphy,
- &hwsim_world_regdom_custom_01);
- } else if (i == 1) {
- hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
- wiphy_apply_custom_regulatory(hw->wiphy,
- &hwsim_world_regdom_custom_02);
- }
- break;
- case HWSIM_REGTEST_STRICT_ALL:
- hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY;
- break;
- case HWSIM_REGTEST_STRICT_FOLLOW:
- case HWSIM_REGTEST_STRICT_AND_DRIVER_REG:
- if (i == 0)
- hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY;
- break;
- case HWSIM_REGTEST_ALL:
- if (i == 0) {
- hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
- wiphy_apply_custom_regulatory(hw->wiphy,
- &hwsim_world_regdom_custom_01);
- } else if (i == 1) {
- hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
- wiphy_apply_custom_regulatory(hw->wiphy,
- &hwsim_world_regdom_custom_02);
- } else if (i == 4)
- hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY;
- break;
- default:
- break;
- }
-
- /* give the regulatory workqueue a chance to run */
- if (regtest)
- schedule_timeout_interruptible(1);
- err = ieee80211_register_hw(hw);
- if (err < 0) {
- printk(KERN_DEBUG "mac80211_hwsim: "
- "ieee80211_register_hw failed (%d)\n", err);
- goto failed_hw;
- }
-
- /* Work to be done after to ieee80211_register_hw() */
- switch (regtest) {
- case HWSIM_REGTEST_WORLD_ROAM:
- case HWSIM_REGTEST_DISABLED:
+ if (i < ARRAY_SIZE(hwsim_alpha2s))
+ reg_alpha2 = hwsim_alpha2s[i];
break;
case HWSIM_REGTEST_DRIVER_REG_FOLLOW:
if (!i)
- regulatory_hint(hw->wiphy, hwsim_alpha2s[0]);
+ reg_alpha2 = hwsim_alpha2s[0];
break;
- case HWSIM_REGTEST_DRIVER_REG_ALL:
case HWSIM_REGTEST_STRICT_ALL:
- regulatory_hint(hw->wiphy, hwsim_alpha2s[0]);
+ reg_strict = true;
+ case HWSIM_REGTEST_DRIVER_REG_ALL:
+ reg_alpha2 = hwsim_alpha2s[0];
break;
- case HWSIM_REGTEST_DIFF_COUNTRY:
- if (i < ARRAY_SIZE(hwsim_alpha2s))
- regulatory_hint(hw->wiphy, hwsim_alpha2s[i]);
+ case HWSIM_REGTEST_WORLD_ROAM:
+ if (i == 0)
+ regd = &hwsim_world_regdom_custom_01;
break;
case HWSIM_REGTEST_CUSTOM_WORLD:
+ regd = &hwsim_world_regdom_custom_01;
+ break;
case HWSIM_REGTEST_CUSTOM_WORLD_2:
- /*
- * Nothing to be done for custom world regulatory
- * domains after to ieee80211_register_hw
- */
+ if (i == 0)
+ regd = &hwsim_world_regdom_custom_01;
+ else if (i == 1)
+ regd = &hwsim_world_regdom_custom_02;
break;
case HWSIM_REGTEST_STRICT_FOLLOW:
- if (i == 0)
- regulatory_hint(hw->wiphy, hwsim_alpha2s[0]);
+ if (i == 0) {
+ reg_strict = true;
+ reg_alpha2 = hwsim_alpha2s[0];
+ }
break;
case HWSIM_REGTEST_STRICT_AND_DRIVER_REG:
- if (i == 0)
- regulatory_hint(hw->wiphy, hwsim_alpha2s[0]);
- else if (i == 1)
- regulatory_hint(hw->wiphy, hwsim_alpha2s[1]);
+ if (i == 0) {
+ reg_strict = true;
+ reg_alpha2 = hwsim_alpha2s[0];
+ } else if (i == 1) {
+ reg_alpha2 = hwsim_alpha2s[1];
+ }
break;
case HWSIM_REGTEST_ALL:
- if (i == 2)
- regulatory_hint(hw->wiphy, hwsim_alpha2s[0]);
- else if (i == 3)
- regulatory_hint(hw->wiphy, hwsim_alpha2s[1]);
- else if (i == 4)
- regulatory_hint(hw->wiphy, hwsim_alpha2s[2]);
+ switch (i) {
+ case 0:
+ regd = &hwsim_world_regdom_custom_01;
+ break;
+ case 1:
+ regd = &hwsim_world_regdom_custom_02;
+ break;
+ case 2:
+ reg_alpha2 = hwsim_alpha2s[0];
+ break;
+ case 3:
+ reg_alpha2 = hwsim_alpha2s[1];
+ break;
+ case 4:
+ reg_strict = true;
+ reg_alpha2 = hwsim_alpha2s[2];
+ break;
+ }
break;
default:
break;
}
- wiphy_debug(hw->wiphy, "hwaddr %pm registered\n",
- hw->wiphy->perm_addr);
-
- data->debugfs = debugfs_create_dir("hwsim",
- hw->wiphy->debugfsdir);
- data->debugfs_ps = debugfs_create_file("ps", 0666,
- data->debugfs, data,
- &hwsim_fops_ps);
- data->debugfs_group = debugfs_create_file("group", 0666,
- data->debugfs, data,
- &hwsim_fops_group);
-
- tasklet_hrtimer_init(&data->beacon_timer,
- mac80211_hwsim_beacon,
- CLOCK_REALTIME, HRTIMER_MODE_ABS);
-
- list_add_tail(&data->list, &hwsim_radios);
+ err = mac80211_hwsim_create_radio(channels, reg_alpha2,
+ regd, reg_strict);
+ if (err < 0)
+ goto out_free_radios;
}
hwsim_mon = alloc_netdev(0, "hwsim%d", hwsim_mon_setup);
if (hwsim_mon == NULL) {
err = -ENOMEM;
- goto failed;
+ goto out_free_radios;
}
rtnl_lock();
-
err = dev_alloc_name(hwsim_mon, hwsim_mon->name);
- if (err < 0)
- goto failed_mon;
-
+ if (err < 0) {
+ rtnl_unlock();
+ goto out_free_radios;
+ }
err = register_netdevice(hwsim_mon);
- if (err < 0)
- goto failed_mon;
-
+ if (err < 0) {
+ rtnl_unlock();
+ goto out_free_mon;
+ }
rtnl_unlock();
err = hwsim_init_netlink();
if (err < 0)
- goto failed_nl;
+ goto out_free_mon;
return 0;
-failed_nl:
- printk(KERN_DEBUG "mac_80211_hwsim: failed initializing netlink\n");
- return err;
-
-failed_mon:
- rtnl_unlock();
+out_free_mon:
free_netdev(hwsim_mon);
+out_free_radios:
mac80211_hwsim_free();
- return err;
-
-failed_hw:
- device_unregister(data->dev);
-failed_drvdata:
- ieee80211_free_hw(hw);
-failed:
- mac80211_hwsim_free();
-failed_unregister_driver:
+out_unregister_driver:
platform_driver_unregister(&mac80211_hwsim_driver);
return err;
}