[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250603190506.6382-1-ramonreisfontes@gmail.com>
Date: Tue, 3 Jun 2025 16:05:06 -0300
From: Ramon Fontes <ramonreisfontes@...il.com>
To: davem@...emloft.net,
kuba@...nel.org,
pabeni@...hat.com
Cc: linux-wpan@...r.kernel.org,
alex.aring@...il.com,
miquel.raynal@...tlin.com,
netdev@...r.kernel.org,
Ramon Fontes <ramonreisfontes@...il.com>
Subject: [PATCH] Integration with the user space
This PR introduces support for use space tools such as wmediumd in the mac802154_hwsim kernel module, similar to the existing support for Wi-Fi simulations via mac80211_hwsim. With this addition, it becomes possible to simulate interference and control transmission behavior in IEEE 802.15.4 networks using a userspace backend.
Key changes:
* Introduced communication between mac802154_hwsim and the wmediumd daemon via netlink.
* Included a packet queue (skb_queue) that ensures packets are only transmitted after validation by wmediumd.
* Integrated support to consult wmediumd before transmitting packets, enabling the simulation of interference or transmission failure.
Backward Compatibility:
The current behavior of mac802154_hwsim remains unchanged when wmediumd is not running. If the daemon is unavailable or disabled, the module continues to operate normally, ensuring full compatibility with existing tools and workflows.
Purpose:
Enable more realistic simulations of IEEE 802.15.4-based sensor and IoT networks, accounting for factors like interference, link quality, and connectivity. This fills a gap in current emulation tools where mac802154_hwsim lacks channel behavior modeling.
Testing:
A wmediumd release for mac802154_hwsim is available at https://github.com/ramonfontes/wmediumd_802154/ and you can follow updated instructions there. However, you can also follow the steps below:
1. Building wmediumd_802154:
$ cd wmediumd_802154
$ make
$ sudo make install
2. Loading the modified mac802154_hwsim kernel module (this PR)
$ sudo modprobe mac802154_hwsim radios=3
3. Running wmediumd_802154
$ cd tests
$ sudo ./interference.sh
$ sudo wmediumd_802154 -s -c tree.cfg
4. Terminal 1: Pinging virtual sensors
The network topology of tree.cfg looks like below:
s1 -- s0 -- s2
s2 is too far away from s0 and the communication between s0 and s2 is not possible. You can now test connectivity between the virtual sensors using IPv6 link-local addresses.
Ping from sensor0 to sensor1:
ping -c 2 fe80::2
PING fe80::2 (fe80::2) 56 data bytes
64 bytes from fe80::2%pan0: icmp_seq=1 ttl=64 time=2.33 ms
64 bytes from fe80::2%pan0: icmp_seq=2 ttl=64 time=1.77 ms
--- fe80::2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 1.770/2.051/2.332/0.281 ms
Ping from sensor0 to sensor2:
ping -c 2 fe80::3
PING fe80::3 (fe80::3) 56 data bytes
--- fe80::3 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1054ms
Simulated custom topologies using Mininet-WiFi with 802.15.4 support is also possible by running https://github.com/intrig-unicamp/mininet-wifi/blob/9f2cba0d6ebcfebf6c19161b81b6186cfa110cca/examples/wmediumd_interference_lowpan.py
Signed-off-by: Ramon Fontes <ramonreisfontes@...il.com>
---
drivers/net/ieee802154/mac802154_hwsim.c | 975 ++++++++++++++++++++++-
drivers/net/ieee802154/mac802154_hwsim.h | 57 +-
2 files changed, 999 insertions(+), 33 deletions(-)
diff --git a/drivers/net/ieee802154/mac802154_hwsim.c b/drivers/net/ieee802154/mac802154_hwsim.c
index 2f7520454..782aba5fa 100644
--- a/drivers/net/ieee802154/mac802154_hwsim.c
+++ b/drivers/net/ieee802154/mac802154_hwsim.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/platform_device.h>
+#include <linux/debugfs.h>
#include <linux/rtnetlink.h>
#include <linux/netdevice.h>
#include <linux/device.h>
@@ -22,13 +23,23 @@
#include <net/mac802154.h>
#include <net/cfg802154.h>
#include <net/genetlink.h>
+#include <net/netns/generic.h>
+#include <linux/rhashtable.h>
#include "mac802154_hwsim.h"
+#include <linux/virtio.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
MODULE_DESCRIPTION("Software simulator of IEEE 802.15.4 radio(s) for mac802154");
MODULE_LICENSE("GPL");
+static int radios = 2;
+module_param(radios, int, 0444);
+MODULE_PARM_DESC(radios, "Number of simulated radios");
+
static LIST_HEAD(hwsim_phys);
static DEFINE_MUTEX(hwsim_phys_lock);
+static int hwsim_radios_generation = 1;
static struct platform_device *mac802154hwsim_dev;
@@ -37,6 +48,50 @@ static struct genl_family hwsim_genl_family;
static int hwsim_radio_idx;
+static unsigned int hwsim_net_id;
+
+static DEFINE_IDA(hwsim_netgroup_ida);
+
+static struct class *hwsim_class;
+
+struct hwsim_net {
+ int netgroup;
+ u32 wmediumd;
+};
+
+struct hwsim_cb {
+ uintptr_t cookie;
+};
+
+static inline u32 hwsim_net_get_wmediumd(struct net *net)
+{
+ struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id);
+
+ return hwsim_net->wmediumd;
+}
+
+static inline void hwsim_net_set_wmediumd(struct net *net, u32 portid)
+{
+ struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id);
+
+ hwsim_net->wmediumd = portid;
+}
+
+static inline int hwsim_net_get_netgroup(struct net *net)
+{
+ struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id);
+
+ return hwsim_net->netgroup;
+}
+
+static inline int hwsim_net_set_netgroup(struct net *net)
+{
+ struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id);
+
+ hwsim_net->netgroup = ida_alloc(&hwsim_netgroup_ida, GFP_KERNEL);
+ return hwsim_net->netgroup >= 0 ? 0 : -ENOMEM;
+}
+
enum hwsim_multicast_groups {
HWSIM_MCGRP_CONFIG,
};
@@ -45,6 +100,14 @@ static const struct genl_multicast_group hwsim_mcgrps[] = {
[HWSIM_MCGRP_CONFIG] = { .name = "config", },
};
+struct hwsim_pending_tx {
+ struct ieee802154_hw *hw;
+ struct sk_buff *skb;
+ struct list_head list;
+};
+
+static LIST_HEAD(pending_tx_list);
+
struct hwsim_pib {
u8 page;
u8 channel;
@@ -73,13 +136,42 @@ struct hwsim_phy {
u32 idx;
struct hwsim_pib __rcu *pib;
+ bool rht_inserted;
+ u8 ieee_addr[8];
+
+ struct rhash_head rht;
+ struct dentry *debugfs;
+ atomic_t pending_cookie;
+ struct sk_buff_head pending;
+ struct sk_buff *pending_skb;
+ struct device *dev;
+ struct mutex mutex;
+
+ bool destroy_on_close;
+ u32 portid;
bool suspended;
struct list_head edges;
struct list_head list;
+
+ /* group shared by radios created in the same netns */
+ int netgroup;
+ /* wmediumd portid responsible for netgroup of this radio */
+ u32 wmediumd;
};
+static const struct rhashtable_params hwsim_rht_params = {
+ .nelem_hint = 2,
+ .automatic_shrinking = true,
+ .key_len = 8, /* ETH_ALEN */
+ .key_offset = offsetof(struct hwsim_phy, ieee_addr),
+ .head_offset = offsetof(struct hwsim_phy, rht),
+};
+
+static DEFINE_SPINLOCK(hwsim_radio_lock);
+static struct rhashtable hwsim_radios_rht;
+
static int hwsim_add_one(struct genl_info *info, struct device *dev,
bool init);
static void hwsim_del(struct hwsim_phy *phy);
@@ -91,6 +183,17 @@ static int hwsim_hw_ed(struct ieee802154_hw *hw, u8 *level)
return 0;
}
+static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
+ struct genl_info *info)
+{
+ if (info)
+ genl_notify(&hwsim_genl_family, mcast_skb, info,
+ HWSIM_MCGRP_CONFIG, GFP_KERNEL);
+ else
+ genlmsg_multicast(&hwsim_genl_family, mcast_skb, 0,
+ HWSIM_MCGRP_CONFIG, GFP_KERNEL);
+}
+
static int hwsim_update_pib(struct ieee802154_hw *hw, u8 page, u8 channel,
struct ieee802154_hw_addr_filt *filt,
enum ieee802154_filtering_level filt_level)
@@ -114,9 +217,15 @@ static int hwsim_update_pib(struct ieee802154_hw *hw, u8 page, u8 channel,
rcu_assign_pointer(phy->pib, pib);
kfree_rcu(pib_old, rcu);
+
return 0;
}
+static struct hwsim_phy *get_hwsim_data_ref_from_addr(const u8 *addr)
+{
+ return rhashtable_lookup_fast(&hwsim_radios_rht, addr, hwsim_rht_params);
+}
+
static int hwsim_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
{
struct hwsim_phy *phy = hw->priv;
@@ -147,6 +256,140 @@ static int hwsim_hw_addr_filt(struct ieee802154_hw *hw,
return ret;
}
+static int hwsim_unicast_netgroup(struct hwsim_phy *data,
+ struct sk_buff *skb, int portid)
+{
+ struct net *net;
+ bool found = false;
+ int res = -ENOENT;
+
+ rcu_read_lock();
+ for_each_net_rcu(net) {
+ if (data->netgroup == hwsim_net_get_netgroup(net)) {
+ res = genlmsg_unicast(net, skb, portid);
+ found = true;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ if (!found)
+ nlmsg_free(skb);
+
+ return res;
+}
+
+#if IS_REACHABLE(CONFIG_VIRTIO)
+
+/* MAC80211_HWSIM virtio queues */
+static struct virtqueue *hwsim_vqs[HWSIM_NUM_VQS];
+static bool hwsim_virtio_enabled;
+static DEFINE_SPINLOCK(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 hwsim_phy *phy,
+ 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 hwsim_phy *phy,
+ struct sk_buff *skb);
+#define hwsim_virtio_enabled false
+#endif
+
+static void mac802154_hwsim_tx_frame_nl(struct ieee802154_hw *hw, struct sk_buff *my_skb,
+ int dst_portid)
+{
+ struct sk_buff *skb;
+ struct hwsim_phy *phy = hw->priv;
+ void *msg_head;
+ unsigned int hwsim_flags = 0;
+ uintptr_t cookie;
+
+ skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+
+ if (skb == NULL)
+ goto nla_put_failure;
+
+ msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
+ MAC802154_HWSIM_CMD_FRAME);
+ if (msg_head == NULL) {
+ pr_debug("mac802154_hwsim: problem with msg_head\n");
+ goto nla_put_failure;
+ }
+
+ u8 addr_buf[8];
+ put_unaligned_le64(hw->phy->perm_extended_addr, addr_buf);
+ put_unaligned_le64(hw->phy->perm_extended_addr, phy->ieee_addr);
+
+ if (nla_put(skb, MAC802154_HWSIM_ATTR_ADDR_TRANSMITTER,
+ 8, addr_buf))
+ goto nla_put_failure;
+
+ /* We get the skb->data */
+ if (nla_put(skb, MAC802154_HWSIM_ATTR_FRAME, my_skb->len, my_skb->data))
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, MAC802154_HWSIM_ATTR_FLAGS, hwsim_flags))
+ goto nla_put_failure;
+
+ /* We create a cookie to identify this skb */
+ cookie = atomic_inc_return(&phy->pending_cookie);
+
+ if (nla_put_u64_64bit(skb, MAC802154_HWSIM_ATTR_COOKIE, cookie, MAC802154_HWSIM_ATTR_PAD))
+ goto nla_put_failure;
+
+ genlmsg_end(skb, msg_head);
+
+ struct sk_buff *skb_2 = skb_clone(my_skb, GFP_ATOMIC);
+ if (!skb_2)
+ goto err_free_txskb;
+
+ if (hwsim_virtio_enabled) {
+ if (hwsim_tx_virtio(phy, skb))
+ goto err_free_txskb;
+ } else {
+ if (hwsim_unicast_netgroup(phy, skb, dst_portid))
+ goto err_free_txskb;
+ }
+
+ /* Enqueue the packet */
+ skb_queue_tail(&phy->pending, skb_2);
+
+ return;
+
+nla_put_failure:
+ nlmsg_free(skb);
+err_free_txskb:
+ pr_debug("mac802154_hwsim: error occurred in %s\n", __func__);
+}
+
static void hwsim_hw_receive(struct ieee802154_hw *hw, struct sk_buff *skb,
u8 lqi)
{
@@ -259,25 +502,142 @@ static int hwsim_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
struct hwsim_pib *current_pib, *endpoint_pib;
struct hwsim_edge_info *einfo;
struct hwsim_edge *e;
+ u32 _portid;
WARN_ON(current_phy->suspended);
+ /* wmediumd mode check */
+ _portid = READ_ONCE(current_phy->wmediumd);
+
+ if (_portid || hwsim_virtio_enabled){
+ mac802154_hwsim_tx_frame_nl(hw, skb, _portid);
+
+ ieee802154_xmit_complete(hw, skb, false);
+ return 0;
+ }
+ else{
+ rcu_read_lock();
+ current_pib = rcu_dereference(current_phy->pib);
+
+ list_for_each_entry_rcu(e, ¤t_phy->edges, list) {
+ /* Can be changed later in rx_irqsafe, but this is only a
+ * performance tweak. Received radio should drop the frame
+ * in mac802154 stack anyway... so we don't need to be
+ * 100% of locking here to check on suspended
+ */
+ if (e->endpoint->suspended)
+ continue;
+
+ endpoint_pib = rcu_dereference(e->endpoint->pib);
+ if (current_pib->page == endpoint_pib->page &&
+ current_pib->channel == endpoint_pib->channel) {
+ struct sk_buff *newskb = pskb_copy(skb, GFP_ATOMIC);
+
+ einfo = rcu_dereference(e->info);
+ if (newskb)
+ hwsim_hw_receive(e->endpoint->hw, newskb, einfo->lqi);
+ }
+ }
+ rcu_read_unlock();
+
+ ieee802154_xmit_complete(hw, skb, false);
+ return 0;
+ }
+}
+
+static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+ struct hwsim_phy *data2;
+ struct ieee802154_hdr *hdr;
+ const u8 *dst;
+ int frame_data_len;
+ void *frame_data;
+ struct sk_buff *skb = NULL;
+ struct hwsim_pib *current_pib, *endpoint_pib;
+ struct hwsim_edge *e;
+ struct hwsim_edge_info *einfo;
+
+ if (!info->attrs[MAC802154_HWSIM_ATTR_ADDR_RECEIVER] ||
+ !info->attrs[MAC802154_HWSIM_ATTR_FRAME])
+ goto out;
+
+ dst = (void *)nla_data(info->attrs[MAC802154_HWSIM_ATTR_ADDR_RECEIVER]);
+ frame_data_len = nla_len(info->attrs[MAC802154_HWSIM_ATTR_FRAME]);
+ frame_data = (void *)nla_data(info->attrs[MAC802154_HWSIM_ATTR_FRAME]);
+
+ if (frame_data_len < IEEE802154_MIN_HDR_LEN ||
+ frame_data_len > IEEE802154_MAX_FRAME_LEN)
+ goto err;
+
+ /* Allocate new skb here */
+ skb = alloc_skb(frame_data_len, GFP_KERNEL);
+ if (skb == NULL)
+ goto err;
+
+ /* Copy the data */
+ skb_put_data(skb, frame_data, frame_data_len);
+
+ ieee802154_hdr_peek_addrs(skb, hdr);
+
+ u8 *frame = frame_data;
+ u16 fcf = le16_to_cpup((__le16 *)frame);
+ u8 *ptr = frame + 3;
+
+ u8 src_addr[8];
+ u8 dst_addr_mode = (fcf >> 10) & 0x3;
+ u8 src_addr_mode = (fcf >> 14) & 0x3;
+ bool intra_pan = (fcf >> 6) & 0x1;
+
+ /* skip the destination */
+ if (dst_addr_mode == IEEE802154_ADDR_SHORT) {
+ /* dest_pan + short addr */
+ ptr += 2 + 2;
+ } else if (dst_addr_mode == IEEE802154_ADDR_LONG) {
+ /* dest_pan + extended addr */
+ ptr += 2 + 8;
+ }
+
+ /* src_pan */
+ if (!intra_pan)
+ ptr += 2;
+
+ if (src_addr_mode == IEEE802154_ADDR_SHORT)
+ goto out;
+ else if (src_addr_mode == IEEE802154_ADDR_LONG)
+ memcpy(src_addr, ptr, 8);
+
+ data2 = get_hwsim_data_ref_from_addr(src_addr);
+ if (!data2)
+ goto out;
+
+ struct ieee802154_hw *hw = data2->hw;
+
+ struct hwsim_phy *current_phy = hw->priv;
+
rcu_read_lock();
current_pib = rcu_dereference(current_phy->pib);
list_for_each_entry_rcu(e, ¤t_phy->edges, list) {
/* Can be changed later in rx_irqsafe, but this is only a
- * performance tweak. Received radio should drop the frame
- * in mac802154 stack anyway... so we don't need to be
- * 100% of locking here to check on suspended
- */
+ * performance tweak. Received radio should drop the frame
+ * in mac802154 stack anyway... so we don't need to be
+ * 100% of locking here to check on suspended
+ */
if (e->endpoint->suspended)
continue;
endpoint_pib = rcu_dereference(e->endpoint->pib);
if (current_pib->page == endpoint_pib->page &&
- current_pib->channel == endpoint_pib->channel) {
- struct sk_buff *newskb = pskb_copy(skb, GFP_ATOMIC);
+ current_pib->channel == endpoint_pib->channel) {
+
+ struct ieee802154_hw *hw1 = e->endpoint->hw;
+ struct hwsim_phy *phy1 = hw1->priv;
+ u8* addr64 = phy1->ieee_addr;
+ if (dst_addr_mode == IEEE802154_ADDR_LONG && memcmp(dst, addr64, 8) != 0)
+ continue;
+
+ struct sk_buff *newskb = pskb_copy(skb, GFP_ATOMIC);
einfo = rcu_dereference(e->info);
if (newskb)
hwsim_hw_receive(e->endpoint->hw, newskb, einfo->lqi);
@@ -285,8 +645,12 @@ static int hwsim_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
}
rcu_read_unlock();
- ieee802154_xmit_complete(hw, skb, false);
return 0;
+err:
+ pr_debug("mac802154_hwsim: error occurred in %s\n", __func__);
+out:
+ dev_kfree_skb(skb);
+ return -EINVAL;
}
static int hwsim_hw_start(struct ieee802154_hw *hw)
@@ -342,6 +706,36 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
return hwsim_add_one(info, &mac802154hwsim_dev->dev, false);
}
+static void hwsim_mcast_del_radio(int id, const char *hwname,
+ struct genl_info *info)
+{
+ struct sk_buff *skb;
+ void *data;
+ int ret;
+
+ skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb)
+ return;
+
+ data = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
+ MAC802154_HWSIM_CMD_DEL_RADIO);
+ if (!data)
+ goto error;
+
+ ret = nla_put_u32(skb, MAC802154_HWSIM_ATTR_RADIO_ID, id);
+ if (ret < 0)
+ goto error;
+
+ genlmsg_end(skb, data);
+
+ hwsim_mcast_config_msg(skb, info);
+
+ return;
+
+error:
+ nlmsg_free(skb);
+}
+
static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
{
struct hwsim_phy *phy, *tmp;
@@ -365,6 +759,19 @@ static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
return -ENODEV;
}
+static void hwsim_del_radio(struct hwsim_phy *data,
+ const char *hwname,
+ struct genl_info *info)
+{
+ hwsim_mcast_del_radio(data->idx, hwname, info);
+ debugfs_remove_recursive(data->debugfs);
+ ieee802154_unregister_hw(data->hw);
+ device_release_driver(data->dev);
+ device_unregister(data->dev);
+ ieee802154_free_hw(data->hw);
+}
+
+
static int append_radio_msg(struct sk_buff *skb, struct hwsim_phy *phy)
{
struct nlattr *nl_edges, *nl_edge;
@@ -459,6 +866,7 @@ static int hwsim_get_radio_nl(struct sk_buff *msg, struct genl_info *info)
struct sk_buff *skb;
int idx, res = -ENODEV;
+
if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID])
return -EINVAL;
idx = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
@@ -739,12 +1147,103 @@ static int hwsim_set_edge_lqi(struct sk_buff *msg, struct genl_info *info)
return -ENOENT;
}
+static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+ struct nlattr *edge_attrs[MAC802154_HWSIM_EDGE_ATTR_MAX + 1];
+ struct hwsim_edge_info *einfo, *einfo_old;
+ struct hwsim_phy *phy_v0;
+ struct hwsim_edge *e;
+ u8 lqi;
+ u32 v0, v1;
+
+ if (nla_parse_nested_deprecated(edge_attrs, MAC802154_HWSIM_EDGE_ATTR_MAX, info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE], hwsim_edge_policy, NULL))
+ return -EINVAL;
+
+ if (!edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID] ||
+ !edge_attrs[MAC802154_HWSIM_EDGE_ATTR_LQI])
+ return -EINVAL;
+
+ v0 = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
+ v1 = nla_get_u32(edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID]);
+ lqi = nla_get_u8(edge_attrs[MAC802154_HWSIM_EDGE_ATTR_LQI]);
+
+ mutex_lock(&hwsim_phys_lock);
+ phy_v0 = hwsim_get_radio_by_id(v0);
+ if (!phy_v0) {
+ mutex_unlock(&hwsim_phys_lock);
+ return -ENOENT;
+ }
+
+ einfo = kzalloc(sizeof(*einfo), GFP_KERNEL);
+ if (!einfo) {
+ mutex_unlock(&hwsim_phys_lock);
+ return -ENOMEM;
+ }
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(e, &phy_v0->edges, list) {
+ if (e->endpoint->idx == v1) {
+ einfo->lqi = lqi;
+ einfo_old = rcu_replace_pointer(e->info, einfo,
+ lockdep_is_held(&hwsim_phys_lock));
+ rcu_read_unlock();
+ kfree_rcu(einfo_old, rcu);
+ mutex_unlock(&hwsim_phys_lock);
+ return 0;
+ }
+ }
+ rcu_read_unlock();
+
+ kfree(einfo);
+ mutex_unlock(&hwsim_phys_lock);
+
+ return -ENOENT;
+}
+
+static void hwsim_register_wmediumd(struct net *net, u32 portid)
+{
+ struct hwsim_phy *data;
+
+ hwsim_net_set_wmediumd(net, portid);
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry(data, &hwsim_phys, list) {
+ if (data->netgroup == hwsim_net_get_netgroup(net))
+ data->wmediumd = portid;
+ }
+ spin_unlock_bh(&hwsim_radio_lock);
+}
+
+static int hwsim_register_received_nl(struct sk_buff *msg, struct genl_info *info)
+{
+ struct net *net = genl_info_net(info);
+ int chans = 1;
+
+ if (chans > 1)
+ return -EOPNOTSUPP;
+
+ if (hwsim_net_get_wmediumd(net))
+ return -EBUSY;
+
+ hwsim_register_wmediumd(net, info->snd_portid);
+
+ pr_debug("mac802154_hwsim: received a REGISTER, "
+ "switching to wmediumd mode with pid %d\n", info->snd_portid);
+
+ return 0;
+}
+
/* MAC802154_HWSIM netlink policy */
static const struct nla_policy hwsim_genl_policy[MAC802154_HWSIM_ATTR_MAX + 1] = {
[MAC802154_HWSIM_ATTR_RADIO_ID] = { .type = NLA_U32 },
[MAC802154_HWSIM_ATTR_RADIO_EDGE] = { .type = NLA_NESTED },
[MAC802154_HWSIM_ATTR_RADIO_EDGES] = { .type = NLA_NESTED },
+ [MAC802154_HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_BINARY, .len = 8 },
+ [MAC802154_HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_BINARY, .len = 8 },
+ [MAC802154_HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
+ .len = IEEE802154_MAX_FRAME_LEN },
};
/* Generic Netlink operations array */
@@ -785,6 +1284,22 @@ static const struct genl_small_ops hwsim_nl_ops[] = {
.doit = hwsim_set_edge_lqi,
.flags = GENL_UNS_ADMIN_PERM,
},
+ {
+ .cmd = MAC802154_HWSIM_CMD_REGISTER,
+ .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .doit = hwsim_register_received_nl,
+ .flags = GENL_UNS_ADMIN_PERM,
+ },
+ {
+ .cmd = MAC802154_HWSIM_CMD_TX_INFO_FRAME,
+ .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .doit = hwsim_tx_info_frame_received_nl,
+ },
+ {
+ .cmd = MAC802154_HWSIM_CMD_FRAME,
+ .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .doit = hwsim_cloned_frame_received_nl,
+ },
};
static struct genl_family hwsim_genl_family __ro_after_init = {
@@ -792,25 +1307,15 @@ static struct genl_family hwsim_genl_family __ro_after_init = {
.version = 1,
.maxattr = MAC802154_HWSIM_ATTR_MAX,
.policy = hwsim_genl_policy,
+ .netnsok = true,
.module = THIS_MODULE,
.small_ops = hwsim_nl_ops,
.n_small_ops = ARRAY_SIZE(hwsim_nl_ops),
- .resv_start_op = MAC802154_HWSIM_CMD_NEW_EDGE + 1,
+ .resv_start_op = MAC802154_HWSIM_CMD_TX_INFO_FRAME + 1,
.mcgrps = hwsim_mcgrps,
.n_mcgrps = ARRAY_SIZE(hwsim_mcgrps),
};
-static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
- struct genl_info *info)
-{
- if (info)
- genl_notify(&hwsim_genl_family, mcast_skb, info,
- HWSIM_MCGRP_CONFIG, GFP_KERNEL);
- else
- genlmsg_multicast(&hwsim_genl_family, mcast_skb, 0,
- HWSIM_MCGRP_CONFIG, GFP_KERNEL);
-}
-
static void hwsim_mcast_new_radio(struct genl_info *info, struct hwsim_phy *phy)
{
struct sk_buff *mcast_skb;
@@ -899,8 +1404,10 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev,
struct ieee802154_hw *hw;
struct hwsim_phy *phy;
struct hwsim_pib *pib;
+ struct net *net;
int idx;
int err;
+ int ret;
idx = hwsim_radio_idx++;
@@ -908,9 +1415,17 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev,
if (!hw)
return -ENOMEM;
+ if (info)
+ net = genl_info_net(info);
+ else
+ net = &init_net;
+ wpan_phy_net_set(hw->phy, net);
+
phy = hw->priv;
phy->hw = hw;
+ skb_queue_head_init(&phy->pending);
+
/* 868 MHz BPSK 802.15.4-2003 */
hw->phy->supported.channels[0] |= 1;
/* 915 MHz BPSK 802.15.4-2003 */
@@ -943,6 +1458,7 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev,
hw->phy->supported.channels[6] |= 0x3ffc00;
hw->phy->perm_extended_addr = cpu_to_le64(((u64)0x02 << 56) | ((u64)idx));
+ memcpy(phy->ieee_addr, &hw->phy->perm_extended_addr, 8);
/* hwsim phy channel 13 as default */
hw->phy->current_channel = 13;
@@ -952,6 +1468,9 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev,
goto err_pib;
}
+ if (info)
+ phy->portid = info->snd_portid;
+
pib->channel = 13;
pib->filt.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
pib->filt.pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
@@ -962,6 +1481,9 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev,
hw->flags = IEEE802154_HW_PROMISCUOUS;
hw->parent = dev;
+ phy->netgroup = hwsim_net_get_netgroup(net);
+ phy->wmediumd = hwsim_net_get_wmediumd(net);
+
err = ieee802154_register_hw(hw);
if (err)
goto err_reg;
@@ -977,10 +1499,20 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev,
list_add_tail(&phy->list, &hwsim_phys);
mutex_unlock(&hwsim_phys_lock);
+ ret = rhashtable_insert_fast(&hwsim_radios_rht, &phy->rht, hwsim_rht_params);
+ if (ret < 0) {
+ pr_err("Error in adding PHY into rhashtable: %d\n", ret);
+ goto failed_final_insert;
+ }
+ phy->rht_inserted = true;
+
hwsim_mcast_new_radio(info, phy);
return idx;
+failed_final_insert:
+ debugfs_remove_recursive(phy->debugfs);
+ ieee802154_unregister_hw(phy->hw);
err_subscribe:
ieee802154_unregister_hw(phy->hw);
err_reg:
@@ -1011,6 +1543,7 @@ static void hwsim_del(struct hwsim_phy *phy)
ieee802154_unregister_hw(phy->hw);
ieee802154_free_hw(phy->hw);
+ class_destroy(hwsim_class);
}
static int hwsim_probe(struct platform_device *pdev)
@@ -1018,13 +1551,13 @@ static int hwsim_probe(struct platform_device *pdev)
struct hwsim_phy *phy, *tmp;
int err, i;
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < radios; i++) {
err = hwsim_add_one(NULL, &pdev->dev, true);
if (err < 0)
goto err_slave;
}
- dev_info(&pdev->dev, "Added 2 mac802154 hwsim hardware radios\n");
+ dev_info(&pdev->dev, "Added %d mac802154 hwsim hardware radios\n", radios);
return 0;
err_slave:
@@ -1053,40 +1586,420 @@ static struct platform_driver mac802154hwsim_driver = {
},
};
-static __init int hwsim_init_module(void)
+static void remove_user_radios(u32 portid)
+{
+ struct hwsim_phy *entry, *tmp;
+ LIST_HEAD(list);
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry_safe(entry, tmp, &hwsim_phys, list) {
+ if (entry->destroy_on_close && entry->portid == portid) {
+ list_move(&entry->list, &list);
+ rhashtable_remove_fast(&hwsim_radios_rht, &entry->rht,
+ hwsim_rht_params);
+ hwsim_radios_generation++;
+ }
+ }
+ spin_unlock_bh(&hwsim_radio_lock);
+
+ list_for_each_entry_safe(entry, tmp, &list, list) {
+ list_del(&entry->list);
+ hwsim_del_radio(entry, wpan_phy_name(entry->hw->phy),
+ NULL);
+ }
+}
+
+static int mac802154_hwsim_netlink_notify(struct notifier_block *nb,
+ unsigned long state,
+ void *_notify)
+{
+ struct netlink_notify *notify = _notify;
+
+ if (state != NETLINK_URELEASE)
+ return NOTIFY_DONE;
+
+ remove_user_radios(notify->portid);
+
+ if (notify->portid == hwsim_net_get_wmediumd(notify->net)) {
+ printk(KERN_INFO "mac802154_hwsim: wmediumd released netlink"
+ " socket, switching to perfect channel medium\n");
+ hwsim_register_wmediumd(notify->net, 0);
+ }
+ return NOTIFY_DONE;
+
+}
+
+static void __net_exit hwsim_exit_net(struct net *net)
+{
+ struct hwsim_phy *data, *tmp;
+ LIST_HEAD(list);
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry_safe(data, tmp, &hwsim_phys, list) {
+ if (!net_eq(wpan_phy_net(data->hw->phy), net))
+ continue;
+
+ /* Radios created in init_net are returned to init_net. */
+ if (data->netgroup == hwsim_net_get_netgroup(&init_net))
+ continue;
+
+ list_move(&data->list, &list);
+ rhashtable_remove_fast(&hwsim_radios_rht, &data->rht,
+ hwsim_rht_params);
+ hwsim_radios_generation++;
+ }
+ spin_unlock_bh(&hwsim_radio_lock);
+
+ list_for_each_entry_safe(data, tmp, &list, list) {
+ list_del(&data->list);
+ hwsim_del_radio(data,
+ wpan_phy_name(data->hw->phy),
+ NULL);
+ }
+
+ ida_free(&hwsim_netgroup_ida, hwsim_net_get_netgroup(net));
+}
+
+#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)))
+ dev_kfree_skb_irq(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[MAC802154_HWSIM_ATTR_MAX + 1];
+ struct genl_info info = {};
+ int err;
+
+ nlh = nlmsg_hdr(skb);
+ gnlh = nlmsg_data(nlh);
+
+ if (skb->len < nlh->nlmsg_len)
+ return -EINVAL;
+
+ err = genlmsg_parse(nlh, &hwsim_genl_family, tb, MAC802154_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 MAC802154_HWSIM_CMD_FRAME:
+ hwsim_cloned_frame_received_nl(skb, &info);
+ break;
+ case MAC802154_HWSIM_CMD_TX_INFO_FRAME:
+ hwsim_tx_info_frame_received_nl(skb, &info);
+ break;
+ case HWSIM_CMD_REPORT_PMSR:
+ hwsim_pmsr_report_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_reset_tail_pointer(skb);
+ skb_put(skb, len);
+ hwsim_virtio_handle_cmd(skb);
+
+ spin_lock_irqsave(&hwsim_virtio_lock, flags);
+ if (!hwsim_virtio_enabled) {
+ dev_kfree_skb_irq(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_ATOMIC);
+ if (WARN(err, "virtqueue_add_inbuf returned %d\n", err))
+ dev_kfree_skb_irq(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)
+{
+ struct virtqueue_info vqs_info[HWSIM_NUM_VQS] = {
+ [HWSIM_VQ_TX] = { "tx", hwsim_virtio_tx_done },
+ [HWSIM_VQ_RX] = { "rx", hwsim_virtio_rx_done },
+ };
+
+ return virtio_find_vqs(vdev, HWSIM_NUM_VQS,
+ hwsim_vqs, vqs_info, 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;
+
+ virtio_reset_device(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;
+
+ virtio_device_ready(vdev);
+
+ 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);
+}
+
+/* MAC802154_HWSIM virtio device id table */
+static const struct virtio_device_id id_table[] = {
+ { 42, VIRTIO_DEV_ANY_ID },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(virtio, id_table);
+
+static struct virtio_driver virtio_hwsim = {
+ .driver.name = KBUILD_MODNAME,
+ .id_table = id_table,
+ .probe = hwsim_virtio_probe,
+ .remove = hwsim_virtio_remove,
+};
+
+static int hwsim_register_virtio_driver(void)
+{
+ 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 struct notifier_block hwsim_netlink_notifier = {
+ .notifier_call = mac802154_hwsim_netlink_notify,
+};
+
+static int __init hwsim_init_netlink(void)
{
int rc;
+ printk(KERN_INFO "mac802154_hwsim: initializing netlink\n");
+
rc = genl_register_family(&hwsim_genl_family);
if (rc)
- return rc;
+ goto failure;
+
+ rc = netlink_register_notifier(&hwsim_netlink_notifier);
+ if (rc) {
+ genl_unregister_family(&hwsim_genl_family);
+ goto failure;
+ }
+
+ return 0;
+
+failure:
+ pr_debug("mac802154_hwsim: error occurred in %s\n", __func__);
+ return -EINVAL;
+}
+
+static __net_init int hwsim_init_net(struct net *net)
+{
+ return hwsim_net_set_netgroup(net);
+}
+
+static struct pernet_operations hwsim_net_ops = {
+ .init = hwsim_init_net,
+ .exit = hwsim_exit_net,
+ .id = &hwsim_net_id,
+ .size = sizeof(struct hwsim_net),
+};
+
+static void hwsim_exit_netlink(void)
+{
+ /* unregister the notifier */
+ netlink_unregister_notifier(&hwsim_netlink_notifier);
+ /* unregister the family */
+ genl_unregister_family(&hwsim_genl_family);
+}
+
+static __init int hwsim_init_module(void)
+{
+ int rc, err;
+
+ if (radios < 0)
+ return -EINVAL;
+
+ err = rhashtable_init(&hwsim_radios_rht, &hwsim_rht_params);
+ if (err)
+ return err;
+
+ err = register_pernet_device(&hwsim_net_ops);
+ if (err)
+ goto out_free_rht;
+
+ err = hwsim_init_netlink();
+ if (err)
+ goto out_unregister_driver;
+
+ err = hwsim_register_virtio_driver();
+ if (err)
+ goto out_exit_netlink;
+
+ hwsim_class = class_create("mac802154_hwsim");
+ if (IS_ERR(hwsim_class)) {
+ err = PTR_ERR(hwsim_class);
+ goto out_exit_virtio;
+ }
mac802154hwsim_dev = platform_device_register_simple("mac802154_hwsim",
-1, NULL, 0);
if (IS_ERR(mac802154hwsim_dev)) {
rc = PTR_ERR(mac802154hwsim_dev);
- goto platform_dev;
+ goto out_unregister_driver;
}
rc = platform_driver_register(&mac802154hwsim_driver);
if (rc < 0)
- goto platform_drv;
+ goto out_unregister_pernet;
return 0;
-platform_drv:
- platform_device_unregister(mac802154hwsim_dev);
-platform_dev:
- genl_unregister_family(&hwsim_genl_family);
+out_exit_virtio:
+ hwsim_unregister_virtio_driver();
+out_exit_netlink:
+ hwsim_exit_netlink();
+out_unregister_pernet:
+ unregister_pernet_device(&hwsim_net_ops);
return rc;
+out_unregister_driver:
+ platform_driver_unregister(&mac802154hwsim_driver);
+out_free_rht:
+ rhashtable_destroy(&hwsim_radios_rht);
+ return err;
}
static __exit void hwsim_remove_module(void)
{
- genl_unregister_family(&hwsim_genl_family);
+ pr_debug("mac80211_hwsim: unregister radios\n");
+ hwsim_unregister_virtio_driver();
+ hwsim_exit_netlink();
+ rhashtable_destroy(&hwsim_radios_rht);
platform_driver_unregister(&mac802154hwsim_driver);
platform_device_unregister(mac802154hwsim_dev);
+ unregister_pernet_device(&hwsim_net_ops);
}
module_init(hwsim_init_module);
-module_exit(hwsim_remove_module);
+module_exit(hwsim_remove_module);
\ No newline at end of file
diff --git a/drivers/net/ieee802154/mac802154_hwsim.h b/drivers/net/ieee802154/mac802154_hwsim.h
index 6c6e30e38..b1d83ff75 100644
--- a/drivers/net/ieee802154/mac802154_hwsim.h
+++ b/drivers/net/ieee802154/mac802154_hwsim.h
@@ -1,8 +1,31 @@
#ifndef __MAC802154_HWSIM_H
#define __MAC802154_HWSIM_H
-/* mac802154 hwsim netlink commands
+
+#define IEEE802154_MAX_FRAME_LEN 127
+#define IEEE802154_MIN_HDR_LEN 3
+//#define IEEE802154_MAX_DATA_LEN (IEEE802154_PHY_FRAME_LEN - IEEE802154_MAX_HEADER_LEN)
+/**
+ * enum hwsim_tx_control_flags - flags to describe transmission info/status
+ *
+ * These flags are used to give the wmediumd extra information in order to
+ * modify its behavior for each frame
+ *
+ * @HWSIM_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame.
+ * @HWSIM_TX_CTL_NO_ACK: tell the wmediumd not to wait for an ack
+ * @HWSIM_TX_STAT_ACK: Frame was acknowledged
*
+ */
+enum hwsim_tx_control_flags {
+ MAC802154_HWSIM_TX_CTL_REQ_TX_STATUS = BIT(0),
+ MAC802154_HWSIM_TX_CTL_NO_ACK = BIT(1),
+ MAC802154_HWSIM_TX_STAT_ACK = BIT(2),
+};
+
+
+/* mac802154 hwsim netlink commands
+ * @HWSIM_CMD_REGISTER: request to register and received all broadcasted
+ * frames by any mac802154_hwsim radio device.
* @MAC802154_HWSIM_CMD_UNSPEC: unspecified command to catch error
* @MAC802154_HWSIM_CMD_GET_RADIO: fetch information about existing radios
* @MAC802154_HWSIM_CMD_SET_RADIO: change radio parameters during runtime
@@ -28,6 +51,9 @@ enum {
MAC802154_HWSIM_CMD_SET_EDGE,
MAC802154_HWSIM_CMD_DEL_EDGE,
MAC802154_HWSIM_CMD_NEW_EDGE,
+ MAC802154_HWSIM_CMD_REGISTER,
+ MAC802154_HWSIM_CMD_FRAME,
+ MAC802154_HWSIM_CMD_TX_INFO_FRAME,
__MAC802154_HWSIM_CMD_MAX,
};
@@ -48,6 +74,13 @@ enum {
MAC802154_HWSIM_ATTR_RADIO_ID,
MAC802154_HWSIM_ATTR_RADIO_EDGE,
MAC802154_HWSIM_ATTR_RADIO_EDGES,
+ MAC802154_HWSIM_ATTR_COOKIE,
+ MAC802154_HWSIM_ATTR_PAD,
+ MAC802154_HWSIM_ATTR_FRAME,
+ MAC802154_HWSIM_ATTR_ADDR_TRANSMITTER,
+ MAC802154_HWSIM_ATTR_ADDR_RECEIVER,
+ MAC802154_HWSIM_ATTR_TX_INFO,
+ MAC802154_HWSIM_ATTR_FLAGS,
__MAC802154_HWSIM_ATTR_MAX,
};
@@ -70,4 +103,24 @@ enum {
#define MAC802154_HWSIM_EDGE_ATTR_MAX (__MAC802154_HWSIM_EDGE_ATTR_MAX - 1)
-#endif /* __MAC802154_HWSIM_H */
+/**
+ * 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_vqs {
+ HWSIM_VQ_TX,
+ HWSIM_VQ_RX,
+ HWSIM_NUM_VQS,
+};
+
+#endif /* __MAC802154_HWSIM_H */
\ No newline at end of file
--
2.43.0
Powered by blists - more mailing lists