aboutsummaryrefslogtreecommitdiffstats
path: root/net/ieee802154
diff options
context:
space:
mode:
Diffstat (limited to 'net/ieee802154')
-rw-r--r--net/ieee802154/6lowpan_rtnl.c38
-rw-r--r--net/ieee802154/Makefile2
-rw-r--r--net/ieee802154/core.c220
-rw-r--r--net/ieee802154/core.h46
-rw-r--r--net/ieee802154/ieee802154.h2
-rw-r--r--net/ieee802154/netlink.c2
-rw-r--r--net/ieee802154/nl-mac.c226
-rw-r--r--net/ieee802154/nl-phy.c23
-rw-r--r--net/ieee802154/nl802154.c957
-rw-r--r--net/ieee802154/nl802154.h7
-rw-r--r--net/ieee802154/rdev-ops.h89
-rw-r--r--net/ieee802154/sysfs.c40
12 files changed, 1382 insertions, 270 deletions
diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c
index 519a65452d90..290e14f2e92e 100644
--- a/net/ieee802154/6lowpan_rtnl.c
+++ b/net/ieee802154/6lowpan_rtnl.c
@@ -176,13 +176,13 @@ iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
176 raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len); 176 raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len);
177 /* at least two bytes will be used for the encoding */ 177 /* at least two bytes will be used for the encoding */
178 if (skb->len < 2) 178 if (skb->len < 2)
179 goto drop; 179 return -EINVAL;
180 180
181 if (lowpan_fetch_skb_u8(skb, &iphc0)) 181 if (lowpan_fetch_skb_u8(skb, &iphc0))
182 goto drop; 182 return -EINVAL;
183 183
184 if (lowpan_fetch_skb_u8(skb, &iphc1)) 184 if (lowpan_fetch_skb_u8(skb, &iphc1))
185 goto drop; 185 return -EINVAL;
186 186
187 ieee802154_addr_to_sa(&sa, &hdr->source); 187 ieee802154_addr_to_sa(&sa, &hdr->source);
188 ieee802154_addr_to_sa(&da, &hdr->dest); 188 ieee802154_addr_to_sa(&da, &hdr->dest);
@@ -200,23 +200,6 @@ iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
200 return lowpan_header_decompress(skb, skb->dev, sap, sa.addr_type, 200 return lowpan_header_decompress(skb, skb->dev, sap, sa.addr_type,
201 IEEE802154_ADDR_LEN, dap, da.addr_type, 201 IEEE802154_ADDR_LEN, dap, da.addr_type,
202 IEEE802154_ADDR_LEN, iphc0, iphc1); 202 IEEE802154_ADDR_LEN, iphc0, iphc1);
203
204drop:
205 kfree_skb(skb);
206 return -EINVAL;
207}
208
209static int lowpan_set_address(struct net_device *dev, void *p)
210{
211 struct sockaddr *sa = p;
212
213 if (netif_running(dev))
214 return -EBUSY;
215
216 /* TODO: validate addr */
217 memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
218
219 return 0;
220} 203}
221 204
222static struct sk_buff* 205static struct sk_buff*
@@ -420,13 +403,6 @@ static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
420 } 403 }
421} 404}
422 405
423static struct wpan_phy *lowpan_get_phy(const struct net_device *dev)
424{
425 struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
426
427 return ieee802154_mlme_ops(real_dev)->get_phy(real_dev);
428}
429
430static __le16 lowpan_get_pan_id(const struct net_device *dev) 406static __le16 lowpan_get_pan_id(const struct net_device *dev)
431{ 407{
432 struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; 408 struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
@@ -474,12 +450,10 @@ static int lowpan_dev_init(struct net_device *dev)
474static const struct net_device_ops lowpan_netdev_ops = { 450static const struct net_device_ops lowpan_netdev_ops = {
475 .ndo_init = lowpan_dev_init, 451 .ndo_init = lowpan_dev_init,
476 .ndo_start_xmit = lowpan_xmit, 452 .ndo_start_xmit = lowpan_xmit,
477 .ndo_set_mac_address = lowpan_set_address,
478}; 453};
479 454
480static struct ieee802154_mlme_ops lowpan_mlme = { 455static struct ieee802154_mlme_ops lowpan_mlme = {
481 .get_pan_id = lowpan_get_pan_id, 456 .get_pan_id = lowpan_get_pan_id,
482 .get_phy = lowpan_get_phy,
483 .get_short_addr = lowpan_get_short_addr, 457 .get_short_addr = lowpan_get_short_addr,
484 .get_dsn = lowpan_get_dsn, 458 .get_dsn = lowpan_get_dsn,
485}; 459};
@@ -544,7 +518,7 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
544 case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ 518 case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */
545 ret = iphc_decompress(skb, &hdr); 519 ret = iphc_decompress(skb, &hdr);
546 if (ret < 0) 520 if (ret < 0)
547 goto drop; 521 goto drop_skb;
548 522
549 return lowpan_give_skb_to_devices(skb, NULL); 523 return lowpan_give_skb_to_devices(skb, NULL);
550 case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ 524 case LOWPAN_DISPATCH_FRAG1: /* first fragment header */
@@ -552,7 +526,7 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
552 if (ret == 1) { 526 if (ret == 1) {
553 ret = iphc_decompress(skb, &hdr); 527 ret = iphc_decompress(skb, &hdr);
554 if (ret < 0) 528 if (ret < 0)
555 goto drop; 529 goto drop_skb;
556 530
557 return lowpan_give_skb_to_devices(skb, NULL); 531 return lowpan_give_skb_to_devices(skb, NULL);
558 } else if (ret == -1) { 532 } else if (ret == -1) {
@@ -565,7 +539,7 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
565 if (ret == 1) { 539 if (ret == 1) {
566 ret = iphc_decompress(skb, &hdr); 540 ret = iphc_decompress(skb, &hdr);
567 if (ret < 0) 541 if (ret < 0)
568 goto drop; 542 goto drop_skb;
569 543
570 return lowpan_give_skb_to_devices(skb, NULL); 544 return lowpan_give_skb_to_devices(skb, NULL);
571 } else if (ret == -1) { 545 } else if (ret == -1) {
diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile
index 38354d4a70cb..9f6970f2a28b 100644
--- a/net/ieee802154/Makefile
+++ b/net/ieee802154/Makefile
@@ -3,7 +3,7 @@ obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o
3 3
4ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o 4ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o
5ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \ 5ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \
6 header_ops.o sysfs.o 6 header_ops.o sysfs.o nl802154.o
7af_802154-y := af_ieee802154.o raw.o dgram.o 7af_802154-y := af_ieee802154.o raw.o dgram.o
8 8
9ccflags-y += -D__CHECK_ENDIAN__ 9ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c
index 620abc2ba5fc..18bc7e738507 100644
--- a/net/ieee802154/core.c
+++ b/net/ieee802154/core.c
@@ -18,12 +18,16 @@
18#include <linux/device.h> 18#include <linux/device.h>
19 19
20#include <net/cfg802154.h> 20#include <net/cfg802154.h>
21#include <net/rtnetlink.h>
21 22
22#include "ieee802154.h" 23#include "ieee802154.h"
24#include "nl802154.h"
23#include "sysfs.h" 25#include "sysfs.h"
26#include "core.h"
24 27
25static DEFINE_MUTEX(wpan_phy_mutex); 28/* RCU-protected (and RTNL for writers) */
26static int wpan_phy_idx; 29LIST_HEAD(cfg802154_rdev_list);
30int cfg802154_rdev_list_generation;
27 31
28static int wpan_phy_match(struct device *dev, const void *data) 32static int wpan_phy_match(struct device *dev, const void *data)
29{ 33{
@@ -71,54 +75,116 @@ int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
71} 75}
72EXPORT_SYMBOL(wpan_phy_for_each); 76EXPORT_SYMBOL(wpan_phy_for_each);
73 77
74static int wpan_phy_idx_valid(int idx) 78struct cfg802154_registered_device *
79cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx)
75{ 80{
76 return idx >= 0; 81 struct cfg802154_registered_device *result = NULL, *rdev;
82
83 ASSERT_RTNL();
84
85 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
86 if (rdev->wpan_phy_idx == wpan_phy_idx) {
87 result = rdev;
88 break;
89 }
90 }
91
92 return result;
77} 93}
78 94
79struct wpan_phy *wpan_phy_alloc(size_t priv_size) 95struct wpan_phy *
96wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
80{ 97{
81 struct wpan_phy *phy = kzalloc(sizeof(*phy) + priv_size, 98 static atomic_t wpan_phy_counter = ATOMIC_INIT(0);
82 GFP_KERNEL); 99 struct cfg802154_registered_device *rdev;
83 100 size_t alloc_size;
84 if (!phy) 101
85 goto out; 102 alloc_size = sizeof(*rdev) + priv_size;
86 mutex_lock(&wpan_phy_mutex); 103 rdev = kzalloc(alloc_size, GFP_KERNEL);
87 phy->idx = wpan_phy_idx++; 104 if (!rdev)
88 if (unlikely(!wpan_phy_idx_valid(phy->idx))) { 105 return NULL;
89 wpan_phy_idx--; 106
90 mutex_unlock(&wpan_phy_mutex); 107 rdev->ops = ops;
91 kfree(phy); 108
92 goto out; 109 rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter);
110
111 if (unlikely(rdev->wpan_phy_idx < 0)) {
112 /* ugh, wrapped! */
113 atomic_dec(&wpan_phy_counter);
114 kfree(rdev);
115 return NULL;
93 } 116 }
94 mutex_unlock(&wpan_phy_mutex);
95 117
96 mutex_init(&phy->pib_lock); 118 /* atomic_inc_return makes it start at 1, make it start at 0 */
119 rdev->wpan_phy_idx--;
97 120
98 device_initialize(&phy->dev); 121 mutex_init(&rdev->wpan_phy.pib_lock);
99 dev_set_name(&phy->dev, "wpan-phy%d", phy->idx);
100 122
101 phy->dev.class = &wpan_phy_class; 123 INIT_LIST_HEAD(&rdev->wpan_dev_list);
124 device_initialize(&rdev->wpan_phy.dev);
125 dev_set_name(&rdev->wpan_phy.dev, "wpan-phy%d", rdev->wpan_phy_idx);
102 126
103 phy->current_channel = -1; /* not initialised */ 127 rdev->wpan_phy.dev.class = &wpan_phy_class;
104 phy->current_page = 0; /* for compatibility */ 128 rdev->wpan_phy.dev.platform_data = rdev;
105 129
106 return phy; 130 init_waitqueue_head(&rdev->dev_wait);
107 131
108out: 132 return &rdev->wpan_phy;
109 return NULL;
110} 133}
111EXPORT_SYMBOL(wpan_phy_alloc); 134EXPORT_SYMBOL(wpan_phy_new);
112 135
113int wpan_phy_register(struct wpan_phy *phy) 136int wpan_phy_register(struct wpan_phy *phy)
114{ 137{
115 return device_add(&phy->dev); 138 struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
139 int ret;
140
141 rtnl_lock();
142 ret = device_add(&phy->dev);
143 if (ret) {
144 rtnl_unlock();
145 return ret;
146 }
147
148 list_add_rcu(&rdev->list, &cfg802154_rdev_list);
149 cfg802154_rdev_list_generation++;
150
151 /* TODO phy registered lock */
152 rtnl_unlock();
153
154 /* TODO nl802154 phy notify */
155
156 return 0;
116} 157}
117EXPORT_SYMBOL(wpan_phy_register); 158EXPORT_SYMBOL(wpan_phy_register);
118 159
119void wpan_phy_unregister(struct wpan_phy *phy) 160void wpan_phy_unregister(struct wpan_phy *phy)
120{ 161{
162 struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
163
164 wait_event(rdev->dev_wait, ({
165 int __count;
166 rtnl_lock();
167 __count = rdev->opencount;
168 rtnl_unlock();
169 __count == 0; }));
170
171 rtnl_lock();
172 /* TODO nl802154 phy notify */
173 /* TODO phy registered lock */
174
175 WARN_ON(!list_empty(&rdev->wpan_dev_list));
176
177 /* First remove the hardware from everywhere, this makes
178 * it impossible to find from userspace.
179 */
180 list_del_rcu(&rdev->list);
181 synchronize_rcu();
182
183 cfg802154_rdev_list_generation++;
184
121 device_del(&phy->dev); 185 device_del(&phy->dev);
186
187 rtnl_unlock();
122} 188}
123EXPORT_SYMBOL(wpan_phy_unregister); 189EXPORT_SYMBOL(wpan_phy_unregister);
124 190
@@ -128,6 +194,84 @@ void wpan_phy_free(struct wpan_phy *phy)
128} 194}
129EXPORT_SYMBOL(wpan_phy_free); 195EXPORT_SYMBOL(wpan_phy_free);
130 196
197void cfg802154_dev_free(struct cfg802154_registered_device *rdev)
198{
199 kfree(rdev);
200}
201
202static void
203cfg802154_update_iface_num(struct cfg802154_registered_device *rdev,
204 int iftype, int num)
205{
206 ASSERT_RTNL();
207
208 rdev->num_running_ifaces += num;
209}
210
211static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
212 unsigned long state, void *ptr)
213{
214 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
215 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
216 struct cfg802154_registered_device *rdev;
217
218 if (!wpan_dev)
219 return NOTIFY_DONE;
220
221 rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
222
223 /* TODO WARN_ON unspec type */
224
225 switch (state) {
226 /* TODO NETDEV_DEVTYPE */
227 case NETDEV_REGISTER:
228 wpan_dev->identifier = ++rdev->wpan_dev_id;
229 list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
230 rdev->devlist_generation++;
231
232 wpan_dev->netdev = dev;
233 break;
234 case NETDEV_DOWN:
235 cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1);
236
237 rdev->opencount--;
238 wake_up(&rdev->dev_wait);
239 break;
240 case NETDEV_UP:
241 cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1);
242
243 rdev->opencount++;
244 break;
245 case NETDEV_UNREGISTER:
246 /* It is possible to get NETDEV_UNREGISTER
247 * multiple times. To detect that, check
248 * that the interface is still on the list
249 * of registered interfaces, and only then
250 * remove and clean it up.
251 */
252 if (!list_empty(&wpan_dev->list)) {
253 list_del_rcu(&wpan_dev->list);
254 rdev->devlist_generation++;
255 }
256 /* synchronize (so that we won't find this netdev
257 * from other code any more) and then clear the list
258 * head so that the above code can safely check for
259 * !list_empty() to avoid double-cleanup.
260 */
261 synchronize_rcu();
262 INIT_LIST_HEAD(&wpan_dev->list);
263 break;
264 default:
265 return NOTIFY_DONE;
266 }
267
268 return NOTIFY_OK;
269}
270
271static struct notifier_block cfg802154_netdev_notifier = {
272 .notifier_call = cfg802154_netdev_notifier_call,
273};
274
131static int __init wpan_phy_class_init(void) 275static int __init wpan_phy_class_init(void)
132{ 276{
133 int rc; 277 int rc;
@@ -136,11 +280,25 @@ static int __init wpan_phy_class_init(void)
136 if (rc) 280 if (rc)
137 goto err; 281 goto err;
138 282
139 rc = ieee802154_nl_init(); 283 rc = register_netdevice_notifier(&cfg802154_netdev_notifier);
140 if (rc) 284 if (rc)
141 goto err_nl; 285 goto err_nl;
142 286
287 rc = ieee802154_nl_init();
288 if (rc)
289 goto err_notifier;
290
291 rc = nl802154_init();
292 if (rc)
293 goto err_ieee802154_nl;
294
143 return 0; 295 return 0;
296
297err_ieee802154_nl:
298 ieee802154_nl_exit();
299
300err_notifier:
301 unregister_netdevice_notifier(&cfg802154_netdev_notifier);
144err_nl: 302err_nl:
145 wpan_phy_sysfs_exit(); 303 wpan_phy_sysfs_exit();
146err: 304err:
@@ -150,7 +308,9 @@ subsys_initcall(wpan_phy_class_init);
150 308
151static void __exit wpan_phy_class_exit(void) 309static void __exit wpan_phy_class_exit(void)
152{ 310{
311 nl802154_exit();
153 ieee802154_nl_exit(); 312 ieee802154_nl_exit();
313 unregister_netdevice_notifier(&cfg802154_netdev_notifier);
154 wpan_phy_sysfs_exit(); 314 wpan_phy_sysfs_exit();
155} 315}
156module_exit(wpan_phy_class_exit); 316module_exit(wpan_phy_class_exit);
diff --git a/net/ieee802154/core.h b/net/ieee802154/core.h
new file mode 100644
index 000000000000..f3e95580caee
--- /dev/null
+++ b/net/ieee802154/core.h
@@ -0,0 +1,46 @@
1#ifndef __IEEE802154_CORE_H
2#define __IEEE802154_CORE_H
3
4#include <net/cfg802154.h>
5
6struct cfg802154_registered_device {
7 const struct cfg802154_ops *ops;
8 struct list_head list;
9
10 /* wpan_phy index, internal only */
11 int wpan_phy_idx;
12
13 /* also protected by devlist_mtx */
14 int opencount;
15 wait_queue_head_t dev_wait;
16
17 /* protected by RTNL only */
18 int num_running_ifaces;
19
20 /* associated wpan interfaces, protected by rtnl or RCU */
21 struct list_head wpan_dev_list;
22 int devlist_generation, wpan_dev_id;
23
24 /* must be last because of the way we do wpan_phy_priv(),
25 * and it should at least be aligned to NETDEV_ALIGN
26 */
27 struct wpan_phy wpan_phy __aligned(NETDEV_ALIGN);
28};
29
30static inline struct cfg802154_registered_device *
31wpan_phy_to_rdev(struct wpan_phy *wpan_phy)
32{
33 BUG_ON(!wpan_phy);
34 return container_of(wpan_phy, struct cfg802154_registered_device,
35 wpan_phy);
36}
37
38extern struct list_head cfg802154_rdev_list;
39extern int cfg802154_rdev_list_generation;
40
41/* free object */
42void cfg802154_dev_free(struct cfg802154_registered_device *rdev);
43struct cfg802154_registered_device *
44cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx);
45
46#endif /* __IEEE802154_CORE_H */
diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h
index 42ae63a345ab..a5d7515b7f62 100644
--- a/net/ieee802154/ieee802154.h
+++ b/net/ieee802154/ieee802154.h
@@ -15,7 +15,7 @@
15#define IEEE_802154_LOCAL_H 15#define IEEE_802154_LOCAL_H
16 16
17int __init ieee802154_nl_init(void); 17int __init ieee802154_nl_init(void);
18void __exit ieee802154_nl_exit(void); 18void ieee802154_nl_exit(void);
19 19
20#define IEEE802154_OP(_cmd, _func) \ 20#define IEEE802154_OP(_cmd, _func) \
21 { \ 21 { \
diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c
index 6c3c2595a201..63ee7d66950e 100644
--- a/net/ieee802154/netlink.c
+++ b/net/ieee802154/netlink.c
@@ -155,7 +155,7 @@ int __init ieee802154_nl_init(void)
155 ieee802154_mcgrps); 155 ieee802154_mcgrps);
156} 156}
157 157
158void __exit ieee802154_nl_exit(void) 158void ieee802154_nl_exit(void)
159{ 159{
160 genl_unregister_family(&nl802154_family); 160 genl_unregister_family(&nl802154_family);
161} 161}
diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c
index abd0f31bdc66..fe77f0c770b8 100644
--- a/net/ieee802154/nl-mac.c
+++ b/net/ieee802154/nl-mac.c
@@ -29,7 +29,6 @@
29#include <linux/nl802154.h> 29#include <linux/nl802154.h>
30#include <linux/export.h> 30#include <linux/export.h>
31#include <net/af_ieee802154.h> 31#include <net/af_ieee802154.h>
32#include <net/nl802154.h>
33#include <net/ieee802154_netdev.h> 32#include <net/ieee802154_netdev.h>
34#include <net/cfg802154.h> 33#include <net/cfg802154.h>
35 34
@@ -55,186 +54,7 @@ static __le16 nla_get_shortaddr(const struct nlattr *nla)
55 return cpu_to_le16(nla_get_u16(nla)); 54 return cpu_to_le16(nla_get_u16(nla));
56} 55}
57 56
58int ieee802154_nl_assoc_indic(struct net_device *dev, 57static int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
59 struct ieee802154_addr *addr,
60 u8 cap)
61{
62 struct sk_buff *msg;
63
64 pr_debug("%s\n", __func__);
65
66 if (addr->mode != IEEE802154_ADDR_LONG) {
67 pr_err("%s: received non-long source address!\n", __func__);
68 return -EINVAL;
69 }
70
71 msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC);
72 if (!msg)
73 return -ENOBUFS;
74
75 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
76 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
77 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
78 dev->dev_addr) ||
79 nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR,
80 addr->extended_addr) ||
81 nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap))
82 goto nla_put_failure;
83
84 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
85
86nla_put_failure:
87 nlmsg_free(msg);
88 return -ENOBUFS;
89}
90EXPORT_SYMBOL(ieee802154_nl_assoc_indic);
91
92int ieee802154_nl_assoc_confirm(struct net_device *dev, __le16 short_addr,
93 u8 status)
94{
95 struct sk_buff *msg;
96
97 pr_debug("%s\n", __func__);
98
99 msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF);
100 if (!msg)
101 return -ENOBUFS;
102
103 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
104 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
105 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
106 dev->dev_addr) ||
107 nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
108 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
109 goto nla_put_failure;
110 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
111
112nla_put_failure:
113 nlmsg_free(msg);
114 return -ENOBUFS;
115}
116EXPORT_SYMBOL(ieee802154_nl_assoc_confirm);
117
118int ieee802154_nl_disassoc_indic(struct net_device *dev,
119 struct ieee802154_addr *addr,
120 u8 reason)
121{
122 struct sk_buff *msg;
123
124 pr_debug("%s\n", __func__);
125
126 msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC);
127 if (!msg)
128 return -ENOBUFS;
129
130 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
131 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
132 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
133 dev->dev_addr))
134 goto nla_put_failure;
135 if (addr->mode == IEEE802154_ADDR_LONG) {
136 if (nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR,
137 addr->extended_addr))
138 goto nla_put_failure;
139 } else {
140 if (nla_put_shortaddr(msg, IEEE802154_ATTR_SRC_SHORT_ADDR,
141 addr->short_addr))
142 goto nla_put_failure;
143 }
144 if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason))
145 goto nla_put_failure;
146 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
147
148nla_put_failure:
149 nlmsg_free(msg);
150 return -ENOBUFS;
151}
152EXPORT_SYMBOL(ieee802154_nl_disassoc_indic);
153
154int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)
155{
156 struct sk_buff *msg;
157
158 pr_debug("%s\n", __func__);
159
160 msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF);
161 if (!msg)
162 return -ENOBUFS;
163
164 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
165 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
166 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
167 dev->dev_addr) ||
168 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
169 goto nla_put_failure;
170 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
171
172nla_put_failure:
173 nlmsg_free(msg);
174 return -ENOBUFS;
175}
176EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm);
177
178int ieee802154_nl_beacon_indic(struct net_device *dev, __le16 panid,
179 __le16 coord_addr)
180{
181 struct sk_buff *msg;
182
183 pr_debug("%s\n", __func__);
184
185 msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC);
186 if (!msg)
187 return -ENOBUFS;
188
189 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
190 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
191 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
192 dev->dev_addr) ||
193 nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_SHORT_ADDR,
194 coord_addr) ||
195 nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_PAN_ID, panid))
196 goto nla_put_failure;
197 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
198
199nla_put_failure:
200 nlmsg_free(msg);
201 return -ENOBUFS;
202}
203EXPORT_SYMBOL(ieee802154_nl_beacon_indic);
204
205int ieee802154_nl_scan_confirm(struct net_device *dev,
206 u8 status, u8 scan_type,
207 u32 unscanned, u8 page,
208 u8 *edl/* , struct list_head *pan_desc_list */)
209{
210 struct sk_buff *msg;
211
212 pr_debug("%s\n", __func__);
213
214 msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF);
215 if (!msg)
216 return -ENOBUFS;
217
218 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
219 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
220 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
221 dev->dev_addr) ||
222 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status) ||
223 nla_put_u8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type) ||
224 nla_put_u32(msg, IEEE802154_ATTR_CHANNELS, unscanned) ||
225 nla_put_u8(msg, IEEE802154_ATTR_PAGE, page) ||
226 (edl &&
227 nla_put(msg, IEEE802154_ATTR_ED_LIST, 27, edl)))
228 goto nla_put_failure;
229 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
230
231nla_put_failure:
232 nlmsg_free(msg);
233 return -ENOBUFS;
234}
235EXPORT_SYMBOL(ieee802154_nl_scan_confirm);
236
237int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
238{ 58{
239 struct sk_buff *msg; 59 struct sk_buff *msg;
240 60
@@ -274,8 +94,9 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
274 goto out; 94 goto out;
275 95
276 ops = ieee802154_mlme_ops(dev); 96 ops = ieee802154_mlme_ops(dev);
277 phy = ops->get_phy(dev); 97 phy = dev->ieee802154_ptr->wpan_phy;
278 BUG_ON(!phy); 98 BUG_ON(!phy);
99 get_device(&phy->dev);
279 100
280 short_addr = ops->get_short_addr(dev); 101 short_addr = ops->get_short_addr(dev);
281 pan_id = ops->get_pan_id(dev); 102 pan_id = ops->get_pan_id(dev);
@@ -292,7 +113,9 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
292 if (ops->get_mac_params) { 113 if (ops->get_mac_params) {
293 struct ieee802154_mac_params params; 114 struct ieee802154_mac_params params;
294 115
116 rtnl_lock();
295 ops->get_mac_params(dev, &params); 117 ops->get_mac_params(dev, &params);
118 rtnl_unlock();
296 119
297 if (nla_put_s8(msg, IEEE802154_ATTR_TXPOWER, 120 if (nla_put_s8(msg, IEEE802154_ATTR_TXPOWER,
298 params.transmit_power) || 121 params.transmit_power) ||
@@ -343,7 +166,10 @@ static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)
343 if (!dev) 166 if (!dev)
344 return NULL; 167 return NULL;
345 168
346 if (dev->type != ARPHRD_IEEE802154) { 169 /* Check on mtu is currently a hacked solution because lowpan
170 * and wpan have the same ARPHRD type.
171 */
172 if (dev->type != ARPHRD_IEEE802154 || dev->mtu != IEEE802154_MTU) {
347 dev_put(dev); 173 dev_put(dev);
348 return NULL; 174 return NULL;
349 } 175 }
@@ -477,7 +303,7 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
477 u8 channel, bcn_ord, sf_ord; 303 u8 channel, bcn_ord, sf_ord;
478 u8 page; 304 u8 page;
479 int pan_coord, blx, coord_realign; 305 int pan_coord, blx, coord_realign;
480 int ret = -EOPNOTSUPP; 306 int ret = -EBUSY;
481 307
482 if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] || 308 if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
483 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] || 309 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] ||
@@ -493,8 +319,14 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
493 dev = ieee802154_nl_get_dev(info); 319 dev = ieee802154_nl_get_dev(info);
494 if (!dev) 320 if (!dev)
495 return -ENODEV; 321 return -ENODEV;
496 if (!ieee802154_mlme_ops(dev)->start_req) 322
323 if (netif_running(dev))
324 goto out;
325
326 if (!ieee802154_mlme_ops(dev)->start_req) {
327 ret = -EOPNOTSUPP;
497 goto out; 328 goto out;
329 }
498 330
499 addr.mode = IEEE802154_ADDR_SHORT; 331 addr.mode = IEEE802154_ADDR_SHORT;
500 addr.short_addr = nla_get_shortaddr( 332 addr.short_addr = nla_get_shortaddr(
@@ -521,8 +353,15 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
521 return -EINVAL; 353 return -EINVAL;
522 } 354 }
523 355
356 rtnl_lock();
524 ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page, 357 ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
525 bcn_ord, sf_ord, pan_coord, blx, coord_realign); 358 bcn_ord, sf_ord, pan_coord, blx, coord_realign);
359 rtnl_unlock();
360
361 /* FIXME: add validation for unused parameters to be sane
362 * for SoftMAC
363 */
364 ieee802154_nl_start_confirm(dev, IEEE802154_SUCCESS);
526 365
527out: 366out:
528 dev_put(dev); 367 dev_put(dev);
@@ -612,7 +451,11 @@ int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb)
612 451
613 idx = 0; 452 idx = 0;
614 for_each_netdev(net, dev) { 453 for_each_netdev(net, dev) {
615 if (idx < s_idx || (dev->type != ARPHRD_IEEE802154)) 454 /* Check on mtu is currently a hacked solution because lowpan
455 * and wpan have the same ARPHRD type.
456 */
457 if (idx < s_idx || dev->type != ARPHRD_IEEE802154 ||
458 dev->mtu != IEEE802154_MTU)
616 goto cont; 459 goto cont;
617 460
618 if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid, 461 if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid,
@@ -662,8 +505,10 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
662 !info->attrs[IEEE802154_ATTR_FRAME_RETRIES]) 505 !info->attrs[IEEE802154_ATTR_FRAME_RETRIES])
663 goto out; 506 goto out;
664 507
665 phy = ops->get_phy(dev); 508 phy = dev->ieee802154_ptr->wpan_phy;
509 get_device(&phy->dev);
666 510
511 rtnl_lock();
667 ops->get_mac_params(dev, &params); 512 ops->get_mac_params(dev, &params);
668 513
669 if (info->attrs[IEEE802154_ATTR_TXPOWER]) 514 if (info->attrs[IEEE802154_ATTR_TXPOWER])
@@ -691,6 +536,7 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
691 params.frame_retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]); 536 params.frame_retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]);
692 537
693 rc = ops->set_mac_params(dev, &params); 538 rc = ops->set_mac_params(dev, &params);
539 rtnl_unlock();
694 540
695 wpan_phy_put(phy); 541 wpan_phy_put(phy);
696 dev_put(dev); 542 dev_put(dev);
@@ -943,7 +789,11 @@ ieee802154_llsec_dump_table(struct sk_buff *skb, struct netlink_callback *cb,
943 int rc; 789 int rc;
944 790
945 for_each_netdev(net, dev) { 791 for_each_netdev(net, dev) {
946 if (idx < first_dev || dev->type != ARPHRD_IEEE802154) 792 /* Check on mtu is currently a hacked solution because lowpan
793 * and wpan have the same ARPHRD type.
794 */
795 if (idx < first_dev || dev->type != ARPHRD_IEEE802154 ||
796 dev->mtu != IEEE802154_MTU)
947 goto skip; 797 goto skip;
948 798
949 data.ops = ieee802154_mlme_ops(dev); 799 data.ops = ieee802154_mlme_ops(dev);
diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c
index 0afe760ff512..80a946dddd90 100644
--- a/net/ieee802154/nl-phy.c
+++ b/net/ieee802154/nl-phy.c
@@ -30,6 +30,8 @@
30#include <linux/nl802154.h> 30#include <linux/nl802154.h>
31 31
32#include "ieee802154.h" 32#include "ieee802154.h"
33#include "rdev-ops.h"
34#include "core.h"
33 35
34static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid, 36static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid,
35 u32 seq, int flags, struct wpan_phy *phy) 37 u32 seq, int flags, struct wpan_phy *phy)
@@ -203,11 +205,6 @@ int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
203 if (!msg) 205 if (!msg)
204 goto out_dev; 206 goto out_dev;
205 207
206 if (!phy->add_iface) {
207 rc = -EINVAL;
208 goto nla_put_failure;
209 }
210
211 if (info->attrs[IEEE802154_ATTR_HW_ADDR] && 208 if (info->attrs[IEEE802154_ATTR_HW_ADDR] &&
212 nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) != 209 nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) !=
213 IEEE802154_ADDR_LEN) { 210 IEEE802154_ADDR_LEN) {
@@ -223,11 +220,13 @@ int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
223 } 220 }
224 } 221 }
225 222
226 dev = phy->add_iface(phy, devname, type); 223 dev = rdev_add_virtual_intf_deprecated(wpan_phy_to_rdev(phy), devname,
224 type);
227 if (IS_ERR(dev)) { 225 if (IS_ERR(dev)) {
228 rc = PTR_ERR(dev); 226 rc = PTR_ERR(dev);
229 goto nla_put_failure; 227 goto nla_put_failure;
230 } 228 }
229 dev_hold(dev);
231 230
232 if (info->attrs[IEEE802154_ATTR_HW_ADDR]) { 231 if (info->attrs[IEEE802154_ATTR_HW_ADDR]) {
233 struct sockaddr addr; 232 struct sockaddr addr;
@@ -257,7 +256,7 @@ int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
257 256
258dev_unregister: 257dev_unregister:
259 rtnl_lock(); /* del_iface must be called with RTNL lock */ 258 rtnl_lock(); /* del_iface must be called with RTNL lock */
260 phy->del_iface(phy, dev); 259 rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
261 dev_put(dev); 260 dev_put(dev);
262 rtnl_unlock(); 261 rtnl_unlock();
263nla_put_failure: 262nla_put_failure:
@@ -288,8 +287,9 @@ int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info)
288 if (!dev) 287 if (!dev)
289 return -ENODEV; 288 return -ENODEV;
290 289
291 phy = ieee802154_mlme_ops(dev)->get_phy(dev); 290 phy = dev->ieee802154_ptr->wpan_phy;
292 BUG_ON(!phy); 291 BUG_ON(!phy);
292 get_device(&phy->dev);
293 293
294 rc = -EINVAL; 294 rc = -EINVAL;
295 /* phy name is optional, but should be checked if it's given */ 295 /* phy name is optional, but should be checked if it's given */
@@ -319,13 +319,8 @@ int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info)
319 if (!msg) 319 if (!msg)
320 goto out_dev; 320 goto out_dev;
321 321
322 if (!phy->del_iface) {
323 rc = -EINVAL;
324 goto nla_put_failure;
325 }
326
327 rtnl_lock(); 322 rtnl_lock();
328 phy->del_iface(phy, dev); 323 rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
329 324
330 /* We don't have device anymore */ 325 /* We don't have device anymore */
331 dev_put(dev); 326 dev_put(dev);
diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c
new file mode 100644
index 000000000000..889647744697
--- /dev/null
+++ b/net/ieee802154/nl802154.c
@@ -0,0 +1,957 @@
1/* This program is free software; you can redistribute it and/or modify
2 * it under the terms of the GNU General Public License version 2
3 * as published by the Free Software Foundation.
4 *
5 * This program is distributed in the hope that it will be useful,
6 * but WITHOUT ANY WARRANTY; without even the implied warranty of
7 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8 * GNU General Public License for more details.
9 *
10 * Authors:
11 * Alexander Aring <aar@pengutronix.de>
12 *
13 * Based on: net/wireless/nl80211.c
14 */
15
16#include <linux/rtnetlink.h>
17
18#include <net/cfg802154.h>
19#include <net/genetlink.h>
20#include <net/mac802154.h>
21#include <net/netlink.h>
22#include <net/nl802154.h>
23#include <net/sock.h>
24
25#include "nl802154.h"
26#include "rdev-ops.h"
27#include "core.h"
28
29static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
30 struct genl_info *info);
31
32static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
33 struct genl_info *info);
34
35/* the netlink family */
36static struct genl_family nl802154_fam = {
37 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
38 .name = NL802154_GENL_NAME, /* have users key off the name instead */
39 .hdrsize = 0, /* no private header */
40 .version = 1, /* no particular meaning now */
41 .maxattr = NL802154_ATTR_MAX,
42 .netnsok = true,
43 .pre_doit = nl802154_pre_doit,
44 .post_doit = nl802154_post_doit,
45};
46
47/* multicast groups */
48enum nl802154_multicast_groups {
49 NL802154_MCGRP_CONFIG,
50};
51
52static const struct genl_multicast_group nl802154_mcgrps[] = {
53 [NL802154_MCGRP_CONFIG] = { .name = "config", },
54};
55
56/* returns ERR_PTR values */
57static struct wpan_dev *
58__cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs)
59{
60 struct cfg802154_registered_device *rdev;
61 struct wpan_dev *result = NULL;
62 bool have_ifidx = attrs[NL802154_ATTR_IFINDEX];
63 bool have_wpan_dev_id = attrs[NL802154_ATTR_WPAN_DEV];
64 u64 wpan_dev_id;
65 int wpan_phy_idx = -1;
66 int ifidx = -1;
67
68 ASSERT_RTNL();
69
70 if (!have_ifidx && !have_wpan_dev_id)
71 return ERR_PTR(-EINVAL);
72
73 if (have_ifidx)
74 ifidx = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
75 if (have_wpan_dev_id) {
76 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
77 wpan_phy_idx = wpan_dev_id >> 32;
78 }
79
80 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
81 struct wpan_dev *wpan_dev;
82
83 /* TODO netns compare */
84
85 if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx)
86 continue;
87
88 list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
89 if (have_ifidx && wpan_dev->netdev &&
90 wpan_dev->netdev->ifindex == ifidx) {
91 result = wpan_dev;
92 break;
93 }
94 if (have_wpan_dev_id &&
95 wpan_dev->identifier == (u32)wpan_dev_id) {
96 result = wpan_dev;
97 break;
98 }
99 }
100
101 if (result)
102 break;
103 }
104
105 if (result)
106 return result;
107
108 return ERR_PTR(-ENODEV);
109}
110
111static struct cfg802154_registered_device *
112__cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
113{
114 struct cfg802154_registered_device *rdev = NULL, *tmp;
115 struct net_device *netdev;
116
117 ASSERT_RTNL();
118
119 if (!attrs[NL802154_ATTR_WPAN_PHY] &&
120 !attrs[NL802154_ATTR_IFINDEX] &&
121 !attrs[NL802154_ATTR_WPAN_DEV])
122 return ERR_PTR(-EINVAL);
123
124 if (attrs[NL802154_ATTR_WPAN_PHY])
125 rdev = cfg802154_rdev_by_wpan_phy_idx(
126 nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY]));
127
128 if (attrs[NL802154_ATTR_WPAN_DEV]) {
129 u64 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
130 struct wpan_dev *wpan_dev;
131 bool found = false;
132
133 tmp = cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id >> 32);
134 if (tmp) {
135 /* make sure wpan_dev exists */
136 list_for_each_entry(wpan_dev, &tmp->wpan_dev_list, list) {
137 if (wpan_dev->identifier != (u32)wpan_dev_id)
138 continue;
139 found = true;
140 break;
141 }
142
143 if (!found)
144 tmp = NULL;
145
146 if (rdev && tmp != rdev)
147 return ERR_PTR(-EINVAL);
148 rdev = tmp;
149 }
150 }
151
152 if (attrs[NL802154_ATTR_IFINDEX]) {
153 int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
154
155 netdev = __dev_get_by_index(netns, ifindex);
156 if (netdev) {
157 if (netdev->ieee802154_ptr)
158 tmp = wpan_phy_to_rdev(
159 netdev->ieee802154_ptr->wpan_phy);
160 else
161 tmp = NULL;
162
163 /* not wireless device -- return error */
164 if (!tmp)
165 return ERR_PTR(-EINVAL);
166
167 /* mismatch -- return error */
168 if (rdev && tmp != rdev)
169 return ERR_PTR(-EINVAL);
170
171 rdev = tmp;
172 }
173 }
174
175 if (!rdev)
176 return ERR_PTR(-ENODEV);
177
178 /* TODO netns compare */
179
180 return rdev;
181}
182
183/* This function returns a pointer to the driver
184 * that the genl_info item that is passed refers to.
185 *
186 * The result of this can be a PTR_ERR and hence must
187 * be checked with IS_ERR() for errors.
188 */
189static struct cfg802154_registered_device *
190cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info)
191{
192 return __cfg802154_rdev_from_attrs(netns, info->attrs);
193}
194
195/* policy for the attributes */
196static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
197 [NL802154_ATTR_WPAN_PHY] = { .type = NLA_U32 },
198 [NL802154_ATTR_WPAN_PHY_NAME] = { .type = NLA_NUL_STRING,
199 .len = 20-1 },
200
201 [NL802154_ATTR_IFINDEX] = { .type = NLA_U32 },
202 [NL802154_ATTR_IFTYPE] = { .type = NLA_U32 },
203 [NL802154_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
204
205 [NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 },
206
207 [NL802154_ATTR_PAGE] = { .type = NLA_U8, },
208 [NL802154_ATTR_CHANNEL] = { .type = NLA_U8, },
209
210 [NL802154_ATTR_TX_POWER] = { .type = NLA_S8, },
211
212 [NL802154_ATTR_CCA_MODE] = { .type = NLA_U8, },
213
214 [NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, },
215
216 [NL802154_ATTR_PAN_ID] = { .type = NLA_U16, },
217 [NL802154_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
218 [NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, },
219
220 [NL802154_ATTR_MIN_BE] = { .type = NLA_U8, },
221 [NL802154_ATTR_MAX_BE] = { .type = NLA_U8, },
222 [NL802154_ATTR_MAX_CSMA_BACKOFFS] = { .type = NLA_U8, },
223
224 [NL802154_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_S8, },
225
226 [NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, },
227};
228
229/* message building helper */
230static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
231 int flags, u8 cmd)
232{
233 /* since there is no private header just add the generic one */
234 return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd);
235}
236
237static int
238nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev,
239 struct sk_buff *msg)
240{
241 struct nlattr *nl_page;
242 unsigned long page;
243
244 nl_page = nla_nest_start(msg, NL802154_ATTR_CHANNELS_SUPPORTED);
245 if (!nl_page)
246 return -ENOBUFS;
247
248 for (page = 0; page <= IEEE802154_MAX_PAGE; page++) {
249 if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL,
250 rdev->wpan_phy.channels_supported[page]))
251 return -ENOBUFS;
252 }
253 nla_nest_end(msg, nl_page);
254
255 return 0;
256}
257
258static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev,
259 enum nl802154_commands cmd,
260 struct sk_buff *msg, u32 portid, u32 seq,
261 int flags)
262{
263 void *hdr;
264
265 hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
266 if (!hdr)
267 return -ENOBUFS;
268
269 if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
270 nla_put_string(msg, NL802154_ATTR_WPAN_PHY_NAME,
271 wpan_phy_name(&rdev->wpan_phy)) ||
272 nla_put_u32(msg, NL802154_ATTR_GENERATION,
273 cfg802154_rdev_list_generation))
274 goto nla_put_failure;
275
276 if (cmd != NL802154_CMD_NEW_WPAN_PHY)
277 goto finish;
278
279 /* DUMP PHY PIB */
280
281 /* current channel settings */
282 if (nla_put_u8(msg, NL802154_ATTR_PAGE,
283 rdev->wpan_phy.current_page) ||
284 nla_put_u8(msg, NL802154_ATTR_CHANNEL,
285 rdev->wpan_phy.current_channel))
286 goto nla_put_failure;
287
288 /* supported channels array */
289 if (nl802154_send_wpan_phy_channels(rdev, msg))
290 goto nla_put_failure;
291
292 /* cca mode */
293 if (nla_put_u8(msg, NL802154_ATTR_CCA_MODE,
294 rdev->wpan_phy.cca_mode))
295 goto nla_put_failure;
296
297 if (nla_put_s8(msg, NL802154_ATTR_TX_POWER,
298 rdev->wpan_phy.transmit_power))
299 goto nla_put_failure;
300
301finish:
302 return genlmsg_end(msg, hdr);
303
304nla_put_failure:
305 genlmsg_cancel(msg, hdr);
306 return -EMSGSIZE;
307}
308
309struct nl802154_dump_wpan_phy_state {
310 s64 filter_wpan_phy;
311 long start;
312
313};
314
315static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb,
316 struct netlink_callback *cb,
317 struct nl802154_dump_wpan_phy_state *state)
318{
319 struct nlattr **tb = nl802154_fam.attrbuf;
320 int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize,
321 tb, nl802154_fam.maxattr, nl802154_policy);
322
323 /* TODO check if we can handle error here,
324 * we have no backward compatibility
325 */
326 if (ret)
327 return 0;
328
329 if (tb[NL802154_ATTR_WPAN_PHY])
330 state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]);
331 if (tb[NL802154_ATTR_WPAN_DEV])
332 state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32;
333 if (tb[NL802154_ATTR_IFINDEX]) {
334 struct net_device *netdev;
335 struct cfg802154_registered_device *rdev;
336 int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]);
337
338 /* TODO netns */
339 netdev = __dev_get_by_index(&init_net, ifidx);
340 if (!netdev)
341 return -ENODEV;
342 if (netdev->ieee802154_ptr) {
343 rdev = wpan_phy_to_rdev(
344 netdev->ieee802154_ptr->wpan_phy);
345 state->filter_wpan_phy = rdev->wpan_phy_idx;
346 }
347 }
348
349 return 0;
350}
351
352static int
353nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb)
354{
355 int idx = 0, ret;
356 struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0];
357 struct cfg802154_registered_device *rdev;
358
359 rtnl_lock();
360 if (!state) {
361 state = kzalloc(sizeof(*state), GFP_KERNEL);
362 if (!state) {
363 rtnl_unlock();
364 return -ENOMEM;
365 }
366 state->filter_wpan_phy = -1;
367 ret = nl802154_dump_wpan_phy_parse(skb, cb, state);
368 if (ret) {
369 kfree(state);
370 rtnl_unlock();
371 return ret;
372 }
373 cb->args[0] = (long)state;
374 }
375
376 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
377 /* TODO net ns compare */
378 if (++idx <= state->start)
379 continue;
380 if (state->filter_wpan_phy != -1 &&
381 state->filter_wpan_phy != rdev->wpan_phy_idx)
382 continue;
383 /* attempt to fit multiple wpan_phy data chunks into the skb */
384 ret = nl802154_send_wpan_phy(rdev,
385 NL802154_CMD_NEW_WPAN_PHY,
386 skb,
387 NETLINK_CB(cb->skb).portid,
388 cb->nlh->nlmsg_seq, NLM_F_MULTI);
389 if (ret < 0) {
390 if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
391 !skb->len && cb->min_dump_alloc < 4096) {
392 cb->min_dump_alloc = 4096;
393 rtnl_unlock();
394 return 1;
395 }
396 idx--;
397 break;
398 }
399 break;
400 }
401 rtnl_unlock();
402
403 state->start = idx;
404
405 return skb->len;
406}
407
408static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
409{
410 kfree((void *)cb->args[0]);
411 return 0;
412}
413
414static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info)
415{
416 struct sk_buff *msg;
417 struct cfg802154_registered_device *rdev = info->user_ptr[0];
418
419 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
420 if (!msg)
421 return -ENOMEM;
422
423 if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg,
424 info->snd_portid, info->snd_seq, 0) < 0) {
425 nlmsg_free(msg);
426 return -ENOBUFS;
427 }
428
429 return genlmsg_reply(msg, info);
430}
431
432static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev)
433{
434 return (u64)wpan_dev->identifier |
435 ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
436}
437
438static int
439nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
440 struct cfg802154_registered_device *rdev,
441 struct wpan_dev *wpan_dev)
442{
443 struct net_device *dev = wpan_dev->netdev;
444 void *hdr;
445
446 hdr = nl802154hdr_put(msg, portid, seq, flags,
447 NL802154_CMD_NEW_INTERFACE);
448 if (!hdr)
449 return -1;
450
451 if (dev &&
452 (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) ||
453 nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name)))
454 goto nla_put_failure;
455
456 if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
457 nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) ||
458 nla_put_u64(msg, NL802154_ATTR_WPAN_DEV, wpan_dev_id(wpan_dev)) ||
459 nla_put_u32(msg, NL802154_ATTR_GENERATION,
460 rdev->devlist_generation ^
461 (cfg802154_rdev_list_generation << 2)))
462 goto nla_put_failure;
463
464 /* address settings */
465 if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR,
466 wpan_dev->extended_addr) ||
467 nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR,
468 wpan_dev->short_addr) ||
469 nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id))
470 goto nla_put_failure;
471
472 /* ARET handling */
473 if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES,
474 wpan_dev->frame_retries) ||
475 nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) ||
476 nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS,
477 wpan_dev->csma_retries) ||
478 nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be))
479 goto nla_put_failure;
480
481 /* listen before transmit */
482 if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt))
483 goto nla_put_failure;
484
485 return genlmsg_end(msg, hdr);
486
487nla_put_failure:
488 genlmsg_cancel(msg, hdr);
489 return -EMSGSIZE;
490}
491
492static int
493nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
494{
495 int wp_idx = 0;
496 int if_idx = 0;
497 int wp_start = cb->args[0];
498 int if_start = cb->args[1];
499 struct cfg802154_registered_device *rdev;
500 struct wpan_dev *wpan_dev;
501
502 rtnl_lock();
503 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
504 /* TODO netns compare */
505 if (wp_idx < wp_start) {
506 wp_idx++;
507 continue;
508 }
509 if_idx = 0;
510
511 list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
512 if (if_idx < if_start) {
513 if_idx++;
514 continue;
515 }
516 if (nl802154_send_iface(skb, NETLINK_CB(cb->skb).portid,
517 cb->nlh->nlmsg_seq, NLM_F_MULTI,
518 rdev, wpan_dev) < 0) {
519 goto out;
520 }
521 if_idx++;
522 }
523
524 wp_idx++;
525 }
526out:
527 rtnl_unlock();
528
529 cb->args[0] = wp_idx;
530 cb->args[1] = if_idx;
531
532 return skb->len;
533}
534
535static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info)
536{
537 struct sk_buff *msg;
538 struct cfg802154_registered_device *rdev = info->user_ptr[0];
539 struct wpan_dev *wdev = info->user_ptr[1];
540
541 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
542 if (!msg)
543 return -ENOMEM;
544
545 if (nl802154_send_iface(msg, info->snd_portid, info->snd_seq, 0,
546 rdev, wdev) < 0) {
547 nlmsg_free(msg);
548 return -ENOBUFS;
549 }
550
551 return genlmsg_reply(msg, info);
552}
553
554static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
555{
556 struct cfg802154_registered_device *rdev = info->user_ptr[0];
557 enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC;
558 __le64 extended_addr = cpu_to_le64(0x0000000000000000ULL);
559
560 /* TODO avoid failing a new interface
561 * creation due to pending removal?
562 */
563
564 if (!info->attrs[NL802154_ATTR_IFNAME])
565 return -EINVAL;
566
567 if (info->attrs[NL802154_ATTR_IFTYPE]) {
568 type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]);
569 if (type > NL802154_IFTYPE_MAX)
570 return -EINVAL;
571 }
572
573 /* TODO add nla_get_le64 to netlink */
574 if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
575 extended_addr = (__force __le64)nla_get_u64(
576 info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
577
578 if (!rdev->ops->add_virtual_intf)
579 return -EOPNOTSUPP;
580
581 return rdev_add_virtual_intf(rdev,
582 nla_data(info->attrs[NL802154_ATTR_IFNAME]),
583 type, extended_addr);
584}
585
586static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info)
587{
588 struct cfg802154_registered_device *rdev = info->user_ptr[0];
589 struct wpan_dev *wpan_dev = info->user_ptr[1];
590
591 if (!rdev->ops->del_virtual_intf)
592 return -EOPNOTSUPP;
593
594 /* If we remove a wpan device without a netdev then clear
595 * user_ptr[1] so that nl802154_post_doit won't dereference it
596 * to check if it needs to do dev_put(). Otherwise it crashes
597 * since the wpan_dev has been freed, unlike with a netdev where
598 * we need the dev_put() for the netdev to really be freed.
599 */
600 if (!wpan_dev->netdev)
601 info->user_ptr[1] = NULL;
602
603 return rdev_del_virtual_intf(rdev, wpan_dev);
604}
605
606static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info)
607{
608 struct cfg802154_registered_device *rdev = info->user_ptr[0];
609 u8 channel, page;
610
611 if (!info->attrs[NL802154_ATTR_PAGE] ||
612 !info->attrs[NL802154_ATTR_CHANNEL])
613 return -EINVAL;
614
615 page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
616 channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]);
617
618 /* check 802.15.4 constraints */
619 if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL)
620 return -EINVAL;
621
622 return rdev_set_channel(rdev, page, channel);
623}
624
625static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
626{
627 struct cfg802154_registered_device *rdev = info->user_ptr[0];
628 struct net_device *dev = info->user_ptr[1];
629 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
630 __le16 pan_id;
631
632 /* conflict here while tx/rx calls */
633 if (netif_running(dev))
634 return -EBUSY;
635
636 /* don't change address fields on monitor */
637 if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
638 return -EINVAL;
639
640 if (!info->attrs[NL802154_ATTR_PAN_ID])
641 return -EINVAL;
642
643 pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]);
644
645 return rdev_set_pan_id(rdev, wpan_dev, pan_id);
646}
647
648static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info)
649{
650 struct cfg802154_registered_device *rdev = info->user_ptr[0];
651 struct net_device *dev = info->user_ptr[1];
652 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
653 __le16 short_addr;
654
655 /* conflict here while tx/rx calls */
656 if (netif_running(dev))
657 return -EBUSY;
658
659 /* don't change address fields on monitor */
660 if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
661 return -EINVAL;
662
663 if (!info->attrs[NL802154_ATTR_SHORT_ADDR])
664 return -EINVAL;
665
666 short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]);
667
668 return rdev_set_short_addr(rdev, wpan_dev, short_addr);
669}
670
671static int
672nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info)
673{
674 struct cfg802154_registered_device *rdev = info->user_ptr[0];
675 struct net_device *dev = info->user_ptr[1];
676 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
677 u8 min_be, max_be;
678
679 /* should be set on netif open inside phy settings */
680 if (netif_running(dev))
681 return -EBUSY;
682
683 if (!info->attrs[NL802154_ATTR_MIN_BE] ||
684 !info->attrs[NL802154_ATTR_MAX_BE])
685 return -EINVAL;
686
687 min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]);
688 max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]);
689
690 /* check 802.15.4 constraints */
691 if (max_be < 3 || max_be > 8 || min_be > max_be)
692 return -EINVAL;
693
694 return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be);
695}
696
697static int
698nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info)
699{
700 struct cfg802154_registered_device *rdev = info->user_ptr[0];
701 struct net_device *dev = info->user_ptr[1];
702 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
703 u8 max_csma_backoffs;
704
705 /* conflict here while other running iface settings */
706 if (netif_running(dev))
707 return -EBUSY;
708
709 if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS])
710 return -EINVAL;
711
712 max_csma_backoffs = nla_get_u8(
713 info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]);
714
715 /* check 802.15.4 constraints */
716 if (max_csma_backoffs > 5)
717 return -EINVAL;
718
719 return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs);
720}
721
722static int
723nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info)
724{
725 struct cfg802154_registered_device *rdev = info->user_ptr[0];
726 struct net_device *dev = info->user_ptr[1];
727 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
728 s8 max_frame_retries;
729
730 if (netif_running(dev))
731 return -EBUSY;
732
733 if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES])
734 return -EINVAL;
735
736 max_frame_retries = nla_get_s8(
737 info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]);
738
739 /* check 802.15.4 constraints */
740 if (max_frame_retries < -1 || max_frame_retries > 7)
741 return -EINVAL;
742
743 return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries);
744}
745
746static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info)
747{
748 struct cfg802154_registered_device *rdev = info->user_ptr[0];
749 struct net_device *dev = info->user_ptr[1];
750 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
751 bool mode;
752
753 if (netif_running(dev))
754 return -EBUSY;
755
756 if (!info->attrs[NL802154_ATTR_LBT_MODE])
757 return -EINVAL;
758
759 mode = !!nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]);
760 return rdev_set_lbt_mode(rdev, wpan_dev, mode);
761}
762
763#define NL802154_FLAG_NEED_WPAN_PHY 0x01
764#define NL802154_FLAG_NEED_NETDEV 0x02
765#define NL802154_FLAG_NEED_RTNL 0x04
766#define NL802154_FLAG_CHECK_NETDEV_UP 0x08
767#define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\
768 NL802154_FLAG_CHECK_NETDEV_UP)
769#define NL802154_FLAG_NEED_WPAN_DEV 0x10
770#define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\
771 NL802154_FLAG_CHECK_NETDEV_UP)
772
773static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
774 struct genl_info *info)
775{
776 struct cfg802154_registered_device *rdev;
777 struct wpan_dev *wpan_dev;
778 struct net_device *dev;
779 bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
780
781 if (rtnl)
782 rtnl_lock();
783
784 if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
785 rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
786 if (IS_ERR(rdev)) {
787 if (rtnl)
788 rtnl_unlock();
789 return PTR_ERR(rdev);
790 }
791 info->user_ptr[0] = rdev;
792 } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
793 ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
794 ASSERT_RTNL();
795 wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
796 info->attrs);
797 if (IS_ERR(wpan_dev)) {
798 if (rtnl)
799 rtnl_unlock();
800 return PTR_ERR(wpan_dev);
801 }
802
803 dev = wpan_dev->netdev;
804 rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
805
806 if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
807 if (!dev) {
808 if (rtnl)
809 rtnl_unlock();
810 return -EINVAL;
811 }
812
813 info->user_ptr[1] = dev;
814 } else {
815 info->user_ptr[1] = wpan_dev;
816 }
817
818 if (dev) {
819 if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
820 !netif_running(dev)) {
821 if (rtnl)
822 rtnl_unlock();
823 return -ENETDOWN;
824 }
825
826 dev_hold(dev);
827 }
828
829 info->user_ptr[0] = rdev;
830 }
831
832 return 0;
833}
834
835static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
836 struct genl_info *info)
837{
838 if (info->user_ptr[1]) {
839 if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
840 struct wpan_dev *wpan_dev = info->user_ptr[1];
841
842 if (wpan_dev->netdev)
843 dev_put(wpan_dev->netdev);
844 } else {
845 dev_put(info->user_ptr[1]);
846 }
847 }
848
849 if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
850 rtnl_unlock();
851}
852
853static const struct genl_ops nl802154_ops[] = {
854 {
855 .cmd = NL802154_CMD_GET_WPAN_PHY,
856 .doit = nl802154_get_wpan_phy,
857 .dumpit = nl802154_dump_wpan_phy,
858 .done = nl802154_dump_wpan_phy_done,
859 .policy = nl802154_policy,
860 /* can be retrieved by unprivileged users */
861 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
862 NL802154_FLAG_NEED_RTNL,
863 },
864 {
865 .cmd = NL802154_CMD_GET_INTERFACE,
866 .doit = nl802154_get_interface,
867 .dumpit = nl802154_dump_interface,
868 .policy = nl802154_policy,
869 /* can be retrieved by unprivileged users */
870 .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
871 NL802154_FLAG_NEED_RTNL,
872 },
873 {
874 .cmd = NL802154_CMD_NEW_INTERFACE,
875 .doit = nl802154_new_interface,
876 .policy = nl802154_policy,
877 .flags = GENL_ADMIN_PERM,
878 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
879 NL802154_FLAG_NEED_RTNL,
880 },
881 {
882 .cmd = NL802154_CMD_DEL_INTERFACE,
883 .doit = nl802154_del_interface,
884 .policy = nl802154_policy,
885 .flags = GENL_ADMIN_PERM,
886 .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
887 NL802154_FLAG_NEED_RTNL,
888 },
889 {
890 .cmd = NL802154_CMD_SET_CHANNEL,
891 .doit = nl802154_set_channel,
892 .policy = nl802154_policy,
893 .flags = GENL_ADMIN_PERM,
894 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
895 NL802154_FLAG_NEED_RTNL,
896 },
897 {
898 .cmd = NL802154_CMD_SET_PAN_ID,
899 .doit = nl802154_set_pan_id,
900 .policy = nl802154_policy,
901 .flags = GENL_ADMIN_PERM,
902 .internal_flags = NL802154_FLAG_NEED_NETDEV |
903 NL802154_FLAG_NEED_RTNL,
904 },
905 {
906 .cmd = NL802154_CMD_SET_SHORT_ADDR,
907 .doit = nl802154_set_short_addr,
908 .policy = nl802154_policy,
909 .flags = GENL_ADMIN_PERM,
910 .internal_flags = NL802154_FLAG_NEED_NETDEV |
911 NL802154_FLAG_NEED_RTNL,
912 },
913 {
914 .cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
915 .doit = nl802154_set_backoff_exponent,
916 .policy = nl802154_policy,
917 .flags = GENL_ADMIN_PERM,
918 .internal_flags = NL802154_FLAG_NEED_NETDEV |
919 NL802154_FLAG_NEED_RTNL,
920 },
921 {
922 .cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
923 .doit = nl802154_set_max_csma_backoffs,
924 .policy = nl802154_policy,
925 .flags = GENL_ADMIN_PERM,
926 .internal_flags = NL802154_FLAG_NEED_NETDEV |
927 NL802154_FLAG_NEED_RTNL,
928 },
929 {
930 .cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
931 .doit = nl802154_set_max_frame_retries,
932 .policy = nl802154_policy,
933 .flags = GENL_ADMIN_PERM,
934 .internal_flags = NL802154_FLAG_NEED_NETDEV |
935 NL802154_FLAG_NEED_RTNL,
936 },
937 {
938 .cmd = NL802154_CMD_SET_LBT_MODE,
939 .doit = nl802154_set_lbt_mode,
940 .policy = nl802154_policy,
941 .flags = GENL_ADMIN_PERM,
942 .internal_flags = NL802154_FLAG_NEED_NETDEV |
943 NL802154_FLAG_NEED_RTNL,
944 },
945};
946
947/* initialisation/exit functions */
948int nl802154_init(void)
949{
950 return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops,
951 nl802154_mcgrps);
952}
953
954void nl802154_exit(void)
955{
956 genl_unregister_family(&nl802154_fam);
957}
diff --git a/net/ieee802154/nl802154.h b/net/ieee802154/nl802154.h
new file mode 100644
index 000000000000..3846a89d0958
--- /dev/null
+++ b/net/ieee802154/nl802154.h
@@ -0,0 +1,7 @@
1#ifndef __IEEE802154_NL802154_H
2#define __IEEE802154_NL802154_H
3
4int nl802154_init(void);
5void nl802154_exit(void);
6
7#endif /* __IEEE802154_NL802154_H */
diff --git a/net/ieee802154/rdev-ops.h b/net/ieee802154/rdev-ops.h
new file mode 100644
index 000000000000..aff54fbd9264
--- /dev/null
+++ b/net/ieee802154/rdev-ops.h
@@ -0,0 +1,89 @@
1#ifndef __CFG802154_RDEV_OPS
2#define __CFG802154_RDEV_OPS
3
4#include <net/cfg802154.h>
5
6#include "core.h"
7
8static inline struct net_device *
9rdev_add_virtual_intf_deprecated(struct cfg802154_registered_device *rdev,
10 const char *name, int type)
11{
12 return rdev->ops->add_virtual_intf_deprecated(&rdev->wpan_phy, name,
13 type);
14}
15
16static inline void
17rdev_del_virtual_intf_deprecated(struct cfg802154_registered_device *rdev,
18 struct net_device *dev)
19{
20 rdev->ops->del_virtual_intf_deprecated(&rdev->wpan_phy, dev);
21}
22
23static inline int
24rdev_add_virtual_intf(struct cfg802154_registered_device *rdev, char *name,
25 enum nl802154_iftype type, __le64 extended_addr)
26{
27 return rdev->ops->add_virtual_intf(&rdev->wpan_phy, name, type,
28 extended_addr);
29}
30
31static inline int
32rdev_del_virtual_intf(struct cfg802154_registered_device *rdev,
33 struct wpan_dev *wpan_dev)
34{
35 return rdev->ops->del_virtual_intf(&rdev->wpan_phy, wpan_dev);
36}
37
38static inline int
39rdev_set_channel(struct cfg802154_registered_device *rdev, u8 page, u8 channel)
40{
41 return rdev->ops->set_channel(&rdev->wpan_phy, page, channel);
42}
43
44static inline int
45rdev_set_pan_id(struct cfg802154_registered_device *rdev,
46 struct wpan_dev *wpan_dev, __le16 pan_id)
47{
48 return rdev->ops->set_pan_id(&rdev->wpan_phy, wpan_dev, pan_id);
49}
50
51static inline int
52rdev_set_short_addr(struct cfg802154_registered_device *rdev,
53 struct wpan_dev *wpan_dev, __le16 short_addr)
54{
55 return rdev->ops->set_short_addr(&rdev->wpan_phy, wpan_dev, short_addr);
56}
57
58static inline int
59rdev_set_backoff_exponent(struct cfg802154_registered_device *rdev,
60 struct wpan_dev *wpan_dev, u8 min_be, u8 max_be)
61{
62 return rdev->ops->set_backoff_exponent(&rdev->wpan_phy, wpan_dev,
63 min_be, max_be);
64}
65
66static inline int
67rdev_set_max_csma_backoffs(struct cfg802154_registered_device *rdev,
68 struct wpan_dev *wpan_dev, u8 max_csma_backoffs)
69{
70 return rdev->ops->set_max_csma_backoffs(&rdev->wpan_phy, wpan_dev,
71 max_csma_backoffs);
72}
73
74static inline int
75rdev_set_max_frame_retries(struct cfg802154_registered_device *rdev,
76 struct wpan_dev *wpan_dev, s8 max_frame_retries)
77{
78 return rdev->ops->set_max_frame_retries(&rdev->wpan_phy, wpan_dev,
79 max_frame_retries);
80}
81
82static inline int
83rdev_set_lbt_mode(struct cfg802154_registered_device *rdev,
84 struct wpan_dev *wpan_dev, bool mode)
85{
86 return rdev->ops->set_lbt_mode(&rdev->wpan_phy, wpan_dev, mode);
87}
88
89#endif /* __CFG802154_RDEV_OPS */
diff --git a/net/ieee802154/sysfs.c b/net/ieee802154/sysfs.c
index eb9ca6f99122..1613b9c65dfa 100644
--- a/net/ieee802154/sysfs.c
+++ b/net/ieee802154/sysfs.c
@@ -17,6 +17,37 @@
17 17
18#include <net/cfg802154.h> 18#include <net/cfg802154.h>
19 19
20#include "core.h"
21#include "sysfs.h"
22
23static inline struct cfg802154_registered_device *
24dev_to_rdev(struct device *dev)
25{
26 return container_of(dev, struct cfg802154_registered_device,
27 wpan_phy.dev);
28}
29
30#define SHOW_FMT(name, fmt, member) \
31static ssize_t name ## _show(struct device *dev, \
32 struct device_attribute *attr, \
33 char *buf) \
34{ \
35 return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
36} \
37static DEVICE_ATTR_RO(name)
38
39SHOW_FMT(index, "%d", wpan_phy_idx);
40
41static ssize_t name_show(struct device *dev,
42 struct device_attribute *attr,
43 char *buf)
44{
45 struct wpan_phy *wpan_phy = &dev_to_rdev(dev)->wpan_phy;
46
47 return sprintf(buf, "%s\n", dev_name(&wpan_phy->dev));
48}
49static DEVICE_ATTR_RO(name);
50
20#define MASTER_SHOW_COMPLEX(name, format_string, args...) \ 51#define MASTER_SHOW_COMPLEX(name, format_string, args...) \
21static ssize_t name ## _show(struct device *dev, \ 52static ssize_t name ## _show(struct device *dev, \
22 struct device_attribute *attr, char *buf) \ 53 struct device_attribute *attr, char *buf) \
@@ -60,14 +91,17 @@ static ssize_t channels_supported_show(struct device *dev,
60} 91}
61static DEVICE_ATTR_RO(channels_supported); 92static DEVICE_ATTR_RO(channels_supported);
62 93
63static void wpan_phy_release(struct device *d) 94static void wpan_phy_release(struct device *dev)
64{ 95{
65 struct wpan_phy *phy = container_of(d, struct wpan_phy, dev); 96 struct cfg802154_registered_device *rdev = dev_to_rdev(dev);
66 97
67 kfree(phy); 98 cfg802154_dev_free(rdev);
68} 99}
69 100
70static struct attribute *pmib_attrs[] = { 101static struct attribute *pmib_attrs[] = {
102 &dev_attr_index.attr,
103 &dev_attr_name.attr,
104 /* below will be removed soon */
71 &dev_attr_current_channel.attr, 105 &dev_attr_current_channel.attr,
72 &dev_attr_current_page.attr, 106 &dev_attr_current_page.attr,
73 &dev_attr_channels_supported.attr, 107 &dev_attr_channels_supported.attr,