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