ieee802154: add new interface command
[pandora-kernel.git] / net / ieee802154 / nl802154.c
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 < WPAN_NUM_PAGES; 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
559         /* TODO avoid failing a new interface
560          * creation due to pending removal?
561          */
562
563         if (!info->attrs[NL802154_ATTR_IFNAME])
564                 return -EINVAL;
565
566         if (info->attrs[NL802154_ATTR_IFTYPE]) {
567                 type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]);
568                 if (type > NL802154_IFTYPE_MAX)
569                         return -EINVAL;
570         }
571
572         if (!rdev->ops->add_virtual_intf)
573                 return -EOPNOTSUPP;
574
575         return rdev_add_virtual_intf(rdev,
576                                      nla_data(info->attrs[NL802154_ATTR_IFNAME]),
577                                      type);
578 }
579
580 static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info)
581 {
582         struct cfg802154_registered_device *rdev = info->user_ptr[0];
583         u8 channel, page;
584
585         if (!info->attrs[NL802154_ATTR_PAGE] ||
586             !info->attrs[NL802154_ATTR_CHANNEL])
587                 return -EINVAL;
588
589         page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
590         channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]);
591
592         /* check 802.15.4 constraints */
593         if (page >= WPAN_NUM_PAGES || channel >= WPAN_NUM_CHANNELS)
594                 return -EINVAL;
595
596         return rdev_set_channel(rdev, page, channel);
597 }
598
599 static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
600 {
601         struct cfg802154_registered_device *rdev = info->user_ptr[0];
602         struct net_device *dev = info->user_ptr[1];
603         struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
604         u16 pan_id;
605
606         /* conflict here while tx/rx calls */
607         if (netif_running(dev))
608                 return -EBUSY;
609
610         /* don't change address fields on monitor */
611         if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
612                 return -EINVAL;
613
614         if (!info->attrs[NL802154_ATTR_PAN_ID])
615                 return -EINVAL;
616
617         pan_id = nla_get_u16(info->attrs[NL802154_ATTR_PAN_ID]);
618
619         return rdev_set_pan_id(rdev, wpan_dev, pan_id);
620 }
621
622 static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info)
623 {
624         struct cfg802154_registered_device *rdev = info->user_ptr[0];
625         struct net_device *dev = info->user_ptr[1];
626         struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
627         u16 short_addr;
628
629         /* conflict here while tx/rx calls */
630         if (netif_running(dev))
631                 return -EBUSY;
632
633         /* don't change address fields on monitor */
634         if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
635                 return -EINVAL;
636
637         if (!info->attrs[NL802154_ATTR_SHORT_ADDR])
638                 return -EINVAL;
639
640         short_addr = nla_get_u16(info->attrs[NL802154_ATTR_SHORT_ADDR]);
641
642         return rdev_set_short_addr(rdev, wpan_dev, short_addr);
643 }
644
645 static int
646 nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info)
647 {
648         struct cfg802154_registered_device *rdev = info->user_ptr[0];
649         struct net_device *dev = info->user_ptr[1];
650         struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
651         u8 min_be, max_be;
652
653         /* should be set on netif open inside phy settings */
654         if (netif_running(dev))
655                 return -EBUSY;
656
657         if (!info->attrs[NL802154_ATTR_MIN_BE] ||
658             !info->attrs[NL802154_ATTR_MAX_BE])
659                 return -EINVAL;
660
661         min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]);
662         max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]);
663
664         /* check 802.15.4 constraints */
665         if (max_be < 3 || max_be > 8 || min_be > max_be)
666                 return -EINVAL;
667
668         return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be);
669 }
670
671 static int
672 nl802154_set_max_csma_backoffs(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 max_csma_backoffs;
678
679         /* conflict here while other running iface settings */
680         if (netif_running(dev))
681                 return -EBUSY;
682
683         if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS])
684                 return -EINVAL;
685
686         max_csma_backoffs = nla_get_u8(
687                         info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]);
688
689         /* check 802.15.4 constraints */
690         if (max_csma_backoffs > 5)
691                 return -EINVAL;
692
693         return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs);
694 }
695
696 static int
697 nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info)
698 {
699         struct cfg802154_registered_device *rdev = info->user_ptr[0];
700         struct net_device *dev = info->user_ptr[1];
701         struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
702         s8 max_frame_retries;
703
704         if (netif_running(dev))
705                 return -EBUSY;
706
707         if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES])
708                 return -EINVAL;
709
710         max_frame_retries = nla_get_s8(
711                         info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]);
712
713         /* check 802.15.4 constraints */
714         if (max_frame_retries < -1 || max_frame_retries > 7)
715                 return -EINVAL;
716
717         return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries);
718 }
719
720 static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info)
721 {
722         struct cfg802154_registered_device *rdev = info->user_ptr[0];
723         struct net_device *dev = info->user_ptr[1];
724         struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
725         bool mode;
726
727         if (netif_running(dev))
728                 return -EBUSY;
729
730         if (!info->attrs[NL802154_ATTR_LBT_MODE])
731                 return -EINVAL;
732
733         mode = !!nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]);
734         return rdev_set_lbt_mode(rdev, wpan_dev, mode);
735 }
736
737 #define NL802154_FLAG_NEED_WPAN_PHY     0x01
738 #define NL802154_FLAG_NEED_NETDEV       0x02
739 #define NL802154_FLAG_NEED_RTNL         0x04
740 #define NL802154_FLAG_CHECK_NETDEV_UP   0x08
741 #define NL802154_FLAG_NEED_NETDEV_UP    (NL802154_FLAG_NEED_NETDEV |\
742                                          NL802154_FLAG_CHECK_NETDEV_UP)
743 #define NL802154_FLAG_NEED_WPAN_DEV     0x10
744 #define NL802154_FLAG_NEED_WPAN_DEV_UP  (NL802154_FLAG_NEED_WPAN_DEV |\
745                                          NL802154_FLAG_CHECK_NETDEV_UP)
746
747 static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
748                              struct genl_info *info)
749 {
750         struct cfg802154_registered_device *rdev;
751         struct wpan_dev *wpan_dev;
752         struct net_device *dev;
753         bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
754
755         if (rtnl)
756                 rtnl_lock();
757
758         if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
759                 rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
760                 if (IS_ERR(rdev)) {
761                         if (rtnl)
762                                 rtnl_unlock();
763                         return PTR_ERR(rdev);
764                 }
765                 info->user_ptr[0] = rdev;
766         } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
767                    ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
768                 ASSERT_RTNL();
769                 wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
770                                                            info->attrs);
771                 if (IS_ERR(wpan_dev)) {
772                         if (rtnl)
773                                 rtnl_unlock();
774                         return PTR_ERR(wpan_dev);
775                 }
776
777                 dev = wpan_dev->netdev;
778                 rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
779
780                 if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
781                         if (!dev) {
782                                 if (rtnl)
783                                         rtnl_unlock();
784                                 return -EINVAL;
785                         }
786
787                         info->user_ptr[1] = dev;
788                 } else {
789                         info->user_ptr[1] = wpan_dev;
790                 }
791
792                 if (dev) {
793                         if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
794                             !netif_running(dev)) {
795                                 if (rtnl)
796                                         rtnl_unlock();
797                                 return -ENETDOWN;
798                         }
799
800                         dev_hold(dev);
801                 }
802
803                 info->user_ptr[0] = rdev;
804         }
805
806         return 0;
807 }
808
809 static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
810                                struct genl_info *info)
811 {
812         if (info->user_ptr[1]) {
813                 if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
814                         struct wpan_dev *wpan_dev = info->user_ptr[1];
815
816                         if (wpan_dev->netdev)
817                                 dev_put(wpan_dev->netdev);
818                 } else {
819                         dev_put(info->user_ptr[1]);
820                 }
821         }
822
823         if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
824                 rtnl_unlock();
825 }
826
827 static const struct genl_ops nl802154_ops[] = {
828         {
829                 .cmd = NL802154_CMD_GET_WPAN_PHY,
830                 .doit = nl802154_get_wpan_phy,
831                 .dumpit = nl802154_dump_wpan_phy,
832                 .done = nl802154_dump_wpan_phy_done,
833                 .policy = nl802154_policy,
834                 /* can be retrieved by unprivileged users */
835                 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
836                                   NL802154_FLAG_NEED_RTNL,
837         },
838         {
839                 .cmd = NL802154_CMD_GET_INTERFACE,
840                 .doit = nl802154_get_interface,
841                 .dumpit = nl802154_dump_interface,
842                 .policy = nl802154_policy,
843                 /* can be retrieved by unprivileged users */
844                 .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
845                                   NL802154_FLAG_NEED_RTNL,
846         },
847         {
848                 .cmd = NL802154_CMD_NEW_INTERFACE,
849                 .doit = nl802154_new_interface,
850                 .policy = nl802154_policy,
851                 .flags = GENL_ADMIN_PERM,
852                 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
853                                   NL802154_FLAG_NEED_RTNL,
854         },
855         {
856                 .cmd = NL802154_CMD_SET_CHANNEL,
857                 .doit = nl802154_set_channel,
858                 .policy = nl802154_policy,
859                 .flags = GENL_ADMIN_PERM,
860                 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
861                                   NL802154_FLAG_NEED_RTNL,
862         },
863         {
864                 .cmd = NL802154_CMD_SET_PAN_ID,
865                 .doit = nl802154_set_pan_id,
866                 .policy = nl802154_policy,
867                 .flags = GENL_ADMIN_PERM,
868                 .internal_flags = NL802154_FLAG_NEED_NETDEV |
869                                   NL802154_FLAG_NEED_RTNL,
870         },
871         {
872                 .cmd = NL802154_CMD_SET_SHORT_ADDR,
873                 .doit = nl802154_set_short_addr,
874                 .policy = nl802154_policy,
875                 .flags = GENL_ADMIN_PERM,
876                 .internal_flags = NL802154_FLAG_NEED_NETDEV |
877                                   NL802154_FLAG_NEED_RTNL,
878         },
879         {
880                 .cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
881                 .doit = nl802154_set_backoff_exponent,
882                 .policy = nl802154_policy,
883                 .flags = GENL_ADMIN_PERM,
884                 .internal_flags = NL802154_FLAG_NEED_NETDEV |
885                                   NL802154_FLAG_NEED_RTNL,
886         },
887         {
888                 .cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
889                 .doit = nl802154_set_max_csma_backoffs,
890                 .policy = nl802154_policy,
891                 .flags = GENL_ADMIN_PERM,
892                 .internal_flags = NL802154_FLAG_NEED_NETDEV |
893                                   NL802154_FLAG_NEED_RTNL,
894         },
895         {
896                 .cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
897                 .doit = nl802154_set_max_frame_retries,
898                 .policy = nl802154_policy,
899                 .flags = GENL_ADMIN_PERM,
900                 .internal_flags = NL802154_FLAG_NEED_NETDEV |
901                                   NL802154_FLAG_NEED_RTNL,
902         },
903         {
904                 .cmd = NL802154_CMD_SET_LBT_MODE,
905                 .doit = nl802154_set_lbt_mode,
906                 .policy = nl802154_policy,
907                 .flags = GENL_ADMIN_PERM,
908                 .internal_flags = NL802154_FLAG_NEED_NETDEV |
909                                   NL802154_FLAG_NEED_RTNL,
910         },
911 };
912
913 /* initialisation/exit functions */
914 int nl802154_init(void)
915 {
916         return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops,
917                                                     nl802154_mcgrps);
918 }
919
920 void nl802154_exit(void)
921 {
922         genl_unregister_family(&nl802154_fam);
923 }