Linux 4.1.18
[linux/fpc-iii.git] / net / ieee802154 / nl802154.c
blobf3c12f6a4a392ad301e5a79fcabb2bb9f8521431
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.
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.
10 * Authors:
11 * Alexander Aring <aar@pengutronix.de>
13 * Based on: net/wireless/nl80211.c
16 #include <linux/rtnetlink.h>
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>
25 #include "nl802154.h"
26 #include "rdev-ops.h"
27 #include "core.h"
29 static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
30 struct genl_info *info);
32 static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
33 struct genl_info *info);
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,
47 /* multicast groups */
48 enum nl802154_multicast_groups {
49 NL802154_MCGRP_CONFIG,
52 static const struct genl_multicast_group nl802154_mcgrps[] = {
53 [NL802154_MCGRP_CONFIG] = { .name = "config", },
56 /* returns ERR_PTR values */
57 static struct wpan_dev *
58 __cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs)
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;
68 ASSERT_RTNL();
70 if (!have_ifidx && !have_wpan_dev_id)
71 return ERR_PTR(-EINVAL);
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;
80 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
81 struct wpan_dev *wpan_dev;
83 /* TODO netns compare */
85 if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx)
86 continue;
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;
94 if (have_wpan_dev_id &&
95 wpan_dev->identifier == (u32)wpan_dev_id) {
96 result = wpan_dev;
97 break;
101 if (result)
102 break;
105 if (result)
106 return result;
108 return ERR_PTR(-ENODEV);
111 static struct cfg802154_registered_device *
112 __cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
114 struct cfg802154_registered_device *rdev = NULL, *tmp;
115 struct net_device *netdev;
117 ASSERT_RTNL();
119 if (!attrs[NL802154_ATTR_WPAN_PHY] &&
120 !attrs[NL802154_ATTR_IFINDEX] &&
121 !attrs[NL802154_ATTR_WPAN_DEV])
122 return ERR_PTR(-EINVAL);
124 if (attrs[NL802154_ATTR_WPAN_PHY])
125 rdev = cfg802154_rdev_by_wpan_phy_idx(
126 nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY]));
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;
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;
143 if (!found)
144 tmp = NULL;
146 if (rdev && tmp != rdev)
147 return ERR_PTR(-EINVAL);
148 rdev = tmp;
152 if (attrs[NL802154_ATTR_IFINDEX]) {
153 int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
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;
163 /* not wireless device -- return error */
164 if (!tmp)
165 return ERR_PTR(-EINVAL);
167 /* mismatch -- return error */
168 if (rdev && tmp != rdev)
169 return ERR_PTR(-EINVAL);
171 rdev = tmp;
175 if (!rdev)
176 return ERR_PTR(-ENODEV);
178 /* TODO netns compare */
180 return rdev;
183 /* This function returns a pointer to the driver
184 * that the genl_info item that is passed refers to.
186 * The result of this can be a PTR_ERR and hence must
187 * be checked with IS_ERR() for errors.
189 static struct cfg802154_registered_device *
190 cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info)
192 return __cfg802154_rdev_from_attrs(netns, info->attrs);
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 },
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 },
205 [NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 },
207 [NL802154_ATTR_PAGE] = { .type = NLA_U8, },
208 [NL802154_ATTR_CHANNEL] = { .type = NLA_U8, },
210 [NL802154_ATTR_TX_POWER] = { .type = NLA_S8, },
212 [NL802154_ATTR_CCA_MODE] = { .type = NLA_U32, },
213 [NL802154_ATTR_CCA_OPT] = { .type = NLA_U32, },
215 [NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, },
217 [NL802154_ATTR_PAN_ID] = { .type = NLA_U16, },
218 [NL802154_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
219 [NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, },
221 [NL802154_ATTR_MIN_BE] = { .type = NLA_U8, },
222 [NL802154_ATTR_MAX_BE] = { .type = NLA_U8, },
223 [NL802154_ATTR_MAX_CSMA_BACKOFFS] = { .type = NLA_U8, },
225 [NL802154_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_S8, },
227 [NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, },
230 /* message building helper */
231 static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
232 int flags, u8 cmd)
234 /* since there is no private header just add the generic one */
235 return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd);
238 static int
239 nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev,
240 struct sk_buff *msg)
242 struct nlattr *nl_page;
243 unsigned long page;
245 nl_page = nla_nest_start(msg, NL802154_ATTR_CHANNELS_SUPPORTED);
246 if (!nl_page)
247 return -ENOBUFS;
249 for (page = 0; page <= IEEE802154_MAX_PAGE; page++) {
250 if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL,
251 rdev->wpan_phy.channels_supported[page]))
252 return -ENOBUFS;
254 nla_nest_end(msg, nl_page);
256 return 0;
259 static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev,
260 enum nl802154_commands cmd,
261 struct sk_buff *msg, u32 portid, u32 seq,
262 int flags)
264 void *hdr;
266 hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
267 if (!hdr)
268 return -ENOBUFS;
270 if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
271 nla_put_string(msg, NL802154_ATTR_WPAN_PHY_NAME,
272 wpan_phy_name(&rdev->wpan_phy)) ||
273 nla_put_u32(msg, NL802154_ATTR_GENERATION,
274 cfg802154_rdev_list_generation))
275 goto nla_put_failure;
277 if (cmd != NL802154_CMD_NEW_WPAN_PHY)
278 goto finish;
280 /* DUMP PHY PIB */
282 /* current channel settings */
283 if (nla_put_u8(msg, NL802154_ATTR_PAGE,
284 rdev->wpan_phy.current_page) ||
285 nla_put_u8(msg, NL802154_ATTR_CHANNEL,
286 rdev->wpan_phy.current_channel))
287 goto nla_put_failure;
289 /* supported channels array */
290 if (nl802154_send_wpan_phy_channels(rdev, msg))
291 goto nla_put_failure;
293 /* cca mode */
294 if (nla_put_u32(msg, NL802154_ATTR_CCA_MODE,
295 rdev->wpan_phy.cca.mode))
296 goto nla_put_failure;
298 if (rdev->wpan_phy.cca.mode == NL802154_CCA_ENERGY_CARRIER) {
299 if (nla_put_u32(msg, NL802154_ATTR_CCA_OPT,
300 rdev->wpan_phy.cca.opt))
301 goto nla_put_failure;
304 if (nla_put_s8(msg, NL802154_ATTR_TX_POWER,
305 rdev->wpan_phy.transmit_power))
306 goto nla_put_failure;
308 finish:
309 genlmsg_end(msg, hdr);
310 return 0;
312 nla_put_failure:
313 genlmsg_cancel(msg, hdr);
314 return -EMSGSIZE;
317 struct nl802154_dump_wpan_phy_state {
318 s64 filter_wpan_phy;
319 long start;
323 static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb,
324 struct netlink_callback *cb,
325 struct nl802154_dump_wpan_phy_state *state)
327 struct nlattr **tb = nl802154_fam.attrbuf;
328 int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize,
329 tb, nl802154_fam.maxattr, nl802154_policy);
331 /* TODO check if we can handle error here,
332 * we have no backward compatibility
334 if (ret)
335 return 0;
337 if (tb[NL802154_ATTR_WPAN_PHY])
338 state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]);
339 if (tb[NL802154_ATTR_WPAN_DEV])
340 state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32;
341 if (tb[NL802154_ATTR_IFINDEX]) {
342 struct net_device *netdev;
343 struct cfg802154_registered_device *rdev;
344 int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]);
346 /* TODO netns */
347 netdev = __dev_get_by_index(&init_net, ifidx);
348 if (!netdev)
349 return -ENODEV;
350 if (netdev->ieee802154_ptr) {
351 rdev = wpan_phy_to_rdev(
352 netdev->ieee802154_ptr->wpan_phy);
353 state->filter_wpan_phy = rdev->wpan_phy_idx;
357 return 0;
360 static int
361 nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb)
363 int idx = 0, ret;
364 struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0];
365 struct cfg802154_registered_device *rdev;
367 rtnl_lock();
368 if (!state) {
369 state = kzalloc(sizeof(*state), GFP_KERNEL);
370 if (!state) {
371 rtnl_unlock();
372 return -ENOMEM;
374 state->filter_wpan_phy = -1;
375 ret = nl802154_dump_wpan_phy_parse(skb, cb, state);
376 if (ret) {
377 kfree(state);
378 rtnl_unlock();
379 return ret;
381 cb->args[0] = (long)state;
384 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
385 /* TODO net ns compare */
386 if (++idx <= state->start)
387 continue;
388 if (state->filter_wpan_phy != -1 &&
389 state->filter_wpan_phy != rdev->wpan_phy_idx)
390 continue;
391 /* attempt to fit multiple wpan_phy data chunks into the skb */
392 ret = nl802154_send_wpan_phy(rdev,
393 NL802154_CMD_NEW_WPAN_PHY,
394 skb,
395 NETLINK_CB(cb->skb).portid,
396 cb->nlh->nlmsg_seq, NLM_F_MULTI);
397 if (ret < 0) {
398 if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
399 !skb->len && cb->min_dump_alloc < 4096) {
400 cb->min_dump_alloc = 4096;
401 rtnl_unlock();
402 return 1;
404 idx--;
405 break;
407 break;
409 rtnl_unlock();
411 state->start = idx;
413 return skb->len;
416 static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
418 kfree((void *)cb->args[0]);
419 return 0;
422 static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info)
424 struct sk_buff *msg;
425 struct cfg802154_registered_device *rdev = info->user_ptr[0];
427 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
428 if (!msg)
429 return -ENOMEM;
431 if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg,
432 info->snd_portid, info->snd_seq, 0) < 0) {
433 nlmsg_free(msg);
434 return -ENOBUFS;
437 return genlmsg_reply(msg, info);
440 static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev)
442 return (u64)wpan_dev->identifier |
443 ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
446 static int
447 nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
448 struct cfg802154_registered_device *rdev,
449 struct wpan_dev *wpan_dev)
451 struct net_device *dev = wpan_dev->netdev;
452 void *hdr;
454 hdr = nl802154hdr_put(msg, portid, seq, flags,
455 NL802154_CMD_NEW_INTERFACE);
456 if (!hdr)
457 return -1;
459 if (dev &&
460 (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) ||
461 nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name)))
462 goto nla_put_failure;
464 if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
465 nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) ||
466 nla_put_u64(msg, NL802154_ATTR_WPAN_DEV, wpan_dev_id(wpan_dev)) ||
467 nla_put_u32(msg, NL802154_ATTR_GENERATION,
468 rdev->devlist_generation ^
469 (cfg802154_rdev_list_generation << 2)))
470 goto nla_put_failure;
472 /* address settings */
473 if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR,
474 wpan_dev->extended_addr) ||
475 nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR,
476 wpan_dev->short_addr) ||
477 nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id))
478 goto nla_put_failure;
480 /* ARET handling */
481 if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES,
482 wpan_dev->frame_retries) ||
483 nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) ||
484 nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS,
485 wpan_dev->csma_retries) ||
486 nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be))
487 goto nla_put_failure;
489 /* listen before transmit */
490 if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt))
491 goto nla_put_failure;
493 genlmsg_end(msg, hdr);
494 return 0;
496 nla_put_failure:
497 genlmsg_cancel(msg, hdr);
498 return -EMSGSIZE;
501 static int
502 nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
504 int wp_idx = 0;
505 int if_idx = 0;
506 int wp_start = cb->args[0];
507 int if_start = cb->args[1];
508 struct cfg802154_registered_device *rdev;
509 struct wpan_dev *wpan_dev;
511 rtnl_lock();
512 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
513 /* TODO netns compare */
514 if (wp_idx < wp_start) {
515 wp_idx++;
516 continue;
518 if_idx = 0;
520 list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
521 if (if_idx < if_start) {
522 if_idx++;
523 continue;
525 if (nl802154_send_iface(skb, NETLINK_CB(cb->skb).portid,
526 cb->nlh->nlmsg_seq, NLM_F_MULTI,
527 rdev, wpan_dev) < 0) {
528 goto out;
530 if_idx++;
533 wp_idx++;
535 out:
536 rtnl_unlock();
538 cb->args[0] = wp_idx;
539 cb->args[1] = if_idx;
541 return skb->len;
544 static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info)
546 struct sk_buff *msg;
547 struct cfg802154_registered_device *rdev = info->user_ptr[0];
548 struct wpan_dev *wdev = info->user_ptr[1];
550 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
551 if (!msg)
552 return -ENOMEM;
554 if (nl802154_send_iface(msg, info->snd_portid, info->snd_seq, 0,
555 rdev, wdev) < 0) {
556 nlmsg_free(msg);
557 return -ENOBUFS;
560 return genlmsg_reply(msg, info);
563 static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
565 struct cfg802154_registered_device *rdev = info->user_ptr[0];
566 enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC;
567 __le64 extended_addr = cpu_to_le64(0x0000000000000000ULL);
569 /* TODO avoid failing a new interface
570 * creation due to pending removal?
573 if (!info->attrs[NL802154_ATTR_IFNAME])
574 return -EINVAL;
576 if (info->attrs[NL802154_ATTR_IFTYPE]) {
577 type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]);
578 if (type > NL802154_IFTYPE_MAX)
579 return -EINVAL;
582 /* TODO add nla_get_le64 to netlink */
583 if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
584 extended_addr = (__force __le64)nla_get_u64(
585 info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
587 if (!rdev->ops->add_virtual_intf)
588 return -EOPNOTSUPP;
590 return rdev_add_virtual_intf(rdev,
591 nla_data(info->attrs[NL802154_ATTR_IFNAME]),
592 NET_NAME_USER, type, extended_addr);
595 static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info)
597 struct cfg802154_registered_device *rdev = info->user_ptr[0];
598 struct wpan_dev *wpan_dev = info->user_ptr[1];
600 if (!rdev->ops->del_virtual_intf)
601 return -EOPNOTSUPP;
603 /* If we remove a wpan device without a netdev then clear
604 * user_ptr[1] so that nl802154_post_doit won't dereference it
605 * to check if it needs to do dev_put(). Otherwise it crashes
606 * since the wpan_dev has been freed, unlike with a netdev where
607 * we need the dev_put() for the netdev to really be freed.
609 if (!wpan_dev->netdev)
610 info->user_ptr[1] = NULL;
612 return rdev_del_virtual_intf(rdev, wpan_dev);
615 static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info)
617 struct cfg802154_registered_device *rdev = info->user_ptr[0];
618 u8 channel, page;
620 if (!info->attrs[NL802154_ATTR_PAGE] ||
621 !info->attrs[NL802154_ATTR_CHANNEL])
622 return -EINVAL;
624 page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
625 channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]);
627 /* check 802.15.4 constraints */
628 if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL)
629 return -EINVAL;
631 return rdev_set_channel(rdev, page, channel);
634 static int nl802154_set_cca_mode(struct sk_buff *skb, struct genl_info *info)
636 struct cfg802154_registered_device *rdev = info->user_ptr[0];
637 struct wpan_phy_cca cca;
639 if (!info->attrs[NL802154_ATTR_CCA_MODE])
640 return -EINVAL;
642 cca.mode = nla_get_u32(info->attrs[NL802154_ATTR_CCA_MODE]);
643 /* checking 802.15.4 constraints */
644 if (cca.mode < NL802154_CCA_ENERGY || cca.mode > NL802154_CCA_ATTR_MAX)
645 return -EINVAL;
647 if (cca.mode == NL802154_CCA_ENERGY_CARRIER) {
648 if (!info->attrs[NL802154_ATTR_CCA_OPT])
649 return -EINVAL;
651 cca.opt = nla_get_u32(info->attrs[NL802154_ATTR_CCA_OPT]);
652 if (cca.opt > NL802154_CCA_OPT_ATTR_MAX)
653 return -EINVAL;
656 return rdev_set_cca_mode(rdev, &cca);
659 static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
661 struct cfg802154_registered_device *rdev = info->user_ptr[0];
662 struct net_device *dev = info->user_ptr[1];
663 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
664 __le16 pan_id;
666 /* conflict here while tx/rx calls */
667 if (netif_running(dev))
668 return -EBUSY;
670 /* don't change address fields on monitor */
671 if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
672 return -EINVAL;
674 if (!info->attrs[NL802154_ATTR_PAN_ID])
675 return -EINVAL;
677 pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]);
679 return rdev_set_pan_id(rdev, wpan_dev, pan_id);
682 static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info)
684 struct cfg802154_registered_device *rdev = info->user_ptr[0];
685 struct net_device *dev = info->user_ptr[1];
686 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
687 __le16 short_addr;
689 /* conflict here while tx/rx calls */
690 if (netif_running(dev))
691 return -EBUSY;
693 /* don't change address fields on monitor */
694 if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
695 return -EINVAL;
697 if (!info->attrs[NL802154_ATTR_SHORT_ADDR])
698 return -EINVAL;
700 short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]);
702 return rdev_set_short_addr(rdev, wpan_dev, short_addr);
705 static int
706 nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info)
708 struct cfg802154_registered_device *rdev = info->user_ptr[0];
709 struct net_device *dev = info->user_ptr[1];
710 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
711 u8 min_be, max_be;
713 /* should be set on netif open inside phy settings */
714 if (netif_running(dev))
715 return -EBUSY;
717 if (!info->attrs[NL802154_ATTR_MIN_BE] ||
718 !info->attrs[NL802154_ATTR_MAX_BE])
719 return -EINVAL;
721 min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]);
722 max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]);
724 /* check 802.15.4 constraints */
725 if (max_be < 3 || max_be > 8 || min_be > max_be)
726 return -EINVAL;
728 return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be);
731 static int
732 nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info)
734 struct cfg802154_registered_device *rdev = info->user_ptr[0];
735 struct net_device *dev = info->user_ptr[1];
736 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
737 u8 max_csma_backoffs;
739 /* conflict here while other running iface settings */
740 if (netif_running(dev))
741 return -EBUSY;
743 if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS])
744 return -EINVAL;
746 max_csma_backoffs = nla_get_u8(
747 info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]);
749 /* check 802.15.4 constraints */
750 if (max_csma_backoffs > 5)
751 return -EINVAL;
753 return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs);
756 static int
757 nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info)
759 struct cfg802154_registered_device *rdev = info->user_ptr[0];
760 struct net_device *dev = info->user_ptr[1];
761 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
762 s8 max_frame_retries;
764 if (netif_running(dev))
765 return -EBUSY;
767 if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES])
768 return -EINVAL;
770 max_frame_retries = nla_get_s8(
771 info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]);
773 /* check 802.15.4 constraints */
774 if (max_frame_retries < -1 || max_frame_retries > 7)
775 return -EINVAL;
777 return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries);
780 static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info)
782 struct cfg802154_registered_device *rdev = info->user_ptr[0];
783 struct net_device *dev = info->user_ptr[1];
784 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
785 bool mode;
787 if (netif_running(dev))
788 return -EBUSY;
790 if (!info->attrs[NL802154_ATTR_LBT_MODE])
791 return -EINVAL;
793 mode = !!nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]);
794 return rdev_set_lbt_mode(rdev, wpan_dev, mode);
797 #define NL802154_FLAG_NEED_WPAN_PHY 0x01
798 #define NL802154_FLAG_NEED_NETDEV 0x02
799 #define NL802154_FLAG_NEED_RTNL 0x04
800 #define NL802154_FLAG_CHECK_NETDEV_UP 0x08
801 #define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\
802 NL802154_FLAG_CHECK_NETDEV_UP)
803 #define NL802154_FLAG_NEED_WPAN_DEV 0x10
804 #define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\
805 NL802154_FLAG_CHECK_NETDEV_UP)
807 static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
808 struct genl_info *info)
810 struct cfg802154_registered_device *rdev;
811 struct wpan_dev *wpan_dev;
812 struct net_device *dev;
813 bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
815 if (rtnl)
816 rtnl_lock();
818 if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
819 rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
820 if (IS_ERR(rdev)) {
821 if (rtnl)
822 rtnl_unlock();
823 return PTR_ERR(rdev);
825 info->user_ptr[0] = rdev;
826 } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
827 ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
828 ASSERT_RTNL();
829 wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
830 info->attrs);
831 if (IS_ERR(wpan_dev)) {
832 if (rtnl)
833 rtnl_unlock();
834 return PTR_ERR(wpan_dev);
837 dev = wpan_dev->netdev;
838 rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
840 if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
841 if (!dev) {
842 if (rtnl)
843 rtnl_unlock();
844 return -EINVAL;
847 info->user_ptr[1] = dev;
848 } else {
849 info->user_ptr[1] = wpan_dev;
852 if (dev) {
853 if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
854 !netif_running(dev)) {
855 if (rtnl)
856 rtnl_unlock();
857 return -ENETDOWN;
860 dev_hold(dev);
863 info->user_ptr[0] = rdev;
866 return 0;
869 static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
870 struct genl_info *info)
872 if (info->user_ptr[1]) {
873 if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
874 struct wpan_dev *wpan_dev = info->user_ptr[1];
876 if (wpan_dev->netdev)
877 dev_put(wpan_dev->netdev);
878 } else {
879 dev_put(info->user_ptr[1]);
883 if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
884 rtnl_unlock();
887 static const struct genl_ops nl802154_ops[] = {
889 .cmd = NL802154_CMD_GET_WPAN_PHY,
890 .doit = nl802154_get_wpan_phy,
891 .dumpit = nl802154_dump_wpan_phy,
892 .done = nl802154_dump_wpan_phy_done,
893 .policy = nl802154_policy,
894 /* can be retrieved by unprivileged users */
895 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
896 NL802154_FLAG_NEED_RTNL,
899 .cmd = NL802154_CMD_GET_INTERFACE,
900 .doit = nl802154_get_interface,
901 .dumpit = nl802154_dump_interface,
902 .policy = nl802154_policy,
903 /* can be retrieved by unprivileged users */
904 .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
905 NL802154_FLAG_NEED_RTNL,
908 .cmd = NL802154_CMD_NEW_INTERFACE,
909 .doit = nl802154_new_interface,
910 .policy = nl802154_policy,
911 .flags = GENL_ADMIN_PERM,
912 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
913 NL802154_FLAG_NEED_RTNL,
916 .cmd = NL802154_CMD_DEL_INTERFACE,
917 .doit = nl802154_del_interface,
918 .policy = nl802154_policy,
919 .flags = GENL_ADMIN_PERM,
920 .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
921 NL802154_FLAG_NEED_RTNL,
924 .cmd = NL802154_CMD_SET_CHANNEL,
925 .doit = nl802154_set_channel,
926 .policy = nl802154_policy,
927 .flags = GENL_ADMIN_PERM,
928 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
929 NL802154_FLAG_NEED_RTNL,
932 .cmd = NL802154_CMD_SET_CCA_MODE,
933 .doit = nl802154_set_cca_mode,
934 .policy = nl802154_policy,
935 .flags = GENL_ADMIN_PERM,
936 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
937 NL802154_FLAG_NEED_RTNL,
940 .cmd = NL802154_CMD_SET_PAN_ID,
941 .doit = nl802154_set_pan_id,
942 .policy = nl802154_policy,
943 .flags = GENL_ADMIN_PERM,
944 .internal_flags = NL802154_FLAG_NEED_NETDEV |
945 NL802154_FLAG_NEED_RTNL,
948 .cmd = NL802154_CMD_SET_SHORT_ADDR,
949 .doit = nl802154_set_short_addr,
950 .policy = nl802154_policy,
951 .flags = GENL_ADMIN_PERM,
952 .internal_flags = NL802154_FLAG_NEED_NETDEV |
953 NL802154_FLAG_NEED_RTNL,
956 .cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
957 .doit = nl802154_set_backoff_exponent,
958 .policy = nl802154_policy,
959 .flags = GENL_ADMIN_PERM,
960 .internal_flags = NL802154_FLAG_NEED_NETDEV |
961 NL802154_FLAG_NEED_RTNL,
964 .cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
965 .doit = nl802154_set_max_csma_backoffs,
966 .policy = nl802154_policy,
967 .flags = GENL_ADMIN_PERM,
968 .internal_flags = NL802154_FLAG_NEED_NETDEV |
969 NL802154_FLAG_NEED_RTNL,
972 .cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
973 .doit = nl802154_set_max_frame_retries,
974 .policy = nl802154_policy,
975 .flags = GENL_ADMIN_PERM,
976 .internal_flags = NL802154_FLAG_NEED_NETDEV |
977 NL802154_FLAG_NEED_RTNL,
980 .cmd = NL802154_CMD_SET_LBT_MODE,
981 .doit = nl802154_set_lbt_mode,
982 .policy = nl802154_policy,
983 .flags = GENL_ADMIN_PERM,
984 .internal_flags = NL802154_FLAG_NEED_NETDEV |
985 NL802154_FLAG_NEED_RTNL,
989 /* initialisation/exit functions */
990 int nl802154_init(void)
992 return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops,
993 nl802154_mcgrps);
996 void nl802154_exit(void)
998 genl_unregister_family(&nl802154_fam);