|
@@ -163,6 +163,11 @@ static const struct ieee80211_regdomain hwsim_world_regdom_custom_02 = {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+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;
|
|
@@ -401,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)
|
|
|
{
|
|
@@ -639,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 */
|
|
@@ -878,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;
|
|
@@ -906,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));
|
|
@@ -1013,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,
|
|
@@ -1050,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));
|
|
@@ -1141,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)
|
|
@@ -1388,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)
|
|
@@ -1700,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,
|
|
@@ -1726,217 +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_recursive(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,
|
|
|
- },
|
|
|
-};
|
|
|
+ skb_queue_head_init(&data->pending);
|
|
|
|
|
|
-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,
|
|
|
-};
|
|
|
+ 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);
|
|
|
+ }
|
|
|
|
|
|
-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;
|
|
|
-}
|
|
|
+ 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;
|
|
|
+ }
|
|
|
|
|
|
+ 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;
|
|
|
+ }
|
|
|
|
|
|
-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;
|
|
|
+ /* By default all radios belong to the first group */
|
|
|
+ data->group = 1;
|
|
|
+ mutex_init(&data->mutex);
|
|
|
|
|
|
- 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();
|
|
|
-}
|
|
|
+ /* 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;
|
|
|
-}
|
|
|
-
|
|
|
-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;
|
|
|
+ spin_unlock_bh(&hwsim_radio_lock);
|
|
|
+ class_destroy(hwsim_class);
|
|
|
}
|
|
|
|
|
|
-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 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;
|
|
|
}
|
|
@@ -1959,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 */
|
|
@@ -2036,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 */
|
|
@@ -2075,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;
|
|
@@ -2097,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;
|
|
|
|
|
@@ -2106,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 */
|
|
@@ -2129,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,
|
|
@@ -2157,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);
|
|
@@ -2180,94 +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 const struct ieee80211_iface_limit hwsim_if_dfs_limits[] = {
|
|
|
- { .max = 8, .types = BIT(NL80211_IFTYPE_AP) },
|
|
|
-};
|
|
|
-
|
|
|
-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,
|
|
|
- },
|
|
|
- {
|
|
|
- .limits = hwsim_if_dfs_limits,
|
|
|
- .n_limits = ARRAY_SIZE(hwsim_if_dfs_limits),
|
|
|
- .max_interfaces = 8,
|
|
|
- .num_different_channels = 1,
|
|
|
- .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
|
|
|
- BIT(NL80211_CHAN_WIDTH_20) |
|
|
|
- BIT(NL80211_CHAN_WIDTH_40) |
|
|
|
- BIT(NL80211_CHAN_WIDTH_80) |
|
|
|
- BIT(NL80211_CHAN_WIDTH_160),
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
static int __init 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[0].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);
|
|
@@ -2279,361 +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);
|
|
|
-
|
|
|
- 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 = ARRAY_SIZE(hwsim_if_comb);
|
|
|
-
|
|
|
- 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;
|
|
|
- /* For channels > 1 DFS is not allowed */
|
|
|
- hw->wiphy->n_iface_combinations = 1;
|
|
|
- }
|
|
|
-
|
|
|
- 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 |
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|
|
|
- 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;
|
|
|
- }
|
|
|
- /* By default all radios are belonging to the first group */
|
|
|
- data->group = 1;
|
|
|
- mutex_init(&data->mutex);
|
|
|
+ const char *reg_alpha2 = NULL;
|
|
|
+ const struct ieee80211_regdomain *regd = NULL;
|
|
|
+ bool reg_strict = false;
|
|
|
|
|
|
- /* 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->regulatory_flags |=
|
|
|
- REGULATORY_CUSTOM_REG;
|
|
|
- wiphy_apply_custom_regulatory(hw->wiphy,
|
|
|
- &hwsim_world_regdom_custom_01);
|
|
|
- }
|
|
|
- break;
|
|
|
- case HWSIM_REGTEST_CUSTOM_WORLD:
|
|
|
- hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
|
|
|
- wiphy_apply_custom_regulatory(hw->wiphy,
|
|
|
- &hwsim_world_regdom_custom_01);
|
|
|
- break;
|
|
|
- case HWSIM_REGTEST_CUSTOM_WORLD_2:
|
|
|
- if (i == 0) {
|
|
|
- hw->wiphy->regulatory_flags |=
|
|
|
- REGULATORY_CUSTOM_REG;
|
|
|
- wiphy_apply_custom_regulatory(hw->wiphy,
|
|
|
- &hwsim_world_regdom_custom_01);
|
|
|
- } else if (i == 1) {
|
|
|
- hw->wiphy->regulatory_flags |=
|
|
|
- REGULATORY_CUSTOM_REG;
|
|
|
- wiphy_apply_custom_regulatory(hw->wiphy,
|
|
|
- &hwsim_world_regdom_custom_02);
|
|
|
- }
|
|
|
- break;
|
|
|
- case HWSIM_REGTEST_STRICT_ALL:
|
|
|
- hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
|
|
|
- break;
|
|
|
- case HWSIM_REGTEST_STRICT_FOLLOW:
|
|
|
- case HWSIM_REGTEST_STRICT_AND_DRIVER_REG:
|
|
|
- if (i == 0)
|
|
|
- hw->wiphy->regulatory_flags |=
|
|
|
- REGULATORY_STRICT_REG;
|
|
|
- break;
|
|
|
- case HWSIM_REGTEST_ALL:
|
|
|
- if (i == 0) {
|
|
|
- hw->wiphy->regulatory_flags |=
|
|
|
- REGULATORY_CUSTOM_REG;
|
|
|
- wiphy_apply_custom_regulatory(hw->wiphy,
|
|
|
- &hwsim_world_regdom_custom_01);
|
|
|
- } else if (i == 1) {
|
|
|
- hw->wiphy->regulatory_flags |=
|
|
|
- REGULATORY_CUSTOM_REG;
|
|
|
- wiphy_apply_custom_regulatory(hw->wiphy,
|
|
|
- &hwsim_world_regdom_custom_02);
|
|
|
- } else if (i == 4)
|
|
|
- hw->wiphy->regulatory_flags |=
|
|
|
- REGULATORY_STRICT_REG;
|
|
|
- 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);
|
|
|
- debugfs_create_file("ps", 0666, data->debugfs, data,
|
|
|
- &hwsim_fops_ps);
|
|
|
- debugfs_create_file("group", 0666, data->debugfs, data,
|
|
|
- &hwsim_fops_group);
|
|
|
- if (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);
|
|
|
-
|
|
|
- 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;
|
|
|
}
|