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 if (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 nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, 71 addr->hwaddr) || 72 nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap)) 73 goto nla_put_failure; 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 if (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 nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) || 99 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status)) 100 goto nla_put_failure; 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 if (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 goto nla_put_failure; 125 if (addr->addr_type == IEEE802154_ADDR_LONG) { 126 if (nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN, 127 addr->hwaddr)) 128 goto nla_put_failure; 129 } else { 130 if (nla_put_u16(msg, IEEE802154_ATTR_SRC_SHORT_ADDR, 131 addr->short_addr)) 132 goto nla_put_failure; 133 } 134 if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason)) 135 goto nla_put_failure; 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 if (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 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status)) 159 goto nla_put_failure; 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 if (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 goto nla_put_failure; 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 if (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 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 (edl && 215 nla_put(msg, IEEE802154_ATTR_ED_LIST, 27, edl))) 216 goto nla_put_failure; 217 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 218 219 nla_put_failure: 220 nlmsg_free(msg); 221 return -ENOBUFS; 222 } 223 EXPORT_SYMBOL(ieee802154_nl_scan_confirm); 224 225 int ieee802154_nl_start_confirm(struct net_device *dev, u8 status) 226 { 227 struct sk_buff *msg; 228 229 pr_debug("%s\n", __func__); 230 231 msg = ieee802154_nl_create(0, IEEE802154_START_CONF); 232 if (!msg) 233 return -ENOBUFS; 234 235 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || 236 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || 237 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 238 dev->dev_addr) || 239 nla_put_u8(msg, IEEE802154_ATTR_STATUS, status)) 240 goto nla_put_failure; 241 return ieee802154_nl_mcast(msg, ieee802154_coord_mcgrp.id); 242 243 nla_put_failure: 244 nlmsg_free(msg); 245 return -ENOBUFS; 246 } 247 EXPORT_SYMBOL(ieee802154_nl_start_confirm); 248 249 static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid, 250 u32 seq, int flags, struct net_device *dev) 251 { 252 void *hdr; 253 struct wpan_phy *phy; 254 255 pr_debug("%s\n", __func__); 256 257 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags, 258 IEEE802154_LIST_IFACE); 259 if (!hdr) 260 goto out; 261 262 phy = ieee802154_mlme_ops(dev)->get_phy(dev); 263 BUG_ON(!phy); 264 265 if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || 266 nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) || 267 nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || 268 nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, 269 dev->dev_addr) || 270 nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, 271 ieee802154_mlme_ops(dev)->get_short_addr(dev)) || 272 nla_put_u16(msg, IEEE802154_ATTR_PAN_ID, 273 ieee802154_mlme_ops(dev)->get_pan_id(dev))) 274 goto nla_put_failure; 275 wpan_phy_put(phy); 276 return genlmsg_end(msg, hdr); 277 278 nla_put_failure: 279 wpan_phy_put(phy); 280 genlmsg_cancel(msg, hdr); 281 out: 282 return -EMSGSIZE; 283 } 284 285 /* Requests from userspace */ 286 static struct net_device *ieee802154_nl_get_dev(struct genl_info *info) 287 { 288 struct net_device *dev; 289 290 if (info->attrs[IEEE802154_ATTR_DEV_NAME]) { 291 char name[IFNAMSIZ + 1]; 292 nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME], 293 sizeof(name)); 294 dev = dev_get_by_name(&init_net, name); 295 } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX]) 296 dev = dev_get_by_index(&init_net, 297 nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX])); 298 else 299 return NULL; 300 301 if (!dev) 302 return NULL; 303 304 if (dev->type != ARPHRD_IEEE802154) { 305 dev_put(dev); 306 return NULL; 307 } 308 309 return dev; 310 } 311 312 static int ieee802154_associate_req(struct sk_buff *skb, 313 struct genl_info *info) 314 { 315 struct net_device *dev; 316 struct ieee802154_addr addr; 317 u8 page; 318 int ret = -EINVAL; 319 320 if (!info->attrs[IEEE802154_ATTR_CHANNEL] || 321 !info->attrs[IEEE802154_ATTR_COORD_PAN_ID] || 322 (!info->attrs[IEEE802154_ATTR_COORD_HW_ADDR] && 323 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]) || 324 !info->attrs[IEEE802154_ATTR_CAPABILITY]) 325 return -EINVAL; 326 327 dev = ieee802154_nl_get_dev(info); 328 if (!dev) 329 return -ENODEV; 330 331 if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) { 332 addr.addr_type = IEEE802154_ADDR_LONG; 333 nla_memcpy(addr.hwaddr, 334 info->attrs[IEEE802154_ATTR_COORD_HW_ADDR], 335 IEEE802154_ADDR_LEN); 336 } else { 337 addr.addr_type = IEEE802154_ADDR_SHORT; 338 addr.short_addr = nla_get_u16( 339 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]); 340 } 341 addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); 342 343 if (info->attrs[IEEE802154_ATTR_PAGE]) 344 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); 345 else 346 page = 0; 347 348 ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr, 349 nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]), 350 page, 351 nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY])); 352 353 dev_put(dev); 354 return ret; 355 } 356 357 static int ieee802154_associate_resp(struct sk_buff *skb, 358 struct genl_info *info) 359 { 360 struct net_device *dev; 361 struct ieee802154_addr addr; 362 int ret = -EINVAL; 363 364 if (!info->attrs[IEEE802154_ATTR_STATUS] || 365 !info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] || 366 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) 367 return -EINVAL; 368 369 dev = ieee802154_nl_get_dev(info); 370 if (!dev) 371 return -ENODEV; 372 373 addr.addr_type = IEEE802154_ADDR_LONG; 374 nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], 375 IEEE802154_ADDR_LEN); 376 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); 377 378 379 ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr, 380 nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]), 381 nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS])); 382 383 dev_put(dev); 384 return ret; 385 } 386 387 static int ieee802154_disassociate_req(struct sk_buff *skb, 388 struct genl_info *info) 389 { 390 struct net_device *dev; 391 struct ieee802154_addr addr; 392 int ret = -EINVAL; 393 394 if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] && 395 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) || 396 !info->attrs[IEEE802154_ATTR_REASON]) 397 return -EINVAL; 398 399 dev = ieee802154_nl_get_dev(info); 400 if (!dev) 401 return -ENODEV; 402 403 if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) { 404 addr.addr_type = IEEE802154_ADDR_LONG; 405 nla_memcpy(addr.hwaddr, 406 info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], 407 IEEE802154_ADDR_LEN); 408 } else { 409 addr.addr_type = IEEE802154_ADDR_SHORT; 410 addr.short_addr = nla_get_u16( 411 info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]); 412 } 413 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); 414 415 ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr, 416 nla_get_u8(info->attrs[IEEE802154_ATTR_REASON])); 417 418 dev_put(dev); 419 return ret; 420 } 421 422 /* 423 * PANid, channel, beacon_order = 15, superframe_order = 15, 424 * PAN_coordinator, battery_life_extension = 0, 425 * coord_realignment = 0, security_enable = 0 426 */ 427 static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) 428 { 429 struct net_device *dev; 430 struct ieee802154_addr addr; 431 432 u8 channel, bcn_ord, sf_ord; 433 u8 page; 434 int pan_coord, blx, coord_realign; 435 int ret; 436 437 if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] || 438 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] || 439 !info->attrs[IEEE802154_ATTR_CHANNEL] || 440 !info->attrs[IEEE802154_ATTR_BCN_ORD] || 441 !info->attrs[IEEE802154_ATTR_SF_ORD] || 442 !info->attrs[IEEE802154_ATTR_PAN_COORD] || 443 !info->attrs[IEEE802154_ATTR_BAT_EXT] || 444 !info->attrs[IEEE802154_ATTR_COORD_REALIGN] 445 ) 446 return -EINVAL; 447 448 dev = ieee802154_nl_get_dev(info); 449 if (!dev) 450 return -ENODEV; 451 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]); 456 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]); 463 464 if (info->attrs[IEEE802154_ATTR_PAGE]) 465 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); 466 else 467 page = 0; 468 469 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; 474 } 475 476 ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page, 477 bcn_ord, sf_ord, pan_coord, blx, coord_realign); 478 479 dev_put(dev); 480 return ret; 481 } 482 483 static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info) 484 { 485 struct net_device *dev; 486 int ret; 487 u8 type; 488 u32 channels; 489 u8 duration; 490 u8 page; 491 492 if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] || 493 !info->attrs[IEEE802154_ATTR_CHANNELS] || 494 !info->attrs[IEEE802154_ATTR_DURATION]) 495 return -EINVAL; 496 497 dev = ieee802154_nl_get_dev(info); 498 if (!dev) 499 return -ENODEV; 500 501 type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]); 502 channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]); 503 duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]); 504 505 if (info->attrs[IEEE802154_ATTR_PAGE]) 506 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); 507 else 508 page = 0; 509 510 511 ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page, 512 duration); 513 514 dev_put(dev); 515 return ret; 516 } 517 518 static int ieee802154_list_iface(struct sk_buff *skb, 519 struct genl_info *info) 520 { 521 /* Request for interface name, index, type, IEEE address, 522 PAN Id, short address */ 523 struct sk_buff *msg; 524 struct net_device *dev = NULL; 525 int rc = -ENOBUFS; 526 527 pr_debug("%s\n", __func__); 528 529 dev = ieee802154_nl_get_dev(info); 530 if (!dev) 531 return -ENODEV; 532 533 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 534 if (!msg) 535 goto out_dev; 536 537 rc = ieee802154_nl_fill_iface(msg, info->snd_portid, info->snd_seq, 538 0, dev); 539 if (rc < 0) 540 goto out_free; 541 542 dev_put(dev); 543 544 return genlmsg_reply(msg, info); 545 out_free: 546 nlmsg_free(msg); 547 out_dev: 548 dev_put(dev); 549 return rc; 550 551 } 552 553 static int ieee802154_dump_iface(struct sk_buff *skb, 554 struct netlink_callback *cb) 555 { 556 struct net *net = sock_net(skb->sk); 557 struct net_device *dev; 558 int idx; 559 int s_idx = cb->args[0]; 560 561 pr_debug("%s\n", __func__); 562 563 idx = 0; 564 for_each_netdev(net, dev) { 565 if (idx < s_idx || (dev->type != ARPHRD_IEEE802154)) 566 goto cont; 567 568 if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid, 569 cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0) 570 break; 571 cont: 572 idx++; 573 } 574 cb->args[0] = idx; 575 576 return skb->len; 577 } 578 579 static struct genl_ops ieee802154_coordinator_ops[] = { 580 IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req), 581 IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp), 582 IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req), 583 IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req), 584 IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req), 585 IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface, 586 ieee802154_dump_iface), 587 }; 588 589 /* 590 * No need to unregister as family unregistration will do it. 591 */ 592 int nl802154_mac_register(void) 593 { 594 int i; 595 int rc; 596 597 rc = genl_register_mc_group(&nl802154_family, 598 &ieee802154_coord_mcgrp); 599 if (rc) 600 return rc; 601 602 rc = genl_register_mc_group(&nl802154_family, 603 &ieee802154_beacon_mcgrp); 604 if (rc) 605 return rc; 606 607 for (i = 0; i < ARRAY_SIZE(ieee802154_coordinator_ops); i++) { 608 rc = genl_register_ops(&nl802154_family, 609 &ieee802154_coordinator_ops[i]); 610 if (rc) 611 return rc; 612 } 613 614 return 0; 615 } 616