123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * HWSIM IEEE 802.15.4 interface
- *
- * (C) 2018 Mojatau, Alexander Aring <[email protected]>
- * Copyright 2007-2012 Siemens AG
- *
- * Based on fakelb, original Written by:
- * Sergey Lapin <[email protected]>
- * Dmitry Eremin-Solenikov <[email protected]>
- * Alexander Smirnov <[email protected]>
- */
- #include <linux/module.h>
- #include <linux/timer.h>
- #include <linux/platform_device.h>
- #include <linux/rtnetlink.h>
- #include <linux/netdevice.h>
- #include <linux/device.h>
- #include <linux/spinlock.h>
- #include <net/mac802154.h>
- #include <net/cfg802154.h>
- #include <net/genetlink.h>
- #include "mac802154_hwsim.h"
- MODULE_DESCRIPTION("Software simulator of IEEE 802.15.4 radio(s) for mac802154");
- MODULE_LICENSE("GPL");
- static LIST_HEAD(hwsim_phys);
- static DEFINE_MUTEX(hwsim_phys_lock);
- static struct platform_device *mac802154hwsim_dev;
- /* MAC802154_HWSIM netlink family */
- static struct genl_family hwsim_genl_family;
- static int hwsim_radio_idx;
- enum hwsim_multicast_groups {
- HWSIM_MCGRP_CONFIG,
- };
- static const struct genl_multicast_group hwsim_mcgrps[] = {
- [HWSIM_MCGRP_CONFIG] = { .name = "config", },
- };
- struct hwsim_pib {
- u8 page;
- u8 channel;
- struct rcu_head rcu;
- };
- struct hwsim_edge_info {
- u8 lqi;
- struct rcu_head rcu;
- };
- struct hwsim_edge {
- struct hwsim_phy *endpoint;
- struct hwsim_edge_info __rcu *info;
- struct list_head list;
- struct rcu_head rcu;
- };
- struct hwsim_phy {
- struct ieee802154_hw *hw;
- u32 idx;
- struct hwsim_pib __rcu *pib;
- bool suspended;
- struct list_head edges;
- struct list_head list;
- };
- static int hwsim_add_one(struct genl_info *info, struct device *dev,
- bool init);
- static void hwsim_del(struct hwsim_phy *phy);
- static int hwsim_hw_ed(struct ieee802154_hw *hw, u8 *level)
- {
- *level = 0xbe;
- return 0;
- }
- static int hwsim_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
- {
- struct hwsim_phy *phy = hw->priv;
- struct hwsim_pib *pib, *pib_old;
- pib = kzalloc(sizeof(*pib), GFP_KERNEL);
- if (!pib)
- return -ENOMEM;
- pib->page = page;
- pib->channel = channel;
- pib_old = rtnl_dereference(phy->pib);
- rcu_assign_pointer(phy->pib, pib);
- kfree_rcu(pib_old, rcu);
- return 0;
- }
- static int hwsim_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
- {
- struct hwsim_phy *current_phy = hw->priv;
- struct hwsim_pib *current_pib, *endpoint_pib;
- struct hwsim_edge_info *einfo;
- struct hwsim_edge *e;
- WARN_ON(current_phy->suspended);
- 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)
- ieee802154_rx_irqsafe(e->endpoint->hw, newskb,
- einfo->lqi);
- }
- }
- rcu_read_unlock();
- ieee802154_xmit_complete(hw, skb, false);
- return 0;
- }
- static int hwsim_hw_start(struct ieee802154_hw *hw)
- {
- struct hwsim_phy *phy = hw->priv;
- phy->suspended = false;
- return 0;
- }
- static void hwsim_hw_stop(struct ieee802154_hw *hw)
- {
- struct hwsim_phy *phy = hw->priv;
- phy->suspended = true;
- }
- static int
- hwsim_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on)
- {
- return 0;
- }
- static const struct ieee802154_ops hwsim_ops = {
- .owner = THIS_MODULE,
- .xmit_async = hwsim_hw_xmit,
- .ed = hwsim_hw_ed,
- .set_channel = hwsim_hw_channel,
- .start = hwsim_hw_start,
- .stop = hwsim_hw_stop,
- .set_promiscuous_mode = hwsim_set_promiscuous_mode,
- };
- static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
- {
- return hwsim_add_one(info, &mac802154hwsim_dev->dev, false);
- }
- static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
- {
- struct hwsim_phy *phy, *tmp;
- s64 idx = -1;
- if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID])
- return -EINVAL;
- idx = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
- mutex_lock(&hwsim_phys_lock);
- list_for_each_entry_safe(phy, tmp, &hwsim_phys, list) {
- if (idx == phy->idx) {
- hwsim_del(phy);
- mutex_unlock(&hwsim_phys_lock);
- return 0;
- }
- }
- mutex_unlock(&hwsim_phys_lock);
- return -ENODEV;
- }
- static int append_radio_msg(struct sk_buff *skb, struct hwsim_phy *phy)
- {
- struct nlattr *nl_edges, *nl_edge;
- struct hwsim_edge_info *einfo;
- struct hwsim_edge *e;
- int ret;
- ret = nla_put_u32(skb, MAC802154_HWSIM_ATTR_RADIO_ID, phy->idx);
- if (ret < 0)
- return ret;
- rcu_read_lock();
- if (list_empty(&phy->edges)) {
- rcu_read_unlock();
- return 0;
- }
- nl_edges = nla_nest_start_noflag(skb,
- MAC802154_HWSIM_ATTR_RADIO_EDGES);
- if (!nl_edges) {
- rcu_read_unlock();
- return -ENOBUFS;
- }
- list_for_each_entry_rcu(e, &phy->edges, list) {
- nl_edge = nla_nest_start_noflag(skb,
- MAC802154_HWSIM_ATTR_RADIO_EDGE);
- if (!nl_edge) {
- rcu_read_unlock();
- nla_nest_cancel(skb, nl_edges);
- return -ENOBUFS;
- }
- ret = nla_put_u32(skb, MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID,
- e->endpoint->idx);
- if (ret < 0) {
- rcu_read_unlock();
- nla_nest_cancel(skb, nl_edge);
- nla_nest_cancel(skb, nl_edges);
- return ret;
- }
- einfo = rcu_dereference(e->info);
- ret = nla_put_u8(skb, MAC802154_HWSIM_EDGE_ATTR_LQI,
- einfo->lqi);
- if (ret < 0) {
- rcu_read_unlock();
- nla_nest_cancel(skb, nl_edge);
- nla_nest_cancel(skb, nl_edges);
- return ret;
- }
- nla_nest_end(skb, nl_edge);
- }
- rcu_read_unlock();
- nla_nest_end(skb, nl_edges);
- return 0;
- }
- static int hwsim_get_radio(struct sk_buff *skb, struct hwsim_phy *phy,
- u32 portid, u32 seq,
- struct netlink_callback *cb, int flags)
- {
- void *hdr;
- int res;
- hdr = genlmsg_put(skb, portid, seq, &hwsim_genl_family, flags,
- MAC802154_HWSIM_CMD_GET_RADIO);
- if (!hdr)
- return -EMSGSIZE;
- if (cb)
- genl_dump_check_consistent(cb, hdr);
- res = append_radio_msg(skb, phy);
- if (res < 0)
- goto out_err;
- genlmsg_end(skb, hdr);
- return 0;
- out_err:
- genlmsg_cancel(skb, hdr);
- return res;
- }
- static int hwsim_get_radio_nl(struct sk_buff *msg, struct genl_info *info)
- {
- struct hwsim_phy *phy;
- 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]);
- mutex_lock(&hwsim_phys_lock);
- list_for_each_entry(phy, &hwsim_phys, list) {
- if (phy->idx != idx)
- continue;
- skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
- if (!skb) {
- res = -ENOMEM;
- goto out_err;
- }
- res = hwsim_get_radio(skb, phy, info->snd_portid,
- info->snd_seq, NULL, 0);
- if (res < 0) {
- nlmsg_free(skb);
- goto out_err;
- }
- res = genlmsg_reply(skb, info);
- break;
- }
- out_err:
- mutex_unlock(&hwsim_phys_lock);
- return res;
- }
- static int hwsim_dump_radio_nl(struct sk_buff *skb,
- struct netlink_callback *cb)
- {
- int idx = cb->args[0];
- struct hwsim_phy *phy;
- int res;
- mutex_lock(&hwsim_phys_lock);
- if (idx == hwsim_radio_idx)
- goto done;
- list_for_each_entry(phy, &hwsim_phys, list) {
- if (phy->idx < idx)
- continue;
- res = hwsim_get_radio(skb, phy, NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq, cb, NLM_F_MULTI);
- if (res < 0)
- break;
- idx = phy->idx + 1;
- }
- cb->args[0] = idx;
- done:
- mutex_unlock(&hwsim_phys_lock);
- return skb->len;
- }
- /* caller need to held hwsim_phys_lock */
- static struct hwsim_phy *hwsim_get_radio_by_id(uint32_t idx)
- {
- struct hwsim_phy *phy;
- list_for_each_entry(phy, &hwsim_phys, list) {
- if (phy->idx == idx)
- return phy;
- }
- return NULL;
- }
- static const struct nla_policy hwsim_edge_policy[MAC802154_HWSIM_EDGE_ATTR_MAX + 1] = {
- [MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID] = { .type = NLA_U32 },
- [MAC802154_HWSIM_EDGE_ATTR_LQI] = { .type = NLA_U8 },
- };
- static struct hwsim_edge *hwsim_alloc_edge(struct hwsim_phy *endpoint, u8 lqi)
- {
- struct hwsim_edge_info *einfo;
- struct hwsim_edge *e;
- e = kzalloc(sizeof(*e), GFP_KERNEL);
- if (!e)
- return NULL;
- einfo = kzalloc(sizeof(*einfo), GFP_KERNEL);
- if (!einfo) {
- kfree(e);
- return NULL;
- }
- einfo->lqi = 0xff;
- rcu_assign_pointer(e->info, einfo);
- e->endpoint = endpoint;
- return e;
- }
- static void hwsim_free_edge(struct hwsim_edge *e)
- {
- struct hwsim_edge_info *einfo;
- rcu_read_lock();
- einfo = rcu_dereference(e->info);
- rcu_read_unlock();
- kfree_rcu(einfo, rcu);
- kfree_rcu(e, rcu);
- }
- static int hwsim_new_edge_nl(struct sk_buff *msg, struct genl_info *info)
- {
- struct nlattr *edge_attrs[MAC802154_HWSIM_EDGE_ATTR_MAX + 1];
- struct hwsim_phy *phy_v0, *phy_v1;
- struct hwsim_edge *e;
- u32 v0, v1;
- if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID] ||
- !info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE])
- return -EINVAL;
- 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])
- 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]);
- if (v0 == v1)
- return -EINVAL;
- mutex_lock(&hwsim_phys_lock);
- phy_v0 = hwsim_get_radio_by_id(v0);
- if (!phy_v0) {
- mutex_unlock(&hwsim_phys_lock);
- return -ENOENT;
- }
- phy_v1 = hwsim_get_radio_by_id(v1);
- if (!phy_v1) {
- mutex_unlock(&hwsim_phys_lock);
- return -ENOENT;
- }
- rcu_read_lock();
- list_for_each_entry_rcu(e, &phy_v0->edges, list) {
- if (e->endpoint->idx == v1) {
- mutex_unlock(&hwsim_phys_lock);
- rcu_read_unlock();
- return -EEXIST;
- }
- }
- rcu_read_unlock();
- e = hwsim_alloc_edge(phy_v1, 0xff);
- if (!e) {
- mutex_unlock(&hwsim_phys_lock);
- return -ENOMEM;
- }
- list_add_rcu(&e->list, &phy_v0->edges);
- /* wait until changes are done under hwsim_phys_lock lock
- * should prevent of calling this function twice while
- * edges list has not the changes yet.
- */
- synchronize_rcu();
- mutex_unlock(&hwsim_phys_lock);
- return 0;
- }
- static int hwsim_del_edge_nl(struct sk_buff *msg, struct genl_info *info)
- {
- struct nlattr *edge_attrs[MAC802154_HWSIM_EDGE_ATTR_MAX + 1];
- struct hwsim_phy *phy_v0;
- struct hwsim_edge *e;
- u32 v0, v1;
- if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID] ||
- !info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE])
- return -EINVAL;
- 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])
- 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]);
- mutex_lock(&hwsim_phys_lock);
- phy_v0 = hwsim_get_radio_by_id(v0);
- if (!phy_v0) {
- mutex_unlock(&hwsim_phys_lock);
- return -ENOENT;
- }
- rcu_read_lock();
- list_for_each_entry_rcu(e, &phy_v0->edges, list) {
- if (e->endpoint->idx == v1) {
- rcu_read_unlock();
- list_del_rcu(&e->list);
- hwsim_free_edge(e);
- /* same again - wait until list changes are done */
- synchronize_rcu();
- mutex_unlock(&hwsim_phys_lock);
- return 0;
- }
- }
- rcu_read_unlock();
- mutex_unlock(&hwsim_phys_lock);
- return -ENOENT;
- }
- static int hwsim_set_edge_lqi(struct sk_buff *msg, 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;
- u32 v0, v1;
- u8 lqi;
- if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID] ||
- !info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE])
- return -EINVAL;
- 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;
- }
- /* 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 },
- };
- /* Generic Netlink operations array */
- static const struct genl_small_ops hwsim_nl_ops[] = {
- {
- .cmd = MAC802154_HWSIM_CMD_NEW_RADIO,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = hwsim_new_radio_nl,
- .flags = GENL_UNS_ADMIN_PERM,
- },
- {
- .cmd = MAC802154_HWSIM_CMD_DEL_RADIO,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = hwsim_del_radio_nl,
- .flags = GENL_UNS_ADMIN_PERM,
- },
- {
- .cmd = MAC802154_HWSIM_CMD_GET_RADIO,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = hwsim_get_radio_nl,
- .dumpit = hwsim_dump_radio_nl,
- },
- {
- .cmd = MAC802154_HWSIM_CMD_NEW_EDGE,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = hwsim_new_edge_nl,
- .flags = GENL_UNS_ADMIN_PERM,
- },
- {
- .cmd = MAC802154_HWSIM_CMD_DEL_EDGE,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = hwsim_del_edge_nl,
- .flags = GENL_UNS_ADMIN_PERM,
- },
- {
- .cmd = MAC802154_HWSIM_CMD_SET_EDGE,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = hwsim_set_edge_lqi,
- .flags = GENL_UNS_ADMIN_PERM,
- },
- };
- static struct genl_family hwsim_genl_family __ro_after_init = {
- .name = "MAC802154_HWSIM",
- .version = 1,
- .maxattr = MAC802154_HWSIM_ATTR_MAX,
- .policy = hwsim_genl_policy,
- .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,
- .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;
- void *data;
- mcast_skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!mcast_skb)
- return;
- data = genlmsg_put(mcast_skb, 0, 0, &hwsim_genl_family, 0,
- MAC802154_HWSIM_CMD_NEW_RADIO);
- if (!data)
- goto out_err;
- if (append_radio_msg(mcast_skb, phy) < 0)
- goto out_err;
- genlmsg_end(mcast_skb, data);
- hwsim_mcast_config_msg(mcast_skb, info);
- return;
- out_err:
- genlmsg_cancel(mcast_skb, data);
- nlmsg_free(mcast_skb);
- }
- static void hwsim_edge_unsubscribe_me(struct hwsim_phy *phy)
- {
- struct hwsim_phy *tmp;
- struct hwsim_edge *e;
- rcu_read_lock();
- /* going to all phy edges and remove phy from it */
- list_for_each_entry(tmp, &hwsim_phys, list) {
- list_for_each_entry_rcu(e, &tmp->edges, list) {
- if (e->endpoint->idx == phy->idx) {
- list_del_rcu(&e->list);
- hwsim_free_edge(e);
- }
- }
- }
- rcu_read_unlock();
- synchronize_rcu();
- }
- static int hwsim_subscribe_all_others(struct hwsim_phy *phy)
- {
- struct hwsim_phy *sub;
- struct hwsim_edge *e;
- list_for_each_entry(sub, &hwsim_phys, list) {
- e = hwsim_alloc_edge(sub, 0xff);
- if (!e)
- goto me_fail;
- list_add_rcu(&e->list, &phy->edges);
- }
- list_for_each_entry(sub, &hwsim_phys, list) {
- e = hwsim_alloc_edge(phy, 0xff);
- if (!e)
- goto sub_fail;
- list_add_rcu(&e->list, &sub->edges);
- }
- return 0;
- sub_fail:
- hwsim_edge_unsubscribe_me(phy);
- me_fail:
- rcu_read_lock();
- list_for_each_entry_rcu(e, &phy->edges, list) {
- list_del_rcu(&e->list);
- hwsim_free_edge(e);
- }
- rcu_read_unlock();
- return -ENOMEM;
- }
- static int hwsim_add_one(struct genl_info *info, struct device *dev,
- bool init)
- {
- struct ieee802154_hw *hw;
- struct hwsim_phy *phy;
- struct hwsim_pib *pib;
- int idx;
- int err;
- idx = hwsim_radio_idx++;
- hw = ieee802154_alloc_hw(sizeof(*phy), &hwsim_ops);
- if (!hw)
- return -ENOMEM;
- phy = hw->priv;
- phy->hw = hw;
- /* 868 MHz BPSK 802.15.4-2003 */
- hw->phy->supported.channels[0] |= 1;
- /* 915 MHz BPSK 802.15.4-2003 */
- hw->phy->supported.channels[0] |= 0x7fe;
- /* 2.4 GHz O-QPSK 802.15.4-2003 */
- hw->phy->supported.channels[0] |= 0x7FFF800;
- /* 868 MHz ASK 802.15.4-2006 */
- hw->phy->supported.channels[1] |= 1;
- /* 915 MHz ASK 802.15.4-2006 */
- hw->phy->supported.channels[1] |= 0x7fe;
- /* 868 MHz O-QPSK 802.15.4-2006 */
- hw->phy->supported.channels[2] |= 1;
- /* 915 MHz O-QPSK 802.15.4-2006 */
- hw->phy->supported.channels[2] |= 0x7fe;
- /* 2.4 GHz CSS 802.15.4a-2007 */
- hw->phy->supported.channels[3] |= 0x3fff;
- /* UWB Sub-gigahertz 802.15.4a-2007 */
- hw->phy->supported.channels[4] |= 1;
- /* UWB Low band 802.15.4a-2007 */
- hw->phy->supported.channels[4] |= 0x1e;
- /* UWB High band 802.15.4a-2007 */
- hw->phy->supported.channels[4] |= 0xffe0;
- /* 750 MHz O-QPSK 802.15.4c-2009 */
- hw->phy->supported.channels[5] |= 0xf;
- /* 750 MHz MPSK 802.15.4c-2009 */
- hw->phy->supported.channels[5] |= 0xf0;
- /* 950 MHz BPSK 802.15.4d-2009 */
- hw->phy->supported.channels[6] |= 0x3ff;
- /* 950 MHz GFSK 802.15.4d-2009 */
- hw->phy->supported.channels[6] |= 0x3ffc00;
- ieee802154_random_extended_addr(&hw->phy->perm_extended_addr);
- /* hwsim phy channel 13 as default */
- hw->phy->current_channel = 13;
- pib = kzalloc(sizeof(*pib), GFP_KERNEL);
- if (!pib) {
- err = -ENOMEM;
- goto err_pib;
- }
- pib->channel = 13;
- rcu_assign_pointer(phy->pib, pib);
- phy->idx = idx;
- INIT_LIST_HEAD(&phy->edges);
- hw->flags = IEEE802154_HW_PROMISCUOUS | IEEE802154_HW_RX_DROP_BAD_CKSUM;
- hw->parent = dev;
- err = ieee802154_register_hw(hw);
- if (err)
- goto err_reg;
- mutex_lock(&hwsim_phys_lock);
- if (init) {
- err = hwsim_subscribe_all_others(phy);
- if (err < 0) {
- mutex_unlock(&hwsim_phys_lock);
- goto err_subscribe;
- }
- }
- list_add_tail(&phy->list, &hwsim_phys);
- mutex_unlock(&hwsim_phys_lock);
- hwsim_mcast_new_radio(info, phy);
- return idx;
- err_subscribe:
- ieee802154_unregister_hw(phy->hw);
- err_reg:
- kfree(pib);
- err_pib:
- ieee802154_free_hw(phy->hw);
- return err;
- }
- static void hwsim_del(struct hwsim_phy *phy)
- {
- struct hwsim_pib *pib;
- struct hwsim_edge *e;
- hwsim_edge_unsubscribe_me(phy);
- list_del(&phy->list);
- rcu_read_lock();
- list_for_each_entry_rcu(e, &phy->edges, list) {
- list_del_rcu(&e->list);
- hwsim_free_edge(e);
- }
- pib = rcu_dereference(phy->pib);
- rcu_read_unlock();
- kfree_rcu(pib, rcu);
- ieee802154_unregister_hw(phy->hw);
- ieee802154_free_hw(phy->hw);
- }
- static int hwsim_probe(struct platform_device *pdev)
- {
- struct hwsim_phy *phy, *tmp;
- int err, i;
- for (i = 0; i < 2; 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");
- return 0;
- err_slave:
- mutex_lock(&hwsim_phys_lock);
- list_for_each_entry_safe(phy, tmp, &hwsim_phys, list)
- hwsim_del(phy);
- mutex_unlock(&hwsim_phys_lock);
- return err;
- }
- static int hwsim_remove(struct platform_device *pdev)
- {
- struct hwsim_phy *phy, *tmp;
- mutex_lock(&hwsim_phys_lock);
- list_for_each_entry_safe(phy, tmp, &hwsim_phys, list)
- hwsim_del(phy);
- mutex_unlock(&hwsim_phys_lock);
- return 0;
- }
- static struct platform_driver mac802154hwsim_driver = {
- .probe = hwsim_probe,
- .remove = hwsim_remove,
- .driver = {
- .name = "mac802154_hwsim",
- },
- };
- static __init int hwsim_init_module(void)
- {
- int rc;
- rc = genl_register_family(&hwsim_genl_family);
- if (rc)
- return rc;
- mac802154hwsim_dev = platform_device_register_simple("mac802154_hwsim",
- -1, NULL, 0);
- if (IS_ERR(mac802154hwsim_dev)) {
- rc = PTR_ERR(mac802154hwsim_dev);
- goto platform_dev;
- }
- rc = platform_driver_register(&mac802154hwsim_driver);
- if (rc < 0)
- goto platform_drv;
- return 0;
- platform_drv:
- platform_device_unregister(mac802154hwsim_dev);
- platform_dev:
- genl_unregister_family(&hwsim_genl_family);
- return rc;
- }
- static __exit void hwsim_remove_module(void)
- {
- genl_unregister_family(&hwsim_genl_family);
- platform_driver_unregister(&mac802154hwsim_driver);
- platform_device_unregister(mac802154hwsim_dev);
- }
- module_init(hwsim_init_module);
- module_exit(hwsim_remove_module);
|