aboutsummaryrefslogtreecommitdiffstats
path: root/net/ieee802154
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2014-11-21 16:39:45 -0500
committerDavid S. Miller <davem@davemloft.net>2014-11-21 16:39:45 -0500
commit53b15ef3c2a6bac8e3d9bb58c5689d731ed9593b (patch)
tree6b3194af697d91ac4c2ab1c716bd30c972cf4949 /net/ieee802154
parentb48c5ec53ef9f0fe617aafa94a752f528fdad149 (diff)
parent9e6f3f472c8f95021ad048acc7cd3e40a827f8ce (diff)
Merge tag 'master-2014-11-20' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next
John W. Linville says: ==================== pull request: wireless-next 2014-11-21 Please pull this batch of updates intended for the 3.19 stream... For the mac80211 bits, Johannes says: "It has been a while since my last pull request, so we accumulated another relatively large set of changes: * TDLS off-channel support set from Arik/Liad, with some support patches I did * custom regulatory fixes from Arik * minstrel VHT fix (and a small optimisation) from Felix * add back radiotap vendor namespace support (myself) * random MAC address scanning for cfg80211/mac80211/hwsim (myself) * CSA improvements (Luca) * WoWLAN Net Detect (wake on network found) support (Luca) * and lots of other smaller changes from many people" For the Bluetooth bits, Johan says: "Here's another set of patches for 3.19. Most of it is again fixes and cleanups to ieee802154 related code from Alexander Aring. We've also got better handling of hardware error events along with a proper API for HCI drivers to notify the HCI core of such situations. There's also a minor fix for mgmt events as well as a sparse warning fix. The code for sending HCI commands synchronously also gets a fix where we might loose the completion event in the case of very fast HW (particularly easily reproducible with an emulated HCI device)." And... "Here's another bluetooth-next pull request for 3.19. We've got: - Various fixes, cleanups and improvements to ieee802154/mac802154 - Support for a Broadcom BCM20702A1 variant - Lots of lockdep fixes - Fixed handling of LE CoC errors that should trigger SMP" For the Atheros bits, Kalle says: "One ath6kl patch and rest for ath10k, but nothing really major which stands out. Most notable: o fix resume (Bartosz) o firmware restart is now faster and more reliable (Michal) o it's now possible to test hardware restart functionality without crashing the firmware using hw-restart parameter with simulate_fw_crash debugfs file (Michal)" On top of that...both ath9k and mwifiex get their usual level of updates. Of note is the ath9k spectral scan work from Oleksij Rempel. I also pulled from the wireless tree in order to avoid some merge issues. Please let me know if there are problems! ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
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,