netlink.c revision 8e753dd0a82bd266256c20a20b98dfa48f98d21e
1/* 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 25#include <linux/kernel.h> 26#include <linux/if_arp.h> 27#include <linux/netdevice.h> 28#include <net/netlink.h> 29#include <net/genetlink.h> 30#include <net/sock.h> 31#include <linux/nl802154.h> 32#include <net/af_ieee802154.h> 33#include <net/nl802154.h> 34#include <net/ieee802154_netdev.h> 35 36static unsigned int ieee802154_seq_num; 37 38static struct genl_family ieee802154_coordinator_family = { 39 .id = GENL_ID_GENERATE, 40 .hdrsize = 0, 41 .name = IEEE802154_NL_NAME, 42 .version = 1, 43 .maxattr = IEEE802154_ATTR_MAX, 44}; 45 46static struct genl_multicast_group ieee802154_coord_mcgrp = { 47 .name = IEEE802154_MCAST_COORD_NAME, 48}; 49 50static struct genl_multicast_group ieee802154_beacon_mcgrp = { 51 .name = IEEE802154_MCAST_BEACON_NAME, 52}; 53 54/* Requests to userspace */ 55static struct sk_buff *ieee802154_nl_create(int flags, u8 req) 56{ 57 void *hdr; 58 struct sk_buff *msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); 59 60 if (!msg) 61 return NULL; 62 63 hdr = genlmsg_put(msg, 0, ieee802154_seq_num++, 64 &ieee802154_coordinator_family, flags, req); 65 if (!hdr) { 66 nlmsg_free(msg); 67 return NULL; 68 } 69 70 return msg; 71} 72 73static int ieee802154_nl_finish(struct sk_buff *msg) 74{ 75 /* XXX: nlh is right at the start of msg */ 76 void *hdr = genlmsg_data(NLMSG_DATA(msg->data)); 77 78 if (genlmsg_end(msg, hdr) < 0) 79 goto out; 80 81 return genlmsg_multicast(msg, 0, ieee802154_coord_mcgrp.id, 82 GFP_ATOMIC); 83out: 84 nlmsg_free(msg); 85 return -ENOBUFS; 86} 87 88int ieee802154_nl_assoc_indic(struct net_device *dev, 89 struct ieee802154_addr *addr, u8 cap) 90{ 91 struct sk_buff *msg; 92 93 pr_debug("%s\n", __func__); 94 95 if (addr->addr_type != IEEE802154_ADDR_LONG) { 96 pr_err("%s: received non-long source address!\n", __func__); 97 return -EINVAL; 98 } 99 100 msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC); 101 if (!msg) 102 return -ENOBUFS; 103 104 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 105 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 106 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 107 dev->dev_addr); 108 109 NLA_PUT(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, 110 addr->hwaddr); 111 112 NLA_PUT_U8(msg, IEEE802154_ATTR_CAPABILITY, cap); 113 114 return ieee802154_nl_finish(msg); 115 116nla_put_failure: 117 nlmsg_free(msg); 118 return -ENOBUFS; 119} 120EXPORT_SYMBOL(ieee802154_nl_assoc_indic); 121 122int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr, 123 u8 status) 124{ 125 struct sk_buff *msg; 126 127 pr_debug("%s\n", __func__); 128 129 msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF); 130 if (!msg) 131 return -ENOBUFS; 132 133 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 134 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 135 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 136 dev->dev_addr); 137 138 NLA_PUT_U16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr); 139 NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); 140 141 return ieee802154_nl_finish(msg); 142 143nla_put_failure: 144 nlmsg_free(msg); 145 return -ENOBUFS; 146} 147EXPORT_SYMBOL(ieee802154_nl_assoc_confirm); 148 149int ieee802154_nl_disassoc_indic(struct net_device *dev, 150 struct ieee802154_addr *addr, u8 reason) 151{ 152 struct sk_buff *msg; 153 154 pr_debug("%s\n", __func__); 155 156 msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC); 157 if (!msg) 158 return -ENOBUFS; 159 160 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 161 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 162 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 163 dev->dev_addr); 164 165 if (addr->addr_type == IEEE802154_ADDR_LONG) 166 NLA_PUT(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, 167 addr->hwaddr); 168 else 169 NLA_PUT_U16(msg, IEEE802154_ATTR_SRC_SHORT_ADDR, 170 addr->short_addr); 171 172 NLA_PUT_U8(msg, IEEE802154_ATTR_REASON, reason); 173 174 return ieee802154_nl_finish(msg); 175 176nla_put_failure: 177 nlmsg_free(msg); 178 return -ENOBUFS; 179} 180EXPORT_SYMBOL(ieee802154_nl_disassoc_indic); 181 182int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status) 183{ 184 struct sk_buff *msg; 185 186 pr_debug("%s\n", __func__); 187 188 msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF); 189 if (!msg) 190 return -ENOBUFS; 191 192 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 193 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 194 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 195 dev->dev_addr); 196 197 NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); 198 199 return ieee802154_nl_finish(msg); 200 201nla_put_failure: 202 nlmsg_free(msg); 203 return -ENOBUFS; 204} 205EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm); 206 207int ieee802154_nl_beacon_indic(struct net_device *dev, 208 u16 panid, u16 coord_addr) 209{ 210 struct sk_buff *msg; 211 212 pr_debug("%s\n", __func__); 213 214 msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC); 215 if (!msg) 216 return -ENOBUFS; 217 218 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 219 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 220 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 221 dev->dev_addr); 222 NLA_PUT_U16(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, coord_addr); 223 NLA_PUT_U16(msg, IEEE802154_ATTR_COORD_PAN_ID, panid); 224 225 return ieee802154_nl_finish(msg); 226 227nla_put_failure: 228 nlmsg_free(msg); 229 return -ENOBUFS; 230} 231EXPORT_SYMBOL(ieee802154_nl_beacon_indic); 232 233int ieee802154_nl_scan_confirm(struct net_device *dev, 234 u8 status, u8 scan_type, u32 unscanned, 235 u8 *edl/* , struct list_head *pan_desc_list */) 236{ 237 struct sk_buff *msg; 238 239 pr_debug("%s\n", __func__); 240 241 msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF); 242 if (!msg) 243 return -ENOBUFS; 244 245 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 246 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 247 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 248 dev->dev_addr); 249 250 NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); 251 NLA_PUT_U8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type); 252 NLA_PUT_U32(msg, IEEE802154_ATTR_CHANNELS, unscanned); 253 254 if (edl) 255 NLA_PUT(msg, IEEE802154_ATTR_ED_LIST, 27, edl); 256 257 return ieee802154_nl_finish(msg); 258 259nla_put_failure: 260 nlmsg_free(msg); 261 return -ENOBUFS; 262} 263EXPORT_SYMBOL(ieee802154_nl_scan_confirm); 264 265static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 pid, 266 u32 seq, int flags, struct net_device *dev) 267{ 268 void *hdr; 269 270 pr_debug("%s\n", __func__); 271 272 hdr = genlmsg_put(msg, 0, seq, &ieee802154_coordinator_family, flags, 273 IEEE802154_LIST_IFACE); 274 if (!hdr) 275 goto out; 276 277 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 278 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 279 280 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 281 dev->dev_addr); 282 NLA_PUT_U16(msg, IEEE802154_ATTR_SHORT_ADDR, 283 ieee802154_mlme_ops(dev)->get_short_addr(dev)); 284 NLA_PUT_U16(msg, IEEE802154_ATTR_PAN_ID, 285 ieee802154_mlme_ops(dev)->get_pan_id(dev)); 286 return genlmsg_end(msg, hdr); 287 288nla_put_failure: 289 genlmsg_cancel(msg, hdr); 290out: 291 return -EMSGSIZE; 292} 293 294/* Requests from userspace */ 295static struct net_device *ieee802154_nl_get_dev(struct genl_info *info) 296{ 297 struct net_device *dev; 298 299 if (info->attrs[IEEE802154_ATTR_DEV_NAME]) { 300 char name[IFNAMSIZ + 1]; 301 nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME], 302 sizeof(name)); 303 dev = dev_get_by_name(&init_net, name); 304 } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX]) 305 dev = dev_get_by_index(&init_net, 306 nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX])); 307 else 308 return NULL; 309 310 if (!dev) 311 return NULL; 312 313 if (dev->type != ARPHRD_IEEE802154) { 314 dev_put(dev); 315 return NULL; 316 } 317 318 return dev; 319} 320 321static int ieee802154_associate_req(struct sk_buff *skb, 322 struct genl_info *info) 323{ 324 struct net_device *dev; 325 struct ieee802154_addr addr; 326 int ret = -EINVAL; 327 328 if (!info->attrs[IEEE802154_ATTR_CHANNEL] || 329 !info->attrs[IEEE802154_ATTR_COORD_PAN_ID] || 330 (!info->attrs[IEEE802154_ATTR_COORD_HW_ADDR] && 331 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]) || 332 !info->attrs[IEEE802154_ATTR_CAPABILITY]) 333 return -EINVAL; 334 335 dev = ieee802154_nl_get_dev(info); 336 if (!dev) 337 return -ENODEV; 338 339 if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) { 340 addr.addr_type = IEEE802154_ADDR_LONG; 341 nla_memcpy(addr.hwaddr, 342 info->attrs[IEEE802154_ATTR_COORD_HW_ADDR], 343 IEEE802154_ADDR_LEN); 344 } else { 345 addr.addr_type = IEEE802154_ADDR_SHORT; 346 addr.short_addr = nla_get_u16( 347 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]); 348 } 349 addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); 350 351 ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr, 352 nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]), 353 nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY])); 354 355 dev_put(dev); 356 return ret; 357} 358 359static int ieee802154_associate_resp(struct sk_buff *skb, 360 struct genl_info *info) 361{ 362 struct net_device *dev; 363 struct ieee802154_addr addr; 364 int ret = -EINVAL; 365 366 if (!info->attrs[IEEE802154_ATTR_STATUS] || 367 !info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] || 368 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) 369 return -EINVAL; 370 371 dev = ieee802154_nl_get_dev(info); 372 if (!dev) 373 return -ENODEV; 374 375 addr.addr_type = IEEE802154_ADDR_LONG; 376 nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], 377 IEEE802154_ADDR_LEN); 378 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); 379 380 381 ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr, 382 nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]), 383 nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS])); 384 385 dev_put(dev); 386 return ret; 387} 388 389static int ieee802154_disassociate_req(struct sk_buff *skb, 390 struct genl_info *info) 391{ 392 struct net_device *dev; 393 struct ieee802154_addr addr; 394 int ret = -EINVAL; 395 396 if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] && 397 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) || 398 !info->attrs[IEEE802154_ATTR_REASON]) 399 return -EINVAL; 400 401 dev = ieee802154_nl_get_dev(info); 402 if (!dev) 403 return -ENODEV; 404 405 if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) { 406 addr.addr_type = IEEE802154_ADDR_LONG; 407 nla_memcpy(addr.hwaddr, 408 info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], 409 IEEE802154_ADDR_LEN); 410 } else { 411 addr.addr_type = IEEE802154_ADDR_SHORT; 412 addr.short_addr = nla_get_u16( 413 info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]); 414 } 415 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); 416 417 ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr, 418 nla_get_u8(info->attrs[IEEE802154_ATTR_REASON])); 419 420 dev_put(dev); 421 return ret; 422} 423 424/* 425 * PANid, channel, beacon_order = 15, superframe_order = 15, 426 * PAN_coordinator, battery_life_extension = 0, 427 * coord_realignment = 0, security_enable = 0 428*/ 429static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) 430{ 431 struct net_device *dev; 432 struct ieee802154_addr addr; 433 434 u8 channel, bcn_ord, sf_ord; 435 int pan_coord, blx, coord_realign; 436 int ret; 437 438 if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] || 439 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] || 440 !info->attrs[IEEE802154_ATTR_CHANNEL] || 441 !info->attrs[IEEE802154_ATTR_BCN_ORD] || 442 !info->attrs[IEEE802154_ATTR_SF_ORD] || 443 !info->attrs[IEEE802154_ATTR_PAN_COORD] || 444 !info->attrs[IEEE802154_ATTR_BAT_EXT] || 445 !info->attrs[IEEE802154_ATTR_COORD_REALIGN] 446 ) 447 return -EINVAL; 448 449 dev = ieee802154_nl_get_dev(info); 450 if (!dev) 451 return -ENODEV; 452 453 addr.addr_type = IEEE802154_ADDR_SHORT; 454 addr.short_addr = nla_get_u16( 455 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]); 456 addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); 457 458 channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]); 459 bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]); 460 sf_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_SF_ORD]); 461 pan_coord = nla_get_u8(info->attrs[IEEE802154_ATTR_PAN_COORD]); 462 blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]); 463 coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]); 464 465 ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, 466 bcn_ord, sf_ord, pan_coord, blx, coord_realign); 467 468 dev_put(dev); 469 return ret; 470} 471 472static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info) 473{ 474 struct net_device *dev; 475 int ret; 476 u8 type; 477 u32 channels; 478 u8 duration; 479 480 if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] || 481 !info->attrs[IEEE802154_ATTR_CHANNELS] || 482 !info->attrs[IEEE802154_ATTR_DURATION]) 483 return -EINVAL; 484 485 dev = ieee802154_nl_get_dev(info); 486 if (!dev) 487 return -ENODEV; 488 489 type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]); 490 channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]); 491 duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]); 492 493 ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, 494 duration); 495 496 dev_put(dev); 497 return ret; 498} 499 500static int ieee802154_list_iface(struct sk_buff *skb, 501 struct genl_info *info) 502{ 503 /* Request for interface name, index, type, IEEE address, 504 PAN Id, short address */ 505 struct sk_buff *msg; 506 struct net_device *dev = NULL; 507 int rc = -ENOBUFS; 508 509 pr_debug("%s\n", __func__); 510 511 dev = ieee802154_nl_get_dev(info); 512 if (!dev) 513 return -ENODEV; 514 515 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 516 if (!msg) 517 goto out_dev; 518 519 rc = ieee802154_nl_fill_iface(msg, info->snd_pid, info->snd_seq, 520 0, dev); 521 if (rc < 0) 522 goto out_free; 523 524 dev_put(dev); 525 526 return genlmsg_unicast(&init_net, msg, info->snd_pid); 527out_free: 528 nlmsg_free(msg); 529out_dev: 530 dev_put(dev); 531 return rc; 532 533} 534 535static int ieee802154_dump_iface(struct sk_buff *skb, 536 struct netlink_callback *cb) 537{ 538 struct net *net = sock_net(skb->sk); 539 struct net_device *dev; 540 int idx; 541 int s_idx = cb->args[0]; 542 543 pr_debug("%s\n", __func__); 544 545 idx = 0; 546 for_each_netdev(net, dev) { 547 if (idx < s_idx || (dev->type != ARPHRD_IEEE802154)) 548 goto cont; 549 550 if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).pid, 551 cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0) 552 break; 553cont: 554 idx++; 555 } 556 cb->args[0] = idx; 557 558 return skb->len; 559} 560 561#define IEEE802154_OP(_cmd, _func) \ 562 { \ 563 .cmd = _cmd, \ 564 .policy = ieee802154_policy, \ 565 .doit = _func, \ 566 .dumpit = NULL, \ 567 .flags = GENL_ADMIN_PERM, \ 568 } 569 570#define IEEE802154_DUMP(_cmd, _func, _dump) \ 571 { \ 572 .cmd = _cmd, \ 573 .policy = ieee802154_policy, \ 574 .doit = _func, \ 575 .dumpit = _dump, \ 576 } 577 578static struct genl_ops ieee802154_coordinator_ops[] = { 579 IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req), 580 IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp), 581 IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req), 582 IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req), 583 IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req), 584 IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface, 585 ieee802154_dump_iface), 586}; 587 588static int __init ieee802154_nl_init(void) 589{ 590 int rc; 591 int i; 592 593 rc = genl_register_family(&ieee802154_coordinator_family); 594 if (rc) 595 goto fail; 596 597 rc = genl_register_mc_group(&ieee802154_coordinator_family, 598 &ieee802154_coord_mcgrp); 599 if (rc) 600 goto fail; 601 602 rc = genl_register_mc_group(&ieee802154_coordinator_family, 603 &ieee802154_beacon_mcgrp); 604 if (rc) 605 goto fail; 606 607 608 for (i = 0; i < ARRAY_SIZE(ieee802154_coordinator_ops); i++) { 609 rc = genl_register_ops(&ieee802154_coordinator_family, 610 &ieee802154_coordinator_ops[i]); 611 if (rc) 612 goto fail; 613 } 614 615 return 0; 616 617fail: 618 genl_unregister_family(&ieee802154_coordinator_family); 619 return rc; 620} 621module_init(ieee802154_nl_init); 622 623static void __exit ieee802154_nl_exit(void) 624{ 625 genl_unregister_family(&ieee802154_coordinator_family); 626} 627module_exit(ieee802154_nl_exit); 628 629MODULE_LICENSE("GPL v2"); 630MODULE_DESCRIPTION("ieee 802.15.4 configuration interface"); 631 632