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