diff options
Diffstat (limited to 'net/ieee802154')
-rw-r--r-- | net/ieee802154/6lowpan_rtnl.c | 38 | ||||
-rw-r--r-- | net/ieee802154/Makefile | 2 | ||||
-rw-r--r-- | net/ieee802154/core.c | 220 | ||||
-rw-r--r-- | net/ieee802154/core.h | 46 | ||||
-rw-r--r-- | net/ieee802154/ieee802154.h | 2 | ||||
-rw-r--r-- | net/ieee802154/netlink.c | 2 | ||||
-rw-r--r-- | net/ieee802154/nl-mac.c | 226 | ||||
-rw-r--r-- | net/ieee802154/nl-phy.c | 23 | ||||
-rw-r--r-- | net/ieee802154/nl802154.c | 957 | ||||
-rw-r--r-- | net/ieee802154/nl802154.h | 7 | ||||
-rw-r--r-- | net/ieee802154/rdev-ops.h | 89 | ||||
-rw-r--r-- | net/ieee802154/sysfs.c | 40 |
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 | |||
204 | drop: | ||
205 | kfree_skb(skb); | ||
206 | return -EINVAL; | ||
207 | } | ||
208 | |||
209 | static 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 | ||
222 | static struct sk_buff* | 205 | static 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 | ||
423 | static 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 | |||
430 | static __le16 lowpan_get_pan_id(const struct net_device *dev) | 406 | static __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) | |||
474 | static const struct net_device_ops lowpan_netdev_ops = { | 450 | static 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 | ||
480 | static struct ieee802154_mlme_ops lowpan_mlme = { | 455 | static 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 | ||
4 | ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o | 4 | ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o |
5 | ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \ | 5 | ieee802154-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 |
7 | af_802154-y := af_ieee802154.o raw.o dgram.o | 7 | af_802154-y := af_ieee802154.o raw.o dgram.o |
8 | 8 | ||
9 | ccflags-y += -D__CHECK_ENDIAN__ | 9 | ccflags-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 | ||
25 | static DEFINE_MUTEX(wpan_phy_mutex); | 28 | /* RCU-protected (and RTNL for writers) */ |
26 | static int wpan_phy_idx; | 29 | LIST_HEAD(cfg802154_rdev_list); |
30 | int cfg802154_rdev_list_generation; | ||
27 | 31 | ||
28 | static int wpan_phy_match(struct device *dev, const void *data) | 32 | static 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 | } |
72 | EXPORT_SYMBOL(wpan_phy_for_each); | 76 | EXPORT_SYMBOL(wpan_phy_for_each); |
73 | 77 | ||
74 | static int wpan_phy_idx_valid(int idx) | 78 | struct cfg802154_registered_device * |
79 | cfg802154_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 | ||
79 | struct wpan_phy *wpan_phy_alloc(size_t priv_size) | 95 | struct wpan_phy * |
96 | wpan_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 | ||
108 | out: | 132 | return &rdev->wpan_phy; |
109 | return NULL; | ||
110 | } | 133 | } |
111 | EXPORT_SYMBOL(wpan_phy_alloc); | 134 | EXPORT_SYMBOL(wpan_phy_new); |
112 | 135 | ||
113 | int wpan_phy_register(struct wpan_phy *phy) | 136 | int 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 | } |
117 | EXPORT_SYMBOL(wpan_phy_register); | 158 | EXPORT_SYMBOL(wpan_phy_register); |
118 | 159 | ||
119 | void wpan_phy_unregister(struct wpan_phy *phy) | 160 | void 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 | } |
123 | EXPORT_SYMBOL(wpan_phy_unregister); | 189 | EXPORT_SYMBOL(wpan_phy_unregister); |
124 | 190 | ||
@@ -128,6 +194,84 @@ void wpan_phy_free(struct wpan_phy *phy) | |||
128 | } | 194 | } |
129 | EXPORT_SYMBOL(wpan_phy_free); | 195 | EXPORT_SYMBOL(wpan_phy_free); |
130 | 196 | ||
197 | void cfg802154_dev_free(struct cfg802154_registered_device *rdev) | ||
198 | { | ||
199 | kfree(rdev); | ||
200 | } | ||
201 | |||
202 | static void | ||
203 | cfg802154_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 | |||
211 | static 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 | |||
271 | static struct notifier_block cfg802154_netdev_notifier = { | ||
272 | .notifier_call = cfg802154_netdev_notifier_call, | ||
273 | }; | ||
274 | |||
131 | static int __init wpan_phy_class_init(void) | 275 | static 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 | |||
297 | err_ieee802154_nl: | ||
298 | ieee802154_nl_exit(); | ||
299 | |||
300 | err_notifier: | ||
301 | unregister_netdevice_notifier(&cfg802154_netdev_notifier); | ||
144 | err_nl: | 302 | err_nl: |
145 | wpan_phy_sysfs_exit(); | 303 | wpan_phy_sysfs_exit(); |
146 | err: | 304 | err: |
@@ -150,7 +308,9 @@ subsys_initcall(wpan_phy_class_init); | |||
150 | 308 | ||
151 | static void __exit wpan_phy_class_exit(void) | 309 | static 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 | } |
156 | module_exit(wpan_phy_class_exit); | 316 | module_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 | |||
6 | struct 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 | |||
30 | static inline struct cfg802154_registered_device * | ||
31 | wpan_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 | |||
38 | extern struct list_head cfg802154_rdev_list; | ||
39 | extern int cfg802154_rdev_list_generation; | ||
40 | |||
41 | /* free object */ | ||
42 | void cfg802154_dev_free(struct cfg802154_registered_device *rdev); | ||
43 | struct cfg802154_registered_device * | ||
44 | cfg802154_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 | ||
17 | int __init ieee802154_nl_init(void); | 17 | int __init ieee802154_nl_init(void); |
18 | void __exit ieee802154_nl_exit(void); | 18 | void 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 | ||
158 | void __exit ieee802154_nl_exit(void) | 158 | void 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 | ||
58 | int ieee802154_nl_assoc_indic(struct net_device *dev, | 57 | static 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 | |||
86 | nla_put_failure: | ||
87 | nlmsg_free(msg); | ||
88 | return -ENOBUFS; | ||
89 | } | ||
90 | EXPORT_SYMBOL(ieee802154_nl_assoc_indic); | ||
91 | |||
92 | int 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 | |||
112 | nla_put_failure: | ||
113 | nlmsg_free(msg); | ||
114 | return -ENOBUFS; | ||
115 | } | ||
116 | EXPORT_SYMBOL(ieee802154_nl_assoc_confirm); | ||
117 | |||
118 | int 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 | |||
148 | nla_put_failure: | ||
149 | nlmsg_free(msg); | ||
150 | return -ENOBUFS; | ||
151 | } | ||
152 | EXPORT_SYMBOL(ieee802154_nl_disassoc_indic); | ||
153 | |||
154 | int 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 | |||
172 | nla_put_failure: | ||
173 | nlmsg_free(msg); | ||
174 | return -ENOBUFS; | ||
175 | } | ||
176 | EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm); | ||
177 | |||
178 | int 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 | |||
199 | nla_put_failure: | ||
200 | nlmsg_free(msg); | ||
201 | return -ENOBUFS; | ||
202 | } | ||
203 | EXPORT_SYMBOL(ieee802154_nl_beacon_indic); | ||
204 | |||
205 | int 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 | |||
231 | nla_put_failure: | ||
232 | nlmsg_free(msg); | ||
233 | return -ENOBUFS; | ||
234 | } | ||
235 | EXPORT_SYMBOL(ieee802154_nl_scan_confirm); | ||
236 | |||
237 | int 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, ¶ms); | 117 | ops->get_mac_params(dev, ¶ms); |
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 | ||
527 | out: | 366 | out: |
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, ¶ms); | 512 | ops->get_mac_params(dev, ¶ms); |
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, ¶ms); | 538 | rc = ops->set_mac_params(dev, ¶ms); |
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 | ||
34 | static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid, | 36 | static 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 | ||
258 | dev_unregister: | 257 | dev_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(); |
263 | nla_put_failure: | 262 | nla_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 | |||
29 | static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, | ||
30 | struct genl_info *info); | ||
31 | |||
32 | static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb, | ||
33 | struct genl_info *info); | ||
34 | |||
35 | /* the netlink family */ | ||
36 | static 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 */ | ||
48 | enum nl802154_multicast_groups { | ||
49 | NL802154_MCGRP_CONFIG, | ||
50 | }; | ||
51 | |||
52 | static const struct genl_multicast_group nl802154_mcgrps[] = { | ||
53 | [NL802154_MCGRP_CONFIG] = { .name = "config", }, | ||
54 | }; | ||
55 | |||
56 | /* returns ERR_PTR values */ | ||
57 | static 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 | |||
111 | static 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 | */ | ||
189 | static struct cfg802154_registered_device * | ||
190 | cfg802154_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 */ | ||
196 | static 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 */ | ||
230 | static 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 | |||
237 | static int | ||
238 | nl802154_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 | |||
258 | static 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 | |||
301 | finish: | ||
302 | return genlmsg_end(msg, hdr); | ||
303 | |||
304 | nla_put_failure: | ||
305 | genlmsg_cancel(msg, hdr); | ||
306 | return -EMSGSIZE; | ||
307 | } | ||
308 | |||
309 | struct nl802154_dump_wpan_phy_state { | ||
310 | s64 filter_wpan_phy; | ||
311 | long start; | ||
312 | |||
313 | }; | ||
314 | |||
315 | static 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 | |||
352 | static int | ||
353 | nl802154_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 | |||
408 | static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb) | ||
409 | { | ||
410 | kfree((void *)cb->args[0]); | ||
411 | return 0; | ||
412 | } | ||
413 | |||
414 | static 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 | |||
432 | static 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 | |||
438 | static int | ||
439 | nl802154_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 | |||
487 | nla_put_failure: | ||
488 | genlmsg_cancel(msg, hdr); | ||
489 | return -EMSGSIZE; | ||
490 | } | ||
491 | |||
492 | static int | ||
493 | nl802154_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 | } | ||
526 | out: | ||
527 | rtnl_unlock(); | ||
528 | |||
529 | cb->args[0] = wp_idx; | ||
530 | cb->args[1] = if_idx; | ||
531 | |||
532 | return skb->len; | ||
533 | } | ||
534 | |||
535 | static 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 | |||
554 | static 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 | |||
586 | static 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 | |||
606 | static 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 | |||
625 | static 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 | |||
648 | static 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 | |||
671 | static int | ||
672 | nl802154_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 | |||
697 | static int | ||
698 | nl802154_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 | |||
722 | static int | ||
723 | nl802154_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 | |||
746 | static 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 | |||
773 | static 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 | |||
835 | static 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 | |||
853 | static 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 */ | ||
948 | int nl802154_init(void) | ||
949 | { | ||
950 | return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops, | ||
951 | nl802154_mcgrps); | ||
952 | } | ||
953 | |||
954 | void 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 | |||
4 | int nl802154_init(void); | ||
5 | void 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 | |||
8 | static inline struct net_device * | ||
9 | rdev_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 | |||
16 | static inline void | ||
17 | rdev_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 | |||
23 | static inline int | ||
24 | rdev_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 | |||
31 | static inline int | ||
32 | rdev_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 | |||
38 | static inline int | ||
39 | rdev_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 | |||
44 | static inline int | ||
45 | rdev_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 | |||
51 | static inline int | ||
52 | rdev_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 | |||
58 | static inline int | ||
59 | rdev_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 | |||
66 | static inline int | ||
67 | rdev_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 | |||
74 | static inline int | ||
75 | rdev_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 | |||
82 | static inline int | ||
83 | rdev_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 | |||
23 | static inline struct cfg802154_registered_device * | ||
24 | dev_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) \ | ||
31 | static 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 | } \ | ||
37 | static DEVICE_ATTR_RO(name) | ||
38 | |||
39 | SHOW_FMT(index, "%d", wpan_phy_idx); | ||
40 | |||
41 | static 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 | } | ||
49 | static DEVICE_ATTR_RO(name); | ||
50 | |||
20 | #define MASTER_SHOW_COMPLEX(name, format_string, args...) \ | 51 | #define MASTER_SHOW_COMPLEX(name, format_string, args...) \ |
21 | static ssize_t name ## _show(struct device *dev, \ | 52 | static 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 | } |
61 | static DEVICE_ATTR_RO(channels_supported); | 92 | static DEVICE_ATTR_RO(channels_supported); |
62 | 93 | ||
63 | static void wpan_phy_release(struct device *d) | 94 | static 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 | ||
70 | static struct attribute *pmib_attrs[] = { | 101 | static 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, |