]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/blame - net/ieee802154/nl802154.c
cfg802154: introduce wpan phy flags
[mirror_ubuntu-eoan-kernel.git] / net / ieee802154 / nl802154.c
CommitLineData
79fe1a2a
AA
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"
ab0bd561 26#include "rdev-ops.h"
79fe1a2a
AA
27#include "core.h"
28
29static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
30 struct genl_info *info);
31
32static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
33 struct genl_info *info);
34
35/* the netlink family */
36static struct genl_family nl802154_fam = {
37 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
38 .name = NL802154_GENL_NAME, /* have users key off the name instead */
39 .hdrsize = 0, /* no private header */
40 .version = 1, /* no particular meaning now */
41 .maxattr = NL802154_ATTR_MAX,
42 .netnsok = true,
43 .pre_doit = nl802154_pre_doit,
44 .post_doit = nl802154_post_doit,
45};
46
47/* multicast groups */
48enum nl802154_multicast_groups {
49 NL802154_MCGRP_CONFIG,
50};
51
52static const struct genl_multicast_group nl802154_mcgrps[] = {
53 [NL802154_MCGRP_CONFIG] = { .name = "config", },
54};
55
56/* returns ERR_PTR values */
57static struct wpan_dev *
58__cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs)
59{
60 struct cfg802154_registered_device *rdev;
61 struct wpan_dev *result = NULL;
62 bool have_ifidx = attrs[NL802154_ATTR_IFINDEX];
63 bool have_wpan_dev_id = attrs[NL802154_ATTR_WPAN_DEV];
64 u64 wpan_dev_id;
65 int wpan_phy_idx = -1;
66 int ifidx = -1;
67
68 ASSERT_RTNL();
69
70 if (!have_ifidx && !have_wpan_dev_id)
71 return ERR_PTR(-EINVAL);
72
73 if (have_ifidx)
74 ifidx = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
75 if (have_wpan_dev_id) {
76 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
77 wpan_phy_idx = wpan_dev_id >> 32;
78 }
79
80 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
81 struct wpan_dev *wpan_dev;
82
83 /* TODO netns compare */
84
85 if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx)
86 continue;
87
88 list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
89 if (have_ifidx && wpan_dev->netdev &&
90 wpan_dev->netdev->ifindex == ifidx) {
91 result = wpan_dev;
92 break;
93 }
94 if (have_wpan_dev_id &&
95 wpan_dev->identifier == (u32)wpan_dev_id) {
96 result = wpan_dev;
97 break;
98 }
99 }
100
101 if (result)
102 break;
103 }
104
105 if (result)
106 return result;
107
108 return ERR_PTR(-ENODEV);
109}
110
111static struct cfg802154_registered_device *
112__cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
113{
114 struct cfg802154_registered_device *rdev = NULL, *tmp;
115 struct net_device *netdev;
116
117 ASSERT_RTNL();
118
119 if (!attrs[NL802154_ATTR_WPAN_PHY] &&
120 !attrs[NL802154_ATTR_IFINDEX] &&
121 !attrs[NL802154_ATTR_WPAN_DEV])
122 return ERR_PTR(-EINVAL);
123
124 if (attrs[NL802154_ATTR_WPAN_PHY])
125 rdev = cfg802154_rdev_by_wpan_phy_idx(
126 nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY]));
127
128 if (attrs[NL802154_ATTR_WPAN_DEV]) {
129 u64 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
130 struct wpan_dev *wpan_dev;
131 bool found = false;
132
133 tmp = cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id >> 32);
134 if (tmp) {
135 /* make sure wpan_dev exists */
136 list_for_each_entry(wpan_dev, &tmp->wpan_dev_list, list) {
137 if (wpan_dev->identifier != (u32)wpan_dev_id)
138 continue;
139 found = true;
140 break;
141 }
142
143 if (!found)
144 tmp = NULL;
145
146 if (rdev && tmp != rdev)
147 return ERR_PTR(-EINVAL);
148 rdev = tmp;
149 }
150 }
151
152 if (attrs[NL802154_ATTR_IFINDEX]) {
153 int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
154
155 netdev = __dev_get_by_index(netns, ifindex);
156 if (netdev) {
157 if (netdev->ieee802154_ptr)
158 tmp = wpan_phy_to_rdev(
159 netdev->ieee802154_ptr->wpan_phy);
160 else
161 tmp = NULL;
162
163 /* not wireless device -- return error */
164 if (!tmp)
165 return ERR_PTR(-EINVAL);
166
167 /* mismatch -- return error */
168 if (rdev && tmp != rdev)
169 return ERR_PTR(-EINVAL);
170
171 rdev = tmp;
172 }
173 }
174
175 if (!rdev)
176 return ERR_PTR(-ENODEV);
177
178 /* TODO netns compare */
179
180 return rdev;
181}
182
183/* This function returns a pointer to the driver
184 * that the genl_info item that is passed refers to.
185 *
186 * The result of this can be a PTR_ERR and hence must
187 * be checked with IS_ERR() for errors.
188 */
189static struct cfg802154_registered_device *
190cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info)
191{
192 return __cfg802154_rdev_from_attrs(netns, info->attrs);
193}
194
195/* policy for the attributes */
196static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
ca20ce20
AA
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 },
4b96aea0
AA
202 [NL802154_ATTR_IFTYPE] = { .type = NLA_U32 },
203 [NL802154_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
ca20ce20
AA
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
1a19cb68 210 [NL802154_ATTR_TX_POWER] = { .type = NLA_S32, },
ca20ce20 211
ba2a9506
AA
212 [NL802154_ATTR_CCA_MODE] = { .type = NLA_U32, },
213 [NL802154_ATTR_CCA_OPT] = { .type = NLA_U32, },
ca20ce20
AA
214
215 [NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, },
4b96aea0
AA
216
217 [NL802154_ATTR_PAN_ID] = { .type = NLA_U16, },
218 [NL802154_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
219 [NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, },
220
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, },
224
225 [NL802154_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_S8, },
226
227 [NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, },
79fe1a2a
AA
228};
229
230/* message building helper */
231static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
232 int flags, u8 cmd)
233{
234 /* since there is no private header just add the generic one */
235 return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd);
236}
237
ca20ce20
AA
238static int
239nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev,
240 struct sk_buff *msg)
241{
242 struct nlattr *nl_page;
243 unsigned long page;
244
245 nl_page = nla_nest_start(msg, NL802154_ATTR_CHANNELS_SUPPORTED);
246 if (!nl_page)
247 return -ENOBUFS;
248
cb41c8dd 249 for (page = 0; page <= IEEE802154_MAX_PAGE; page++) {
ca20ce20 250 if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL,
72f655e4 251 rdev->wpan_phy.supported.channels[page]))
ca20ce20
AA
252 return -ENOBUFS;
253 }
254 nla_nest_end(msg, nl_page);
255
256 return 0;
257}
258
259static 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)
263{
264 void *hdr;
265
266 hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
267 if (!hdr)
268 return -ENOBUFS;
269
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;
276
277 if (cmd != NL802154_CMD_NEW_WPAN_PHY)
278 goto finish;
279
280 /* DUMP PHY PIB */
281
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;
288
289 /* supported channels array */
290 if (nl802154_send_wpan_phy_channels(rdev, msg))
291 goto nla_put_failure;
292
293 /* cca mode */
edea8f7c
AA
294 if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE) {
295 if (nla_put_u32(msg, NL802154_ATTR_CCA_MODE,
296 rdev->wpan_phy.cca.mode))
ba2a9506 297 goto nla_put_failure;
edea8f7c
AA
298
299 if (rdev->wpan_phy.cca.mode == NL802154_CCA_ENERGY_CARRIER) {
300 if (nla_put_u32(msg, NL802154_ATTR_CCA_OPT,
301 rdev->wpan_phy.cca.opt))
302 goto nla_put_failure;
303 }
ba2a9506
AA
304 }
305
edea8f7c
AA
306 if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER) {
307 if (nla_put_s32(msg, NL802154_ATTR_TX_POWER,
308 rdev->wpan_phy.transmit_power))
309 goto nla_put_failure;
310 }
ca20ce20
AA
311
312finish:
053c095a
JB
313 genlmsg_end(msg, hdr);
314 return 0;
ca20ce20
AA
315
316nla_put_failure:
317 genlmsg_cancel(msg, hdr);
318 return -EMSGSIZE;
319}
320
321struct nl802154_dump_wpan_phy_state {
322 s64 filter_wpan_phy;
323 long start;
324
325};
326
327static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb,
328 struct netlink_callback *cb,
329 struct nl802154_dump_wpan_phy_state *state)
330{
331 struct nlattr **tb = nl802154_fam.attrbuf;
332 int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize,
333 tb, nl802154_fam.maxattr, nl802154_policy);
334
335 /* TODO check if we can handle error here,
336 * we have no backward compatibility
337 */
338 if (ret)
339 return 0;
340
341 if (tb[NL802154_ATTR_WPAN_PHY])
342 state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]);
343 if (tb[NL802154_ATTR_WPAN_DEV])
344 state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32;
345 if (tb[NL802154_ATTR_IFINDEX]) {
346 struct net_device *netdev;
347 struct cfg802154_registered_device *rdev;
348 int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]);
349
350 /* TODO netns */
351 netdev = __dev_get_by_index(&init_net, ifidx);
352 if (!netdev)
353 return -ENODEV;
354 if (netdev->ieee802154_ptr) {
355 rdev = wpan_phy_to_rdev(
356 netdev->ieee802154_ptr->wpan_phy);
357 state->filter_wpan_phy = rdev->wpan_phy_idx;
358 }
359 }
360
361 return 0;
362}
363
364static int
365nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb)
366{
367 int idx = 0, ret;
368 struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0];
369 struct cfg802154_registered_device *rdev;
370
371 rtnl_lock();
372 if (!state) {
373 state = kzalloc(sizeof(*state), GFP_KERNEL);
374 if (!state) {
375 rtnl_unlock();
376 return -ENOMEM;
377 }
378 state->filter_wpan_phy = -1;
379 ret = nl802154_dump_wpan_phy_parse(skb, cb, state);
380 if (ret) {
381 kfree(state);
382 rtnl_unlock();
383 return ret;
384 }
385 cb->args[0] = (long)state;
386 }
387
388 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
389 /* TODO net ns compare */
390 if (++idx <= state->start)
391 continue;
392 if (state->filter_wpan_phy != -1 &&
393 state->filter_wpan_phy != rdev->wpan_phy_idx)
394 continue;
395 /* attempt to fit multiple wpan_phy data chunks into the skb */
396 ret = nl802154_send_wpan_phy(rdev,
397 NL802154_CMD_NEW_WPAN_PHY,
398 skb,
399 NETLINK_CB(cb->skb).portid,
400 cb->nlh->nlmsg_seq, NLM_F_MULTI);
401 if (ret < 0) {
402 if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
403 !skb->len && cb->min_dump_alloc < 4096) {
404 cb->min_dump_alloc = 4096;
405 rtnl_unlock();
406 return 1;
407 }
408 idx--;
409 break;
410 }
411 break;
412 }
413 rtnl_unlock();
414
415 state->start = idx;
416
417 return skb->len;
418}
419
420static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
421{
422 kfree((void *)cb->args[0]);
423 return 0;
424}
425
426static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info)
427{
428 struct sk_buff *msg;
429 struct cfg802154_registered_device *rdev = info->user_ptr[0];
430
431 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
432 if (!msg)
433 return -ENOMEM;
434
435 if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg,
436 info->snd_portid, info->snd_seq, 0) < 0) {
437 nlmsg_free(msg);
438 return -ENOBUFS;
439 }
440
441 return genlmsg_reply(msg, info);
442}
443
4b96aea0
AA
444static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev)
445{
446 return (u64)wpan_dev->identifier |
447 ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
448}
449
450static int
451nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
452 struct cfg802154_registered_device *rdev,
453 struct wpan_dev *wpan_dev)
454{
455 struct net_device *dev = wpan_dev->netdev;
456 void *hdr;
457
458 hdr = nl802154hdr_put(msg, portid, seq, flags,
459 NL802154_CMD_NEW_INTERFACE);
460 if (!hdr)
461 return -1;
462
463 if (dev &&
464 (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) ||
465 nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name)))
466 goto nla_put_failure;
467
468 if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
469 nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) ||
470 nla_put_u64(msg, NL802154_ATTR_WPAN_DEV, wpan_dev_id(wpan_dev)) ||
471 nla_put_u32(msg, NL802154_ATTR_GENERATION,
472 rdev->devlist_generation ^
473 (cfg802154_rdev_list_generation << 2)))
474 goto nla_put_failure;
475
476 /* address settings */
477 if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR,
478 wpan_dev->extended_addr) ||
479 nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR,
480 wpan_dev->short_addr) ||
481 nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id))
482 goto nla_put_failure;
483
484 /* ARET handling */
485 if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES,
486 wpan_dev->frame_retries) ||
487 nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) ||
488 nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS,
489 wpan_dev->csma_retries) ||
490 nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be))
491 goto nla_put_failure;
492
493 /* listen before transmit */
494 if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt))
495 goto nla_put_failure;
496
053c095a
JB
497 genlmsg_end(msg, hdr);
498 return 0;
4b96aea0
AA
499
500nla_put_failure:
501 genlmsg_cancel(msg, hdr);
502 return -EMSGSIZE;
503}
504
505static int
506nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
507{
508 int wp_idx = 0;
509 int if_idx = 0;
510 int wp_start = cb->args[0];
511 int if_start = cb->args[1];
512 struct cfg802154_registered_device *rdev;
513 struct wpan_dev *wpan_dev;
514
515 rtnl_lock();
516 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
517 /* TODO netns compare */
518 if (wp_idx < wp_start) {
519 wp_idx++;
520 continue;
521 }
522 if_idx = 0;
523
524 list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
525 if (if_idx < if_start) {
526 if_idx++;
527 continue;
528 }
529 if (nl802154_send_iface(skb, NETLINK_CB(cb->skb).portid,
530 cb->nlh->nlmsg_seq, NLM_F_MULTI,
531 rdev, wpan_dev) < 0) {
532 goto out;
533 }
534 if_idx++;
535 }
536
537 wp_idx++;
538 }
539out:
540 rtnl_unlock();
541
542 cb->args[0] = wp_idx;
543 cb->args[1] = if_idx;
544
545 return skb->len;
546}
547
548static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info)
549{
550 struct sk_buff *msg;
551 struct cfg802154_registered_device *rdev = info->user_ptr[0];
552 struct wpan_dev *wdev = info->user_ptr[1];
553
554 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
555 if (!msg)
556 return -ENOMEM;
557
558 if (nl802154_send_iface(msg, info->snd_portid, info->snd_seq, 0,
559 rdev, wdev) < 0) {
560 nlmsg_free(msg);
561 return -ENOBUFS;
562 }
563
564 return genlmsg_reply(msg, info);
565}
566
f3ea5e44
AA
567static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
568{
569 struct cfg802154_registered_device *rdev = info->user_ptr[0];
570 enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC;
0e57547e 571 __le64 extended_addr = cpu_to_le64(0x0000000000000000ULL);
f3ea5e44
AA
572
573 /* TODO avoid failing a new interface
574 * creation due to pending removal?
575 */
576
577 if (!info->attrs[NL802154_ATTR_IFNAME])
578 return -EINVAL;
579
580 if (info->attrs[NL802154_ATTR_IFTYPE]) {
581 type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]);
582 if (type > NL802154_IFTYPE_MAX)
583 return -EINVAL;
584 }
585
0e57547e
AA
586 /* TODO add nla_get_le64 to netlink */
587 if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
588 extended_addr = (__force __le64)nla_get_u64(
589 info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
590
f3ea5e44
AA
591 if (!rdev->ops->add_virtual_intf)
592 return -EOPNOTSUPP;
593
594 return rdev_add_virtual_intf(rdev,
595 nla_data(info->attrs[NL802154_ATTR_IFNAME]),
5b4a1039 596 NET_NAME_USER, type, extended_addr);
f3ea5e44
AA
597}
598
b821ecd4
AA
599static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info)
600{
601 struct cfg802154_registered_device *rdev = info->user_ptr[0];
602 struct wpan_dev *wpan_dev = info->user_ptr[1];
603
604 if (!rdev->ops->del_virtual_intf)
605 return -EOPNOTSUPP;
606
607 /* If we remove a wpan device without a netdev then clear
608 * user_ptr[1] so that nl802154_post_doit won't dereference it
609 * to check if it needs to do dev_put(). Otherwise it crashes
610 * since the wpan_dev has been freed, unlike with a netdev where
611 * we need the dev_put() for the netdev to really be freed.
612 */
613 if (!wpan_dev->netdev)
614 info->user_ptr[1] = NULL;
615
616 return rdev_del_virtual_intf(rdev, wpan_dev);
617}
618
ab0bd561
AA
619static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info)
620{
621 struct cfg802154_registered_device *rdev = info->user_ptr[0];
622 u8 channel, page;
623
624 if (!info->attrs[NL802154_ATTR_PAGE] ||
625 !info->attrs[NL802154_ATTR_CHANNEL])
626 return -EINVAL;
627
628 page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
629 channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]);
630
631 /* check 802.15.4 constraints */
673692fa 632 if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL ||
72f655e4 633 !(rdev->wpan_phy.supported.channels[page] & BIT(channel)))
ab0bd561
AA
634 return -EINVAL;
635
636 return rdev_set_channel(rdev, page, channel);
637}
638
ba2a9506
AA
639static int nl802154_set_cca_mode(struct sk_buff *skb, struct genl_info *info)
640{
641 struct cfg802154_registered_device *rdev = info->user_ptr[0];
642 struct wpan_phy_cca cca;
643
edea8f7c
AA
644 if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE)
645 return -EOPNOTSUPP;
646
ba2a9506
AA
647 if (!info->attrs[NL802154_ATTR_CCA_MODE])
648 return -EINVAL;
649
650 cca.mode = nla_get_u32(info->attrs[NL802154_ATTR_CCA_MODE]);
651 /* checking 802.15.4 constraints */
fea3318d
AA
652 if (cca.mode < NL802154_CCA_ENERGY ||
653 cca.mode > NL802154_CCA_ATTR_MAX ||
654 !(rdev->wpan_phy.supported.cca_modes & BIT(cca.mode)))
ba2a9506
AA
655 return -EINVAL;
656
657 if (cca.mode == NL802154_CCA_ENERGY_CARRIER) {
658 if (!info->attrs[NL802154_ATTR_CCA_OPT])
659 return -EINVAL;
660
661 cca.opt = nla_get_u32(info->attrs[NL802154_ATTR_CCA_OPT]);
fea3318d
AA
662 if (cca.opt > NL802154_CCA_OPT_ATTR_MAX ||
663 !(rdev->wpan_phy.supported.cca_opts & BIT(cca.opt)))
ba2a9506
AA
664 return -EINVAL;
665 }
666
667 return rdev_set_cca_mode(rdev, &cca);
668}
669
702bf371
AA
670static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
671{
672 struct cfg802154_registered_device *rdev = info->user_ptr[0];
673 struct net_device *dev = info->user_ptr[1];
674 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
ee7b9053 675 __le16 pan_id;
702bf371
AA
676
677 /* conflict here while tx/rx calls */
678 if (netif_running(dev))
679 return -EBUSY;
680
681 /* don't change address fields on monitor */
0cf0879a
AA
682 if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR ||
683 !info->attrs[NL802154_ATTR_PAN_ID])
702bf371
AA
684 return -EINVAL;
685
ee7b9053 686 pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]);
702bf371 687
673692fa
AA
688 /* TODO
689 * I am not sure about to check here on broadcast pan_id.
690 * Broadcast is a valid setting, comment from 802.15.4:
691 * If this value is 0xffff, the device is not associated.
692 *
693 * This could useful to simple deassociate an device.
694 */
695 if (pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
696 return -EINVAL;
697
702bf371
AA
698 return rdev_set_pan_id(rdev, wpan_dev, pan_id);
699}
700
9830c62a
AA
701static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info)
702{
703 struct cfg802154_registered_device *rdev = info->user_ptr[0];
704 struct net_device *dev = info->user_ptr[1];
705 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
ee7b9053 706 __le16 short_addr;
9830c62a
AA
707
708 /* conflict here while tx/rx calls */
709 if (netif_running(dev))
710 return -EBUSY;
711
712 /* don't change address fields on monitor */
0cf0879a
AA
713 if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR ||
714 !info->attrs[NL802154_ATTR_SHORT_ADDR])
9830c62a
AA
715 return -EINVAL;
716
ee7b9053 717 short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]);
9830c62a 718
673692fa
AA
719 /* TODO
720 * I am not sure about to check here on broadcast short_addr.
721 * Broadcast is a valid setting, comment from 802.15.4:
722 * A value of 0xfffe indicates that the device has
723 * associated but has not been allocated an address. A
724 * value of 0xffff indicates that the device does not
725 * have a short address.
726 *
727 * I think we should allow to set these settings but
728 * don't allow to allow socket communication with it.
729 */
730 if (short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC) ||
731 short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST))
732 return -EINVAL;
733
9830c62a
AA
734 return rdev_set_short_addr(rdev, wpan_dev, short_addr);
735}
736
656a999e
AA
737static int
738nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info)
739{
740 struct cfg802154_registered_device *rdev = info->user_ptr[0];
741 struct net_device *dev = info->user_ptr[1];
742 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
743 u8 min_be, max_be;
744
745 /* should be set on netif open inside phy settings */
746 if (netif_running(dev))
747 return -EBUSY;
748
749 if (!info->attrs[NL802154_ATTR_MIN_BE] ||
750 !info->attrs[NL802154_ATTR_MAX_BE])
751 return -EINVAL;
752
753 min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]);
754 max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]);
755
756 /* check 802.15.4 constraints */
fea3318d
AA
757 if (min_be < rdev->wpan_phy.supported.min_minbe ||
758 min_be > rdev->wpan_phy.supported.max_minbe ||
759 max_be < rdev->wpan_phy.supported.min_maxbe ||
760 max_be > rdev->wpan_phy.supported.max_maxbe ||
761 min_be > max_be)
656a999e
AA
762 return -EINVAL;
763
764 return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be);
765}
766
a01ba765
AA
767static int
768nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info)
769{
770 struct cfg802154_registered_device *rdev = info->user_ptr[0];
771 struct net_device *dev = info->user_ptr[1];
772 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
773 u8 max_csma_backoffs;
774
775 /* conflict here while other running iface settings */
776 if (netif_running(dev))
777 return -EBUSY;
778
779 if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS])
780 return -EINVAL;
781
782 max_csma_backoffs = nla_get_u8(
783 info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]);
784
785 /* check 802.15.4 constraints */
fea3318d
AA
786 if (max_csma_backoffs < rdev->wpan_phy.supported.min_csma_backoffs ||
787 max_csma_backoffs > rdev->wpan_phy.supported.max_csma_backoffs)
a01ba765
AA
788 return -EINVAL;
789
790 return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs);
791}
792
17a3a46b
AA
793static int
794nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info)
795{
796 struct cfg802154_registered_device *rdev = info->user_ptr[0];
797 struct net_device *dev = info->user_ptr[1];
798 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
799 s8 max_frame_retries;
800
801 if (netif_running(dev))
802 return -EBUSY;
803
804 if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES])
805 return -EINVAL;
806
807 max_frame_retries = nla_get_s8(
808 info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]);
809
810 /* check 802.15.4 constraints */
fea3318d
AA
811 if (max_frame_retries < rdev->wpan_phy.supported.min_frame_retries ||
812 max_frame_retries > rdev->wpan_phy.supported.max_frame_retries)
17a3a46b
AA
813 return -EINVAL;
814
815 return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries);
816}
817
c8937a1d
AA
818static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info)
819{
820 struct cfg802154_registered_device *rdev = info->user_ptr[0];
821 struct net_device *dev = info->user_ptr[1];
822 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
823 bool mode;
824
825 if (netif_running(dev))
826 return -EBUSY;
827
828 if (!info->attrs[NL802154_ATTR_LBT_MODE])
829 return -EINVAL;
830
831 mode = !!nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]);
fea3318d
AA
832 if (!wpan_phy_supported_bool(mode, rdev->wpan_phy.supported.lbt))
833 return -EINVAL;
834
c8937a1d
AA
835 return rdev_set_lbt_mode(rdev, wpan_dev, mode);
836}
837
79fe1a2a
AA
838#define NL802154_FLAG_NEED_WPAN_PHY 0x01
839#define NL802154_FLAG_NEED_NETDEV 0x02
840#define NL802154_FLAG_NEED_RTNL 0x04
841#define NL802154_FLAG_CHECK_NETDEV_UP 0x08
842#define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\
843 NL802154_FLAG_CHECK_NETDEV_UP)
844#define NL802154_FLAG_NEED_WPAN_DEV 0x10
845#define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\
846 NL802154_FLAG_CHECK_NETDEV_UP)
847
848static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
849 struct genl_info *info)
850{
851 struct cfg802154_registered_device *rdev;
852 struct wpan_dev *wpan_dev;
853 struct net_device *dev;
854 bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
855
856 if (rtnl)
857 rtnl_lock();
858
859 if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
860 rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
861 if (IS_ERR(rdev)) {
862 if (rtnl)
863 rtnl_unlock();
864 return PTR_ERR(rdev);
865 }
866 info->user_ptr[0] = rdev;
867 } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
868 ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
869 ASSERT_RTNL();
870 wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
871 info->attrs);
872 if (IS_ERR(wpan_dev)) {
873 if (rtnl)
874 rtnl_unlock();
875 return PTR_ERR(wpan_dev);
876 }
877
878 dev = wpan_dev->netdev;
879 rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
880
881 if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
882 if (!dev) {
883 if (rtnl)
884 rtnl_unlock();
885 return -EINVAL;
886 }
887
888 info->user_ptr[1] = dev;
889 } else {
890 info->user_ptr[1] = wpan_dev;
891 }
892
893 if (dev) {
894 if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
895 !netif_running(dev)) {
896 if (rtnl)
897 rtnl_unlock();
898 return -ENETDOWN;
899 }
900
901 dev_hold(dev);
902 }
903
904 info->user_ptr[0] = rdev;
905 }
906
907 return 0;
908}
909
910static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
911 struct genl_info *info)
912{
913 if (info->user_ptr[1]) {
914 if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
915 struct wpan_dev *wpan_dev = info->user_ptr[1];
916
917 if (wpan_dev->netdev)
918 dev_put(wpan_dev->netdev);
919 } else {
920 dev_put(info->user_ptr[1]);
921 }
922 }
923
924 if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
925 rtnl_unlock();
926}
927
928static const struct genl_ops nl802154_ops[] = {
ca20ce20
AA
929 {
930 .cmd = NL802154_CMD_GET_WPAN_PHY,
931 .doit = nl802154_get_wpan_phy,
932 .dumpit = nl802154_dump_wpan_phy,
933 .done = nl802154_dump_wpan_phy_done,
934 .policy = nl802154_policy,
935 /* can be retrieved by unprivileged users */
936 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
937 NL802154_FLAG_NEED_RTNL,
938 },
4b96aea0
AA
939 {
940 .cmd = NL802154_CMD_GET_INTERFACE,
941 .doit = nl802154_get_interface,
942 .dumpit = nl802154_dump_interface,
943 .policy = nl802154_policy,
944 /* can be retrieved by unprivileged users */
945 .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
946 NL802154_FLAG_NEED_RTNL,
947 },
f3ea5e44
AA
948 {
949 .cmd = NL802154_CMD_NEW_INTERFACE,
950 .doit = nl802154_new_interface,
951 .policy = nl802154_policy,
952 .flags = GENL_ADMIN_PERM,
953 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
954 NL802154_FLAG_NEED_RTNL,
955 },
b821ecd4
AA
956 {
957 .cmd = NL802154_CMD_DEL_INTERFACE,
958 .doit = nl802154_del_interface,
959 .policy = nl802154_policy,
960 .flags = GENL_ADMIN_PERM,
961 .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
962 NL802154_FLAG_NEED_RTNL,
963 },
ab0bd561
AA
964 {
965 .cmd = NL802154_CMD_SET_CHANNEL,
966 .doit = nl802154_set_channel,
967 .policy = nl802154_policy,
968 .flags = GENL_ADMIN_PERM,
969 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
970 NL802154_FLAG_NEED_RTNL,
971 },
ba2a9506
AA
972 {
973 .cmd = NL802154_CMD_SET_CCA_MODE,
974 .doit = nl802154_set_cca_mode,
975 .policy = nl802154_policy,
976 .flags = GENL_ADMIN_PERM,
977 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
978 NL802154_FLAG_NEED_RTNL,
979 },
702bf371
AA
980 {
981 .cmd = NL802154_CMD_SET_PAN_ID,
982 .doit = nl802154_set_pan_id,
983 .policy = nl802154_policy,
984 .flags = GENL_ADMIN_PERM,
985 .internal_flags = NL802154_FLAG_NEED_NETDEV |
986 NL802154_FLAG_NEED_RTNL,
987 },
9830c62a
AA
988 {
989 .cmd = NL802154_CMD_SET_SHORT_ADDR,
990 .doit = nl802154_set_short_addr,
991 .policy = nl802154_policy,
992 .flags = GENL_ADMIN_PERM,
993 .internal_flags = NL802154_FLAG_NEED_NETDEV |
994 NL802154_FLAG_NEED_RTNL,
995 },
656a999e
AA
996 {
997 .cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
998 .doit = nl802154_set_backoff_exponent,
999 .policy = nl802154_policy,
1000 .flags = GENL_ADMIN_PERM,
1001 .internal_flags = NL802154_FLAG_NEED_NETDEV |
1002 NL802154_FLAG_NEED_RTNL,
1003 },
a01ba765
AA
1004 {
1005 .cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
1006 .doit = nl802154_set_max_csma_backoffs,
1007 .policy = nl802154_policy,
1008 .flags = GENL_ADMIN_PERM,
1009 .internal_flags = NL802154_FLAG_NEED_NETDEV |
1010 NL802154_FLAG_NEED_RTNL,
1011 },
17a3a46b
AA
1012 {
1013 .cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
1014 .doit = nl802154_set_max_frame_retries,
1015 .policy = nl802154_policy,
1016 .flags = GENL_ADMIN_PERM,
1017 .internal_flags = NL802154_FLAG_NEED_NETDEV |
1018 NL802154_FLAG_NEED_RTNL,
1019 },
c8937a1d
AA
1020 {
1021 .cmd = NL802154_CMD_SET_LBT_MODE,
1022 .doit = nl802154_set_lbt_mode,
1023 .policy = nl802154_policy,
1024 .flags = GENL_ADMIN_PERM,
1025 .internal_flags = NL802154_FLAG_NEED_NETDEV |
1026 NL802154_FLAG_NEED_RTNL,
1027 },
79fe1a2a
AA
1028};
1029
1030/* initialisation/exit functions */
1031int nl802154_init(void)
1032{
1033 return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops,
1034 nl802154_mcgrps);
1035}
1036
1037void nl802154_exit(void)
1038{
1039 genl_unregister_family(&nl802154_fam);
1040}