Merge branch 'clk-next-unregister' into clk-next
[linux-2.6/btrfs-unstable.git] / net / ieee802154 / nl-mac.c
blobba5c1e002f37b2630c53a0284736f5d177bded76
1 /*
2 * Netlink inteface for IEEE 802.15.4 stack
4 * Copyright 2007, 2008 Siemens AG
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.
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.
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.
19 * Written by:
20 * Sergey Lapin <slapin@ossfans.org>
21 * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
22 * Maxim Osipov <maxim.osipov@siemens.com>
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>
40 #include "ieee802154.h"
42 int ieee802154_nl_assoc_indic(struct net_device *dev,
43 struct ieee802154_addr *addr, u8 cap)
45 struct sk_buff *msg;
47 pr_debug("%s\n", __func__);
49 if (addr->addr_type != IEEE802154_ADDR_LONG) {
50 pr_err("%s: received non-long source address!\n", __func__);
51 return -EINVAL;
54 msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC);
55 if (!msg)
56 return -ENOBUFS;
58 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;
67 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
69 nla_put_failure:
70 nlmsg_free(msg);
71 return -ENOBUFS;
73 EXPORT_SYMBOL(ieee802154_nl_assoc_indic);
75 int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr,
76 u8 status)
78 struct sk_buff *msg;
80 pr_debug("%s\n", __func__);
82 msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF);
83 if (!msg)
84 return -ENOBUFS;
86 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;
93 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
95 nla_put_failure:
96 nlmsg_free(msg);
97 return -ENOBUFS;
99 EXPORT_SYMBOL(ieee802154_nl_assoc_confirm);
101 int ieee802154_nl_disassoc_indic(struct net_device *dev,
102 struct ieee802154_addr *addr, u8 reason)
104 struct sk_buff *msg;
106 pr_debug("%s\n", __func__);
108 msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC);
109 if (!msg)
110 return -ENOBUFS;
112 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;
126 if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason))
127 goto nla_put_failure;
128 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
130 nla_put_failure:
131 nlmsg_free(msg);
132 return -ENOBUFS;
134 EXPORT_SYMBOL(ieee802154_nl_disassoc_indic);
136 int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)
138 struct sk_buff *msg;
140 pr_debug("%s\n", __func__);
142 msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF);
143 if (!msg)
144 return -ENOBUFS;
146 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;
152 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
154 nla_put_failure:
155 nlmsg_free(msg);
156 return -ENOBUFS;
158 EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm);
160 int ieee802154_nl_beacon_indic(struct net_device *dev,
161 u16 panid, u16 coord_addr)
163 struct sk_buff *msg;
165 pr_debug("%s\n", __func__);
167 msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC);
168 if (!msg)
169 return -ENOBUFS;
171 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;
178 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
180 nla_put_failure:
181 nlmsg_free(msg);
182 return -ENOBUFS;
184 EXPORT_SYMBOL(ieee802154_nl_beacon_indic);
186 int 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 */)
190 struct sk_buff *msg;
192 pr_debug("%s\n", __func__);
194 msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF);
195 if (!msg)
196 return -ENOBUFS;
198 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;
209 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
211 nla_put_failure:
212 nlmsg_free(msg);
213 return -ENOBUFS;
215 EXPORT_SYMBOL(ieee802154_nl_scan_confirm);
217 int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
219 struct sk_buff *msg;
221 pr_debug("%s\n", __func__);
223 msg = ieee802154_nl_create(0, IEEE802154_START_CONF);
224 if (!msg)
225 return -ENOBUFS;
227 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;
233 return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
235 nla_put_failure:
236 nlmsg_free(msg);
237 return -ENOBUFS;
239 EXPORT_SYMBOL(ieee802154_nl_start_confirm);
241 static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
242 u32 seq, int flags, struct net_device *dev)
244 void *hdr;
245 struct wpan_phy *phy;
247 pr_debug("%s\n", __func__);
249 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
250 IEEE802154_LIST_IFACE);
251 if (!hdr)
252 goto out;
254 phy = ieee802154_mlme_ops(dev)->get_phy(dev);
255 BUG_ON(!phy);
257 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;
267 wpan_phy_put(phy);
268 return genlmsg_end(msg, hdr);
270 nla_put_failure:
271 wpan_phy_put(phy);
272 genlmsg_cancel(msg, hdr);
273 out:
274 return -EMSGSIZE;
277 /* Requests from userspace */
278 static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)
280 struct net_device *dev;
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;
293 if (!dev)
294 return NULL;
296 if (dev->type != ARPHRD_IEEE802154) {
297 dev_put(dev);
298 return NULL;
301 return dev;
304 int ieee802154_associate_req(struct sk_buff *skb, struct genl_info *info)
306 struct net_device *dev;
307 struct ieee802154_addr addr;
308 u8 page;
309 int ret = -EOPNOTSUPP;
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;
318 dev = ieee802154_nl_get_dev(info);
319 if (!dev)
320 return -ENODEV;
321 if (!ieee802154_mlme_ops(dev)->assoc_req)
322 goto out;
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]);
334 addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
336 if (info->attrs[IEEE802154_ATTR_PAGE])
337 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
338 else
339 page = 0;
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]));
346 out:
347 dev_put(dev);
348 return ret;
351 int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info)
353 struct net_device *dev;
354 struct ieee802154_addr addr;
355 int ret = -EOPNOTSUPP;
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;
362 dev = ieee802154_nl_get_dev(info);
363 if (!dev)
364 return -ENODEV;
365 if (!ieee802154_mlme_ops(dev)->assoc_resp)
366 goto out;
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);
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]));
378 out:
379 dev_put(dev);
380 return ret;
383 int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info)
385 struct net_device *dev;
386 struct ieee802154_addr addr;
387 int ret = -EOPNOTSUPP;
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;
394 dev = ieee802154_nl_get_dev(info);
395 if (!dev)
396 return -ENODEV;
397 if (!ieee802154_mlme_ops(dev)->disassoc_req)
398 goto out;
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]);
410 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
412 ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr,
413 nla_get_u8(info->attrs[IEEE802154_ATTR_REASON]));
415 out:
416 dev_put(dev);
417 return ret;
421 * PANid, channel, beacon_order = 15, superframe_order = 15,
422 * PAN_coordinator, battery_life_extension = 0,
423 * coord_realignment = 0, security_enable = 0
425 int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
427 struct net_device *dev;
428 struct ieee802154_addr addr;
430 u8 channel, bcn_ord, sf_ord;
431 u8 page;
432 int pan_coord, blx, coord_realign;
433 int ret = -EOPNOTSUPP;
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]
444 return -EINVAL;
446 dev = ieee802154_nl_get_dev(info);
447 if (!dev)
448 return -ENODEV;
449 if (!ieee802154_mlme_ops(dev)->start_req)
450 goto out;
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]);
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]);
464 if (info->attrs[IEEE802154_ATTR_PAGE])
465 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
466 else
467 page = 0;
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;
476 ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
477 bcn_ord, sf_ord, pan_coord, blx, coord_realign);
479 out:
480 dev_put(dev);
481 return ret;
484 int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
486 struct net_device *dev;
487 int ret = -EOPNOTSUPP;
488 u8 type;
489 u32 channels;
490 u8 duration;
491 u8 page;
493 if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] ||
494 !info->attrs[IEEE802154_ATTR_CHANNELS] ||
495 !info->attrs[IEEE802154_ATTR_DURATION])
496 return -EINVAL;
498 dev = ieee802154_nl_get_dev(info);
499 if (!dev)
500 return -ENODEV;
501 if (!ieee802154_mlme_ops(dev)->scan_req)
502 goto out;
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]);
508 if (info->attrs[IEEE802154_ATTR_PAGE])
509 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
510 else
511 page = 0;
514 ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page,
515 duration);
517 out:
518 dev_put(dev);
519 return ret;
522 int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info)
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;
530 pr_debug("%s\n", __func__);
532 dev = ieee802154_nl_get_dev(info);
533 if (!dev)
534 return -ENODEV;
536 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
537 if (!msg)
538 goto out_dev;
540 rc = ieee802154_nl_fill_iface(msg, info->snd_portid, info->snd_seq,
541 0, dev);
542 if (rc < 0)
543 goto out_free;
545 dev_put(dev);
547 return genlmsg_reply(msg, info);
548 out_free:
549 nlmsg_free(msg);
550 out_dev:
551 dev_put(dev);
552 return rc;
556 int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb)
558 struct net *net = sock_net(skb->sk);
559 struct net_device *dev;
560 int idx;
561 int s_idx = cb->args[0];
563 pr_debug("%s\n", __func__);
565 idx = 0;
566 for_each_netdev(net, dev) {
567 if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
568 goto cont;
570 if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid,
571 cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0)
572 break;
573 cont:
574 idx++;
576 cb->args[0] = idx;
578 return skb->len;