blob: 96bb08abece29408f0d9d65f453ee7f2c0c55c4e [file] [log] [blame]
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +04001/*
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
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090025#include <linux/gfp.h>
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040026#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>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040033#include <linux/export.h>
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040034#include <net/af_ieee802154.h>
35#include <net/nl802154.h>
36#include <net/ieee802154.h>
37#include <net/ieee802154_netdev.h>
Dmitry Eremin-Solenikov0a868b22009-10-29 16:28:52 +030038#include <net/wpan-phy.h>
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040039
40#include "ieee802154.h"
41
42static struct genl_multicast_group ieee802154_coord_mcgrp = {
43 .name = IEEE802154_MCAST_COORD_NAME,
44};
45
46static struct genl_multicast_group ieee802154_beacon_mcgrp = {
47 .name = IEEE802154_MCAST_BEACON_NAME,
48};
49
50int ieee802154_nl_assoc_indic(struct net_device *dev,
51 struct ieee802154_addr *addr, u8 cap)
52{
53 struct sk_buff *msg;
54
55 pr_debug("%s\n", __func__);
56
57 if (addr->addr_type != IEEE802154_ADDR_LONG) {
58 pr_err("%s: received non-long source address!\n", __func__);
59 return -EINVAL;
60 }
61
62 msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC);
63 if (!msg)
64 return -ENOBUFS;
65
David S. Millerbe51da02012-04-01 20:45:25 -040066 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
67 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
68 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
69 dev->dev_addr) ||
70 nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN,
71 addr->hwaddr) ||
72 nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap))
73 goto nla_put_failure;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040074
75 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
76
77nla_put_failure:
78 nlmsg_free(msg);
79 return -ENOBUFS;
80}
81EXPORT_SYMBOL(ieee802154_nl_assoc_indic);
82
83int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr,
84 u8 status)
85{
86 struct sk_buff *msg;
87
88 pr_debug("%s\n", __func__);
89
90 msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF);
91 if (!msg)
92 return -ENOBUFS;
93
David S. Millerbe51da02012-04-01 20:45:25 -040094 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
95 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
96 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
97 dev->dev_addr) ||
98 nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
99 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
100 goto nla_put_failure;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400101 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
102
103nla_put_failure:
104 nlmsg_free(msg);
105 return -ENOBUFS;
106}
107EXPORT_SYMBOL(ieee802154_nl_assoc_confirm);
108
109int ieee802154_nl_disassoc_indic(struct net_device *dev,
110 struct ieee802154_addr *addr, u8 reason)
111{
112 struct sk_buff *msg;
113
114 pr_debug("%s\n", __func__);
115
116 msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC);
117 if (!msg)
118 return -ENOBUFS;
119
David S. Millerbe51da02012-04-01 20:45:25 -0400120 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
121 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
122 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
123 dev->dev_addr))
124 goto nla_put_failure;
125 if (addr->addr_type == IEEE802154_ADDR_LONG) {
126 if (nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN,
127 addr->hwaddr))
128 goto nla_put_failure;
129 } else {
130 if (nla_put_u16(msg, IEEE802154_ATTR_SRC_SHORT_ADDR,
131 addr->short_addr))
132 goto nla_put_failure;
133 }
134 if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason))
135 goto nla_put_failure;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400136 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
137
138nla_put_failure:
139 nlmsg_free(msg);
140 return -ENOBUFS;
141}
142EXPORT_SYMBOL(ieee802154_nl_disassoc_indic);
143
144int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)
145{
146 struct sk_buff *msg;
147
148 pr_debug("%s\n", __func__);
149
150 msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF);
151 if (!msg)
152 return -ENOBUFS;
153
David S. Millerbe51da02012-04-01 20:45:25 -0400154 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
155 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
156 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
157 dev->dev_addr) ||
158 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
159 goto nla_put_failure;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400160 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
161
162nla_put_failure:
163 nlmsg_free(msg);
164 return -ENOBUFS;
165}
166EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm);
167
168int ieee802154_nl_beacon_indic(struct net_device *dev,
169 u16 panid, u16 coord_addr)
170{
171 struct sk_buff *msg;
172
173 pr_debug("%s\n", __func__);
174
175 msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC);
176 if (!msg)
177 return -ENOBUFS;
178
David S. Millerbe51da02012-04-01 20:45:25 -0400179 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
180 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
181 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
182 dev->dev_addr) ||
183 nla_put_u16(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, coord_addr) ||
184 nla_put_u16(msg, IEEE802154_ATTR_COORD_PAN_ID, panid))
185 goto nla_put_failure;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400186 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
187
188nla_put_failure:
189 nlmsg_free(msg);
190 return -ENOBUFS;
191}
192EXPORT_SYMBOL(ieee802154_nl_beacon_indic);
193
194int ieee802154_nl_scan_confirm(struct net_device *dev,
195 u8 status, u8 scan_type, u32 unscanned, u8 page,
196 u8 *edl/* , struct list_head *pan_desc_list */)
197{
198 struct sk_buff *msg;
199
200 pr_debug("%s\n", __func__);
201
202 msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF);
203 if (!msg)
204 return -ENOBUFS;
205
David S. Millerbe51da02012-04-01 20:45:25 -0400206 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
207 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
208 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
209 dev->dev_addr) ||
210 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status) ||
211 nla_put_u8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type) ||
212 nla_put_u32(msg, IEEE802154_ATTR_CHANNELS, unscanned) ||
213 nla_put_u8(msg, IEEE802154_ATTR_PAGE, page) ||
214 (edl &&
215 nla_put(msg, IEEE802154_ATTR_ED_LIST, 27, edl)))
216 goto nla_put_failure;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400217 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
218
219nla_put_failure:
220 nlmsg_free(msg);
221 return -ENOBUFS;
222}
223EXPORT_SYMBOL(ieee802154_nl_scan_confirm);
224
225int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
226{
227 struct sk_buff *msg;
228
229 pr_debug("%s\n", __func__);
230
231 msg = ieee802154_nl_create(0, IEEE802154_START_CONF);
232 if (!msg)
233 return -ENOBUFS;
234
David S. Millerbe51da02012-04-01 20:45:25 -0400235 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
236 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
237 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
238 dev->dev_addr) ||
239 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
240 goto nla_put_failure;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400241 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id);
242
243nla_put_failure:
244 nlmsg_free(msg);
245 return -ENOBUFS;
246}
247EXPORT_SYMBOL(ieee802154_nl_start_confirm);
248
Eric W. Biederman15e47302012-09-07 20:12:54 +0000249static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400250 u32 seq, int flags, struct net_device *dev)
251{
252 void *hdr;
Dmitry Eremin-Solenikov0a868b22009-10-29 16:28:52 +0300253 struct wpan_phy *phy;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400254
255 pr_debug("%s\n", __func__);
256
257 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
258 IEEE802154_LIST_IFACE);
259 if (!hdr)
260 goto out;
261
Dmitry Eremin-Solenikov0a868b22009-10-29 16:28:52 +0300262 phy = ieee802154_mlme_ops(dev)->get_phy(dev);
263 BUG_ON(!phy);
264
David S. Millerbe51da02012-04-01 20:45:25 -0400265 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
266 nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
267 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
268 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
269 dev->dev_addr) ||
270 nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR,
271 ieee802154_mlme_ops(dev)->get_short_addr(dev)) ||
272 nla_put_u16(msg, IEEE802154_ATTR_PAN_ID,
273 ieee802154_mlme_ops(dev)->get_pan_id(dev)))
274 goto nla_put_failure;
Dmitry Eremin-Solenikov0a868b22009-10-29 16:28:52 +0300275 wpan_phy_put(phy);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400276 return genlmsg_end(msg, hdr);
277
278nla_put_failure:
Dmitry Eremin-Solenikov0a868b22009-10-29 16:28:52 +0300279 wpan_phy_put(phy);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400280 genlmsg_cancel(msg, hdr);
281out:
282 return -EMSGSIZE;
283}
284
285/* Requests from userspace */
286static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)
287{
288 struct net_device *dev;
289
290 if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
291 char name[IFNAMSIZ + 1];
292 nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME],
293 sizeof(name));
294 dev = dev_get_by_name(&init_net, name);
295 } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX])
296 dev = dev_get_by_index(&init_net,
297 nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX]));
298 else
299 return NULL;
300
301 if (!dev)
302 return NULL;
303
304 if (dev->type != ARPHRD_IEEE802154) {
305 dev_put(dev);
306 return NULL;
307 }
308
309 return dev;
310}
311
312static int ieee802154_associate_req(struct sk_buff *skb,
313 struct genl_info *info)
314{
315 struct net_device *dev;
316 struct ieee802154_addr addr;
317 u8 page;
318 int ret = -EINVAL;
319
320 if (!info->attrs[IEEE802154_ATTR_CHANNEL] ||
321 !info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
322 (!info->attrs[IEEE802154_ATTR_COORD_HW_ADDR] &&
323 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]) ||
324 !info->attrs[IEEE802154_ATTR_CAPABILITY])
325 return -EINVAL;
326
327 dev = ieee802154_nl_get_dev(info);
328 if (!dev)
329 return -ENODEV;
330
331 if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) {
332 addr.addr_type = IEEE802154_ADDR_LONG;
333 nla_memcpy(addr.hwaddr,
334 info->attrs[IEEE802154_ATTR_COORD_HW_ADDR],
335 IEEE802154_ADDR_LEN);
336 } else {
337 addr.addr_type = IEEE802154_ADDR_SHORT;
338 addr.short_addr = nla_get_u16(
339 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
340 }
341 addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
342
343 if (info->attrs[IEEE802154_ATTR_PAGE])
344 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
345 else
346 page = 0;
347
348 ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr,
349 nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]),
350 page,
351 nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY]));
352
353 dev_put(dev);
354 return ret;
355}
356
357static int ieee802154_associate_resp(struct sk_buff *skb,
358 struct genl_info *info)
359{
360 struct net_device *dev;
361 struct ieee802154_addr addr;
362 int ret = -EINVAL;
363
364 if (!info->attrs[IEEE802154_ATTR_STATUS] ||
365 !info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] ||
366 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR])
367 return -EINVAL;
368
369 dev = ieee802154_nl_get_dev(info);
370 if (!dev)
371 return -ENODEV;
372
373 addr.addr_type = IEEE802154_ADDR_LONG;
374 nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
375 IEEE802154_ADDR_LEN);
376 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
377
378
379 ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr,
380 nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]),
381 nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS]));
382
383 dev_put(dev);
384 return ret;
385}
386
387static int ieee802154_disassociate_req(struct sk_buff *skb,
388 struct genl_info *info)
389{
390 struct net_device *dev;
391 struct ieee802154_addr addr;
392 int ret = -EINVAL;
393
394 if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] &&
395 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) ||
396 !info->attrs[IEEE802154_ATTR_REASON])
397 return -EINVAL;
398
399 dev = ieee802154_nl_get_dev(info);
400 if (!dev)
401 return -ENODEV;
402
403 if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) {
404 addr.addr_type = IEEE802154_ADDR_LONG;
405 nla_memcpy(addr.hwaddr,
406 info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
407 IEEE802154_ADDR_LEN);
408 } else {
409 addr.addr_type = IEEE802154_ADDR_SHORT;
410 addr.short_addr = nla_get_u16(
411 info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]);
412 }
413 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
414
415 ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr,
416 nla_get_u8(info->attrs[IEEE802154_ATTR_REASON]));
417
418 dev_put(dev);
419 return ret;
420}
421
422/*
423 * PANid, channel, beacon_order = 15, superframe_order = 15,
424 * PAN_coordinator, battery_life_extension = 0,
425 * coord_realignment = 0, security_enable = 0
426*/
427static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
428{
429 struct net_device *dev;
430 struct ieee802154_addr addr;
431
432 u8 channel, bcn_ord, sf_ord;
433 u8 page;
434 int pan_coord, blx, coord_realign;
435 int ret;
436
437 if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
438 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] ||
439 !info->attrs[IEEE802154_ATTR_CHANNEL] ||
440 !info->attrs[IEEE802154_ATTR_BCN_ORD] ||
441 !info->attrs[IEEE802154_ATTR_SF_ORD] ||
442 !info->attrs[IEEE802154_ATTR_PAN_COORD] ||
443 !info->attrs[IEEE802154_ATTR_BAT_EXT] ||
444 !info->attrs[IEEE802154_ATTR_COORD_REALIGN]
445 )
446 return -EINVAL;
447
448 dev = ieee802154_nl_get_dev(info);
449 if (!dev)
450 return -ENODEV;
451
452 addr.addr_type = IEEE802154_ADDR_SHORT;
453 addr.short_addr = nla_get_u16(
454 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
455 addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
456
457 channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]);
458 bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]);
459 sf_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_SF_ORD]);
460 pan_coord = nla_get_u8(info->attrs[IEEE802154_ATTR_PAN_COORD]);
461 blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]);
462 coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]);
463
464 if (info->attrs[IEEE802154_ATTR_PAGE])
465 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
466 else
467 page = 0;
468
469
470 if (addr.short_addr == IEEE802154_ADDR_BROADCAST) {
471 ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS);
472 dev_put(dev);
473 return -EINVAL;
474 }
475
476 ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
477 bcn_ord, sf_ord, pan_coord, blx, coord_realign);
478
479 dev_put(dev);
480 return ret;
481}
482
483static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
484{
485 struct net_device *dev;
486 int ret;
487 u8 type;
488 u32 channels;
489 u8 duration;
490 u8 page;
491
492 if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] ||
493 !info->attrs[IEEE802154_ATTR_CHANNELS] ||
494 !info->attrs[IEEE802154_ATTR_DURATION])
495 return -EINVAL;
496
497 dev = ieee802154_nl_get_dev(info);
498 if (!dev)
499 return -ENODEV;
500
501 type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]);
502 channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]);
503 duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]);
504
505 if (info->attrs[IEEE802154_ATTR_PAGE])
506 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
507 else
508 page = 0;
509
510
511 ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page,
512 duration);
513
514 dev_put(dev);
515 return ret;
516}
517
518static int ieee802154_list_iface(struct sk_buff *skb,
519 struct genl_info *info)
520{
521 /* Request for interface name, index, type, IEEE address,
522 PAN Id, short address */
523 struct sk_buff *msg;
524 struct net_device *dev = NULL;
525 int rc = -ENOBUFS;
526
527 pr_debug("%s\n", __func__);
528
529 dev = ieee802154_nl_get_dev(info);
530 if (!dev)
531 return -ENODEV;
532
Thomas Graf58050fc2012-06-28 03:57:45 +0000533 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400534 if (!msg)
535 goto out_dev;
536
Eric W. Biederman15e47302012-09-07 20:12:54 +0000537 rc = ieee802154_nl_fill_iface(msg, info->snd_portid, info->snd_seq,
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400538 0, dev);
539 if (rc < 0)
540 goto out_free;
541
542 dev_put(dev);
543
544 return genlmsg_reply(msg, info);
545out_free:
546 nlmsg_free(msg);
547out_dev:
548 dev_put(dev);
549 return rc;
550
551}
552
553static int ieee802154_dump_iface(struct sk_buff *skb,
554 struct netlink_callback *cb)
555{
556 struct net *net = sock_net(skb->sk);
557 struct net_device *dev;
558 int idx;
559 int s_idx = cb->args[0];
560
561 pr_debug("%s\n", __func__);
562
563 idx = 0;
564 for_each_netdev(net, dev) {
565 if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
566 goto cont;
567
Eric W. Biederman15e47302012-09-07 20:12:54 +0000568 if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid,
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400569 cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0)
570 break;
571cont:
572 idx++;
573 }
574 cb->args[0] = idx;
575
576 return skb->len;
577}
578
579static struct genl_ops ieee802154_coordinator_ops[] = {
580 IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req),
581 IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp),
582 IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req),
583 IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req),
584 IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req),
585 IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface,
586 ieee802154_dump_iface),
587};
588
589/*
590 * No need to unregister as family unregistration will do it.
591 */
592int nl802154_mac_register(void)
593{
594 int i;
595 int rc;
596
597 rc = genl_register_mc_group(&nl802154_family,
598 &ieee802154_coord_mcgrp);
599 if (rc)
600 return rc;
601
602 rc = genl_register_mc_group(&nl802154_family,
603 &ieee802154_beacon_mcgrp);
604 if (rc)
605 return rc;
606
607 for (i = 0; i < ARRAY_SIZE(ieee802154_coordinator_ops); i++) {
608 rc = genl_register_ops(&nl802154_family,
609 &ieee802154_coordinator_ops[i]);
610 if (rc)
611 return rc;
612 }
613
614 return 0;
615}