net/mlx4_core: Adjust port number in qp_attach wrapper when detaching
[cascardo/linux.git] / net / ieee802154 / nl-mac.c
1 /*
2  * Netlink inteface for IEEE 802.15.4 stack
3  *
4  * Copyright 2007, 2008 Siemens AG
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Written by:
20  * Sergey Lapin <slapin@ossfans.org>
21  * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
22  * Maxim Osipov <maxim.osipov@siemens.com>
23  */
24
25 #include <linux/gfp.h>
26 #include <linux/kernel.h>
27 #include <linux/if_arp.h>
28 #include <linux/netdevice.h>
29 #include <net/netlink.h>
30 #include <net/genetlink.h>
31 #include <net/sock.h>
32 #include <linux/nl802154.h>
33 #include <linux/export.h>
34 #include <net/af_ieee802154.h>
35 #include <net/nl802154.h>
36 #include <net/ieee802154.h>
37 #include <net/ieee802154_netdev.h>
38 #include <net/wpan-phy.h>
39
40 #include "ieee802154.h"
41
42 static int nla_put_hwaddr(struct sk_buff *msg, int type, __le64 hwaddr)
43 {
44         return nla_put_u64(msg, type, swab64((__force u64)hwaddr));
45 }
46
47 static __le64 nla_get_hwaddr(const struct nlattr *nla)
48 {
49         return ieee802154_devaddr_from_raw(nla_data(nla));
50 }
51
52 static int nla_put_shortaddr(struct sk_buff *msg, int type, __le16 addr)
53 {
54         return nla_put_u16(msg, type, le16_to_cpu(addr));
55 }
56
57 static __le16 nla_get_shortaddr(const struct nlattr *nla)
58 {
59         return cpu_to_le16(nla_get_u16(nla));
60 }
61
62 int ieee802154_nl_assoc_indic(struct net_device *dev,
63                 struct ieee802154_addr *addr, u8 cap)
64 {
65         struct sk_buff *msg;
66
67         pr_debug("%s\n", __func__);
68
69         if (addr->mode != IEEE802154_ADDR_LONG) {
70                 pr_err("%s: received non-long source address!\n", __func__);
71                 return -EINVAL;
72         }
73
74         msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC);
75         if (!msg)
76                 return -ENOBUFS;
77
78         if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
79             nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
80             nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
81                     dev->dev_addr) ||
82             nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR,
83                            addr->extended_addr) ||
84             nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap))
85                 goto nla_put_failure;
86
87         return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
88
89 nla_put_failure:
90         nlmsg_free(msg);
91         return -ENOBUFS;
92 }
93 EXPORT_SYMBOL(ieee802154_nl_assoc_indic);
94
95 int ieee802154_nl_assoc_confirm(struct net_device *dev, __le16 short_addr,
96                 u8 status)
97 {
98         struct sk_buff *msg;
99
100         pr_debug("%s\n", __func__);
101
102         msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF);
103         if (!msg)
104                 return -ENOBUFS;
105
106         if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
107             nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
108             nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
109                     dev->dev_addr) ||
110             nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
111             nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
112                 goto nla_put_failure;
113         return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
114
115 nla_put_failure:
116         nlmsg_free(msg);
117         return -ENOBUFS;
118 }
119 EXPORT_SYMBOL(ieee802154_nl_assoc_confirm);
120
121 int ieee802154_nl_disassoc_indic(struct net_device *dev,
122                 struct ieee802154_addr *addr, u8 reason)
123 {
124         struct sk_buff *msg;
125
126         pr_debug("%s\n", __func__);
127
128         msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC);
129         if (!msg)
130                 return -ENOBUFS;
131
132         if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
133             nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
134             nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
135                     dev->dev_addr))
136                 goto nla_put_failure;
137         if (addr->mode == IEEE802154_ADDR_LONG) {
138                 if (nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR,
139                                    addr->extended_addr))
140                         goto nla_put_failure;
141         } else {
142                 if (nla_put_shortaddr(msg, IEEE802154_ATTR_SRC_SHORT_ADDR,
143                                       addr->short_addr))
144                         goto nla_put_failure;
145         }
146         if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason))
147                 goto nla_put_failure;
148         return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
149
150 nla_put_failure:
151         nlmsg_free(msg);
152         return -ENOBUFS;
153 }
154 EXPORT_SYMBOL(ieee802154_nl_disassoc_indic);
155
156 int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)
157 {
158         struct sk_buff *msg;
159
160         pr_debug("%s\n", __func__);
161
162         msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF);
163         if (!msg)
164                 return -ENOBUFS;
165
166         if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
167             nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
168             nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
169                     dev->dev_addr) ||
170             nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
171                 goto nla_put_failure;
172         return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
173
174 nla_put_failure:
175         nlmsg_free(msg);
176         return -ENOBUFS;
177 }
178 EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm);
179
180 int ieee802154_nl_beacon_indic(struct net_device *dev, __le16 panid,
181                                __le16 coord_addr)
182 {
183         struct sk_buff *msg;
184
185         pr_debug("%s\n", __func__);
186
187         msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC);
188         if (!msg)
189                 return -ENOBUFS;
190
191         if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
192             nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
193             nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
194                     dev->dev_addr) ||
195             nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_SHORT_ADDR,
196                               coord_addr) ||
197             nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_PAN_ID, panid))
198                 goto nla_put_failure;
199         return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
200
201 nla_put_failure:
202         nlmsg_free(msg);
203         return -ENOBUFS;
204 }
205 EXPORT_SYMBOL(ieee802154_nl_beacon_indic);
206
207 int ieee802154_nl_scan_confirm(struct net_device *dev,
208                 u8 status, u8 scan_type, u32 unscanned, u8 page,
209                 u8 *edl/* , struct list_head *pan_desc_list */)
210 {
211         struct sk_buff *msg;
212
213         pr_debug("%s\n", __func__);
214
215         msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF);
216         if (!msg)
217                 return -ENOBUFS;
218
219         if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
220             nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
221             nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
222                     dev->dev_addr) ||
223             nla_put_u8(msg, IEEE802154_ATTR_STATUS, status) ||
224             nla_put_u8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type) ||
225             nla_put_u32(msg, IEEE802154_ATTR_CHANNELS, unscanned) ||
226             nla_put_u8(msg, IEEE802154_ATTR_PAGE, page) ||
227             (edl &&
228              nla_put(msg, IEEE802154_ATTR_ED_LIST, 27, edl)))
229                 goto nla_put_failure;
230         return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
231
232 nla_put_failure:
233         nlmsg_free(msg);
234         return -ENOBUFS;
235 }
236 EXPORT_SYMBOL(ieee802154_nl_scan_confirm);
237
238 int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
239 {
240         struct sk_buff *msg;
241
242         pr_debug("%s\n", __func__);
243
244         msg = ieee802154_nl_create(0, IEEE802154_START_CONF);
245         if (!msg)
246                 return -ENOBUFS;
247
248         if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
249             nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
250             nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
251                     dev->dev_addr) ||
252             nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
253                 goto nla_put_failure;
254         return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
255
256 nla_put_failure:
257         nlmsg_free(msg);
258         return -ENOBUFS;
259 }
260 EXPORT_SYMBOL(ieee802154_nl_start_confirm);
261
262 static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
263         u32 seq, int flags, struct net_device *dev)
264 {
265         void *hdr;
266         struct wpan_phy *phy;
267         struct ieee802154_mlme_ops *ops;
268         __le16 short_addr, pan_id;
269
270         pr_debug("%s\n", __func__);
271
272         hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
273                 IEEE802154_LIST_IFACE);
274         if (!hdr)
275                 goto out;
276
277         ops = ieee802154_mlme_ops(dev);
278         phy = ops->get_phy(dev);
279         BUG_ON(!phy);
280
281         short_addr = ops->get_short_addr(dev);
282         pan_id = ops->get_pan_id(dev);
283
284         if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
285             nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
286             nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
287             nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
288                     dev->dev_addr) ||
289             nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
290             nla_put_shortaddr(msg, IEEE802154_ATTR_PAN_ID, pan_id))
291                 goto nla_put_failure;
292
293         if (ops->get_mac_params) {
294                 struct ieee802154_mac_params params;
295
296                 ops->get_mac_params(dev, &params);
297
298                 if (nla_put_s8(msg, IEEE802154_ATTR_TXPOWER,
299                                params.transmit_power) ||
300                     nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, params.lbt) ||
301                     nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE,
302                                params.cca_mode) ||
303                     nla_put_s32(msg, IEEE802154_ATTR_CCA_ED_LEVEL,
304                                 params.cca_ed_level) ||
305                     nla_put_u8(msg, IEEE802154_ATTR_CSMA_RETRIES,
306                                params.csma_retries) ||
307                     nla_put_u8(msg, IEEE802154_ATTR_CSMA_MIN_BE,
308                                params.min_be) ||
309                     nla_put_u8(msg, IEEE802154_ATTR_CSMA_MAX_BE,
310                                params.max_be) ||
311                     nla_put_s8(msg, IEEE802154_ATTR_FRAME_RETRIES,
312                                params.frame_retries))
313                         goto nla_put_failure;
314         }
315
316         wpan_phy_put(phy);
317         return genlmsg_end(msg, hdr);
318
319 nla_put_failure:
320         wpan_phy_put(phy);
321         genlmsg_cancel(msg, hdr);
322 out:
323         return -EMSGSIZE;
324 }
325
326 /* Requests from userspace */
327 static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)
328 {
329         struct net_device *dev;
330
331         if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
332                 char name[IFNAMSIZ + 1];
333                 nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME],
334                                 sizeof(name));
335                 dev = dev_get_by_name(&init_net, name);
336         } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX])
337                 dev = dev_get_by_index(&init_net,
338                         nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX]));
339         else
340                 return NULL;
341
342         if (!dev)
343                 return NULL;
344
345         if (dev->type != ARPHRD_IEEE802154) {
346                 dev_put(dev);
347                 return NULL;
348         }
349
350         return dev;
351 }
352
353 int ieee802154_associate_req(struct sk_buff *skb, struct genl_info *info)
354 {
355         struct net_device *dev;
356         struct ieee802154_addr addr;
357         u8 page;
358         int ret = -EOPNOTSUPP;
359
360         if (!info->attrs[IEEE802154_ATTR_CHANNEL] ||
361             !info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
362             (!info->attrs[IEEE802154_ATTR_COORD_HW_ADDR] &&
363                 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]) ||
364             !info->attrs[IEEE802154_ATTR_CAPABILITY])
365                 return -EINVAL;
366
367         dev = ieee802154_nl_get_dev(info);
368         if (!dev)
369                 return -ENODEV;
370         if (!ieee802154_mlme_ops(dev)->assoc_req)
371                 goto out;
372
373         if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) {
374                 addr.mode = IEEE802154_ADDR_LONG;
375                 addr.extended_addr = nla_get_hwaddr(
376                                 info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]);
377         } else {
378                 addr.mode = IEEE802154_ADDR_SHORT;
379                 addr.short_addr = nla_get_shortaddr(
380                                 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
381         }
382         addr.pan_id = nla_get_shortaddr(
383                         info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
384
385         if (info->attrs[IEEE802154_ATTR_PAGE])
386                 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
387         else
388                 page = 0;
389
390         ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr,
391                         nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]),
392                         page,
393                         nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY]));
394
395 out:
396         dev_put(dev);
397         return ret;
398 }
399
400 int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info)
401 {
402         struct net_device *dev;
403         struct ieee802154_addr addr;
404         int ret = -EOPNOTSUPP;
405
406         if (!info->attrs[IEEE802154_ATTR_STATUS] ||
407             !info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] ||
408             !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR])
409                 return -EINVAL;
410
411         dev = ieee802154_nl_get_dev(info);
412         if (!dev)
413                 return -ENODEV;
414         if (!ieee802154_mlme_ops(dev)->assoc_resp)
415                 goto out;
416
417         addr.mode = IEEE802154_ADDR_LONG;
418         addr.extended_addr = nla_get_hwaddr(
419                         info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]);
420         addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
421
422         ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr,
423                 nla_get_shortaddr(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]),
424                 nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS]));
425
426 out:
427         dev_put(dev);
428         return ret;
429 }
430
431 int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info)
432 {
433         struct net_device *dev;
434         struct ieee802154_addr addr;
435         int ret = -EOPNOTSUPP;
436
437         if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] &&
438                 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) ||
439             !info->attrs[IEEE802154_ATTR_REASON])
440                 return -EINVAL;
441
442         dev = ieee802154_nl_get_dev(info);
443         if (!dev)
444                 return -ENODEV;
445         if (!ieee802154_mlme_ops(dev)->disassoc_req)
446                 goto out;
447
448         if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) {
449                 addr.mode = IEEE802154_ADDR_LONG;
450                 addr.extended_addr = nla_get_hwaddr(
451                                 info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]);
452         } else {
453                 addr.mode = IEEE802154_ADDR_SHORT;
454                 addr.short_addr = nla_get_shortaddr(
455                                 info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]);
456         }
457         addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
458
459         ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr,
460                         nla_get_u8(info->attrs[IEEE802154_ATTR_REASON]));
461
462 out:
463         dev_put(dev);
464         return ret;
465 }
466
467 /*
468  * PANid, channel, beacon_order = 15, superframe_order = 15,
469  * PAN_coordinator, battery_life_extension = 0,
470  * coord_realignment = 0, security_enable = 0
471 */
472 int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
473 {
474         struct net_device *dev;
475         struct ieee802154_addr addr;
476
477         u8 channel, bcn_ord, sf_ord;
478         u8 page;
479         int pan_coord, blx, coord_realign;
480         int ret = -EOPNOTSUPP;
481
482         if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
483             !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] ||
484             !info->attrs[IEEE802154_ATTR_CHANNEL] ||
485             !info->attrs[IEEE802154_ATTR_BCN_ORD] ||
486             !info->attrs[IEEE802154_ATTR_SF_ORD] ||
487             !info->attrs[IEEE802154_ATTR_PAN_COORD] ||
488             !info->attrs[IEEE802154_ATTR_BAT_EXT] ||
489             !info->attrs[IEEE802154_ATTR_COORD_REALIGN]
490          )
491                 return -EINVAL;
492
493         dev = ieee802154_nl_get_dev(info);
494         if (!dev)
495                 return -ENODEV;
496         if (!ieee802154_mlme_ops(dev)->start_req)
497                 goto out;
498
499         addr.mode = IEEE802154_ADDR_SHORT;
500         addr.short_addr = nla_get_shortaddr(
501                         info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
502         addr.pan_id = nla_get_shortaddr(
503                         info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
504
505         channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]);
506         bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]);
507         sf_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_SF_ORD]);
508         pan_coord = nla_get_u8(info->attrs[IEEE802154_ATTR_PAN_COORD]);
509         blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]);
510         coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]);
511
512         if (info->attrs[IEEE802154_ATTR_PAGE])
513                 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
514         else
515                 page = 0;
516
517
518         if (addr.short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) {
519                 ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS);
520                 dev_put(dev);
521                 return -EINVAL;
522         }
523
524         ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
525                 bcn_ord, sf_ord, pan_coord, blx, coord_realign);
526
527 out:
528         dev_put(dev);
529         return ret;
530 }
531
532 int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
533 {
534         struct net_device *dev;
535         int ret = -EOPNOTSUPP;
536         u8 type;
537         u32 channels;
538         u8 duration;
539         u8 page;
540
541         if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] ||
542             !info->attrs[IEEE802154_ATTR_CHANNELS] ||
543             !info->attrs[IEEE802154_ATTR_DURATION])
544                 return -EINVAL;
545
546         dev = ieee802154_nl_get_dev(info);
547         if (!dev)
548                 return -ENODEV;
549         if (!ieee802154_mlme_ops(dev)->scan_req)
550                 goto out;
551
552         type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]);
553         channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]);
554         duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]);
555
556         if (info->attrs[IEEE802154_ATTR_PAGE])
557                 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
558         else
559                 page = 0;
560
561
562         ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page,
563                         duration);
564
565 out:
566         dev_put(dev);
567         return ret;
568 }
569
570 int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info)
571 {
572         /* Request for interface name, index, type, IEEE address,
573            PAN Id, short address */
574         struct sk_buff *msg;
575         struct net_device *dev = NULL;
576         int rc = -ENOBUFS;
577
578         pr_debug("%s\n", __func__);
579
580         dev = ieee802154_nl_get_dev(info);
581         if (!dev)
582                 return -ENODEV;
583
584         msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
585         if (!msg)
586                 goto out_dev;
587
588         rc = ieee802154_nl_fill_iface(msg, info->snd_portid, info->snd_seq,
589                         0, dev);
590         if (rc < 0)
591                 goto out_free;
592
593         dev_put(dev);
594
595         return genlmsg_reply(msg, info);
596 out_free:
597         nlmsg_free(msg);
598 out_dev:
599         dev_put(dev);
600         return rc;
601
602 }
603
604 int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb)
605 {
606         struct net *net = sock_net(skb->sk);
607         struct net_device *dev;
608         int idx;
609         int s_idx = cb->args[0];
610
611         pr_debug("%s\n", __func__);
612
613         idx = 0;
614         for_each_netdev(net, dev) {
615                 if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
616                         goto cont;
617
618                 if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid,
619                         cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0)
620                         break;
621 cont:
622                 idx++;
623         }
624         cb->args[0] = idx;
625
626         return skb->len;
627 }
628
629 int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
630 {
631         struct net_device *dev = NULL;
632         struct ieee802154_mlme_ops *ops;
633         struct ieee802154_mac_params params;
634         struct wpan_phy *phy;
635         int rc = -EINVAL;
636
637         pr_debug("%s\n", __func__);
638
639         dev = ieee802154_nl_get_dev(info);
640         if (!dev)
641                 return -ENODEV;
642
643         ops = ieee802154_mlme_ops(dev);
644
645         if (!ops->get_mac_params || !ops->set_mac_params) {
646                 rc = -EOPNOTSUPP;
647                 goto out;
648         }
649
650         if (netif_running(dev)) {
651                 rc = -EBUSY;
652                 goto out;
653         }
654
655         if (!info->attrs[IEEE802154_ATTR_LBT_ENABLED] &&
656             !info->attrs[IEEE802154_ATTR_CCA_MODE] &&
657             !info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL] &&
658             !info->attrs[IEEE802154_ATTR_CSMA_RETRIES] &&
659             !info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] &&
660             !info->attrs[IEEE802154_ATTR_CSMA_MAX_BE] &&
661             !info->attrs[IEEE802154_ATTR_FRAME_RETRIES])
662                 goto out;
663
664         phy = ops->get_phy(dev);
665
666         if ((!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]) ||
667             (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE]) ||
668             (!phy->set_cca_ed_level &&
669              info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) ||
670             (!phy->set_csma_params &&
671              (info->attrs[IEEE802154_ATTR_CSMA_RETRIES] ||
672               info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] ||
673               info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])) ||
674             (!phy->set_frame_retries &&
675              info->attrs[IEEE802154_ATTR_FRAME_RETRIES])) {
676                 rc = -EOPNOTSUPP;
677                 goto out_phy;
678         }
679
680         ops->get_mac_params(dev, &params);
681
682         if (info->attrs[IEEE802154_ATTR_TXPOWER])
683                 params.transmit_power = nla_get_s8(info->attrs[IEEE802154_ATTR_TXPOWER]);
684
685         if (info->attrs[IEEE802154_ATTR_LBT_ENABLED])
686                 params.lbt = nla_get_u8(info->attrs[IEEE802154_ATTR_LBT_ENABLED]);
687
688         if (info->attrs[IEEE802154_ATTR_CCA_MODE])
689                 params.cca_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]);
690
691         if (info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL])
692                 params.cca_ed_level = nla_get_s32(info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]);
693
694         if (info->attrs[IEEE802154_ATTR_CSMA_RETRIES])
695                 params.csma_retries = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_RETRIES]);
696
697         if (info->attrs[IEEE802154_ATTR_CSMA_MIN_BE])
698                 params.min_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MIN_BE]);
699
700         if (info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])
701                 params.max_be = nla_get_u8(info->attrs[IEEE802154_ATTR_CSMA_MAX_BE]);
702
703         if (info->attrs[IEEE802154_ATTR_FRAME_RETRIES])
704                 params.frame_retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]);
705
706         rc = ops->set_mac_params(dev, &params);
707
708         wpan_phy_put(phy);
709         dev_put(dev);
710         return rc;
711
712 out_phy:
713         wpan_phy_put(phy);
714 out:
715         dev_put(dev);
716         return rc;
717 }