mac80211_hwsim: add frame transmission support over virtio
This allows communication with external entities. It also required fixing up the netlink policy, since NLA_UNSPEC attributes are no longer accepted. Signed-off-by: Erel Geron <erelx.geron@intel.com> [port to backports, inline the ID, use 29 as the ID as requested, drop != NULL checks, reduce ifdefs] Link: https://lore.kernel.org/r/20200305143212.c6e4c87d225b.I7ce60bf143e863dcdf0fb8040aab7168ba549b99@changeid Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Родитель
1f7e9f46c2
Коммит
5d44fe7c98
|
@ -4,7 +4,7 @@
|
||||||
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
|
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
|
||||||
* Copyright (c) 2011, Javier Lopez <jlopex@gmail.com>
|
* Copyright (c) 2011, Javier Lopez <jlopex@gmail.com>
|
||||||
* Copyright (c) 2016 - 2017 Intel Deutschland GmbH
|
* Copyright (c) 2016 - 2017 Intel Deutschland GmbH
|
||||||
* Copyright (C) 2018 Intel Corporation
|
* Copyright (C) 2018 - 2020 Intel Corporation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -33,6 +33,9 @@
|
||||||
#include <net/netns/generic.h>
|
#include <net/netns/generic.h>
|
||||||
#include <linux/rhashtable.h>
|
#include <linux/rhashtable.h>
|
||||||
#include <linux/nospec.h>
|
#include <linux/nospec.h>
|
||||||
|
#include <linux/virtio.h>
|
||||||
|
#include <linux/virtio_ids.h>
|
||||||
|
#include <linux/virtio_config.h>
|
||||||
#include "mac80211_hwsim.h"
|
#include "mac80211_hwsim.h"
|
||||||
|
|
||||||
#define WARN_QUEUE 100
|
#define WARN_QUEUE 100
|
||||||
|
@ -613,14 +616,14 @@ static const struct genl_multicast_group hwsim_mcgrps[] = {
|
||||||
/* MAC80211_HWSIM netlink policy */
|
/* MAC80211_HWSIM netlink policy */
|
||||||
|
|
||||||
static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
|
static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
|
||||||
[HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC, .len = ETH_ALEN },
|
[HWSIM_ATTR_ADDR_RECEIVER] = NLA_POLICY_ETH_ADDR_COMPAT,
|
||||||
[HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC, .len = ETH_ALEN },
|
[HWSIM_ATTR_ADDR_TRANSMITTER] = NLA_POLICY_ETH_ADDR_COMPAT,
|
||||||
[HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
|
[HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
|
||||||
.len = IEEE80211_MAX_DATA_LEN },
|
.len = IEEE80211_MAX_DATA_LEN },
|
||||||
[HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
|
[HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
|
||||||
[HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 },
|
[HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 },
|
||||||
[HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
|
[HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
|
||||||
[HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC,
|
[HWSIM_ATTR_TX_INFO] = { .type = NLA_BINARY,
|
||||||
.len = IEEE80211_TX_MAX_RATES *
|
.len = IEEE80211_TX_MAX_RATES *
|
||||||
sizeof(struct hwsim_tx_rate)},
|
sizeof(struct hwsim_tx_rate)},
|
||||||
[HWSIM_ATTR_COOKIE] = { .type = NLA_U64 },
|
[HWSIM_ATTR_COOKIE] = { .type = NLA_U64 },
|
||||||
|
@ -630,15 +633,61 @@ static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
|
||||||
[HWSIM_ATTR_REG_CUSTOM_REG] = { .type = NLA_U32 },
|
[HWSIM_ATTR_REG_CUSTOM_REG] = { .type = NLA_U32 },
|
||||||
[HWSIM_ATTR_REG_STRICT_REG] = { .type = NLA_FLAG },
|
[HWSIM_ATTR_REG_STRICT_REG] = { .type = NLA_FLAG },
|
||||||
[HWSIM_ATTR_SUPPORT_P2P_DEVICE] = { .type = NLA_FLAG },
|
[HWSIM_ATTR_SUPPORT_P2P_DEVICE] = { .type = NLA_FLAG },
|
||||||
|
[HWSIM_ATTR_USE_CHANCTX] = { .type = NLA_FLAG },
|
||||||
[HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE] = { .type = NLA_FLAG },
|
[HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE] = { .type = NLA_FLAG },
|
||||||
[HWSIM_ATTR_RADIO_NAME] = { .type = NLA_STRING },
|
[HWSIM_ATTR_RADIO_NAME] = { .type = NLA_STRING },
|
||||||
[HWSIM_ATTR_NO_VIF] = { .type = NLA_FLAG },
|
[HWSIM_ATTR_NO_VIF] = { .type = NLA_FLAG },
|
||||||
[HWSIM_ATTR_FREQ] = { .type = NLA_U32 },
|
[HWSIM_ATTR_FREQ] = { .type = NLA_U32 },
|
||||||
[HWSIM_ATTR_PERM_ADDR] = { .type = NLA_UNSPEC, .len = ETH_ALEN },
|
[HWSIM_ATTR_TX_INFO_FLAGS] = { .type = NLA_BINARY },
|
||||||
|
[HWSIM_ATTR_PERM_ADDR] = NLA_POLICY_ETH_ADDR_COMPAT,
|
||||||
[HWSIM_ATTR_IFTYPE_SUPPORT] = { .type = NLA_U32 },
|
[HWSIM_ATTR_IFTYPE_SUPPORT] = { .type = NLA_U32 },
|
||||||
[HWSIM_ATTR_CIPHER_SUPPORT] = { .type = NLA_BINARY },
|
[HWSIM_ATTR_CIPHER_SUPPORT] = { .type = NLA_BINARY },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if IS_REACHABLE(CONFIG_VIRTIO)
|
||||||
|
|
||||||
|
/* MAC80211_HWSIM virtio queues */
|
||||||
|
static struct virtqueue *hwsim_vqs[HWSIM_NUM_VQS];
|
||||||
|
static bool hwsim_virtio_enabled;
|
||||||
|
static spinlock_t hwsim_virtio_lock;
|
||||||
|
|
||||||
|
static void hwsim_virtio_rx_work(struct work_struct *work);
|
||||||
|
static DECLARE_WORK(hwsim_virtio_rx, hwsim_virtio_rx_work);
|
||||||
|
|
||||||
|
static int hwsim_tx_virtio(struct mac80211_hwsim_data *data,
|
||||||
|
struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct scatterlist sg[1];
|
||||||
|
unsigned long flags;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&hwsim_virtio_lock, flags);
|
||||||
|
if (!hwsim_virtio_enabled) {
|
||||||
|
err = -ENODEV;
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
sg_init_one(sg, skb->head, skb_end_offset(skb));
|
||||||
|
err = virtqueue_add_outbuf(hwsim_vqs[HWSIM_VQ_TX], sg, 1, skb,
|
||||||
|
GFP_ATOMIC);
|
||||||
|
if (err)
|
||||||
|
goto out_free;
|
||||||
|
virtqueue_kick(hwsim_vqs[HWSIM_VQ_TX]);
|
||||||
|
spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_free:
|
||||||
|
spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
|
||||||
|
nlmsg_free(skb);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* cause a linker error if this ends up being needed */
|
||||||
|
extern int hwsim_tx_virtio(struct mac80211_hwsim_data *data,
|
||||||
|
struct sk_buff *skb);
|
||||||
|
#define hwsim_virtio_enabled false
|
||||||
|
#endif
|
||||||
|
|
||||||
static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
|
static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
|
||||||
struct sk_buff *skb,
|
struct sk_buff *skb,
|
||||||
struct ieee80211_channel *chan);
|
struct ieee80211_channel *chan);
|
||||||
|
@ -1138,8 +1187,14 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
|
|
||||||
genlmsg_end(skb, msg_head);
|
genlmsg_end(skb, msg_head);
|
||||||
if (hwsim_unicast_netgroup(data, skb, dst_portid))
|
|
||||||
goto err_free_txskb;
|
if (hwsim_virtio_enabled) {
|
||||||
|
if (hwsim_tx_virtio(data, skb))
|
||||||
|
goto err_free_txskb;
|
||||||
|
} else {
|
||||||
|
if (hwsim_unicast_netgroup(data, skb, dst_portid))
|
||||||
|
goto err_free_txskb;
|
||||||
|
}
|
||||||
|
|
||||||
/* Enqueue the packet */
|
/* Enqueue the packet */
|
||||||
skb_queue_tail(&data->pending, my_skb);
|
skb_queue_tail(&data->pending, my_skb);
|
||||||
|
@ -1441,7 +1496,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
|
||||||
/* wmediumd mode check */
|
/* wmediumd mode check */
|
||||||
_portid = READ_ONCE(data->wmediumd);
|
_portid = READ_ONCE(data->wmediumd);
|
||||||
|
|
||||||
if (_portid)
|
if (_portid || hwsim_virtio_enabled)
|
||||||
return mac80211_hwsim_tx_frame_nl(hw, skb, _portid);
|
return mac80211_hwsim_tx_frame_nl(hw, skb, _portid);
|
||||||
|
|
||||||
/* NO wmediumd detected, perfect medium simulation */
|
/* NO wmediumd detected, perfect medium simulation */
|
||||||
|
@ -1547,7 +1602,7 @@ static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
|
||||||
|
|
||||||
mac80211_hwsim_monitor_rx(hw, skb, chan);
|
mac80211_hwsim_monitor_rx(hw, skb, chan);
|
||||||
|
|
||||||
if (_pid)
|
if (_pid || hwsim_virtio_enabled)
|
||||||
return mac80211_hwsim_tx_frame_nl(hw, skb, _pid);
|
return mac80211_hwsim_tx_frame_nl(hw, skb, _pid);
|
||||||
|
|
||||||
mac80211_hwsim_tx_frame_no_nl(hw, skb, chan);
|
mac80211_hwsim_tx_frame_no_nl(hw, skb, chan);
|
||||||
|
@ -3293,11 +3348,14 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
|
||||||
if (!data2)
|
if (!data2)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (hwsim_net_get_netgroup(genl_info_net(info)) != data2->netgroup)
|
if (!hwsim_virtio_enabled) {
|
||||||
goto out;
|
if (hwsim_net_get_netgroup(genl_info_net(info)) !=
|
||||||
|
data2->netgroup)
|
||||||
|
goto out;
|
||||||
|
|
||||||
if (info->snd_portid != data2->wmediumd)
|
if (info->snd_portid != data2->wmediumd)
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* look for the skb matching the cookie passed back from user */
|
/* look for the skb matching the cookie passed back from user */
|
||||||
skb_queue_walk_safe(&data2->pending, skb, tmp) {
|
skb_queue_walk_safe(&data2->pending, skb, tmp) {
|
||||||
|
@ -3387,11 +3445,14 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
|
||||||
if (!data2)
|
if (!data2)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (hwsim_net_get_netgroup(genl_info_net(info)) != data2->netgroup)
|
if (!hwsim_virtio_enabled) {
|
||||||
goto out;
|
if (hwsim_net_get_netgroup(genl_info_net(info)) !=
|
||||||
|
data2->netgroup)
|
||||||
|
goto out;
|
||||||
|
|
||||||
if (info->snd_portid != data2->wmediumd)
|
if (info->snd_portid != data2->wmediumd)
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
/* check if radio is configured properly */
|
/* check if radio is configured properly */
|
||||||
|
|
||||||
|
@ -3932,6 +3993,229 @@ static void hwsim_exit_netlink(void)
|
||||||
genl_unregister_family(&hwsim_genl_family);
|
genl_unregister_family(&hwsim_genl_family);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if IS_REACHABLE(CONFIG_VIRTIO)
|
||||||
|
static void hwsim_virtio_tx_done(struct virtqueue *vq)
|
||||||
|
{
|
||||||
|
unsigned int len;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&hwsim_virtio_lock, flags);
|
||||||
|
while ((skb = virtqueue_get_buf(vq, &len)))
|
||||||
|
nlmsg_free(skb);
|
||||||
|
spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hwsim_virtio_handle_cmd(struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
struct genlmsghdr *gnlh;
|
||||||
|
struct nlattr *tb[HWSIM_ATTR_MAX + 1];
|
||||||
|
struct genl_info info = {};
|
||||||
|
int err;
|
||||||
|
|
||||||
|
nlh = nlmsg_hdr(skb);
|
||||||
|
gnlh = nlmsg_data(nlh);
|
||||||
|
err = genlmsg_parse(nlh, &hwsim_genl_family, tb, HWSIM_ATTR_MAX,
|
||||||
|
hwsim_genl_policy, NULL);
|
||||||
|
if (err) {
|
||||||
|
pr_err_ratelimited("hwsim: genlmsg_parse returned %d\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.attrs = tb;
|
||||||
|
|
||||||
|
switch (gnlh->cmd) {
|
||||||
|
case HWSIM_CMD_FRAME:
|
||||||
|
hwsim_cloned_frame_received_nl(skb, &info);
|
||||||
|
break;
|
||||||
|
case HWSIM_CMD_TX_INFO_FRAME:
|
||||||
|
hwsim_tx_info_frame_received_nl(skb, &info);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pr_err_ratelimited("hwsim: invalid cmd: %d\n", gnlh->cmd);
|
||||||
|
return -EPROTO;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hwsim_virtio_rx_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct virtqueue *vq;
|
||||||
|
unsigned int len;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
struct scatterlist sg[1];
|
||||||
|
int err;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&hwsim_virtio_lock, flags);
|
||||||
|
if (!hwsim_virtio_enabled)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
skb = virtqueue_get_buf(hwsim_vqs[HWSIM_VQ_RX], &len);
|
||||||
|
if (!skb)
|
||||||
|
goto out_unlock;
|
||||||
|
spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
|
||||||
|
|
||||||
|
skb->data = skb->head;
|
||||||
|
skb_set_tail_pointer(skb, len);
|
||||||
|
hwsim_virtio_handle_cmd(skb);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&hwsim_virtio_lock, flags);
|
||||||
|
if (!hwsim_virtio_enabled) {
|
||||||
|
nlmsg_free(skb);
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
vq = hwsim_vqs[HWSIM_VQ_RX];
|
||||||
|
sg_init_one(sg, skb->head, skb_end_offset(skb));
|
||||||
|
err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_KERNEL);
|
||||||
|
if (WARN(err, "virtqueue_add_inbuf returned %d\n", err))
|
||||||
|
nlmsg_free(skb);
|
||||||
|
else
|
||||||
|
virtqueue_kick(vq);
|
||||||
|
schedule_work(&hwsim_virtio_rx);
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
|
spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hwsim_virtio_rx_done(struct virtqueue *vq)
|
||||||
|
{
|
||||||
|
schedule_work(&hwsim_virtio_rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init_vqs(struct virtio_device *vdev)
|
||||||
|
{
|
||||||
|
vq_callback_t *callbacks[HWSIM_NUM_VQS] = {
|
||||||
|
[HWSIM_VQ_TX] = hwsim_virtio_tx_done,
|
||||||
|
[HWSIM_VQ_RX] = hwsim_virtio_rx_done,
|
||||||
|
};
|
||||||
|
const char *names[HWSIM_NUM_VQS] = {
|
||||||
|
[HWSIM_VQ_TX] = "tx",
|
||||||
|
[HWSIM_VQ_RX] = "rx",
|
||||||
|
};
|
||||||
|
|
||||||
|
return virtio_find_vqs(vdev, HWSIM_NUM_VQS,
|
||||||
|
hwsim_vqs, callbacks, names, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fill_vq(struct virtqueue *vq)
|
||||||
|
{
|
||||||
|
int i, err;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
struct scatterlist sg[1];
|
||||||
|
|
||||||
|
for (i = 0; i < virtqueue_get_vring_size(vq); i++) {
|
||||||
|
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||||
|
if (!skb)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
sg_init_one(sg, skb->head, skb_end_offset(skb));
|
||||||
|
err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_KERNEL);
|
||||||
|
if (err) {
|
||||||
|
nlmsg_free(skb);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtqueue_kick(vq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_vqs(struct virtio_device *vdev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
vdev->config->reset(vdev);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(hwsim_vqs); i++) {
|
||||||
|
struct virtqueue *vq = hwsim_vqs[i];
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
while ((skb = virtqueue_detach_unused_buf(vq)))
|
||||||
|
nlmsg_free(skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
vdev->config->del_vqs(vdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hwsim_virtio_probe(struct virtio_device *vdev)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&hwsim_virtio_lock, flags);
|
||||||
|
if (hwsim_virtio_enabled) {
|
||||||
|
spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
|
||||||
|
return -EEXIST;
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
|
||||||
|
|
||||||
|
err = init_vqs(vdev);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = fill_vq(hwsim_vqs[HWSIM_VQ_RX]);
|
||||||
|
if (err)
|
||||||
|
goto out_remove;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&hwsim_virtio_lock, flags);
|
||||||
|
hwsim_virtio_enabled = true;
|
||||||
|
spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
|
||||||
|
|
||||||
|
schedule_work(&hwsim_virtio_rx);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_remove:
|
||||||
|
remove_vqs(vdev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hwsim_virtio_remove(struct virtio_device *vdev)
|
||||||
|
{
|
||||||
|
hwsim_virtio_enabled = false;
|
||||||
|
|
||||||
|
cancel_work_sync(&hwsim_virtio_rx);
|
||||||
|
|
||||||
|
remove_vqs(vdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MAC80211_HWSIM virtio device id table */
|
||||||
|
static const struct virtio_device_id id_table[] = {
|
||||||
|
{ VIRTIO_ID_MAC80211_HWSIM, VIRTIO_DEV_ANY_ID },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(virtio, id_table);
|
||||||
|
|
||||||
|
static struct virtio_driver virtio_hwsim = {
|
||||||
|
.driver.name = KBUILD_MODNAME,
|
||||||
|
.driver.owner = THIS_MODULE,
|
||||||
|
.id_table = id_table,
|
||||||
|
.probe = hwsim_virtio_probe,
|
||||||
|
.remove = hwsim_virtio_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int hwsim_register_virtio_driver(void)
|
||||||
|
{
|
||||||
|
spin_lock_init(&hwsim_virtio_lock);
|
||||||
|
|
||||||
|
return register_virtio_driver(&virtio_hwsim);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hwsim_unregister_virtio_driver(void)
|
||||||
|
{
|
||||||
|
unregister_virtio_driver(&virtio_hwsim);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline int hwsim_register_virtio_driver(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void hwsim_unregister_virtio_driver(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int __init init_mac80211_hwsim(void)
|
static int __init init_mac80211_hwsim(void)
|
||||||
{
|
{
|
||||||
int i, err;
|
int i, err;
|
||||||
|
@ -3960,10 +4244,14 @@ static int __init init_mac80211_hwsim(void)
|
||||||
if (err)
|
if (err)
|
||||||
goto out_unregister_driver;
|
goto out_unregister_driver;
|
||||||
|
|
||||||
|
err = hwsim_register_virtio_driver();
|
||||||
|
if (err)
|
||||||
|
goto out_exit_netlink;
|
||||||
|
|
||||||
hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim");
|
hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim");
|
||||||
if (IS_ERR(hwsim_class)) {
|
if (IS_ERR(hwsim_class)) {
|
||||||
err = PTR_ERR(hwsim_class);
|
err = PTR_ERR(hwsim_class);
|
||||||
goto out_exit_netlink;
|
goto out_exit_virtio;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < radios; i++) {
|
for (i = 0; i < radios; i++) {
|
||||||
|
@ -4075,6 +4363,8 @@ out_free_mon:
|
||||||
free_netdev(hwsim_mon);
|
free_netdev(hwsim_mon);
|
||||||
out_free_radios:
|
out_free_radios:
|
||||||
mac80211_hwsim_free();
|
mac80211_hwsim_free();
|
||||||
|
out_exit_virtio:
|
||||||
|
hwsim_unregister_virtio_driver();
|
||||||
out_exit_netlink:
|
out_exit_netlink:
|
||||||
hwsim_exit_netlink();
|
hwsim_exit_netlink();
|
||||||
out_unregister_driver:
|
out_unregister_driver:
|
||||||
|
@ -4091,6 +4381,7 @@ static void __exit exit_mac80211_hwsim(void)
|
||||||
{
|
{
|
||||||
pr_debug("mac80211_hwsim: unregister radios\n");
|
pr_debug("mac80211_hwsim: unregister radios\n");
|
||||||
|
|
||||||
|
hwsim_unregister_virtio_driver();
|
||||||
hwsim_exit_netlink();
|
hwsim_exit_netlink();
|
||||||
|
|
||||||
mac80211_hwsim_free();
|
mac80211_hwsim_free();
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
|
* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
|
||||||
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
|
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
|
||||||
* Copyright (c) 2011, Javier Lopez <jlopex@gmail.com>
|
* Copyright (c) 2011, Javier Lopez <jlopex@gmail.com>
|
||||||
|
* Copyright (C) 2020 Intel Corporation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __MAC80211_HWSIM_H
|
#ifndef __MAC80211_HWSIM_H
|
||||||
|
@ -245,4 +246,24 @@ struct hwsim_tx_rate_flag {
|
||||||
s8 idx;
|
s8 idx;
|
||||||
u16 flags;
|
u16 flags;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DOC: Frame transmission support over virtio
|
||||||
|
*
|
||||||
|
* Frame transmission is also supported over virtio to allow communication
|
||||||
|
* with external entities.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum hwsim_vqs - queues for virtio frame transmission
|
||||||
|
*
|
||||||
|
* @HWSIM_VQ_TX: send frames to external entity
|
||||||
|
* @HWSIM_VQ_RX: receive frames and transmission info reports
|
||||||
|
* @HWSIM_NUM_VQS: enum limit
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
HWSIM_VQ_TX,
|
||||||
|
HWSIM_VQ_RX,
|
||||||
|
HWSIM_NUM_VQS,
|
||||||
|
};
|
||||||
#endif /* __MAC80211_HWSIM_H */
|
#endif /* __MAC80211_HWSIM_H */
|
||||||
|
|
|
@ -46,5 +46,6 @@
|
||||||
#define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */
|
#define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */
|
||||||
#define VIRTIO_ID_FS 26 /* virtio filesystem */
|
#define VIRTIO_ID_FS 26 /* virtio filesystem */
|
||||||
#define VIRTIO_ID_PMEM 27 /* virtio pmem */
|
#define VIRTIO_ID_PMEM 27 /* virtio pmem */
|
||||||
|
#define VIRTIO_ID_MAC80211_HWSIM 29 /* virtio mac80211-hwsim */
|
||||||
|
|
||||||
#endif /* _LINUX_VIRTIO_IDS_H */
|
#endif /* _LINUX_VIRTIO_IDS_H */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче