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/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 <net/af_ieee802154.h> 34 #include <net/nl802154.h> 35 #include <net/ieee802154.h> 36 #include <net/ieee802154_netdev.h> 37 #include <net/wpan-phy.h> 38 39 #include "ieee802154.h" 40 41 static struct genl_multicast_group ieee802154_coord_mcgrp = { 42 .name = IEEE802154_MCAST_COORD_NAME, 43 }; 44 45 static struct genl_multicast_group ieee802154_beacon_mcgrp = { 46 .name = IEEE802154_MCAST_BEACON_NAME, 47 }; 48 49 int ieee802154_nl_assoc_indic(struct net_device *dev, 50 struct ieee802154_addr *addr, u8 cap) 51 { 52 struct sk_buff *msg; 53 54 pr_debug("%s\n", __func__); 55 56 if (addr->addr_type != IEEE802154_ADDR_LONG) { 57 pr_err("%s: received non-long source address!\n", __func__); 58 return -EINVAL; 59 } 60 61 msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC); 62 if (!msg) 63 return -ENOBUFS; 64 65 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 66 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 67 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 68 dev->dev_addr); 69 70 NLA_PUT(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, 71 addr->hwaddr); 72 73 NLA_PUT_U8(msg, IEEE802154_ATTR_CAPABILITY, cap); 74 75 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 76 77 nla_put_failure: 78 nlmsg_free(msg); 79 return -ENOBUFS; 80 } 81 EXPORT_SYMBOL(ieee802154_nl_assoc_indic); 82 83 int 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 94 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 99 NLA_PUT_U16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr); 100 NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); 101 102 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 103 104 nla_put_failure: 105 nlmsg_free(msg); 106 return -ENOBUFS; 107 } 108 EXPORT_SYMBOL(ieee802154_nl_assoc_confirm); 109 110 int ieee802154_nl_disassoc_indic(struct net_device *dev, 111 struct ieee802154_addr *addr, u8 reason) 112 { 113 struct sk_buff *msg; 114 115 pr_debug("%s\n", __func__); 116 117 msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC); 118 if (!msg) 119 return -ENOBUFS; 120 121 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 122 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 123 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 124 dev->dev_addr); 125 126 if (addr->addr_type == IEEE802154_ADDR_LONG) 127 NLA_PUT(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, 128 addr->hwaddr); 129 else 130 NLA_PUT_U16(msg, IEEE802154_ATTR_SRC_SHORT_ADDR, 131 addr->short_addr); 132 133 NLA_PUT_U8(msg, IEEE802154_ATTR_REASON, reason); 134 135 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 136 137 nla_put_failure: 138 nlmsg_free(msg); 139 return -ENOBUFS; 140 } 141 EXPORT_SYMBOL(ieee802154_nl_disassoc_indic); 142 143 int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status) 144 { 145 struct sk_buff *msg; 146 147 pr_debug("%s\n", __func__); 148 149 msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF); 150 if (!msg) 151 return -ENOBUFS; 152 153 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 154 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 155 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 156 dev->dev_addr); 157 158 NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); 159 160 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 161 162 nla_put_failure: 163 nlmsg_free(msg); 164 return -ENOBUFS; 165 } 166 EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm); 167 168 int 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 179 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 186 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 187 188 nla_put_failure: 189 nlmsg_free(msg); 190 return -ENOBUFS; 191 } 192 EXPORT_SYMBOL(ieee802154_nl_beacon_indic); 193 194 int 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 206 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 211 NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); 212 NLA_PUT_U8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type); 213 NLA_PUT_U32(msg, IEEE802154_ATTR_CHANNELS, unscanned); 214 NLA_PUT_U8(msg, IEEE802154_ATTR_PAGE, page); 215 216 if (edl) 217 NLA_PUT(msg, IEEE802154_ATTR_ED_LIST, 27, edl); 218 219 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 220 221 nla_put_failure: 222 nlmsg_free(msg); 223 return -ENOBUFS; 224 } 225 EXPORT_SYMBOL(ieee802154_nl_scan_confirm); 226 227 int ieee802154_nl_start_confirm(struct net_device *dev, u8 status) 228 { 229 struct sk_buff *msg; 230 231 pr_debug("%s\n", __func__); 232 233 msg = ieee802154_nl_create(0, IEEE802154_START_CONF); 234 if (!msg) 235 return -ENOBUFS; 236 237 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 238 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 239 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 240 dev->dev_addr); 241 242 NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); 243 244 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 245 246 nla_put_failure: 247 nlmsg_free(msg); 248 return -ENOBUFS; 249 } 250 EXPORT_SYMBOL(ieee802154_nl_start_confirm); 251 252 static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 pid, 253 u32 seq, int flags, struct net_device *dev) 254 { 255 void *hdr; 256 struct wpan_phy *phy; 257 258 pr_debug("%s\n", __func__); 259 260 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags, 261 IEEE802154_LIST_IFACE); 262 if (!hdr) 263 goto out; 264 265 phy = ieee802154_mlme_ops(dev)->get_phy(dev); 266 BUG_ON(!phy); 267 268 NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); 269 NLA_PUT_STRING(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)); 270 NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); 271 272 NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 273 dev->dev_addr); 274 NLA_PUT_U16(msg, IEEE802154_ATTR_SHORT_ADDR, 275 ieee802154_mlme_ops(dev)->get_short_addr(dev)); 276 NLA_PUT_U16(msg, IEEE802154_ATTR_PAN_ID, 277 ieee802154_mlme_ops(dev)->get_pan_id(dev)); 278 wpan_phy_put(phy); 279 return genlmsg_end(msg, hdr); 280 281 nla_put_failure: 282 wpan_phy_put(phy); 283 genlmsg_cancel(msg, hdr); 284 out: 285 return -EMSGSIZE; 286 } 287 288 /* Requests from userspace */ 289 static struct net_device *ieee802154_nl_get_dev(struct genl_info *info) 290 { 291 struct net_device *dev; 292 293 if (info->attrs[IEEE802154_ATTR_DEV_NAME]) { 294 char name[IFNAMSIZ + 1]; 295 nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME], 296 sizeof(name)); 297 dev = dev_get_by_name(&init_net, name); 298 } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX]) 299 dev = dev_get_by_index(&init_net, 300 nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX])); 301 else 302 return NULL; 303 304 if (!dev) 305 return NULL; 306 307 if (dev->type != ARPHRD_IEEE802154) { 308 dev_put(dev); 309 return NULL; 310 } 311 312 return dev; 313 } 314 315 static int ieee802154_associate_req(struct sk_buff *skb, 316 struct genl_info *info) 317 { 318 struct net_device *dev; 319 struct ieee802154_addr addr; 320 u8 page; 321 int ret = -EINVAL; 322 323 if (!info->attrs[IEEE802154_ATTR_CHANNEL] || 324 !info->attrs[IEEE802154_ATTR_COORD_PAN_ID] || 325 (!info->attrs[IEEE802154_ATTR_COORD_HW_ADDR] && 326 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]) || 327 !info->attrs[IEEE802154_ATTR_CAPABILITY]) 328 return -EINVAL; 329 330 dev = ieee802154_nl_get_dev(info); 331 if (!dev) 332 return -ENODEV; 333 334 if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) { 335 addr.addr_type = IEEE802154_ADDR_LONG; 336 nla_memcpy(addr.hwaddr, 337 info->attrs[IEEE802154_ATTR_COORD_HW_ADDR], 338 IEEE802154_ADDR_LEN); 339 } else { 340 addr.addr_type = IEEE802154_ADDR_SHORT; 341 addr.short_addr = nla_get_u16( 342 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]); 343 } 344 addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); 345 346 if (info->attrs[IEEE802154_ATTR_PAGE]) 347 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); 348 else 349 page = 0; 350 351 ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr, 352 nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]), 353 page, 354 nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY])); 355 356 dev_put(dev); 357 return ret; 358 } 359 360 static int ieee802154_associate_resp(struct sk_buff *skb, 361 struct genl_info *info) 362 { 363 struct net_device *dev; 364 struct ieee802154_addr addr; 365 int ret = -EINVAL; 366 367 if (!info->attrs[IEEE802154_ATTR_STATUS] || 368 !info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] || 369 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) 370 return -EINVAL; 371 372 dev = ieee802154_nl_get_dev(info); 373 if (!dev) 374 return -ENODEV; 375 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 386 dev_put(dev); 387 return ret; 388 } 389 390 static int ieee802154_disassociate_req(struct sk_buff *skb, 391 struct genl_info *info) 392 { 393 struct net_device *dev; 394 struct ieee802154_addr addr; 395 int ret = -EINVAL; 396 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; 405 406 if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) { 407 addr.addr_type = IEEE802154_ADDR_LONG; 408 nla_memcpy(addr.hwaddr, 409 info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], 410 IEEE802154_ADDR_LEN); 411 } else { 412 addr.addr_type = IEEE802154_ADDR_SHORT; 413 addr.short_addr = nla_get_u16( 414 info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]); 415 } 416 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); 417 418 ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr, 419 nla_get_u8(info->attrs[IEEE802154_ATTR_REASON])); 420 421 dev_put(dev); 422 return ret; 423 } 424 425 /* 426 * PANid, channel, beacon_order = 15, superframe_order = 15, 427 * PAN_coordinator, battery_life_extension = 0, 428 * coord_realignment = 0, security_enable = 0 429 */ 430 static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) 431 { 432 struct net_device *dev; 433 struct ieee802154_addr addr; 434 435 u8 channel, bcn_ord, sf_ord; 436 u8 page; 437 int pan_coord, blx, coord_realign; 438 int ret; 439 440 if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] || 441 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] || 442 !info->attrs[IEEE802154_ATTR_CHANNEL] || 443 !info->attrs[IEEE802154_ATTR_BCN_ORD] || 444 !info->attrs[IEEE802154_ATTR_SF_ORD] || 445 !info->attrs[IEEE802154_ATTR_PAN_COORD] || 446 !info->attrs[IEEE802154_ATTR_BAT_EXT] || 447 !info->attrs[IEEE802154_ATTR_COORD_REALIGN] 448 ) 449 return -EINVAL; 450 451 dev = ieee802154_nl_get_dev(info); 452 if (!dev) 453 return -ENODEV; 454 455 addr.addr_type = IEEE802154_ADDR_SHORT; 456 addr.short_addr = nla_get_u16( 457 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]); 458 addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); 459 460 channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]); 461 bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]); 462 sf_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_SF_ORD]); 463 pan_coord = nla_get_u8(info->attrs[IEEE802154_ATTR_PAN_COORD]); 464 blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]); 465 coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]); 466 467 if (info->attrs[IEEE802154_ATTR_PAGE]) 468 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); 469 else 470 page = 0; 471 472 473 if (addr.short_addr == IEEE802154_ADDR_BROADCAST) { 474 ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS); 475 dev_put(dev); 476 return -EINVAL; 477 } 478 479 ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page, 480 bcn_ord, sf_ord, pan_coord, blx, coord_realign); 481 482 dev_put(dev); 483 return ret; 484 } 485 486 static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info) 487 { 488 struct net_device *dev; 489 int ret; 490 u8 type; 491 u32 channels; 492 u8 duration; 493 u8 page; 494 495 if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] || 496 !info->attrs[IEEE802154_ATTR_CHANNELS] || 497 !info->attrs[IEEE802154_ATTR_DURATION]) 498 return -EINVAL; 499 500 dev = ieee802154_nl_get_dev(info); 501 if (!dev) 502 return -ENODEV; 503 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 517 dev_put(dev); 518 return ret; 519 } 520 521 static int ieee802154_list_iface(struct sk_buff *skb, 522 struct genl_info *info) 523 { 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 536 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 537 if (!msg) 538 goto out_dev; 539 540 rc = ieee802154_nl_fill_iface(msg, info->snd_pid, info->snd_seq, 541 0, dev); 542 if (rc < 0) 543 goto out_free; 544 545 dev_put(dev); 546 547 return genlmsg_reply(msg, info); 548 out_free: 549 nlmsg_free(msg); 550 out_dev: 551 dev_put(dev); 552 return rc; 553 554 } 555 556 static int ieee802154_dump_iface(struct sk_buff *skb, 557 struct netlink_callback *cb) 558 { 559 struct net *net = sock_net(skb->sk); 560 struct net_device *dev; 561 int idx; 562 int s_idx = cb->args[0]; 563 564 pr_debug("%s\n", __func__); 565 566 idx = 0; 567 for_each_netdev(net, dev) { 568 if (idx < s_idx || (dev->type != ARPHRD_IEEE802154)) 569 goto cont; 570 571 if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).pid, 572 cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0) 573 break; 574 cont: 575 idx++; 576 } 577 cb->args[0] = idx; 578 579 return skb->len; 580 } 581 582 static struct genl_ops ieee802154_coordinator_ops[] = { 583 IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req), 584 IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp), 585 IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req), 586 IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req), 587 IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req), 588 IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface, 589 ieee802154_dump_iface), 590 }; 591 592 /* 593 * No need to unregister as family unregistration will do it. 594 */ 595 int nl802154_mac_register(void) 596 { 597 int i; 598 int rc; 599 600 rc = genl_register_mc_group(&nl802154_family, 601 &ieee802154_coord_mcgrp); 602 if (rc) 603 return rc; 604 605 rc = genl_register_mc_group(&nl802154_family, 606 &ieee802154_beacon_mcgrp); 607 if (rc) 608 return rc; 609 610 for (i = 0; i < ARRAY_SIZE(ieee802154_coordinator_ops); i++) { 611 rc = genl_register_ops(&nl802154_family, 612 &ieee802154_coordinator_ops[i]); 613 if (rc) 614 return rc; 615 } 616 617 return 0; 618 } 619