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