1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * HWSIM IEEE 802.15.4 interface
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * (C) 2018 Mojatau, Alexander Aring <aring@mojatau.com>
6*4882a593Smuzhiyun * Copyright 2007-2012 Siemens AG
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Based on fakelb, original Written by:
9*4882a593Smuzhiyun * Sergey Lapin <slapin@ossfans.org>
10*4882a593Smuzhiyun * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
11*4882a593Smuzhiyun * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
12*4882a593Smuzhiyun */
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/timer.h>
16*4882a593Smuzhiyun #include <linux/platform_device.h>
17*4882a593Smuzhiyun #include <linux/rtnetlink.h>
18*4882a593Smuzhiyun #include <linux/netdevice.h>
19*4882a593Smuzhiyun #include <linux/device.h>
20*4882a593Smuzhiyun #include <linux/spinlock.h>
21*4882a593Smuzhiyun #include <net/mac802154.h>
22*4882a593Smuzhiyun #include <net/cfg802154.h>
23*4882a593Smuzhiyun #include <net/genetlink.h>
24*4882a593Smuzhiyun #include "mac802154_hwsim.h"
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun MODULE_DESCRIPTION("Software simulator of IEEE 802.15.4 radio(s) for mac802154");
27*4882a593Smuzhiyun MODULE_LICENSE("GPL");
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun static LIST_HEAD(hwsim_phys);
30*4882a593Smuzhiyun static DEFINE_MUTEX(hwsim_phys_lock);
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun static struct platform_device *mac802154hwsim_dev;
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun /* MAC802154_HWSIM netlink family */
35*4882a593Smuzhiyun static struct genl_family hwsim_genl_family;
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun static int hwsim_radio_idx;
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun enum hwsim_multicast_groups {
40*4882a593Smuzhiyun HWSIM_MCGRP_CONFIG,
41*4882a593Smuzhiyun };
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun static const struct genl_multicast_group hwsim_mcgrps[] = {
44*4882a593Smuzhiyun [HWSIM_MCGRP_CONFIG] = { .name = "config", },
45*4882a593Smuzhiyun };
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun struct hwsim_pib {
48*4882a593Smuzhiyun u8 page;
49*4882a593Smuzhiyun u8 channel;
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun struct rcu_head rcu;
52*4882a593Smuzhiyun };
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun struct hwsim_edge_info {
55*4882a593Smuzhiyun u8 lqi;
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun struct rcu_head rcu;
58*4882a593Smuzhiyun };
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun struct hwsim_edge {
61*4882a593Smuzhiyun struct hwsim_phy *endpoint;
62*4882a593Smuzhiyun struct hwsim_edge_info __rcu *info;
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun struct list_head list;
65*4882a593Smuzhiyun struct rcu_head rcu;
66*4882a593Smuzhiyun };
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun struct hwsim_phy {
69*4882a593Smuzhiyun struct ieee802154_hw *hw;
70*4882a593Smuzhiyun u32 idx;
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun struct hwsim_pib __rcu *pib;
73*4882a593Smuzhiyun
74*4882a593Smuzhiyun bool suspended;
75*4882a593Smuzhiyun struct list_head edges;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun struct list_head list;
78*4882a593Smuzhiyun };
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun static int hwsim_add_one(struct genl_info *info, struct device *dev,
81*4882a593Smuzhiyun bool init);
82*4882a593Smuzhiyun static void hwsim_del(struct hwsim_phy *phy);
83*4882a593Smuzhiyun
hwsim_hw_ed(struct ieee802154_hw * hw,u8 * level)84*4882a593Smuzhiyun static int hwsim_hw_ed(struct ieee802154_hw *hw, u8 *level)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun *level = 0xbe;
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun return 0;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
hwsim_hw_channel(struct ieee802154_hw * hw,u8 page,u8 channel)91*4882a593Smuzhiyun static int hwsim_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun struct hwsim_phy *phy = hw->priv;
94*4882a593Smuzhiyun struct hwsim_pib *pib, *pib_old;
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun pib = kzalloc(sizeof(*pib), GFP_KERNEL);
97*4882a593Smuzhiyun if (!pib)
98*4882a593Smuzhiyun return -ENOMEM;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun pib->page = page;
101*4882a593Smuzhiyun pib->channel = channel;
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun pib_old = rtnl_dereference(phy->pib);
104*4882a593Smuzhiyun rcu_assign_pointer(phy->pib, pib);
105*4882a593Smuzhiyun kfree_rcu(pib_old, rcu);
106*4882a593Smuzhiyun return 0;
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun
hwsim_hw_xmit(struct ieee802154_hw * hw,struct sk_buff * skb)109*4882a593Smuzhiyun static int hwsim_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun struct hwsim_phy *current_phy = hw->priv;
112*4882a593Smuzhiyun struct hwsim_pib *current_pib, *endpoint_pib;
113*4882a593Smuzhiyun struct hwsim_edge_info *einfo;
114*4882a593Smuzhiyun struct hwsim_edge *e;
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun WARN_ON(current_phy->suspended);
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun rcu_read_lock();
119*4882a593Smuzhiyun current_pib = rcu_dereference(current_phy->pib);
120*4882a593Smuzhiyun list_for_each_entry_rcu(e, ¤t_phy->edges, list) {
121*4882a593Smuzhiyun /* Can be changed later in rx_irqsafe, but this is only a
122*4882a593Smuzhiyun * performance tweak. Received radio should drop the frame
123*4882a593Smuzhiyun * in mac802154 stack anyway... so we don't need to be
124*4882a593Smuzhiyun * 100% of locking here to check on suspended
125*4882a593Smuzhiyun */
126*4882a593Smuzhiyun if (e->endpoint->suspended)
127*4882a593Smuzhiyun continue;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun endpoint_pib = rcu_dereference(e->endpoint->pib);
130*4882a593Smuzhiyun if (current_pib->page == endpoint_pib->page &&
131*4882a593Smuzhiyun current_pib->channel == endpoint_pib->channel) {
132*4882a593Smuzhiyun struct sk_buff *newskb = pskb_copy(skb, GFP_ATOMIC);
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun einfo = rcu_dereference(e->info);
135*4882a593Smuzhiyun if (newskb)
136*4882a593Smuzhiyun ieee802154_rx_irqsafe(e->endpoint->hw, newskb,
137*4882a593Smuzhiyun einfo->lqi);
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun rcu_read_unlock();
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun ieee802154_xmit_complete(hw, skb, false);
143*4882a593Smuzhiyun return 0;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun
hwsim_hw_start(struct ieee802154_hw * hw)146*4882a593Smuzhiyun static int hwsim_hw_start(struct ieee802154_hw *hw)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun struct hwsim_phy *phy = hw->priv;
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun phy->suspended = false;
151*4882a593Smuzhiyun return 0;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
hwsim_hw_stop(struct ieee802154_hw * hw)154*4882a593Smuzhiyun static void hwsim_hw_stop(struct ieee802154_hw *hw)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun struct hwsim_phy *phy = hw->priv;
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun phy->suspended = true;
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun static int
hwsim_set_promiscuous_mode(struct ieee802154_hw * hw,const bool on)162*4882a593Smuzhiyun hwsim_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on)
163*4882a593Smuzhiyun {
164*4882a593Smuzhiyun return 0;
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun static const struct ieee802154_ops hwsim_ops = {
168*4882a593Smuzhiyun .owner = THIS_MODULE,
169*4882a593Smuzhiyun .xmit_async = hwsim_hw_xmit,
170*4882a593Smuzhiyun .ed = hwsim_hw_ed,
171*4882a593Smuzhiyun .set_channel = hwsim_hw_channel,
172*4882a593Smuzhiyun .start = hwsim_hw_start,
173*4882a593Smuzhiyun .stop = hwsim_hw_stop,
174*4882a593Smuzhiyun .set_promiscuous_mode = hwsim_set_promiscuous_mode,
175*4882a593Smuzhiyun };
176*4882a593Smuzhiyun
hwsim_new_radio_nl(struct sk_buff * msg,struct genl_info * info)177*4882a593Smuzhiyun static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun return hwsim_add_one(info, &mac802154hwsim_dev->dev, false);
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun
hwsim_del_radio_nl(struct sk_buff * msg,struct genl_info * info)182*4882a593Smuzhiyun static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun struct hwsim_phy *phy, *tmp;
185*4882a593Smuzhiyun s64 idx = -1;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID])
188*4882a593Smuzhiyun return -EINVAL;
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun idx = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun mutex_lock(&hwsim_phys_lock);
193*4882a593Smuzhiyun list_for_each_entry_safe(phy, tmp, &hwsim_phys, list) {
194*4882a593Smuzhiyun if (idx == phy->idx) {
195*4882a593Smuzhiyun hwsim_del(phy);
196*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
197*4882a593Smuzhiyun return 0;
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun return -ENODEV;
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun
append_radio_msg(struct sk_buff * skb,struct hwsim_phy * phy)205*4882a593Smuzhiyun static int append_radio_msg(struct sk_buff *skb, struct hwsim_phy *phy)
206*4882a593Smuzhiyun {
207*4882a593Smuzhiyun struct nlattr *nl_edges, *nl_edge;
208*4882a593Smuzhiyun struct hwsim_edge_info *einfo;
209*4882a593Smuzhiyun struct hwsim_edge *e;
210*4882a593Smuzhiyun int ret;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun ret = nla_put_u32(skb, MAC802154_HWSIM_ATTR_RADIO_ID, phy->idx);
213*4882a593Smuzhiyun if (ret < 0)
214*4882a593Smuzhiyun return ret;
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun rcu_read_lock();
217*4882a593Smuzhiyun if (list_empty(&phy->edges)) {
218*4882a593Smuzhiyun rcu_read_unlock();
219*4882a593Smuzhiyun return 0;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun nl_edges = nla_nest_start_noflag(skb,
223*4882a593Smuzhiyun MAC802154_HWSIM_ATTR_RADIO_EDGES);
224*4882a593Smuzhiyun if (!nl_edges) {
225*4882a593Smuzhiyun rcu_read_unlock();
226*4882a593Smuzhiyun return -ENOBUFS;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun list_for_each_entry_rcu(e, &phy->edges, list) {
230*4882a593Smuzhiyun nl_edge = nla_nest_start_noflag(skb,
231*4882a593Smuzhiyun MAC802154_HWSIM_ATTR_RADIO_EDGE);
232*4882a593Smuzhiyun if (!nl_edge) {
233*4882a593Smuzhiyun rcu_read_unlock();
234*4882a593Smuzhiyun nla_nest_cancel(skb, nl_edges);
235*4882a593Smuzhiyun return -ENOBUFS;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun ret = nla_put_u32(skb, MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID,
239*4882a593Smuzhiyun e->endpoint->idx);
240*4882a593Smuzhiyun if (ret < 0) {
241*4882a593Smuzhiyun rcu_read_unlock();
242*4882a593Smuzhiyun nla_nest_cancel(skb, nl_edge);
243*4882a593Smuzhiyun nla_nest_cancel(skb, nl_edges);
244*4882a593Smuzhiyun return ret;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun einfo = rcu_dereference(e->info);
248*4882a593Smuzhiyun ret = nla_put_u8(skb, MAC802154_HWSIM_EDGE_ATTR_LQI,
249*4882a593Smuzhiyun einfo->lqi);
250*4882a593Smuzhiyun if (ret < 0) {
251*4882a593Smuzhiyun rcu_read_unlock();
252*4882a593Smuzhiyun nla_nest_cancel(skb, nl_edge);
253*4882a593Smuzhiyun nla_nest_cancel(skb, nl_edges);
254*4882a593Smuzhiyun return ret;
255*4882a593Smuzhiyun }
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun nla_nest_end(skb, nl_edge);
258*4882a593Smuzhiyun }
259*4882a593Smuzhiyun rcu_read_unlock();
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun nla_nest_end(skb, nl_edges);
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun return 0;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun
hwsim_get_radio(struct sk_buff * skb,struct hwsim_phy * phy,u32 portid,u32 seq,struct netlink_callback * cb,int flags)266*4882a593Smuzhiyun static int hwsim_get_radio(struct sk_buff *skb, struct hwsim_phy *phy,
267*4882a593Smuzhiyun u32 portid, u32 seq,
268*4882a593Smuzhiyun struct netlink_callback *cb, int flags)
269*4882a593Smuzhiyun {
270*4882a593Smuzhiyun void *hdr;
271*4882a593Smuzhiyun int res = -EMSGSIZE;
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun hdr = genlmsg_put(skb, portid, seq, &hwsim_genl_family, flags,
274*4882a593Smuzhiyun MAC802154_HWSIM_CMD_GET_RADIO);
275*4882a593Smuzhiyun if (!hdr)
276*4882a593Smuzhiyun return -EMSGSIZE;
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun if (cb)
279*4882a593Smuzhiyun genl_dump_check_consistent(cb, hdr);
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun res = append_radio_msg(skb, phy);
282*4882a593Smuzhiyun if (res < 0)
283*4882a593Smuzhiyun goto out_err;
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun genlmsg_end(skb, hdr);
286*4882a593Smuzhiyun return 0;
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun out_err:
289*4882a593Smuzhiyun genlmsg_cancel(skb, hdr);
290*4882a593Smuzhiyun return res;
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun
hwsim_get_radio_nl(struct sk_buff * msg,struct genl_info * info)293*4882a593Smuzhiyun static int hwsim_get_radio_nl(struct sk_buff *msg, struct genl_info *info)
294*4882a593Smuzhiyun {
295*4882a593Smuzhiyun struct hwsim_phy *phy;
296*4882a593Smuzhiyun struct sk_buff *skb;
297*4882a593Smuzhiyun int idx, res = -ENODEV;
298*4882a593Smuzhiyun
299*4882a593Smuzhiyun if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID])
300*4882a593Smuzhiyun return -EINVAL;
301*4882a593Smuzhiyun idx = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun mutex_lock(&hwsim_phys_lock);
304*4882a593Smuzhiyun list_for_each_entry(phy, &hwsim_phys, list) {
305*4882a593Smuzhiyun if (phy->idx != idx)
306*4882a593Smuzhiyun continue;
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
309*4882a593Smuzhiyun if (!skb) {
310*4882a593Smuzhiyun res = -ENOMEM;
311*4882a593Smuzhiyun goto out_err;
312*4882a593Smuzhiyun }
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun res = hwsim_get_radio(skb, phy, info->snd_portid,
315*4882a593Smuzhiyun info->snd_seq, NULL, 0);
316*4882a593Smuzhiyun if (res < 0) {
317*4882a593Smuzhiyun nlmsg_free(skb);
318*4882a593Smuzhiyun goto out_err;
319*4882a593Smuzhiyun }
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun res = genlmsg_reply(skb, info);
322*4882a593Smuzhiyun break;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun out_err:
326*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun return res;
329*4882a593Smuzhiyun }
330*4882a593Smuzhiyun
hwsim_dump_radio_nl(struct sk_buff * skb,struct netlink_callback * cb)331*4882a593Smuzhiyun static int hwsim_dump_radio_nl(struct sk_buff *skb,
332*4882a593Smuzhiyun struct netlink_callback *cb)
333*4882a593Smuzhiyun {
334*4882a593Smuzhiyun int idx = cb->args[0];
335*4882a593Smuzhiyun struct hwsim_phy *phy;
336*4882a593Smuzhiyun int res;
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun mutex_lock(&hwsim_phys_lock);
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun if (idx == hwsim_radio_idx)
341*4882a593Smuzhiyun goto done;
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun list_for_each_entry(phy, &hwsim_phys, list) {
344*4882a593Smuzhiyun if (phy->idx < idx)
345*4882a593Smuzhiyun continue;
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun res = hwsim_get_radio(skb, phy, NETLINK_CB(cb->skb).portid,
348*4882a593Smuzhiyun cb->nlh->nlmsg_seq, cb, NLM_F_MULTI);
349*4882a593Smuzhiyun if (res < 0)
350*4882a593Smuzhiyun break;
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun idx = phy->idx + 1;
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun cb->args[0] = idx;
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun done:
358*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
359*4882a593Smuzhiyun return skb->len;
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun
362*4882a593Smuzhiyun /* caller need to held hwsim_phys_lock */
hwsim_get_radio_by_id(uint32_t idx)363*4882a593Smuzhiyun static struct hwsim_phy *hwsim_get_radio_by_id(uint32_t idx)
364*4882a593Smuzhiyun {
365*4882a593Smuzhiyun struct hwsim_phy *phy;
366*4882a593Smuzhiyun
367*4882a593Smuzhiyun list_for_each_entry(phy, &hwsim_phys, list) {
368*4882a593Smuzhiyun if (phy->idx == idx)
369*4882a593Smuzhiyun return phy;
370*4882a593Smuzhiyun }
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun return NULL;
373*4882a593Smuzhiyun }
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun static const struct nla_policy hwsim_edge_policy[MAC802154_HWSIM_EDGE_ATTR_MAX + 1] = {
376*4882a593Smuzhiyun [MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID] = { .type = NLA_U32 },
377*4882a593Smuzhiyun [MAC802154_HWSIM_EDGE_ATTR_LQI] = { .type = NLA_U8 },
378*4882a593Smuzhiyun };
379*4882a593Smuzhiyun
hwsim_alloc_edge(struct hwsim_phy * endpoint,u8 lqi)380*4882a593Smuzhiyun static struct hwsim_edge *hwsim_alloc_edge(struct hwsim_phy *endpoint, u8 lqi)
381*4882a593Smuzhiyun {
382*4882a593Smuzhiyun struct hwsim_edge_info *einfo;
383*4882a593Smuzhiyun struct hwsim_edge *e;
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun e = kzalloc(sizeof(*e), GFP_KERNEL);
386*4882a593Smuzhiyun if (!e)
387*4882a593Smuzhiyun return NULL;
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun einfo = kzalloc(sizeof(*einfo), GFP_KERNEL);
390*4882a593Smuzhiyun if (!einfo) {
391*4882a593Smuzhiyun kfree(e);
392*4882a593Smuzhiyun return NULL;
393*4882a593Smuzhiyun }
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun einfo->lqi = 0xff;
396*4882a593Smuzhiyun rcu_assign_pointer(e->info, einfo);
397*4882a593Smuzhiyun e->endpoint = endpoint;
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun return e;
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun
hwsim_free_edge(struct hwsim_edge * e)402*4882a593Smuzhiyun static void hwsim_free_edge(struct hwsim_edge *e)
403*4882a593Smuzhiyun {
404*4882a593Smuzhiyun struct hwsim_edge_info *einfo;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun rcu_read_lock();
407*4882a593Smuzhiyun einfo = rcu_dereference(e->info);
408*4882a593Smuzhiyun rcu_read_unlock();
409*4882a593Smuzhiyun
410*4882a593Smuzhiyun kfree_rcu(einfo, rcu);
411*4882a593Smuzhiyun kfree_rcu(e, rcu);
412*4882a593Smuzhiyun }
413*4882a593Smuzhiyun
hwsim_new_edge_nl(struct sk_buff * msg,struct genl_info * info)414*4882a593Smuzhiyun static int hwsim_new_edge_nl(struct sk_buff *msg, struct genl_info *info)
415*4882a593Smuzhiyun {
416*4882a593Smuzhiyun struct nlattr *edge_attrs[MAC802154_HWSIM_EDGE_ATTR_MAX + 1];
417*4882a593Smuzhiyun struct hwsim_phy *phy_v0, *phy_v1;
418*4882a593Smuzhiyun struct hwsim_edge *e;
419*4882a593Smuzhiyun u32 v0, v1;
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID] ||
422*4882a593Smuzhiyun !info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE])
423*4882a593Smuzhiyun return -EINVAL;
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun if (nla_parse_nested_deprecated(edge_attrs, MAC802154_HWSIM_EDGE_ATTR_MAX, info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE], hwsim_edge_policy, NULL))
426*4882a593Smuzhiyun return -EINVAL;
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun if (!edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID])
429*4882a593Smuzhiyun return -EINVAL;
430*4882a593Smuzhiyun
431*4882a593Smuzhiyun v0 = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
432*4882a593Smuzhiyun v1 = nla_get_u32(edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID]);
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun if (v0 == v1)
435*4882a593Smuzhiyun return -EINVAL;
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun mutex_lock(&hwsim_phys_lock);
438*4882a593Smuzhiyun phy_v0 = hwsim_get_radio_by_id(v0);
439*4882a593Smuzhiyun if (!phy_v0) {
440*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
441*4882a593Smuzhiyun return -ENOENT;
442*4882a593Smuzhiyun }
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun phy_v1 = hwsim_get_radio_by_id(v1);
445*4882a593Smuzhiyun if (!phy_v1) {
446*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
447*4882a593Smuzhiyun return -ENOENT;
448*4882a593Smuzhiyun }
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun rcu_read_lock();
451*4882a593Smuzhiyun list_for_each_entry_rcu(e, &phy_v0->edges, list) {
452*4882a593Smuzhiyun if (e->endpoint->idx == v1) {
453*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
454*4882a593Smuzhiyun rcu_read_unlock();
455*4882a593Smuzhiyun return -EEXIST;
456*4882a593Smuzhiyun }
457*4882a593Smuzhiyun }
458*4882a593Smuzhiyun rcu_read_unlock();
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun e = hwsim_alloc_edge(phy_v1, 0xff);
461*4882a593Smuzhiyun if (!e) {
462*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
463*4882a593Smuzhiyun return -ENOMEM;
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun list_add_rcu(&e->list, &phy_v0->edges);
466*4882a593Smuzhiyun /* wait until changes are done under hwsim_phys_lock lock
467*4882a593Smuzhiyun * should prevent of calling this function twice while
468*4882a593Smuzhiyun * edges list has not the changes yet.
469*4882a593Smuzhiyun */
470*4882a593Smuzhiyun synchronize_rcu();
471*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun return 0;
474*4882a593Smuzhiyun }
475*4882a593Smuzhiyun
hwsim_del_edge_nl(struct sk_buff * msg,struct genl_info * info)476*4882a593Smuzhiyun static int hwsim_del_edge_nl(struct sk_buff *msg, struct genl_info *info)
477*4882a593Smuzhiyun {
478*4882a593Smuzhiyun struct nlattr *edge_attrs[MAC802154_HWSIM_EDGE_ATTR_MAX + 1];
479*4882a593Smuzhiyun struct hwsim_phy *phy_v0;
480*4882a593Smuzhiyun struct hwsim_edge *e;
481*4882a593Smuzhiyun u32 v0, v1;
482*4882a593Smuzhiyun
483*4882a593Smuzhiyun if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID] ||
484*4882a593Smuzhiyun !info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE])
485*4882a593Smuzhiyun return -EINVAL;
486*4882a593Smuzhiyun
487*4882a593Smuzhiyun if (nla_parse_nested_deprecated(edge_attrs, MAC802154_HWSIM_EDGE_ATTR_MAX, info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE], hwsim_edge_policy, NULL))
488*4882a593Smuzhiyun return -EINVAL;
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun if (!edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID])
491*4882a593Smuzhiyun return -EINVAL;
492*4882a593Smuzhiyun
493*4882a593Smuzhiyun v0 = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
494*4882a593Smuzhiyun v1 = nla_get_u32(edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID]);
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun mutex_lock(&hwsim_phys_lock);
497*4882a593Smuzhiyun phy_v0 = hwsim_get_radio_by_id(v0);
498*4882a593Smuzhiyun if (!phy_v0) {
499*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
500*4882a593Smuzhiyun return -ENOENT;
501*4882a593Smuzhiyun }
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun rcu_read_lock();
504*4882a593Smuzhiyun list_for_each_entry_rcu(e, &phy_v0->edges, list) {
505*4882a593Smuzhiyun if (e->endpoint->idx == v1) {
506*4882a593Smuzhiyun rcu_read_unlock();
507*4882a593Smuzhiyun list_del_rcu(&e->list);
508*4882a593Smuzhiyun hwsim_free_edge(e);
509*4882a593Smuzhiyun /* same again - wait until list changes are done */
510*4882a593Smuzhiyun synchronize_rcu();
511*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
512*4882a593Smuzhiyun return 0;
513*4882a593Smuzhiyun }
514*4882a593Smuzhiyun }
515*4882a593Smuzhiyun rcu_read_unlock();
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
518*4882a593Smuzhiyun
519*4882a593Smuzhiyun return -ENOENT;
520*4882a593Smuzhiyun }
521*4882a593Smuzhiyun
hwsim_set_edge_lqi(struct sk_buff * msg,struct genl_info * info)522*4882a593Smuzhiyun static int hwsim_set_edge_lqi(struct sk_buff *msg, struct genl_info *info)
523*4882a593Smuzhiyun {
524*4882a593Smuzhiyun struct nlattr *edge_attrs[MAC802154_HWSIM_EDGE_ATTR_MAX + 1];
525*4882a593Smuzhiyun struct hwsim_edge_info *einfo;
526*4882a593Smuzhiyun struct hwsim_phy *phy_v0;
527*4882a593Smuzhiyun struct hwsim_edge *e;
528*4882a593Smuzhiyun u32 v0, v1;
529*4882a593Smuzhiyun u8 lqi;
530*4882a593Smuzhiyun
531*4882a593Smuzhiyun if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID] ||
532*4882a593Smuzhiyun !info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE])
533*4882a593Smuzhiyun return -EINVAL;
534*4882a593Smuzhiyun
535*4882a593Smuzhiyun if (nla_parse_nested_deprecated(edge_attrs, MAC802154_HWSIM_EDGE_ATTR_MAX, info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE], hwsim_edge_policy, NULL))
536*4882a593Smuzhiyun return -EINVAL;
537*4882a593Smuzhiyun
538*4882a593Smuzhiyun if (!edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID] ||
539*4882a593Smuzhiyun !edge_attrs[MAC802154_HWSIM_EDGE_ATTR_LQI])
540*4882a593Smuzhiyun return -EINVAL;
541*4882a593Smuzhiyun
542*4882a593Smuzhiyun v0 = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
543*4882a593Smuzhiyun v1 = nla_get_u32(edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID]);
544*4882a593Smuzhiyun lqi = nla_get_u8(edge_attrs[MAC802154_HWSIM_EDGE_ATTR_LQI]);
545*4882a593Smuzhiyun
546*4882a593Smuzhiyun mutex_lock(&hwsim_phys_lock);
547*4882a593Smuzhiyun phy_v0 = hwsim_get_radio_by_id(v0);
548*4882a593Smuzhiyun if (!phy_v0) {
549*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
550*4882a593Smuzhiyun return -ENOENT;
551*4882a593Smuzhiyun }
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun einfo = kzalloc(sizeof(*einfo), GFP_KERNEL);
554*4882a593Smuzhiyun if (!einfo) {
555*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
556*4882a593Smuzhiyun return -ENOMEM;
557*4882a593Smuzhiyun }
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun rcu_read_lock();
560*4882a593Smuzhiyun list_for_each_entry_rcu(e, &phy_v0->edges, list) {
561*4882a593Smuzhiyun if (e->endpoint->idx == v1) {
562*4882a593Smuzhiyun einfo->lqi = lqi;
563*4882a593Smuzhiyun rcu_assign_pointer(e->info, einfo);
564*4882a593Smuzhiyun rcu_read_unlock();
565*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
566*4882a593Smuzhiyun return 0;
567*4882a593Smuzhiyun }
568*4882a593Smuzhiyun }
569*4882a593Smuzhiyun rcu_read_unlock();
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun kfree(einfo);
572*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun return -ENOENT;
575*4882a593Smuzhiyun }
576*4882a593Smuzhiyun
577*4882a593Smuzhiyun /* MAC802154_HWSIM netlink policy */
578*4882a593Smuzhiyun
579*4882a593Smuzhiyun static const struct nla_policy hwsim_genl_policy[MAC802154_HWSIM_ATTR_MAX + 1] = {
580*4882a593Smuzhiyun [MAC802154_HWSIM_ATTR_RADIO_ID] = { .type = NLA_U32 },
581*4882a593Smuzhiyun [MAC802154_HWSIM_ATTR_RADIO_EDGE] = { .type = NLA_NESTED },
582*4882a593Smuzhiyun [MAC802154_HWSIM_ATTR_RADIO_EDGES] = { .type = NLA_NESTED },
583*4882a593Smuzhiyun };
584*4882a593Smuzhiyun
585*4882a593Smuzhiyun /* Generic Netlink operations array */
586*4882a593Smuzhiyun static const struct genl_small_ops hwsim_nl_ops[] = {
587*4882a593Smuzhiyun {
588*4882a593Smuzhiyun .cmd = MAC802154_HWSIM_CMD_NEW_RADIO,
589*4882a593Smuzhiyun .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
590*4882a593Smuzhiyun .doit = hwsim_new_radio_nl,
591*4882a593Smuzhiyun .flags = GENL_UNS_ADMIN_PERM,
592*4882a593Smuzhiyun },
593*4882a593Smuzhiyun {
594*4882a593Smuzhiyun .cmd = MAC802154_HWSIM_CMD_DEL_RADIO,
595*4882a593Smuzhiyun .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
596*4882a593Smuzhiyun .doit = hwsim_del_radio_nl,
597*4882a593Smuzhiyun .flags = GENL_UNS_ADMIN_PERM,
598*4882a593Smuzhiyun },
599*4882a593Smuzhiyun {
600*4882a593Smuzhiyun .cmd = MAC802154_HWSIM_CMD_GET_RADIO,
601*4882a593Smuzhiyun .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
602*4882a593Smuzhiyun .doit = hwsim_get_radio_nl,
603*4882a593Smuzhiyun .dumpit = hwsim_dump_radio_nl,
604*4882a593Smuzhiyun },
605*4882a593Smuzhiyun {
606*4882a593Smuzhiyun .cmd = MAC802154_HWSIM_CMD_NEW_EDGE,
607*4882a593Smuzhiyun .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
608*4882a593Smuzhiyun .doit = hwsim_new_edge_nl,
609*4882a593Smuzhiyun .flags = GENL_UNS_ADMIN_PERM,
610*4882a593Smuzhiyun },
611*4882a593Smuzhiyun {
612*4882a593Smuzhiyun .cmd = MAC802154_HWSIM_CMD_DEL_EDGE,
613*4882a593Smuzhiyun .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
614*4882a593Smuzhiyun .doit = hwsim_del_edge_nl,
615*4882a593Smuzhiyun .flags = GENL_UNS_ADMIN_PERM,
616*4882a593Smuzhiyun },
617*4882a593Smuzhiyun {
618*4882a593Smuzhiyun .cmd = MAC802154_HWSIM_CMD_SET_EDGE,
619*4882a593Smuzhiyun .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
620*4882a593Smuzhiyun .doit = hwsim_set_edge_lqi,
621*4882a593Smuzhiyun .flags = GENL_UNS_ADMIN_PERM,
622*4882a593Smuzhiyun },
623*4882a593Smuzhiyun };
624*4882a593Smuzhiyun
625*4882a593Smuzhiyun static struct genl_family hwsim_genl_family __ro_after_init = {
626*4882a593Smuzhiyun .name = "MAC802154_HWSIM",
627*4882a593Smuzhiyun .version = 1,
628*4882a593Smuzhiyun .maxattr = MAC802154_HWSIM_ATTR_MAX,
629*4882a593Smuzhiyun .policy = hwsim_genl_policy,
630*4882a593Smuzhiyun .module = THIS_MODULE,
631*4882a593Smuzhiyun .small_ops = hwsim_nl_ops,
632*4882a593Smuzhiyun .n_small_ops = ARRAY_SIZE(hwsim_nl_ops),
633*4882a593Smuzhiyun .mcgrps = hwsim_mcgrps,
634*4882a593Smuzhiyun .n_mcgrps = ARRAY_SIZE(hwsim_mcgrps),
635*4882a593Smuzhiyun };
636*4882a593Smuzhiyun
hwsim_mcast_config_msg(struct sk_buff * mcast_skb,struct genl_info * info)637*4882a593Smuzhiyun static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
638*4882a593Smuzhiyun struct genl_info *info)
639*4882a593Smuzhiyun {
640*4882a593Smuzhiyun if (info)
641*4882a593Smuzhiyun genl_notify(&hwsim_genl_family, mcast_skb, info,
642*4882a593Smuzhiyun HWSIM_MCGRP_CONFIG, GFP_KERNEL);
643*4882a593Smuzhiyun else
644*4882a593Smuzhiyun genlmsg_multicast(&hwsim_genl_family, mcast_skb, 0,
645*4882a593Smuzhiyun HWSIM_MCGRP_CONFIG, GFP_KERNEL);
646*4882a593Smuzhiyun }
647*4882a593Smuzhiyun
hwsim_mcast_new_radio(struct genl_info * info,struct hwsim_phy * phy)648*4882a593Smuzhiyun static void hwsim_mcast_new_radio(struct genl_info *info, struct hwsim_phy *phy)
649*4882a593Smuzhiyun {
650*4882a593Smuzhiyun struct sk_buff *mcast_skb;
651*4882a593Smuzhiyun void *data;
652*4882a593Smuzhiyun
653*4882a593Smuzhiyun mcast_skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
654*4882a593Smuzhiyun if (!mcast_skb)
655*4882a593Smuzhiyun return;
656*4882a593Smuzhiyun
657*4882a593Smuzhiyun data = genlmsg_put(mcast_skb, 0, 0, &hwsim_genl_family, 0,
658*4882a593Smuzhiyun MAC802154_HWSIM_CMD_NEW_RADIO);
659*4882a593Smuzhiyun if (!data)
660*4882a593Smuzhiyun goto out_err;
661*4882a593Smuzhiyun
662*4882a593Smuzhiyun if (append_radio_msg(mcast_skb, phy) < 0)
663*4882a593Smuzhiyun goto out_err;
664*4882a593Smuzhiyun
665*4882a593Smuzhiyun genlmsg_end(mcast_skb, data);
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun hwsim_mcast_config_msg(mcast_skb, info);
668*4882a593Smuzhiyun return;
669*4882a593Smuzhiyun
670*4882a593Smuzhiyun out_err:
671*4882a593Smuzhiyun genlmsg_cancel(mcast_skb, data);
672*4882a593Smuzhiyun nlmsg_free(mcast_skb);
673*4882a593Smuzhiyun }
674*4882a593Smuzhiyun
hwsim_edge_unsubscribe_me(struct hwsim_phy * phy)675*4882a593Smuzhiyun static void hwsim_edge_unsubscribe_me(struct hwsim_phy *phy)
676*4882a593Smuzhiyun {
677*4882a593Smuzhiyun struct hwsim_phy *tmp;
678*4882a593Smuzhiyun struct hwsim_edge *e;
679*4882a593Smuzhiyun
680*4882a593Smuzhiyun rcu_read_lock();
681*4882a593Smuzhiyun /* going to all phy edges and remove phy from it */
682*4882a593Smuzhiyun list_for_each_entry(tmp, &hwsim_phys, list) {
683*4882a593Smuzhiyun list_for_each_entry_rcu(e, &tmp->edges, list) {
684*4882a593Smuzhiyun if (e->endpoint->idx == phy->idx) {
685*4882a593Smuzhiyun list_del_rcu(&e->list);
686*4882a593Smuzhiyun hwsim_free_edge(e);
687*4882a593Smuzhiyun }
688*4882a593Smuzhiyun }
689*4882a593Smuzhiyun }
690*4882a593Smuzhiyun rcu_read_unlock();
691*4882a593Smuzhiyun
692*4882a593Smuzhiyun synchronize_rcu();
693*4882a593Smuzhiyun }
694*4882a593Smuzhiyun
hwsim_subscribe_all_others(struct hwsim_phy * phy)695*4882a593Smuzhiyun static int hwsim_subscribe_all_others(struct hwsim_phy *phy)
696*4882a593Smuzhiyun {
697*4882a593Smuzhiyun struct hwsim_phy *sub;
698*4882a593Smuzhiyun struct hwsim_edge *e;
699*4882a593Smuzhiyun
700*4882a593Smuzhiyun list_for_each_entry(sub, &hwsim_phys, list) {
701*4882a593Smuzhiyun e = hwsim_alloc_edge(sub, 0xff);
702*4882a593Smuzhiyun if (!e)
703*4882a593Smuzhiyun goto me_fail;
704*4882a593Smuzhiyun
705*4882a593Smuzhiyun list_add_rcu(&e->list, &phy->edges);
706*4882a593Smuzhiyun }
707*4882a593Smuzhiyun
708*4882a593Smuzhiyun list_for_each_entry(sub, &hwsim_phys, list) {
709*4882a593Smuzhiyun e = hwsim_alloc_edge(phy, 0xff);
710*4882a593Smuzhiyun if (!e)
711*4882a593Smuzhiyun goto sub_fail;
712*4882a593Smuzhiyun
713*4882a593Smuzhiyun list_add_rcu(&e->list, &sub->edges);
714*4882a593Smuzhiyun }
715*4882a593Smuzhiyun
716*4882a593Smuzhiyun return 0;
717*4882a593Smuzhiyun
718*4882a593Smuzhiyun sub_fail:
719*4882a593Smuzhiyun hwsim_edge_unsubscribe_me(phy);
720*4882a593Smuzhiyun me_fail:
721*4882a593Smuzhiyun rcu_read_lock();
722*4882a593Smuzhiyun list_for_each_entry_rcu(e, &phy->edges, list) {
723*4882a593Smuzhiyun list_del_rcu(&e->list);
724*4882a593Smuzhiyun hwsim_free_edge(e);
725*4882a593Smuzhiyun }
726*4882a593Smuzhiyun rcu_read_unlock();
727*4882a593Smuzhiyun return -ENOMEM;
728*4882a593Smuzhiyun }
729*4882a593Smuzhiyun
hwsim_add_one(struct genl_info * info,struct device * dev,bool init)730*4882a593Smuzhiyun static int hwsim_add_one(struct genl_info *info, struct device *dev,
731*4882a593Smuzhiyun bool init)
732*4882a593Smuzhiyun {
733*4882a593Smuzhiyun struct ieee802154_hw *hw;
734*4882a593Smuzhiyun struct hwsim_phy *phy;
735*4882a593Smuzhiyun struct hwsim_pib *pib;
736*4882a593Smuzhiyun int idx;
737*4882a593Smuzhiyun int err;
738*4882a593Smuzhiyun
739*4882a593Smuzhiyun idx = hwsim_radio_idx++;
740*4882a593Smuzhiyun
741*4882a593Smuzhiyun hw = ieee802154_alloc_hw(sizeof(*phy), &hwsim_ops);
742*4882a593Smuzhiyun if (!hw)
743*4882a593Smuzhiyun return -ENOMEM;
744*4882a593Smuzhiyun
745*4882a593Smuzhiyun phy = hw->priv;
746*4882a593Smuzhiyun phy->hw = hw;
747*4882a593Smuzhiyun
748*4882a593Smuzhiyun /* 868 MHz BPSK 802.15.4-2003 */
749*4882a593Smuzhiyun hw->phy->supported.channels[0] |= 1;
750*4882a593Smuzhiyun /* 915 MHz BPSK 802.15.4-2003 */
751*4882a593Smuzhiyun hw->phy->supported.channels[0] |= 0x7fe;
752*4882a593Smuzhiyun /* 2.4 GHz O-QPSK 802.15.4-2003 */
753*4882a593Smuzhiyun hw->phy->supported.channels[0] |= 0x7FFF800;
754*4882a593Smuzhiyun /* 868 MHz ASK 802.15.4-2006 */
755*4882a593Smuzhiyun hw->phy->supported.channels[1] |= 1;
756*4882a593Smuzhiyun /* 915 MHz ASK 802.15.4-2006 */
757*4882a593Smuzhiyun hw->phy->supported.channels[1] |= 0x7fe;
758*4882a593Smuzhiyun /* 868 MHz O-QPSK 802.15.4-2006 */
759*4882a593Smuzhiyun hw->phy->supported.channels[2] |= 1;
760*4882a593Smuzhiyun /* 915 MHz O-QPSK 802.15.4-2006 */
761*4882a593Smuzhiyun hw->phy->supported.channels[2] |= 0x7fe;
762*4882a593Smuzhiyun /* 2.4 GHz CSS 802.15.4a-2007 */
763*4882a593Smuzhiyun hw->phy->supported.channels[3] |= 0x3fff;
764*4882a593Smuzhiyun /* UWB Sub-gigahertz 802.15.4a-2007 */
765*4882a593Smuzhiyun hw->phy->supported.channels[4] |= 1;
766*4882a593Smuzhiyun /* UWB Low band 802.15.4a-2007 */
767*4882a593Smuzhiyun hw->phy->supported.channels[4] |= 0x1e;
768*4882a593Smuzhiyun /* UWB High band 802.15.4a-2007 */
769*4882a593Smuzhiyun hw->phy->supported.channels[4] |= 0xffe0;
770*4882a593Smuzhiyun /* 750 MHz O-QPSK 802.15.4c-2009 */
771*4882a593Smuzhiyun hw->phy->supported.channels[5] |= 0xf;
772*4882a593Smuzhiyun /* 750 MHz MPSK 802.15.4c-2009 */
773*4882a593Smuzhiyun hw->phy->supported.channels[5] |= 0xf0;
774*4882a593Smuzhiyun /* 950 MHz BPSK 802.15.4d-2009 */
775*4882a593Smuzhiyun hw->phy->supported.channels[6] |= 0x3ff;
776*4882a593Smuzhiyun /* 950 MHz GFSK 802.15.4d-2009 */
777*4882a593Smuzhiyun hw->phy->supported.channels[6] |= 0x3ffc00;
778*4882a593Smuzhiyun
779*4882a593Smuzhiyun ieee802154_random_extended_addr(&hw->phy->perm_extended_addr);
780*4882a593Smuzhiyun
781*4882a593Smuzhiyun /* hwsim phy channel 13 as default */
782*4882a593Smuzhiyun hw->phy->current_channel = 13;
783*4882a593Smuzhiyun pib = kzalloc(sizeof(*pib), GFP_KERNEL);
784*4882a593Smuzhiyun if (!pib) {
785*4882a593Smuzhiyun err = -ENOMEM;
786*4882a593Smuzhiyun goto err_pib;
787*4882a593Smuzhiyun }
788*4882a593Smuzhiyun
789*4882a593Smuzhiyun pib->channel = 13;
790*4882a593Smuzhiyun rcu_assign_pointer(phy->pib, pib);
791*4882a593Smuzhiyun phy->idx = idx;
792*4882a593Smuzhiyun INIT_LIST_HEAD(&phy->edges);
793*4882a593Smuzhiyun
794*4882a593Smuzhiyun hw->flags = IEEE802154_HW_PROMISCUOUS;
795*4882a593Smuzhiyun hw->parent = dev;
796*4882a593Smuzhiyun
797*4882a593Smuzhiyun err = ieee802154_register_hw(hw);
798*4882a593Smuzhiyun if (err)
799*4882a593Smuzhiyun goto err_reg;
800*4882a593Smuzhiyun
801*4882a593Smuzhiyun mutex_lock(&hwsim_phys_lock);
802*4882a593Smuzhiyun if (init) {
803*4882a593Smuzhiyun err = hwsim_subscribe_all_others(phy);
804*4882a593Smuzhiyun if (err < 0) {
805*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
806*4882a593Smuzhiyun goto err_subscribe;
807*4882a593Smuzhiyun }
808*4882a593Smuzhiyun }
809*4882a593Smuzhiyun list_add_tail(&phy->list, &hwsim_phys);
810*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
811*4882a593Smuzhiyun
812*4882a593Smuzhiyun hwsim_mcast_new_radio(info, phy);
813*4882a593Smuzhiyun
814*4882a593Smuzhiyun return idx;
815*4882a593Smuzhiyun
816*4882a593Smuzhiyun err_subscribe:
817*4882a593Smuzhiyun ieee802154_unregister_hw(phy->hw);
818*4882a593Smuzhiyun err_reg:
819*4882a593Smuzhiyun kfree(pib);
820*4882a593Smuzhiyun err_pib:
821*4882a593Smuzhiyun ieee802154_free_hw(phy->hw);
822*4882a593Smuzhiyun return err;
823*4882a593Smuzhiyun }
824*4882a593Smuzhiyun
hwsim_del(struct hwsim_phy * phy)825*4882a593Smuzhiyun static void hwsim_del(struct hwsim_phy *phy)
826*4882a593Smuzhiyun {
827*4882a593Smuzhiyun struct hwsim_pib *pib;
828*4882a593Smuzhiyun struct hwsim_edge *e;
829*4882a593Smuzhiyun
830*4882a593Smuzhiyun hwsim_edge_unsubscribe_me(phy);
831*4882a593Smuzhiyun
832*4882a593Smuzhiyun list_del(&phy->list);
833*4882a593Smuzhiyun
834*4882a593Smuzhiyun rcu_read_lock();
835*4882a593Smuzhiyun list_for_each_entry_rcu(e, &phy->edges, list) {
836*4882a593Smuzhiyun list_del_rcu(&e->list);
837*4882a593Smuzhiyun hwsim_free_edge(e);
838*4882a593Smuzhiyun }
839*4882a593Smuzhiyun pib = rcu_dereference(phy->pib);
840*4882a593Smuzhiyun rcu_read_unlock();
841*4882a593Smuzhiyun
842*4882a593Smuzhiyun kfree_rcu(pib, rcu);
843*4882a593Smuzhiyun
844*4882a593Smuzhiyun ieee802154_unregister_hw(phy->hw);
845*4882a593Smuzhiyun ieee802154_free_hw(phy->hw);
846*4882a593Smuzhiyun }
847*4882a593Smuzhiyun
hwsim_probe(struct platform_device * pdev)848*4882a593Smuzhiyun static int hwsim_probe(struct platform_device *pdev)
849*4882a593Smuzhiyun {
850*4882a593Smuzhiyun struct hwsim_phy *phy, *tmp;
851*4882a593Smuzhiyun int err, i;
852*4882a593Smuzhiyun
853*4882a593Smuzhiyun for (i = 0; i < 2; i++) {
854*4882a593Smuzhiyun err = hwsim_add_one(NULL, &pdev->dev, true);
855*4882a593Smuzhiyun if (err < 0)
856*4882a593Smuzhiyun goto err_slave;
857*4882a593Smuzhiyun }
858*4882a593Smuzhiyun
859*4882a593Smuzhiyun dev_info(&pdev->dev, "Added 2 mac802154 hwsim hardware radios\n");
860*4882a593Smuzhiyun return 0;
861*4882a593Smuzhiyun
862*4882a593Smuzhiyun err_slave:
863*4882a593Smuzhiyun mutex_lock(&hwsim_phys_lock);
864*4882a593Smuzhiyun list_for_each_entry_safe(phy, tmp, &hwsim_phys, list)
865*4882a593Smuzhiyun hwsim_del(phy);
866*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
867*4882a593Smuzhiyun return err;
868*4882a593Smuzhiyun }
869*4882a593Smuzhiyun
hwsim_remove(struct platform_device * pdev)870*4882a593Smuzhiyun static int hwsim_remove(struct platform_device *pdev)
871*4882a593Smuzhiyun {
872*4882a593Smuzhiyun struct hwsim_phy *phy, *tmp;
873*4882a593Smuzhiyun
874*4882a593Smuzhiyun mutex_lock(&hwsim_phys_lock);
875*4882a593Smuzhiyun list_for_each_entry_safe(phy, tmp, &hwsim_phys, list)
876*4882a593Smuzhiyun hwsim_del(phy);
877*4882a593Smuzhiyun mutex_unlock(&hwsim_phys_lock);
878*4882a593Smuzhiyun
879*4882a593Smuzhiyun return 0;
880*4882a593Smuzhiyun }
881*4882a593Smuzhiyun
882*4882a593Smuzhiyun static struct platform_driver mac802154hwsim_driver = {
883*4882a593Smuzhiyun .probe = hwsim_probe,
884*4882a593Smuzhiyun .remove = hwsim_remove,
885*4882a593Smuzhiyun .driver = {
886*4882a593Smuzhiyun .name = "mac802154_hwsim",
887*4882a593Smuzhiyun },
888*4882a593Smuzhiyun };
889*4882a593Smuzhiyun
hwsim_init_module(void)890*4882a593Smuzhiyun static __init int hwsim_init_module(void)
891*4882a593Smuzhiyun {
892*4882a593Smuzhiyun int rc;
893*4882a593Smuzhiyun
894*4882a593Smuzhiyun rc = genl_register_family(&hwsim_genl_family);
895*4882a593Smuzhiyun if (rc)
896*4882a593Smuzhiyun return rc;
897*4882a593Smuzhiyun
898*4882a593Smuzhiyun mac802154hwsim_dev = platform_device_register_simple("mac802154_hwsim",
899*4882a593Smuzhiyun -1, NULL, 0);
900*4882a593Smuzhiyun if (IS_ERR(mac802154hwsim_dev)) {
901*4882a593Smuzhiyun rc = PTR_ERR(mac802154hwsim_dev);
902*4882a593Smuzhiyun goto platform_dev;
903*4882a593Smuzhiyun }
904*4882a593Smuzhiyun
905*4882a593Smuzhiyun rc = platform_driver_register(&mac802154hwsim_driver);
906*4882a593Smuzhiyun if (rc < 0)
907*4882a593Smuzhiyun goto platform_drv;
908*4882a593Smuzhiyun
909*4882a593Smuzhiyun return 0;
910*4882a593Smuzhiyun
911*4882a593Smuzhiyun platform_drv:
912*4882a593Smuzhiyun platform_device_unregister(mac802154hwsim_dev);
913*4882a593Smuzhiyun platform_dev:
914*4882a593Smuzhiyun genl_unregister_family(&hwsim_genl_family);
915*4882a593Smuzhiyun return rc;
916*4882a593Smuzhiyun }
917*4882a593Smuzhiyun
hwsim_remove_module(void)918*4882a593Smuzhiyun static __exit void hwsim_remove_module(void)
919*4882a593Smuzhiyun {
920*4882a593Smuzhiyun genl_unregister_family(&hwsim_genl_family);
921*4882a593Smuzhiyun platform_driver_unregister(&mac802154hwsim_driver);
922*4882a593Smuzhiyun platform_device_unregister(mac802154hwsim_dev);
923*4882a593Smuzhiyun }
924*4882a593Smuzhiyun
925*4882a593Smuzhiyun module_init(hwsim_init_module);
926*4882a593Smuzhiyun module_exit(hwsim_remove_module);
927