xref: /linux/drivers/net/ieee802154/mac802154_hwsim.c (revision 06d07429858317ded2db7986113a9e0129cd599b)
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, &current_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