11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f25da51fSAlexander Aring /*
3f25da51fSAlexander Aring * HWSIM IEEE 802.15.4 interface
4f25da51fSAlexander Aring *
5f25da51fSAlexander Aring * (C) 2018 Mojatau, Alexander Aring <aring@mojatau.com>
6f25da51fSAlexander Aring * Copyright 2007-2012 Siemens AG
7f25da51fSAlexander Aring *
8f25da51fSAlexander Aring * Based on fakelb, original Written by:
9f25da51fSAlexander Aring * Sergey Lapin <slapin@ossfans.org>
10f25da51fSAlexander Aring * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
11f25da51fSAlexander Aring * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
12f25da51fSAlexander Aring */
13f25da51fSAlexander Aring
14f25da51fSAlexander Aring #include <linux/module.h>
15f25da51fSAlexander Aring #include <linux/timer.h>
16f25da51fSAlexander Aring #include <linux/platform_device.h>
171c9f4a3fSAlexander Aring #include <linux/rtnetlink.h>
18f25da51fSAlexander Aring #include <linux/netdevice.h>
19f25da51fSAlexander Aring #include <linux/device.h>
20f25da51fSAlexander Aring #include <linux/spinlock.h>
21ea562d8cSMiquel Raynal #include <net/ieee802154_netdev.h>
22f25da51fSAlexander Aring #include <net/mac802154.h>
23f25da51fSAlexander Aring #include <net/cfg802154.h>
24f25da51fSAlexander Aring #include <net/genetlink.h>
25f25da51fSAlexander Aring #include "mac802154_hwsim.h"
26f25da51fSAlexander Aring
27f25da51fSAlexander Aring MODULE_DESCRIPTION("Software simulator of IEEE 802.15.4 radio(s) for mac802154");
28f25da51fSAlexander Aring MODULE_LICENSE("GPL");
29f25da51fSAlexander Aring
30f25da51fSAlexander Aring static LIST_HEAD(hwsim_phys);
31f25da51fSAlexander Aring static DEFINE_MUTEX(hwsim_phys_lock);
32f25da51fSAlexander Aring
33f25da51fSAlexander Aring static struct platform_device *mac802154hwsim_dev;
34f25da51fSAlexander Aring
35f25da51fSAlexander Aring /* MAC802154_HWSIM netlink family */
36f25da51fSAlexander Aring static struct genl_family hwsim_genl_family;
37f25da51fSAlexander Aring
38f25da51fSAlexander Aring static int hwsim_radio_idx;
39f25da51fSAlexander Aring
40f25da51fSAlexander Aring enum hwsim_multicast_groups {
41f25da51fSAlexander Aring HWSIM_MCGRP_CONFIG,
42f25da51fSAlexander Aring };
43f25da51fSAlexander Aring
44f25da51fSAlexander Aring static const struct genl_multicast_group hwsim_mcgrps[] = {
45f25da51fSAlexander Aring [HWSIM_MCGRP_CONFIG] = { .name = "config", },
46f25da51fSAlexander Aring };
47f25da51fSAlexander Aring
48f25da51fSAlexander Aring struct hwsim_pib {
49f25da51fSAlexander Aring u8 page;
50f25da51fSAlexander Aring u8 channel;
51a87815b7SMiquel Raynal struct ieee802154_hw_addr_filt filt;
5219177eedSMiquel Raynal enum ieee802154_filtering_level filt_level;
53f25da51fSAlexander Aring
54f25da51fSAlexander Aring struct rcu_head rcu;
55f25da51fSAlexander Aring };
56f25da51fSAlexander Aring
57f25da51fSAlexander Aring struct hwsim_edge_info {
58f25da51fSAlexander Aring u8 lqi;
59f25da51fSAlexander Aring
60f25da51fSAlexander Aring struct rcu_head rcu;
61f25da51fSAlexander Aring };
62f25da51fSAlexander Aring
63f25da51fSAlexander Aring struct hwsim_edge {
64f25da51fSAlexander Aring struct hwsim_phy *endpoint;
65c5d99d2bSAlexander Aring struct hwsim_edge_info __rcu *info;
66f25da51fSAlexander Aring
67f25da51fSAlexander Aring struct list_head list;
68f25da51fSAlexander Aring struct rcu_head rcu;
69f25da51fSAlexander Aring };
70f25da51fSAlexander Aring
71f25da51fSAlexander Aring struct hwsim_phy {
72f25da51fSAlexander Aring struct ieee802154_hw *hw;
73f25da51fSAlexander Aring u32 idx;
74f25da51fSAlexander Aring
75f25da51fSAlexander Aring struct hwsim_pib __rcu *pib;
76f25da51fSAlexander Aring
77f25da51fSAlexander Aring bool suspended;
78c5d99d2bSAlexander Aring struct list_head edges;
79f25da51fSAlexander Aring
80f25da51fSAlexander Aring struct list_head list;
81f25da51fSAlexander Aring };
82f25da51fSAlexander Aring
83f25da51fSAlexander Aring static int hwsim_add_one(struct genl_info *info, struct device *dev,
84f25da51fSAlexander Aring bool init);
85f25da51fSAlexander Aring static void hwsim_del(struct hwsim_phy *phy);
86f25da51fSAlexander Aring
hwsim_hw_ed(struct ieee802154_hw * hw,u8 * level)87f25da51fSAlexander Aring static int hwsim_hw_ed(struct ieee802154_hw *hw, u8 *level)
88f25da51fSAlexander Aring {
89f25da51fSAlexander Aring *level = 0xbe;
90f25da51fSAlexander Aring
91f25da51fSAlexander Aring return 0;
92f25da51fSAlexander Aring }
93f25da51fSAlexander Aring
hwsim_update_pib(struct ieee802154_hw * hw,u8 page,u8 channel,struct ieee802154_hw_addr_filt * filt,enum ieee802154_filtering_level filt_level)949a60850eSMiquel Raynal static int hwsim_update_pib(struct ieee802154_hw *hw, u8 page, u8 channel,
9519177eedSMiquel Raynal struct ieee802154_hw_addr_filt *filt,
9619177eedSMiquel Raynal enum ieee802154_filtering_level filt_level)
97f25da51fSAlexander Aring {
98f25da51fSAlexander Aring struct hwsim_phy *phy = hw->priv;
99f25da51fSAlexander Aring struct hwsim_pib *pib, *pib_old;
100f25da51fSAlexander Aring
1019a60850eSMiquel Raynal pib = kzalloc(sizeof(*pib), GFP_ATOMIC);
102f25da51fSAlexander Aring if (!pib)
103f25da51fSAlexander Aring return -ENOMEM;
104f25da51fSAlexander Aring
1059a60850eSMiquel Raynal pib_old = rtnl_dereference(phy->pib);
1069a60850eSMiquel Raynal
107f25da51fSAlexander Aring pib->page = page;
108f25da51fSAlexander Aring pib->channel = channel;
109a87815b7SMiquel Raynal pib->filt.short_addr = filt->short_addr;
110a87815b7SMiquel Raynal pib->filt.pan_id = filt->pan_id;
111a87815b7SMiquel Raynal pib->filt.ieee_addr = filt->ieee_addr;
112a87815b7SMiquel Raynal pib->filt.pan_coord = filt->pan_coord;
11319177eedSMiquel Raynal pib->filt_level = filt_level;
114f25da51fSAlexander Aring
115f25da51fSAlexander Aring rcu_assign_pointer(phy->pib, pib);
116f25da51fSAlexander Aring kfree_rcu(pib_old, rcu);
117f25da51fSAlexander Aring return 0;
118f25da51fSAlexander Aring }
119f25da51fSAlexander Aring
hwsim_hw_channel(struct ieee802154_hw * hw,u8 page,u8 channel)1209a60850eSMiquel Raynal static int hwsim_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
1219a60850eSMiquel Raynal {
1229a60850eSMiquel Raynal struct hwsim_phy *phy = hw->priv;
1239a60850eSMiquel Raynal struct hwsim_pib *pib;
1249a60850eSMiquel Raynal int ret;
1259a60850eSMiquel Raynal
1269a60850eSMiquel Raynal rcu_read_lock();
1279a60850eSMiquel Raynal pib = rcu_dereference(phy->pib);
12819177eedSMiquel Raynal ret = hwsim_update_pib(hw, page, channel, &pib->filt, pib->filt_level);
1299a60850eSMiquel Raynal rcu_read_unlock();
1309a60850eSMiquel Raynal
1319a60850eSMiquel Raynal return ret;
1329a60850eSMiquel Raynal }
1339a60850eSMiquel Raynal
hwsim_hw_addr_filt(struct ieee802154_hw * hw,struct ieee802154_hw_addr_filt * filt,unsigned long changed)1349a60850eSMiquel Raynal static int hwsim_hw_addr_filt(struct ieee802154_hw *hw,
1359a60850eSMiquel Raynal struct ieee802154_hw_addr_filt *filt,
1369a60850eSMiquel Raynal unsigned long changed)
1379a60850eSMiquel Raynal {
1389a60850eSMiquel Raynal struct hwsim_phy *phy = hw->priv;
1399a60850eSMiquel Raynal struct hwsim_pib *pib;
1409a60850eSMiquel Raynal int ret;
1419a60850eSMiquel Raynal
1429a60850eSMiquel Raynal rcu_read_lock();
1439a60850eSMiquel Raynal pib = rcu_dereference(phy->pib);
14419177eedSMiquel Raynal ret = hwsim_update_pib(hw, pib->page, pib->channel, filt, pib->filt_level);
1459a60850eSMiquel Raynal rcu_read_unlock();
1469a60850eSMiquel Raynal
1479a60850eSMiquel Raynal return ret;
1489a60850eSMiquel Raynal }
1499a60850eSMiquel Raynal
hwsim_hw_receive(struct ieee802154_hw * hw,struct sk_buff * skb,u8 lqi)150ea562d8cSMiquel Raynal static void hwsim_hw_receive(struct ieee802154_hw *hw, struct sk_buff *skb,
151ea562d8cSMiquel Raynal u8 lqi)
152ea562d8cSMiquel Raynal {
153ea562d8cSMiquel Raynal struct ieee802154_hdr hdr;
154ea562d8cSMiquel Raynal struct hwsim_phy *phy = hw->priv;
155ea562d8cSMiquel Raynal struct hwsim_pib *pib;
156ea562d8cSMiquel Raynal
157ea562d8cSMiquel Raynal rcu_read_lock();
158ea562d8cSMiquel Raynal pib = rcu_dereference(phy->pib);
159ea562d8cSMiquel Raynal
160ea562d8cSMiquel Raynal if (!pskb_may_pull(skb, 3)) {
161ea562d8cSMiquel Raynal dev_dbg(hw->parent, "invalid frame\n");
162ea562d8cSMiquel Raynal goto drop;
163ea562d8cSMiquel Raynal }
164ea562d8cSMiquel Raynal
165ea562d8cSMiquel Raynal memcpy(&hdr, skb->data, 3);
166ea562d8cSMiquel Raynal
167ea562d8cSMiquel Raynal /* Level 4 filtering: Frame fields validity */
16819177eedSMiquel Raynal if (pib->filt_level == IEEE802154_FILTERING_4_FRAME_FIELDS) {
169ea562d8cSMiquel Raynal /* a) Drop reserved frame types */
170ea562d8cSMiquel Raynal switch (mac_cb(skb)->type) {
171ea562d8cSMiquel Raynal case IEEE802154_FC_TYPE_BEACON:
172ea562d8cSMiquel Raynal case IEEE802154_FC_TYPE_DATA:
173ea562d8cSMiquel Raynal case IEEE802154_FC_TYPE_ACK:
174ea562d8cSMiquel Raynal case IEEE802154_FC_TYPE_MAC_CMD:
175ea562d8cSMiquel Raynal break;
176ea562d8cSMiquel Raynal default:
177ea562d8cSMiquel Raynal dev_dbg(hw->parent, "unrecognized frame type 0x%x\n",
178ea562d8cSMiquel Raynal mac_cb(skb)->type);
179ea562d8cSMiquel Raynal goto drop;
180ea562d8cSMiquel Raynal }
181ea562d8cSMiquel Raynal
182ea562d8cSMiquel Raynal /* b) Drop reserved frame versions */
183ea562d8cSMiquel Raynal switch (hdr.fc.version) {
184ea562d8cSMiquel Raynal case IEEE802154_2003_STD:
185ea562d8cSMiquel Raynal case IEEE802154_2006_STD:
186ea562d8cSMiquel Raynal case IEEE802154_STD:
187ea562d8cSMiquel Raynal break;
188ea562d8cSMiquel Raynal default:
189ea562d8cSMiquel Raynal dev_dbg(hw->parent,
190ea562d8cSMiquel Raynal "unrecognized frame version 0x%x\n",
191ea562d8cSMiquel Raynal hdr.fc.version);
192ea562d8cSMiquel Raynal goto drop;
193ea562d8cSMiquel Raynal }
194ea562d8cSMiquel Raynal
195ea562d8cSMiquel Raynal /* c) PAN ID constraints */
196ea562d8cSMiquel Raynal if ((mac_cb(skb)->dest.mode == IEEE802154_ADDR_LONG ||
197ea562d8cSMiquel Raynal mac_cb(skb)->dest.mode == IEEE802154_ADDR_SHORT) &&
198ea562d8cSMiquel Raynal mac_cb(skb)->dest.pan_id != pib->filt.pan_id &&
199ea562d8cSMiquel Raynal mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
200ea562d8cSMiquel Raynal dev_dbg(hw->parent,
201ea562d8cSMiquel Raynal "unrecognized PAN ID %04x\n",
202ea562d8cSMiquel Raynal le16_to_cpu(mac_cb(skb)->dest.pan_id));
203ea562d8cSMiquel Raynal goto drop;
204ea562d8cSMiquel Raynal }
205ea562d8cSMiquel Raynal
206ea562d8cSMiquel Raynal /* d1) Short address constraints */
207ea562d8cSMiquel Raynal if (mac_cb(skb)->dest.mode == IEEE802154_ADDR_SHORT &&
208ea562d8cSMiquel Raynal mac_cb(skb)->dest.short_addr != pib->filt.short_addr &&
209ea562d8cSMiquel Raynal mac_cb(skb)->dest.short_addr != cpu_to_le16(IEEE802154_ADDR_BROADCAST)) {
210ea562d8cSMiquel Raynal dev_dbg(hw->parent,
211ea562d8cSMiquel Raynal "unrecognized short address %04x\n",
212ea562d8cSMiquel Raynal le16_to_cpu(mac_cb(skb)->dest.short_addr));
213ea562d8cSMiquel Raynal goto drop;
214ea562d8cSMiquel Raynal }
215ea562d8cSMiquel Raynal
216ea562d8cSMiquel Raynal /* d2) Extended address constraints */
217ea562d8cSMiquel Raynal if (mac_cb(skb)->dest.mode == IEEE802154_ADDR_LONG &&
218ea562d8cSMiquel Raynal mac_cb(skb)->dest.extended_addr != pib->filt.ieee_addr) {
219ea562d8cSMiquel Raynal dev_dbg(hw->parent,
220ea562d8cSMiquel Raynal "unrecognized long address 0x%016llx\n",
221ea562d8cSMiquel Raynal mac_cb(skb)->dest.extended_addr);
222ea562d8cSMiquel Raynal goto drop;
223ea562d8cSMiquel Raynal }
224ea562d8cSMiquel Raynal
225ea562d8cSMiquel Raynal /* d4) Specific PAN coordinator case (no parent) */
226ea562d8cSMiquel Raynal if ((mac_cb(skb)->type == IEEE802154_FC_TYPE_DATA ||
227ea562d8cSMiquel Raynal mac_cb(skb)->type == IEEE802154_FC_TYPE_MAC_CMD) &&
228ea562d8cSMiquel Raynal mac_cb(skb)->dest.mode == IEEE802154_ADDR_NONE) {
229ea562d8cSMiquel Raynal dev_dbg(hw->parent,
230ea562d8cSMiquel Raynal "relaying is not supported\n");
231ea562d8cSMiquel Raynal goto drop;
232ea562d8cSMiquel Raynal }
233ea562d8cSMiquel Raynal
234ea562d8cSMiquel Raynal /* e) Beacon frames follow specific PAN ID rules */
235ea562d8cSMiquel Raynal if (mac_cb(skb)->type == IEEE802154_FC_TYPE_BEACON &&
236ea562d8cSMiquel Raynal pib->filt.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST) &&
237ea562d8cSMiquel Raynal mac_cb(skb)->dest.pan_id != pib->filt.pan_id) {
238ea562d8cSMiquel Raynal dev_dbg(hw->parent,
239ea562d8cSMiquel Raynal "invalid beacon PAN ID %04x\n",
240ea562d8cSMiquel Raynal le16_to_cpu(mac_cb(skb)->dest.pan_id));
241ea562d8cSMiquel Raynal goto drop;
242ea562d8cSMiquel Raynal }
243ea562d8cSMiquel Raynal }
244ea562d8cSMiquel Raynal
245ea562d8cSMiquel Raynal rcu_read_unlock();
246ea562d8cSMiquel Raynal
247ea562d8cSMiquel Raynal ieee802154_rx_irqsafe(hw, skb, lqi);
248ea562d8cSMiquel Raynal
249ea562d8cSMiquel Raynal return;
250ea562d8cSMiquel Raynal
251ea562d8cSMiquel Raynal drop:
252ea562d8cSMiquel Raynal rcu_read_unlock();
253ea562d8cSMiquel Raynal kfree_skb(skb);
254ea562d8cSMiquel Raynal }
255ea562d8cSMiquel Raynal
hwsim_hw_xmit(struct ieee802154_hw * hw,struct sk_buff * skb)256f25da51fSAlexander Aring static int hwsim_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
257f25da51fSAlexander Aring {
258f25da51fSAlexander Aring struct hwsim_phy *current_phy = hw->priv;
259f25da51fSAlexander Aring struct hwsim_pib *current_pib, *endpoint_pib;
260f25da51fSAlexander Aring struct hwsim_edge_info *einfo;
261f25da51fSAlexander Aring struct hwsim_edge *e;
262f25da51fSAlexander Aring
263f25da51fSAlexander Aring WARN_ON(current_phy->suspended);
264f25da51fSAlexander Aring
265f25da51fSAlexander Aring rcu_read_lock();
266f25da51fSAlexander Aring current_pib = rcu_dereference(current_phy->pib);
267f25da51fSAlexander Aring list_for_each_entry_rcu(e, ¤t_phy->edges, list) {
268f25da51fSAlexander Aring /* Can be changed later in rx_irqsafe, but this is only a
269f25da51fSAlexander Aring * performance tweak. Received radio should drop the frame
270f25da51fSAlexander Aring * in mac802154 stack anyway... so we don't need to be
271f25da51fSAlexander Aring * 100% of locking here to check on suspended
272f25da51fSAlexander Aring */
273f25da51fSAlexander Aring if (e->endpoint->suspended)
274f25da51fSAlexander Aring continue;
275f25da51fSAlexander Aring
276f25da51fSAlexander Aring endpoint_pib = rcu_dereference(e->endpoint->pib);
277f25da51fSAlexander Aring if (current_pib->page == endpoint_pib->page &&
278f25da51fSAlexander Aring current_pib->channel == endpoint_pib->channel) {
279f25da51fSAlexander Aring struct sk_buff *newskb = pskb_copy(skb, GFP_ATOMIC);
280f25da51fSAlexander Aring
281f25da51fSAlexander Aring einfo = rcu_dereference(e->info);
282f25da51fSAlexander Aring if (newskb)
283ea562d8cSMiquel Raynal hwsim_hw_receive(e->endpoint->hw, newskb, einfo->lqi);
284f25da51fSAlexander Aring }
285f25da51fSAlexander Aring }
286f25da51fSAlexander Aring rcu_read_unlock();
287f25da51fSAlexander Aring
288f25da51fSAlexander Aring ieee802154_xmit_complete(hw, skb, false);
289f25da51fSAlexander Aring return 0;
290f25da51fSAlexander Aring }
291f25da51fSAlexander Aring
hwsim_hw_start(struct ieee802154_hw * hw)292f25da51fSAlexander Aring static int hwsim_hw_start(struct ieee802154_hw *hw)
293f25da51fSAlexander Aring {
294f25da51fSAlexander Aring struct hwsim_phy *phy = hw->priv;
295f25da51fSAlexander Aring
296f25da51fSAlexander Aring phy->suspended = false;
297a4b5b4c5SMiquel Raynal
298f25da51fSAlexander Aring return 0;
299f25da51fSAlexander Aring }
300f25da51fSAlexander Aring
hwsim_hw_stop(struct ieee802154_hw * hw)301f25da51fSAlexander Aring static void hwsim_hw_stop(struct ieee802154_hw *hw)
302f25da51fSAlexander Aring {
303f25da51fSAlexander Aring struct hwsim_phy *phy = hw->priv;
304f25da51fSAlexander Aring
305f25da51fSAlexander Aring phy->suspended = true;
306f25da51fSAlexander Aring }
307f25da51fSAlexander Aring
308f25da51fSAlexander Aring static int
hwsim_set_promiscuous_mode(struct ieee802154_hw * hw,const bool on)309f25da51fSAlexander Aring hwsim_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on)
310f25da51fSAlexander Aring {
31119177eedSMiquel Raynal enum ieee802154_filtering_level filt_level;
31219177eedSMiquel Raynal struct hwsim_phy *phy = hw->priv;
31319177eedSMiquel Raynal struct hwsim_pib *pib;
31419177eedSMiquel Raynal int ret;
31519177eedSMiquel Raynal
31619177eedSMiquel Raynal if (on)
31719177eedSMiquel Raynal filt_level = IEEE802154_FILTERING_NONE;
31819177eedSMiquel Raynal else
31919177eedSMiquel Raynal filt_level = IEEE802154_FILTERING_4_FRAME_FIELDS;
32019177eedSMiquel Raynal
32119177eedSMiquel Raynal rcu_read_lock();
32219177eedSMiquel Raynal pib = rcu_dereference(phy->pib);
32319177eedSMiquel Raynal ret = hwsim_update_pib(hw, pib->page, pib->channel, &pib->filt, filt_level);
32419177eedSMiquel Raynal rcu_read_unlock();
32519177eedSMiquel Raynal
32619177eedSMiquel Raynal return ret;
327f25da51fSAlexander Aring }
328f25da51fSAlexander Aring
329f25da51fSAlexander Aring static const struct ieee802154_ops hwsim_ops = {
330f25da51fSAlexander Aring .owner = THIS_MODULE,
331f25da51fSAlexander Aring .xmit_async = hwsim_hw_xmit,
332f25da51fSAlexander Aring .ed = hwsim_hw_ed,
333f25da51fSAlexander Aring .set_channel = hwsim_hw_channel,
334f25da51fSAlexander Aring .start = hwsim_hw_start,
335f25da51fSAlexander Aring .stop = hwsim_hw_stop,
336f25da51fSAlexander Aring .set_promiscuous_mode = hwsim_set_promiscuous_mode,
337a87815b7SMiquel Raynal .set_hw_addr_filt = hwsim_hw_addr_filt,
338f25da51fSAlexander Aring };
339f25da51fSAlexander Aring
hwsim_new_radio_nl(struct sk_buff * msg,struct genl_info * info)340f25da51fSAlexander Aring static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
341f25da51fSAlexander Aring {
342f25da51fSAlexander Aring return hwsim_add_one(info, &mac802154hwsim_dev->dev, false);
343f25da51fSAlexander Aring }
344f25da51fSAlexander Aring
hwsim_del_radio_nl(struct sk_buff * msg,struct genl_info * info)345f25da51fSAlexander Aring static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
346f25da51fSAlexander Aring {
347f25da51fSAlexander Aring struct hwsim_phy *phy, *tmp;
348f25da51fSAlexander Aring s64 idx = -1;
349f25da51fSAlexander Aring
350f25da51fSAlexander Aring if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID])
351f25da51fSAlexander Aring return -EINVAL;
352f25da51fSAlexander Aring
353f25da51fSAlexander Aring idx = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
354f25da51fSAlexander Aring
355f25da51fSAlexander Aring mutex_lock(&hwsim_phys_lock);
356f25da51fSAlexander Aring list_for_each_entry_safe(phy, tmp, &hwsim_phys, list) {
357f25da51fSAlexander Aring if (idx == phy->idx) {
358f25da51fSAlexander Aring hwsim_del(phy);
359f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
360f25da51fSAlexander Aring return 0;
361f25da51fSAlexander Aring }
362f25da51fSAlexander Aring }
363f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
364f25da51fSAlexander Aring
365f25da51fSAlexander Aring return -ENODEV;
366f25da51fSAlexander Aring }
367f25da51fSAlexander Aring
append_radio_msg(struct sk_buff * skb,struct hwsim_phy * phy)368f25da51fSAlexander Aring static int append_radio_msg(struct sk_buff *skb, struct hwsim_phy *phy)
369f25da51fSAlexander Aring {
370f25da51fSAlexander Aring struct nlattr *nl_edges, *nl_edge;
371f25da51fSAlexander Aring struct hwsim_edge_info *einfo;
372f25da51fSAlexander Aring struct hwsim_edge *e;
373f25da51fSAlexander Aring int ret;
374f25da51fSAlexander Aring
375f25da51fSAlexander Aring ret = nla_put_u32(skb, MAC802154_HWSIM_ATTR_RADIO_ID, phy->idx);
376f25da51fSAlexander Aring if (ret < 0)
377f25da51fSAlexander Aring return ret;
378f25da51fSAlexander Aring
379f25da51fSAlexander Aring rcu_read_lock();
380f25da51fSAlexander Aring if (list_empty(&phy->edges)) {
381f25da51fSAlexander Aring rcu_read_unlock();
382f25da51fSAlexander Aring return 0;
383f25da51fSAlexander Aring }
384f25da51fSAlexander Aring
385ae0be8deSMichal Kubecek nl_edges = nla_nest_start_noflag(skb,
386ae0be8deSMichal Kubecek MAC802154_HWSIM_ATTR_RADIO_EDGES);
387f25da51fSAlexander Aring if (!nl_edges) {
388f25da51fSAlexander Aring rcu_read_unlock();
389f25da51fSAlexander Aring return -ENOBUFS;
390f25da51fSAlexander Aring }
391f25da51fSAlexander Aring
392f25da51fSAlexander Aring list_for_each_entry_rcu(e, &phy->edges, list) {
393ae0be8deSMichal Kubecek nl_edge = nla_nest_start_noflag(skb,
394ae0be8deSMichal Kubecek MAC802154_HWSIM_ATTR_RADIO_EDGE);
395f25da51fSAlexander Aring if (!nl_edge) {
396f25da51fSAlexander Aring rcu_read_unlock();
397f25da51fSAlexander Aring nla_nest_cancel(skb, nl_edges);
398f25da51fSAlexander Aring return -ENOBUFS;
399f25da51fSAlexander Aring }
400f25da51fSAlexander Aring
401f25da51fSAlexander Aring ret = nla_put_u32(skb, MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID,
402f25da51fSAlexander Aring e->endpoint->idx);
403f25da51fSAlexander Aring if (ret < 0) {
404f25da51fSAlexander Aring rcu_read_unlock();
405f25da51fSAlexander Aring nla_nest_cancel(skb, nl_edge);
406f25da51fSAlexander Aring nla_nest_cancel(skb, nl_edges);
407f25da51fSAlexander Aring return ret;
408f25da51fSAlexander Aring }
409f25da51fSAlexander Aring
410f25da51fSAlexander Aring einfo = rcu_dereference(e->info);
411f25da51fSAlexander Aring ret = nla_put_u8(skb, MAC802154_HWSIM_EDGE_ATTR_LQI,
412f25da51fSAlexander Aring einfo->lqi);
413f25da51fSAlexander Aring if (ret < 0) {
414f25da51fSAlexander Aring rcu_read_unlock();
415f25da51fSAlexander Aring nla_nest_cancel(skb, nl_edge);
416f25da51fSAlexander Aring nla_nest_cancel(skb, nl_edges);
417f25da51fSAlexander Aring return ret;
418f25da51fSAlexander Aring }
419f25da51fSAlexander Aring
420f25da51fSAlexander Aring nla_nest_end(skb, nl_edge);
421f25da51fSAlexander Aring }
422f25da51fSAlexander Aring rcu_read_unlock();
423f25da51fSAlexander Aring
424f25da51fSAlexander Aring nla_nest_end(skb, nl_edges);
425f25da51fSAlexander Aring
426f25da51fSAlexander Aring return 0;
427f25da51fSAlexander Aring }
428f25da51fSAlexander Aring
hwsim_get_radio(struct sk_buff * skb,struct hwsim_phy * phy,u32 portid,u32 seq,struct netlink_callback * cb,int flags)429f25da51fSAlexander Aring static int hwsim_get_radio(struct sk_buff *skb, struct hwsim_phy *phy,
430f25da51fSAlexander Aring u32 portid, u32 seq,
431f25da51fSAlexander Aring struct netlink_callback *cb, int flags)
432f25da51fSAlexander Aring {
433f25da51fSAlexander Aring void *hdr;
43424ad92c8SColin Ian King int res;
435f25da51fSAlexander Aring
436f25da51fSAlexander Aring hdr = genlmsg_put(skb, portid, seq, &hwsim_genl_family, flags,
437f25da51fSAlexander Aring MAC802154_HWSIM_CMD_GET_RADIO);
438f25da51fSAlexander Aring if (!hdr)
439f25da51fSAlexander Aring return -EMSGSIZE;
440f25da51fSAlexander Aring
441f25da51fSAlexander Aring if (cb)
442f25da51fSAlexander Aring genl_dump_check_consistent(cb, hdr);
443f25da51fSAlexander Aring
444f25da51fSAlexander Aring res = append_radio_msg(skb, phy);
445f25da51fSAlexander Aring if (res < 0)
446f25da51fSAlexander Aring goto out_err;
447f25da51fSAlexander Aring
448f25da51fSAlexander Aring genlmsg_end(skb, hdr);
449f25da51fSAlexander Aring return 0;
450f25da51fSAlexander Aring
451f25da51fSAlexander Aring out_err:
452f25da51fSAlexander Aring genlmsg_cancel(skb, hdr);
453f25da51fSAlexander Aring return res;
454f25da51fSAlexander Aring }
455f25da51fSAlexander Aring
hwsim_get_radio_nl(struct sk_buff * msg,struct genl_info * info)456f25da51fSAlexander Aring static int hwsim_get_radio_nl(struct sk_buff *msg, struct genl_info *info)
457f25da51fSAlexander Aring {
458f25da51fSAlexander Aring struct hwsim_phy *phy;
459f25da51fSAlexander Aring struct sk_buff *skb;
460f25da51fSAlexander Aring int idx, res = -ENODEV;
461f25da51fSAlexander Aring
462f25da51fSAlexander Aring if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID])
463f25da51fSAlexander Aring return -EINVAL;
464f25da51fSAlexander Aring idx = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
465f25da51fSAlexander Aring
466f25da51fSAlexander Aring mutex_lock(&hwsim_phys_lock);
467f25da51fSAlexander Aring list_for_each_entry(phy, &hwsim_phys, list) {
468f25da51fSAlexander Aring if (phy->idx != idx)
469f25da51fSAlexander Aring continue;
470f25da51fSAlexander Aring
471f25da51fSAlexander Aring skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
472f25da51fSAlexander Aring if (!skb) {
473f25da51fSAlexander Aring res = -ENOMEM;
474f25da51fSAlexander Aring goto out_err;
475f25da51fSAlexander Aring }
476f25da51fSAlexander Aring
477f25da51fSAlexander Aring res = hwsim_get_radio(skb, phy, info->snd_portid,
478f25da51fSAlexander Aring info->snd_seq, NULL, 0);
479f25da51fSAlexander Aring if (res < 0) {
480f25da51fSAlexander Aring nlmsg_free(skb);
481f25da51fSAlexander Aring goto out_err;
482f25da51fSAlexander Aring }
483f25da51fSAlexander Aring
48419b39a25SLi RongQing res = genlmsg_reply(skb, info);
485f25da51fSAlexander Aring break;
486f25da51fSAlexander Aring }
487f25da51fSAlexander Aring
488f25da51fSAlexander Aring out_err:
489f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
490f25da51fSAlexander Aring
491f25da51fSAlexander Aring return res;
492f25da51fSAlexander Aring }
493f25da51fSAlexander Aring
hwsim_dump_radio_nl(struct sk_buff * skb,struct netlink_callback * cb)494f25da51fSAlexander Aring static int hwsim_dump_radio_nl(struct sk_buff *skb,
495f25da51fSAlexander Aring struct netlink_callback *cb)
496f25da51fSAlexander Aring {
497f25da51fSAlexander Aring int idx = cb->args[0];
498f25da51fSAlexander Aring struct hwsim_phy *phy;
499f25da51fSAlexander Aring int res;
500f25da51fSAlexander Aring
501f25da51fSAlexander Aring mutex_lock(&hwsim_phys_lock);
502f25da51fSAlexander Aring
503f25da51fSAlexander Aring if (idx == hwsim_radio_idx)
504f25da51fSAlexander Aring goto done;
505f25da51fSAlexander Aring
506f25da51fSAlexander Aring list_for_each_entry(phy, &hwsim_phys, list) {
507f25da51fSAlexander Aring if (phy->idx < idx)
508f25da51fSAlexander Aring continue;
509f25da51fSAlexander Aring
510f25da51fSAlexander Aring res = hwsim_get_radio(skb, phy, NETLINK_CB(cb->skb).portid,
511f25da51fSAlexander Aring cb->nlh->nlmsg_seq, cb, NLM_F_MULTI);
512f25da51fSAlexander Aring if (res < 0)
513f25da51fSAlexander Aring break;
514f25da51fSAlexander Aring
515f25da51fSAlexander Aring idx = phy->idx + 1;
516f25da51fSAlexander Aring }
517f25da51fSAlexander Aring
518f25da51fSAlexander Aring cb->args[0] = idx;
519f25da51fSAlexander Aring
520f25da51fSAlexander Aring done:
521f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
522f25da51fSAlexander Aring return skb->len;
523f25da51fSAlexander Aring }
524f25da51fSAlexander Aring
525f25da51fSAlexander Aring /* caller need to held hwsim_phys_lock */
hwsim_get_radio_by_id(uint32_t idx)526f25da51fSAlexander Aring static struct hwsim_phy *hwsim_get_radio_by_id(uint32_t idx)
527f25da51fSAlexander Aring {
528f25da51fSAlexander Aring struct hwsim_phy *phy;
529f25da51fSAlexander Aring
530f25da51fSAlexander Aring list_for_each_entry(phy, &hwsim_phys, list) {
531f25da51fSAlexander Aring if (phy->idx == idx)
532f25da51fSAlexander Aring return phy;
533f25da51fSAlexander Aring }
534f25da51fSAlexander Aring
535f25da51fSAlexander Aring return NULL;
536f25da51fSAlexander Aring }
537f25da51fSAlexander Aring
538f25da51fSAlexander Aring static const struct nla_policy hwsim_edge_policy[MAC802154_HWSIM_EDGE_ATTR_MAX + 1] = {
539f25da51fSAlexander Aring [MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID] = { .type = NLA_U32 },
540f25da51fSAlexander Aring [MAC802154_HWSIM_EDGE_ATTR_LQI] = { .type = NLA_U8 },
541f25da51fSAlexander Aring };
542f25da51fSAlexander Aring
hwsim_alloc_edge(struct hwsim_phy * endpoint,u8 lqi)543f25da51fSAlexander Aring static struct hwsim_edge *hwsim_alloc_edge(struct hwsim_phy *endpoint, u8 lqi)
544f25da51fSAlexander Aring {
545f25da51fSAlexander Aring struct hwsim_edge_info *einfo;
546f25da51fSAlexander Aring struct hwsim_edge *e;
547f25da51fSAlexander Aring
548f25da51fSAlexander Aring e = kzalloc(sizeof(*e), GFP_KERNEL);
549f25da51fSAlexander Aring if (!e)
550f25da51fSAlexander Aring return NULL;
551f25da51fSAlexander Aring
552f25da51fSAlexander Aring einfo = kzalloc(sizeof(*einfo), GFP_KERNEL);
553f25da51fSAlexander Aring if (!einfo) {
554f25da51fSAlexander Aring kfree(e);
555f25da51fSAlexander Aring return NULL;
556f25da51fSAlexander Aring }
557f25da51fSAlexander Aring
558f25da51fSAlexander Aring einfo->lqi = 0xff;
5591c9f4a3fSAlexander Aring rcu_assign_pointer(e->info, einfo);
560f25da51fSAlexander Aring e->endpoint = endpoint;
561f25da51fSAlexander Aring
562f25da51fSAlexander Aring return e;
563f25da51fSAlexander Aring }
564f25da51fSAlexander Aring
hwsim_free_edge(struct hwsim_edge * e)565f25da51fSAlexander Aring static void hwsim_free_edge(struct hwsim_edge *e)
566f25da51fSAlexander Aring {
5671c9f4a3fSAlexander Aring struct hwsim_edge_info *einfo;
5681c9f4a3fSAlexander Aring
5691c9f4a3fSAlexander Aring rcu_read_lock();
5701c9f4a3fSAlexander Aring einfo = rcu_dereference(e->info);
5711c9f4a3fSAlexander Aring rcu_read_unlock();
5721c9f4a3fSAlexander Aring
5731c9f4a3fSAlexander Aring kfree_rcu(einfo, rcu);
574f25da51fSAlexander Aring kfree_rcu(e, rcu);
575f25da51fSAlexander Aring }
576f25da51fSAlexander Aring
hwsim_new_edge_nl(struct sk_buff * msg,struct genl_info * info)577f25da51fSAlexander Aring static int hwsim_new_edge_nl(struct sk_buff *msg, struct genl_info *info)
578f25da51fSAlexander Aring {
579f25da51fSAlexander Aring struct nlattr *edge_attrs[MAC802154_HWSIM_EDGE_ATTR_MAX + 1];
580f25da51fSAlexander Aring struct hwsim_phy *phy_v0, *phy_v1;
581f25da51fSAlexander Aring struct hwsim_edge *e;
582f25da51fSAlexander Aring u32 v0, v1;
583f25da51fSAlexander Aring
584889d0e7dSDongliang Mu if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID] ||
585f25da51fSAlexander Aring !info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE])
586f25da51fSAlexander Aring return -EINVAL;
587f25da51fSAlexander Aring
5888cb08174SJohannes Berg if (nla_parse_nested_deprecated(edge_attrs, MAC802154_HWSIM_EDGE_ATTR_MAX, info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE], hwsim_edge_policy, NULL))
589f25da51fSAlexander Aring return -EINVAL;
590f25da51fSAlexander Aring
591f25da51fSAlexander Aring if (!edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID])
592f25da51fSAlexander Aring return -EINVAL;
593f25da51fSAlexander Aring
594f25da51fSAlexander Aring v0 = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
595f25da51fSAlexander Aring v1 = nla_get_u32(edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID]);
596f25da51fSAlexander Aring
597f25da51fSAlexander Aring if (v0 == v1)
598f25da51fSAlexander Aring return -EINVAL;
599f25da51fSAlexander Aring
600f25da51fSAlexander Aring mutex_lock(&hwsim_phys_lock);
601f25da51fSAlexander Aring phy_v0 = hwsim_get_radio_by_id(v0);
602f25da51fSAlexander Aring if (!phy_v0) {
603f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
604f25da51fSAlexander Aring return -ENOENT;
605f25da51fSAlexander Aring }
606f25da51fSAlexander Aring
607f25da51fSAlexander Aring phy_v1 = hwsim_get_radio_by_id(v1);
608f25da51fSAlexander Aring if (!phy_v1) {
609f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
610f25da51fSAlexander Aring return -ENOENT;
611f25da51fSAlexander Aring }
612f25da51fSAlexander Aring
613f25da51fSAlexander Aring rcu_read_lock();
614f25da51fSAlexander Aring list_for_each_entry_rcu(e, &phy_v0->edges, list) {
615f25da51fSAlexander Aring if (e->endpoint->idx == v1) {
616f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
617f25da51fSAlexander Aring rcu_read_unlock();
618f25da51fSAlexander Aring return -EEXIST;
619f25da51fSAlexander Aring }
620f25da51fSAlexander Aring }
621f25da51fSAlexander Aring rcu_read_unlock();
622f25da51fSAlexander Aring
623f25da51fSAlexander Aring e = hwsim_alloc_edge(phy_v1, 0xff);
624f25da51fSAlexander Aring if (!e) {
625f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
626f25da51fSAlexander Aring return -ENOMEM;
627f25da51fSAlexander Aring }
628f25da51fSAlexander Aring list_add_rcu(&e->list, &phy_v0->edges);
629f25da51fSAlexander Aring /* wait until changes are done under hwsim_phys_lock lock
630f25da51fSAlexander Aring * should prevent of calling this function twice while
631f25da51fSAlexander Aring * edges list has not the changes yet.
632f25da51fSAlexander Aring */
633f25da51fSAlexander Aring synchronize_rcu();
634f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
635f25da51fSAlexander Aring
636f25da51fSAlexander Aring return 0;
637f25da51fSAlexander Aring }
638f25da51fSAlexander Aring
hwsim_del_edge_nl(struct sk_buff * msg,struct genl_info * info)639f25da51fSAlexander Aring static int hwsim_del_edge_nl(struct sk_buff *msg, struct genl_info *info)
640f25da51fSAlexander Aring {
641f25da51fSAlexander Aring struct nlattr *edge_attrs[MAC802154_HWSIM_EDGE_ATTR_MAX + 1];
642f25da51fSAlexander Aring struct hwsim_phy *phy_v0;
643f25da51fSAlexander Aring struct hwsim_edge *e;
644f25da51fSAlexander Aring u32 v0, v1;
645f25da51fSAlexander Aring
6460303b303SEric Dumazet if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID] ||
647f25da51fSAlexander Aring !info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE])
648f25da51fSAlexander Aring return -EINVAL;
649f25da51fSAlexander Aring
6508cb08174SJohannes Berg if (nla_parse_nested_deprecated(edge_attrs, MAC802154_HWSIM_EDGE_ATTR_MAX, info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE], hwsim_edge_policy, NULL))
651f25da51fSAlexander Aring return -EINVAL;
652f25da51fSAlexander Aring
653f25da51fSAlexander Aring if (!edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID])
654f25da51fSAlexander Aring return -EINVAL;
655f25da51fSAlexander Aring
656f25da51fSAlexander Aring v0 = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
657f25da51fSAlexander Aring v1 = nla_get_u32(edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID]);
658f25da51fSAlexander Aring
659f25da51fSAlexander Aring mutex_lock(&hwsim_phys_lock);
660f25da51fSAlexander Aring phy_v0 = hwsim_get_radio_by_id(v0);
661f25da51fSAlexander Aring if (!phy_v0) {
662f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
663f25da51fSAlexander Aring return -ENOENT;
664f25da51fSAlexander Aring }
665f25da51fSAlexander Aring
666f25da51fSAlexander Aring rcu_read_lock();
667f25da51fSAlexander Aring list_for_each_entry_rcu(e, &phy_v0->edges, list) {
668f25da51fSAlexander Aring if (e->endpoint->idx == v1) {
669f25da51fSAlexander Aring rcu_read_unlock();
670f25da51fSAlexander Aring list_del_rcu(&e->list);
671f25da51fSAlexander Aring hwsim_free_edge(e);
672f25da51fSAlexander Aring /* same again - wait until list changes are done */
673f25da51fSAlexander Aring synchronize_rcu();
674f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
675f25da51fSAlexander Aring return 0;
676f25da51fSAlexander Aring }
677f25da51fSAlexander Aring }
678f25da51fSAlexander Aring rcu_read_unlock();
679f25da51fSAlexander Aring
680f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
681f25da51fSAlexander Aring
682f25da51fSAlexander Aring return -ENOENT;
683f25da51fSAlexander Aring }
684f25da51fSAlexander Aring
hwsim_set_edge_lqi(struct sk_buff * msg,struct genl_info * info)685f25da51fSAlexander Aring static int hwsim_set_edge_lqi(struct sk_buff *msg, struct genl_info *info)
686f25da51fSAlexander Aring {
687f25da51fSAlexander Aring struct nlattr *edge_attrs[MAC802154_HWSIM_EDGE_ATTR_MAX + 1];
688a6167529SChen Aotian struct hwsim_edge_info *einfo, *einfo_old;
689f25da51fSAlexander Aring struct hwsim_phy *phy_v0;
690f25da51fSAlexander Aring struct hwsim_edge *e;
691f25da51fSAlexander Aring u32 v0, v1;
692f25da51fSAlexander Aring u8 lqi;
693f25da51fSAlexander Aring
694e9faf53cSDongliang Mu if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID] ||
695f25da51fSAlexander Aring !info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE])
696f25da51fSAlexander Aring return -EINVAL;
697f25da51fSAlexander Aring
6988cb08174SJohannes Berg if (nla_parse_nested_deprecated(edge_attrs, MAC802154_HWSIM_EDGE_ATTR_MAX, info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE], hwsim_edge_policy, NULL))
699f25da51fSAlexander Aring return -EINVAL;
700f25da51fSAlexander Aring
701e9faf53cSDongliang Mu if (!edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID] ||
702f25da51fSAlexander Aring !edge_attrs[MAC802154_HWSIM_EDGE_ATTR_LQI])
703f25da51fSAlexander Aring return -EINVAL;
704f25da51fSAlexander Aring
705f25da51fSAlexander Aring v0 = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
706f25da51fSAlexander Aring v1 = nla_get_u32(edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID]);
707f25da51fSAlexander Aring lqi = nla_get_u8(edge_attrs[MAC802154_HWSIM_EDGE_ATTR_LQI]);
708f25da51fSAlexander Aring
709f25da51fSAlexander Aring mutex_lock(&hwsim_phys_lock);
710f25da51fSAlexander Aring phy_v0 = hwsim_get_radio_by_id(v0);
711f25da51fSAlexander Aring if (!phy_v0) {
712f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
713f25da51fSAlexander Aring return -ENOENT;
714f25da51fSAlexander Aring }
715f25da51fSAlexander Aring
716f25da51fSAlexander Aring einfo = kzalloc(sizeof(*einfo), GFP_KERNEL);
717470770bfSWei Yongjun if (!einfo) {
718f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
719f25da51fSAlexander Aring return -ENOMEM;
720f25da51fSAlexander Aring }
721f25da51fSAlexander Aring
722f25da51fSAlexander Aring rcu_read_lock();
723f25da51fSAlexander Aring list_for_each_entry_rcu(e, &phy_v0->edges, list) {
724f25da51fSAlexander Aring if (e->endpoint->idx == v1) {
725f25da51fSAlexander Aring einfo->lqi = lqi;
726a6167529SChen Aotian einfo_old = rcu_replace_pointer(e->info, einfo,
727a6167529SChen Aotian lockdep_is_held(&hwsim_phys_lock));
728f25da51fSAlexander Aring rcu_read_unlock();
729a6167529SChen Aotian kfree_rcu(einfo_old, rcu);
730f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
731f25da51fSAlexander Aring return 0;
732f25da51fSAlexander Aring }
733f25da51fSAlexander Aring }
734f25da51fSAlexander Aring rcu_read_unlock();
735f25da51fSAlexander Aring
736f25da51fSAlexander Aring kfree(einfo);
737f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
738f25da51fSAlexander Aring
739f25da51fSAlexander Aring return -ENOENT;
740f25da51fSAlexander Aring }
741f25da51fSAlexander Aring
742f25da51fSAlexander Aring /* MAC802154_HWSIM netlink policy */
743f25da51fSAlexander Aring
744f25da51fSAlexander Aring static const struct nla_policy hwsim_genl_policy[MAC802154_HWSIM_ATTR_MAX + 1] = {
745f25da51fSAlexander Aring [MAC802154_HWSIM_ATTR_RADIO_ID] = { .type = NLA_U32 },
746f25da51fSAlexander Aring [MAC802154_HWSIM_ATTR_RADIO_EDGE] = { .type = NLA_NESTED },
747f25da51fSAlexander Aring [MAC802154_HWSIM_ATTR_RADIO_EDGES] = { .type = NLA_NESTED },
748f25da51fSAlexander Aring };
749f25da51fSAlexander Aring
750f25da51fSAlexander Aring /* Generic Netlink operations array */
75166a9b928SJakub Kicinski static const struct genl_small_ops hwsim_nl_ops[] = {
752f25da51fSAlexander Aring {
753f25da51fSAlexander Aring .cmd = MAC802154_HWSIM_CMD_NEW_RADIO,
754ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
755f25da51fSAlexander Aring .doit = hwsim_new_radio_nl,
756f25da51fSAlexander Aring .flags = GENL_UNS_ADMIN_PERM,
757f25da51fSAlexander Aring },
758f25da51fSAlexander Aring {
759f25da51fSAlexander Aring .cmd = MAC802154_HWSIM_CMD_DEL_RADIO,
760ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
761f25da51fSAlexander Aring .doit = hwsim_del_radio_nl,
762f25da51fSAlexander Aring .flags = GENL_UNS_ADMIN_PERM,
763f25da51fSAlexander Aring },
764f25da51fSAlexander Aring {
765f25da51fSAlexander Aring .cmd = MAC802154_HWSIM_CMD_GET_RADIO,
766ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
767f25da51fSAlexander Aring .doit = hwsim_get_radio_nl,
768f25da51fSAlexander Aring .dumpit = hwsim_dump_radio_nl,
769f25da51fSAlexander Aring },
770f25da51fSAlexander Aring {
771f25da51fSAlexander Aring .cmd = MAC802154_HWSIM_CMD_NEW_EDGE,
772ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
773f25da51fSAlexander Aring .doit = hwsim_new_edge_nl,
774f25da51fSAlexander Aring .flags = GENL_UNS_ADMIN_PERM,
775f25da51fSAlexander Aring },
776f25da51fSAlexander Aring {
777f25da51fSAlexander Aring .cmd = MAC802154_HWSIM_CMD_DEL_EDGE,
778ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
779f25da51fSAlexander Aring .doit = hwsim_del_edge_nl,
780f25da51fSAlexander Aring .flags = GENL_UNS_ADMIN_PERM,
781f25da51fSAlexander Aring },
782f25da51fSAlexander Aring {
783f25da51fSAlexander Aring .cmd = MAC802154_HWSIM_CMD_SET_EDGE,
784ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
785f25da51fSAlexander Aring .doit = hwsim_set_edge_lqi,
786f25da51fSAlexander Aring .flags = GENL_UNS_ADMIN_PERM,
787f25da51fSAlexander Aring },
788f25da51fSAlexander Aring };
789f25da51fSAlexander Aring
790f25da51fSAlexander Aring static struct genl_family hwsim_genl_family __ro_after_init = {
791f25da51fSAlexander Aring .name = "MAC802154_HWSIM",
792f25da51fSAlexander Aring .version = 1,
793f25da51fSAlexander Aring .maxattr = MAC802154_HWSIM_ATTR_MAX,
7943b0f31f2SJohannes Berg .policy = hwsim_genl_policy,
795f25da51fSAlexander Aring .module = THIS_MODULE,
79666a9b928SJakub Kicinski .small_ops = hwsim_nl_ops,
79766a9b928SJakub Kicinski .n_small_ops = ARRAY_SIZE(hwsim_nl_ops),
7989c5d03d3SJakub Kicinski .resv_start_op = MAC802154_HWSIM_CMD_NEW_EDGE + 1,
799f25da51fSAlexander Aring .mcgrps = hwsim_mcgrps,
800f25da51fSAlexander Aring .n_mcgrps = ARRAY_SIZE(hwsim_mcgrps),
801f25da51fSAlexander Aring };
802f25da51fSAlexander Aring
hwsim_mcast_config_msg(struct sk_buff * mcast_skb,struct genl_info * info)803f25da51fSAlexander Aring static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
804f25da51fSAlexander Aring struct genl_info *info)
805f25da51fSAlexander Aring {
806f25da51fSAlexander Aring if (info)
807f25da51fSAlexander Aring genl_notify(&hwsim_genl_family, mcast_skb, info,
808f25da51fSAlexander Aring HWSIM_MCGRP_CONFIG, GFP_KERNEL);
809f25da51fSAlexander Aring else
810f25da51fSAlexander Aring genlmsg_multicast(&hwsim_genl_family, mcast_skb, 0,
811f25da51fSAlexander Aring HWSIM_MCGRP_CONFIG, GFP_KERNEL);
812f25da51fSAlexander Aring }
813f25da51fSAlexander Aring
hwsim_mcast_new_radio(struct genl_info * info,struct hwsim_phy * phy)814f25da51fSAlexander Aring static void hwsim_mcast_new_radio(struct genl_info *info, struct hwsim_phy *phy)
815f25da51fSAlexander Aring {
816f25da51fSAlexander Aring struct sk_buff *mcast_skb;
817f25da51fSAlexander Aring void *data;
818f25da51fSAlexander Aring
819f25da51fSAlexander Aring mcast_skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
820f25da51fSAlexander Aring if (!mcast_skb)
821f25da51fSAlexander Aring return;
822f25da51fSAlexander Aring
823f25da51fSAlexander Aring data = genlmsg_put(mcast_skb, 0, 0, &hwsim_genl_family, 0,
824f25da51fSAlexander Aring MAC802154_HWSIM_CMD_NEW_RADIO);
825f25da51fSAlexander Aring if (!data)
826f25da51fSAlexander Aring goto out_err;
827f25da51fSAlexander Aring
828f25da51fSAlexander Aring if (append_radio_msg(mcast_skb, phy) < 0)
829f25da51fSAlexander Aring goto out_err;
830f25da51fSAlexander Aring
831f25da51fSAlexander Aring genlmsg_end(mcast_skb, data);
832f25da51fSAlexander Aring
833f25da51fSAlexander Aring hwsim_mcast_config_msg(mcast_skb, info);
834f25da51fSAlexander Aring return;
835f25da51fSAlexander Aring
836f25da51fSAlexander Aring out_err:
837f25da51fSAlexander Aring genlmsg_cancel(mcast_skb, data);
838f25da51fSAlexander Aring nlmsg_free(mcast_skb);
839f25da51fSAlexander Aring }
840f25da51fSAlexander Aring
hwsim_edge_unsubscribe_me(struct hwsim_phy * phy)841f25da51fSAlexander Aring static void hwsim_edge_unsubscribe_me(struct hwsim_phy *phy)
842f25da51fSAlexander Aring {
843f25da51fSAlexander Aring struct hwsim_phy *tmp;
844f25da51fSAlexander Aring struct hwsim_edge *e;
845f25da51fSAlexander Aring
846f25da51fSAlexander Aring rcu_read_lock();
847f25da51fSAlexander Aring /* going to all phy edges and remove phy from it */
848f25da51fSAlexander Aring list_for_each_entry(tmp, &hwsim_phys, list) {
849f25da51fSAlexander Aring list_for_each_entry_rcu(e, &tmp->edges, list) {
850f25da51fSAlexander Aring if (e->endpoint->idx == phy->idx) {
851f25da51fSAlexander Aring list_del_rcu(&e->list);
852f25da51fSAlexander Aring hwsim_free_edge(e);
853f25da51fSAlexander Aring }
854f25da51fSAlexander Aring }
855f25da51fSAlexander Aring }
856f25da51fSAlexander Aring rcu_read_unlock();
857f25da51fSAlexander Aring
858f25da51fSAlexander Aring synchronize_rcu();
859f25da51fSAlexander Aring }
860f25da51fSAlexander Aring
hwsim_subscribe_all_others(struct hwsim_phy * phy)861f25da51fSAlexander Aring static int hwsim_subscribe_all_others(struct hwsim_phy *phy)
862f25da51fSAlexander Aring {
863f25da51fSAlexander Aring struct hwsim_phy *sub;
864f25da51fSAlexander Aring struct hwsim_edge *e;
865f25da51fSAlexander Aring
866f25da51fSAlexander Aring list_for_each_entry(sub, &hwsim_phys, list) {
867f25da51fSAlexander Aring e = hwsim_alloc_edge(sub, 0xff);
868f25da51fSAlexander Aring if (!e)
869f25da51fSAlexander Aring goto me_fail;
870f25da51fSAlexander Aring
871f25da51fSAlexander Aring list_add_rcu(&e->list, &phy->edges);
872f25da51fSAlexander Aring }
873f25da51fSAlexander Aring
874f25da51fSAlexander Aring list_for_each_entry(sub, &hwsim_phys, list) {
875f25da51fSAlexander Aring e = hwsim_alloc_edge(phy, 0xff);
876f25da51fSAlexander Aring if (!e)
877f25da51fSAlexander Aring goto sub_fail;
878f25da51fSAlexander Aring
879f25da51fSAlexander Aring list_add_rcu(&e->list, &sub->edges);
880f25da51fSAlexander Aring }
881f25da51fSAlexander Aring
882f25da51fSAlexander Aring return 0;
883f25da51fSAlexander Aring
884ab372c22SDongliang Mu sub_fail:
885ab372c22SDongliang Mu hwsim_edge_unsubscribe_me(phy);
886f25da51fSAlexander Aring me_fail:
8871c89a8e3SAlexander Aring rcu_read_lock();
8881c89a8e3SAlexander Aring list_for_each_entry_rcu(e, &phy->edges, list) {
889f25da51fSAlexander Aring list_del_rcu(&e->list);
890f25da51fSAlexander Aring hwsim_free_edge(e);
891f25da51fSAlexander Aring }
8921c89a8e3SAlexander Aring rcu_read_unlock();
893f25da51fSAlexander Aring return -ENOMEM;
894f25da51fSAlexander Aring }
895f25da51fSAlexander Aring
hwsim_add_one(struct genl_info * info,struct device * dev,bool init)896f25da51fSAlexander Aring static int hwsim_add_one(struct genl_info *info, struct device *dev,
897f25da51fSAlexander Aring bool init)
898f25da51fSAlexander Aring {
899f25da51fSAlexander Aring struct ieee802154_hw *hw;
900f25da51fSAlexander Aring struct hwsim_phy *phy;
901f25da51fSAlexander Aring struct hwsim_pib *pib;
902f25da51fSAlexander Aring int idx;
903f25da51fSAlexander Aring int err;
904f25da51fSAlexander Aring
905f25da51fSAlexander Aring idx = hwsim_radio_idx++;
906f25da51fSAlexander Aring
907f25da51fSAlexander Aring hw = ieee802154_alloc_hw(sizeof(*phy), &hwsim_ops);
908f25da51fSAlexander Aring if (!hw)
909f25da51fSAlexander Aring return -ENOMEM;
910f25da51fSAlexander Aring
911f25da51fSAlexander Aring phy = hw->priv;
912f25da51fSAlexander Aring phy->hw = hw;
913f25da51fSAlexander Aring
914f25da51fSAlexander Aring /* 868 MHz BPSK 802.15.4-2003 */
915f25da51fSAlexander Aring hw->phy->supported.channels[0] |= 1;
916f25da51fSAlexander Aring /* 915 MHz BPSK 802.15.4-2003 */
917f25da51fSAlexander Aring hw->phy->supported.channels[0] |= 0x7fe;
918f25da51fSAlexander Aring /* 2.4 GHz O-QPSK 802.15.4-2003 */
919f25da51fSAlexander Aring hw->phy->supported.channels[0] |= 0x7FFF800;
920f25da51fSAlexander Aring /* 868 MHz ASK 802.15.4-2006 */
921f25da51fSAlexander Aring hw->phy->supported.channels[1] |= 1;
922f25da51fSAlexander Aring /* 915 MHz ASK 802.15.4-2006 */
923f25da51fSAlexander Aring hw->phy->supported.channels[1] |= 0x7fe;
924f25da51fSAlexander Aring /* 868 MHz O-QPSK 802.15.4-2006 */
925f25da51fSAlexander Aring hw->phy->supported.channels[2] |= 1;
926f25da51fSAlexander Aring /* 915 MHz O-QPSK 802.15.4-2006 */
927f25da51fSAlexander Aring hw->phy->supported.channels[2] |= 0x7fe;
928f25da51fSAlexander Aring /* 2.4 GHz CSS 802.15.4a-2007 */
929f25da51fSAlexander Aring hw->phy->supported.channels[3] |= 0x3fff;
930f25da51fSAlexander Aring /* UWB Sub-gigahertz 802.15.4a-2007 */
931f25da51fSAlexander Aring hw->phy->supported.channels[4] |= 1;
932f25da51fSAlexander Aring /* UWB Low band 802.15.4a-2007 */
933f25da51fSAlexander Aring hw->phy->supported.channels[4] |= 0x1e;
934f25da51fSAlexander Aring /* UWB High band 802.15.4a-2007 */
935f25da51fSAlexander Aring hw->phy->supported.channels[4] |= 0xffe0;
936f25da51fSAlexander Aring /* 750 MHz O-QPSK 802.15.4c-2009 */
937f25da51fSAlexander Aring hw->phy->supported.channels[5] |= 0xf;
938f25da51fSAlexander Aring /* 750 MHz MPSK 802.15.4c-2009 */
939f25da51fSAlexander Aring hw->phy->supported.channels[5] |= 0xf0;
940f25da51fSAlexander Aring /* 950 MHz BPSK 802.15.4d-2009 */
941f25da51fSAlexander Aring hw->phy->supported.channels[6] |= 0x3ff;
942f25da51fSAlexander Aring /* 950 MHz GFSK 802.15.4d-2009 */
943f25da51fSAlexander Aring hw->phy->supported.channels[6] |= 0x3ffc00;
944f25da51fSAlexander Aring
945f25da51fSAlexander Aring ieee802154_random_extended_addr(&hw->phy->perm_extended_addr);
946f25da51fSAlexander Aring
947f25da51fSAlexander Aring /* hwsim phy channel 13 as default */
948f25da51fSAlexander Aring hw->phy->current_channel = 13;
949f25da51fSAlexander Aring pib = kzalloc(sizeof(*pib), GFP_KERNEL);
950f25da51fSAlexander Aring if (!pib) {
951f25da51fSAlexander Aring err = -ENOMEM;
952f25da51fSAlexander Aring goto err_pib;
953f25da51fSAlexander Aring }
954f25da51fSAlexander Aring
9551293fcccSMiquel Raynal pib->channel = 13;
956a87815b7SMiquel Raynal pib->filt.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
957a87815b7SMiquel Raynal pib->filt.pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
9581c9f4a3fSAlexander Aring rcu_assign_pointer(phy->pib, pib);
959f25da51fSAlexander Aring phy->idx = idx;
960f25da51fSAlexander Aring INIT_LIST_HEAD(&phy->edges);
961f25da51fSAlexander Aring
962a4b5b4c5SMiquel Raynal hw->flags = IEEE802154_HW_PROMISCUOUS;
963f25da51fSAlexander Aring hw->parent = dev;
964f25da51fSAlexander Aring
965f25da51fSAlexander Aring err = ieee802154_register_hw(hw);
966f25da51fSAlexander Aring if (err)
967f25da51fSAlexander Aring goto err_reg;
968f25da51fSAlexander Aring
969f25da51fSAlexander Aring mutex_lock(&hwsim_phys_lock);
970f25da51fSAlexander Aring if (init) {
971f25da51fSAlexander Aring err = hwsim_subscribe_all_others(phy);
97213403d69SWei Yongjun if (err < 0) {
97313403d69SWei Yongjun mutex_unlock(&hwsim_phys_lock);
974de166bbeSYueHaibing goto err_subscribe;
975f25da51fSAlexander Aring }
97613403d69SWei Yongjun }
977f25da51fSAlexander Aring list_add_tail(&phy->list, &hwsim_phys);
978f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
979f25da51fSAlexander Aring
980f25da51fSAlexander Aring hwsim_mcast_new_radio(info, phy);
981f25da51fSAlexander Aring
982f25da51fSAlexander Aring return idx;
983f25da51fSAlexander Aring
984de166bbeSYueHaibing err_subscribe:
985de166bbeSYueHaibing ieee802154_unregister_hw(phy->hw);
986f25da51fSAlexander Aring err_reg:
987f25da51fSAlexander Aring kfree(pib);
988f25da51fSAlexander Aring err_pib:
989f25da51fSAlexander Aring ieee802154_free_hw(phy->hw);
990f25da51fSAlexander Aring return err;
991f25da51fSAlexander Aring }
992f25da51fSAlexander Aring
hwsim_del(struct hwsim_phy * phy)993f25da51fSAlexander Aring static void hwsim_del(struct hwsim_phy *phy)
994f25da51fSAlexander Aring {
9951c9f4a3fSAlexander Aring struct hwsim_pib *pib;
99628a5501cSDongliang Mu struct hwsim_edge *e;
9971c9f4a3fSAlexander Aring
998f25da51fSAlexander Aring hwsim_edge_unsubscribe_me(phy);
999f25da51fSAlexander Aring
1000f25da51fSAlexander Aring list_del(&phy->list);
10011c9f4a3fSAlexander Aring
10021c9f4a3fSAlexander Aring rcu_read_lock();
100328a5501cSDongliang Mu list_for_each_entry_rcu(e, &phy->edges, list) {
100428a5501cSDongliang Mu list_del_rcu(&e->list);
100528a5501cSDongliang Mu hwsim_free_edge(e);
100628a5501cSDongliang Mu }
10071c9f4a3fSAlexander Aring pib = rcu_dereference(phy->pib);
10081c9f4a3fSAlexander Aring rcu_read_unlock();
10091c9f4a3fSAlexander Aring
10101c9f4a3fSAlexander Aring kfree_rcu(pib, rcu);
1011f25da51fSAlexander Aring
1012f25da51fSAlexander Aring ieee802154_unregister_hw(phy->hw);
1013f25da51fSAlexander Aring ieee802154_free_hw(phy->hw);
1014f25da51fSAlexander Aring }
1015f25da51fSAlexander Aring
hwsim_probe(struct platform_device * pdev)1016f25da51fSAlexander Aring static int hwsim_probe(struct platform_device *pdev)
1017f25da51fSAlexander Aring {
1018f25da51fSAlexander Aring struct hwsim_phy *phy, *tmp;
1019f25da51fSAlexander Aring int err, i;
1020f25da51fSAlexander Aring
1021f25da51fSAlexander Aring for (i = 0; i < 2; i++) {
1022f25da51fSAlexander Aring err = hwsim_add_one(NULL, &pdev->dev, true);
1023f25da51fSAlexander Aring if (err < 0)
1024f25da51fSAlexander Aring goto err_slave;
1025f25da51fSAlexander Aring }
1026f25da51fSAlexander Aring
1027f25da51fSAlexander Aring dev_info(&pdev->dev, "Added 2 mac802154 hwsim hardware radios\n");
1028f25da51fSAlexander Aring return 0;
1029f25da51fSAlexander Aring
1030f25da51fSAlexander Aring err_slave:
1031f25da51fSAlexander Aring mutex_lock(&hwsim_phys_lock);
1032f25da51fSAlexander Aring list_for_each_entry_safe(phy, tmp, &hwsim_phys, list)
1033f25da51fSAlexander Aring hwsim_del(phy);
1034f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
1035f25da51fSAlexander Aring return err;
1036f25da51fSAlexander Aring }
1037f25da51fSAlexander Aring
hwsim_remove(struct platform_device * pdev)1038*9d4ccdefSUwe Kleine-König static void hwsim_remove(struct platform_device *pdev)
1039f25da51fSAlexander Aring {
1040f25da51fSAlexander Aring struct hwsim_phy *phy, *tmp;
1041f25da51fSAlexander Aring
1042f25da51fSAlexander Aring mutex_lock(&hwsim_phys_lock);
1043f25da51fSAlexander Aring list_for_each_entry_safe(phy, tmp, &hwsim_phys, list)
1044f25da51fSAlexander Aring hwsim_del(phy);
1045f25da51fSAlexander Aring mutex_unlock(&hwsim_phys_lock);
1046f25da51fSAlexander Aring }
1047f25da51fSAlexander Aring
1048f25da51fSAlexander Aring static struct platform_driver mac802154hwsim_driver = {
1049f25da51fSAlexander Aring .probe = hwsim_probe,
1050*9d4ccdefSUwe Kleine-König .remove_new = hwsim_remove,
1051f25da51fSAlexander Aring .driver = {
1052f25da51fSAlexander Aring .name = "mac802154_hwsim",
1053f25da51fSAlexander Aring },
1054f25da51fSAlexander Aring };
1055f25da51fSAlexander Aring
hwsim_init_module(void)1056f25da51fSAlexander Aring static __init int hwsim_init_module(void)
1057f25da51fSAlexander Aring {
1058f25da51fSAlexander Aring int rc;
1059f25da51fSAlexander Aring
1060f25da51fSAlexander Aring rc = genl_register_family(&hwsim_genl_family);
1061f25da51fSAlexander Aring if (rc)
1062f25da51fSAlexander Aring return rc;
1063f25da51fSAlexander Aring
1064f25da51fSAlexander Aring mac802154hwsim_dev = platform_device_register_simple("mac802154_hwsim",
1065f25da51fSAlexander Aring -1, NULL, 0);
1066f25da51fSAlexander Aring if (IS_ERR(mac802154hwsim_dev)) {
1067f25da51fSAlexander Aring rc = PTR_ERR(mac802154hwsim_dev);
1068f25da51fSAlexander Aring goto platform_dev;
1069f25da51fSAlexander Aring }
1070f25da51fSAlexander Aring
1071f25da51fSAlexander Aring rc = platform_driver_register(&mac802154hwsim_driver);
1072f25da51fSAlexander Aring if (rc < 0)
1073f25da51fSAlexander Aring goto platform_drv;
1074f25da51fSAlexander Aring
1075f25da51fSAlexander Aring return 0;
1076f25da51fSAlexander Aring
1077f25da51fSAlexander Aring platform_drv:
1078f25da51fSAlexander Aring platform_device_unregister(mac802154hwsim_dev);
10791cbbbf39SYueHaibing platform_dev:
10801cbbbf39SYueHaibing genl_unregister_family(&hwsim_genl_family);
1081f25da51fSAlexander Aring return rc;
1082f25da51fSAlexander Aring }
1083f25da51fSAlexander Aring
hwsim_remove_module(void)1084f25da51fSAlexander Aring static __exit void hwsim_remove_module(void)
1085f25da51fSAlexander Aring {
1086f25da51fSAlexander Aring genl_unregister_family(&hwsim_genl_family);
1087f25da51fSAlexander Aring platform_driver_unregister(&mac802154hwsim_driver);
1088f25da51fSAlexander Aring platform_device_unregister(mac802154hwsim_dev);
1089f25da51fSAlexander Aring }
1090f25da51fSAlexander Aring
1091f25da51fSAlexander Aring module_init(hwsim_init_module);
1092f25da51fSAlexander Aring module_exit(hwsim_remove_module);
1093