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 = -EOPNOTSUPP; 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 if (!ieee802154_mlme_ops(dev)->assoc_req) 331 goto out; 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 out: 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 = -EOPNOTSUPP; 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 if (!ieee802154_mlme_ops(dev)->assoc_resp) 376 goto out; 377 378 addr.addr_type = IEEE802154_ADDR_LONG; 379 nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], 380 IEEE802154_ADDR_LEN); 381 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); 382 383 384 ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr, 385 nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]), 386 nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS])); 387 388 out: 389 dev_put(dev); 390 return ret; 391 } 392 393 static int ieee802154_disassociate_req(struct sk_buff *skb, 394 struct genl_info *info) 395 { 396 struct net_device *dev; 397 struct ieee802154_addr addr; 398 int ret = -EOPNOTSUPP; 399 400 if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] && 401 !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) || 402 !info->attrs[IEEE802154_ATTR_REASON]) 403 return -EINVAL; 404 405 dev = ieee802154_nl_get_dev(info); 406 if (!dev) 407 return -ENODEV; 408 if (!ieee802154_mlme_ops(dev)->disassoc_req) 409 goto out; 410 411 if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) { 412 addr.addr_type = IEEE802154_ADDR_LONG; 413 nla_memcpy(addr.hwaddr, 414 info->attrs[IEEE802154_ATTR_DEST_HW_ADDR], 415 IEEE802154_ADDR_LEN); 416 } else { 417 addr.addr_type = IEEE802154_ADDR_SHORT; 418 addr.short_addr = nla_get_u16( 419 info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]); 420 } 421 addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev); 422 423 ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr, 424 nla_get_u8(info->attrs[IEEE802154_ATTR_REASON])); 425 426 out: 427 dev_put(dev); 428 return ret; 429 } 430 431 /* 432 * PANid, channel, beacon_order = 15, superframe_order = 15, 433 * PAN_coordinator, battery_life_extension = 0, 434 * coord_realignment = 0, security_enable = 0 435 */ 436 static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) 437 { 438 struct net_device *dev; 439 struct ieee802154_addr addr; 440 441 u8 channel, bcn_ord, sf_ord; 442 u8 page; 443 int pan_coord, blx, coord_realign; 444 int ret = -EOPNOTSUPP; 445 446 if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] || 447 !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] || 448 !info->attrs[IEEE802154_ATTR_CHANNEL] || 449 !info->attrs[IEEE802154_ATTR_BCN_ORD] || 450 !info->attrs[IEEE802154_ATTR_SF_ORD] || 451 !info->attrs[IEEE802154_ATTR_PAN_COORD] || 452 !info->attrs[IEEE802154_ATTR_BAT_EXT] || 453 !info->attrs[IEEE802154_ATTR_COORD_REALIGN] 454 ) 455 return -EINVAL; 456 457 dev = ieee802154_nl_get_dev(info); 458 if (!dev) 459 return -ENODEV; 460 if (!ieee802154_mlme_ops(dev)->start_req) 461 goto out; 462 463 addr.addr_type = IEEE802154_ADDR_SHORT; 464 addr.short_addr = nla_get_u16( 465 info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]); 466 addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); 467 468 channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]); 469 bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]); 470 sf_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_SF_ORD]); 471 pan_coord = nla_get_u8(info->attrs[IEEE802154_ATTR_PAN_COORD]); 472 blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]); 473 coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]); 474 475 if (info->attrs[IEEE802154_ATTR_PAGE]) 476 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); 477 else 478 page = 0; 479 480 481 if (addr.short_addr == IEEE802154_ADDR_BROADCAST) { 482 ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS); 483 dev_put(dev); 484 return -EINVAL; 485 } 486 487 ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page, 488 bcn_ord, sf_ord, pan_coord, blx, coord_realign); 489 490 out: 491 dev_put(dev); 492 return ret; 493 } 494 495 static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info) 496 { 497 struct net_device *dev; 498 int ret = -EOPNOTSUPP; 499 u8 type; 500 u32 channels; 501 u8 duration; 502 u8 page; 503 504 if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] || 505 !info->attrs[IEEE802154_ATTR_CHANNELS] || 506 !info->attrs[IEEE802154_ATTR_DURATION]) 507 return -EINVAL; 508 509 dev = ieee802154_nl_get_dev(info); 510 if (!dev) 511 return -ENODEV; 512 if (!ieee802154_mlme_ops(dev)->scan_req) 513 goto out; 514 515 type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]); 516 channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]); 517 duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]); 518 519 if (info->attrs[IEEE802154_ATTR_PAGE]) 520 page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); 521 else 522 page = 0; 523 524 525 ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page, 526 duration); 527 528 out: 529 dev_put(dev); 530 return ret; 531 } 532 533 static int ieee802154_list_iface(struct sk_buff *skb, 534 struct genl_info *info) 535 { 536 /* Request for interface name, index, type, IEEE address, 537 PAN Id, short address */ 538 struct sk_buff *msg; 539 struct net_device *dev = NULL; 540 int rc = -ENOBUFS; 541 542 pr_debug("%s\n", __func__); 543 544 dev = ieee802154_nl_get_dev(info); 545 if (!dev) 546 return -ENODEV; 547 548 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 549 if (!msg) 550 goto out_dev; 551 552 rc = ieee802154_nl_fill_iface(msg, info->snd_portid, info->snd_seq, 553 0, dev); 554 if (rc < 0) 555 goto out_free; 556 557 dev_put(dev); 558 559 return genlmsg_reply(msg, info); 560 out_free: 561 nlmsg_free(msg); 562 out_dev: 563 dev_put(dev); 564 return rc; 565 566 } 567 568 static int ieee802154_dump_iface(struct sk_buff *skb, 569 struct netlink_callback *cb) 570 { 571 struct net *net = sock_net(skb->sk); 572 struct net_device *dev; 573 int idx; 574 int s_idx = cb->args[0]; 575 576 pr_debug("%s\n", __func__); 577 578 idx = 0; 579 for_each_netdev(net, dev) { 580 if (idx < s_idx || (dev->type != ARPHRD_IEEE802154)) 581 goto cont; 582 583 if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid, 584 cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0) 585 break; 586 cont: 587 idx++; 588 } 589 cb->args[0] = idx; 590 591 return skb->len; 592 } 593 594 static struct genl_ops ieee802154_coordinator_ops[] = { 595 IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req), 596 IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp), 597 IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req), 598 IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req), 599 IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req), 600 IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface, 601 ieee802154_dump_iface), 602 }; 603 604 /* 605 * No need to unregister as family unregistration will do it. 606 */ 607 int nl802154_mac_register(void) 608 { 609 int i; 610 int rc; 611 612 rc = genl_register_mc_group(&nl802154_family, 613 &ieee802154_coord_mcgrp); 614 if (rc) 615 return rc; 616 617 rc = genl_register_mc_group(&nl802154_family, 618 &ieee802154_beacon_mcgrp); 619 if (rc) 620 return rc; 621 622 for (i = 0; i < ARRAY_SIZE(ieee802154_coordinator_ops); i++) { 623 rc = genl_register_ops(&nl802154_family, 624 &ieee802154_coordinator_ops[i]); 625 if (rc) 626 return rc; 627 } 628 629 return 0; 630 } 631