179fe1a2aSAlexander Aring /* This program is free software; you can redistribute it and/or modify 279fe1a2aSAlexander Aring * it under the terms of the GNU General Public License version 2 379fe1a2aSAlexander Aring * as published by the Free Software Foundation. 479fe1a2aSAlexander Aring * 579fe1a2aSAlexander Aring * This program is distributed in the hope that it will be useful, 679fe1a2aSAlexander Aring * but WITHOUT ANY WARRANTY; without even the implied warranty of 779fe1a2aSAlexander Aring * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 879fe1a2aSAlexander Aring * GNU General Public License for more details. 979fe1a2aSAlexander Aring * 1079fe1a2aSAlexander Aring * Authors: 1179fe1a2aSAlexander Aring * Alexander Aring <aar@pengutronix.de> 1279fe1a2aSAlexander Aring * 1379fe1a2aSAlexander Aring * Based on: net/wireless/nl80211.c 1479fe1a2aSAlexander Aring */ 1579fe1a2aSAlexander Aring 1679fe1a2aSAlexander Aring #include <linux/rtnetlink.h> 1779fe1a2aSAlexander Aring 1879fe1a2aSAlexander Aring #include <net/cfg802154.h> 1979fe1a2aSAlexander Aring #include <net/genetlink.h> 2079fe1a2aSAlexander Aring #include <net/mac802154.h> 2179fe1a2aSAlexander Aring #include <net/netlink.h> 2279fe1a2aSAlexander Aring #include <net/nl802154.h> 2379fe1a2aSAlexander Aring #include <net/sock.h> 2479fe1a2aSAlexander Aring 2579fe1a2aSAlexander Aring #include "nl802154.h" 26ab0bd561SAlexander Aring #include "rdev-ops.h" 2779fe1a2aSAlexander Aring #include "core.h" 2879fe1a2aSAlexander Aring 2979fe1a2aSAlexander Aring static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, 3079fe1a2aSAlexander Aring struct genl_info *info); 3179fe1a2aSAlexander Aring 3279fe1a2aSAlexander Aring static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb, 3379fe1a2aSAlexander Aring struct genl_info *info); 3479fe1a2aSAlexander Aring 3579fe1a2aSAlexander Aring /* the netlink family */ 3679fe1a2aSAlexander Aring static struct genl_family nl802154_fam = { 3779fe1a2aSAlexander Aring .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */ 3879fe1a2aSAlexander Aring .name = NL802154_GENL_NAME, /* have users key off the name instead */ 3979fe1a2aSAlexander Aring .hdrsize = 0, /* no private header */ 4079fe1a2aSAlexander Aring .version = 1, /* no particular meaning now */ 4179fe1a2aSAlexander Aring .maxattr = NL802154_ATTR_MAX, 4279fe1a2aSAlexander Aring .netnsok = true, 4379fe1a2aSAlexander Aring .pre_doit = nl802154_pre_doit, 4479fe1a2aSAlexander Aring .post_doit = nl802154_post_doit, 4579fe1a2aSAlexander Aring }; 4679fe1a2aSAlexander Aring 4779fe1a2aSAlexander Aring /* multicast groups */ 4879fe1a2aSAlexander Aring enum nl802154_multicast_groups { 4979fe1a2aSAlexander Aring NL802154_MCGRP_CONFIG, 5079fe1a2aSAlexander Aring }; 5179fe1a2aSAlexander Aring 5279fe1a2aSAlexander Aring static const struct genl_multicast_group nl802154_mcgrps[] = { 5379fe1a2aSAlexander Aring [NL802154_MCGRP_CONFIG] = { .name = "config", }, 5479fe1a2aSAlexander Aring }; 5579fe1a2aSAlexander Aring 5679fe1a2aSAlexander Aring /* returns ERR_PTR values */ 5779fe1a2aSAlexander Aring static struct wpan_dev * 5879fe1a2aSAlexander Aring __cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs) 5979fe1a2aSAlexander Aring { 6079fe1a2aSAlexander Aring struct cfg802154_registered_device *rdev; 6179fe1a2aSAlexander Aring struct wpan_dev *result = NULL; 6279fe1a2aSAlexander Aring bool have_ifidx = attrs[NL802154_ATTR_IFINDEX]; 6379fe1a2aSAlexander Aring bool have_wpan_dev_id = attrs[NL802154_ATTR_WPAN_DEV]; 6479fe1a2aSAlexander Aring u64 wpan_dev_id; 6579fe1a2aSAlexander Aring int wpan_phy_idx = -1; 6679fe1a2aSAlexander Aring int ifidx = -1; 6779fe1a2aSAlexander Aring 6879fe1a2aSAlexander Aring ASSERT_RTNL(); 6979fe1a2aSAlexander Aring 7079fe1a2aSAlexander Aring if (!have_ifidx && !have_wpan_dev_id) 7179fe1a2aSAlexander Aring return ERR_PTR(-EINVAL); 7279fe1a2aSAlexander Aring 7379fe1a2aSAlexander Aring if (have_ifidx) 7479fe1a2aSAlexander Aring ifidx = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]); 7579fe1a2aSAlexander Aring if (have_wpan_dev_id) { 7679fe1a2aSAlexander Aring wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]); 7779fe1a2aSAlexander Aring wpan_phy_idx = wpan_dev_id >> 32; 7879fe1a2aSAlexander Aring } 7979fe1a2aSAlexander Aring 8079fe1a2aSAlexander Aring list_for_each_entry(rdev, &cfg802154_rdev_list, list) { 8179fe1a2aSAlexander Aring struct wpan_dev *wpan_dev; 8279fe1a2aSAlexander Aring 8379fe1a2aSAlexander Aring /* TODO netns compare */ 8479fe1a2aSAlexander Aring 8579fe1a2aSAlexander Aring if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx) 8679fe1a2aSAlexander Aring continue; 8779fe1a2aSAlexander Aring 8879fe1a2aSAlexander Aring list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) { 8979fe1a2aSAlexander Aring if (have_ifidx && wpan_dev->netdev && 9079fe1a2aSAlexander Aring wpan_dev->netdev->ifindex == ifidx) { 9179fe1a2aSAlexander Aring result = wpan_dev; 9279fe1a2aSAlexander Aring break; 9379fe1a2aSAlexander Aring } 9479fe1a2aSAlexander Aring if (have_wpan_dev_id && 9579fe1a2aSAlexander Aring wpan_dev->identifier == (u32)wpan_dev_id) { 9679fe1a2aSAlexander Aring result = wpan_dev; 9779fe1a2aSAlexander Aring break; 9879fe1a2aSAlexander Aring } 9979fe1a2aSAlexander Aring } 10079fe1a2aSAlexander Aring 10179fe1a2aSAlexander Aring if (result) 10279fe1a2aSAlexander Aring break; 10379fe1a2aSAlexander Aring } 10479fe1a2aSAlexander Aring 10579fe1a2aSAlexander Aring if (result) 10679fe1a2aSAlexander Aring return result; 10779fe1a2aSAlexander Aring 10879fe1a2aSAlexander Aring return ERR_PTR(-ENODEV); 10979fe1a2aSAlexander Aring } 11079fe1a2aSAlexander Aring 11179fe1a2aSAlexander Aring static struct cfg802154_registered_device * 11279fe1a2aSAlexander Aring __cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs) 11379fe1a2aSAlexander Aring { 11479fe1a2aSAlexander Aring struct cfg802154_registered_device *rdev = NULL, *tmp; 11579fe1a2aSAlexander Aring struct net_device *netdev; 11679fe1a2aSAlexander Aring 11779fe1a2aSAlexander Aring ASSERT_RTNL(); 11879fe1a2aSAlexander Aring 11979fe1a2aSAlexander Aring if (!attrs[NL802154_ATTR_WPAN_PHY] && 12079fe1a2aSAlexander Aring !attrs[NL802154_ATTR_IFINDEX] && 12179fe1a2aSAlexander Aring !attrs[NL802154_ATTR_WPAN_DEV]) 12279fe1a2aSAlexander Aring return ERR_PTR(-EINVAL); 12379fe1a2aSAlexander Aring 12479fe1a2aSAlexander Aring if (attrs[NL802154_ATTR_WPAN_PHY]) 12579fe1a2aSAlexander Aring rdev = cfg802154_rdev_by_wpan_phy_idx( 12679fe1a2aSAlexander Aring nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY])); 12779fe1a2aSAlexander Aring 12879fe1a2aSAlexander Aring if (attrs[NL802154_ATTR_WPAN_DEV]) { 12979fe1a2aSAlexander Aring u64 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]); 13079fe1a2aSAlexander Aring struct wpan_dev *wpan_dev; 13179fe1a2aSAlexander Aring bool found = false; 13279fe1a2aSAlexander Aring 13379fe1a2aSAlexander Aring tmp = cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id >> 32); 13479fe1a2aSAlexander Aring if (tmp) { 13579fe1a2aSAlexander Aring /* make sure wpan_dev exists */ 13679fe1a2aSAlexander Aring list_for_each_entry(wpan_dev, &tmp->wpan_dev_list, list) { 13779fe1a2aSAlexander Aring if (wpan_dev->identifier != (u32)wpan_dev_id) 13879fe1a2aSAlexander Aring continue; 13979fe1a2aSAlexander Aring found = true; 14079fe1a2aSAlexander Aring break; 14179fe1a2aSAlexander Aring } 14279fe1a2aSAlexander Aring 14379fe1a2aSAlexander Aring if (!found) 14479fe1a2aSAlexander Aring tmp = NULL; 14579fe1a2aSAlexander Aring 14679fe1a2aSAlexander Aring if (rdev && tmp != rdev) 14779fe1a2aSAlexander Aring return ERR_PTR(-EINVAL); 14879fe1a2aSAlexander Aring rdev = tmp; 14979fe1a2aSAlexander Aring } 15079fe1a2aSAlexander Aring } 15179fe1a2aSAlexander Aring 15279fe1a2aSAlexander Aring if (attrs[NL802154_ATTR_IFINDEX]) { 15379fe1a2aSAlexander Aring int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]); 15479fe1a2aSAlexander Aring 15579fe1a2aSAlexander Aring netdev = __dev_get_by_index(netns, ifindex); 15679fe1a2aSAlexander Aring if (netdev) { 15779fe1a2aSAlexander Aring if (netdev->ieee802154_ptr) 15879fe1a2aSAlexander Aring tmp = wpan_phy_to_rdev( 15979fe1a2aSAlexander Aring netdev->ieee802154_ptr->wpan_phy); 16079fe1a2aSAlexander Aring else 16179fe1a2aSAlexander Aring tmp = NULL; 16279fe1a2aSAlexander Aring 16379fe1a2aSAlexander Aring /* not wireless device -- return error */ 16479fe1a2aSAlexander Aring if (!tmp) 16579fe1a2aSAlexander Aring return ERR_PTR(-EINVAL); 16679fe1a2aSAlexander Aring 16779fe1a2aSAlexander Aring /* mismatch -- return error */ 16879fe1a2aSAlexander Aring if (rdev && tmp != rdev) 16979fe1a2aSAlexander Aring return ERR_PTR(-EINVAL); 17079fe1a2aSAlexander Aring 17179fe1a2aSAlexander Aring rdev = tmp; 17279fe1a2aSAlexander Aring } 17379fe1a2aSAlexander Aring } 17479fe1a2aSAlexander Aring 17579fe1a2aSAlexander Aring if (!rdev) 17679fe1a2aSAlexander Aring return ERR_PTR(-ENODEV); 17779fe1a2aSAlexander Aring 17879fe1a2aSAlexander Aring /* TODO netns compare */ 17979fe1a2aSAlexander Aring 18079fe1a2aSAlexander Aring return rdev; 18179fe1a2aSAlexander Aring } 18279fe1a2aSAlexander Aring 18379fe1a2aSAlexander Aring /* This function returns a pointer to the driver 18479fe1a2aSAlexander Aring * that the genl_info item that is passed refers to. 18579fe1a2aSAlexander Aring * 18679fe1a2aSAlexander Aring * The result of this can be a PTR_ERR and hence must 18779fe1a2aSAlexander Aring * be checked with IS_ERR() for errors. 18879fe1a2aSAlexander Aring */ 18979fe1a2aSAlexander Aring static struct cfg802154_registered_device * 19079fe1a2aSAlexander Aring cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info) 19179fe1a2aSAlexander Aring { 19279fe1a2aSAlexander Aring return __cfg802154_rdev_from_attrs(netns, info->attrs); 19379fe1a2aSAlexander Aring } 19479fe1a2aSAlexander Aring 19579fe1a2aSAlexander Aring /* policy for the attributes */ 19679fe1a2aSAlexander Aring static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = { 197ca20ce20SAlexander Aring [NL802154_ATTR_WPAN_PHY] = { .type = NLA_U32 }, 198ca20ce20SAlexander Aring [NL802154_ATTR_WPAN_PHY_NAME] = { .type = NLA_NUL_STRING, 199ca20ce20SAlexander Aring .len = 20-1 }, 200ca20ce20SAlexander Aring 201ca20ce20SAlexander Aring [NL802154_ATTR_IFINDEX] = { .type = NLA_U32 }, 2024b96aea0SAlexander Aring [NL802154_ATTR_IFTYPE] = { .type = NLA_U32 }, 2034b96aea0SAlexander Aring [NL802154_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, 204ca20ce20SAlexander Aring 205ca20ce20SAlexander Aring [NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 }, 206ca20ce20SAlexander Aring 207ca20ce20SAlexander Aring [NL802154_ATTR_PAGE] = { .type = NLA_U8, }, 208ca20ce20SAlexander Aring [NL802154_ATTR_CHANNEL] = { .type = NLA_U8, }, 209ca20ce20SAlexander Aring 2101a19cb68SAlexander Aring [NL802154_ATTR_TX_POWER] = { .type = NLA_S32, }, 211ca20ce20SAlexander Aring 212ba2a9506SAlexander Aring [NL802154_ATTR_CCA_MODE] = { .type = NLA_U32, }, 213ba2a9506SAlexander Aring [NL802154_ATTR_CCA_OPT] = { .type = NLA_U32, }, 214ca20ce20SAlexander Aring 215ca20ce20SAlexander Aring [NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, }, 2164b96aea0SAlexander Aring 2174b96aea0SAlexander Aring [NL802154_ATTR_PAN_ID] = { .type = NLA_U16, }, 2184b96aea0SAlexander Aring [NL802154_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 }, 2194b96aea0SAlexander Aring [NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, }, 2204b96aea0SAlexander Aring 2214b96aea0SAlexander Aring [NL802154_ATTR_MIN_BE] = { .type = NLA_U8, }, 2224b96aea0SAlexander Aring [NL802154_ATTR_MAX_BE] = { .type = NLA_U8, }, 2234b96aea0SAlexander Aring [NL802154_ATTR_MAX_CSMA_BACKOFFS] = { .type = NLA_U8, }, 2244b96aea0SAlexander Aring 2254b96aea0SAlexander Aring [NL802154_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_S8, }, 2264b96aea0SAlexander Aring 2274b96aea0SAlexander Aring [NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, }, 22879fe1a2aSAlexander Aring }; 22979fe1a2aSAlexander Aring 23079fe1a2aSAlexander Aring /* message building helper */ 23179fe1a2aSAlexander Aring static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq, 23279fe1a2aSAlexander Aring int flags, u8 cmd) 23379fe1a2aSAlexander Aring { 23479fe1a2aSAlexander Aring /* since there is no private header just add the generic one */ 23579fe1a2aSAlexander Aring return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd); 23679fe1a2aSAlexander Aring } 23779fe1a2aSAlexander Aring 238ca20ce20SAlexander Aring static int 239ca20ce20SAlexander Aring nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev, 240ca20ce20SAlexander Aring struct sk_buff *msg) 241ca20ce20SAlexander Aring { 242ca20ce20SAlexander Aring struct nlattr *nl_page; 243ca20ce20SAlexander Aring unsigned long page; 244ca20ce20SAlexander Aring 245ca20ce20SAlexander Aring nl_page = nla_nest_start(msg, NL802154_ATTR_CHANNELS_SUPPORTED); 246ca20ce20SAlexander Aring if (!nl_page) 247ca20ce20SAlexander Aring return -ENOBUFS; 248ca20ce20SAlexander Aring 249cb41c8ddSAlexander Aring for (page = 0; page <= IEEE802154_MAX_PAGE; page++) { 250ca20ce20SAlexander Aring if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL, 25172f655e4SAlexander Aring rdev->wpan_phy.supported.channels[page])) 252ca20ce20SAlexander Aring return -ENOBUFS; 253ca20ce20SAlexander Aring } 254ca20ce20SAlexander Aring nla_nest_end(msg, nl_page); 255ca20ce20SAlexander Aring 256ca20ce20SAlexander Aring return 0; 257ca20ce20SAlexander Aring } 258ca20ce20SAlexander Aring 259ca20ce20SAlexander Aring static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev, 260ca20ce20SAlexander Aring enum nl802154_commands cmd, 261ca20ce20SAlexander Aring struct sk_buff *msg, u32 portid, u32 seq, 262ca20ce20SAlexander Aring int flags) 263ca20ce20SAlexander Aring { 264ca20ce20SAlexander Aring void *hdr; 265ca20ce20SAlexander Aring 266ca20ce20SAlexander Aring hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); 267ca20ce20SAlexander Aring if (!hdr) 268ca20ce20SAlexander Aring return -ENOBUFS; 269ca20ce20SAlexander Aring 270ca20ce20SAlexander Aring if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) || 271ca20ce20SAlexander Aring nla_put_string(msg, NL802154_ATTR_WPAN_PHY_NAME, 272ca20ce20SAlexander Aring wpan_phy_name(&rdev->wpan_phy)) || 273ca20ce20SAlexander Aring nla_put_u32(msg, NL802154_ATTR_GENERATION, 274ca20ce20SAlexander Aring cfg802154_rdev_list_generation)) 275ca20ce20SAlexander Aring goto nla_put_failure; 276ca20ce20SAlexander Aring 277ca20ce20SAlexander Aring if (cmd != NL802154_CMD_NEW_WPAN_PHY) 278ca20ce20SAlexander Aring goto finish; 279ca20ce20SAlexander Aring 280ca20ce20SAlexander Aring /* DUMP PHY PIB */ 281ca20ce20SAlexander Aring 282ca20ce20SAlexander Aring /* current channel settings */ 283ca20ce20SAlexander Aring if (nla_put_u8(msg, NL802154_ATTR_PAGE, 284ca20ce20SAlexander Aring rdev->wpan_phy.current_page) || 285ca20ce20SAlexander Aring nla_put_u8(msg, NL802154_ATTR_CHANNEL, 286ca20ce20SAlexander Aring rdev->wpan_phy.current_channel)) 287ca20ce20SAlexander Aring goto nla_put_failure; 288ca20ce20SAlexander Aring 289ca20ce20SAlexander Aring /* supported channels array */ 290ca20ce20SAlexander Aring if (nl802154_send_wpan_phy_channels(rdev, msg)) 291ca20ce20SAlexander Aring goto nla_put_failure; 292ca20ce20SAlexander Aring 293ca20ce20SAlexander Aring /* cca mode */ 294*edea8f7cSAlexander Aring if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE) { 295ba2a9506SAlexander Aring if (nla_put_u32(msg, NL802154_ATTR_CCA_MODE, 2967fe9a388SAlexander Aring rdev->wpan_phy.cca.mode)) 297ca20ce20SAlexander Aring goto nla_put_failure; 298ca20ce20SAlexander Aring 299ba2a9506SAlexander Aring if (rdev->wpan_phy.cca.mode == NL802154_CCA_ENERGY_CARRIER) { 300ba2a9506SAlexander Aring if (nla_put_u32(msg, NL802154_ATTR_CCA_OPT, 301ba2a9506SAlexander Aring rdev->wpan_phy.cca.opt)) 302ba2a9506SAlexander Aring goto nla_put_failure; 303ba2a9506SAlexander Aring } 304*edea8f7cSAlexander Aring } 305ba2a9506SAlexander Aring 306*edea8f7cSAlexander Aring if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER) { 3071a19cb68SAlexander Aring if (nla_put_s32(msg, NL802154_ATTR_TX_POWER, 308ca20ce20SAlexander Aring rdev->wpan_phy.transmit_power)) 309ca20ce20SAlexander Aring goto nla_put_failure; 310*edea8f7cSAlexander Aring } 311ca20ce20SAlexander Aring 312ca20ce20SAlexander Aring finish: 313053c095aSJohannes Berg genlmsg_end(msg, hdr); 314053c095aSJohannes Berg return 0; 315ca20ce20SAlexander Aring 316ca20ce20SAlexander Aring nla_put_failure: 317ca20ce20SAlexander Aring genlmsg_cancel(msg, hdr); 318ca20ce20SAlexander Aring return -EMSGSIZE; 319ca20ce20SAlexander Aring } 320ca20ce20SAlexander Aring 321ca20ce20SAlexander Aring struct nl802154_dump_wpan_phy_state { 322ca20ce20SAlexander Aring s64 filter_wpan_phy; 323ca20ce20SAlexander Aring long start; 324ca20ce20SAlexander Aring 325ca20ce20SAlexander Aring }; 326ca20ce20SAlexander Aring 327ca20ce20SAlexander Aring static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb, 328ca20ce20SAlexander Aring struct netlink_callback *cb, 329ca20ce20SAlexander Aring struct nl802154_dump_wpan_phy_state *state) 330ca20ce20SAlexander Aring { 331ca20ce20SAlexander Aring struct nlattr **tb = nl802154_fam.attrbuf; 332ca20ce20SAlexander Aring int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize, 333ca20ce20SAlexander Aring tb, nl802154_fam.maxattr, nl802154_policy); 334ca20ce20SAlexander Aring 335ca20ce20SAlexander Aring /* TODO check if we can handle error here, 336ca20ce20SAlexander Aring * we have no backward compatibility 337ca20ce20SAlexander Aring */ 338ca20ce20SAlexander Aring if (ret) 339ca20ce20SAlexander Aring return 0; 340ca20ce20SAlexander Aring 341ca20ce20SAlexander Aring if (tb[NL802154_ATTR_WPAN_PHY]) 342ca20ce20SAlexander Aring state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]); 343ca20ce20SAlexander Aring if (tb[NL802154_ATTR_WPAN_DEV]) 344ca20ce20SAlexander Aring state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32; 345ca20ce20SAlexander Aring if (tb[NL802154_ATTR_IFINDEX]) { 346ca20ce20SAlexander Aring struct net_device *netdev; 347ca20ce20SAlexander Aring struct cfg802154_registered_device *rdev; 348ca20ce20SAlexander Aring int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]); 349ca20ce20SAlexander Aring 350ca20ce20SAlexander Aring /* TODO netns */ 351ca20ce20SAlexander Aring netdev = __dev_get_by_index(&init_net, ifidx); 352ca20ce20SAlexander Aring if (!netdev) 353ca20ce20SAlexander Aring return -ENODEV; 354ca20ce20SAlexander Aring if (netdev->ieee802154_ptr) { 355ca20ce20SAlexander Aring rdev = wpan_phy_to_rdev( 356ca20ce20SAlexander Aring netdev->ieee802154_ptr->wpan_phy); 357ca20ce20SAlexander Aring state->filter_wpan_phy = rdev->wpan_phy_idx; 358ca20ce20SAlexander Aring } 359ca20ce20SAlexander Aring } 360ca20ce20SAlexander Aring 361ca20ce20SAlexander Aring return 0; 362ca20ce20SAlexander Aring } 363ca20ce20SAlexander Aring 364ca20ce20SAlexander Aring static int 365ca20ce20SAlexander Aring nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb) 366ca20ce20SAlexander Aring { 367ca20ce20SAlexander Aring int idx = 0, ret; 368ca20ce20SAlexander Aring struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0]; 369ca20ce20SAlexander Aring struct cfg802154_registered_device *rdev; 370ca20ce20SAlexander Aring 371ca20ce20SAlexander Aring rtnl_lock(); 372ca20ce20SAlexander Aring if (!state) { 373ca20ce20SAlexander Aring state = kzalloc(sizeof(*state), GFP_KERNEL); 374ca20ce20SAlexander Aring if (!state) { 375ca20ce20SAlexander Aring rtnl_unlock(); 376ca20ce20SAlexander Aring return -ENOMEM; 377ca20ce20SAlexander Aring } 378ca20ce20SAlexander Aring state->filter_wpan_phy = -1; 379ca20ce20SAlexander Aring ret = nl802154_dump_wpan_phy_parse(skb, cb, state); 380ca20ce20SAlexander Aring if (ret) { 381ca20ce20SAlexander Aring kfree(state); 382ca20ce20SAlexander Aring rtnl_unlock(); 383ca20ce20SAlexander Aring return ret; 384ca20ce20SAlexander Aring } 385ca20ce20SAlexander Aring cb->args[0] = (long)state; 386ca20ce20SAlexander Aring } 387ca20ce20SAlexander Aring 388ca20ce20SAlexander Aring list_for_each_entry(rdev, &cfg802154_rdev_list, list) { 389ca20ce20SAlexander Aring /* TODO net ns compare */ 390ca20ce20SAlexander Aring if (++idx <= state->start) 391ca20ce20SAlexander Aring continue; 392ca20ce20SAlexander Aring if (state->filter_wpan_phy != -1 && 393ca20ce20SAlexander Aring state->filter_wpan_phy != rdev->wpan_phy_idx) 394ca20ce20SAlexander Aring continue; 395ca20ce20SAlexander Aring /* attempt to fit multiple wpan_phy data chunks into the skb */ 396ca20ce20SAlexander Aring ret = nl802154_send_wpan_phy(rdev, 397ca20ce20SAlexander Aring NL802154_CMD_NEW_WPAN_PHY, 398ca20ce20SAlexander Aring skb, 399ca20ce20SAlexander Aring NETLINK_CB(cb->skb).portid, 400ca20ce20SAlexander Aring cb->nlh->nlmsg_seq, NLM_F_MULTI); 401ca20ce20SAlexander Aring if (ret < 0) { 402ca20ce20SAlexander Aring if ((ret == -ENOBUFS || ret == -EMSGSIZE) && 403ca20ce20SAlexander Aring !skb->len && cb->min_dump_alloc < 4096) { 404ca20ce20SAlexander Aring cb->min_dump_alloc = 4096; 405ca20ce20SAlexander Aring rtnl_unlock(); 406ca20ce20SAlexander Aring return 1; 407ca20ce20SAlexander Aring } 408ca20ce20SAlexander Aring idx--; 409ca20ce20SAlexander Aring break; 410ca20ce20SAlexander Aring } 411ca20ce20SAlexander Aring break; 412ca20ce20SAlexander Aring } 413ca20ce20SAlexander Aring rtnl_unlock(); 414ca20ce20SAlexander Aring 415ca20ce20SAlexander Aring state->start = idx; 416ca20ce20SAlexander Aring 417ca20ce20SAlexander Aring return skb->len; 418ca20ce20SAlexander Aring } 419ca20ce20SAlexander Aring 420ca20ce20SAlexander Aring static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb) 421ca20ce20SAlexander Aring { 422ca20ce20SAlexander Aring kfree((void *)cb->args[0]); 423ca20ce20SAlexander Aring return 0; 424ca20ce20SAlexander Aring } 425ca20ce20SAlexander Aring 426ca20ce20SAlexander Aring static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info) 427ca20ce20SAlexander Aring { 428ca20ce20SAlexander Aring struct sk_buff *msg; 429ca20ce20SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 430ca20ce20SAlexander Aring 431ca20ce20SAlexander Aring msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 432ca20ce20SAlexander Aring if (!msg) 433ca20ce20SAlexander Aring return -ENOMEM; 434ca20ce20SAlexander Aring 435ca20ce20SAlexander Aring if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg, 436ca20ce20SAlexander Aring info->snd_portid, info->snd_seq, 0) < 0) { 437ca20ce20SAlexander Aring nlmsg_free(msg); 438ca20ce20SAlexander Aring return -ENOBUFS; 439ca20ce20SAlexander Aring } 440ca20ce20SAlexander Aring 441ca20ce20SAlexander Aring return genlmsg_reply(msg, info); 442ca20ce20SAlexander Aring } 443ca20ce20SAlexander Aring 4444b96aea0SAlexander Aring static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev) 4454b96aea0SAlexander Aring { 4464b96aea0SAlexander Aring return (u64)wpan_dev->identifier | 4474b96aea0SAlexander Aring ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32); 4484b96aea0SAlexander Aring } 4494b96aea0SAlexander Aring 4504b96aea0SAlexander Aring static int 4514b96aea0SAlexander Aring nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags, 4524b96aea0SAlexander Aring struct cfg802154_registered_device *rdev, 4534b96aea0SAlexander Aring struct wpan_dev *wpan_dev) 4544b96aea0SAlexander Aring { 4554b96aea0SAlexander Aring struct net_device *dev = wpan_dev->netdev; 4564b96aea0SAlexander Aring void *hdr; 4574b96aea0SAlexander Aring 4584b96aea0SAlexander Aring hdr = nl802154hdr_put(msg, portid, seq, flags, 4594b96aea0SAlexander Aring NL802154_CMD_NEW_INTERFACE); 4604b96aea0SAlexander Aring if (!hdr) 4614b96aea0SAlexander Aring return -1; 4624b96aea0SAlexander Aring 4634b96aea0SAlexander Aring if (dev && 4644b96aea0SAlexander Aring (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) || 4654b96aea0SAlexander Aring nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name))) 4664b96aea0SAlexander Aring goto nla_put_failure; 4674b96aea0SAlexander Aring 4684b96aea0SAlexander Aring if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) || 4694b96aea0SAlexander Aring nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) || 4704b96aea0SAlexander Aring nla_put_u64(msg, NL802154_ATTR_WPAN_DEV, wpan_dev_id(wpan_dev)) || 4714b96aea0SAlexander Aring nla_put_u32(msg, NL802154_ATTR_GENERATION, 4724b96aea0SAlexander Aring rdev->devlist_generation ^ 4734b96aea0SAlexander Aring (cfg802154_rdev_list_generation << 2))) 4744b96aea0SAlexander Aring goto nla_put_failure; 4754b96aea0SAlexander Aring 4764b96aea0SAlexander Aring /* address settings */ 4774b96aea0SAlexander Aring if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR, 4784b96aea0SAlexander Aring wpan_dev->extended_addr) || 4794b96aea0SAlexander Aring nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR, 4804b96aea0SAlexander Aring wpan_dev->short_addr) || 4814b96aea0SAlexander Aring nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id)) 4824b96aea0SAlexander Aring goto nla_put_failure; 4834b96aea0SAlexander Aring 4844b96aea0SAlexander Aring /* ARET handling */ 4854b96aea0SAlexander Aring if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES, 4864b96aea0SAlexander Aring wpan_dev->frame_retries) || 4874b96aea0SAlexander Aring nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) || 4884b96aea0SAlexander Aring nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS, 4894b96aea0SAlexander Aring wpan_dev->csma_retries) || 4904b96aea0SAlexander Aring nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be)) 4914b96aea0SAlexander Aring goto nla_put_failure; 4924b96aea0SAlexander Aring 4934b96aea0SAlexander Aring /* listen before transmit */ 4944b96aea0SAlexander Aring if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt)) 4954b96aea0SAlexander Aring goto nla_put_failure; 4964b96aea0SAlexander Aring 497053c095aSJohannes Berg genlmsg_end(msg, hdr); 498053c095aSJohannes Berg return 0; 4994b96aea0SAlexander Aring 5004b96aea0SAlexander Aring nla_put_failure: 5014b96aea0SAlexander Aring genlmsg_cancel(msg, hdr); 5024b96aea0SAlexander Aring return -EMSGSIZE; 5034b96aea0SAlexander Aring } 5044b96aea0SAlexander Aring 5054b96aea0SAlexander Aring static int 5064b96aea0SAlexander Aring nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb) 5074b96aea0SAlexander Aring { 5084b96aea0SAlexander Aring int wp_idx = 0; 5094b96aea0SAlexander Aring int if_idx = 0; 5104b96aea0SAlexander Aring int wp_start = cb->args[0]; 5114b96aea0SAlexander Aring int if_start = cb->args[1]; 5124b96aea0SAlexander Aring struct cfg802154_registered_device *rdev; 5134b96aea0SAlexander Aring struct wpan_dev *wpan_dev; 5144b96aea0SAlexander Aring 5154b96aea0SAlexander Aring rtnl_lock(); 5164b96aea0SAlexander Aring list_for_each_entry(rdev, &cfg802154_rdev_list, list) { 5174b96aea0SAlexander Aring /* TODO netns compare */ 5184b96aea0SAlexander Aring if (wp_idx < wp_start) { 5194b96aea0SAlexander Aring wp_idx++; 5204b96aea0SAlexander Aring continue; 5214b96aea0SAlexander Aring } 5224b96aea0SAlexander Aring if_idx = 0; 5234b96aea0SAlexander Aring 5244b96aea0SAlexander Aring list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) { 5254b96aea0SAlexander Aring if (if_idx < if_start) { 5264b96aea0SAlexander Aring if_idx++; 5274b96aea0SAlexander Aring continue; 5284b96aea0SAlexander Aring } 5294b96aea0SAlexander Aring if (nl802154_send_iface(skb, NETLINK_CB(cb->skb).portid, 5304b96aea0SAlexander Aring cb->nlh->nlmsg_seq, NLM_F_MULTI, 5314b96aea0SAlexander Aring rdev, wpan_dev) < 0) { 5324b96aea0SAlexander Aring goto out; 5334b96aea0SAlexander Aring } 5344b96aea0SAlexander Aring if_idx++; 5354b96aea0SAlexander Aring } 5364b96aea0SAlexander Aring 5374b96aea0SAlexander Aring wp_idx++; 5384b96aea0SAlexander Aring } 5394b96aea0SAlexander Aring out: 5404b96aea0SAlexander Aring rtnl_unlock(); 5414b96aea0SAlexander Aring 5424b96aea0SAlexander Aring cb->args[0] = wp_idx; 5434b96aea0SAlexander Aring cb->args[1] = if_idx; 5444b96aea0SAlexander Aring 5454b96aea0SAlexander Aring return skb->len; 5464b96aea0SAlexander Aring } 5474b96aea0SAlexander Aring 5484b96aea0SAlexander Aring static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info) 5494b96aea0SAlexander Aring { 5504b96aea0SAlexander Aring struct sk_buff *msg; 5514b96aea0SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 5524b96aea0SAlexander Aring struct wpan_dev *wdev = info->user_ptr[1]; 5534b96aea0SAlexander Aring 5544b96aea0SAlexander Aring msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 5554b96aea0SAlexander Aring if (!msg) 5564b96aea0SAlexander Aring return -ENOMEM; 5574b96aea0SAlexander Aring 5584b96aea0SAlexander Aring if (nl802154_send_iface(msg, info->snd_portid, info->snd_seq, 0, 5594b96aea0SAlexander Aring rdev, wdev) < 0) { 5604b96aea0SAlexander Aring nlmsg_free(msg); 5614b96aea0SAlexander Aring return -ENOBUFS; 5624b96aea0SAlexander Aring } 5634b96aea0SAlexander Aring 5644b96aea0SAlexander Aring return genlmsg_reply(msg, info); 5654b96aea0SAlexander Aring } 5664b96aea0SAlexander Aring 567f3ea5e44SAlexander Aring static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info) 568f3ea5e44SAlexander Aring { 569f3ea5e44SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 570f3ea5e44SAlexander Aring enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC; 5710e57547eSAlexander Aring __le64 extended_addr = cpu_to_le64(0x0000000000000000ULL); 572f3ea5e44SAlexander Aring 573f3ea5e44SAlexander Aring /* TODO avoid failing a new interface 574f3ea5e44SAlexander Aring * creation due to pending removal? 575f3ea5e44SAlexander Aring */ 576f3ea5e44SAlexander Aring 577f3ea5e44SAlexander Aring if (!info->attrs[NL802154_ATTR_IFNAME]) 578f3ea5e44SAlexander Aring return -EINVAL; 579f3ea5e44SAlexander Aring 580f3ea5e44SAlexander Aring if (info->attrs[NL802154_ATTR_IFTYPE]) { 581f3ea5e44SAlexander Aring type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]); 582f3ea5e44SAlexander Aring if (type > NL802154_IFTYPE_MAX) 583f3ea5e44SAlexander Aring return -EINVAL; 584f3ea5e44SAlexander Aring } 585f3ea5e44SAlexander Aring 5860e57547eSAlexander Aring /* TODO add nla_get_le64 to netlink */ 5870e57547eSAlexander Aring if (info->attrs[NL802154_ATTR_EXTENDED_ADDR]) 5880e57547eSAlexander Aring extended_addr = (__force __le64)nla_get_u64( 5890e57547eSAlexander Aring info->attrs[NL802154_ATTR_EXTENDED_ADDR]); 5900e57547eSAlexander Aring 591f3ea5e44SAlexander Aring if (!rdev->ops->add_virtual_intf) 592f3ea5e44SAlexander Aring return -EOPNOTSUPP; 593f3ea5e44SAlexander Aring 594f3ea5e44SAlexander Aring return rdev_add_virtual_intf(rdev, 595f3ea5e44SAlexander Aring nla_data(info->attrs[NL802154_ATTR_IFNAME]), 5965b4a1039SVarka Bhadram NET_NAME_USER, type, extended_addr); 597f3ea5e44SAlexander Aring } 598f3ea5e44SAlexander Aring 599b821ecd4SAlexander Aring static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info) 600b821ecd4SAlexander Aring { 601b821ecd4SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 602b821ecd4SAlexander Aring struct wpan_dev *wpan_dev = info->user_ptr[1]; 603b821ecd4SAlexander Aring 604b821ecd4SAlexander Aring if (!rdev->ops->del_virtual_intf) 605b821ecd4SAlexander Aring return -EOPNOTSUPP; 606b821ecd4SAlexander Aring 607b821ecd4SAlexander Aring /* If we remove a wpan device without a netdev then clear 608b821ecd4SAlexander Aring * user_ptr[1] so that nl802154_post_doit won't dereference it 609b821ecd4SAlexander Aring * to check if it needs to do dev_put(). Otherwise it crashes 610b821ecd4SAlexander Aring * since the wpan_dev has been freed, unlike with a netdev where 611b821ecd4SAlexander Aring * we need the dev_put() for the netdev to really be freed. 612b821ecd4SAlexander Aring */ 613b821ecd4SAlexander Aring if (!wpan_dev->netdev) 614b821ecd4SAlexander Aring info->user_ptr[1] = NULL; 615b821ecd4SAlexander Aring 616b821ecd4SAlexander Aring return rdev_del_virtual_intf(rdev, wpan_dev); 617b821ecd4SAlexander Aring } 618b821ecd4SAlexander Aring 619ab0bd561SAlexander Aring static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info) 620ab0bd561SAlexander Aring { 621ab0bd561SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 622ab0bd561SAlexander Aring u8 channel, page; 623ab0bd561SAlexander Aring 624ab0bd561SAlexander Aring if (!info->attrs[NL802154_ATTR_PAGE] || 625ab0bd561SAlexander Aring !info->attrs[NL802154_ATTR_CHANNEL]) 626ab0bd561SAlexander Aring return -EINVAL; 627ab0bd561SAlexander Aring 628ab0bd561SAlexander Aring page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]); 629ab0bd561SAlexander Aring channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]); 630ab0bd561SAlexander Aring 631ab0bd561SAlexander Aring /* check 802.15.4 constraints */ 632673692faSAlexander Aring if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL || 63372f655e4SAlexander Aring !(rdev->wpan_phy.supported.channels[page] & BIT(channel))) 634ab0bd561SAlexander Aring return -EINVAL; 635ab0bd561SAlexander Aring 636ab0bd561SAlexander Aring return rdev_set_channel(rdev, page, channel); 637ab0bd561SAlexander Aring } 638ab0bd561SAlexander Aring 639ba2a9506SAlexander Aring static int nl802154_set_cca_mode(struct sk_buff *skb, struct genl_info *info) 640ba2a9506SAlexander Aring { 641ba2a9506SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 642ba2a9506SAlexander Aring struct wpan_phy_cca cca; 643ba2a9506SAlexander Aring 644*edea8f7cSAlexander Aring if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE) 645*edea8f7cSAlexander Aring return -EOPNOTSUPP; 646*edea8f7cSAlexander Aring 647ba2a9506SAlexander Aring if (!info->attrs[NL802154_ATTR_CCA_MODE]) 648ba2a9506SAlexander Aring return -EINVAL; 649ba2a9506SAlexander Aring 650ba2a9506SAlexander Aring cca.mode = nla_get_u32(info->attrs[NL802154_ATTR_CCA_MODE]); 651ba2a9506SAlexander Aring /* checking 802.15.4 constraints */ 652fea3318dSAlexander Aring if (cca.mode < NL802154_CCA_ENERGY || 653fea3318dSAlexander Aring cca.mode > NL802154_CCA_ATTR_MAX || 654fea3318dSAlexander Aring !(rdev->wpan_phy.supported.cca_modes & BIT(cca.mode))) 655ba2a9506SAlexander Aring return -EINVAL; 656ba2a9506SAlexander Aring 657ba2a9506SAlexander Aring if (cca.mode == NL802154_CCA_ENERGY_CARRIER) { 658ba2a9506SAlexander Aring if (!info->attrs[NL802154_ATTR_CCA_OPT]) 659ba2a9506SAlexander Aring return -EINVAL; 660ba2a9506SAlexander Aring 661ba2a9506SAlexander Aring cca.opt = nla_get_u32(info->attrs[NL802154_ATTR_CCA_OPT]); 662fea3318dSAlexander Aring if (cca.opt > NL802154_CCA_OPT_ATTR_MAX || 663fea3318dSAlexander Aring !(rdev->wpan_phy.supported.cca_opts & BIT(cca.opt))) 664ba2a9506SAlexander Aring return -EINVAL; 665ba2a9506SAlexander Aring } 666ba2a9506SAlexander Aring 667ba2a9506SAlexander Aring return rdev_set_cca_mode(rdev, &cca); 668ba2a9506SAlexander Aring } 669ba2a9506SAlexander Aring 670702bf371SAlexander Aring static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info) 671702bf371SAlexander Aring { 672702bf371SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 673702bf371SAlexander Aring struct net_device *dev = info->user_ptr[1]; 674702bf371SAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 675ee7b9053SAlexander Aring __le16 pan_id; 676702bf371SAlexander Aring 677702bf371SAlexander Aring /* conflict here while tx/rx calls */ 678702bf371SAlexander Aring if (netif_running(dev)) 679702bf371SAlexander Aring return -EBUSY; 680702bf371SAlexander Aring 681702bf371SAlexander Aring /* don't change address fields on monitor */ 6820cf0879aSAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR || 6830cf0879aSAlexander Aring !info->attrs[NL802154_ATTR_PAN_ID]) 684702bf371SAlexander Aring return -EINVAL; 685702bf371SAlexander Aring 686ee7b9053SAlexander Aring pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]); 687702bf371SAlexander Aring 688673692faSAlexander Aring /* TODO 689673692faSAlexander Aring * I am not sure about to check here on broadcast pan_id. 690673692faSAlexander Aring * Broadcast is a valid setting, comment from 802.15.4: 691673692faSAlexander Aring * If this value is 0xffff, the device is not associated. 692673692faSAlexander Aring * 693673692faSAlexander Aring * This could useful to simple deassociate an device. 694673692faSAlexander Aring */ 695673692faSAlexander Aring if (pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST)) 696673692faSAlexander Aring return -EINVAL; 697673692faSAlexander Aring 698702bf371SAlexander Aring return rdev_set_pan_id(rdev, wpan_dev, pan_id); 699702bf371SAlexander Aring } 700702bf371SAlexander Aring 7019830c62aSAlexander Aring static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info) 7029830c62aSAlexander Aring { 7039830c62aSAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 7049830c62aSAlexander Aring struct net_device *dev = info->user_ptr[1]; 7059830c62aSAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 706ee7b9053SAlexander Aring __le16 short_addr; 7079830c62aSAlexander Aring 7089830c62aSAlexander Aring /* conflict here while tx/rx calls */ 7099830c62aSAlexander Aring if (netif_running(dev)) 7109830c62aSAlexander Aring return -EBUSY; 7119830c62aSAlexander Aring 7129830c62aSAlexander Aring /* don't change address fields on monitor */ 7130cf0879aSAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR || 7140cf0879aSAlexander Aring !info->attrs[NL802154_ATTR_SHORT_ADDR]) 7159830c62aSAlexander Aring return -EINVAL; 7169830c62aSAlexander Aring 717ee7b9053SAlexander Aring short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]); 7189830c62aSAlexander Aring 719673692faSAlexander Aring /* TODO 720673692faSAlexander Aring * I am not sure about to check here on broadcast short_addr. 721673692faSAlexander Aring * Broadcast is a valid setting, comment from 802.15.4: 722673692faSAlexander Aring * A value of 0xfffe indicates that the device has 723673692faSAlexander Aring * associated but has not been allocated an address. A 724673692faSAlexander Aring * value of 0xffff indicates that the device does not 725673692faSAlexander Aring * have a short address. 726673692faSAlexander Aring * 727673692faSAlexander Aring * I think we should allow to set these settings but 728673692faSAlexander Aring * don't allow to allow socket communication with it. 729673692faSAlexander Aring */ 730673692faSAlexander Aring if (short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC) || 731673692faSAlexander Aring short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST)) 732673692faSAlexander Aring return -EINVAL; 733673692faSAlexander Aring 7349830c62aSAlexander Aring return rdev_set_short_addr(rdev, wpan_dev, short_addr); 7359830c62aSAlexander Aring } 7369830c62aSAlexander Aring 737656a999eSAlexander Aring static int 738656a999eSAlexander Aring nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info) 739656a999eSAlexander Aring { 740656a999eSAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 741656a999eSAlexander Aring struct net_device *dev = info->user_ptr[1]; 742656a999eSAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 743656a999eSAlexander Aring u8 min_be, max_be; 744656a999eSAlexander Aring 745656a999eSAlexander Aring /* should be set on netif open inside phy settings */ 746656a999eSAlexander Aring if (netif_running(dev)) 747656a999eSAlexander Aring return -EBUSY; 748656a999eSAlexander Aring 749656a999eSAlexander Aring if (!info->attrs[NL802154_ATTR_MIN_BE] || 750656a999eSAlexander Aring !info->attrs[NL802154_ATTR_MAX_BE]) 751656a999eSAlexander Aring return -EINVAL; 752656a999eSAlexander Aring 753656a999eSAlexander Aring min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]); 754656a999eSAlexander Aring max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]); 755656a999eSAlexander Aring 756656a999eSAlexander Aring /* check 802.15.4 constraints */ 757fea3318dSAlexander Aring if (min_be < rdev->wpan_phy.supported.min_minbe || 758fea3318dSAlexander Aring min_be > rdev->wpan_phy.supported.max_minbe || 759fea3318dSAlexander Aring max_be < rdev->wpan_phy.supported.min_maxbe || 760fea3318dSAlexander Aring max_be > rdev->wpan_phy.supported.max_maxbe || 761fea3318dSAlexander Aring min_be > max_be) 762656a999eSAlexander Aring return -EINVAL; 763656a999eSAlexander Aring 764656a999eSAlexander Aring return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be); 765656a999eSAlexander Aring } 766656a999eSAlexander Aring 767a01ba765SAlexander Aring static int 768a01ba765SAlexander Aring nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info) 769a01ba765SAlexander Aring { 770a01ba765SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 771a01ba765SAlexander Aring struct net_device *dev = info->user_ptr[1]; 772a01ba765SAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 773a01ba765SAlexander Aring u8 max_csma_backoffs; 774a01ba765SAlexander Aring 775a01ba765SAlexander Aring /* conflict here while other running iface settings */ 776a01ba765SAlexander Aring if (netif_running(dev)) 777a01ba765SAlexander Aring return -EBUSY; 778a01ba765SAlexander Aring 779a01ba765SAlexander Aring if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]) 780a01ba765SAlexander Aring return -EINVAL; 781a01ba765SAlexander Aring 782a01ba765SAlexander Aring max_csma_backoffs = nla_get_u8( 783a01ba765SAlexander Aring info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]); 784a01ba765SAlexander Aring 785a01ba765SAlexander Aring /* check 802.15.4 constraints */ 786fea3318dSAlexander Aring if (max_csma_backoffs < rdev->wpan_phy.supported.min_csma_backoffs || 787fea3318dSAlexander Aring max_csma_backoffs > rdev->wpan_phy.supported.max_csma_backoffs) 788a01ba765SAlexander Aring return -EINVAL; 789a01ba765SAlexander Aring 790a01ba765SAlexander Aring return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs); 791a01ba765SAlexander Aring } 792a01ba765SAlexander Aring 79317a3a46bSAlexander Aring static int 79417a3a46bSAlexander Aring nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info) 79517a3a46bSAlexander Aring { 79617a3a46bSAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 79717a3a46bSAlexander Aring struct net_device *dev = info->user_ptr[1]; 79817a3a46bSAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 79917a3a46bSAlexander Aring s8 max_frame_retries; 80017a3a46bSAlexander Aring 80117a3a46bSAlexander Aring if (netif_running(dev)) 80217a3a46bSAlexander Aring return -EBUSY; 80317a3a46bSAlexander Aring 80417a3a46bSAlexander Aring if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]) 80517a3a46bSAlexander Aring return -EINVAL; 80617a3a46bSAlexander Aring 80717a3a46bSAlexander Aring max_frame_retries = nla_get_s8( 80817a3a46bSAlexander Aring info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]); 80917a3a46bSAlexander Aring 81017a3a46bSAlexander Aring /* check 802.15.4 constraints */ 811fea3318dSAlexander Aring if (max_frame_retries < rdev->wpan_phy.supported.min_frame_retries || 812fea3318dSAlexander Aring max_frame_retries > rdev->wpan_phy.supported.max_frame_retries) 81317a3a46bSAlexander Aring return -EINVAL; 81417a3a46bSAlexander Aring 81517a3a46bSAlexander Aring return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries); 81617a3a46bSAlexander Aring } 81717a3a46bSAlexander Aring 818c8937a1dSAlexander Aring static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info) 819c8937a1dSAlexander Aring { 820c8937a1dSAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 821c8937a1dSAlexander Aring struct net_device *dev = info->user_ptr[1]; 822c8937a1dSAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 823c8937a1dSAlexander Aring bool mode; 824c8937a1dSAlexander Aring 825c8937a1dSAlexander Aring if (netif_running(dev)) 826c8937a1dSAlexander Aring return -EBUSY; 827c8937a1dSAlexander Aring 828c8937a1dSAlexander Aring if (!info->attrs[NL802154_ATTR_LBT_MODE]) 829c8937a1dSAlexander Aring return -EINVAL; 830c8937a1dSAlexander Aring 831c8937a1dSAlexander Aring mode = !!nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]); 832fea3318dSAlexander Aring if (!wpan_phy_supported_bool(mode, rdev->wpan_phy.supported.lbt)) 833fea3318dSAlexander Aring return -EINVAL; 834fea3318dSAlexander Aring 835c8937a1dSAlexander Aring return rdev_set_lbt_mode(rdev, wpan_dev, mode); 836c8937a1dSAlexander Aring } 837c8937a1dSAlexander Aring 83879fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_WPAN_PHY 0x01 83979fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_NETDEV 0x02 84079fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_RTNL 0x04 84179fe1a2aSAlexander Aring #define NL802154_FLAG_CHECK_NETDEV_UP 0x08 84279fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\ 84379fe1a2aSAlexander Aring NL802154_FLAG_CHECK_NETDEV_UP) 84479fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_WPAN_DEV 0x10 84579fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\ 84679fe1a2aSAlexander Aring NL802154_FLAG_CHECK_NETDEV_UP) 84779fe1a2aSAlexander Aring 84879fe1a2aSAlexander Aring static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, 84979fe1a2aSAlexander Aring struct genl_info *info) 85079fe1a2aSAlexander Aring { 85179fe1a2aSAlexander Aring struct cfg802154_registered_device *rdev; 85279fe1a2aSAlexander Aring struct wpan_dev *wpan_dev; 85379fe1a2aSAlexander Aring struct net_device *dev; 85479fe1a2aSAlexander Aring bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL; 85579fe1a2aSAlexander Aring 85679fe1a2aSAlexander Aring if (rtnl) 85779fe1a2aSAlexander Aring rtnl_lock(); 85879fe1a2aSAlexander Aring 85979fe1a2aSAlexander Aring if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) { 86079fe1a2aSAlexander Aring rdev = cfg802154_get_dev_from_info(genl_info_net(info), info); 86179fe1a2aSAlexander Aring if (IS_ERR(rdev)) { 86279fe1a2aSAlexander Aring if (rtnl) 86379fe1a2aSAlexander Aring rtnl_unlock(); 86479fe1a2aSAlexander Aring return PTR_ERR(rdev); 86579fe1a2aSAlexander Aring } 86679fe1a2aSAlexander Aring info->user_ptr[0] = rdev; 86779fe1a2aSAlexander Aring } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV || 86879fe1a2aSAlexander Aring ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) { 86979fe1a2aSAlexander Aring ASSERT_RTNL(); 87079fe1a2aSAlexander Aring wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info), 87179fe1a2aSAlexander Aring info->attrs); 87279fe1a2aSAlexander Aring if (IS_ERR(wpan_dev)) { 87379fe1a2aSAlexander Aring if (rtnl) 87479fe1a2aSAlexander Aring rtnl_unlock(); 87579fe1a2aSAlexander Aring return PTR_ERR(wpan_dev); 87679fe1a2aSAlexander Aring } 87779fe1a2aSAlexander Aring 87879fe1a2aSAlexander Aring dev = wpan_dev->netdev; 87979fe1a2aSAlexander Aring rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy); 88079fe1a2aSAlexander Aring 88179fe1a2aSAlexander Aring if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) { 88279fe1a2aSAlexander Aring if (!dev) { 88379fe1a2aSAlexander Aring if (rtnl) 88479fe1a2aSAlexander Aring rtnl_unlock(); 88579fe1a2aSAlexander Aring return -EINVAL; 88679fe1a2aSAlexander Aring } 88779fe1a2aSAlexander Aring 88879fe1a2aSAlexander Aring info->user_ptr[1] = dev; 88979fe1a2aSAlexander Aring } else { 89079fe1a2aSAlexander Aring info->user_ptr[1] = wpan_dev; 89179fe1a2aSAlexander Aring } 89279fe1a2aSAlexander Aring 89379fe1a2aSAlexander Aring if (dev) { 89479fe1a2aSAlexander Aring if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP && 89579fe1a2aSAlexander Aring !netif_running(dev)) { 89679fe1a2aSAlexander Aring if (rtnl) 89779fe1a2aSAlexander Aring rtnl_unlock(); 89879fe1a2aSAlexander Aring return -ENETDOWN; 89979fe1a2aSAlexander Aring } 90079fe1a2aSAlexander Aring 90179fe1a2aSAlexander Aring dev_hold(dev); 90279fe1a2aSAlexander Aring } 90379fe1a2aSAlexander Aring 90479fe1a2aSAlexander Aring info->user_ptr[0] = rdev; 90579fe1a2aSAlexander Aring } 90679fe1a2aSAlexander Aring 90779fe1a2aSAlexander Aring return 0; 90879fe1a2aSAlexander Aring } 90979fe1a2aSAlexander Aring 91079fe1a2aSAlexander Aring static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb, 91179fe1a2aSAlexander Aring struct genl_info *info) 91279fe1a2aSAlexander Aring { 91379fe1a2aSAlexander Aring if (info->user_ptr[1]) { 91479fe1a2aSAlexander Aring if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) { 91579fe1a2aSAlexander Aring struct wpan_dev *wpan_dev = info->user_ptr[1]; 91679fe1a2aSAlexander Aring 91779fe1a2aSAlexander Aring if (wpan_dev->netdev) 91879fe1a2aSAlexander Aring dev_put(wpan_dev->netdev); 91979fe1a2aSAlexander Aring } else { 92079fe1a2aSAlexander Aring dev_put(info->user_ptr[1]); 92179fe1a2aSAlexander Aring } 92279fe1a2aSAlexander Aring } 92379fe1a2aSAlexander Aring 92479fe1a2aSAlexander Aring if (ops->internal_flags & NL802154_FLAG_NEED_RTNL) 92579fe1a2aSAlexander Aring rtnl_unlock(); 92679fe1a2aSAlexander Aring } 92779fe1a2aSAlexander Aring 92879fe1a2aSAlexander Aring static const struct genl_ops nl802154_ops[] = { 929ca20ce20SAlexander Aring { 930ca20ce20SAlexander Aring .cmd = NL802154_CMD_GET_WPAN_PHY, 931ca20ce20SAlexander Aring .doit = nl802154_get_wpan_phy, 932ca20ce20SAlexander Aring .dumpit = nl802154_dump_wpan_phy, 933ca20ce20SAlexander Aring .done = nl802154_dump_wpan_phy_done, 934ca20ce20SAlexander Aring .policy = nl802154_policy, 935ca20ce20SAlexander Aring /* can be retrieved by unprivileged users */ 936ca20ce20SAlexander Aring .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | 937ca20ce20SAlexander Aring NL802154_FLAG_NEED_RTNL, 938ca20ce20SAlexander Aring }, 9394b96aea0SAlexander Aring { 9404b96aea0SAlexander Aring .cmd = NL802154_CMD_GET_INTERFACE, 9414b96aea0SAlexander Aring .doit = nl802154_get_interface, 9424b96aea0SAlexander Aring .dumpit = nl802154_dump_interface, 9434b96aea0SAlexander Aring .policy = nl802154_policy, 9444b96aea0SAlexander Aring /* can be retrieved by unprivileged users */ 9454b96aea0SAlexander Aring .internal_flags = NL802154_FLAG_NEED_WPAN_DEV | 9464b96aea0SAlexander Aring NL802154_FLAG_NEED_RTNL, 9474b96aea0SAlexander Aring }, 948ab0bd561SAlexander Aring { 949f3ea5e44SAlexander Aring .cmd = NL802154_CMD_NEW_INTERFACE, 950f3ea5e44SAlexander Aring .doit = nl802154_new_interface, 951f3ea5e44SAlexander Aring .policy = nl802154_policy, 952f3ea5e44SAlexander Aring .flags = GENL_ADMIN_PERM, 953f3ea5e44SAlexander Aring .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | 954f3ea5e44SAlexander Aring NL802154_FLAG_NEED_RTNL, 955f3ea5e44SAlexander Aring }, 956f3ea5e44SAlexander Aring { 957b821ecd4SAlexander Aring .cmd = NL802154_CMD_DEL_INTERFACE, 958b821ecd4SAlexander Aring .doit = nl802154_del_interface, 959b821ecd4SAlexander Aring .policy = nl802154_policy, 960b821ecd4SAlexander Aring .flags = GENL_ADMIN_PERM, 961b821ecd4SAlexander Aring .internal_flags = NL802154_FLAG_NEED_WPAN_DEV | 962b821ecd4SAlexander Aring NL802154_FLAG_NEED_RTNL, 963b821ecd4SAlexander Aring }, 964b821ecd4SAlexander Aring { 965ab0bd561SAlexander Aring .cmd = NL802154_CMD_SET_CHANNEL, 966ab0bd561SAlexander Aring .doit = nl802154_set_channel, 967ab0bd561SAlexander Aring .policy = nl802154_policy, 968ab0bd561SAlexander Aring .flags = GENL_ADMIN_PERM, 969ab0bd561SAlexander Aring .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | 970ab0bd561SAlexander Aring NL802154_FLAG_NEED_RTNL, 971ab0bd561SAlexander Aring }, 972702bf371SAlexander Aring { 973ba2a9506SAlexander Aring .cmd = NL802154_CMD_SET_CCA_MODE, 974ba2a9506SAlexander Aring .doit = nl802154_set_cca_mode, 975ba2a9506SAlexander Aring .policy = nl802154_policy, 976ba2a9506SAlexander Aring .flags = GENL_ADMIN_PERM, 977ba2a9506SAlexander Aring .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | 978ba2a9506SAlexander Aring NL802154_FLAG_NEED_RTNL, 979ba2a9506SAlexander Aring }, 980ba2a9506SAlexander Aring { 981702bf371SAlexander Aring .cmd = NL802154_CMD_SET_PAN_ID, 982702bf371SAlexander Aring .doit = nl802154_set_pan_id, 983702bf371SAlexander Aring .policy = nl802154_policy, 984702bf371SAlexander Aring .flags = GENL_ADMIN_PERM, 985702bf371SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 986702bf371SAlexander Aring NL802154_FLAG_NEED_RTNL, 987702bf371SAlexander Aring }, 9889830c62aSAlexander Aring { 9899830c62aSAlexander Aring .cmd = NL802154_CMD_SET_SHORT_ADDR, 9909830c62aSAlexander Aring .doit = nl802154_set_short_addr, 9919830c62aSAlexander Aring .policy = nl802154_policy, 9929830c62aSAlexander Aring .flags = GENL_ADMIN_PERM, 9939830c62aSAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 9949830c62aSAlexander Aring NL802154_FLAG_NEED_RTNL, 9959830c62aSAlexander Aring }, 996656a999eSAlexander Aring { 997656a999eSAlexander Aring .cmd = NL802154_CMD_SET_BACKOFF_EXPONENT, 998656a999eSAlexander Aring .doit = nl802154_set_backoff_exponent, 999656a999eSAlexander Aring .policy = nl802154_policy, 1000656a999eSAlexander Aring .flags = GENL_ADMIN_PERM, 1001656a999eSAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 1002656a999eSAlexander Aring NL802154_FLAG_NEED_RTNL, 1003656a999eSAlexander Aring }, 1004a01ba765SAlexander Aring { 1005a01ba765SAlexander Aring .cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS, 1006a01ba765SAlexander Aring .doit = nl802154_set_max_csma_backoffs, 1007a01ba765SAlexander Aring .policy = nl802154_policy, 1008a01ba765SAlexander Aring .flags = GENL_ADMIN_PERM, 1009a01ba765SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 1010a01ba765SAlexander Aring NL802154_FLAG_NEED_RTNL, 1011a01ba765SAlexander Aring }, 101217a3a46bSAlexander Aring { 101317a3a46bSAlexander Aring .cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES, 101417a3a46bSAlexander Aring .doit = nl802154_set_max_frame_retries, 101517a3a46bSAlexander Aring .policy = nl802154_policy, 101617a3a46bSAlexander Aring .flags = GENL_ADMIN_PERM, 101717a3a46bSAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 101817a3a46bSAlexander Aring NL802154_FLAG_NEED_RTNL, 101917a3a46bSAlexander Aring }, 1020c8937a1dSAlexander Aring { 1021c8937a1dSAlexander Aring .cmd = NL802154_CMD_SET_LBT_MODE, 1022c8937a1dSAlexander Aring .doit = nl802154_set_lbt_mode, 1023c8937a1dSAlexander Aring .policy = nl802154_policy, 1024c8937a1dSAlexander Aring .flags = GENL_ADMIN_PERM, 1025c8937a1dSAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 1026c8937a1dSAlexander Aring NL802154_FLAG_NEED_RTNL, 1027c8937a1dSAlexander Aring }, 102879fe1a2aSAlexander Aring }; 102979fe1a2aSAlexander Aring 103079fe1a2aSAlexander Aring /* initialisation/exit functions */ 103179fe1a2aSAlexander Aring int nl802154_init(void) 103279fe1a2aSAlexander Aring { 103379fe1a2aSAlexander Aring return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops, 103479fe1a2aSAlexander Aring nl802154_mcgrps); 103579fe1a2aSAlexander Aring } 103679fe1a2aSAlexander Aring 103779fe1a2aSAlexander Aring void nl802154_exit(void) 103879fe1a2aSAlexander Aring { 103979fe1a2aSAlexander Aring genl_unregister_family(&nl802154_fam); 104079fe1a2aSAlexander Aring } 1041