1 // SPDX-License-Identifier: GPL-2.0-only
3 * HWSIM IEEE 802.15.4 interface
5 * (C) 2018 Mojatau, Alexander Aring <aring@mojatau.com>
6 * Copyright 2007-2012 Siemens AG
8 * Based on fakelb, original Written by:
9 * Sergey Lapin <slapin@ossfans.org>
10 * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
11 * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
14 #include <linux/module.h>
15 #include <linux/timer.h>
16 #include <linux/platform_device.h>
17 #include <linux/rtnetlink.h>
18 #include <linux/netdevice.h>
19 #include <linux/device.h>
20 #include <linux/spinlock.h>
21 #include <net/mac802154.h>
22 #include <net/cfg802154.h>
23 #include <net/genetlink.h>
24 #include "mac802154_hwsim.h"
26 MODULE_DESCRIPTION("Software simulator of IEEE 802.15.4 radio(s) for mac802154");
27 MODULE_LICENSE("GPL");
29 static LIST_HEAD(hwsim_phys
);
30 static DEFINE_MUTEX(hwsim_phys_lock
);
32 static struct platform_device
*mac802154hwsim_dev
;
34 /* MAC802154_HWSIM netlink family */
35 static struct genl_family hwsim_genl_family
;
37 static int hwsim_radio_idx
;
39 enum hwsim_multicast_groups
{
43 static const struct genl_multicast_group hwsim_mcgrps
[] = {
44 [HWSIM_MCGRP_CONFIG
] = { .name
= "config", },
54 struct hwsim_edge_info
{
61 struct hwsim_phy
*endpoint
;
62 struct hwsim_edge_info __rcu
*info
;
64 struct list_head list
;
69 struct ieee802154_hw
*hw
;
72 struct hwsim_pib __rcu
*pib
;
75 struct list_head edges
;
77 struct list_head list
;
80 static int hwsim_add_one(struct genl_info
*info
, struct device
*dev
,
82 static void hwsim_del(struct hwsim_phy
*phy
);
84 static int hwsim_hw_ed(struct ieee802154_hw
*hw
, u8
*level
)
91 static int hwsim_hw_channel(struct ieee802154_hw
*hw
, u8 page
, u8 channel
)
93 struct hwsim_phy
*phy
= hw
->priv
;
94 struct hwsim_pib
*pib
, *pib_old
;
96 pib
= kzalloc(sizeof(*pib
), GFP_KERNEL
);
101 pib
->channel
= channel
;
103 pib_old
= rtnl_dereference(phy
->pib
);
104 rcu_assign_pointer(phy
->pib
, pib
);
105 kfree_rcu(pib_old
, rcu
);
109 static int hwsim_hw_xmit(struct ieee802154_hw
*hw
, struct sk_buff
*skb
)
111 struct hwsim_phy
*current_phy
= hw
->priv
;
112 struct hwsim_pib
*current_pib
, *endpoint_pib
;
113 struct hwsim_edge_info
*einfo
;
114 struct hwsim_edge
*e
;
116 WARN_ON(current_phy
->suspended
);
119 current_pib
= rcu_dereference(current_phy
->pib
);
120 list_for_each_entry_rcu(e
, ¤t_phy
->edges
, list
) {
121 /* Can be changed later in rx_irqsafe, but this is only a
122 * performance tweak. Received radio should drop the frame
123 * in mac802154 stack anyway... so we don't need to be
124 * 100% of locking here to check on suspended
126 if (e
->endpoint
->suspended
)
129 endpoint_pib
= rcu_dereference(e
->endpoint
->pib
);
130 if (current_pib
->page
== endpoint_pib
->page
&&
131 current_pib
->channel
== endpoint_pib
->channel
) {
132 struct sk_buff
*newskb
= pskb_copy(skb
, GFP_ATOMIC
);
134 einfo
= rcu_dereference(e
->info
);
136 ieee802154_rx_irqsafe(e
->endpoint
->hw
, newskb
,
142 ieee802154_xmit_complete(hw
, skb
, false);
146 static int hwsim_hw_start(struct ieee802154_hw
*hw
)
148 struct hwsim_phy
*phy
= hw
->priv
;
150 phy
->suspended
= false;
154 static void hwsim_hw_stop(struct ieee802154_hw
*hw
)
156 struct hwsim_phy
*phy
= hw
->priv
;
158 phy
->suspended
= true;
162 hwsim_set_promiscuous_mode(struct ieee802154_hw
*hw
, const bool on
)
167 static const struct ieee802154_ops hwsim_ops
= {
168 .owner
= THIS_MODULE
,
169 .xmit_async
= hwsim_hw_xmit
,
171 .set_channel
= hwsim_hw_channel
,
172 .start
= hwsim_hw_start
,
173 .stop
= hwsim_hw_stop
,
174 .set_promiscuous_mode
= hwsim_set_promiscuous_mode
,
177 static int hwsim_new_radio_nl(struct sk_buff
*msg
, struct genl_info
*info
)
179 return hwsim_add_one(info
, &mac802154hwsim_dev
->dev
, false);
182 static int hwsim_del_radio_nl(struct sk_buff
*msg
, struct genl_info
*info
)
184 struct hwsim_phy
*phy
, *tmp
;
187 if (!info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_ID
])
190 idx
= nla_get_u32(info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_ID
]);
192 mutex_lock(&hwsim_phys_lock
);
193 list_for_each_entry_safe(phy
, tmp
, &hwsim_phys
, list
) {
194 if (idx
== phy
->idx
) {
196 mutex_unlock(&hwsim_phys_lock
);
200 mutex_unlock(&hwsim_phys_lock
);
205 static int append_radio_msg(struct sk_buff
*skb
, struct hwsim_phy
*phy
)
207 struct nlattr
*nl_edges
, *nl_edge
;
208 struct hwsim_edge_info
*einfo
;
209 struct hwsim_edge
*e
;
212 ret
= nla_put_u32(skb
, MAC802154_HWSIM_ATTR_RADIO_ID
, phy
->idx
);
217 if (list_empty(&phy
->edges
)) {
222 nl_edges
= nla_nest_start_noflag(skb
,
223 MAC802154_HWSIM_ATTR_RADIO_EDGES
);
229 list_for_each_entry_rcu(e
, &phy
->edges
, list
) {
230 nl_edge
= nla_nest_start_noflag(skb
,
231 MAC802154_HWSIM_ATTR_RADIO_EDGE
);
234 nla_nest_cancel(skb
, nl_edges
);
238 ret
= nla_put_u32(skb
, MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID
,
242 nla_nest_cancel(skb
, nl_edge
);
243 nla_nest_cancel(skb
, nl_edges
);
247 einfo
= rcu_dereference(e
->info
);
248 ret
= nla_put_u8(skb
, MAC802154_HWSIM_EDGE_ATTR_LQI
,
252 nla_nest_cancel(skb
, nl_edge
);
253 nla_nest_cancel(skb
, nl_edges
);
257 nla_nest_end(skb
, nl_edge
);
261 nla_nest_end(skb
, nl_edges
);
266 static int hwsim_get_radio(struct sk_buff
*skb
, struct hwsim_phy
*phy
,
268 struct netlink_callback
*cb
, int flags
)
273 hdr
= genlmsg_put(skb
, portid
, seq
, &hwsim_genl_family
, flags
,
274 MAC802154_HWSIM_CMD_GET_RADIO
);
279 genl_dump_check_consistent(cb
, hdr
);
281 res
= append_radio_msg(skb
, phy
);
285 genlmsg_end(skb
, hdr
);
289 genlmsg_cancel(skb
, hdr
);
293 static int hwsim_get_radio_nl(struct sk_buff
*msg
, struct genl_info
*info
)
295 struct hwsim_phy
*phy
;
297 int idx
, res
= -ENODEV
;
299 if (!info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_ID
])
301 idx
= nla_get_u32(info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_ID
]);
303 mutex_lock(&hwsim_phys_lock
);
304 list_for_each_entry(phy
, &hwsim_phys
, list
) {
308 skb
= nlmsg_new(NLMSG_DEFAULT_SIZE
, GFP_ATOMIC
);
314 res
= hwsim_get_radio(skb
, phy
, info
->snd_portid
,
315 info
->snd_seq
, NULL
, 0);
321 res
= genlmsg_reply(skb
, info
);
326 mutex_unlock(&hwsim_phys_lock
);
331 static int hwsim_dump_radio_nl(struct sk_buff
*skb
,
332 struct netlink_callback
*cb
)
334 int idx
= cb
->args
[0];
335 struct hwsim_phy
*phy
;
338 mutex_lock(&hwsim_phys_lock
);
340 if (idx
== hwsim_radio_idx
)
343 list_for_each_entry(phy
, &hwsim_phys
, list
) {
347 res
= hwsim_get_radio(skb
, phy
, NETLINK_CB(cb
->skb
).portid
,
348 cb
->nlh
->nlmsg_seq
, cb
, NLM_F_MULTI
);
358 mutex_unlock(&hwsim_phys_lock
);
362 /* caller need to held hwsim_phys_lock */
363 static struct hwsim_phy
*hwsim_get_radio_by_id(uint32_t idx
)
365 struct hwsim_phy
*phy
;
367 list_for_each_entry(phy
, &hwsim_phys
, list
) {
375 static const struct nla_policy hwsim_edge_policy
[MAC802154_HWSIM_EDGE_ATTR_MAX
+ 1] = {
376 [MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID
] = { .type
= NLA_U32
},
377 [MAC802154_HWSIM_EDGE_ATTR_LQI
] = { .type
= NLA_U8
},
380 static struct hwsim_edge
*hwsim_alloc_edge(struct hwsim_phy
*endpoint
, u8 lqi
)
382 struct hwsim_edge_info
*einfo
;
383 struct hwsim_edge
*e
;
385 e
= kzalloc(sizeof(*e
), GFP_KERNEL
);
389 einfo
= kzalloc(sizeof(*einfo
), GFP_KERNEL
);
396 rcu_assign_pointer(e
->info
, einfo
);
397 e
->endpoint
= endpoint
;
402 static void hwsim_free_edge(struct hwsim_edge
*e
)
404 struct hwsim_edge_info
*einfo
;
407 einfo
= rcu_dereference(e
->info
);
410 kfree_rcu(einfo
, rcu
);
414 static int hwsim_new_edge_nl(struct sk_buff
*msg
, struct genl_info
*info
)
416 struct nlattr
*edge_attrs
[MAC802154_HWSIM_EDGE_ATTR_MAX
+ 1];
417 struct hwsim_phy
*phy_v0
, *phy_v1
;
418 struct hwsim_edge
*e
;
421 if (!info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_ID
] &&
422 !info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_EDGE
])
425 if (nla_parse_nested_deprecated(edge_attrs
, MAC802154_HWSIM_EDGE_ATTR_MAX
, info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_EDGE
], hwsim_edge_policy
, NULL
))
428 if (!edge_attrs
[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID
])
431 v0
= nla_get_u32(info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_ID
]);
432 v1
= nla_get_u32(edge_attrs
[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID
]);
437 mutex_lock(&hwsim_phys_lock
);
438 phy_v0
= hwsim_get_radio_by_id(v0
);
440 mutex_unlock(&hwsim_phys_lock
);
444 phy_v1
= hwsim_get_radio_by_id(v1
);
446 mutex_unlock(&hwsim_phys_lock
);
451 list_for_each_entry_rcu(e
, &phy_v0
->edges
, list
) {
452 if (e
->endpoint
->idx
== v1
) {
453 mutex_unlock(&hwsim_phys_lock
);
460 e
= hwsim_alloc_edge(phy_v1
, 0xff);
462 mutex_unlock(&hwsim_phys_lock
);
465 list_add_rcu(&e
->list
, &phy_v0
->edges
);
466 /* wait until changes are done under hwsim_phys_lock lock
467 * should prevent of calling this function twice while
468 * edges list has not the changes yet.
471 mutex_unlock(&hwsim_phys_lock
);
476 static int hwsim_del_edge_nl(struct sk_buff
*msg
, struct genl_info
*info
)
478 struct nlattr
*edge_attrs
[MAC802154_HWSIM_EDGE_ATTR_MAX
+ 1];
479 struct hwsim_phy
*phy_v0
;
480 struct hwsim_edge
*e
;
483 if (!info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_ID
] &&
484 !info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_EDGE
])
487 if (nla_parse_nested_deprecated(edge_attrs
, MAC802154_HWSIM_EDGE_ATTR_MAX
, info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_EDGE
], hwsim_edge_policy
, NULL
))
490 if (!edge_attrs
[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID
])
493 v0
= nla_get_u32(info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_ID
]);
494 v1
= nla_get_u32(edge_attrs
[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID
]);
496 mutex_lock(&hwsim_phys_lock
);
497 phy_v0
= hwsim_get_radio_by_id(v0
);
499 mutex_unlock(&hwsim_phys_lock
);
504 list_for_each_entry_rcu(e
, &phy_v0
->edges
, list
) {
505 if (e
->endpoint
->idx
== v1
) {
507 list_del_rcu(&e
->list
);
509 /* same again - wait until list changes are done */
511 mutex_unlock(&hwsim_phys_lock
);
517 mutex_unlock(&hwsim_phys_lock
);
522 static int hwsim_set_edge_lqi(struct sk_buff
*msg
, struct genl_info
*info
)
524 struct nlattr
*edge_attrs
[MAC802154_HWSIM_EDGE_ATTR_MAX
+ 1];
525 struct hwsim_edge_info
*einfo
;
526 struct hwsim_phy
*phy_v0
;
527 struct hwsim_edge
*e
;
531 if (!info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_ID
] &&
532 !info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_EDGE
])
535 if (nla_parse_nested_deprecated(edge_attrs
, MAC802154_HWSIM_EDGE_ATTR_MAX
, info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_EDGE
], hwsim_edge_policy
, NULL
))
538 if (!edge_attrs
[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID
] &&
539 !edge_attrs
[MAC802154_HWSIM_EDGE_ATTR_LQI
])
542 v0
= nla_get_u32(info
->attrs
[MAC802154_HWSIM_ATTR_RADIO_ID
]);
543 v1
= nla_get_u32(edge_attrs
[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID
]);
544 lqi
= nla_get_u8(edge_attrs
[MAC802154_HWSIM_EDGE_ATTR_LQI
]);
546 mutex_lock(&hwsim_phys_lock
);
547 phy_v0
= hwsim_get_radio_by_id(v0
);
549 mutex_unlock(&hwsim_phys_lock
);
553 einfo
= kzalloc(sizeof(*einfo
), GFP_KERNEL
);
555 mutex_unlock(&hwsim_phys_lock
);
560 list_for_each_entry_rcu(e
, &phy_v0
->edges
, list
) {
561 if (e
->endpoint
->idx
== v1
) {
563 rcu_assign_pointer(e
->info
, einfo
);
565 mutex_unlock(&hwsim_phys_lock
);
572 mutex_unlock(&hwsim_phys_lock
);
577 /* MAC802154_HWSIM netlink policy */
579 static const struct nla_policy hwsim_genl_policy
[MAC802154_HWSIM_ATTR_MAX
+ 1] = {
580 [MAC802154_HWSIM_ATTR_RADIO_ID
] = { .type
= NLA_U32
},
581 [MAC802154_HWSIM_ATTR_RADIO_EDGE
] = { .type
= NLA_NESTED
},
582 [MAC802154_HWSIM_ATTR_RADIO_EDGES
] = { .type
= NLA_NESTED
},
585 /* Generic Netlink operations array */
586 static const struct genl_ops hwsim_nl_ops
[] = {
588 .cmd
= MAC802154_HWSIM_CMD_NEW_RADIO
,
589 .validate
= GENL_DONT_VALIDATE_STRICT
| GENL_DONT_VALIDATE_DUMP
,
590 .doit
= hwsim_new_radio_nl
,
591 .flags
= GENL_UNS_ADMIN_PERM
,
594 .cmd
= MAC802154_HWSIM_CMD_DEL_RADIO
,
595 .validate
= GENL_DONT_VALIDATE_STRICT
| GENL_DONT_VALIDATE_DUMP
,
596 .doit
= hwsim_del_radio_nl
,
597 .flags
= GENL_UNS_ADMIN_PERM
,
600 .cmd
= MAC802154_HWSIM_CMD_GET_RADIO
,
601 .validate
= GENL_DONT_VALIDATE_STRICT
| GENL_DONT_VALIDATE_DUMP
,
602 .doit
= hwsim_get_radio_nl
,
603 .dumpit
= hwsim_dump_radio_nl
,
606 .cmd
= MAC802154_HWSIM_CMD_NEW_EDGE
,
607 .validate
= GENL_DONT_VALIDATE_STRICT
| GENL_DONT_VALIDATE_DUMP
,
608 .doit
= hwsim_new_edge_nl
,
609 .flags
= GENL_UNS_ADMIN_PERM
,
612 .cmd
= MAC802154_HWSIM_CMD_DEL_EDGE
,
613 .validate
= GENL_DONT_VALIDATE_STRICT
| GENL_DONT_VALIDATE_DUMP
,
614 .doit
= hwsim_del_edge_nl
,
615 .flags
= GENL_UNS_ADMIN_PERM
,
618 .cmd
= MAC802154_HWSIM_CMD_SET_EDGE
,
619 .validate
= GENL_DONT_VALIDATE_STRICT
| GENL_DONT_VALIDATE_DUMP
,
620 .doit
= hwsim_set_edge_lqi
,
621 .flags
= GENL_UNS_ADMIN_PERM
,
625 static struct genl_family hwsim_genl_family __ro_after_init
= {
626 .name
= "MAC802154_HWSIM",
628 .maxattr
= MAC802154_HWSIM_ATTR_MAX
,
629 .policy
= hwsim_genl_policy
,
630 .module
= THIS_MODULE
,
632 .n_ops
= ARRAY_SIZE(hwsim_nl_ops
),
633 .mcgrps
= hwsim_mcgrps
,
634 .n_mcgrps
= ARRAY_SIZE(hwsim_mcgrps
),
637 static void hwsim_mcast_config_msg(struct sk_buff
*mcast_skb
,
638 struct genl_info
*info
)
641 genl_notify(&hwsim_genl_family
, mcast_skb
, info
,
642 HWSIM_MCGRP_CONFIG
, GFP_KERNEL
);
644 genlmsg_multicast(&hwsim_genl_family
, mcast_skb
, 0,
645 HWSIM_MCGRP_CONFIG
, GFP_KERNEL
);
648 static void hwsim_mcast_new_radio(struct genl_info
*info
, struct hwsim_phy
*phy
)
650 struct sk_buff
*mcast_skb
;
653 mcast_skb
= genlmsg_new(GENLMSG_DEFAULT_SIZE
, GFP_KERNEL
);
657 data
= genlmsg_put(mcast_skb
, 0, 0, &hwsim_genl_family
, 0,
658 MAC802154_HWSIM_CMD_NEW_RADIO
);
662 if (append_radio_msg(mcast_skb
, phy
) < 0)
665 genlmsg_end(mcast_skb
, data
);
667 hwsim_mcast_config_msg(mcast_skb
, info
);
671 genlmsg_cancel(mcast_skb
, data
);
672 nlmsg_free(mcast_skb
);
675 static void hwsim_edge_unsubscribe_me(struct hwsim_phy
*phy
)
677 struct hwsim_phy
*tmp
;
678 struct hwsim_edge
*e
;
681 /* going to all phy edges and remove phy from it */
682 list_for_each_entry(tmp
, &hwsim_phys
, list
) {
683 list_for_each_entry_rcu(e
, &tmp
->edges
, list
) {
684 if (e
->endpoint
->idx
== phy
->idx
) {
685 list_del_rcu(&e
->list
);
695 static int hwsim_subscribe_all_others(struct hwsim_phy
*phy
)
697 struct hwsim_phy
*sub
;
698 struct hwsim_edge
*e
;
700 list_for_each_entry(sub
, &hwsim_phys
, list
) {
701 e
= hwsim_alloc_edge(sub
, 0xff);
705 list_add_rcu(&e
->list
, &phy
->edges
);
708 list_for_each_entry(sub
, &hwsim_phys
, list
) {
709 e
= hwsim_alloc_edge(phy
, 0xff);
713 list_add_rcu(&e
->list
, &sub
->edges
);
720 list_for_each_entry_rcu(e
, &phy
->edges
, list
) {
721 list_del_rcu(&e
->list
);
726 hwsim_edge_unsubscribe_me(phy
);
730 static int hwsim_add_one(struct genl_info
*info
, struct device
*dev
,
733 struct ieee802154_hw
*hw
;
734 struct hwsim_phy
*phy
;
735 struct hwsim_pib
*pib
;
739 idx
= hwsim_radio_idx
++;
741 hw
= ieee802154_alloc_hw(sizeof(*phy
), &hwsim_ops
);
748 /* 868 MHz BPSK 802.15.4-2003 */
749 hw
->phy
->supported
.channels
[0] |= 1;
750 /* 915 MHz BPSK 802.15.4-2003 */
751 hw
->phy
->supported
.channels
[0] |= 0x7fe;
752 /* 2.4 GHz O-QPSK 802.15.4-2003 */
753 hw
->phy
->supported
.channels
[0] |= 0x7FFF800;
754 /* 868 MHz ASK 802.15.4-2006 */
755 hw
->phy
->supported
.channels
[1] |= 1;
756 /* 915 MHz ASK 802.15.4-2006 */
757 hw
->phy
->supported
.channels
[1] |= 0x7fe;
758 /* 868 MHz O-QPSK 802.15.4-2006 */
759 hw
->phy
->supported
.channels
[2] |= 1;
760 /* 915 MHz O-QPSK 802.15.4-2006 */
761 hw
->phy
->supported
.channels
[2] |= 0x7fe;
762 /* 2.4 GHz CSS 802.15.4a-2007 */
763 hw
->phy
->supported
.channels
[3] |= 0x3fff;
764 /* UWB Sub-gigahertz 802.15.4a-2007 */
765 hw
->phy
->supported
.channels
[4] |= 1;
766 /* UWB Low band 802.15.4a-2007 */
767 hw
->phy
->supported
.channels
[4] |= 0x1e;
768 /* UWB High band 802.15.4a-2007 */
769 hw
->phy
->supported
.channels
[4] |= 0xffe0;
770 /* 750 MHz O-QPSK 802.15.4c-2009 */
771 hw
->phy
->supported
.channels
[5] |= 0xf;
772 /* 750 MHz MPSK 802.15.4c-2009 */
773 hw
->phy
->supported
.channels
[5] |= 0xf0;
774 /* 950 MHz BPSK 802.15.4d-2009 */
775 hw
->phy
->supported
.channels
[6] |= 0x3ff;
776 /* 950 MHz GFSK 802.15.4d-2009 */
777 hw
->phy
->supported
.channels
[6] |= 0x3ffc00;
779 ieee802154_random_extended_addr(&hw
->phy
->perm_extended_addr
);
781 /* hwsim phy channel 13 as default */
782 hw
->phy
->current_channel
= 13;
783 pib
= kzalloc(sizeof(*pib
), GFP_KERNEL
);
789 rcu_assign_pointer(phy
->pib
, pib
);
791 INIT_LIST_HEAD(&phy
->edges
);
793 hw
->flags
= IEEE802154_HW_PROMISCUOUS
;
796 err
= ieee802154_register_hw(hw
);
800 mutex_lock(&hwsim_phys_lock
);
802 err
= hwsim_subscribe_all_others(phy
);
804 mutex_unlock(&hwsim_phys_lock
);
808 list_add_tail(&phy
->list
, &hwsim_phys
);
809 mutex_unlock(&hwsim_phys_lock
);
811 hwsim_mcast_new_radio(info
, phy
);
816 ieee802154_unregister_hw(phy
->hw
);
820 ieee802154_free_hw(phy
->hw
);
824 static void hwsim_del(struct hwsim_phy
*phy
)
826 struct hwsim_pib
*pib
;
828 hwsim_edge_unsubscribe_me(phy
);
830 list_del(&phy
->list
);
833 pib
= rcu_dereference(phy
->pib
);
838 ieee802154_unregister_hw(phy
->hw
);
839 ieee802154_free_hw(phy
->hw
);
842 static int hwsim_probe(struct platform_device
*pdev
)
844 struct hwsim_phy
*phy
, *tmp
;
847 for (i
= 0; i
< 2; i
++) {
848 err
= hwsim_add_one(NULL
, &pdev
->dev
, true);
853 dev_info(&pdev
->dev
, "Added 2 mac802154 hwsim hardware radios\n");
857 mutex_lock(&hwsim_phys_lock
);
858 list_for_each_entry_safe(phy
, tmp
, &hwsim_phys
, list
)
860 mutex_unlock(&hwsim_phys_lock
);
864 static int hwsim_remove(struct platform_device
*pdev
)
866 struct hwsim_phy
*phy
, *tmp
;
868 mutex_lock(&hwsim_phys_lock
);
869 list_for_each_entry_safe(phy
, tmp
, &hwsim_phys
, list
)
871 mutex_unlock(&hwsim_phys_lock
);
876 static struct platform_driver mac802154hwsim_driver
= {
877 .probe
= hwsim_probe
,
878 .remove
= hwsim_remove
,
880 .name
= "mac802154_hwsim",
884 static __init
int hwsim_init_module(void)
888 rc
= genl_register_family(&hwsim_genl_family
);
892 mac802154hwsim_dev
= platform_device_register_simple("mac802154_hwsim",
894 if (IS_ERR(mac802154hwsim_dev
)) {
895 rc
= PTR_ERR(mac802154hwsim_dev
);
899 rc
= platform_driver_register(&mac802154hwsim_driver
);
906 platform_device_unregister(mac802154hwsim_dev
);
908 genl_unregister_family(&hwsim_genl_family
);
912 static __exit
void hwsim_remove_module(void)
914 genl_unregister_family(&hwsim_genl_family
);
915 platform_driver_unregister(&mac802154hwsim_driver
);
916 platform_device_unregister(mac802154hwsim_dev
);
919 module_init(hwsim_init_module
);
920 module_exit(hwsim_remove_module
);