atm: remove deprecated use of pci api
[deliverable/linux.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
210 [NL802154_ATTR_TX_POWER] = { .type = NLA_S8, },
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
AA
250 if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL,
251 rdev->wpan_phy.channels_supported[page]))
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 */
ba2a9506
AA
294 if (nla_put_u32(msg, NL802154_ATTR_CCA_MODE,
295 rdev->wpan_phy.cca.mode))
ca20ce20
AA
296 goto nla_put_failure;
297
ba2a9506
AA
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;
302 }
303
ca20ce20
AA
304 if (nla_put_s8(msg, NL802154_ATTR_TX_POWER,
305 rdev->wpan_phy.transmit_power))
306 goto nla_put_failure;
307
308finish:
309 return genlmsg_end(msg, hdr);
310
311nla_put_failure:
312 genlmsg_cancel(msg, hdr);
313 return -EMSGSIZE;
314}
315
316struct nl802154_dump_wpan_phy_state {
317 s64 filter_wpan_phy;
318 long start;
319
320};
321
322static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb,
323 struct netlink_callback *cb,
324 struct nl802154_dump_wpan_phy_state *state)
325{
326 struct nlattr **tb = nl802154_fam.attrbuf;
327 int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize,
328 tb, nl802154_fam.maxattr, nl802154_policy);
329
330 /* TODO check if we can handle error here,
331 * we have no backward compatibility
332 */
333 if (ret)
334 return 0;
335
336 if (tb[NL802154_ATTR_WPAN_PHY])
337 state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]);
338 if (tb[NL802154_ATTR_WPAN_DEV])
339 state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32;
340 if (tb[NL802154_ATTR_IFINDEX]) {
341 struct net_device *netdev;
342 struct cfg802154_registered_device *rdev;
343 int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]);
344
345 /* TODO netns */
346 netdev = __dev_get_by_index(&init_net, ifidx);
347 if (!netdev)
348 return -ENODEV;
349 if (netdev->ieee802154_ptr) {
350 rdev = wpan_phy_to_rdev(
351 netdev->ieee802154_ptr->wpan_phy);
352 state->filter_wpan_phy = rdev->wpan_phy_idx;
353 }
354 }
355
356 return 0;
357}
358
359static int
360nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb)
361{
362 int idx = 0, ret;
363 struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0];
364 struct cfg802154_registered_device *rdev;
365
366 rtnl_lock();
367 if (!state) {
368 state = kzalloc(sizeof(*state), GFP_KERNEL);
369 if (!state) {
370 rtnl_unlock();
371 return -ENOMEM;
372 }
373 state->filter_wpan_phy = -1;
374 ret = nl802154_dump_wpan_phy_parse(skb, cb, state);
375 if (ret) {
376 kfree(state);
377 rtnl_unlock();
378 return ret;
379 }
380 cb->args[0] = (long)state;
381 }
382
383 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
384 /* TODO net ns compare */
385 if (++idx <= state->start)
386 continue;
387 if (state->filter_wpan_phy != -1 &&
388 state->filter_wpan_phy != rdev->wpan_phy_idx)
389 continue;
390 /* attempt to fit multiple wpan_phy data chunks into the skb */
391 ret = nl802154_send_wpan_phy(rdev,
392 NL802154_CMD_NEW_WPAN_PHY,
393 skb,
394 NETLINK_CB(cb->skb).portid,
395 cb->nlh->nlmsg_seq, NLM_F_MULTI);
396 if (ret < 0) {
397 if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
398 !skb->len && cb->min_dump_alloc < 4096) {
399 cb->min_dump_alloc = 4096;
400 rtnl_unlock();
401 return 1;
402 }
403 idx--;
404 break;
405 }
406 break;
407 }
408 rtnl_unlock();
409
410 state->start = idx;
411
412 return skb->len;
413}
414
415static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
416{
417 kfree((void *)cb->args[0]);
418 return 0;
419}
420
421static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info)
422{
423 struct sk_buff *msg;
424 struct cfg802154_registered_device *rdev = info->user_ptr[0];
425
426 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
427 if (!msg)
428 return -ENOMEM;
429
430 if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg,
431 info->snd_portid, info->snd_seq, 0) < 0) {
432 nlmsg_free(msg);
433 return -ENOBUFS;
434 }
435
436 return genlmsg_reply(msg, info);
437}
438
4b96aea0
AA
439static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev)
440{
441 return (u64)wpan_dev->identifier |
442 ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
443}
444
445static int
446nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
447 struct cfg802154_registered_device *rdev,
448 struct wpan_dev *wpan_dev)
449{
450 struct net_device *dev = wpan_dev->netdev;
451 void *hdr;
452
453 hdr = nl802154hdr_put(msg, portid, seq, flags,
454 NL802154_CMD_NEW_INTERFACE);
455 if (!hdr)
456 return -1;
457
458 if (dev &&
459 (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) ||
460 nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name)))
461 goto nla_put_failure;
462
463 if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
464 nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) ||
465 nla_put_u64(msg, NL802154_ATTR_WPAN_DEV, wpan_dev_id(wpan_dev)) ||
466 nla_put_u32(msg, NL802154_ATTR_GENERATION,
467 rdev->devlist_generation ^
468 (cfg802154_rdev_list_generation << 2)))
469 goto nla_put_failure;
470
471 /* address settings */
472 if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR,
473 wpan_dev->extended_addr) ||
474 nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR,
475 wpan_dev->short_addr) ||
476 nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id))
477 goto nla_put_failure;
478
479 /* ARET handling */
480 if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES,
481 wpan_dev->frame_retries) ||
482 nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) ||
483 nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS,
484 wpan_dev->csma_retries) ||
485 nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be))
486 goto nla_put_failure;
487
488 /* listen before transmit */
489 if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt))
490 goto nla_put_failure;
491
492 return genlmsg_end(msg, hdr);
493
494nla_put_failure:
495 genlmsg_cancel(msg, hdr);
496 return -EMSGSIZE;
497}
498
499static int
500nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
501{
502 int wp_idx = 0;
503 int if_idx = 0;
504 int wp_start = cb->args[0];
505 int if_start = cb->args[1];
506 struct cfg802154_registered_device *rdev;
507 struct wpan_dev *wpan_dev;
508
509 rtnl_lock();
510 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
511 /* TODO netns compare */
512 if (wp_idx < wp_start) {
513 wp_idx++;
514 continue;
515 }
516 if_idx = 0;
517
518 list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
519 if (if_idx < if_start) {
520 if_idx++;
521 continue;
522 }
523 if (nl802154_send_iface(skb, NETLINK_CB(cb->skb).portid,
524 cb->nlh->nlmsg_seq, NLM_F_MULTI,
525 rdev, wpan_dev) < 0) {
526 goto out;
527 }
528 if_idx++;
529 }
530
531 wp_idx++;
532 }
533out:
534 rtnl_unlock();
535
536 cb->args[0] = wp_idx;
537 cb->args[1] = if_idx;
538
539 return skb->len;
540}
541
542static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info)
543{
544 struct sk_buff *msg;
545 struct cfg802154_registered_device *rdev = info->user_ptr[0];
546 struct wpan_dev *wdev = info->user_ptr[1];
547
548 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
549 if (!msg)
550 return -ENOMEM;
551
552 if (nl802154_send_iface(msg, info->snd_portid, info->snd_seq, 0,
553 rdev, wdev) < 0) {
554 nlmsg_free(msg);
555 return -ENOBUFS;
556 }
557
558 return genlmsg_reply(msg, info);
559}
560
f3ea5e44
AA
561static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
562{
563 struct cfg802154_registered_device *rdev = info->user_ptr[0];
564 enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC;
0e57547e 565 __le64 extended_addr = cpu_to_le64(0x0000000000000000ULL);
f3ea5e44
AA
566
567 /* TODO avoid failing a new interface
568 * creation due to pending removal?
569 */
570
571 if (!info->attrs[NL802154_ATTR_IFNAME])
572 return -EINVAL;
573
574 if (info->attrs[NL802154_ATTR_IFTYPE]) {
575 type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]);
576 if (type > NL802154_IFTYPE_MAX)
577 return -EINVAL;
578 }
579
0e57547e
AA
580 /* TODO add nla_get_le64 to netlink */
581 if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
582 extended_addr = (__force __le64)nla_get_u64(
583 info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
584
f3ea5e44
AA
585 if (!rdev->ops->add_virtual_intf)
586 return -EOPNOTSUPP;
587
588 return rdev_add_virtual_intf(rdev,
589 nla_data(info->attrs[NL802154_ATTR_IFNAME]),
0e57547e 590 type, extended_addr);
f3ea5e44
AA
591}
592
b821ecd4
AA
593static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info)
594{
595 struct cfg802154_registered_device *rdev = info->user_ptr[0];
596 struct wpan_dev *wpan_dev = info->user_ptr[1];
597
598 if (!rdev->ops->del_virtual_intf)
599 return -EOPNOTSUPP;
600
601 /* If we remove a wpan device without a netdev then clear
602 * user_ptr[1] so that nl802154_post_doit won't dereference it
603 * to check if it needs to do dev_put(). Otherwise it crashes
604 * since the wpan_dev has been freed, unlike with a netdev where
605 * we need the dev_put() for the netdev to really be freed.
606 */
607 if (!wpan_dev->netdev)
608 info->user_ptr[1] = NULL;
609
610 return rdev_del_virtual_intf(rdev, wpan_dev);
611}
612
ab0bd561
AA
613static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info)
614{
615 struct cfg802154_registered_device *rdev = info->user_ptr[0];
616 u8 channel, page;
617
618 if (!info->attrs[NL802154_ATTR_PAGE] ||
619 !info->attrs[NL802154_ATTR_CHANNEL])
620 return -EINVAL;
621
622 page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
623 channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]);
624
625 /* check 802.15.4 constraints */
cb41c8dd 626 if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL)
ab0bd561
AA
627 return -EINVAL;
628
629 return rdev_set_channel(rdev, page, channel);
630}
631
ba2a9506
AA
632static int nl802154_set_cca_mode(struct sk_buff *skb, struct genl_info *info)
633{
634 struct cfg802154_registered_device *rdev = info->user_ptr[0];
635 struct wpan_phy_cca cca;
636
637 if (!info->attrs[NL802154_ATTR_CCA_MODE])
638 return -EINVAL;
639
640 cca.mode = nla_get_u32(info->attrs[NL802154_ATTR_CCA_MODE]);
641 /* checking 802.15.4 constraints */
642 if (cca.mode < NL802154_CCA_ENERGY || cca.mode > NL802154_CCA_ATTR_MAX)
643 return -EINVAL;
644
645 if (cca.mode == NL802154_CCA_ENERGY_CARRIER) {
646 if (!info->attrs[NL802154_ATTR_CCA_OPT])
647 return -EINVAL;
648
649 cca.opt = nla_get_u32(info->attrs[NL802154_ATTR_CCA_OPT]);
650 if (cca.opt > NL802154_CCA_OPT_ATTR_MAX)
651 return -EINVAL;
652 }
653
654 return rdev_set_cca_mode(rdev, &cca);
655}
656
702bf371
AA
657static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
658{
659 struct cfg802154_registered_device *rdev = info->user_ptr[0];
660 struct net_device *dev = info->user_ptr[1];
661 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
ee7b9053 662 __le16 pan_id;
702bf371
AA
663
664 /* conflict here while tx/rx calls */
665 if (netif_running(dev))
666 return -EBUSY;
667
668 /* don't change address fields on monitor */
669 if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
670 return -EINVAL;
671
672 if (!info->attrs[NL802154_ATTR_PAN_ID])
673 return -EINVAL;
674
ee7b9053 675 pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]);
702bf371
AA
676
677 return rdev_set_pan_id(rdev, wpan_dev, pan_id);
678}
679
9830c62a
AA
680static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info)
681{
682 struct cfg802154_registered_device *rdev = info->user_ptr[0];
683 struct net_device *dev = info->user_ptr[1];
684 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
ee7b9053 685 __le16 short_addr;
9830c62a
AA
686
687 /* conflict here while tx/rx calls */
688 if (netif_running(dev))
689 return -EBUSY;
690
691 /* don't change address fields on monitor */
692 if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
693 return -EINVAL;
694
695 if (!info->attrs[NL802154_ATTR_SHORT_ADDR])
696 return -EINVAL;
697
ee7b9053 698 short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]);
9830c62a
AA
699
700 return rdev_set_short_addr(rdev, wpan_dev, short_addr);
701}
702
656a999e
AA
703static int
704nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info)
705{
706 struct cfg802154_registered_device *rdev = info->user_ptr[0];
707 struct net_device *dev = info->user_ptr[1];
708 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
709 u8 min_be, max_be;
710
711 /* should be set on netif open inside phy settings */
712 if (netif_running(dev))
713 return -EBUSY;
714
715 if (!info->attrs[NL802154_ATTR_MIN_BE] ||
716 !info->attrs[NL802154_ATTR_MAX_BE])
717 return -EINVAL;
718
719 min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]);
720 max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]);
721
722 /* check 802.15.4 constraints */
723 if (max_be < 3 || max_be > 8 || min_be > max_be)
724 return -EINVAL;
725
726 return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be);
727}
728
a01ba765
AA
729static int
730nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info)
731{
732 struct cfg802154_registered_device *rdev = info->user_ptr[0];
733 struct net_device *dev = info->user_ptr[1];
734 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
735 u8 max_csma_backoffs;
736
737 /* conflict here while other running iface settings */
738 if (netif_running(dev))
739 return -EBUSY;
740
741 if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS])
742 return -EINVAL;
743
744 max_csma_backoffs = nla_get_u8(
745 info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]);
746
747 /* check 802.15.4 constraints */
748 if (max_csma_backoffs > 5)
749 return -EINVAL;
750
751 return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs);
752}
753
17a3a46b
AA
754static int
755nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info)
756{
757 struct cfg802154_registered_device *rdev = info->user_ptr[0];
758 struct net_device *dev = info->user_ptr[1];
759 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
760 s8 max_frame_retries;
761
762 if (netif_running(dev))
763 return -EBUSY;
764
765 if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES])
766 return -EINVAL;
767
768 max_frame_retries = nla_get_s8(
769 info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]);
770
771 /* check 802.15.4 constraints */
772 if (max_frame_retries < -1 || max_frame_retries > 7)
773 return -EINVAL;
774
775 return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries);
776}
777
c8937a1d
AA
778static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info)
779{
780 struct cfg802154_registered_device *rdev = info->user_ptr[0];
781 struct net_device *dev = info->user_ptr[1];
782 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
783 bool mode;
784
785 if (netif_running(dev))
786 return -EBUSY;
787
788 if (!info->attrs[NL802154_ATTR_LBT_MODE])
789 return -EINVAL;
790
791 mode = !!nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]);
792 return rdev_set_lbt_mode(rdev, wpan_dev, mode);
793}
794
79fe1a2a
AA
795#define NL802154_FLAG_NEED_WPAN_PHY 0x01
796#define NL802154_FLAG_NEED_NETDEV 0x02
797#define NL802154_FLAG_NEED_RTNL 0x04
798#define NL802154_FLAG_CHECK_NETDEV_UP 0x08
799#define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\
800 NL802154_FLAG_CHECK_NETDEV_UP)
801#define NL802154_FLAG_NEED_WPAN_DEV 0x10
802#define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\
803 NL802154_FLAG_CHECK_NETDEV_UP)
804
805static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
806 struct genl_info *info)
807{
808 struct cfg802154_registered_device *rdev;
809 struct wpan_dev *wpan_dev;
810 struct net_device *dev;
811 bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
812
813 if (rtnl)
814 rtnl_lock();
815
816 if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
817 rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
818 if (IS_ERR(rdev)) {
819 if (rtnl)
820 rtnl_unlock();
821 return PTR_ERR(rdev);
822 }
823 info->user_ptr[0] = rdev;
824 } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
825 ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
826 ASSERT_RTNL();
827 wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
828 info->attrs);
829 if (IS_ERR(wpan_dev)) {
830 if (rtnl)
831 rtnl_unlock();
832 return PTR_ERR(wpan_dev);
833 }
834
835 dev = wpan_dev->netdev;
836 rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
837
838 if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
839 if (!dev) {
840 if (rtnl)
841 rtnl_unlock();
842 return -EINVAL;
843 }
844
845 info->user_ptr[1] = dev;
846 } else {
847 info->user_ptr[1] = wpan_dev;
848 }
849
850 if (dev) {
851 if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
852 !netif_running(dev)) {
853 if (rtnl)
854 rtnl_unlock();
855 return -ENETDOWN;
856 }
857
858 dev_hold(dev);
859 }
860
861 info->user_ptr[0] = rdev;
862 }
863
864 return 0;
865}
866
867static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
868 struct genl_info *info)
869{
870 if (info->user_ptr[1]) {
871 if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
872 struct wpan_dev *wpan_dev = info->user_ptr[1];
873
874 if (wpan_dev->netdev)
875 dev_put(wpan_dev->netdev);
876 } else {
877 dev_put(info->user_ptr[1]);
878 }
879 }
880
881 if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
882 rtnl_unlock();
883}
884
885static const struct genl_ops nl802154_ops[] = {
ca20ce20
AA
886 {
887 .cmd = NL802154_CMD_GET_WPAN_PHY,
888 .doit = nl802154_get_wpan_phy,
889 .dumpit = nl802154_dump_wpan_phy,
890 .done = nl802154_dump_wpan_phy_done,
891 .policy = nl802154_policy,
892 /* can be retrieved by unprivileged users */
893 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
894 NL802154_FLAG_NEED_RTNL,
895 },
4b96aea0
AA
896 {
897 .cmd = NL802154_CMD_GET_INTERFACE,
898 .doit = nl802154_get_interface,
899 .dumpit = nl802154_dump_interface,
900 .policy = nl802154_policy,
901 /* can be retrieved by unprivileged users */
902 .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
903 NL802154_FLAG_NEED_RTNL,
904 },
f3ea5e44
AA
905 {
906 .cmd = NL802154_CMD_NEW_INTERFACE,
907 .doit = nl802154_new_interface,
908 .policy = nl802154_policy,
909 .flags = GENL_ADMIN_PERM,
910 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
911 NL802154_FLAG_NEED_RTNL,
912 },
b821ecd4
AA
913 {
914 .cmd = NL802154_CMD_DEL_INTERFACE,
915 .doit = nl802154_del_interface,
916 .policy = nl802154_policy,
917 .flags = GENL_ADMIN_PERM,
918 .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
919 NL802154_FLAG_NEED_RTNL,
920 },
ab0bd561
AA
921 {
922 .cmd = NL802154_CMD_SET_CHANNEL,
923 .doit = nl802154_set_channel,
924 .policy = nl802154_policy,
925 .flags = GENL_ADMIN_PERM,
926 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
927 NL802154_FLAG_NEED_RTNL,
928 },
ba2a9506
AA
929 {
930 .cmd = NL802154_CMD_SET_CCA_MODE,
931 .doit = nl802154_set_cca_mode,
932 .policy = nl802154_policy,
933 .flags = GENL_ADMIN_PERM,
934 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
935 NL802154_FLAG_NEED_RTNL,
936 },
702bf371
AA
937 {
938 .cmd = NL802154_CMD_SET_PAN_ID,
939 .doit = nl802154_set_pan_id,
940 .policy = nl802154_policy,
941 .flags = GENL_ADMIN_PERM,
942 .internal_flags = NL802154_FLAG_NEED_NETDEV |
943 NL802154_FLAG_NEED_RTNL,
944 },
9830c62a
AA
945 {
946 .cmd = NL802154_CMD_SET_SHORT_ADDR,
947 .doit = nl802154_set_short_addr,
948 .policy = nl802154_policy,
949 .flags = GENL_ADMIN_PERM,
950 .internal_flags = NL802154_FLAG_NEED_NETDEV |
951 NL802154_FLAG_NEED_RTNL,
952 },
656a999e
AA
953 {
954 .cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
955 .doit = nl802154_set_backoff_exponent,
956 .policy = nl802154_policy,
957 .flags = GENL_ADMIN_PERM,
958 .internal_flags = NL802154_FLAG_NEED_NETDEV |
959 NL802154_FLAG_NEED_RTNL,
960 },
a01ba765
AA
961 {
962 .cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
963 .doit = nl802154_set_max_csma_backoffs,
964 .policy = nl802154_policy,
965 .flags = GENL_ADMIN_PERM,
966 .internal_flags = NL802154_FLAG_NEED_NETDEV |
967 NL802154_FLAG_NEED_RTNL,
968 },
17a3a46b
AA
969 {
970 .cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
971 .doit = nl802154_set_max_frame_retries,
972 .policy = nl802154_policy,
973 .flags = GENL_ADMIN_PERM,
974 .internal_flags = NL802154_FLAG_NEED_NETDEV |
975 NL802154_FLAG_NEED_RTNL,
976 },
c8937a1d
AA
977 {
978 .cmd = NL802154_CMD_SET_LBT_MODE,
979 .doit = nl802154_set_lbt_mode,
980 .policy = nl802154_policy,
981 .flags = GENL_ADMIN_PERM,
982 .internal_flags = NL802154_FLAG_NEED_NETDEV |
983 NL802154_FLAG_NEED_RTNL,
984 },
79fe1a2a
AA
985};
986
987/* initialisation/exit functions */
988int nl802154_init(void)
989{
990 return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops,
991 nl802154_mcgrps);
992}
993
994void nl802154_exit(void)
995{
996 genl_unregister_family(&nl802154_fam);
997}
This page took 0.077531 seconds and 5 git commands to generate.