blob: ba5c1e002f37b2630c53a0284736f5d177bded76 [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
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040042int ieee802154_nl_assoc_indic(struct net_device *dev,
43 struct ieee802154_addr *addr, u8 cap)
44{
45 struct sk_buff *msg;
46
47 pr_debug("%s\n", __func__);
48
49 if (addr->addr_type != IEEE802154_ADDR_LONG) {
50 pr_err("%s: received non-long source address!\n", __func__);
51 return -EINVAL;
52 }
53
54 msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC);
55 if (!msg)
56 return -ENOBUFS;
57
David S. Millerbe51da02012-04-01 20:45:25 -040058 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
59 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
60 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
61 dev->dev_addr) ||
62 nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN,
63 addr->hwaddr) ||
64 nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap))
65 goto nla_put_failure;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040066
Johannes Berg2a94fe42013-11-19 15:19:39 +010067 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040068
69nla_put_failure:
70 nlmsg_free(msg);
71 return -ENOBUFS;
72}
73EXPORT_SYMBOL(ieee802154_nl_assoc_indic);
74
75int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr,
76 u8 status)
77{
78 struct sk_buff *msg;
79
80 pr_debug("%s\n", __func__);
81
82 msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF);
83 if (!msg)
84 return -ENOBUFS;
85
David S. Millerbe51da02012-04-01 20:45:25 -040086 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
87 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
88 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
89 dev->dev_addr) ||
90 nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
91 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
92 goto nla_put_failure;
Johannes Berg2a94fe42013-11-19 15:19:39 +010093 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +040094
95nla_put_failure:
96 nlmsg_free(msg);
97 return -ENOBUFS;
98}
99EXPORT_SYMBOL(ieee802154_nl_assoc_confirm);
100
101int ieee802154_nl_disassoc_indic(struct net_device *dev,
102 struct ieee802154_addr *addr, u8 reason)
103{
104 struct sk_buff *msg;
105
106 pr_debug("%s\n", __func__);
107
108 msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC);
109 if (!msg)
110 return -ENOBUFS;
111
David S. Millerbe51da02012-04-01 20:45:25 -0400112 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
113 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
114 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
115 dev->dev_addr))
116 goto nla_put_failure;
117 if (addr->addr_type == IEEE802154_ADDR_LONG) {
118 if (nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN,
119 addr->hwaddr))
120 goto nla_put_failure;
121 } else {
122 if (nla_put_u16(msg, IEEE802154_ATTR_SRC_SHORT_ADDR,
123 addr->short_addr))
124 goto nla_put_failure;
125 }
126 if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason))
127 goto nla_put_failure;
Johannes Berg2a94fe42013-11-19 15:19:39 +0100128 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400129
130nla_put_failure:
131 nlmsg_free(msg);
132 return -ENOBUFS;
133}
134EXPORT_SYMBOL(ieee802154_nl_disassoc_indic);
135
136int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)
137{
138 struct sk_buff *msg;
139
140 pr_debug("%s\n", __func__);
141
142 msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF);
143 if (!msg)
144 return -ENOBUFS;
145
David S. Millerbe51da02012-04-01 20:45:25 -0400146 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
147 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
148 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
149 dev->dev_addr) ||
150 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
151 goto nla_put_failure;
Johannes Berg2a94fe42013-11-19 15:19:39 +0100152 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400153
154nla_put_failure:
155 nlmsg_free(msg);
156 return -ENOBUFS;
157}
158EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm);
159
160int ieee802154_nl_beacon_indic(struct net_device *dev,
161 u16 panid, u16 coord_addr)
162{
163 struct sk_buff *msg;
164
165 pr_debug("%s\n", __func__);
166
167 msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC);
168 if (!msg)
169 return -ENOBUFS;
170
David S. Millerbe51da02012-04-01 20:45:25 -0400171 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
172 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
173 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
174 dev->dev_addr) ||
175 nla_put_u16(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, coord_addr) ||
176 nla_put_u16(msg, IEEE802154_ATTR_COORD_PAN_ID, panid))
177 goto nla_put_failure;
Johannes Berg2a94fe42013-11-19 15:19:39 +0100178 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400179
180nla_put_failure:
181 nlmsg_free(msg);
182 return -ENOBUFS;
183}
184EXPORT_SYMBOL(ieee802154_nl_beacon_indic);
185
186int ieee802154_nl_scan_confirm(struct net_device *dev,
187 u8 status, u8 scan_type, u32 unscanned, u8 page,
188 u8 *edl/* , struct list_head *pan_desc_list */)
189{
190 struct sk_buff *msg;
191
192 pr_debug("%s\n", __func__);
193
194 msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF);
195 if (!msg)
196 return -ENOBUFS;
197
David S. Millerbe51da02012-04-01 20:45:25 -0400198 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
199 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
200 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
201 dev->dev_addr) ||
202 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status) ||
203 nla_put_u8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type) ||
204 nla_put_u32(msg, IEEE802154_ATTR_CHANNELS, unscanned) ||
205 nla_put_u8(msg, IEEE802154_ATTR_PAGE, page) ||
206 (edl &&
207 nla_put(msg, IEEE802154_ATTR_ED_LIST, 27, edl)))
208 goto nla_put_failure;
Johannes Berg2a94fe42013-11-19 15:19:39 +0100209 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400210
211nla_put_failure:
212 nlmsg_free(msg);
213 return -ENOBUFS;
214}
215EXPORT_SYMBOL(ieee802154_nl_scan_confirm);
216
217int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
218{
219 struct sk_buff *msg;
220
221 pr_debug("%s\n", __func__);
222
223 msg = ieee802154_nl_create(0, IEEE802154_START_CONF);
224 if (!msg)
225 return -ENOBUFS;
226
David S. Millerbe51da02012-04-01 20:45:25 -0400227 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
228 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
229 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
230 dev->dev_addr) ||
231 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
232 goto nla_put_failure;
Johannes Berg2a94fe42013-11-19 15:19:39 +0100233 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400234
235nla_put_failure:
236 nlmsg_free(msg);
237 return -ENOBUFS;
238}
239EXPORT_SYMBOL(ieee802154_nl_start_confirm);
240
Eric W. Biederman15e47302012-09-07 20:12:54 +0000241static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400242 u32 seq, int flags, struct net_device *dev)
243{
244 void *hdr;
Dmitry Eremin-Solenikov0a868b22009-10-29 16:28:52 +0300245 struct wpan_phy *phy;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400246
247 pr_debug("%s\n", __func__);
248
249 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
250 IEEE802154_LIST_IFACE);
251 if (!hdr)
252 goto out;
253
Dmitry Eremin-Solenikov0a868b22009-10-29 16:28:52 +0300254 phy = ieee802154_mlme_ops(dev)->get_phy(dev);
255 BUG_ON(!phy);
256
David S. Millerbe51da02012-04-01 20:45:25 -0400257 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
258 nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
259 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
260 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
261 dev->dev_addr) ||
262 nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR,
263 ieee802154_mlme_ops(dev)->get_short_addr(dev)) ||
264 nla_put_u16(msg, IEEE802154_ATTR_PAN_ID,
265 ieee802154_mlme_ops(dev)->get_pan_id(dev)))
266 goto nla_put_failure;
Dmitry Eremin-Solenikov0a868b22009-10-29 16:28:52 +0300267 wpan_phy_put(phy);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400268 return genlmsg_end(msg, hdr);
269
270nla_put_failure:
Dmitry Eremin-Solenikov0a868b22009-10-29 16:28:52 +0300271 wpan_phy_put(phy);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400272 genlmsg_cancel(msg, hdr);
273out:
274 return -EMSGSIZE;
275}
276
277/* Requests from userspace */
278static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)
279{
280 struct net_device *dev;
281
282 if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
283 char name[IFNAMSIZ + 1];
284 nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME],
285 sizeof(name));
286 dev = dev_get_by_name(&init_net, name);
287 } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX])
288 dev = dev_get_by_index(&init_net,
289 nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX]));
290 else
291 return NULL;
292
293 if (!dev)
294 return NULL;
295
296 if (dev->type != ARPHRD_IEEE802154) {
297 dev_put(dev);
298 return NULL;
299 }
300
301 return dev;
302}
303
Johannes Berg1c582d92013-11-14 17:14:41 +0100304int ieee802154_associate_req(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400305{
306 struct net_device *dev;
307 struct ieee802154_addr addr;
308 u8 page;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000309 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400310
311 if (!info->attrs[IEEE802154_ATTR_CHANNEL] ||
312 !info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
313 (!info->attrs[IEEE802154_ATTR_COORD_HW_ADDR] &&
314 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]) ||
315 !info->attrs[IEEE802154_ATTR_CAPABILITY])
316 return -EINVAL;
317
318 dev = ieee802154_nl_get_dev(info);
319 if (!dev)
320 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000321 if (!ieee802154_mlme_ops(dev)->assoc_req)
322 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400323
324 if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) {
325 addr.addr_type = IEEE802154_ADDR_LONG;
326 nla_memcpy(addr.hwaddr,
327 info->attrs[IEEE802154_ATTR_COORD_HW_ADDR],
328 IEEE802154_ADDR_LEN);
329 } else {
330 addr.addr_type = IEEE802154_ADDR_SHORT;
331 addr.short_addr = nla_get_u16(
332 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
333 }
334 addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
335
336 if (info->attrs[IEEE802154_ATTR_PAGE])
337 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
338 else
339 page = 0;
340
341 ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr,
342 nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]),
343 page,
344 nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY]));
345
Werner Almesberger56aa0912013-04-04 06:32:35 +0000346out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400347 dev_put(dev);
348 return ret;
349}
350
Johannes Berg1c582d92013-11-14 17:14:41 +0100351int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400352{
353 struct net_device *dev;
354 struct ieee802154_addr addr;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000355 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400356
357 if (!info->attrs[IEEE802154_ATTR_STATUS] ||
358 !info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] ||
359 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR])
360 return -EINVAL;
361
362 dev = ieee802154_nl_get_dev(info);
363 if (!dev)
364 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000365 if (!ieee802154_mlme_ops(dev)->assoc_resp)
366 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400367
368 addr.addr_type = IEEE802154_ADDR_LONG;
369 nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
370 IEEE802154_ADDR_LEN);
371 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
372
373
374 ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr,
375 nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]),
376 nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS]));
377
Werner Almesberger56aa0912013-04-04 06:32:35 +0000378out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400379 dev_put(dev);
380 return ret;
381}
382
Johannes Berg1c582d92013-11-14 17:14:41 +0100383int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400384{
385 struct net_device *dev;
386 struct ieee802154_addr addr;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000387 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400388
389 if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] &&
390 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) ||
391 !info->attrs[IEEE802154_ATTR_REASON])
392 return -EINVAL;
393
394 dev = ieee802154_nl_get_dev(info);
395 if (!dev)
396 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000397 if (!ieee802154_mlme_ops(dev)->disassoc_req)
398 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400399
400 if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) {
401 addr.addr_type = IEEE802154_ADDR_LONG;
402 nla_memcpy(addr.hwaddr,
403 info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
404 IEEE802154_ADDR_LEN);
405 } else {
406 addr.addr_type = IEEE802154_ADDR_SHORT;
407 addr.short_addr = nla_get_u16(
408 info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]);
409 }
410 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
411
412 ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr,
413 nla_get_u8(info->attrs[IEEE802154_ATTR_REASON]));
414
Werner Almesberger56aa0912013-04-04 06:32:35 +0000415out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400416 dev_put(dev);
417 return ret;
418}
419
420/*
421 * PANid, channel, beacon_order = 15, superframe_order = 15,
422 * PAN_coordinator, battery_life_extension = 0,
423 * coord_realignment = 0, security_enable = 0
424*/
Johannes Berg1c582d92013-11-14 17:14:41 +0100425int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400426{
427 struct net_device *dev;
428 struct ieee802154_addr addr;
429
430 u8 channel, bcn_ord, sf_ord;
431 u8 page;
432 int pan_coord, blx, coord_realign;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000433 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400434
435 if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
436 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] ||
437 !info->attrs[IEEE802154_ATTR_CHANNEL] ||
438 !info->attrs[IEEE802154_ATTR_BCN_ORD] ||
439 !info->attrs[IEEE802154_ATTR_SF_ORD] ||
440 !info->attrs[IEEE802154_ATTR_PAN_COORD] ||
441 !info->attrs[IEEE802154_ATTR_BAT_EXT] ||
442 !info->attrs[IEEE802154_ATTR_COORD_REALIGN]
443 )
444 return -EINVAL;
445
446 dev = ieee802154_nl_get_dev(info);
447 if (!dev)
448 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000449 if (!ieee802154_mlme_ops(dev)->start_req)
450 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400451
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
Werner Almesberger56aa0912013-04-04 06:32:35 +0000479out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400480 dev_put(dev);
481 return ret;
482}
483
Johannes Berg1c582d92013-11-14 17:14:41 +0100484int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400485{
486 struct net_device *dev;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000487 int ret = -EOPNOTSUPP;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400488 u8 type;
489 u32 channels;
490 u8 duration;
491 u8 page;
492
493 if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] ||
494 !info->attrs[IEEE802154_ATTR_CHANNELS] ||
495 !info->attrs[IEEE802154_ATTR_DURATION])
496 return -EINVAL;
497
498 dev = ieee802154_nl_get_dev(info);
499 if (!dev)
500 return -ENODEV;
Werner Almesberger56aa0912013-04-04 06:32:35 +0000501 if (!ieee802154_mlme_ops(dev)->scan_req)
502 goto out;
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400503
504 type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]);
505 channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]);
506 duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]);
507
508 if (info->attrs[IEEE802154_ATTR_PAGE])
509 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
510 else
511 page = 0;
512
513
514 ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page,
515 duration);
516
Werner Almesberger56aa0912013-04-04 06:32:35 +0000517out:
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400518 dev_put(dev);
519 return ret;
520}
521
Johannes Berg1c582d92013-11-14 17:14:41 +0100522int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400523{
524 /* Request for interface name, index, type, IEEE address,
525 PAN Id, short address */
526 struct sk_buff *msg;
527 struct net_device *dev = NULL;
528 int rc = -ENOBUFS;
529
530 pr_debug("%s\n", __func__);
531
532 dev = ieee802154_nl_get_dev(info);
533 if (!dev)
534 return -ENODEV;
535
Thomas Graf58050fc2012-06-28 03:57:45 +0000536 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400537 if (!msg)
538 goto out_dev;
539
Eric W. Biederman15e47302012-09-07 20:12:54 +0000540 rc = ieee802154_nl_fill_iface(msg, info->snd_portid, info->snd_seq,
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400541 0, dev);
542 if (rc < 0)
543 goto out_free;
544
545 dev_put(dev);
546
547 return genlmsg_reply(msg, info);
548out_free:
549 nlmsg_free(msg);
550out_dev:
551 dev_put(dev);
552 return rc;
553
554}
555
Johannes Berg1c582d92013-11-14 17:14:41 +0100556int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb)
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400557{
558 struct net *net = sock_net(skb->sk);
559 struct net_device *dev;
560 int idx;
561 int s_idx = cb->args[0];
562
563 pr_debug("%s\n", __func__);
564
565 idx = 0;
566 for_each_netdev(net, dev) {
567 if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
568 goto cont;
569
Eric W. Biederman15e47302012-09-07 20:12:54 +0000570 if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid,
Dmitry Eremin-Solenikov78fe7382009-09-14 18:17:36 +0400571 cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0)
572 break;
573cont:
574 idx++;
575 }
576 cb->args[0] = idx;
577
578 return skb->len;
579}