blob: 28d493032132574d17ecc505465755c1188fe676 [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
Johannes Berg1c582d92013-11-14 17:14:41 +010042struct genl_multicast_group ieee802154_coord_mcgrp = {
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040043 .name = IEEE802154_MCAST_COORD_NAME,
44};
45
Johannes Berg1c582d92013-11-14 17:14:41 +010046struct genl_multicast_group ieee802154_beacon_mcgrp = {
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040047 .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
Johannes Berg1c582d92013-11-14 17:14:41 +0100312int ieee802154_associate_req(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400313{
314 struct net_device *dev;
315 struct ieee802154_addr addr;
316 u8 page;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000317 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400318
319 if (!info->attrs[IEEE802154_ATTR_CHANNEL] ||
320 !info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
321 (!info->attrs[IEEE802154_ATTR_COORD_HW_ADDR] &&
322 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]) ||
323 !info->attrs[IEEE802154_ATTR_CAPABILITY])
324 return -EINVAL;
325
326 dev = ieee802154_nl_get_dev(info);
327 if (!dev)
328 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000329 if (!ieee802154_mlme_ops(dev)->assoc_req)
330 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400331
332 if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) {
333 addr.addr_type = IEEE802154_ADDR_LONG;
334 nla_memcpy(addr.hwaddr,
335 info->attrs[IEEE802154_ATTR_COORD_HW_ADDR],
336 IEEE802154_ADDR_LEN);
337 } else {
338 addr.addr_type = IEEE802154_ADDR_SHORT;
339 addr.short_addr = nla_get_u16(
340 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
341 }
342 addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
343
344 if (info->attrs[IEEE802154_ATTR_PAGE])
345 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
346 else
347 page = 0;
348
349 ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr,
350 nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]),
351 page,
352 nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY]));
353
Werner Almesberger56aa0912013-04-04 06:32:35 +0000354out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400355 dev_put(dev);
356 return ret;
357}
358
Johannes Berg1c582d92013-11-14 17:14:41 +0100359int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400360{
361 struct net_device *dev;
362 struct ieee802154_addr addr;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000363 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400364
365 if (!info->attrs[IEEE802154_ATTR_STATUS] ||
366 !info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] ||
367 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR])
368 return -EINVAL;
369
370 dev = ieee802154_nl_get_dev(info);
371 if (!dev)
372 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000373 if (!ieee802154_mlme_ops(dev)->assoc_resp)
374 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400375
376 addr.addr_type = IEEE802154_ADDR_LONG;
377 nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
378 IEEE802154_ADDR_LEN);
379 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
380
381
382 ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr,
383 nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]),
384 nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS]));
385
Werner Almesberger56aa0912013-04-04 06:32:35 +0000386out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400387 dev_put(dev);
388 return ret;
389}
390
Johannes Berg1c582d92013-11-14 17:14:41 +0100391int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400392{
393 struct net_device *dev;
394 struct ieee802154_addr addr;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000395 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400396
397 if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] &&
398 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) ||
399 !info->attrs[IEEE802154_ATTR_REASON])
400 return -EINVAL;
401
402 dev = ieee802154_nl_get_dev(info);
403 if (!dev)
404 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000405 if (!ieee802154_mlme_ops(dev)->disassoc_req)
406 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400407
408 if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) {
409 addr.addr_type = IEEE802154_ADDR_LONG;
410 nla_memcpy(addr.hwaddr,
411 info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
412 IEEE802154_ADDR_LEN);
413 } else {
414 addr.addr_type = IEEE802154_ADDR_SHORT;
415 addr.short_addr = nla_get_u16(
416 info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]);
417 }
418 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
419
420 ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr,
421 nla_get_u8(info->attrs[IEEE802154_ATTR_REASON]));
422
Werner Almesberger56aa0912013-04-04 06:32:35 +0000423out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400424 dev_put(dev);
425 return ret;
426}
427
428/*
429 * PANid, channel, beacon_order = 15, superframe_order = 15,
430 * PAN_coordinator, battery_life_extension = 0,
431 * coord_realignment = 0, security_enable = 0
432*/
Johannes Berg1c582d92013-11-14 17:14:41 +0100433int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400434{
435 struct net_device *dev;
436 struct ieee802154_addr addr;
437
438 u8 channel, bcn_ord, sf_ord;
439 u8 page;
440 int pan_coord, blx, coord_realign;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000441 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400442
443 if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
444 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] ||
445 !info->attrs[IEEE802154_ATTR_CHANNEL] ||
446 !info->attrs[IEEE802154_ATTR_BCN_ORD] ||
447 !info->attrs[IEEE802154_ATTR_SF_ORD] ||
448 !info->attrs[IEEE802154_ATTR_PAN_COORD] ||
449 !info->attrs[IEEE802154_ATTR_BAT_EXT] ||
450 !info->attrs[IEEE802154_ATTR_COORD_REALIGN]
451 )
452 return -EINVAL;
453
454 dev = ieee802154_nl_get_dev(info);
455 if (!dev)
456 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000457 if (!ieee802154_mlme_ops(dev)->start_req)
458 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400459
460 addr.addr_type = IEEE802154_ADDR_SHORT;
461 addr.short_addr = nla_get_u16(
462 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
463 addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
464
465 channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]);
466 bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]);
467 sf_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_SF_ORD]);
468 pan_coord = nla_get_u8(info->attrs[IEEE802154_ATTR_PAN_COORD]);
469 blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]);
470 coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]);
471
472 if (info->attrs[IEEE802154_ATTR_PAGE])
473 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
474 else
475 page = 0;
476
477
478 if (addr.short_addr == IEEE802154_ADDR_BROADCAST) {
479 ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS);
480 dev_put(dev);
481 return -EINVAL;
482 }
483
484 ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
485 bcn_ord, sf_ord, pan_coord, blx, coord_realign);
486
Werner Almesberger56aa0912013-04-04 06:32:35 +0000487out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400488 dev_put(dev);
489 return ret;
490}
491
Johannes Berg1c582d92013-11-14 17:14:41 +0100492int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400493{
494 struct net_device *dev;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000495 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400496 u8 type;
497 u32 channels;
498 u8 duration;
499 u8 page;
500
501 if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] ||
502 !info->attrs[IEEE802154_ATTR_CHANNELS] ||
503 !info->attrs[IEEE802154_ATTR_DURATION])
504 return -EINVAL;
505
506 dev = ieee802154_nl_get_dev(info);
507 if (!dev)
508 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000509 if (!ieee802154_mlme_ops(dev)->scan_req)
510 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400511
512 type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]);
513 channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]);
514 duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]);
515
516 if (info->attrs[IEEE802154_ATTR_PAGE])
517 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
518 else
519 page = 0;
520
521
522 ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page,
523 duration);
524
Werner Almesberger56aa0912013-04-04 06:32:35 +0000525out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400526 dev_put(dev);
527 return ret;
528}
529
Johannes Berg1c582d92013-11-14 17:14:41 +0100530int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400531{
532 /* Request for interface name, index, type, IEEE address,
533 PAN Id, short address */
534 struct sk_buff *msg;
535 struct net_device *dev = NULL;
536 int rc = -ENOBUFS;
537
538 pr_debug("%s\n", __func__);
539
540 dev = ieee802154_nl_get_dev(info);
541 if (!dev)
542 return -ENODEV;
543
Thomas Graf58050fc2012-06-28 03:57:45 +0000544 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400545 if (!msg)
546 goto out_dev;
547
Eric W. Biederman15e47302012-09-07 20:12:54 +0000548 rc = ieee802154_nl_fill_iface(msg, info->snd_portid, info->snd_seq,
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400549 0, dev);
550 if (rc < 0)
551 goto out_free;
552
553 dev_put(dev);
554
555 return genlmsg_reply(msg, info);
556out_free:
557 nlmsg_free(msg);
558out_dev:
559 dev_put(dev);
560 return rc;
561
562}
563
Johannes Berg1c582d92013-11-14 17:14:41 +0100564int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400565{
566 struct net *net = sock_net(skb->sk);
567 struct net_device *dev;
568 int idx;
569 int s_idx = cb->args[0];
570
571 pr_debug("%s\n", __func__);
572
573 idx = 0;
574 for_each_netdev(net, dev) {
575 if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
576 goto cont;
577
Eric W. Biederman15e47302012-09-07 20:12:54 +0000578 if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid,
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400579 cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0)
580 break;
581cont:
582 idx++;
583 }
584 cb->args[0] = idx;
585
586 return skb->len;
587}