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.h> 35 #include <net/ieee802154_netdev.h> 36 #include <net/wpan-phy.h> 37 38 #include "ieee802154.h" 39 40 static struct genl_multicast_group ieee802154_coord_mcgrp = { 41 .name = IEEE802154_MCAST_COORD_NAME, 42 }; 43 44 static struct genl_multicast_group ieee802154_beacon_mcgrp = { 45 .name = IEEE802154_MCAST_BEACON_NAME, 46 }; 47 48 int ieee802154_nl_assoc_indic(struct net_device *dev, 49 struct ieee802154_addr *addr, u8 cap) 50 { 51 struct sk_buff *msg; 52 53 pr_debug("%s\n", __func__); 54 55 if (addr->addr_type != IEEE802154_ADDR_LONG) { 56 pr_err("%s: received non-long source address!\n", __func__); 57 return -EINVAL; 58 } 59 60 msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC); 61 if (!msg) 62 return -ENOBUFS; 63 64 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 65 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 66 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 67 dev->dev_addr); 68 69 NLA_PUT(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, 70 addr->hwaddr); 71 72 NLA_PUT_U8(msg, IEEE802154_ATTR_CAPABILITY, cap); 73 74 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 75 76 nla_put_failure: 77 nlmsg_free(msg); 78 return -ENOBUFS; 79 } 80 EXPORT_SYMBOL(ieee802154_nl_assoc_indic); 81 82 int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr, 83 u8 status) 84 { 85 struct sk_buff *msg; 86 87 pr_debug("%s\n", __func__); 88 89 msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF); 90 if (!msg) 91 return -ENOBUFS; 92 93 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 94 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 95 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 96 dev->dev_addr); 97 98 NLA_PUT_U16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr); 99 NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); 100 101 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 102 103 nla_put_failure: 104 nlmsg_free(msg); 105 return -ENOBUFS; 106 } 107 EXPORT_SYMBOL(ieee802154_nl_assoc_confirm); 108 109 int 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 120 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 125 if (addr->addr_type == IEEE802154_ADDR_LONG) 126 NLA_PUT(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, 127 addr->hwaddr); 128 else 129 NLA_PUT_U16(msg, IEEE802154_ATTR_SRC_SHORT_ADDR, 130 addr->short_addr); 131 132 NLA_PUT_U8(msg, IEEE802154_ATTR_REASON, reason); 133 134 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 135 136 nla_put_failure: 137 nlmsg_free(msg); 138 return -ENOBUFS; 139 } 140 EXPORT_SYMBOL(ieee802154_nl_disassoc_indic); 141 142 int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status) 143 { 144 struct sk_buff *msg; 145 146 pr_debug("%s\n", __func__); 147 148 msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF); 149 if (!msg) 150 return -ENOBUFS; 151 152 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 153 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 154 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 155 dev->dev_addr); 156 157 NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); 158 159 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 160 161 nla_put_failure: 162 nlmsg_free(msg); 163 return -ENOBUFS; 164 } 165 EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm); 166 167 int ieee802154_nl_beacon_indic(struct net_device *dev, 168 u16 panid, u16 coord_addr) 169 { 170 struct sk_buff *msg; 171 172 pr_debug("%s\n", __func__); 173 174 msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC); 175 if (!msg) 176 return -ENOBUFS; 177 178 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 179 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 180 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 181 dev->dev_addr); 182 NLA_PUT_U16(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, coord_addr); 183 NLA_PUT_U16(msg, IEEE802154_ATTR_COORD_PAN_ID, panid); 184 185 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 186 187 nla_put_failure: 188 nlmsg_free(msg); 189 return -ENOBUFS; 190 } 191 EXPORT_SYMBOL(ieee802154_nl_beacon_indic); 192 193 int ieee802154_nl_scan_confirm(struct net_device *dev, 194 u8 status, u8 scan_type, u32 unscanned, u8 page, 195 u8 *edl/* , struct list_head *pan_desc_list */) 196 { 197 struct sk_buff *msg; 198 199 pr_debug("%s\n", __func__); 200 201 msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF); 202 if (!msg) 203 return -ENOBUFS; 204 205 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 206 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 207 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 208 dev->dev_addr); 209 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 215 if (edl) 216 NLA_PUT(msg, IEEE802154_ATTR_ED_LIST, 27, edl); 217 218 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 219 220 nla_put_failure: 221 nlmsg_free(msg); 222 return -ENOBUFS; 223 } 224 EXPORT_SYMBOL(ieee802154_nl_scan_confirm); 225 226 int ieee802154_nl_start_confirm(struct net_device *dev, u8 status) 227 { 228 struct sk_buff *msg; 229 230 pr_debug("%s\n", __func__); 231 232 msg = ieee802154_nl_create(0, IEEE802154_START_CONF); 233 if (!msg) 234 return -ENOBUFS; 235 236 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 237 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 238 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 239 dev->dev_addr); 240 241 NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); 242 243 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 244 245 nla_put_failure: 246 nlmsg_free(msg); 247 return -ENOBUFS; 248 } 249 EXPORT_SYMBOL(ieee802154_nl_start_confirm); 250 251 static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 pid, 252 u32 seq, int flags, struct net_device *dev) 253 { 254 void *hdr; 255 struct wpan_phy *phy; 256 257 pr_debug("%s\n", __func__); 258 259 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags, 260 IEEE802154_LIST_IFACE); 261 if (!hdr) 262 goto out; 263 264 phy = ieee802154_mlme_ops(dev)->get_phy(dev); 265 BUG_ON(!phy); 266 267 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 268 NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)); 269 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 270 271 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 272 dev->dev_addr); 273 NLA_PUT_U16(msg, IEEE802154_ATTR_SHORT_ADDR, 274 ieee802154_mlme_ops(dev)->get_short_addr(dev)); 275 NLA_PUT_U16(msg, IEEE802154_ATTR_PAN_ID, 276 ieee802154_mlme_ops(dev)->get_pan_id(dev)); 277 wpan_phy_put(phy); 278 return genlmsg_end(msg, hdr); 279 280 nla_put_failure: 281 wpan_phy_put(phy); 282 genlmsg_cancel(msg, hdr); 283 out: 284 return -EMSGSIZE; 285 } 286 287 /* Requests from userspace */ 288 static struct net_device *ieee802154_nl_get_dev(struct genl_info *info) 289 { 290 struct net_device *dev; 291 292 if (info->attrs[IEEE802154_ATTR_DEV_NAME]) { 293 char name[IFNAMSIZ + 1]; 294 nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME], 295 sizeof(name)); 296 dev = dev_get_by_name(&init_net, name); 297 } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX]) 298 dev = dev_get_by_index(&init_net, 299 nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX])); 300 else 301 return NULL; 302 303 if (!dev) 304 return NULL; 305 306 if (dev->type != ARPHRD_IEEE802154) { 307 dev_put(dev); 308 return NULL; 309 } 310 311 return dev; 312 } 313 314 static int ieee802154_associate_req(struct sk_buff *skb, 315 struct genl_info *info) 316 { 317 struct net_device *dev; 318 struct ieee802154_addr addr; 319 u8 page; 320 int ret = -EINVAL; 321 322 if (!info->attrs[IEEE802154_ATTR_CHANNEL] || 323 !info->attrs[IEEE802154_ATTR_COORD_PAN_ID] || 324 (!info->attrs[IEEE802154_ATTR_COORD_HW_ADDR] && 325 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]) || 326 !info->attrs[IEEE802154_ATTR_CAPABILITY]) 327 return -EINVAL; 328 329 dev = ieee802154_nl_get_dev(info); 330 if (!dev) 331 return -ENODEV; 332 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 355 dev_put(dev); 356 return ret; 357 } 358 359 static 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 389 static 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 */ 429 static 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 u8 page; 436 int pan_coord, blx, coord_realign; 437 int ret; 438 439 if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] || 440 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] || 441 !info->attrs[IEEE802154_ATTR_CHANNEL] || 442 !info->attrs[IEEE802154_ATTR_BCN_ORD] || 443 !info->attrs[IEEE802154_ATTR_SF_ORD] || 444 !info->attrs[IEEE802154_ATTR_PAN_COORD] || 445 !info->attrs[IEEE802154_ATTR_BAT_EXT] || 446 !info->attrs[IEEE802154_ATTR_COORD_REALIGN] 447 ) 448 return -EINVAL; 449 450 dev = ieee802154_nl_get_dev(info); 451 if (!dev) 452 return -ENODEV; 453 454 addr.addr_type = IEEE802154_ADDR_SHORT; 455 addr.short_addr = nla_get_u16( 456 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]); 457 addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); 458 459 channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]); 460 bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]); 461 sf_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_SF_ORD]); 462 pan_coord = nla_get_u8(info->attrs[IEEE802154_ATTR_PAN_COORD]); 463 blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]); 464 coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]); 465 466 if (info->attrs[IEEE802154_ATTR_PAGE]) 467 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); 468 else 469 page = 0; 470 471 472 if (addr.short_addr == IEEE802154_ADDR_BROADCAST) { 473 ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS); 474 dev_put(dev); 475 return -EINVAL; 476 } 477 478 ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page, 479 bcn_ord, sf_ord, pan_coord, blx, coord_realign); 480 481 dev_put(dev); 482 return ret; 483 } 484 485 static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info) 486 { 487 struct net_device *dev; 488 int ret; 489 u8 type; 490 u32 channels; 491 u8 duration; 492 u8 page; 493 494 if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] || 495 !info->attrs[IEEE802154_ATTR_CHANNELS] || 496 !info->attrs[IEEE802154_ATTR_DURATION]) 497 return -EINVAL; 498 499 dev = ieee802154_nl_get_dev(info); 500 if (!dev) 501 return -ENODEV; 502 503 type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]); 504 channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]); 505 duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]); 506 507 if (info->attrs[IEEE802154_ATTR_PAGE]) 508 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); 509 else 510 page = 0; 511 512 513 ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page, 514 duration); 515 516 dev_put(dev); 517 return ret; 518 } 519 520 static int ieee802154_list_iface(struct sk_buff *skb, 521 struct genl_info *info) 522 { 523 /* Request for interface name, index, type, IEEE address, 524 PAN Id, short address */ 525 struct sk_buff *msg; 526 struct net_device *dev = NULL; 527 int rc = -ENOBUFS; 528 529 pr_debug("%s\n", __func__); 530 531 dev = ieee802154_nl_get_dev(info); 532 if (!dev) 533 return -ENODEV; 534 535 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 536 if (!msg) 537 goto out_dev; 538 539 rc = ieee802154_nl_fill_iface(msg, info->snd_pid, info->snd_seq, 540 0, dev); 541 if (rc < 0) 542 goto out_free; 543 544 dev_put(dev); 545 546 return genlmsg_reply(msg, info); 547 out_free: 548 nlmsg_free(msg); 549 out_dev: 550 dev_put(dev); 551 return rc; 552 553 } 554 555 static int ieee802154_dump_iface(struct sk_buff *skb, 556 struct netlink_callback *cb) 557 { 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 570 if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).pid, 571 cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0) 572 break; 573 cont: 574 idx++; 575 } 576 cb->args[0] = idx; 577 578 return skb->len; 579 } 580 581 static struct genl_ops ieee802154_coordinator_ops[] = { 582 IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req), 583 IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp), 584 IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req), 585 IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req), 586 IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req), 587 IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface, 588 ieee802154_dump_iface), 589 }; 590 591 /* 592 * No need to unregister as family unregistration will do it. 593 */ 594 int nl802154_mac_register(void) 595 { 596 int i; 597 int rc; 598 599 rc = genl_register_mc_group(&nl802154_family, 600 &ieee802154_coord_mcgrp); 601 if (rc) 602 return rc; 603 604 rc = genl_register_mc_group(&nl802154_family, 605 &ieee802154_beacon_mcgrp); 606 if (rc) 607 return rc; 608 609 for (i = 0; i < ARRAY_SIZE(ieee802154_coordinator_ops); i++) { 610 rc = genl_register_ops(&nl802154_family, 611 &ieee802154_coordinator_ops[i]); 612 if (rc) 613 return rc; 614 } 615 616 return 0; 617 } 618