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