blob: b0bdd8c51e9c83a0ef968fc678588f3807559b13 [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;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000318 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400319
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;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000330 if (!ieee802154_mlme_ops(dev)->assoc_req)
331 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400332
333 if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) {
334 addr.addr_type = IEEE802154_ADDR_LONG;
335 nla_memcpy(addr.hwaddr,
336 info->attrs[IEEE802154_ATTR_COORD_HW_ADDR],
337 IEEE802154_ADDR_LEN);
338 } else {
339 addr.addr_type = IEEE802154_ADDR_SHORT;
340 addr.short_addr = nla_get_u16(
341 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
342 }
343 addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
344
345 if (info->attrs[IEEE802154_ATTR_PAGE])
346 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
347 else
348 page = 0;
349
350 ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr,
351 nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]),
352 page,
353 nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY]));
354
Werner Almesberger56aa0912013-04-04 06:32:35 +0000355out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400356 dev_put(dev);
357 return ret;
358}
359
360static int ieee802154_associate_resp(struct sk_buff *skb,
361 struct genl_info *info)
362{
363 struct net_device *dev;
364 struct ieee802154_addr addr;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000365 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400366
367 if (!info->attrs[IEEE802154_ATTR_STATUS] ||
368 !info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] ||
369 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR])
370 return -EINVAL;
371
372 dev = ieee802154_nl_get_dev(info);
373 if (!dev)
374 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000375 if (!ieee802154_mlme_ops(dev)->assoc_resp)
376 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400377
378 addr.addr_type = IEEE802154_ADDR_LONG;
379 nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
380 IEEE802154_ADDR_LEN);
381 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
382
383
384 ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr,
385 nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]),
386 nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS]));
387
Werner Almesberger56aa0912013-04-04 06:32:35 +0000388out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400389 dev_put(dev);
390 return ret;
391}
392
393static int ieee802154_disassociate_req(struct sk_buff *skb,
394 struct genl_info *info)
395{
396 struct net_device *dev;
397 struct ieee802154_addr addr;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000398 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400399
400 if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] &&
401 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) ||
402 !info->attrs[IEEE802154_ATTR_REASON])
403 return -EINVAL;
404
405 dev = ieee802154_nl_get_dev(info);
406 if (!dev)
407 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000408 if (!ieee802154_mlme_ops(dev)->disassoc_req)
409 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400410
411 if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) {
412 addr.addr_type = IEEE802154_ADDR_LONG;
413 nla_memcpy(addr.hwaddr,
414 info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
415 IEEE802154_ADDR_LEN);
416 } else {
417 addr.addr_type = IEEE802154_ADDR_SHORT;
418 addr.short_addr = nla_get_u16(
419 info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]);
420 }
421 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
422
423 ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr,
424 nla_get_u8(info->attrs[IEEE802154_ATTR_REASON]));
425
Werner Almesberger56aa0912013-04-04 06:32:35 +0000426out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400427 dev_put(dev);
428 return ret;
429}
430
431/*
432 * PANid, channel, beacon_order = 15, superframe_order = 15,
433 * PAN_coordinator, battery_life_extension = 0,
434 * coord_realignment = 0, security_enable = 0
435*/
436static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
437{
438 struct net_device *dev;
439 struct ieee802154_addr addr;
440
441 u8 channel, bcn_ord, sf_ord;
442 u8 page;
443 int pan_coord, blx, coord_realign;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000444 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400445
446 if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
447 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] ||
448 !info->attrs[IEEE802154_ATTR_CHANNEL] ||
449 !info->attrs[IEEE802154_ATTR_BCN_ORD] ||
450 !info->attrs[IEEE802154_ATTR_SF_ORD] ||
451 !info->attrs[IEEE802154_ATTR_PAN_COORD] ||
452 !info->attrs[IEEE802154_ATTR_BAT_EXT] ||
453 !info->attrs[IEEE802154_ATTR_COORD_REALIGN]
454 )
455 return -EINVAL;
456
457 dev = ieee802154_nl_get_dev(info);
458 if (!dev)
459 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000460 if (!ieee802154_mlme_ops(dev)->start_req)
461 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400462
463 addr.addr_type = IEEE802154_ADDR_SHORT;
464 addr.short_addr = nla_get_u16(
465 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
466 addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
467
468 channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]);
469 bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]);
470 sf_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_SF_ORD]);
471 pan_coord = nla_get_u8(info->attrs[IEEE802154_ATTR_PAN_COORD]);
472 blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]);
473 coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]);
474
475 if (info->attrs[IEEE802154_ATTR_PAGE])
476 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
477 else
478 page = 0;
479
480
481 if (addr.short_addr == IEEE802154_ADDR_BROADCAST) {
482 ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS);
483 dev_put(dev);
484 return -EINVAL;
485 }
486
487 ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
488 bcn_ord, sf_ord, pan_coord, blx, coord_realign);
489
Werner Almesberger56aa0912013-04-04 06:32:35 +0000490out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400491 dev_put(dev);
492 return ret;
493}
494
495static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
496{
497 struct net_device *dev;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000498 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400499 u8 type;
500 u32 channels;
501 u8 duration;
502 u8 page;
503
504 if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] ||
505 !info->attrs[IEEE802154_ATTR_CHANNELS] ||
506 !info->attrs[IEEE802154_ATTR_DURATION])
507 return -EINVAL;
508
509 dev = ieee802154_nl_get_dev(info);
510 if (!dev)
511 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000512 if (!ieee802154_mlme_ops(dev)->scan_req)
513 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400514
515 type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]);
516 channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]);
517 duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]);
518
519 if (info->attrs[IEEE802154_ATTR_PAGE])
520 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
521 else
522 page = 0;
523
524
525 ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page,
526 duration);
527
Werner Almesberger56aa0912013-04-04 06:32:35 +0000528out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400529 dev_put(dev);
530 return ret;
531}
532
533static int ieee802154_list_iface(struct sk_buff *skb,
534 struct genl_info *info)
535{
536 /* Request for interface name, index, type, IEEE address,
537 PAN Id, short address */
538 struct sk_buff *msg;
539 struct net_device *dev = NULL;
540 int rc = -ENOBUFS;
541
542 pr_debug("%s\n", __func__);
543
544 dev = ieee802154_nl_get_dev(info);
545 if (!dev)
546 return -ENODEV;
547
Thomas Graf58050fc2012-06-28 03:57:45 +0000548 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400549 if (!msg)
550 goto out_dev;
551
Eric W. Biederman15e47302012-09-07 20:12:54 +0000552 rc = ieee802154_nl_fill_iface(msg, info->snd_portid, info->snd_seq,
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400553 0, dev);
554 if (rc < 0)
555 goto out_free;
556
557 dev_put(dev);
558
559 return genlmsg_reply(msg, info);
560out_free:
561 nlmsg_free(msg);
562out_dev:
563 dev_put(dev);
564 return rc;
565
566}
567
568static int ieee802154_dump_iface(struct sk_buff *skb,
569 struct netlink_callback *cb)
570{
571 struct net *net = sock_net(skb->sk);
572 struct net_device *dev;
573 int idx;
574 int s_idx = cb->args[0];
575
576 pr_debug("%s\n", __func__);
577
578 idx = 0;
579 for_each_netdev(net, dev) {
580 if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
581 goto cont;
582
Eric W. Biederman15e47302012-09-07 20:12:54 +0000583 if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid,
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400584 cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0)
585 break;
586cont:
587 idx++;
588 }
589 cb->args[0] = idx;
590
591 return skb->len;
592}
593
594static struct genl_ops ieee802154_coordinator_ops[] = {
595 IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req),
596 IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp),
597 IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req),
598 IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req),
599 IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req),
600 IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface,
601 ieee802154_dump_iface),
602};
603
604/*
605 * No need to unregister as family unregistration will do it.
606 */
607int nl802154_mac_register(void)
608{
609 int i;
610 int rc;
611
612 rc = genl_register_mc_group(&nl802154_family,
613 &ieee802154_coord_mcgrp);
614 if (rc)
615 return rc;
616
617 rc = genl_register_mc_group(&nl802154_family,
618 &ieee802154_beacon_mcgrp);
619 if (rc)
620 return rc;
621
622 for (i = 0; i < ARRAY_SIZE(ieee802154_coordinator_ops); i++) {
623 rc = genl_register_ops(&nl802154_family,
624 &ieee802154_coordinator_ops[i]);
625 if (rc)
626 return rc;
627 }
628
629 return 0;
630}