xref: /openbmc/linux/net/ieee802154/nl802154.c (revision edea8f7c75ec6c238130bd7e74d9f6f4c26e97b0)
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