xref: /openbmc/linux/net/ieee802154/nl802154.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21802d0beSThomas Gleixner /*
379fe1a2aSAlexander Aring  *
479fe1a2aSAlexander Aring  * Authors:
579fe1a2aSAlexander Aring  * Alexander Aring <aar@pengutronix.de>
679fe1a2aSAlexander Aring  *
779fe1a2aSAlexander Aring  * Based on: net/wireless/nl80211.c
879fe1a2aSAlexander Aring  */
979fe1a2aSAlexander Aring 
1079fe1a2aSAlexander Aring #include <linux/rtnetlink.h>
1179fe1a2aSAlexander Aring 
1279fe1a2aSAlexander Aring #include <net/cfg802154.h>
1379fe1a2aSAlexander Aring #include <net/genetlink.h>
1479fe1a2aSAlexander Aring #include <net/mac802154.h>
1579fe1a2aSAlexander Aring #include <net/netlink.h>
1679fe1a2aSAlexander Aring #include <net/nl802154.h>
1779fe1a2aSAlexander Aring #include <net/sock.h>
1879fe1a2aSAlexander Aring 
1979fe1a2aSAlexander Aring #include "nl802154.h"
20ab0bd561SAlexander Aring #include "rdev-ops.h"
2179fe1a2aSAlexander Aring #include "core.h"
2279fe1a2aSAlexander Aring 
2379fe1a2aSAlexander Aring /* the netlink family */
24489111e5SJohannes Berg static struct genl_family nl802154_fam;
2579fe1a2aSAlexander Aring 
2679fe1a2aSAlexander Aring /* multicast groups */
2779fe1a2aSAlexander Aring enum nl802154_multicast_groups {
2879fe1a2aSAlexander Aring 	NL802154_MCGRP_CONFIG,
2951147284SMiquel Raynal 	NL802154_MCGRP_SCAN,
3079fe1a2aSAlexander Aring };
3179fe1a2aSAlexander Aring 
3279fe1a2aSAlexander Aring static const struct genl_multicast_group nl802154_mcgrps[] = {
3379fe1a2aSAlexander Aring 	[NL802154_MCGRP_CONFIG] = { .name = "config", },
3451147284SMiquel Raynal 	[NL802154_MCGRP_SCAN] = { .name = "scan", },
3579fe1a2aSAlexander Aring };
3679fe1a2aSAlexander Aring 
3779fe1a2aSAlexander Aring /* returns ERR_PTR values */
3879fe1a2aSAlexander Aring static struct wpan_dev *
__cfg802154_wpan_dev_from_attrs(struct net * netns,struct nlattr ** attrs)3979fe1a2aSAlexander Aring __cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs)
4079fe1a2aSAlexander Aring {
4179fe1a2aSAlexander Aring 	struct cfg802154_registered_device *rdev;
4279fe1a2aSAlexander Aring 	struct wpan_dev *result = NULL;
4379fe1a2aSAlexander Aring 	bool have_ifidx = attrs[NL802154_ATTR_IFINDEX];
4479fe1a2aSAlexander Aring 	bool have_wpan_dev_id = attrs[NL802154_ATTR_WPAN_DEV];
4579fe1a2aSAlexander Aring 	u64 wpan_dev_id;
4679fe1a2aSAlexander Aring 	int wpan_phy_idx = -1;
4779fe1a2aSAlexander Aring 	int ifidx = -1;
4879fe1a2aSAlexander Aring 
4979fe1a2aSAlexander Aring 	ASSERT_RTNL();
5079fe1a2aSAlexander Aring 
5179fe1a2aSAlexander Aring 	if (!have_ifidx && !have_wpan_dev_id)
5279fe1a2aSAlexander Aring 		return ERR_PTR(-EINVAL);
5379fe1a2aSAlexander Aring 
5479fe1a2aSAlexander Aring 	if (have_ifidx)
5579fe1a2aSAlexander Aring 		ifidx = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
5679fe1a2aSAlexander Aring 	if (have_wpan_dev_id) {
5779fe1a2aSAlexander Aring 		wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
5879fe1a2aSAlexander Aring 		wpan_phy_idx = wpan_dev_id >> 32;
5979fe1a2aSAlexander Aring 	}
6079fe1a2aSAlexander Aring 
6179fe1a2aSAlexander Aring 	list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
6279fe1a2aSAlexander Aring 		struct wpan_dev *wpan_dev;
6379fe1a2aSAlexander Aring 
6466e5c267SAlexander Aring 		if (wpan_phy_net(&rdev->wpan_phy) != netns)
6566e5c267SAlexander Aring 			continue;
6679fe1a2aSAlexander Aring 
6779fe1a2aSAlexander Aring 		if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx)
6879fe1a2aSAlexander Aring 			continue;
6979fe1a2aSAlexander Aring 
7079fe1a2aSAlexander Aring 		list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
7179fe1a2aSAlexander Aring 			if (have_ifidx && wpan_dev->netdev &&
7279fe1a2aSAlexander Aring 			    wpan_dev->netdev->ifindex == ifidx) {
7379fe1a2aSAlexander Aring 				result = wpan_dev;
7479fe1a2aSAlexander Aring 				break;
7579fe1a2aSAlexander Aring 			}
7679fe1a2aSAlexander Aring 			if (have_wpan_dev_id &&
7779fe1a2aSAlexander Aring 			    wpan_dev->identifier == (u32)wpan_dev_id) {
7879fe1a2aSAlexander Aring 				result = wpan_dev;
7979fe1a2aSAlexander Aring 				break;
8079fe1a2aSAlexander Aring 			}
8179fe1a2aSAlexander Aring 		}
8279fe1a2aSAlexander Aring 
8379fe1a2aSAlexander Aring 		if (result)
8479fe1a2aSAlexander Aring 			break;
8579fe1a2aSAlexander Aring 	}
8679fe1a2aSAlexander Aring 
8779fe1a2aSAlexander Aring 	if (result)
8879fe1a2aSAlexander Aring 		return result;
8979fe1a2aSAlexander Aring 
9079fe1a2aSAlexander Aring 	return ERR_PTR(-ENODEV);
9179fe1a2aSAlexander Aring }
9279fe1a2aSAlexander Aring 
9379fe1a2aSAlexander Aring static struct cfg802154_registered_device *
__cfg802154_rdev_from_attrs(struct net * netns,struct nlattr ** attrs)9479fe1a2aSAlexander Aring __cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
9579fe1a2aSAlexander Aring {
9679fe1a2aSAlexander Aring 	struct cfg802154_registered_device *rdev = NULL, *tmp;
9779fe1a2aSAlexander Aring 	struct net_device *netdev;
9879fe1a2aSAlexander Aring 
9979fe1a2aSAlexander Aring 	ASSERT_RTNL();
10079fe1a2aSAlexander Aring 
10179fe1a2aSAlexander Aring 	if (!attrs[NL802154_ATTR_WPAN_PHY] &&
10279fe1a2aSAlexander Aring 	    !attrs[NL802154_ATTR_IFINDEX] &&
10379fe1a2aSAlexander Aring 	    !attrs[NL802154_ATTR_WPAN_DEV])
10479fe1a2aSAlexander Aring 		return ERR_PTR(-EINVAL);
10579fe1a2aSAlexander Aring 
10679fe1a2aSAlexander Aring 	if (attrs[NL802154_ATTR_WPAN_PHY])
10779fe1a2aSAlexander Aring 		rdev = cfg802154_rdev_by_wpan_phy_idx(
10879fe1a2aSAlexander Aring 				nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY]));
10979fe1a2aSAlexander Aring 
11079fe1a2aSAlexander Aring 	if (attrs[NL802154_ATTR_WPAN_DEV]) {
11179fe1a2aSAlexander Aring 		u64 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
11279fe1a2aSAlexander Aring 		struct wpan_dev *wpan_dev;
11379fe1a2aSAlexander Aring 		bool found = false;
11479fe1a2aSAlexander Aring 
11579fe1a2aSAlexander Aring 		tmp = cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id >> 32);
11679fe1a2aSAlexander Aring 		if (tmp) {
11779fe1a2aSAlexander Aring 			/* make sure wpan_dev exists */
11879fe1a2aSAlexander Aring 			list_for_each_entry(wpan_dev, &tmp->wpan_dev_list, list) {
11979fe1a2aSAlexander Aring 				if (wpan_dev->identifier != (u32)wpan_dev_id)
12079fe1a2aSAlexander Aring 					continue;
12179fe1a2aSAlexander Aring 				found = true;
12279fe1a2aSAlexander Aring 				break;
12379fe1a2aSAlexander Aring 			}
12479fe1a2aSAlexander Aring 
12579fe1a2aSAlexander Aring 			if (!found)
12679fe1a2aSAlexander Aring 				tmp = NULL;
12779fe1a2aSAlexander Aring 
12879fe1a2aSAlexander Aring 			if (rdev && tmp != rdev)
12979fe1a2aSAlexander Aring 				return ERR_PTR(-EINVAL);
13079fe1a2aSAlexander Aring 			rdev = tmp;
13179fe1a2aSAlexander Aring 		}
13279fe1a2aSAlexander Aring 	}
13379fe1a2aSAlexander Aring 
13479fe1a2aSAlexander Aring 	if (attrs[NL802154_ATTR_IFINDEX]) {
13579fe1a2aSAlexander Aring 		int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
13679fe1a2aSAlexander Aring 
13779fe1a2aSAlexander Aring 		netdev = __dev_get_by_index(netns, ifindex);
13879fe1a2aSAlexander Aring 		if (netdev) {
13979fe1a2aSAlexander Aring 			if (netdev->ieee802154_ptr)
14079fe1a2aSAlexander Aring 				tmp = wpan_phy_to_rdev(
14179fe1a2aSAlexander Aring 						netdev->ieee802154_ptr->wpan_phy);
14279fe1a2aSAlexander Aring 			else
14379fe1a2aSAlexander Aring 				tmp = NULL;
14479fe1a2aSAlexander Aring 
14579fe1a2aSAlexander Aring 			/* not wireless device -- return error */
14679fe1a2aSAlexander Aring 			if (!tmp)
14779fe1a2aSAlexander Aring 				return ERR_PTR(-EINVAL);
14879fe1a2aSAlexander Aring 
14979fe1a2aSAlexander Aring 			/* mismatch -- return error */
15079fe1a2aSAlexander Aring 			if (rdev && tmp != rdev)
15179fe1a2aSAlexander Aring 				return ERR_PTR(-EINVAL);
15279fe1a2aSAlexander Aring 
15379fe1a2aSAlexander Aring 			rdev = tmp;
15479fe1a2aSAlexander Aring 		}
15579fe1a2aSAlexander Aring 	}
15679fe1a2aSAlexander Aring 
15779fe1a2aSAlexander Aring 	if (!rdev)
15879fe1a2aSAlexander Aring 		return ERR_PTR(-ENODEV);
15979fe1a2aSAlexander Aring 
16066e5c267SAlexander Aring 	if (netns != wpan_phy_net(&rdev->wpan_phy))
16166e5c267SAlexander Aring 		return ERR_PTR(-ENODEV);
16279fe1a2aSAlexander Aring 
16379fe1a2aSAlexander Aring 	return rdev;
16479fe1a2aSAlexander Aring }
16579fe1a2aSAlexander Aring 
16679fe1a2aSAlexander Aring /* This function returns a pointer to the driver
16779fe1a2aSAlexander Aring  * that the genl_info item that is passed refers to.
16879fe1a2aSAlexander Aring  *
16979fe1a2aSAlexander Aring  * The result of this can be a PTR_ERR and hence must
17079fe1a2aSAlexander Aring  * be checked with IS_ERR() for errors.
17179fe1a2aSAlexander Aring  */
17279fe1a2aSAlexander Aring static struct cfg802154_registered_device *
cfg802154_get_dev_from_info(struct net * netns,struct genl_info * info)17379fe1a2aSAlexander Aring cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info)
17479fe1a2aSAlexander Aring {
17579fe1a2aSAlexander Aring 	return __cfg802154_rdev_from_attrs(netns, info->attrs);
17679fe1a2aSAlexander Aring }
17779fe1a2aSAlexander Aring 
17879fe1a2aSAlexander Aring /* policy for the attributes */
17979fe1a2aSAlexander Aring static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
180ca20ce20SAlexander Aring 	[NL802154_ATTR_WPAN_PHY] = { .type = NLA_U32 },
181ca20ce20SAlexander Aring 	[NL802154_ATTR_WPAN_PHY_NAME] = { .type = NLA_NUL_STRING,
182ca20ce20SAlexander Aring 					  .len = 20-1 },
183ca20ce20SAlexander Aring 
184ca20ce20SAlexander Aring 	[NL802154_ATTR_IFINDEX] = { .type = NLA_U32 },
1854b96aea0SAlexander Aring 	[NL802154_ATTR_IFTYPE] = { .type = NLA_U32 },
1864b96aea0SAlexander Aring 	[NL802154_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
187ca20ce20SAlexander Aring 
188ca20ce20SAlexander Aring 	[NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 },
189ca20ce20SAlexander Aring 
190648324c9SMiquel Raynal 	[NL802154_ATTR_PAGE] = NLA_POLICY_MAX(NLA_U8, IEEE802154_MAX_PAGE),
191648324c9SMiquel Raynal 	[NL802154_ATTR_CHANNEL] = NLA_POLICY_MAX(NLA_U8, IEEE802154_MAX_CHANNEL),
192ca20ce20SAlexander Aring 
1931a19cb68SAlexander Aring 	[NL802154_ATTR_TX_POWER] = { .type = NLA_S32, },
194ca20ce20SAlexander Aring 
195ba2a9506SAlexander Aring 	[NL802154_ATTR_CCA_MODE] = { .type = NLA_U32, },
196ba2a9506SAlexander Aring 	[NL802154_ATTR_CCA_OPT] = { .type = NLA_U32, },
197e4390592SAlexander Aring 	[NL802154_ATTR_CCA_ED_LEVEL] = { .type = NLA_S32, },
198ca20ce20SAlexander Aring 
199ca20ce20SAlexander Aring 	[NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, },
2004b96aea0SAlexander Aring 
2014b96aea0SAlexander Aring 	[NL802154_ATTR_PAN_ID] = { .type = NLA_U16, },
2024b96aea0SAlexander Aring 	[NL802154_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
2034b96aea0SAlexander Aring 	[NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, },
2044b96aea0SAlexander Aring 
2054b96aea0SAlexander Aring 	[NL802154_ATTR_MIN_BE] = { .type = NLA_U8, },
2064b96aea0SAlexander Aring 	[NL802154_ATTR_MAX_BE] = { .type = NLA_U8, },
2074b96aea0SAlexander Aring 	[NL802154_ATTR_MAX_CSMA_BACKOFFS] = { .type = NLA_U8, },
2084b96aea0SAlexander Aring 
2094b96aea0SAlexander Aring 	[NL802154_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_S8, },
2104b96aea0SAlexander Aring 
2114b96aea0SAlexander Aring 	[NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, },
2120e665457SAlexander Aring 
2130e665457SAlexander Aring 	[NL802154_ATTR_WPAN_PHY_CAPS] = { .type = NLA_NESTED },
214133be026SVarka Bhadram 
215133be026SVarka Bhadram 	[NL802154_ATTR_SUPPORTED_COMMANDS] = { .type = NLA_NESTED },
216c91208d8SAlexander Aring 
217c91208d8SAlexander Aring 	[NL802154_ATTR_ACKREQ_DEFAULT] = { .type = NLA_U8 },
218a26c5fd7SAlexander Aring 
21966e5c267SAlexander Aring 	[NL802154_ATTR_PID] = { .type = NLA_U32 },
22066e5c267SAlexander Aring 	[NL802154_ATTR_NETNS_FD] = { .type = NLA_U32 },
22151147284SMiquel Raynal 
22251147284SMiquel Raynal 	[NL802154_ATTR_COORDINATOR] = { .type = NLA_NESTED },
22351147284SMiquel Raynal 
224648324c9SMiquel Raynal 	[NL802154_ATTR_SCAN_TYPE] =
225648324c9SMiquel Raynal 		NLA_POLICY_RANGE(NLA_U8, NL802154_SCAN_ED, NL802154_SCAN_RIT_PASSIVE),
226648324c9SMiquel Raynal 	[NL802154_ATTR_SCAN_CHANNELS] =
227648324c9SMiquel Raynal 		NLA_POLICY_MASK(NLA_U32, GENMASK(IEEE802154_MAX_CHANNEL, 0)),
228648324c9SMiquel Raynal 	[NL802154_ATTR_SCAN_PREAMBLE_CODES] = { .type = NLA_REJECT },
229648324c9SMiquel Raynal 	[NL802154_ATTR_SCAN_MEAN_PRF] = { .type = NLA_REJECT },
230648324c9SMiquel Raynal 	[NL802154_ATTR_SCAN_DURATION] =
231648324c9SMiquel Raynal 		NLA_POLICY_MAX(NLA_U8, IEEE802154_MAX_SCAN_DURATION),
232648324c9SMiquel Raynal 	[NL802154_ATTR_SCAN_DONE_REASON] =
233648324c9SMiquel Raynal 		NLA_POLICY_RANGE(NLA_U8, NL802154_SCAN_DONE_REASON_FINISHED,
234648324c9SMiquel Raynal 				 NL802154_SCAN_DONE_REASON_ABORTED),
235648324c9SMiquel Raynal 	[NL802154_ATTR_BEACON_INTERVAL] =
23626f88e4eSMiquel Raynal 		NLA_POLICY_MAX(NLA_U8, IEEE802154_ACTIVE_SCAN_DURATION),
237ed3557c9SMiquel Raynal 
238a26c5fd7SAlexander Aring #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
239a26c5fd7SAlexander Aring 	[NL802154_ATTR_SEC_ENABLED] = { .type = NLA_U8, },
240a26c5fd7SAlexander Aring 	[NL802154_ATTR_SEC_OUT_LEVEL] = { .type = NLA_U32, },
241a26c5fd7SAlexander Aring 	[NL802154_ATTR_SEC_OUT_KEY_ID] = { .type = NLA_NESTED, },
242a26c5fd7SAlexander Aring 	[NL802154_ATTR_SEC_FRAME_COUNTER] = { .type = NLA_U32 },
243a26c5fd7SAlexander Aring 
244a26c5fd7SAlexander Aring 	[NL802154_ATTR_SEC_LEVEL] = { .type = NLA_NESTED },
245a26c5fd7SAlexander Aring 	[NL802154_ATTR_SEC_DEVICE] = { .type = NLA_NESTED },
246a26c5fd7SAlexander Aring 	[NL802154_ATTR_SEC_DEVKEY] = { .type = NLA_NESTED },
247a26c5fd7SAlexander Aring 	[NL802154_ATTR_SEC_KEY] = { .type = NLA_NESTED },
248a26c5fd7SAlexander Aring #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
24979fe1a2aSAlexander Aring };
25079fe1a2aSAlexander Aring 
251a26c5fd7SAlexander Aring #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
252a26c5fd7SAlexander Aring static int
nl802154_prepare_wpan_dev_dump(struct sk_buff * skb,struct netlink_callback * cb,struct cfg802154_registered_device ** rdev,struct wpan_dev ** wpan_dev)253a26c5fd7SAlexander Aring nl802154_prepare_wpan_dev_dump(struct sk_buff *skb,
254a26c5fd7SAlexander Aring 			       struct netlink_callback *cb,
255a26c5fd7SAlexander Aring 			       struct cfg802154_registered_device **rdev,
256a26c5fd7SAlexander Aring 			       struct wpan_dev **wpan_dev)
257a26c5fd7SAlexander Aring {
25875cdbdd0SJiri Pirko 	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
259a26c5fd7SAlexander Aring 	int err;
260a26c5fd7SAlexander Aring 
261a26c5fd7SAlexander Aring 	rtnl_lock();
262a26c5fd7SAlexander Aring 
263a26c5fd7SAlexander Aring 	if (!cb->args[0]) {
264a26c5fd7SAlexander Aring 		*wpan_dev = __cfg802154_wpan_dev_from_attrs(sock_net(skb->sk),
265*7288dd2fSJakub Kicinski 							    info->info.attrs);
266a26c5fd7SAlexander Aring 		if (IS_ERR(*wpan_dev)) {
267a26c5fd7SAlexander Aring 			err = PTR_ERR(*wpan_dev);
268a26c5fd7SAlexander Aring 			goto out_unlock;
269a26c5fd7SAlexander Aring 		}
270a26c5fd7SAlexander Aring 		*rdev = wpan_phy_to_rdev((*wpan_dev)->wpan_phy);
271a26c5fd7SAlexander Aring 		/* 0 is the first index - add 1 to parse only once */
272a26c5fd7SAlexander Aring 		cb->args[0] = (*rdev)->wpan_phy_idx + 1;
273a26c5fd7SAlexander Aring 		cb->args[1] = (*wpan_dev)->identifier;
274a26c5fd7SAlexander Aring 	} else {
275a26c5fd7SAlexander Aring 		/* subtract the 1 again here */
276a26c5fd7SAlexander Aring 		struct wpan_phy *wpan_phy = wpan_phy_idx_to_wpan_phy(cb->args[0] - 1);
277a26c5fd7SAlexander Aring 		struct wpan_dev *tmp;
278a26c5fd7SAlexander Aring 
279a26c5fd7SAlexander Aring 		if (!wpan_phy) {
280a26c5fd7SAlexander Aring 			err = -ENODEV;
281a26c5fd7SAlexander Aring 			goto out_unlock;
282a26c5fd7SAlexander Aring 		}
283a26c5fd7SAlexander Aring 		*rdev = wpan_phy_to_rdev(wpan_phy);
284a26c5fd7SAlexander Aring 		*wpan_dev = NULL;
285a26c5fd7SAlexander Aring 
286a26c5fd7SAlexander Aring 		list_for_each_entry(tmp, &(*rdev)->wpan_dev_list, list) {
287a26c5fd7SAlexander Aring 			if (tmp->identifier == cb->args[1]) {
288a26c5fd7SAlexander Aring 				*wpan_dev = tmp;
289a26c5fd7SAlexander Aring 				break;
290a26c5fd7SAlexander Aring 			}
291a26c5fd7SAlexander Aring 		}
292a26c5fd7SAlexander Aring 
293a26c5fd7SAlexander Aring 		if (!*wpan_dev) {
294a26c5fd7SAlexander Aring 			err = -ENODEV;
295a26c5fd7SAlexander Aring 			goto out_unlock;
296a26c5fd7SAlexander Aring 		}
297a26c5fd7SAlexander Aring 	}
298a26c5fd7SAlexander Aring 
299a26c5fd7SAlexander Aring 	return 0;
300a26c5fd7SAlexander Aring  out_unlock:
301a26c5fd7SAlexander Aring 	rtnl_unlock();
302a26c5fd7SAlexander Aring 	return err;
303a26c5fd7SAlexander Aring }
304a26c5fd7SAlexander Aring 
305a26c5fd7SAlexander Aring static void
nl802154_finish_wpan_dev_dump(struct cfg802154_registered_device * rdev)306a26c5fd7SAlexander Aring nl802154_finish_wpan_dev_dump(struct cfg802154_registered_device *rdev)
307a26c5fd7SAlexander Aring {
308a26c5fd7SAlexander Aring 	rtnl_unlock();
309a26c5fd7SAlexander Aring }
310a26c5fd7SAlexander Aring #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
311a26c5fd7SAlexander Aring 
31279fe1a2aSAlexander Aring /* message building helper */
nl802154hdr_put(struct sk_buff * skb,u32 portid,u32 seq,int flags,u8 cmd)31379fe1a2aSAlexander Aring static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
31479fe1a2aSAlexander Aring 				    int flags, u8 cmd)
31579fe1a2aSAlexander Aring {
31679fe1a2aSAlexander Aring 	/* since there is no private header just add the generic one */
31779fe1a2aSAlexander Aring 	return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd);
31879fe1a2aSAlexander Aring }
31979fe1a2aSAlexander Aring 
320ca20ce20SAlexander Aring static int
nl802154_put_flags(struct sk_buff * msg,int attr,u32 mask)3210e665457SAlexander Aring nl802154_put_flags(struct sk_buff *msg, int attr, u32 mask)
3220e665457SAlexander Aring {
323ae0be8deSMichal Kubecek 	struct nlattr *nl_flags = nla_nest_start_noflag(msg, attr);
3240e665457SAlexander Aring 	int i;
3250e665457SAlexander Aring 
3260e665457SAlexander Aring 	if (!nl_flags)
3270e665457SAlexander Aring 		return -ENOBUFS;
3280e665457SAlexander Aring 
3290e665457SAlexander Aring 	i = 0;
3300e665457SAlexander Aring 	while (mask) {
3310e665457SAlexander Aring 		if ((mask & 1) && nla_put_flag(msg, i))
3320e665457SAlexander Aring 			return -ENOBUFS;
3330e665457SAlexander Aring 
3340e665457SAlexander Aring 		mask >>= 1;
3350e665457SAlexander Aring 		i++;
3360e665457SAlexander Aring 	}
3370e665457SAlexander Aring 
3380e665457SAlexander Aring 	nla_nest_end(msg, nl_flags);
3390e665457SAlexander Aring 	return 0;
3400e665457SAlexander Aring }
3410e665457SAlexander Aring 
3420e665457SAlexander Aring static int
nl802154_send_wpan_phy_channels(struct cfg802154_registered_device * rdev,struct sk_buff * msg)343ca20ce20SAlexander Aring nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev,
344ca20ce20SAlexander Aring 				struct sk_buff *msg)
345ca20ce20SAlexander Aring {
346ca20ce20SAlexander Aring 	struct nlattr *nl_page;
347ca20ce20SAlexander Aring 	unsigned long page;
348ca20ce20SAlexander Aring 
349ae0be8deSMichal Kubecek 	nl_page = nla_nest_start_noflag(msg, NL802154_ATTR_CHANNELS_SUPPORTED);
350ca20ce20SAlexander Aring 	if (!nl_page)
351ca20ce20SAlexander Aring 		return -ENOBUFS;
352ca20ce20SAlexander Aring 
353cb41c8ddSAlexander Aring 	for (page = 0; page <= IEEE802154_MAX_PAGE; page++) {
354ca20ce20SAlexander Aring 		if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL,
35572f655e4SAlexander Aring 				rdev->wpan_phy.supported.channels[page]))
356ca20ce20SAlexander Aring 			return -ENOBUFS;
357ca20ce20SAlexander Aring 	}
358ca20ce20SAlexander Aring 	nla_nest_end(msg, nl_page);
359ca20ce20SAlexander Aring 
360ca20ce20SAlexander Aring 	return 0;
361ca20ce20SAlexander Aring }
362ca20ce20SAlexander Aring 
3630e665457SAlexander Aring static int
nl802154_put_capabilities(struct sk_buff * msg,struct cfg802154_registered_device * rdev)3640e665457SAlexander Aring nl802154_put_capabilities(struct sk_buff *msg,
3650e665457SAlexander Aring 			  struct cfg802154_registered_device *rdev)
3660e665457SAlexander Aring {
3670e665457SAlexander Aring 	const struct wpan_phy_supported *caps = &rdev->wpan_phy.supported;
3680e665457SAlexander Aring 	struct nlattr *nl_caps, *nl_channels;
3690e665457SAlexander Aring 	int i;
3700e665457SAlexander Aring 
371ae0be8deSMichal Kubecek 	nl_caps = nla_nest_start_noflag(msg, NL802154_ATTR_WPAN_PHY_CAPS);
3720e665457SAlexander Aring 	if (!nl_caps)
3730e665457SAlexander Aring 		return -ENOBUFS;
3740e665457SAlexander Aring 
375ae0be8deSMichal Kubecek 	nl_channels = nla_nest_start_noflag(msg, NL802154_CAP_ATTR_CHANNELS);
3760e665457SAlexander Aring 	if (!nl_channels)
3770e665457SAlexander Aring 		return -ENOBUFS;
3780e665457SAlexander Aring 
3790e665457SAlexander Aring 	for (i = 0; i <= IEEE802154_MAX_PAGE; i++) {
3800e665457SAlexander Aring 		if (caps->channels[i]) {
3810e665457SAlexander Aring 			if (nl802154_put_flags(msg, i, caps->channels[i]))
3820e665457SAlexander Aring 				return -ENOBUFS;
3830e665457SAlexander Aring 		}
3840e665457SAlexander Aring 	}
3850e665457SAlexander Aring 
3860e665457SAlexander Aring 	nla_nest_end(msg, nl_channels);
3870e665457SAlexander Aring 
3880e665457SAlexander Aring 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_ED_LEVEL) {
3890e665457SAlexander Aring 		struct nlattr *nl_ed_lvls;
3900e665457SAlexander Aring 
391ae0be8deSMichal Kubecek 		nl_ed_lvls = nla_nest_start_noflag(msg,
3920e665457SAlexander Aring 						   NL802154_CAP_ATTR_CCA_ED_LEVELS);
3930e665457SAlexander Aring 		if (!nl_ed_lvls)
3940e665457SAlexander Aring 			return -ENOBUFS;
3950e665457SAlexander Aring 
3960e665457SAlexander Aring 		for (i = 0; i < caps->cca_ed_levels_size; i++) {
3970e665457SAlexander Aring 			if (nla_put_s32(msg, i, caps->cca_ed_levels[i]))
3980e665457SAlexander Aring 				return -ENOBUFS;
3990e665457SAlexander Aring 		}
4000e665457SAlexander Aring 
4010e665457SAlexander Aring 		nla_nest_end(msg, nl_ed_lvls);
4020e665457SAlexander Aring 	}
4030e665457SAlexander Aring 
4040e665457SAlexander Aring 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER) {
4050e665457SAlexander Aring 		struct nlattr *nl_tx_pwrs;
4060e665457SAlexander Aring 
407ae0be8deSMichal Kubecek 		nl_tx_pwrs = nla_nest_start_noflag(msg,
408ae0be8deSMichal Kubecek 						   NL802154_CAP_ATTR_TX_POWERS);
4090e665457SAlexander Aring 		if (!nl_tx_pwrs)
4100e665457SAlexander Aring 			return -ENOBUFS;
4110e665457SAlexander Aring 
4120e665457SAlexander Aring 		for (i = 0; i < caps->tx_powers_size; i++) {
4130e665457SAlexander Aring 			if (nla_put_s32(msg, i, caps->tx_powers[i]))
4140e665457SAlexander Aring 				return -ENOBUFS;
4150e665457SAlexander Aring 		}
4160e665457SAlexander Aring 
4170e665457SAlexander Aring 		nla_nest_end(msg, nl_tx_pwrs);
4180e665457SAlexander Aring 	}
4190e665457SAlexander Aring 
4200e665457SAlexander Aring 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE) {
4210e665457SAlexander Aring 		if (nl802154_put_flags(msg, NL802154_CAP_ATTR_CCA_MODES,
4220e665457SAlexander Aring 				       caps->cca_modes) ||
4230e665457SAlexander Aring 		    nl802154_put_flags(msg, NL802154_CAP_ATTR_CCA_OPTS,
4240e665457SAlexander Aring 				       caps->cca_opts))
4250e665457SAlexander Aring 			return -ENOBUFS;
4260e665457SAlexander Aring 	}
4270e665457SAlexander Aring 
4280e665457SAlexander Aring 	if (nla_put_u8(msg, NL802154_CAP_ATTR_MIN_MINBE, caps->min_minbe) ||
4290e665457SAlexander Aring 	    nla_put_u8(msg, NL802154_CAP_ATTR_MAX_MINBE, caps->max_minbe) ||
4300e665457SAlexander Aring 	    nla_put_u8(msg, NL802154_CAP_ATTR_MIN_MAXBE, caps->min_maxbe) ||
4310e665457SAlexander Aring 	    nla_put_u8(msg, NL802154_CAP_ATTR_MAX_MAXBE, caps->max_maxbe) ||
4320e665457SAlexander Aring 	    nla_put_u8(msg, NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS,
4330e665457SAlexander Aring 		       caps->min_csma_backoffs) ||
4340e665457SAlexander Aring 	    nla_put_u8(msg, NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS,
4350e665457SAlexander Aring 		       caps->max_csma_backoffs) ||
4360e665457SAlexander Aring 	    nla_put_s8(msg, NL802154_CAP_ATTR_MIN_FRAME_RETRIES,
4370e665457SAlexander Aring 		       caps->min_frame_retries) ||
4380e665457SAlexander Aring 	    nla_put_s8(msg, NL802154_CAP_ATTR_MAX_FRAME_RETRIES,
4390e665457SAlexander Aring 		       caps->max_frame_retries) ||
4400e665457SAlexander Aring 	    nl802154_put_flags(msg, NL802154_CAP_ATTR_IFTYPES,
4410e665457SAlexander Aring 			       caps->iftypes) ||
4420e665457SAlexander Aring 	    nla_put_u32(msg, NL802154_CAP_ATTR_LBT, caps->lbt))
4430e665457SAlexander Aring 		return -ENOBUFS;
4440e665457SAlexander Aring 
4450e665457SAlexander Aring 	nla_nest_end(msg, nl_caps);
4460e665457SAlexander Aring 
4470e665457SAlexander Aring 	return 0;
4480e665457SAlexander Aring }
4490e665457SAlexander Aring 
nl802154_send_wpan_phy(struct cfg802154_registered_device * rdev,enum nl802154_commands cmd,struct sk_buff * msg,u32 portid,u32 seq,int flags)450ca20ce20SAlexander Aring static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev,
451ca20ce20SAlexander Aring 				  enum nl802154_commands cmd,
452ca20ce20SAlexander Aring 				  struct sk_buff *msg, u32 portid, u32 seq,
453ca20ce20SAlexander Aring 				  int flags)
454ca20ce20SAlexander Aring {
455133be026SVarka Bhadram 	struct nlattr *nl_cmds;
456ca20ce20SAlexander Aring 	void *hdr;
457133be026SVarka Bhadram 	int i;
458ca20ce20SAlexander Aring 
459ca20ce20SAlexander Aring 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
460ca20ce20SAlexander Aring 	if (!hdr)
461ca20ce20SAlexander Aring 		return -ENOBUFS;
462ca20ce20SAlexander Aring 
463ca20ce20SAlexander Aring 	if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
464ca20ce20SAlexander Aring 	    nla_put_string(msg, NL802154_ATTR_WPAN_PHY_NAME,
465ca20ce20SAlexander Aring 			   wpan_phy_name(&rdev->wpan_phy)) ||
466ca20ce20SAlexander Aring 	    nla_put_u32(msg, NL802154_ATTR_GENERATION,
467ca20ce20SAlexander Aring 			cfg802154_rdev_list_generation))
468ca20ce20SAlexander Aring 		goto nla_put_failure;
469ca20ce20SAlexander Aring 
470ca20ce20SAlexander Aring 	if (cmd != NL802154_CMD_NEW_WPAN_PHY)
471ca20ce20SAlexander Aring 		goto finish;
472ca20ce20SAlexander Aring 
473ca20ce20SAlexander Aring 	/* DUMP PHY PIB */
474ca20ce20SAlexander Aring 
475ca20ce20SAlexander Aring 	/* current channel settings */
476ca20ce20SAlexander Aring 	if (nla_put_u8(msg, NL802154_ATTR_PAGE,
477ca20ce20SAlexander Aring 		       rdev->wpan_phy.current_page) ||
478ca20ce20SAlexander Aring 	    nla_put_u8(msg, NL802154_ATTR_CHANNEL,
479ca20ce20SAlexander Aring 		       rdev->wpan_phy.current_channel))
480ca20ce20SAlexander Aring 		goto nla_put_failure;
481ca20ce20SAlexander Aring 
4820e665457SAlexander Aring 	/* TODO remove this behaviour, we still keep support it for a while
4830e665457SAlexander Aring 	 * so users can change the behaviour to the new one.
4840e665457SAlexander Aring 	 */
485ca20ce20SAlexander Aring 	if (nl802154_send_wpan_phy_channels(rdev, msg))
486ca20ce20SAlexander Aring 		goto nla_put_failure;
487ca20ce20SAlexander Aring 
488ca20ce20SAlexander Aring 	/* cca mode */
489edea8f7cSAlexander Aring 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE) {
490ba2a9506SAlexander Aring 		if (nla_put_u32(msg, NL802154_ATTR_CCA_MODE,
4917fe9a388SAlexander Aring 				rdev->wpan_phy.cca.mode))
492ca20ce20SAlexander Aring 			goto nla_put_failure;
493ca20ce20SAlexander Aring 
494ba2a9506SAlexander Aring 		if (rdev->wpan_phy.cca.mode == NL802154_CCA_ENERGY_CARRIER) {
495ba2a9506SAlexander Aring 			if (nla_put_u32(msg, NL802154_ATTR_CCA_OPT,
496ba2a9506SAlexander Aring 					rdev->wpan_phy.cca.opt))
497ba2a9506SAlexander Aring 				goto nla_put_failure;
498ba2a9506SAlexander Aring 		}
499edea8f7cSAlexander Aring 	}
500ba2a9506SAlexander Aring 
501edea8f7cSAlexander Aring 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER) {
5021a19cb68SAlexander Aring 		if (nla_put_s32(msg, NL802154_ATTR_TX_POWER,
503ca20ce20SAlexander Aring 				rdev->wpan_phy.transmit_power))
504ca20ce20SAlexander Aring 			goto nla_put_failure;
505edea8f7cSAlexander Aring 	}
506ca20ce20SAlexander Aring 
507e4390592SAlexander Aring 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_ED_LEVEL) {
508e4390592SAlexander Aring 		if (nla_put_s32(msg, NL802154_ATTR_CCA_ED_LEVEL,
509e4390592SAlexander Aring 				rdev->wpan_phy.cca_ed_level))
510e4390592SAlexander Aring 			goto nla_put_failure;
511e4390592SAlexander Aring 	}
512e4390592SAlexander Aring 
5130e665457SAlexander Aring 	if (nl802154_put_capabilities(msg, rdev))
5140e665457SAlexander Aring 		goto nla_put_failure;
5150e665457SAlexander Aring 
516ae0be8deSMichal Kubecek 	nl_cmds = nla_nest_start_noflag(msg, NL802154_ATTR_SUPPORTED_COMMANDS);
517133be026SVarka Bhadram 	if (!nl_cmds)
518133be026SVarka Bhadram 		goto nla_put_failure;
519133be026SVarka Bhadram 
520133be026SVarka Bhadram 	i = 0;
521133be026SVarka Bhadram #define CMD(op, n)							\
522133be026SVarka Bhadram 	do {								\
523133be026SVarka Bhadram 		if (rdev->ops->op) {					\
524133be026SVarka Bhadram 			i++;						\
525133be026SVarka Bhadram 			if (nla_put_u32(msg, i, NL802154_CMD_ ## n))	\
526133be026SVarka Bhadram 				goto nla_put_failure;			\
527133be026SVarka Bhadram 		}							\
528133be026SVarka Bhadram 	} while (0)
529133be026SVarka Bhadram 
530133be026SVarka Bhadram 	CMD(add_virtual_intf, NEW_INTERFACE);
531133be026SVarka Bhadram 	CMD(del_virtual_intf, DEL_INTERFACE);
532133be026SVarka Bhadram 	CMD(set_channel, SET_CHANNEL);
533133be026SVarka Bhadram 	CMD(set_pan_id, SET_PAN_ID);
534133be026SVarka Bhadram 	CMD(set_short_addr, SET_SHORT_ADDR);
535133be026SVarka Bhadram 	CMD(set_backoff_exponent, SET_BACKOFF_EXPONENT);
536133be026SVarka Bhadram 	CMD(set_max_csma_backoffs, SET_MAX_CSMA_BACKOFFS);
537133be026SVarka Bhadram 	CMD(set_max_frame_retries, SET_MAX_FRAME_RETRIES);
538133be026SVarka Bhadram 	CMD(set_lbt_mode, SET_LBT_MODE);
539c91208d8SAlexander Aring 	CMD(set_ackreq_default, SET_ACKREQ_DEFAULT);
540133be026SVarka Bhadram 
541133be026SVarka Bhadram 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER)
542133be026SVarka Bhadram 		CMD(set_tx_power, SET_TX_POWER);
543133be026SVarka Bhadram 
544133be026SVarka Bhadram 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_ED_LEVEL)
545133be026SVarka Bhadram 		CMD(set_cca_ed_level, SET_CCA_ED_LEVEL);
546133be026SVarka Bhadram 
547133be026SVarka Bhadram 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE)
548133be026SVarka Bhadram 		CMD(set_cca_mode, SET_CCA_MODE);
549133be026SVarka Bhadram 
550133be026SVarka Bhadram #undef CMD
551133be026SVarka Bhadram 	nla_nest_end(msg, nl_cmds);
552133be026SVarka Bhadram 
553ca20ce20SAlexander Aring finish:
554053c095aSJohannes Berg 	genlmsg_end(msg, hdr);
555053c095aSJohannes Berg 	return 0;
556ca20ce20SAlexander Aring 
557ca20ce20SAlexander Aring nla_put_failure:
558ca20ce20SAlexander Aring 	genlmsg_cancel(msg, hdr);
559ca20ce20SAlexander Aring 	return -EMSGSIZE;
560ca20ce20SAlexander Aring }
561ca20ce20SAlexander Aring 
562ca20ce20SAlexander Aring struct nl802154_dump_wpan_phy_state {
563ca20ce20SAlexander Aring 	s64 filter_wpan_phy;
564ca20ce20SAlexander Aring 	long start;
565ca20ce20SAlexander Aring 
566ca20ce20SAlexander Aring };
567ca20ce20SAlexander Aring 
nl802154_dump_wpan_phy_parse(struct sk_buff * skb,struct netlink_callback * cb,struct nl802154_dump_wpan_phy_state * state)568ca20ce20SAlexander Aring static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb,
569ca20ce20SAlexander Aring 					struct netlink_callback *cb,
570ca20ce20SAlexander Aring 					struct nl802154_dump_wpan_phy_state *state)
571ca20ce20SAlexander Aring {
57275cdbdd0SJiri Pirko 	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
573*7288dd2fSJakub Kicinski 	struct nlattr **tb = info->info.attrs;
574ca20ce20SAlexander Aring 
575ca20ce20SAlexander Aring 	if (tb[NL802154_ATTR_WPAN_PHY])
576ca20ce20SAlexander Aring 		state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]);
577ca20ce20SAlexander Aring 	if (tb[NL802154_ATTR_WPAN_DEV])
578ca20ce20SAlexander Aring 		state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32;
579ca20ce20SAlexander Aring 	if (tb[NL802154_ATTR_IFINDEX]) {
580ca20ce20SAlexander Aring 		struct net_device *netdev;
581ca20ce20SAlexander Aring 		struct cfg802154_registered_device *rdev;
582ca20ce20SAlexander Aring 		int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]);
583ca20ce20SAlexander Aring 
584ca20ce20SAlexander Aring 		netdev = __dev_get_by_index(&init_net, ifidx);
585ca20ce20SAlexander Aring 		if (!netdev)
586ca20ce20SAlexander Aring 			return -ENODEV;
587ca20ce20SAlexander Aring 		if (netdev->ieee802154_ptr) {
588ca20ce20SAlexander Aring 			rdev = wpan_phy_to_rdev(
589ca20ce20SAlexander Aring 					netdev->ieee802154_ptr->wpan_phy);
590ca20ce20SAlexander Aring 			state->filter_wpan_phy = rdev->wpan_phy_idx;
591ca20ce20SAlexander Aring 		}
592ca20ce20SAlexander Aring 	}
593ca20ce20SAlexander Aring 
594ca20ce20SAlexander Aring 	return 0;
595ca20ce20SAlexander Aring }
596ca20ce20SAlexander Aring 
597ca20ce20SAlexander Aring static int
nl802154_dump_wpan_phy(struct sk_buff * skb,struct netlink_callback * cb)598ca20ce20SAlexander Aring nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb)
599ca20ce20SAlexander Aring {
600ca20ce20SAlexander Aring 	int idx = 0, ret;
601ca20ce20SAlexander Aring 	struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0];
602ca20ce20SAlexander Aring 	struct cfg802154_registered_device *rdev;
603ca20ce20SAlexander Aring 
604ca20ce20SAlexander Aring 	rtnl_lock();
605ca20ce20SAlexander Aring 	if (!state) {
606ca20ce20SAlexander Aring 		state = kzalloc(sizeof(*state), GFP_KERNEL);
607ca20ce20SAlexander Aring 		if (!state) {
608ca20ce20SAlexander Aring 			rtnl_unlock();
609ca20ce20SAlexander Aring 			return -ENOMEM;
610ca20ce20SAlexander Aring 		}
611ca20ce20SAlexander Aring 		state->filter_wpan_phy = -1;
612ca20ce20SAlexander Aring 		ret = nl802154_dump_wpan_phy_parse(skb, cb, state);
613ca20ce20SAlexander Aring 		if (ret) {
614ca20ce20SAlexander Aring 			kfree(state);
615ca20ce20SAlexander Aring 			rtnl_unlock();
616ca20ce20SAlexander Aring 			return ret;
617ca20ce20SAlexander Aring 		}
618ca20ce20SAlexander Aring 		cb->args[0] = (long)state;
619ca20ce20SAlexander Aring 	}
620ca20ce20SAlexander Aring 
621ca20ce20SAlexander Aring 	list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
62266e5c267SAlexander Aring 		if (!net_eq(wpan_phy_net(&rdev->wpan_phy), sock_net(skb->sk)))
62366e5c267SAlexander Aring 			continue;
624ca20ce20SAlexander Aring 		if (++idx <= state->start)
625ca20ce20SAlexander Aring 			continue;
626ca20ce20SAlexander Aring 		if (state->filter_wpan_phy != -1 &&
627ca20ce20SAlexander Aring 		    state->filter_wpan_phy != rdev->wpan_phy_idx)
628ca20ce20SAlexander Aring 			continue;
629ca20ce20SAlexander Aring 		/* attempt to fit multiple wpan_phy data chunks into the skb */
630ca20ce20SAlexander Aring 		ret = nl802154_send_wpan_phy(rdev,
631ca20ce20SAlexander Aring 					     NL802154_CMD_NEW_WPAN_PHY,
632ca20ce20SAlexander Aring 					     skb,
633ca20ce20SAlexander Aring 					     NETLINK_CB(cb->skb).portid,
634ca20ce20SAlexander Aring 					     cb->nlh->nlmsg_seq, NLM_F_MULTI);
635ca20ce20SAlexander Aring 		if (ret < 0) {
636ca20ce20SAlexander Aring 			if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
637ca20ce20SAlexander Aring 			    !skb->len && cb->min_dump_alloc < 4096) {
638ca20ce20SAlexander Aring 				cb->min_dump_alloc = 4096;
639ca20ce20SAlexander Aring 				rtnl_unlock();
640ca20ce20SAlexander Aring 				return 1;
641ca20ce20SAlexander Aring 			}
642ca20ce20SAlexander Aring 			idx--;
643ca20ce20SAlexander Aring 			break;
644ca20ce20SAlexander Aring 		}
645ca20ce20SAlexander Aring 		break;
646ca20ce20SAlexander Aring 	}
647ca20ce20SAlexander Aring 	rtnl_unlock();
648ca20ce20SAlexander Aring 
649ca20ce20SAlexander Aring 	state->start = idx;
650ca20ce20SAlexander Aring 
651ca20ce20SAlexander Aring 	return skb->len;
652ca20ce20SAlexander Aring }
653ca20ce20SAlexander Aring 
nl802154_dump_wpan_phy_done(struct netlink_callback * cb)654ca20ce20SAlexander Aring static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
655ca20ce20SAlexander Aring {
656ca20ce20SAlexander Aring 	kfree((void *)cb->args[0]);
657ca20ce20SAlexander Aring 	return 0;
658ca20ce20SAlexander Aring }
659ca20ce20SAlexander Aring 
nl802154_get_wpan_phy(struct sk_buff * skb,struct genl_info * info)660ca20ce20SAlexander Aring static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info)
661ca20ce20SAlexander Aring {
662ca20ce20SAlexander Aring 	struct sk_buff *msg;
663ca20ce20SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
664ca20ce20SAlexander Aring 
665ca20ce20SAlexander Aring 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
666ca20ce20SAlexander Aring 	if (!msg)
667ca20ce20SAlexander Aring 		return -ENOMEM;
668ca20ce20SAlexander Aring 
669ca20ce20SAlexander Aring 	if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg,
670ca20ce20SAlexander Aring 				   info->snd_portid, info->snd_seq, 0) < 0) {
671ca20ce20SAlexander Aring 		nlmsg_free(msg);
672ca20ce20SAlexander Aring 		return -ENOBUFS;
673ca20ce20SAlexander Aring 	}
674ca20ce20SAlexander Aring 
675ca20ce20SAlexander Aring 	return genlmsg_reply(msg, info);
676ca20ce20SAlexander Aring }
677ca20ce20SAlexander Aring 
wpan_dev_id(struct wpan_dev * wpan_dev)6784b96aea0SAlexander Aring static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev)
6794b96aea0SAlexander Aring {
6804b96aea0SAlexander Aring 	return (u64)wpan_dev->identifier |
6814b96aea0SAlexander Aring 	       ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
6824b96aea0SAlexander Aring }
6834b96aea0SAlexander Aring 
684a26c5fd7SAlexander Aring #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
685a26c5fd7SAlexander Aring #include <net/ieee802154_netdev.h>
686a26c5fd7SAlexander Aring 
687a26c5fd7SAlexander Aring static int
ieee802154_llsec_send_key_id(struct sk_buff * msg,const struct ieee802154_llsec_key_id * desc)688a26c5fd7SAlexander Aring ieee802154_llsec_send_key_id(struct sk_buff *msg,
689a26c5fd7SAlexander Aring 			     const struct ieee802154_llsec_key_id *desc)
690a26c5fd7SAlexander Aring {
691a26c5fd7SAlexander Aring 	struct nlattr *nl_dev_addr;
692a26c5fd7SAlexander Aring 
693a26c5fd7SAlexander Aring 	if (nla_put_u32(msg, NL802154_KEY_ID_ATTR_MODE, desc->mode))
694a26c5fd7SAlexander Aring 		return -ENOBUFS;
695a26c5fd7SAlexander Aring 
696a26c5fd7SAlexander Aring 	switch (desc->mode) {
697a26c5fd7SAlexander Aring 	case NL802154_KEY_ID_MODE_IMPLICIT:
698ae0be8deSMichal Kubecek 		nl_dev_addr = nla_nest_start_noflag(msg,
699ae0be8deSMichal Kubecek 						    NL802154_KEY_ID_ATTR_IMPLICIT);
700a26c5fd7SAlexander Aring 		if (!nl_dev_addr)
701a26c5fd7SAlexander Aring 			return -ENOBUFS;
702a26c5fd7SAlexander Aring 
703a26c5fd7SAlexander Aring 		if (nla_put_le16(msg, NL802154_DEV_ADDR_ATTR_PAN_ID,
704a26c5fd7SAlexander Aring 				 desc->device_addr.pan_id) ||
705a26c5fd7SAlexander Aring 		    nla_put_u32(msg,  NL802154_DEV_ADDR_ATTR_MODE,
706a26c5fd7SAlexander Aring 				desc->device_addr.mode))
707a26c5fd7SAlexander Aring 			return -ENOBUFS;
708a26c5fd7SAlexander Aring 
709a26c5fd7SAlexander Aring 		switch (desc->device_addr.mode) {
710a26c5fd7SAlexander Aring 		case NL802154_DEV_ADDR_SHORT:
711a26c5fd7SAlexander Aring 			if (nla_put_le16(msg, NL802154_DEV_ADDR_ATTR_SHORT,
712a26c5fd7SAlexander Aring 					 desc->device_addr.short_addr))
713a26c5fd7SAlexander Aring 				return -ENOBUFS;
714a26c5fd7SAlexander Aring 			break;
715a26c5fd7SAlexander Aring 		case NL802154_DEV_ADDR_EXTENDED:
716a26c5fd7SAlexander Aring 			if (nla_put_le64(msg, NL802154_DEV_ADDR_ATTR_EXTENDED,
717e7479122SNicolas Dichtel 					 desc->device_addr.extended_addr,
718e7479122SNicolas Dichtel 					 NL802154_DEV_ADDR_ATTR_PAD))
719a26c5fd7SAlexander Aring 				return -ENOBUFS;
720a26c5fd7SAlexander Aring 			break;
721a26c5fd7SAlexander Aring 		default:
722a26c5fd7SAlexander Aring 			/* userspace should handle unknown */
723a26c5fd7SAlexander Aring 			break;
724a26c5fd7SAlexander Aring 		}
725a26c5fd7SAlexander Aring 
726a26c5fd7SAlexander Aring 		nla_nest_end(msg, nl_dev_addr);
727a26c5fd7SAlexander Aring 		break;
728a26c5fd7SAlexander Aring 	case NL802154_KEY_ID_MODE_INDEX:
729a26c5fd7SAlexander Aring 		break;
730a26c5fd7SAlexander Aring 	case NL802154_KEY_ID_MODE_INDEX_SHORT:
731a26c5fd7SAlexander Aring 		/* TODO renmae short_source? */
732a26c5fd7SAlexander Aring 		if (nla_put_le32(msg, NL802154_KEY_ID_ATTR_SOURCE_SHORT,
733a26c5fd7SAlexander Aring 				 desc->short_source))
734a26c5fd7SAlexander Aring 			return -ENOBUFS;
735a26c5fd7SAlexander Aring 		break;
736a26c5fd7SAlexander Aring 	case NL802154_KEY_ID_MODE_INDEX_EXTENDED:
737a26c5fd7SAlexander Aring 		if (nla_put_le64(msg, NL802154_KEY_ID_ATTR_SOURCE_EXTENDED,
738e7479122SNicolas Dichtel 				 desc->extended_source,
739e7479122SNicolas Dichtel 				 NL802154_KEY_ID_ATTR_PAD))
740a26c5fd7SAlexander Aring 			return -ENOBUFS;
741a26c5fd7SAlexander Aring 		break;
742a26c5fd7SAlexander Aring 	default:
743a26c5fd7SAlexander Aring 		/* userspace should handle unknown */
744a26c5fd7SAlexander Aring 		break;
745a26c5fd7SAlexander Aring 	}
746a26c5fd7SAlexander Aring 
747a26c5fd7SAlexander Aring 	/* TODO key_id to key_idx ? Check naming */
748a26c5fd7SAlexander Aring 	if (desc->mode != NL802154_KEY_ID_MODE_IMPLICIT) {
749a26c5fd7SAlexander Aring 		if (nla_put_u8(msg, NL802154_KEY_ID_ATTR_INDEX, desc->id))
750a26c5fd7SAlexander Aring 			return -ENOBUFS;
751a26c5fd7SAlexander Aring 	}
752a26c5fd7SAlexander Aring 
753a26c5fd7SAlexander Aring 	return 0;
754a26c5fd7SAlexander Aring }
755a26c5fd7SAlexander Aring 
nl802154_get_llsec_params(struct sk_buff * msg,struct cfg802154_registered_device * rdev,struct wpan_dev * wpan_dev)756a26c5fd7SAlexander Aring static int nl802154_get_llsec_params(struct sk_buff *msg,
757a26c5fd7SAlexander Aring 				     struct cfg802154_registered_device *rdev,
758a26c5fd7SAlexander Aring 				     struct wpan_dev *wpan_dev)
759a26c5fd7SAlexander Aring {
760a26c5fd7SAlexander Aring 	struct nlattr *nl_key_id;
761a26c5fd7SAlexander Aring 	struct ieee802154_llsec_params params;
762a26c5fd7SAlexander Aring 	int ret;
763a26c5fd7SAlexander Aring 
764a26c5fd7SAlexander Aring 	ret = rdev_get_llsec_params(rdev, wpan_dev, &params);
765a26c5fd7SAlexander Aring 	if (ret < 0)
766a26c5fd7SAlexander Aring 		return ret;
767a26c5fd7SAlexander Aring 
768a26c5fd7SAlexander Aring 	if (nla_put_u8(msg, NL802154_ATTR_SEC_ENABLED, params.enabled) ||
769a26c5fd7SAlexander Aring 	    nla_put_u32(msg, NL802154_ATTR_SEC_OUT_LEVEL, params.out_level) ||
770a26c5fd7SAlexander Aring 	    nla_put_be32(msg, NL802154_ATTR_SEC_FRAME_COUNTER,
771a26c5fd7SAlexander Aring 			 params.frame_counter))
772a26c5fd7SAlexander Aring 		return -ENOBUFS;
773a26c5fd7SAlexander Aring 
774ae0be8deSMichal Kubecek 	nl_key_id = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_OUT_KEY_ID);
775a26c5fd7SAlexander Aring 	if (!nl_key_id)
776a26c5fd7SAlexander Aring 		return -ENOBUFS;
777a26c5fd7SAlexander Aring 
778a26c5fd7SAlexander Aring 	ret = ieee802154_llsec_send_key_id(msg, &params.out_key);
779a26c5fd7SAlexander Aring 	if (ret < 0)
780a26c5fd7SAlexander Aring 		return ret;
781a26c5fd7SAlexander Aring 
782a26c5fd7SAlexander Aring 	nla_nest_end(msg, nl_key_id);
783a26c5fd7SAlexander Aring 
784a26c5fd7SAlexander Aring 	return 0;
785a26c5fd7SAlexander Aring }
786a26c5fd7SAlexander Aring #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
787a26c5fd7SAlexander Aring 
7884b96aea0SAlexander Aring static int
nl802154_send_iface(struct sk_buff * msg,u32 portid,u32 seq,int flags,struct cfg802154_registered_device * rdev,struct wpan_dev * wpan_dev)7894b96aea0SAlexander Aring nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
7904b96aea0SAlexander Aring 		    struct cfg802154_registered_device *rdev,
7914b96aea0SAlexander Aring 		    struct wpan_dev *wpan_dev)
7924b96aea0SAlexander Aring {
7934b96aea0SAlexander Aring 	struct net_device *dev = wpan_dev->netdev;
7944b96aea0SAlexander Aring 	void *hdr;
7954b96aea0SAlexander Aring 
7964b96aea0SAlexander Aring 	hdr = nl802154hdr_put(msg, portid, seq, flags,
7974b96aea0SAlexander Aring 			      NL802154_CMD_NEW_INTERFACE);
7984b96aea0SAlexander Aring 	if (!hdr)
7994b96aea0SAlexander Aring 		return -1;
8004b96aea0SAlexander Aring 
8014b96aea0SAlexander Aring 	if (dev &&
8024b96aea0SAlexander Aring 	    (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) ||
8034b96aea0SAlexander Aring 	     nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name)))
8044b96aea0SAlexander Aring 		goto nla_put_failure;
8054b96aea0SAlexander Aring 
8064b96aea0SAlexander Aring 	if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
8074b96aea0SAlexander Aring 	    nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) ||
808a558da09SNicolas Dichtel 	    nla_put_u64_64bit(msg, NL802154_ATTR_WPAN_DEV,
809a558da09SNicolas Dichtel 			      wpan_dev_id(wpan_dev), NL802154_ATTR_PAD) ||
8104b96aea0SAlexander Aring 	    nla_put_u32(msg, NL802154_ATTR_GENERATION,
8114b96aea0SAlexander Aring 			rdev->devlist_generation ^
8124b96aea0SAlexander Aring 			(cfg802154_rdev_list_generation << 2)))
8134b96aea0SAlexander Aring 		goto nla_put_failure;
8144b96aea0SAlexander Aring 
8154b96aea0SAlexander Aring 	/* address settings */
8164b96aea0SAlexander Aring 	if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR,
817e7479122SNicolas Dichtel 			 wpan_dev->extended_addr,
818e7479122SNicolas Dichtel 			 NL802154_ATTR_PAD) ||
8194b96aea0SAlexander Aring 	    nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR,
8204b96aea0SAlexander Aring 			 wpan_dev->short_addr) ||
8214b96aea0SAlexander Aring 	    nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id))
8224b96aea0SAlexander Aring 		goto nla_put_failure;
8234b96aea0SAlexander Aring 
8244b96aea0SAlexander Aring 	/* ARET handling */
8254b96aea0SAlexander Aring 	if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES,
8264b96aea0SAlexander Aring 		       wpan_dev->frame_retries) ||
8274b96aea0SAlexander Aring 	    nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) ||
8284b96aea0SAlexander Aring 	    nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS,
8294b96aea0SAlexander Aring 		       wpan_dev->csma_retries) ||
8304b96aea0SAlexander Aring 	    nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be))
8314b96aea0SAlexander Aring 		goto nla_put_failure;
8324b96aea0SAlexander Aring 
8334b96aea0SAlexander Aring 	/* listen before transmit */
8344b96aea0SAlexander Aring 	if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt))
8354b96aea0SAlexander Aring 		goto nla_put_failure;
8364b96aea0SAlexander Aring 
837c91208d8SAlexander Aring 	/* ackreq default behaviour */
838c91208d8SAlexander Aring 	if (nla_put_u8(msg, NL802154_ATTR_ACKREQ_DEFAULT, wpan_dev->ackreq))
839c91208d8SAlexander Aring 		goto nla_put_failure;
840c91208d8SAlexander Aring 
841a26c5fd7SAlexander Aring #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
8421534efc7SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
8431534efc7SAlexander Aring 		goto out;
8441534efc7SAlexander Aring 
845a26c5fd7SAlexander Aring 	if (nl802154_get_llsec_params(msg, rdev, wpan_dev) < 0)
846a26c5fd7SAlexander Aring 		goto nla_put_failure;
8471534efc7SAlexander Aring 
8481534efc7SAlexander Aring out:
849a26c5fd7SAlexander Aring #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
850a26c5fd7SAlexander Aring 
851053c095aSJohannes Berg 	genlmsg_end(msg, hdr);
852053c095aSJohannes Berg 	return 0;
8534b96aea0SAlexander Aring 
8544b96aea0SAlexander Aring nla_put_failure:
8554b96aea0SAlexander Aring 	genlmsg_cancel(msg, hdr);
8564b96aea0SAlexander Aring 	return -EMSGSIZE;
8574b96aea0SAlexander Aring }
8584b96aea0SAlexander Aring 
8594b96aea0SAlexander Aring static int
nl802154_dump_interface(struct sk_buff * skb,struct netlink_callback * cb)8604b96aea0SAlexander Aring nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
8614b96aea0SAlexander Aring {
8624b96aea0SAlexander Aring 	int wp_idx = 0;
8634b96aea0SAlexander Aring 	int if_idx = 0;
8644b96aea0SAlexander Aring 	int wp_start = cb->args[0];
8654b96aea0SAlexander Aring 	int if_start = cb->args[1];
8664b96aea0SAlexander Aring 	struct cfg802154_registered_device *rdev;
8674b96aea0SAlexander Aring 	struct wpan_dev *wpan_dev;
8684b96aea0SAlexander Aring 
8694b96aea0SAlexander Aring 	rtnl_lock();
8704b96aea0SAlexander Aring 	list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
87166e5c267SAlexander Aring 		if (!net_eq(wpan_phy_net(&rdev->wpan_phy), sock_net(skb->sk)))
87266e5c267SAlexander Aring 			continue;
8734b96aea0SAlexander Aring 		if (wp_idx < wp_start) {
8744b96aea0SAlexander Aring 			wp_idx++;
8754b96aea0SAlexander Aring 			continue;
8764b96aea0SAlexander Aring 		}
8774b96aea0SAlexander Aring 		if_idx = 0;
8784b96aea0SAlexander Aring 
8794b96aea0SAlexander Aring 		list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
8804b96aea0SAlexander Aring 			if (if_idx < if_start) {
8814b96aea0SAlexander Aring 				if_idx++;
8824b96aea0SAlexander Aring 				continue;
8834b96aea0SAlexander Aring 			}
8844b96aea0SAlexander Aring 			if (nl802154_send_iface(skb, NETLINK_CB(cb->skb).portid,
8854b96aea0SAlexander Aring 						cb->nlh->nlmsg_seq, NLM_F_MULTI,
8864b96aea0SAlexander Aring 						rdev, wpan_dev) < 0) {
8874b96aea0SAlexander Aring 				goto out;
8884b96aea0SAlexander Aring 			}
8894b96aea0SAlexander Aring 			if_idx++;
8904b96aea0SAlexander Aring 		}
8914b96aea0SAlexander Aring 
8924b96aea0SAlexander Aring 		wp_idx++;
8934b96aea0SAlexander Aring 	}
8944b96aea0SAlexander Aring out:
8954b96aea0SAlexander Aring 	rtnl_unlock();
8964b96aea0SAlexander Aring 
8974b96aea0SAlexander Aring 	cb->args[0] = wp_idx;
8984b96aea0SAlexander Aring 	cb->args[1] = if_idx;
8994b96aea0SAlexander Aring 
9004b96aea0SAlexander Aring 	return skb->len;
9014b96aea0SAlexander Aring }
9024b96aea0SAlexander Aring 
nl802154_get_interface(struct sk_buff * skb,struct genl_info * info)9034b96aea0SAlexander Aring static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info)
9044b96aea0SAlexander Aring {
9054b96aea0SAlexander Aring 	struct sk_buff *msg;
9064b96aea0SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
9074b96aea0SAlexander Aring 	struct wpan_dev *wdev = info->user_ptr[1];
9084b96aea0SAlexander Aring 
9094b96aea0SAlexander Aring 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
9104b96aea0SAlexander Aring 	if (!msg)
9114b96aea0SAlexander Aring 		return -ENOMEM;
9124b96aea0SAlexander Aring 
9134b96aea0SAlexander Aring 	if (nl802154_send_iface(msg, info->snd_portid, info->snd_seq, 0,
9144b96aea0SAlexander Aring 				rdev, wdev) < 0) {
9154b96aea0SAlexander Aring 		nlmsg_free(msg);
9164b96aea0SAlexander Aring 		return -ENOBUFS;
9174b96aea0SAlexander Aring 	}
9184b96aea0SAlexander Aring 
9194b96aea0SAlexander Aring 	return genlmsg_reply(msg, info);
9204b96aea0SAlexander Aring }
9214b96aea0SAlexander Aring 
nl802154_new_interface(struct sk_buff * skb,struct genl_info * info)922f3ea5e44SAlexander Aring static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
923f3ea5e44SAlexander Aring {
924f3ea5e44SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
925f3ea5e44SAlexander Aring 	enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC;
9260e57547eSAlexander Aring 	__le64 extended_addr = cpu_to_le64(0x0000000000000000ULL);
927f3ea5e44SAlexander Aring 
928f3ea5e44SAlexander Aring 	/* TODO avoid failing a new interface
929f3ea5e44SAlexander Aring 	 * creation due to pending removal?
930f3ea5e44SAlexander Aring 	 */
931f3ea5e44SAlexander Aring 
932f3ea5e44SAlexander Aring 	if (!info->attrs[NL802154_ATTR_IFNAME])
933f3ea5e44SAlexander Aring 		return -EINVAL;
934f3ea5e44SAlexander Aring 
935f3ea5e44SAlexander Aring 	if (info->attrs[NL802154_ATTR_IFTYPE]) {
936f3ea5e44SAlexander Aring 		type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]);
93765318680SAlexander Aring 		if (type > NL802154_IFTYPE_MAX ||
93865318680SAlexander Aring 		    !(rdev->wpan_phy.supported.iftypes & BIT(type)))
939f3ea5e44SAlexander Aring 			return -EINVAL;
940f3ea5e44SAlexander Aring 	}
941f3ea5e44SAlexander Aring 
9420e57547eSAlexander Aring 	if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
9431ee06ef1SAlexander Aring 		extended_addr = nla_get_le64(info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
9440e57547eSAlexander Aring 
945f3ea5e44SAlexander Aring 	if (!rdev->ops->add_virtual_intf)
946f3ea5e44SAlexander Aring 		return -EOPNOTSUPP;
947f3ea5e44SAlexander Aring 
948f3ea5e44SAlexander Aring 	return rdev_add_virtual_intf(rdev,
949f3ea5e44SAlexander Aring 				     nla_data(info->attrs[NL802154_ATTR_IFNAME]),
9505b4a1039SVarka Bhadram 				     NET_NAME_USER, type, extended_addr);
951f3ea5e44SAlexander Aring }
952f3ea5e44SAlexander Aring 
nl802154_del_interface(struct sk_buff * skb,struct genl_info * info)953b821ecd4SAlexander Aring static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info)
954b821ecd4SAlexander Aring {
955b821ecd4SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
956b821ecd4SAlexander Aring 	struct wpan_dev *wpan_dev = info->user_ptr[1];
957b821ecd4SAlexander Aring 
958b821ecd4SAlexander Aring 	if (!rdev->ops->del_virtual_intf)
959b821ecd4SAlexander Aring 		return -EOPNOTSUPP;
960b821ecd4SAlexander Aring 
961b821ecd4SAlexander Aring 	/* If we remove a wpan device without a netdev then clear
962b821ecd4SAlexander Aring 	 * user_ptr[1] so that nl802154_post_doit won't dereference it
963b821ecd4SAlexander Aring 	 * to check if it needs to do dev_put(). Otherwise it crashes
964b821ecd4SAlexander Aring 	 * since the wpan_dev has been freed, unlike with a netdev where
965b821ecd4SAlexander Aring 	 * we need the dev_put() for the netdev to really be freed.
966b821ecd4SAlexander Aring 	 */
967b821ecd4SAlexander Aring 	if (!wpan_dev->netdev)
968b821ecd4SAlexander Aring 		info->user_ptr[1] = NULL;
969b821ecd4SAlexander Aring 
970b821ecd4SAlexander Aring 	return rdev_del_virtual_intf(rdev, wpan_dev);
971b821ecd4SAlexander Aring }
972b821ecd4SAlexander Aring 
nl802154_set_channel(struct sk_buff * skb,struct genl_info * info)973ab0bd561SAlexander Aring static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info)
974ab0bd561SAlexander Aring {
975ab0bd561SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
976ab0bd561SAlexander Aring 	u8 channel, page;
977ab0bd561SAlexander Aring 
978ab0bd561SAlexander Aring 	if (!info->attrs[NL802154_ATTR_PAGE] ||
979ab0bd561SAlexander Aring 	    !info->attrs[NL802154_ATTR_CHANNEL])
980ab0bd561SAlexander Aring 		return -EINVAL;
981ab0bd561SAlexander Aring 
982ab0bd561SAlexander Aring 	page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
983ab0bd561SAlexander Aring 	channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]);
984ab0bd561SAlexander Aring 
985ab0bd561SAlexander Aring 	/* check 802.15.4 constraints */
986d2aaf2a0SMiquel Raynal 	if (!ieee802154_chan_is_valid(&rdev->wpan_phy, page, channel))
987ab0bd561SAlexander Aring 		return -EINVAL;
988ab0bd561SAlexander Aring 
989ab0bd561SAlexander Aring 	return rdev_set_channel(rdev, page, channel);
990ab0bd561SAlexander Aring }
991ab0bd561SAlexander Aring 
nl802154_set_cca_mode(struct sk_buff * skb,struct genl_info * info)992ba2a9506SAlexander Aring static int nl802154_set_cca_mode(struct sk_buff *skb, struct genl_info *info)
993ba2a9506SAlexander Aring {
994ba2a9506SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
995ba2a9506SAlexander Aring 	struct wpan_phy_cca cca;
996ba2a9506SAlexander Aring 
997fc4f8052SAlexander Aring 	if (!(rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE))
998edea8f7cSAlexander Aring 		return -EOPNOTSUPP;
999edea8f7cSAlexander Aring 
1000ba2a9506SAlexander Aring 	if (!info->attrs[NL802154_ATTR_CCA_MODE])
1001ba2a9506SAlexander Aring 		return -EINVAL;
1002ba2a9506SAlexander Aring 
1003ba2a9506SAlexander Aring 	cca.mode = nla_get_u32(info->attrs[NL802154_ATTR_CCA_MODE]);
1004ba2a9506SAlexander Aring 	/* checking 802.15.4 constraints */
1005fea3318dSAlexander Aring 	if (cca.mode < NL802154_CCA_ENERGY ||
1006fea3318dSAlexander Aring 	    cca.mode > NL802154_CCA_ATTR_MAX ||
1007fea3318dSAlexander Aring 	    !(rdev->wpan_phy.supported.cca_modes & BIT(cca.mode)))
1008ba2a9506SAlexander Aring 		return -EINVAL;
1009ba2a9506SAlexander Aring 
1010ba2a9506SAlexander Aring 	if (cca.mode == NL802154_CCA_ENERGY_CARRIER) {
1011ba2a9506SAlexander Aring 		if (!info->attrs[NL802154_ATTR_CCA_OPT])
1012ba2a9506SAlexander Aring 			return -EINVAL;
1013ba2a9506SAlexander Aring 
1014ba2a9506SAlexander Aring 		cca.opt = nla_get_u32(info->attrs[NL802154_ATTR_CCA_OPT]);
1015fea3318dSAlexander Aring 		if (cca.opt > NL802154_CCA_OPT_ATTR_MAX ||
1016fea3318dSAlexander Aring 		    !(rdev->wpan_phy.supported.cca_opts & BIT(cca.opt)))
1017ba2a9506SAlexander Aring 			return -EINVAL;
1018ba2a9506SAlexander Aring 	}
1019ba2a9506SAlexander Aring 
1020ba2a9506SAlexander Aring 	return rdev_set_cca_mode(rdev, &cca);
1021ba2a9506SAlexander Aring }
1022ba2a9506SAlexander Aring 
nl802154_set_cca_ed_level(struct sk_buff * skb,struct genl_info * info)1023b69644c1SAlexander Aring static int nl802154_set_cca_ed_level(struct sk_buff *skb, struct genl_info *info)
1024b69644c1SAlexander Aring {
1025b69644c1SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1026b69644c1SAlexander Aring 	s32 ed_level;
1027b69644c1SAlexander Aring 	int i;
1028b69644c1SAlexander Aring 
1029b69644c1SAlexander Aring 	if (!(rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_ED_LEVEL))
1030b69644c1SAlexander Aring 		return -EOPNOTSUPP;
1031b69644c1SAlexander Aring 
1032b69644c1SAlexander Aring 	if (!info->attrs[NL802154_ATTR_CCA_ED_LEVEL])
1033b69644c1SAlexander Aring 		return -EINVAL;
1034b69644c1SAlexander Aring 
1035b69644c1SAlexander Aring 	ed_level = nla_get_s32(info->attrs[NL802154_ATTR_CCA_ED_LEVEL]);
1036b69644c1SAlexander Aring 
1037b69644c1SAlexander Aring 	for (i = 0; i < rdev->wpan_phy.supported.cca_ed_levels_size; i++) {
1038b69644c1SAlexander Aring 		if (ed_level == rdev->wpan_phy.supported.cca_ed_levels[i])
1039b69644c1SAlexander Aring 			return rdev_set_cca_ed_level(rdev, ed_level);
1040b69644c1SAlexander Aring 	}
1041b69644c1SAlexander Aring 
1042b69644c1SAlexander Aring 	return -EINVAL;
1043b69644c1SAlexander Aring }
1044b69644c1SAlexander Aring 
nl802154_set_tx_power(struct sk_buff * skb,struct genl_info * info)10450f999b09SVarka Bhadram static int nl802154_set_tx_power(struct sk_buff *skb, struct genl_info *info)
10460f999b09SVarka Bhadram {
10470f999b09SVarka Bhadram 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
10480f999b09SVarka Bhadram 	s32 power;
10490f999b09SVarka Bhadram 	int i;
10500f999b09SVarka Bhadram 
10510f999b09SVarka Bhadram 	if (!(rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER))
10520f999b09SVarka Bhadram 		return -EOPNOTSUPP;
10530f999b09SVarka Bhadram 
10540f999b09SVarka Bhadram 	if (!info->attrs[NL802154_ATTR_TX_POWER])
10550f999b09SVarka Bhadram 		return -EINVAL;
10560f999b09SVarka Bhadram 
10570f999b09SVarka Bhadram 	power = nla_get_s32(info->attrs[NL802154_ATTR_TX_POWER]);
10580f999b09SVarka Bhadram 
10590f999b09SVarka Bhadram 	for (i = 0; i < rdev->wpan_phy.supported.tx_powers_size; i++) {
10600f999b09SVarka Bhadram 		if (power == rdev->wpan_phy.supported.tx_powers[i])
10610f999b09SVarka Bhadram 			return rdev_set_tx_power(rdev, power);
10620f999b09SVarka Bhadram 	}
10630f999b09SVarka Bhadram 
10640f999b09SVarka Bhadram 	return -EINVAL;
10650f999b09SVarka Bhadram }
10660f999b09SVarka Bhadram 
nl802154_set_pan_id(struct sk_buff * skb,struct genl_info * info)1067702bf371SAlexander Aring static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
1068702bf371SAlexander Aring {
1069702bf371SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1070702bf371SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
1071702bf371SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1072ee7b9053SAlexander Aring 	__le16 pan_id;
1073702bf371SAlexander Aring 
1074702bf371SAlexander Aring 	/* conflict here while tx/rx calls */
1075702bf371SAlexander Aring 	if (netif_running(dev))
1076702bf371SAlexander Aring 		return -EBUSY;
1077702bf371SAlexander Aring 
10789e3b71f3SAlexander Aring 	if (wpan_dev->lowpan_dev) {
10799e3b71f3SAlexander Aring 		if (netif_running(wpan_dev->lowpan_dev))
10809e3b71f3SAlexander Aring 			return -EBUSY;
10819e3b71f3SAlexander Aring 	}
10829e3b71f3SAlexander Aring 
1083702bf371SAlexander Aring 	/* don't change address fields on monitor */
10840cf0879aSAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR ||
10850cf0879aSAlexander Aring 	    !info->attrs[NL802154_ATTR_PAN_ID])
1086702bf371SAlexander Aring 		return -EINVAL;
1087702bf371SAlexander Aring 
1088ee7b9053SAlexander Aring 	pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]);
1089702bf371SAlexander Aring 
1090673692faSAlexander Aring 	/* TODO
1091673692faSAlexander Aring 	 * I am not sure about to check here on broadcast pan_id.
1092673692faSAlexander Aring 	 * Broadcast is a valid setting, comment from 802.15.4:
1093673692faSAlexander Aring 	 * If this value is 0xffff, the device is not associated.
1094673692faSAlexander Aring 	 *
1095673692faSAlexander Aring 	 * This could useful to simple deassociate an device.
1096673692faSAlexander Aring 	 */
1097673692faSAlexander Aring 	if (pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
1098673692faSAlexander Aring 		return -EINVAL;
1099673692faSAlexander Aring 
1100702bf371SAlexander Aring 	return rdev_set_pan_id(rdev, wpan_dev, pan_id);
1101702bf371SAlexander Aring }
1102702bf371SAlexander Aring 
nl802154_set_short_addr(struct sk_buff * skb,struct genl_info * info)11039830c62aSAlexander Aring static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info)
11049830c62aSAlexander Aring {
11059830c62aSAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
11069830c62aSAlexander Aring 	struct net_device *dev = info->user_ptr[1];
11079830c62aSAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1108ee7b9053SAlexander Aring 	__le16 short_addr;
11099830c62aSAlexander Aring 
11109830c62aSAlexander Aring 	/* conflict here while tx/rx calls */
11119830c62aSAlexander Aring 	if (netif_running(dev))
11129830c62aSAlexander Aring 		return -EBUSY;
11139830c62aSAlexander Aring 
11149e3b71f3SAlexander Aring 	if (wpan_dev->lowpan_dev) {
11159e3b71f3SAlexander Aring 		if (netif_running(wpan_dev->lowpan_dev))
11169e3b71f3SAlexander Aring 			return -EBUSY;
11179e3b71f3SAlexander Aring 	}
11189e3b71f3SAlexander Aring 
11199830c62aSAlexander Aring 	/* don't change address fields on monitor */
11200cf0879aSAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR ||
11210cf0879aSAlexander Aring 	    !info->attrs[NL802154_ATTR_SHORT_ADDR])
11229830c62aSAlexander Aring 		return -EINVAL;
11239830c62aSAlexander Aring 
1124ee7b9053SAlexander Aring 	short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]);
11259830c62aSAlexander Aring 
1126673692faSAlexander Aring 	/* TODO
1127673692faSAlexander Aring 	 * I am not sure about to check here on broadcast short_addr.
1128673692faSAlexander Aring 	 * Broadcast is a valid setting, comment from 802.15.4:
1129673692faSAlexander Aring 	 * A value of 0xfffe indicates that the device has
1130673692faSAlexander Aring 	 * associated but has not been allocated an address. A
1131673692faSAlexander Aring 	 * value of 0xffff indicates that the device does not
1132673692faSAlexander Aring 	 * have a short address.
1133673692faSAlexander Aring 	 *
1134673692faSAlexander Aring 	 * I think we should allow to set these settings but
1135673692faSAlexander Aring 	 * don't allow to allow socket communication with it.
1136673692faSAlexander Aring 	 */
1137673692faSAlexander Aring 	if (short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC) ||
1138673692faSAlexander Aring 	    short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST))
1139673692faSAlexander Aring 		return -EINVAL;
1140673692faSAlexander Aring 
11419830c62aSAlexander Aring 	return rdev_set_short_addr(rdev, wpan_dev, short_addr);
11429830c62aSAlexander Aring }
11439830c62aSAlexander Aring 
1144656a999eSAlexander Aring static int
nl802154_set_backoff_exponent(struct sk_buff * skb,struct genl_info * info)1145656a999eSAlexander Aring nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info)
1146656a999eSAlexander Aring {
1147656a999eSAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1148656a999eSAlexander Aring 	struct net_device *dev = info->user_ptr[1];
1149656a999eSAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1150656a999eSAlexander Aring 	u8 min_be, max_be;
1151656a999eSAlexander Aring 
1152656a999eSAlexander Aring 	/* should be set on netif open inside phy settings */
1153656a999eSAlexander Aring 	if (netif_running(dev))
1154656a999eSAlexander Aring 		return -EBUSY;
1155656a999eSAlexander Aring 
1156656a999eSAlexander Aring 	if (!info->attrs[NL802154_ATTR_MIN_BE] ||
1157656a999eSAlexander Aring 	    !info->attrs[NL802154_ATTR_MAX_BE])
1158656a999eSAlexander Aring 		return -EINVAL;
1159656a999eSAlexander Aring 
1160656a999eSAlexander Aring 	min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]);
1161656a999eSAlexander Aring 	max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]);
1162656a999eSAlexander Aring 
1163656a999eSAlexander Aring 	/* check 802.15.4 constraints */
1164fea3318dSAlexander Aring 	if (min_be < rdev->wpan_phy.supported.min_minbe ||
1165fea3318dSAlexander Aring 	    min_be > rdev->wpan_phy.supported.max_minbe ||
1166fea3318dSAlexander Aring 	    max_be < rdev->wpan_phy.supported.min_maxbe ||
1167fea3318dSAlexander Aring 	    max_be > rdev->wpan_phy.supported.max_maxbe ||
1168fea3318dSAlexander Aring 	    min_be > max_be)
1169656a999eSAlexander Aring 		return -EINVAL;
1170656a999eSAlexander Aring 
1171656a999eSAlexander Aring 	return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be);
1172656a999eSAlexander Aring }
1173656a999eSAlexander Aring 
1174a01ba765SAlexander Aring static int
nl802154_set_max_csma_backoffs(struct sk_buff * skb,struct genl_info * info)1175a01ba765SAlexander Aring nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info)
1176a01ba765SAlexander Aring {
1177a01ba765SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1178a01ba765SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
1179a01ba765SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1180a01ba765SAlexander Aring 	u8 max_csma_backoffs;
1181a01ba765SAlexander Aring 
1182a01ba765SAlexander Aring 	/* conflict here while other running iface settings */
1183a01ba765SAlexander Aring 	if (netif_running(dev))
1184a01ba765SAlexander Aring 		return -EBUSY;
1185a01ba765SAlexander Aring 
1186a01ba765SAlexander Aring 	if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS])
1187a01ba765SAlexander Aring 		return -EINVAL;
1188a01ba765SAlexander Aring 
1189a01ba765SAlexander Aring 	max_csma_backoffs = nla_get_u8(
1190a01ba765SAlexander Aring 			info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]);
1191a01ba765SAlexander Aring 
1192a01ba765SAlexander Aring 	/* check 802.15.4 constraints */
1193fea3318dSAlexander Aring 	if (max_csma_backoffs < rdev->wpan_phy.supported.min_csma_backoffs ||
1194fea3318dSAlexander Aring 	    max_csma_backoffs > rdev->wpan_phy.supported.max_csma_backoffs)
1195a01ba765SAlexander Aring 		return -EINVAL;
1196a01ba765SAlexander Aring 
1197a01ba765SAlexander Aring 	return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs);
1198a01ba765SAlexander Aring }
1199a01ba765SAlexander Aring 
120017a3a46bSAlexander Aring static int
nl802154_set_max_frame_retries(struct sk_buff * skb,struct genl_info * info)120117a3a46bSAlexander Aring nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info)
120217a3a46bSAlexander Aring {
120317a3a46bSAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
120417a3a46bSAlexander Aring 	struct net_device *dev = info->user_ptr[1];
120517a3a46bSAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
120617a3a46bSAlexander Aring 	s8 max_frame_retries;
120717a3a46bSAlexander Aring 
120817a3a46bSAlexander Aring 	if (netif_running(dev))
120917a3a46bSAlexander Aring 		return -EBUSY;
121017a3a46bSAlexander Aring 
121117a3a46bSAlexander Aring 	if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES])
121217a3a46bSAlexander Aring 		return -EINVAL;
121317a3a46bSAlexander Aring 
121417a3a46bSAlexander Aring 	max_frame_retries = nla_get_s8(
121517a3a46bSAlexander Aring 			info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]);
121617a3a46bSAlexander Aring 
121717a3a46bSAlexander Aring 	/* check 802.15.4 constraints */
1218fea3318dSAlexander Aring 	if (max_frame_retries < rdev->wpan_phy.supported.min_frame_retries ||
1219fea3318dSAlexander Aring 	    max_frame_retries > rdev->wpan_phy.supported.max_frame_retries)
122017a3a46bSAlexander Aring 		return -EINVAL;
122117a3a46bSAlexander Aring 
122217a3a46bSAlexander Aring 	return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries);
122317a3a46bSAlexander Aring }
122417a3a46bSAlexander Aring 
nl802154_set_lbt_mode(struct sk_buff * skb,struct genl_info * info)1225c8937a1dSAlexander Aring static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info)
1226c8937a1dSAlexander Aring {
1227c8937a1dSAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1228c8937a1dSAlexander Aring 	struct net_device *dev = info->user_ptr[1];
1229c8937a1dSAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
12304e1795deSStefan Schmidt 	int mode;
1231c8937a1dSAlexander Aring 
1232c8937a1dSAlexander Aring 	if (netif_running(dev))
1233c8937a1dSAlexander Aring 		return -EBUSY;
1234c8937a1dSAlexander Aring 
1235c8937a1dSAlexander Aring 	if (!info->attrs[NL802154_ATTR_LBT_MODE])
1236c8937a1dSAlexander Aring 		return -EINVAL;
1237c8937a1dSAlexander Aring 
12384e1795deSStefan Schmidt 	mode = nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]);
12394e1795deSStefan Schmidt 
12404e1795deSStefan Schmidt 	if (mode != 0 && mode != 1)
12414e1795deSStefan Schmidt 		return -EINVAL;
12424e1795deSStefan Schmidt 
1243fea3318dSAlexander Aring 	if (!wpan_phy_supported_bool(mode, rdev->wpan_phy.supported.lbt))
1244fea3318dSAlexander Aring 		return -EINVAL;
1245fea3318dSAlexander Aring 
1246c8937a1dSAlexander Aring 	return rdev_set_lbt_mode(rdev, wpan_dev, mode);
1247c8937a1dSAlexander Aring }
1248c8937a1dSAlexander Aring 
1249c91208d8SAlexander Aring static int
nl802154_set_ackreq_default(struct sk_buff * skb,struct genl_info * info)1250c91208d8SAlexander Aring nl802154_set_ackreq_default(struct sk_buff *skb, struct genl_info *info)
1251c91208d8SAlexander Aring {
1252c91208d8SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1253c91208d8SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
1254c91208d8SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
12554e1795deSStefan Schmidt 	int ackreq;
1256c91208d8SAlexander Aring 
1257c91208d8SAlexander Aring 	if (netif_running(dev))
1258c91208d8SAlexander Aring 		return -EBUSY;
1259c91208d8SAlexander Aring 
1260c91208d8SAlexander Aring 	if (!info->attrs[NL802154_ATTR_ACKREQ_DEFAULT])
1261c91208d8SAlexander Aring 		return -EINVAL;
1262c91208d8SAlexander Aring 
12634e1795deSStefan Schmidt 	ackreq = nla_get_u8(info->attrs[NL802154_ATTR_ACKREQ_DEFAULT]);
12644e1795deSStefan Schmidt 
12654e1795deSStefan Schmidt 	if (ackreq != 0 && ackreq != 1)
12664e1795deSStefan Schmidt 		return -EINVAL;
12674e1795deSStefan Schmidt 
1268c91208d8SAlexander Aring 	return rdev_set_ackreq_default(rdev, wpan_dev, ackreq);
1269c91208d8SAlexander Aring }
1270c91208d8SAlexander Aring 
nl802154_wpan_phy_netns(struct sk_buff * skb,struct genl_info * info)127166e5c267SAlexander Aring static int nl802154_wpan_phy_netns(struct sk_buff *skb, struct genl_info *info)
127266e5c267SAlexander Aring {
127366e5c267SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
127466e5c267SAlexander Aring 	struct net *net;
127566e5c267SAlexander Aring 	int err;
127666e5c267SAlexander Aring 
127766e5c267SAlexander Aring 	if (info->attrs[NL802154_ATTR_PID]) {
127866e5c267SAlexander Aring 		u32 pid = nla_get_u32(info->attrs[NL802154_ATTR_PID]);
127966e5c267SAlexander Aring 
128066e5c267SAlexander Aring 		net = get_net_ns_by_pid(pid);
128166e5c267SAlexander Aring 	} else if (info->attrs[NL802154_ATTR_NETNS_FD]) {
128266e5c267SAlexander Aring 		u32 fd = nla_get_u32(info->attrs[NL802154_ATTR_NETNS_FD]);
128366e5c267SAlexander Aring 
128466e5c267SAlexander Aring 		net = get_net_ns_by_fd(fd);
128566e5c267SAlexander Aring 	} else {
128666e5c267SAlexander Aring 		return -EINVAL;
128766e5c267SAlexander Aring 	}
128866e5c267SAlexander Aring 
128966e5c267SAlexander Aring 	if (IS_ERR(net))
129066e5c267SAlexander Aring 		return PTR_ERR(net);
129166e5c267SAlexander Aring 
129266e5c267SAlexander Aring 	err = 0;
129366e5c267SAlexander Aring 
129466e5c267SAlexander Aring 	/* check if anything to do */
129566e5c267SAlexander Aring 	if (!net_eq(wpan_phy_net(&rdev->wpan_phy), net))
129666e5c267SAlexander Aring 		err = cfg802154_switch_netns(rdev, net);
129766e5c267SAlexander Aring 
129866e5c267SAlexander Aring 	put_net(net);
129966e5c267SAlexander Aring 	return err;
130066e5c267SAlexander Aring }
130166e5c267SAlexander Aring 
nl802154_prep_scan_event_msg(struct sk_buff * msg,struct cfg802154_registered_device * rdev,struct wpan_dev * wpan_dev,u32 portid,u32 seq,int flags,u8 cmd,struct ieee802154_coord_desc * desc)130251147284SMiquel Raynal static int nl802154_prep_scan_event_msg(struct sk_buff *msg,
130351147284SMiquel Raynal 					struct cfg802154_registered_device *rdev,
130451147284SMiquel Raynal 					struct wpan_dev *wpan_dev,
130551147284SMiquel Raynal 					u32 portid, u32 seq, int flags, u8 cmd,
130651147284SMiquel Raynal 					struct ieee802154_coord_desc *desc)
130751147284SMiquel Raynal {
130851147284SMiquel Raynal 	struct nlattr *nla;
130951147284SMiquel Raynal 	void *hdr;
131051147284SMiquel Raynal 
131151147284SMiquel Raynal 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
131251147284SMiquel Raynal 	if (!hdr)
131351147284SMiquel Raynal 		return -ENOBUFS;
131451147284SMiquel Raynal 
131551147284SMiquel Raynal 	if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx))
131651147284SMiquel Raynal 		goto nla_put_failure;
131751147284SMiquel Raynal 
131851147284SMiquel Raynal 	if (wpan_dev->netdev &&
131951147284SMiquel Raynal 	    nla_put_u32(msg, NL802154_ATTR_IFINDEX, wpan_dev->netdev->ifindex))
132051147284SMiquel Raynal 		goto nla_put_failure;
132151147284SMiquel Raynal 
132251147284SMiquel Raynal 	if (nla_put_u64_64bit(msg, NL802154_ATTR_WPAN_DEV,
132351147284SMiquel Raynal 			      wpan_dev_id(wpan_dev), NL802154_ATTR_PAD))
132451147284SMiquel Raynal 		goto nla_put_failure;
132551147284SMiquel Raynal 
132651147284SMiquel Raynal 	nla = nla_nest_start_noflag(msg, NL802154_ATTR_COORDINATOR);
132751147284SMiquel Raynal 	if (!nla)
132851147284SMiquel Raynal 		goto nla_put_failure;
132951147284SMiquel Raynal 
133051147284SMiquel Raynal 	if (nla_put(msg, NL802154_COORD_PANID, IEEE802154_PAN_ID_LEN,
133151147284SMiquel Raynal 		    &desc->addr.pan_id))
133251147284SMiquel Raynal 		goto nla_put_failure;
133351147284SMiquel Raynal 
133451147284SMiquel Raynal 	if (desc->addr.mode == IEEE802154_ADDR_SHORT) {
133551147284SMiquel Raynal 		if (nla_put(msg, NL802154_COORD_ADDR,
133651147284SMiquel Raynal 			    IEEE802154_SHORT_ADDR_LEN,
133751147284SMiquel Raynal 			    &desc->addr.short_addr))
133851147284SMiquel Raynal 			goto nla_put_failure;
133951147284SMiquel Raynal 	} else {
134051147284SMiquel Raynal 		if (nla_put(msg, NL802154_COORD_ADDR,
134151147284SMiquel Raynal 			    IEEE802154_EXTENDED_ADDR_LEN,
134251147284SMiquel Raynal 			    &desc->addr.extended_addr))
134351147284SMiquel Raynal 			goto nla_put_failure;
134451147284SMiquel Raynal 	}
134551147284SMiquel Raynal 
134651147284SMiquel Raynal 	if (nla_put_u8(msg, NL802154_COORD_CHANNEL, desc->channel))
134751147284SMiquel Raynal 		goto nla_put_failure;
134851147284SMiquel Raynal 
134951147284SMiquel Raynal 	if (nla_put_u8(msg, NL802154_COORD_PAGE, desc->page))
135051147284SMiquel Raynal 		goto nla_put_failure;
135151147284SMiquel Raynal 
135251147284SMiquel Raynal 	if (nla_put_u16(msg, NL802154_COORD_SUPERFRAME_SPEC,
135351147284SMiquel Raynal 			desc->superframe_spec))
135451147284SMiquel Raynal 		goto nla_put_failure;
135551147284SMiquel Raynal 
135651147284SMiquel Raynal 	if (nla_put_u8(msg, NL802154_COORD_LINK_QUALITY, desc->link_quality))
135751147284SMiquel Raynal 		goto nla_put_failure;
135851147284SMiquel Raynal 
135951147284SMiquel Raynal 	if (desc->gts_permit && nla_put_flag(msg, NL802154_COORD_GTS_PERMIT))
136051147284SMiquel Raynal 		goto nla_put_failure;
136151147284SMiquel Raynal 
136251147284SMiquel Raynal 	/* TODO: NL802154_COORD_PAYLOAD_DATA if any */
136351147284SMiquel Raynal 
136451147284SMiquel Raynal 	nla_nest_end(msg, nla);
136551147284SMiquel Raynal 
136651147284SMiquel Raynal 	genlmsg_end(msg, hdr);
136751147284SMiquel Raynal 
136851147284SMiquel Raynal 	return 0;
136951147284SMiquel Raynal 
137051147284SMiquel Raynal  nla_put_failure:
137151147284SMiquel Raynal 	genlmsg_cancel(msg, hdr);
137251147284SMiquel Raynal 
137351147284SMiquel Raynal 	return -EMSGSIZE;
137451147284SMiquel Raynal }
137551147284SMiquel Raynal 
nl802154_scan_event(struct wpan_phy * wpan_phy,struct wpan_dev * wpan_dev,struct ieee802154_coord_desc * desc)137651147284SMiquel Raynal int nl802154_scan_event(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
137751147284SMiquel Raynal 			struct ieee802154_coord_desc *desc)
137851147284SMiquel Raynal {
137951147284SMiquel Raynal 	struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(wpan_phy);
138051147284SMiquel Raynal 	struct sk_buff *msg;
138151147284SMiquel Raynal 	int ret;
138251147284SMiquel Raynal 
138351147284SMiquel Raynal 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
138451147284SMiquel Raynal 	if (!msg)
138551147284SMiquel Raynal 		return -ENOMEM;
138651147284SMiquel Raynal 
138751147284SMiquel Raynal 	ret = nl802154_prep_scan_event_msg(msg, rdev, wpan_dev, 0, 0, 0,
138851147284SMiquel Raynal 					   NL802154_CMD_SCAN_EVENT,
138951147284SMiquel Raynal 					   desc);
139051147284SMiquel Raynal 	if (ret < 0) {
139151147284SMiquel Raynal 		nlmsg_free(msg);
139251147284SMiquel Raynal 		return ret;
139351147284SMiquel Raynal 	}
139451147284SMiquel Raynal 
139551147284SMiquel Raynal 	return genlmsg_multicast_netns(&nl802154_fam, wpan_phy_net(wpan_phy),
139651147284SMiquel Raynal 				       msg, 0, NL802154_MCGRP_SCAN, GFP_ATOMIC);
139751147284SMiquel Raynal }
139851147284SMiquel Raynal EXPORT_SYMBOL_GPL(nl802154_scan_event);
139951147284SMiquel Raynal 
nl802154_trigger_scan(struct sk_buff * skb,struct genl_info * info)1400ed3557c9SMiquel Raynal static int nl802154_trigger_scan(struct sk_buff *skb, struct genl_info *info)
1401ed3557c9SMiquel Raynal {
1402ed3557c9SMiquel Raynal 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1403ed3557c9SMiquel Raynal 	struct net_device *dev = info->user_ptr[1];
1404ed3557c9SMiquel Raynal 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1405ed3557c9SMiquel Raynal 	struct wpan_phy *wpan_phy = &rdev->wpan_phy;
1406ed3557c9SMiquel Raynal 	struct cfg802154_scan_request *request;
1407ed3557c9SMiquel Raynal 	u8 type;
1408ed3557c9SMiquel Raynal 	int err;
1409ed3557c9SMiquel Raynal 
1410a0b61066SMiquel Raynal 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) {
1411a0b61066SMiquel Raynal 		NL_SET_ERR_MSG(info->extack, "Monitors are not allowed to perform scans");
14121edecbd0SMiquel Raynal 		return -EOPNOTSUPP;
1413a0b61066SMiquel Raynal 	}
1414a0b61066SMiquel Raynal 
141502f18662SMiquel Raynal 	if (!info->attrs[NL802154_ATTR_SCAN_TYPE]) {
1416a0b61066SMiquel Raynal 		NL_SET_ERR_MSG(info->extack, "Malformed request, missing scan type");
1417a0b61066SMiquel Raynal 		return -EINVAL;
1418a0b61066SMiquel Raynal 	}
1419ed3557c9SMiquel Raynal 
1420822452fbSMiquel Raynal 	if (wpan_phy->flags & WPAN_PHY_FLAG_DATAGRAMS_ONLY) {
1421822452fbSMiquel Raynal 		NL_SET_ERR_MSG(info->extack, "PHY only supports datagrams");
1422822452fbSMiquel Raynal 		return -EOPNOTSUPP;
1423822452fbSMiquel Raynal 	}
1424822452fbSMiquel Raynal 
1425ed3557c9SMiquel Raynal 	request = kzalloc(sizeof(*request), GFP_KERNEL);
1426ed3557c9SMiquel Raynal 	if (!request)
1427ed3557c9SMiquel Raynal 		return -ENOMEM;
1428ed3557c9SMiquel Raynal 
1429ed3557c9SMiquel Raynal 	request->wpan_dev = wpan_dev;
1430ed3557c9SMiquel Raynal 	request->wpan_phy = wpan_phy;
1431ed3557c9SMiquel Raynal 
1432ed3557c9SMiquel Raynal 	type = nla_get_u8(info->attrs[NL802154_ATTR_SCAN_TYPE]);
1433ed3557c9SMiquel Raynal 	switch (type) {
1434b8866426SMiquel Raynal 	case NL802154_SCAN_ACTIVE:
1435ed3557c9SMiquel Raynal 	case NL802154_SCAN_PASSIVE:
1436ed3557c9SMiquel Raynal 		request->type = type;
1437ed3557c9SMiquel Raynal 		break;
1438ed3557c9SMiquel Raynal 	default:
1439a0b61066SMiquel Raynal 		NL_SET_ERR_MSG_FMT(info->extack, "Unsupported scan type: %d", type);
1440ed3557c9SMiquel Raynal 		err = -EINVAL;
1441ed3557c9SMiquel Raynal 		goto free_request;
1442ed3557c9SMiquel Raynal 	}
1443ed3557c9SMiquel Raynal 
1444ed3557c9SMiquel Raynal 	/* Use current page by default */
1445648324c9SMiquel Raynal 	if (info->attrs[NL802154_ATTR_PAGE])
1446648324c9SMiquel Raynal 		request->page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
1447648324c9SMiquel Raynal 	else
1448ed3557c9SMiquel Raynal 		request->page = wpan_phy->current_page;
1449ed3557c9SMiquel Raynal 
1450ed3557c9SMiquel Raynal 	/* Scan all supported channels by default */
1451648324c9SMiquel Raynal 	if (info->attrs[NL802154_ATTR_SCAN_CHANNELS])
1452648324c9SMiquel Raynal 		request->channels = nla_get_u32(info->attrs[NL802154_ATTR_SCAN_CHANNELS]);
1453648324c9SMiquel Raynal 	else
1454ed3557c9SMiquel Raynal 		request->channels = wpan_phy->supported.channels[request->page];
1455ed3557c9SMiquel Raynal 
1456ed3557c9SMiquel Raynal 	/* Use maximum duration order by default */
1457648324c9SMiquel Raynal 	if (info->attrs[NL802154_ATTR_SCAN_DURATION])
1458648324c9SMiquel Raynal 		request->duration = nla_get_u8(info->attrs[NL802154_ATTR_SCAN_DURATION]);
1459648324c9SMiquel Raynal 	else
1460ed3557c9SMiquel Raynal 		request->duration = IEEE802154_MAX_SCAN_DURATION;
1461ed3557c9SMiquel Raynal 
1462ed3557c9SMiquel Raynal 	err = rdev_trigger_scan(rdev, request);
1463ed3557c9SMiquel Raynal 	if (err) {
1464ed3557c9SMiquel Raynal 		pr_err("Failure starting scanning (%d)\n", err);
1465ed9a8ad7SMiquel Raynal 		goto free_request;
1466ed3557c9SMiquel Raynal 	}
1467ed3557c9SMiquel Raynal 
1468ed3557c9SMiquel Raynal 	return 0;
1469ed3557c9SMiquel Raynal 
1470ed3557c9SMiquel Raynal free_request:
1471ed3557c9SMiquel Raynal 	kfree(request);
1472ed3557c9SMiquel Raynal 
1473ed3557c9SMiquel Raynal 	return err;
1474ed3557c9SMiquel Raynal }
1475ed3557c9SMiquel Raynal 
nl802154_prep_scan_msg(struct sk_buff * msg,struct cfg802154_registered_device * rdev,struct wpan_dev * wpan_dev,u32 portid,u32 seq,int flags,u8 cmd,u8 arg)1476ed3557c9SMiquel Raynal static int nl802154_prep_scan_msg(struct sk_buff *msg,
1477ed3557c9SMiquel Raynal 				  struct cfg802154_registered_device *rdev,
1478ed3557c9SMiquel Raynal 				  struct wpan_dev *wpan_dev, u32 portid,
1479ed3557c9SMiquel Raynal 				  u32 seq, int flags, u8 cmd, u8 arg)
1480ed3557c9SMiquel Raynal {
1481ed3557c9SMiquel Raynal 	void *hdr;
1482ed3557c9SMiquel Raynal 
1483ed3557c9SMiquel Raynal 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
1484ed3557c9SMiquel Raynal 	if (!hdr)
1485ed3557c9SMiquel Raynal 		return -ENOBUFS;
1486ed3557c9SMiquel Raynal 
1487ed3557c9SMiquel Raynal 	if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx))
1488ed3557c9SMiquel Raynal 		goto nla_put_failure;
1489ed3557c9SMiquel Raynal 
1490ed3557c9SMiquel Raynal 	if (wpan_dev->netdev &&
1491ed3557c9SMiquel Raynal 	    nla_put_u32(msg, NL802154_ATTR_IFINDEX, wpan_dev->netdev->ifindex))
1492ed3557c9SMiquel Raynal 		goto nla_put_failure;
1493ed3557c9SMiquel Raynal 
1494ed3557c9SMiquel Raynal 	if (nla_put_u64_64bit(msg, NL802154_ATTR_WPAN_DEV,
1495ed3557c9SMiquel Raynal 			      wpan_dev_id(wpan_dev), NL802154_ATTR_PAD))
1496ed3557c9SMiquel Raynal 		goto nla_put_failure;
1497ed3557c9SMiquel Raynal 
1498ed3557c9SMiquel Raynal 	if (cmd == NL802154_CMD_SCAN_DONE &&
1499ed3557c9SMiquel Raynal 	    nla_put_u8(msg, NL802154_ATTR_SCAN_DONE_REASON, arg))
1500ed3557c9SMiquel Raynal 		goto nla_put_failure;
1501ed3557c9SMiquel Raynal 
1502ed3557c9SMiquel Raynal 	genlmsg_end(msg, hdr);
1503ed3557c9SMiquel Raynal 
1504ed3557c9SMiquel Raynal 	return 0;
1505ed3557c9SMiquel Raynal 
1506ed3557c9SMiquel Raynal nla_put_failure:
1507ed3557c9SMiquel Raynal 	genlmsg_cancel(msg, hdr);
1508ed3557c9SMiquel Raynal 
1509ed3557c9SMiquel Raynal 	return -EMSGSIZE;
1510ed3557c9SMiquel Raynal }
1511ed3557c9SMiquel Raynal 
nl802154_send_scan_msg(struct cfg802154_registered_device * rdev,struct wpan_dev * wpan_dev,u8 cmd,u8 arg)1512ed3557c9SMiquel Raynal static int nl802154_send_scan_msg(struct cfg802154_registered_device *rdev,
1513ed3557c9SMiquel Raynal 				  struct wpan_dev *wpan_dev, u8 cmd, u8 arg)
1514ed3557c9SMiquel Raynal {
1515ed3557c9SMiquel Raynal 	struct sk_buff *msg;
1516ed3557c9SMiquel Raynal 	int ret;
1517ed3557c9SMiquel Raynal 
1518ed3557c9SMiquel Raynal 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
1519ed3557c9SMiquel Raynal 	if (!msg)
1520ed3557c9SMiquel Raynal 		return -ENOMEM;
1521ed3557c9SMiquel Raynal 
1522ed3557c9SMiquel Raynal 	ret = nl802154_prep_scan_msg(msg, rdev, wpan_dev, 0, 0, 0, cmd, arg);
1523ed3557c9SMiquel Raynal 	if (ret < 0) {
1524ed3557c9SMiquel Raynal 		nlmsg_free(msg);
1525ed3557c9SMiquel Raynal 		return ret;
1526ed3557c9SMiquel Raynal 	}
1527ed3557c9SMiquel Raynal 
1528ed3557c9SMiquel Raynal 	return genlmsg_multicast_netns(&nl802154_fam,
1529ed3557c9SMiquel Raynal 				       wpan_phy_net(&rdev->wpan_phy), msg, 0,
1530ed3557c9SMiquel Raynal 				       NL802154_MCGRP_SCAN, GFP_KERNEL);
1531ed3557c9SMiquel Raynal }
1532ed3557c9SMiquel Raynal 
nl802154_scan_started(struct wpan_phy * wpan_phy,struct wpan_dev * wpan_dev)1533ed3557c9SMiquel Raynal int nl802154_scan_started(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev)
1534ed3557c9SMiquel Raynal {
1535ed3557c9SMiquel Raynal 	struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(wpan_phy);
1536ed3557c9SMiquel Raynal 	int err;
1537ed3557c9SMiquel Raynal 
1538ed3557c9SMiquel Raynal 	/* Ignore errors when there are no listeners */
1539ed3557c9SMiquel Raynal 	err = nl802154_send_scan_msg(rdev, wpan_dev, NL802154_CMD_TRIGGER_SCAN, 0);
1540ed3557c9SMiquel Raynal 	if (err == -ESRCH)
1541ed3557c9SMiquel Raynal 		err = 0;
1542ed3557c9SMiquel Raynal 
1543ed3557c9SMiquel Raynal 	return err;
1544ed3557c9SMiquel Raynal }
1545ed3557c9SMiquel Raynal EXPORT_SYMBOL_GPL(nl802154_scan_started);
1546ed3557c9SMiquel Raynal 
nl802154_scan_done(struct wpan_phy * wpan_phy,struct wpan_dev * wpan_dev,enum nl802154_scan_done_reasons reason)1547ed3557c9SMiquel Raynal int nl802154_scan_done(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
1548ed3557c9SMiquel Raynal 		       enum nl802154_scan_done_reasons reason)
1549ed3557c9SMiquel Raynal {
1550ed3557c9SMiquel Raynal 	struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(wpan_phy);
1551ed3557c9SMiquel Raynal 	int err;
1552ed3557c9SMiquel Raynal 
1553ed3557c9SMiquel Raynal 	/* Ignore errors when there are no listeners */
1554ed3557c9SMiquel Raynal 	err = nl802154_send_scan_msg(rdev, wpan_dev, NL802154_CMD_SCAN_DONE, reason);
1555ed3557c9SMiquel Raynal 	if (err == -ESRCH)
1556ed3557c9SMiquel Raynal 		err = 0;
1557ed3557c9SMiquel Raynal 
1558ed3557c9SMiquel Raynal 	return err;
1559ed3557c9SMiquel Raynal }
1560ed3557c9SMiquel Raynal EXPORT_SYMBOL_GPL(nl802154_scan_done);
1561ed3557c9SMiquel Raynal 
nl802154_abort_scan(struct sk_buff * skb,struct genl_info * info)1562ed3557c9SMiquel Raynal static int nl802154_abort_scan(struct sk_buff *skb, struct genl_info *info)
1563ed3557c9SMiquel Raynal {
1564ed3557c9SMiquel Raynal 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1565ed3557c9SMiquel Raynal 	struct net_device *dev = info->user_ptr[1];
1566ed3557c9SMiquel Raynal 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1567ed3557c9SMiquel Raynal 
1568ed3557c9SMiquel Raynal 	/* Resources are released in the notification helper above */
1569ed3557c9SMiquel Raynal 	return rdev_abort_scan(rdev, wpan_dev);
1570ed3557c9SMiquel Raynal }
1571ed3557c9SMiquel Raynal 
15729bc11450SMiquel Raynal static int
nl802154_send_beacons(struct sk_buff * skb,struct genl_info * info)15739bc11450SMiquel Raynal nl802154_send_beacons(struct sk_buff *skb, struct genl_info *info)
15749bc11450SMiquel Raynal {
15759bc11450SMiquel Raynal 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
15769bc11450SMiquel Raynal 	struct net_device *dev = info->user_ptr[1];
15779bc11450SMiquel Raynal 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
15789bc11450SMiquel Raynal 	struct wpan_phy *wpan_phy = &rdev->wpan_phy;
15799bc11450SMiquel Raynal 	struct cfg802154_beacon_request *request;
15809bc11450SMiquel Raynal 	int err;
15819bc11450SMiquel Raynal 
1582a0b61066SMiquel Raynal 	if (wpan_dev->iftype != NL802154_IFTYPE_COORD) {
1583a0b61066SMiquel Raynal 		NL_SET_ERR_MSG(info->extack, "Only coordinators can send beacons");
15849bc11450SMiquel Raynal 		return -EOPNOTSUPP;
1585a0b61066SMiquel Raynal 	}
15869bc11450SMiquel Raynal 
15879bc11450SMiquel Raynal 	if (wpan_dev->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
1588a0b61066SMiquel Raynal 		NL_SET_ERR_MSG(info->extack, "Device is not part of any PAN");
15899bc11450SMiquel Raynal 		return -EPERM;
15909bc11450SMiquel Raynal 	}
15919bc11450SMiquel Raynal 
1592822452fbSMiquel Raynal 	if (wpan_phy->flags & WPAN_PHY_FLAG_DATAGRAMS_ONLY) {
1593822452fbSMiquel Raynal 		NL_SET_ERR_MSG(info->extack, "PHY only supports datagrams");
1594822452fbSMiquel Raynal 		return -EOPNOTSUPP;
1595822452fbSMiquel Raynal 	}
1596822452fbSMiquel Raynal 
15979bc11450SMiquel Raynal 	request = kzalloc(sizeof(*request), GFP_KERNEL);
15989bc11450SMiquel Raynal 	if (!request)
15999bc11450SMiquel Raynal 		return -ENOMEM;
16009bc11450SMiquel Raynal 
16019bc11450SMiquel Raynal 	request->wpan_dev = wpan_dev;
16029bc11450SMiquel Raynal 	request->wpan_phy = wpan_phy;
16039bc11450SMiquel Raynal 
16049bc11450SMiquel Raynal 	/* Use maximum duration order by default */
1605648324c9SMiquel Raynal 	if (info->attrs[NL802154_ATTR_BEACON_INTERVAL])
1606648324c9SMiquel Raynal 		request->interval = nla_get_u8(info->attrs[NL802154_ATTR_BEACON_INTERVAL]);
1607648324c9SMiquel Raynal 	else
16089bc11450SMiquel Raynal 		request->interval = IEEE802154_MAX_SCAN_DURATION;
16099bc11450SMiquel Raynal 
16109bc11450SMiquel Raynal 	err = rdev_send_beacons(rdev, request);
16119bc11450SMiquel Raynal 	if (err) {
16129bc11450SMiquel Raynal 		pr_err("Failure starting sending beacons (%d)\n", err);
1613ed9a8ad7SMiquel Raynal 		goto free_request;
16149bc11450SMiquel Raynal 	}
16159bc11450SMiquel Raynal 
16169bc11450SMiquel Raynal 	return 0;
16179bc11450SMiquel Raynal 
1618ed9a8ad7SMiquel Raynal free_request:
16199bc11450SMiquel Raynal 	kfree(request);
16209bc11450SMiquel Raynal 
16219bc11450SMiquel Raynal 	return err;
16229bc11450SMiquel Raynal }
16239bc11450SMiquel Raynal 
nl802154_beaconing_done(struct wpan_dev * wpan_dev)16249bc11450SMiquel Raynal void nl802154_beaconing_done(struct wpan_dev *wpan_dev)
16259bc11450SMiquel Raynal {
1626ed9a8ad7SMiquel Raynal 	/* NOP */
16279bc11450SMiquel Raynal }
16289bc11450SMiquel Raynal EXPORT_SYMBOL_GPL(nl802154_beaconing_done);
16299bc11450SMiquel Raynal 
16309bc11450SMiquel Raynal static int
nl802154_stop_beacons(struct sk_buff * skb,struct genl_info * info)16319bc11450SMiquel Raynal nl802154_stop_beacons(struct sk_buff *skb, struct genl_info *info)
16329bc11450SMiquel Raynal {
16339bc11450SMiquel Raynal 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
16349bc11450SMiquel Raynal 	struct net_device *dev = info->user_ptr[1];
16359bc11450SMiquel Raynal 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
16369bc11450SMiquel Raynal 
16379bc11450SMiquel Raynal 	/* Resources are released in the notification helper above */
16389bc11450SMiquel Raynal 	return rdev_stop_beacons(rdev, wpan_dev);
16399bc11450SMiquel Raynal }
16409bc11450SMiquel Raynal 
1641a26c5fd7SAlexander Aring #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
1642a26c5fd7SAlexander Aring static const struct nla_policy nl802154_dev_addr_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = {
1643a26c5fd7SAlexander Aring 	[NL802154_DEV_ADDR_ATTR_PAN_ID] = { .type = NLA_U16 },
1644a26c5fd7SAlexander Aring 	[NL802154_DEV_ADDR_ATTR_MODE] = { .type = NLA_U32 },
1645a26c5fd7SAlexander Aring 	[NL802154_DEV_ADDR_ATTR_SHORT] = { .type = NLA_U16 },
1646a26c5fd7SAlexander Aring 	[NL802154_DEV_ADDR_ATTR_EXTENDED] = { .type = NLA_U64 },
1647a26c5fd7SAlexander Aring };
1648a26c5fd7SAlexander Aring 
1649a26c5fd7SAlexander Aring static int
ieee802154_llsec_parse_dev_addr(struct nlattr * nla,struct ieee802154_addr * addr)1650a26c5fd7SAlexander Aring ieee802154_llsec_parse_dev_addr(struct nlattr *nla,
1651a26c5fd7SAlexander Aring 				struct ieee802154_addr *addr)
1652a26c5fd7SAlexander Aring {
1653a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_DEV_ADDR_ATTR_MAX + 1];
1654a26c5fd7SAlexander Aring 
16558cb08174SJohannes Berg 	if (!nla || nla_parse_nested_deprecated(attrs, NL802154_DEV_ADDR_ATTR_MAX, nla, nl802154_dev_addr_policy, NULL))
1656a26c5fd7SAlexander Aring 		return -EINVAL;
1657a26c5fd7SAlexander Aring 
16589fdd0491SDan Robertson 	if (!attrs[NL802154_DEV_ADDR_ATTR_PAN_ID] || !attrs[NL802154_DEV_ADDR_ATTR_MODE])
1659a26c5fd7SAlexander Aring 		return -EINVAL;
1660a26c5fd7SAlexander Aring 
1661a26c5fd7SAlexander Aring 	addr->pan_id = nla_get_le16(attrs[NL802154_DEV_ADDR_ATTR_PAN_ID]);
1662a26c5fd7SAlexander Aring 	addr->mode = nla_get_u32(attrs[NL802154_DEV_ADDR_ATTR_MODE]);
1663a26c5fd7SAlexander Aring 	switch (addr->mode) {
1664a26c5fd7SAlexander Aring 	case NL802154_DEV_ADDR_SHORT:
16659fdd0491SDan Robertson 		if (!attrs[NL802154_DEV_ADDR_ATTR_SHORT])
16669fdd0491SDan Robertson 			return -EINVAL;
1667a26c5fd7SAlexander Aring 		addr->short_addr = nla_get_le16(attrs[NL802154_DEV_ADDR_ATTR_SHORT]);
1668a26c5fd7SAlexander Aring 		break;
1669a26c5fd7SAlexander Aring 	case NL802154_DEV_ADDR_EXTENDED:
16709fdd0491SDan Robertson 		if (!attrs[NL802154_DEV_ADDR_ATTR_EXTENDED])
16719fdd0491SDan Robertson 			return -EINVAL;
1672a26c5fd7SAlexander Aring 		addr->extended_addr = nla_get_le64(attrs[NL802154_DEV_ADDR_ATTR_EXTENDED]);
1673a26c5fd7SAlexander Aring 		break;
1674a26c5fd7SAlexander Aring 	default:
1675a26c5fd7SAlexander Aring 		return -EINVAL;
1676a26c5fd7SAlexander Aring 	}
1677a26c5fd7SAlexander Aring 
1678a26c5fd7SAlexander Aring 	return 0;
1679a26c5fd7SAlexander Aring }
1680a26c5fd7SAlexander Aring 
1681a26c5fd7SAlexander Aring static const struct nla_policy nl802154_key_id_policy[NL802154_KEY_ID_ATTR_MAX + 1] = {
1682a26c5fd7SAlexander Aring 	[NL802154_KEY_ID_ATTR_MODE] = { .type = NLA_U32 },
1683a26c5fd7SAlexander Aring 	[NL802154_KEY_ID_ATTR_INDEX] = { .type = NLA_U8 },
1684a26c5fd7SAlexander Aring 	[NL802154_KEY_ID_ATTR_IMPLICIT] = { .type = NLA_NESTED },
1685a26c5fd7SAlexander Aring 	[NL802154_KEY_ID_ATTR_SOURCE_SHORT] = { .type = NLA_U32 },
1686a26c5fd7SAlexander Aring 	[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED] = { .type = NLA_U64 },
1687a26c5fd7SAlexander Aring };
1688a26c5fd7SAlexander Aring 
1689a26c5fd7SAlexander Aring static int
ieee802154_llsec_parse_key_id(struct nlattr * nla,struct ieee802154_llsec_key_id * desc)1690a26c5fd7SAlexander Aring ieee802154_llsec_parse_key_id(struct nlattr *nla,
1691a26c5fd7SAlexander Aring 			      struct ieee802154_llsec_key_id *desc)
1692a26c5fd7SAlexander Aring {
1693a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_KEY_ID_ATTR_MAX + 1];
1694a26c5fd7SAlexander Aring 
16958cb08174SJohannes Berg 	if (!nla || nla_parse_nested_deprecated(attrs, NL802154_KEY_ID_ATTR_MAX, nla, nl802154_key_id_policy, NULL))
1696a26c5fd7SAlexander Aring 		return -EINVAL;
1697a26c5fd7SAlexander Aring 
1698a26c5fd7SAlexander Aring 	if (!attrs[NL802154_KEY_ID_ATTR_MODE])
1699a26c5fd7SAlexander Aring 		return -EINVAL;
1700a26c5fd7SAlexander Aring 
1701a26c5fd7SAlexander Aring 	desc->mode = nla_get_u32(attrs[NL802154_KEY_ID_ATTR_MODE]);
1702a26c5fd7SAlexander Aring 	switch (desc->mode) {
1703a26c5fd7SAlexander Aring 	case NL802154_KEY_ID_MODE_IMPLICIT:
1704a26c5fd7SAlexander Aring 		if (!attrs[NL802154_KEY_ID_ATTR_IMPLICIT])
1705a26c5fd7SAlexander Aring 			return -EINVAL;
1706a26c5fd7SAlexander Aring 
1707a26c5fd7SAlexander Aring 		if (ieee802154_llsec_parse_dev_addr(attrs[NL802154_KEY_ID_ATTR_IMPLICIT],
1708a26c5fd7SAlexander Aring 						    &desc->device_addr) < 0)
1709a26c5fd7SAlexander Aring 			return -EINVAL;
1710a26c5fd7SAlexander Aring 		break;
1711a26c5fd7SAlexander Aring 	case NL802154_KEY_ID_MODE_INDEX:
1712a26c5fd7SAlexander Aring 		break;
1713a26c5fd7SAlexander Aring 	case NL802154_KEY_ID_MODE_INDEX_SHORT:
1714a26c5fd7SAlexander Aring 		if (!attrs[NL802154_KEY_ID_ATTR_SOURCE_SHORT])
1715a26c5fd7SAlexander Aring 			return -EINVAL;
1716a26c5fd7SAlexander Aring 
1717a26c5fd7SAlexander Aring 		desc->short_source = nla_get_le32(attrs[NL802154_KEY_ID_ATTR_SOURCE_SHORT]);
1718a26c5fd7SAlexander Aring 		break;
1719a26c5fd7SAlexander Aring 	case NL802154_KEY_ID_MODE_INDEX_EXTENDED:
1720a26c5fd7SAlexander Aring 		if (!attrs[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED])
1721a26c5fd7SAlexander Aring 			return -EINVAL;
1722a26c5fd7SAlexander Aring 
1723a26c5fd7SAlexander Aring 		desc->extended_source = nla_get_le64(attrs[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED]);
1724a26c5fd7SAlexander Aring 		break;
1725a26c5fd7SAlexander Aring 	default:
1726a26c5fd7SAlexander Aring 		return -EINVAL;
1727a26c5fd7SAlexander Aring 	}
1728a26c5fd7SAlexander Aring 
1729a26c5fd7SAlexander Aring 	if (desc->mode != NL802154_KEY_ID_MODE_IMPLICIT) {
1730a26c5fd7SAlexander Aring 		if (!attrs[NL802154_KEY_ID_ATTR_INDEX])
1731a26c5fd7SAlexander Aring 			return -EINVAL;
1732a26c5fd7SAlexander Aring 
1733a26c5fd7SAlexander Aring 		/* TODO change id to idx */
1734a26c5fd7SAlexander Aring 		desc->id = nla_get_u8(attrs[NL802154_KEY_ID_ATTR_INDEX]);
1735a26c5fd7SAlexander Aring 	}
1736a26c5fd7SAlexander Aring 
1737a26c5fd7SAlexander Aring 	return 0;
1738a26c5fd7SAlexander Aring }
1739a26c5fd7SAlexander Aring 
nl802154_set_llsec_params(struct sk_buff * skb,struct genl_info * info)1740a26c5fd7SAlexander Aring static int nl802154_set_llsec_params(struct sk_buff *skb,
1741a26c5fd7SAlexander Aring 				     struct genl_info *info)
1742a26c5fd7SAlexander Aring {
1743a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1744a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
1745a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1746a26c5fd7SAlexander Aring 	struct ieee802154_llsec_params params;
1747a26c5fd7SAlexander Aring 	u32 changed = 0;
1748a26c5fd7SAlexander Aring 	int ret;
1749a26c5fd7SAlexander Aring 
175088c17855SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
175188c17855SAlexander Aring 		return -EOPNOTSUPP;
175288c17855SAlexander Aring 
1753a26c5fd7SAlexander Aring 	if (info->attrs[NL802154_ATTR_SEC_ENABLED]) {
1754a26c5fd7SAlexander Aring 		u8 enabled;
1755a26c5fd7SAlexander Aring 
1756a26c5fd7SAlexander Aring 		enabled = nla_get_u8(info->attrs[NL802154_ATTR_SEC_ENABLED]);
1757a26c5fd7SAlexander Aring 		if (enabled != 0 && enabled != 1)
1758a26c5fd7SAlexander Aring 			return -EINVAL;
1759a26c5fd7SAlexander Aring 
1760a26c5fd7SAlexander Aring 		params.enabled = nla_get_u8(info->attrs[NL802154_ATTR_SEC_ENABLED]);
1761a26c5fd7SAlexander Aring 		changed |= IEEE802154_LLSEC_PARAM_ENABLED;
1762a26c5fd7SAlexander Aring 	}
1763a26c5fd7SAlexander Aring 
1764a26c5fd7SAlexander Aring 	if (info->attrs[NL802154_ATTR_SEC_OUT_KEY_ID]) {
1765a26c5fd7SAlexander Aring 		ret = ieee802154_llsec_parse_key_id(info->attrs[NL802154_ATTR_SEC_OUT_KEY_ID],
1766a26c5fd7SAlexander Aring 						    &params.out_key);
1767a26c5fd7SAlexander Aring 		if (ret < 0)
1768a26c5fd7SAlexander Aring 			return ret;
1769a26c5fd7SAlexander Aring 
1770a26c5fd7SAlexander Aring 		changed |= IEEE802154_LLSEC_PARAM_OUT_KEY;
1771a26c5fd7SAlexander Aring 	}
1772a26c5fd7SAlexander Aring 
1773a26c5fd7SAlexander Aring 	if (info->attrs[NL802154_ATTR_SEC_OUT_LEVEL]) {
1774a26c5fd7SAlexander Aring 		params.out_level = nla_get_u32(info->attrs[NL802154_ATTR_SEC_OUT_LEVEL]);
1775a26c5fd7SAlexander Aring 		if (params.out_level > NL802154_SECLEVEL_MAX)
1776a26c5fd7SAlexander Aring 			return -EINVAL;
1777a26c5fd7SAlexander Aring 
1778a26c5fd7SAlexander Aring 		changed |= IEEE802154_LLSEC_PARAM_OUT_LEVEL;
1779a26c5fd7SAlexander Aring 	}
1780a26c5fd7SAlexander Aring 
1781a26c5fd7SAlexander Aring 	if (info->attrs[NL802154_ATTR_SEC_FRAME_COUNTER]) {
1782a26c5fd7SAlexander Aring 		params.frame_counter = nla_get_be32(info->attrs[NL802154_ATTR_SEC_FRAME_COUNTER]);
1783a26c5fd7SAlexander Aring 		changed |= IEEE802154_LLSEC_PARAM_FRAME_COUNTER;
1784a26c5fd7SAlexander Aring 	}
1785a26c5fd7SAlexander Aring 
1786a26c5fd7SAlexander Aring 	return rdev_set_llsec_params(rdev, wpan_dev, &params, changed);
1787a26c5fd7SAlexander Aring }
1788a26c5fd7SAlexander Aring 
nl802154_send_key(struct sk_buff * msg,u32 cmd,u32 portid,u32 seq,int flags,struct cfg802154_registered_device * rdev,struct net_device * dev,const struct ieee802154_llsec_key_entry * key)1789a26c5fd7SAlexander Aring static int nl802154_send_key(struct sk_buff *msg, u32 cmd, u32 portid,
1790a26c5fd7SAlexander Aring 			     u32 seq, int flags,
1791a26c5fd7SAlexander Aring 			     struct cfg802154_registered_device *rdev,
1792a26c5fd7SAlexander Aring 			     struct net_device *dev,
1793a26c5fd7SAlexander Aring 			     const struct ieee802154_llsec_key_entry *key)
1794a26c5fd7SAlexander Aring {
1795a26c5fd7SAlexander Aring 	void *hdr;
1796a26c5fd7SAlexander Aring 	u32 commands[NL802154_CMD_FRAME_NR_IDS / 32];
1797a26c5fd7SAlexander Aring 	struct nlattr *nl_key, *nl_key_id;
1798a26c5fd7SAlexander Aring 
1799a26c5fd7SAlexander Aring 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
1800a26c5fd7SAlexander Aring 	if (!hdr)
180179c37ca7SMiquel Raynal 		return -ENOBUFS;
1802a26c5fd7SAlexander Aring 
1803a26c5fd7SAlexander Aring 	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
1804a26c5fd7SAlexander Aring 		goto nla_put_failure;
1805a26c5fd7SAlexander Aring 
1806ae0be8deSMichal Kubecek 	nl_key = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_KEY);
1807a26c5fd7SAlexander Aring 	if (!nl_key)
1808a26c5fd7SAlexander Aring 		goto nla_put_failure;
1809a26c5fd7SAlexander Aring 
1810ae0be8deSMichal Kubecek 	nl_key_id = nla_nest_start_noflag(msg, NL802154_KEY_ATTR_ID);
1811a26c5fd7SAlexander Aring 	if (!nl_key_id)
1812a26c5fd7SAlexander Aring 		goto nla_put_failure;
1813a26c5fd7SAlexander Aring 
1814a26c5fd7SAlexander Aring 	if (ieee802154_llsec_send_key_id(msg, &key->id) < 0)
1815a26c5fd7SAlexander Aring 		goto nla_put_failure;
1816a26c5fd7SAlexander Aring 
1817a26c5fd7SAlexander Aring 	nla_nest_end(msg, nl_key_id);
1818a26c5fd7SAlexander Aring 
1819a26c5fd7SAlexander Aring 	if (nla_put_u8(msg, NL802154_KEY_ATTR_USAGE_FRAMES,
1820a26c5fd7SAlexander Aring 		       key->key->frame_types))
1821a26c5fd7SAlexander Aring 		goto nla_put_failure;
1822a26c5fd7SAlexander Aring 
1823a26c5fd7SAlexander Aring 	if (key->key->frame_types & BIT(NL802154_FRAME_CMD)) {
1824a26c5fd7SAlexander Aring 		/* TODO for each nested */
1825a26c5fd7SAlexander Aring 		memset(commands, 0, sizeof(commands));
1826a26c5fd7SAlexander Aring 		commands[7] = key->key->cmd_frame_ids;
1827a26c5fd7SAlexander Aring 		if (nla_put(msg, NL802154_KEY_ATTR_USAGE_CMDS,
1828a26c5fd7SAlexander Aring 			    sizeof(commands), commands))
1829a26c5fd7SAlexander Aring 			goto nla_put_failure;
1830a26c5fd7SAlexander Aring 	}
1831a26c5fd7SAlexander Aring 
1832a26c5fd7SAlexander Aring 	if (nla_put(msg, NL802154_KEY_ATTR_BYTES, NL802154_KEY_SIZE,
1833a26c5fd7SAlexander Aring 		    key->key->key))
1834a26c5fd7SAlexander Aring 		goto nla_put_failure;
1835a26c5fd7SAlexander Aring 
1836a26c5fd7SAlexander Aring 	nla_nest_end(msg, nl_key);
1837a26c5fd7SAlexander Aring 	genlmsg_end(msg, hdr);
1838a26c5fd7SAlexander Aring 
1839a26c5fd7SAlexander Aring 	return 0;
1840a26c5fd7SAlexander Aring 
1841a26c5fd7SAlexander Aring nla_put_failure:
1842a26c5fd7SAlexander Aring 	genlmsg_cancel(msg, hdr);
1843a26c5fd7SAlexander Aring 	return -EMSGSIZE;
1844a26c5fd7SAlexander Aring }
1845a26c5fd7SAlexander Aring 
1846a26c5fd7SAlexander Aring static int
nl802154_dump_llsec_key(struct sk_buff * skb,struct netlink_callback * cb)1847a26c5fd7SAlexander Aring nl802154_dump_llsec_key(struct sk_buff *skb, struct netlink_callback *cb)
1848a26c5fd7SAlexander Aring {
1849a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = NULL;
1850a26c5fd7SAlexander Aring 	struct ieee802154_llsec_key_entry *key;
1851a26c5fd7SAlexander Aring 	struct ieee802154_llsec_table *table;
1852a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev;
1853a26c5fd7SAlexander Aring 	int err;
1854a26c5fd7SAlexander Aring 
1855a26c5fd7SAlexander Aring 	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
1856a26c5fd7SAlexander Aring 	if (err)
1857a26c5fd7SAlexander Aring 		return err;
1858a26c5fd7SAlexander Aring 
1859fb3c5cdfSAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) {
1860fb3c5cdfSAlexander Aring 		err = skb->len;
1861fb3c5cdfSAlexander Aring 		goto out_err;
1862fb3c5cdfSAlexander Aring 	}
1863fb3c5cdfSAlexander Aring 
1864a26c5fd7SAlexander Aring 	if (!wpan_dev->netdev) {
1865a26c5fd7SAlexander Aring 		err = -EINVAL;
1866a26c5fd7SAlexander Aring 		goto out_err;
1867a26c5fd7SAlexander Aring 	}
1868a26c5fd7SAlexander Aring 
1869a26c5fd7SAlexander Aring 	rdev_lock_llsec_table(rdev, wpan_dev);
1870a26c5fd7SAlexander Aring 	rdev_get_llsec_table(rdev, wpan_dev, &table);
1871a26c5fd7SAlexander Aring 
1872a26c5fd7SAlexander Aring 	/* TODO make it like station dump */
1873a26c5fd7SAlexander Aring 	if (cb->args[2])
1874a26c5fd7SAlexander Aring 		goto out;
1875a26c5fd7SAlexander Aring 
1876a26c5fd7SAlexander Aring 	list_for_each_entry(key, &table->keys, list) {
1877a26c5fd7SAlexander Aring 		if (nl802154_send_key(skb, NL802154_CMD_NEW_SEC_KEY,
1878a26c5fd7SAlexander Aring 				      NETLINK_CB(cb->skb).portid,
1879a26c5fd7SAlexander Aring 				      cb->nlh->nlmsg_seq, NLM_F_MULTI,
1880a26c5fd7SAlexander Aring 				      rdev, wpan_dev->netdev, key) < 0) {
1881a26c5fd7SAlexander Aring 			/* TODO */
1882a26c5fd7SAlexander Aring 			err = -EIO;
1883a26c5fd7SAlexander Aring 			rdev_unlock_llsec_table(rdev, wpan_dev);
1884a26c5fd7SAlexander Aring 			goto out_err;
1885a26c5fd7SAlexander Aring 		}
1886a26c5fd7SAlexander Aring 	}
1887a26c5fd7SAlexander Aring 
1888a26c5fd7SAlexander Aring 	cb->args[2] = 1;
1889a26c5fd7SAlexander Aring 
1890a26c5fd7SAlexander Aring out:
1891a26c5fd7SAlexander Aring 	rdev_unlock_llsec_table(rdev, wpan_dev);
1892a26c5fd7SAlexander Aring 	err = skb->len;
1893a26c5fd7SAlexander Aring out_err:
1894a26c5fd7SAlexander Aring 	nl802154_finish_wpan_dev_dump(rdev);
1895a26c5fd7SAlexander Aring 
1896a26c5fd7SAlexander Aring 	return err;
1897a26c5fd7SAlexander Aring }
1898a26c5fd7SAlexander Aring 
1899a26c5fd7SAlexander Aring static const struct nla_policy nl802154_key_policy[NL802154_KEY_ATTR_MAX + 1] = {
1900a26c5fd7SAlexander Aring 	[NL802154_KEY_ATTR_ID] = { NLA_NESTED },
1901a26c5fd7SAlexander Aring 	/* TODO handle it as for_each_nested and NLA_FLAG? */
1902a26c5fd7SAlexander Aring 	[NL802154_KEY_ATTR_USAGE_FRAMES] = { NLA_U8 },
1903a26c5fd7SAlexander Aring 	/* TODO handle it as for_each_nested, not static array? */
1904a26c5fd7SAlexander Aring 	[NL802154_KEY_ATTR_USAGE_CMDS] = { .len = NL802154_CMD_FRAME_NR_IDS / 8 },
1905a26c5fd7SAlexander Aring 	[NL802154_KEY_ATTR_BYTES] = { .len = NL802154_KEY_SIZE },
1906a26c5fd7SAlexander Aring };
1907a26c5fd7SAlexander Aring 
nl802154_add_llsec_key(struct sk_buff * skb,struct genl_info * info)1908a26c5fd7SAlexander Aring static int nl802154_add_llsec_key(struct sk_buff *skb, struct genl_info *info)
1909a26c5fd7SAlexander Aring {
1910a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1911a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
1912a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1913a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_KEY_ATTR_MAX + 1];
1914a26c5fd7SAlexander Aring 	struct ieee802154_llsec_key key = { };
1915a26c5fd7SAlexander Aring 	struct ieee802154_llsec_key_id id = { };
1916a26c5fd7SAlexander Aring 	u32 commands[NL802154_CMD_FRAME_NR_IDS / 32] = { };
1917a26c5fd7SAlexander Aring 
191808470c54SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
191908470c54SAlexander Aring 		return -EOPNOTSUPP;
192008470c54SAlexander Aring 
192120d5fe2dSAlexander Aring 	if (!info->attrs[NL802154_ATTR_SEC_KEY] ||
192220d5fe2dSAlexander Aring 	    nla_parse_nested_deprecated(attrs, NL802154_KEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_KEY], nl802154_key_policy, info->extack))
1923a26c5fd7SAlexander Aring 		return -EINVAL;
1924a26c5fd7SAlexander Aring 
1925a26c5fd7SAlexander Aring 	if (!attrs[NL802154_KEY_ATTR_USAGE_FRAMES] ||
1926a26c5fd7SAlexander Aring 	    !attrs[NL802154_KEY_ATTR_BYTES])
1927aa655562SDan Carpenter 		return -EINVAL;
1928a26c5fd7SAlexander Aring 
1929a26c5fd7SAlexander Aring 	if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0)
1930a26c5fd7SAlexander Aring 		return -ENOBUFS;
1931a26c5fd7SAlexander Aring 
1932a26c5fd7SAlexander Aring 	key.frame_types = nla_get_u8(attrs[NL802154_KEY_ATTR_USAGE_FRAMES]);
1933a26c5fd7SAlexander Aring 	if (key.frame_types > BIT(NL802154_FRAME_MAX) ||
1934a26c5fd7SAlexander Aring 	    ((key.frame_types & BIT(NL802154_FRAME_CMD)) &&
1935a26c5fd7SAlexander Aring 	     !attrs[NL802154_KEY_ATTR_USAGE_CMDS]))
1936a26c5fd7SAlexander Aring 		return -EINVAL;
1937a26c5fd7SAlexander Aring 
1938a26c5fd7SAlexander Aring 	if (attrs[NL802154_KEY_ATTR_USAGE_CMDS]) {
1939a26c5fd7SAlexander Aring 		/* TODO for each nested */
1940a26c5fd7SAlexander Aring 		nla_memcpy(commands, attrs[NL802154_KEY_ATTR_USAGE_CMDS],
1941a26c5fd7SAlexander Aring 			   NL802154_CMD_FRAME_NR_IDS / 8);
1942a26c5fd7SAlexander Aring 
1943a26c5fd7SAlexander Aring 		/* TODO understand the -EINVAL logic here? last condition */
1944a26c5fd7SAlexander Aring 		if (commands[0] || commands[1] || commands[2] || commands[3] ||
1945a26c5fd7SAlexander Aring 		    commands[4] || commands[5] || commands[6] ||
1946a26c5fd7SAlexander Aring 		    commands[7] > BIT(NL802154_CMD_FRAME_MAX))
1947a26c5fd7SAlexander Aring 			return -EINVAL;
1948a26c5fd7SAlexander Aring 
1949a26c5fd7SAlexander Aring 		key.cmd_frame_ids = commands[7];
1950a26c5fd7SAlexander Aring 	} else {
1951a26c5fd7SAlexander Aring 		key.cmd_frame_ids = 0;
1952a26c5fd7SAlexander Aring 	}
1953a26c5fd7SAlexander Aring 
1954a26c5fd7SAlexander Aring 	nla_memcpy(key.key, attrs[NL802154_KEY_ATTR_BYTES], NL802154_KEY_SIZE);
1955a26c5fd7SAlexander Aring 
1956a26c5fd7SAlexander Aring 	if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0)
1957a26c5fd7SAlexander Aring 		return -ENOBUFS;
1958a26c5fd7SAlexander Aring 
1959a26c5fd7SAlexander Aring 	return rdev_add_llsec_key(rdev, wpan_dev, &id, &key);
1960a26c5fd7SAlexander Aring }
1961a26c5fd7SAlexander Aring 
nl802154_del_llsec_key(struct sk_buff * skb,struct genl_info * info)1962a26c5fd7SAlexander Aring static int nl802154_del_llsec_key(struct sk_buff *skb, struct genl_info *info)
1963a26c5fd7SAlexander Aring {
1964a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1965a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
1966a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1967a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_KEY_ATTR_MAX + 1];
1968a26c5fd7SAlexander Aring 	struct ieee802154_llsec_key_id id;
1969a26c5fd7SAlexander Aring 
1970b6e29495SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
1971b6e29495SAlexander Aring 		return -EOPNOTSUPP;
1972b6e29495SAlexander Aring 
197337feaaf5SAlexander Aring 	if (!info->attrs[NL802154_ATTR_SEC_KEY] ||
197437feaaf5SAlexander Aring 	    nla_parse_nested_deprecated(attrs, NL802154_KEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_KEY], nl802154_key_policy, info->extack))
1975a26c5fd7SAlexander Aring 		return -EINVAL;
1976a26c5fd7SAlexander Aring 
1977a26c5fd7SAlexander Aring 	if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0)
1978a26c5fd7SAlexander Aring 		return -ENOBUFS;
1979a26c5fd7SAlexander Aring 
1980a26c5fd7SAlexander Aring 	return rdev_del_llsec_key(rdev, wpan_dev, &id);
1981a26c5fd7SAlexander Aring }
1982a26c5fd7SAlexander Aring 
nl802154_send_device(struct sk_buff * msg,u32 cmd,u32 portid,u32 seq,int flags,struct cfg802154_registered_device * rdev,struct net_device * dev,const struct ieee802154_llsec_device * dev_desc)1983a26c5fd7SAlexander Aring static int nl802154_send_device(struct sk_buff *msg, u32 cmd, u32 portid,
1984a26c5fd7SAlexander Aring 				u32 seq, int flags,
1985a26c5fd7SAlexander Aring 				struct cfg802154_registered_device *rdev,
1986a26c5fd7SAlexander Aring 				struct net_device *dev,
1987a26c5fd7SAlexander Aring 				const struct ieee802154_llsec_device *dev_desc)
1988a26c5fd7SAlexander Aring {
1989a26c5fd7SAlexander Aring 	void *hdr;
1990a26c5fd7SAlexander Aring 	struct nlattr *nl_device;
1991a26c5fd7SAlexander Aring 
1992a26c5fd7SAlexander Aring 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
1993a26c5fd7SAlexander Aring 	if (!hdr)
199479c37ca7SMiquel Raynal 		return -ENOBUFS;
1995a26c5fd7SAlexander Aring 
1996a26c5fd7SAlexander Aring 	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
1997a26c5fd7SAlexander Aring 		goto nla_put_failure;
1998a26c5fd7SAlexander Aring 
1999ae0be8deSMichal Kubecek 	nl_device = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_DEVICE);
2000a26c5fd7SAlexander Aring 	if (!nl_device)
2001a26c5fd7SAlexander Aring 		goto nla_put_failure;
2002a26c5fd7SAlexander Aring 
2003a26c5fd7SAlexander Aring 	if (nla_put_u32(msg, NL802154_DEV_ATTR_FRAME_COUNTER,
2004a26c5fd7SAlexander Aring 			dev_desc->frame_counter) ||
2005a26c5fd7SAlexander Aring 	    nla_put_le16(msg, NL802154_DEV_ATTR_PAN_ID, dev_desc->pan_id) ||
2006a26c5fd7SAlexander Aring 	    nla_put_le16(msg, NL802154_DEV_ATTR_SHORT_ADDR,
2007a26c5fd7SAlexander Aring 			 dev_desc->short_addr) ||
2008a26c5fd7SAlexander Aring 	    nla_put_le64(msg, NL802154_DEV_ATTR_EXTENDED_ADDR,
2009e7479122SNicolas Dichtel 			 dev_desc->hwaddr, NL802154_DEV_ATTR_PAD) ||
2010a26c5fd7SAlexander Aring 	    nla_put_u8(msg, NL802154_DEV_ATTR_SECLEVEL_EXEMPT,
2011a26c5fd7SAlexander Aring 		       dev_desc->seclevel_exempt) ||
2012a26c5fd7SAlexander Aring 	    nla_put_u32(msg, NL802154_DEV_ATTR_KEY_MODE, dev_desc->key_mode))
2013a26c5fd7SAlexander Aring 		goto nla_put_failure;
2014a26c5fd7SAlexander Aring 
2015a26c5fd7SAlexander Aring 	nla_nest_end(msg, nl_device);
2016a26c5fd7SAlexander Aring 	genlmsg_end(msg, hdr);
2017a26c5fd7SAlexander Aring 
2018a26c5fd7SAlexander Aring 	return 0;
2019a26c5fd7SAlexander Aring 
2020a26c5fd7SAlexander Aring nla_put_failure:
2021a26c5fd7SAlexander Aring 	genlmsg_cancel(msg, hdr);
2022a26c5fd7SAlexander Aring 	return -EMSGSIZE;
2023a26c5fd7SAlexander Aring }
2024a26c5fd7SAlexander Aring 
2025a26c5fd7SAlexander Aring static int
nl802154_dump_llsec_dev(struct sk_buff * skb,struct netlink_callback * cb)2026a26c5fd7SAlexander Aring nl802154_dump_llsec_dev(struct sk_buff *skb, struct netlink_callback *cb)
2027a26c5fd7SAlexander Aring {
2028a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = NULL;
2029a26c5fd7SAlexander Aring 	struct ieee802154_llsec_device *dev;
2030a26c5fd7SAlexander Aring 	struct ieee802154_llsec_table *table;
2031a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev;
2032a26c5fd7SAlexander Aring 	int err;
2033a26c5fd7SAlexander Aring 
2034a26c5fd7SAlexander Aring 	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
2035a26c5fd7SAlexander Aring 	if (err)
2036a26c5fd7SAlexander Aring 		return err;
2037a26c5fd7SAlexander Aring 
20385582d641SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) {
20395582d641SAlexander Aring 		err = skb->len;
20405582d641SAlexander Aring 		goto out_err;
20415582d641SAlexander Aring 	}
20425582d641SAlexander Aring 
2043a26c5fd7SAlexander Aring 	if (!wpan_dev->netdev) {
2044a26c5fd7SAlexander Aring 		err = -EINVAL;
2045a26c5fd7SAlexander Aring 		goto out_err;
2046a26c5fd7SAlexander Aring 	}
2047a26c5fd7SAlexander Aring 
2048a26c5fd7SAlexander Aring 	rdev_lock_llsec_table(rdev, wpan_dev);
2049a26c5fd7SAlexander Aring 	rdev_get_llsec_table(rdev, wpan_dev, &table);
2050a26c5fd7SAlexander Aring 
2051a26c5fd7SAlexander Aring 	/* TODO make it like station dump */
2052a26c5fd7SAlexander Aring 	if (cb->args[2])
2053a26c5fd7SAlexander Aring 		goto out;
2054a26c5fd7SAlexander Aring 
2055a26c5fd7SAlexander Aring 	list_for_each_entry(dev, &table->devices, list) {
2056a26c5fd7SAlexander Aring 		if (nl802154_send_device(skb, NL802154_CMD_NEW_SEC_LEVEL,
2057a26c5fd7SAlexander Aring 					 NETLINK_CB(cb->skb).portid,
2058a26c5fd7SAlexander Aring 					 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2059a26c5fd7SAlexander Aring 					 rdev, wpan_dev->netdev, dev) < 0) {
2060a26c5fd7SAlexander Aring 			/* TODO */
2061a26c5fd7SAlexander Aring 			err = -EIO;
2062a26c5fd7SAlexander Aring 			rdev_unlock_llsec_table(rdev, wpan_dev);
2063a26c5fd7SAlexander Aring 			goto out_err;
2064a26c5fd7SAlexander Aring 		}
2065a26c5fd7SAlexander Aring 	}
2066a26c5fd7SAlexander Aring 
2067a26c5fd7SAlexander Aring 	cb->args[2] = 1;
2068a26c5fd7SAlexander Aring 
2069a26c5fd7SAlexander Aring out:
2070a26c5fd7SAlexander Aring 	rdev_unlock_llsec_table(rdev, wpan_dev);
2071a26c5fd7SAlexander Aring 	err = skb->len;
2072a26c5fd7SAlexander Aring out_err:
2073a26c5fd7SAlexander Aring 	nl802154_finish_wpan_dev_dump(rdev);
2074a26c5fd7SAlexander Aring 
2075a26c5fd7SAlexander Aring 	return err;
2076a26c5fd7SAlexander Aring }
2077a26c5fd7SAlexander Aring 
2078a26c5fd7SAlexander Aring static const struct nla_policy nl802154_dev_policy[NL802154_DEV_ATTR_MAX + 1] = {
2079a26c5fd7SAlexander Aring 	[NL802154_DEV_ATTR_FRAME_COUNTER] = { NLA_U32 },
2080a26c5fd7SAlexander Aring 	[NL802154_DEV_ATTR_PAN_ID] = { .type = NLA_U16 },
2081a26c5fd7SAlexander Aring 	[NL802154_DEV_ATTR_SHORT_ADDR] = { .type = NLA_U16 },
2082a26c5fd7SAlexander Aring 	[NL802154_DEV_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
2083a26c5fd7SAlexander Aring 	[NL802154_DEV_ATTR_SECLEVEL_EXEMPT] = { NLA_U8 },
2084a26c5fd7SAlexander Aring 	[NL802154_DEV_ATTR_KEY_MODE] = { NLA_U32 },
2085a26c5fd7SAlexander Aring };
2086a26c5fd7SAlexander Aring 
2087a26c5fd7SAlexander Aring static int
ieee802154_llsec_parse_device(struct nlattr * nla,struct ieee802154_llsec_device * dev)2088a26c5fd7SAlexander Aring ieee802154_llsec_parse_device(struct nlattr *nla,
2089a26c5fd7SAlexander Aring 			      struct ieee802154_llsec_device *dev)
2090a26c5fd7SAlexander Aring {
2091a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_DEV_ATTR_MAX + 1];
2092a26c5fd7SAlexander Aring 
20938cb08174SJohannes Berg 	if (!nla || nla_parse_nested_deprecated(attrs, NL802154_DEV_ATTR_MAX, nla, nl802154_dev_policy, NULL))
2094a26c5fd7SAlexander Aring 		return -EINVAL;
2095a26c5fd7SAlexander Aring 
2096a26c5fd7SAlexander Aring 	memset(dev, 0, sizeof(*dev));
2097a26c5fd7SAlexander Aring 
2098a26c5fd7SAlexander Aring 	if (!attrs[NL802154_DEV_ATTR_FRAME_COUNTER] ||
2099a26c5fd7SAlexander Aring 	    !attrs[NL802154_DEV_ATTR_PAN_ID] ||
2100a26c5fd7SAlexander Aring 	    !attrs[NL802154_DEV_ATTR_SHORT_ADDR] ||
2101a26c5fd7SAlexander Aring 	    !attrs[NL802154_DEV_ATTR_EXTENDED_ADDR] ||
2102a26c5fd7SAlexander Aring 	    !attrs[NL802154_DEV_ATTR_SECLEVEL_EXEMPT] ||
2103a26c5fd7SAlexander Aring 	    !attrs[NL802154_DEV_ATTR_KEY_MODE])
2104a26c5fd7SAlexander Aring 		return -EINVAL;
2105a26c5fd7SAlexander Aring 
2106a26c5fd7SAlexander Aring 	/* TODO be32 */
2107a26c5fd7SAlexander Aring 	dev->frame_counter = nla_get_u32(attrs[NL802154_DEV_ATTR_FRAME_COUNTER]);
2108a26c5fd7SAlexander Aring 	dev->pan_id = nla_get_le16(attrs[NL802154_DEV_ATTR_PAN_ID]);
2109a26c5fd7SAlexander Aring 	dev->short_addr = nla_get_le16(attrs[NL802154_DEV_ATTR_SHORT_ADDR]);
2110a26c5fd7SAlexander Aring 	/* TODO rename hwaddr to extended_addr */
2111a26c5fd7SAlexander Aring 	dev->hwaddr = nla_get_le64(attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]);
2112a26c5fd7SAlexander Aring 	dev->seclevel_exempt = nla_get_u8(attrs[NL802154_DEV_ATTR_SECLEVEL_EXEMPT]);
2113a26c5fd7SAlexander Aring 	dev->key_mode = nla_get_u32(attrs[NL802154_DEV_ATTR_KEY_MODE]);
2114a26c5fd7SAlexander Aring 
2115a26c5fd7SAlexander Aring 	if (dev->key_mode > NL802154_DEVKEY_MAX ||
2116a26c5fd7SAlexander Aring 	    (dev->seclevel_exempt != 0 && dev->seclevel_exempt != 1))
2117a26c5fd7SAlexander Aring 		return -EINVAL;
2118a26c5fd7SAlexander Aring 
2119a26c5fd7SAlexander Aring 	return 0;
2120a26c5fd7SAlexander Aring }
2121a26c5fd7SAlexander Aring 
nl802154_add_llsec_dev(struct sk_buff * skb,struct genl_info * info)2122a26c5fd7SAlexander Aring static int nl802154_add_llsec_dev(struct sk_buff *skb, struct genl_info *info)
2123a26c5fd7SAlexander Aring {
2124a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
2125a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
2126a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
2127a26c5fd7SAlexander Aring 	struct ieee802154_llsec_device dev_desc;
2128a26c5fd7SAlexander Aring 
21295303f956SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
21305303f956SAlexander Aring 		return -EOPNOTSUPP;
21315303f956SAlexander Aring 
2132a26c5fd7SAlexander Aring 	if (ieee802154_llsec_parse_device(info->attrs[NL802154_ATTR_SEC_DEVICE],
2133a26c5fd7SAlexander Aring 					  &dev_desc) < 0)
2134a26c5fd7SAlexander Aring 		return -EINVAL;
2135a26c5fd7SAlexander Aring 
2136a26c5fd7SAlexander Aring 	return rdev_add_device(rdev, wpan_dev, &dev_desc);
2137a26c5fd7SAlexander Aring }
2138a26c5fd7SAlexander Aring 
nl802154_del_llsec_dev(struct sk_buff * skb,struct genl_info * info)2139a26c5fd7SAlexander Aring static int nl802154_del_llsec_dev(struct sk_buff *skb, struct genl_info *info)
2140a26c5fd7SAlexander Aring {
2141a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
2142a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
2143a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
2144a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_DEV_ATTR_MAX + 1];
2145a26c5fd7SAlexander Aring 	__le64 extended_addr;
2146a26c5fd7SAlexander Aring 
2147ad8f9de1SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
2148ad8f9de1SAlexander Aring 		return -EOPNOTSUPP;
2149ad8f9de1SAlexander Aring 
21503d1eac2fSAlexander Aring 	if (!info->attrs[NL802154_ATTR_SEC_DEVICE] ||
21513d1eac2fSAlexander Aring 	    nla_parse_nested_deprecated(attrs, NL802154_DEV_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVICE], nl802154_dev_policy, info->extack))
2152a26c5fd7SAlexander Aring 		return -EINVAL;
2153a26c5fd7SAlexander Aring 
2154a26c5fd7SAlexander Aring 	if (!attrs[NL802154_DEV_ATTR_EXTENDED_ADDR])
2155a26c5fd7SAlexander Aring 		return -EINVAL;
2156a26c5fd7SAlexander Aring 
2157a26c5fd7SAlexander Aring 	extended_addr = nla_get_le64(attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]);
2158a26c5fd7SAlexander Aring 	return rdev_del_device(rdev, wpan_dev, extended_addr);
2159a26c5fd7SAlexander Aring }
2160a26c5fd7SAlexander Aring 
nl802154_send_devkey(struct sk_buff * msg,u32 cmd,u32 portid,u32 seq,int flags,struct cfg802154_registered_device * rdev,struct net_device * dev,__le64 extended_addr,const struct ieee802154_llsec_device_key * devkey)2161a26c5fd7SAlexander Aring static int nl802154_send_devkey(struct sk_buff *msg, u32 cmd, u32 portid,
2162a26c5fd7SAlexander Aring 				u32 seq, int flags,
2163a26c5fd7SAlexander Aring 				struct cfg802154_registered_device *rdev,
2164a26c5fd7SAlexander Aring 				struct net_device *dev, __le64 extended_addr,
2165a26c5fd7SAlexander Aring 				const struct ieee802154_llsec_device_key *devkey)
2166a26c5fd7SAlexander Aring {
2167a26c5fd7SAlexander Aring 	void *hdr;
2168a26c5fd7SAlexander Aring 	struct nlattr *nl_devkey, *nl_key_id;
2169a26c5fd7SAlexander Aring 
2170a26c5fd7SAlexander Aring 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
2171a26c5fd7SAlexander Aring 	if (!hdr)
217279c37ca7SMiquel Raynal 		return -ENOBUFS;
2173a26c5fd7SAlexander Aring 
2174a26c5fd7SAlexander Aring 	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
2175a26c5fd7SAlexander Aring 		goto nla_put_failure;
2176a26c5fd7SAlexander Aring 
2177ae0be8deSMichal Kubecek 	nl_devkey = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_DEVKEY);
2178a26c5fd7SAlexander Aring 	if (!nl_devkey)
2179a26c5fd7SAlexander Aring 		goto nla_put_failure;
2180a26c5fd7SAlexander Aring 
2181a26c5fd7SAlexander Aring 	if (nla_put_le64(msg, NL802154_DEVKEY_ATTR_EXTENDED_ADDR,
2182e7479122SNicolas Dichtel 			 extended_addr, NL802154_DEVKEY_ATTR_PAD) ||
2183a26c5fd7SAlexander Aring 	    nla_put_u32(msg, NL802154_DEVKEY_ATTR_FRAME_COUNTER,
2184a26c5fd7SAlexander Aring 			devkey->frame_counter))
2185a26c5fd7SAlexander Aring 		goto nla_put_failure;
2186a26c5fd7SAlexander Aring 
2187ae0be8deSMichal Kubecek 	nl_key_id = nla_nest_start_noflag(msg, NL802154_DEVKEY_ATTR_ID);
2188a26c5fd7SAlexander Aring 	if (!nl_key_id)
2189a26c5fd7SAlexander Aring 		goto nla_put_failure;
2190a26c5fd7SAlexander Aring 
2191a26c5fd7SAlexander Aring 	if (ieee802154_llsec_send_key_id(msg, &devkey->key_id) < 0)
2192a26c5fd7SAlexander Aring 		goto nla_put_failure;
2193a26c5fd7SAlexander Aring 
2194a26c5fd7SAlexander Aring 	nla_nest_end(msg, nl_key_id);
2195a26c5fd7SAlexander Aring 	nla_nest_end(msg, nl_devkey);
2196a26c5fd7SAlexander Aring 	genlmsg_end(msg, hdr);
2197a26c5fd7SAlexander Aring 
2198a26c5fd7SAlexander Aring 	return 0;
2199a26c5fd7SAlexander Aring 
2200a26c5fd7SAlexander Aring nla_put_failure:
2201a26c5fd7SAlexander Aring 	genlmsg_cancel(msg, hdr);
2202a26c5fd7SAlexander Aring 	return -EMSGSIZE;
2203a26c5fd7SAlexander Aring }
2204a26c5fd7SAlexander Aring 
2205a26c5fd7SAlexander Aring static int
nl802154_dump_llsec_devkey(struct sk_buff * skb,struct netlink_callback * cb)2206a26c5fd7SAlexander Aring nl802154_dump_llsec_devkey(struct sk_buff *skb, struct netlink_callback *cb)
2207a26c5fd7SAlexander Aring {
2208a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = NULL;
2209a26c5fd7SAlexander Aring 	struct ieee802154_llsec_device_key *kpos;
2210a26c5fd7SAlexander Aring 	struct ieee802154_llsec_device *dpos;
2211a26c5fd7SAlexander Aring 	struct ieee802154_llsec_table *table;
2212a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev;
2213a26c5fd7SAlexander Aring 	int err;
2214a26c5fd7SAlexander Aring 
2215a26c5fd7SAlexander Aring 	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
2216a26c5fd7SAlexander Aring 	if (err)
2217a26c5fd7SAlexander Aring 		return err;
2218a26c5fd7SAlexander Aring 
2219080d1a57SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) {
2220080d1a57SAlexander Aring 		err = skb->len;
2221080d1a57SAlexander Aring 		goto out_err;
2222080d1a57SAlexander Aring 	}
2223080d1a57SAlexander Aring 
2224a26c5fd7SAlexander Aring 	if (!wpan_dev->netdev) {
2225a26c5fd7SAlexander Aring 		err = -EINVAL;
2226a26c5fd7SAlexander Aring 		goto out_err;
2227a26c5fd7SAlexander Aring 	}
2228a26c5fd7SAlexander Aring 
2229a26c5fd7SAlexander Aring 	rdev_lock_llsec_table(rdev, wpan_dev);
2230a26c5fd7SAlexander Aring 	rdev_get_llsec_table(rdev, wpan_dev, &table);
2231a26c5fd7SAlexander Aring 
2232a26c5fd7SAlexander Aring 	/* TODO make it like station dump */
2233a26c5fd7SAlexander Aring 	if (cb->args[2])
2234a26c5fd7SAlexander Aring 		goto out;
2235a26c5fd7SAlexander Aring 
2236a26c5fd7SAlexander Aring 	/* TODO look if remove devkey and do some nested attribute */
2237a26c5fd7SAlexander Aring 	list_for_each_entry(dpos, &table->devices, list) {
2238a26c5fd7SAlexander Aring 		list_for_each_entry(kpos, &dpos->keys, list) {
2239a26c5fd7SAlexander Aring 			if (nl802154_send_devkey(skb,
2240a26c5fd7SAlexander Aring 						 NL802154_CMD_NEW_SEC_LEVEL,
2241a26c5fd7SAlexander Aring 						 NETLINK_CB(cb->skb).portid,
2242a26c5fd7SAlexander Aring 						 cb->nlh->nlmsg_seq,
2243a26c5fd7SAlexander Aring 						 NLM_F_MULTI, rdev,
2244a26c5fd7SAlexander Aring 						 wpan_dev->netdev,
2245a26c5fd7SAlexander Aring 						 dpos->hwaddr,
2246a26c5fd7SAlexander Aring 						 kpos) < 0) {
2247a26c5fd7SAlexander Aring 				/* TODO */
2248a26c5fd7SAlexander Aring 				err = -EIO;
2249a26c5fd7SAlexander Aring 				rdev_unlock_llsec_table(rdev, wpan_dev);
2250a26c5fd7SAlexander Aring 				goto out_err;
2251a26c5fd7SAlexander Aring 			}
2252a26c5fd7SAlexander Aring 		}
2253a26c5fd7SAlexander Aring 	}
2254a26c5fd7SAlexander Aring 
2255a26c5fd7SAlexander Aring 	cb->args[2] = 1;
2256a26c5fd7SAlexander Aring 
2257a26c5fd7SAlexander Aring out:
2258a26c5fd7SAlexander Aring 	rdev_unlock_llsec_table(rdev, wpan_dev);
2259a26c5fd7SAlexander Aring 	err = skb->len;
2260a26c5fd7SAlexander Aring out_err:
2261a26c5fd7SAlexander Aring 	nl802154_finish_wpan_dev_dump(rdev);
2262a26c5fd7SAlexander Aring 
2263a26c5fd7SAlexander Aring 	return err;
2264a26c5fd7SAlexander Aring }
2265a26c5fd7SAlexander Aring 
2266a26c5fd7SAlexander Aring static const struct nla_policy nl802154_devkey_policy[NL802154_DEVKEY_ATTR_MAX + 1] = {
2267a26c5fd7SAlexander Aring 	[NL802154_DEVKEY_ATTR_FRAME_COUNTER] = { NLA_U32 },
2268a26c5fd7SAlexander Aring 	[NL802154_DEVKEY_ATTR_EXTENDED_ADDR] = { NLA_U64 },
2269a26c5fd7SAlexander Aring 	[NL802154_DEVKEY_ATTR_ID] = { NLA_NESTED },
2270a26c5fd7SAlexander Aring };
2271a26c5fd7SAlexander Aring 
nl802154_add_llsec_devkey(struct sk_buff * skb,struct genl_info * info)2272a26c5fd7SAlexander Aring static int nl802154_add_llsec_devkey(struct sk_buff *skb, struct genl_info *info)
2273a26c5fd7SAlexander Aring {
2274a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
2275a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
2276a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
2277a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_DEVKEY_ATTR_MAX + 1];
2278a26c5fd7SAlexander Aring 	struct ieee802154_llsec_device_key key;
2279a26c5fd7SAlexander Aring 	__le64 extended_addr;
2280a26c5fd7SAlexander Aring 
2281a347b3b3SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
2282a347b3b3SAlexander Aring 		return -EOPNOTSUPP;
2283a347b3b3SAlexander Aring 
2284a26c5fd7SAlexander Aring 	if (!info->attrs[NL802154_ATTR_SEC_DEVKEY] ||
22858cb08174SJohannes Berg 	    nla_parse_nested_deprecated(attrs, NL802154_DEVKEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVKEY], nl802154_devkey_policy, info->extack) < 0)
2286a26c5fd7SAlexander Aring 		return -EINVAL;
2287a26c5fd7SAlexander Aring 
2288a26c5fd7SAlexander Aring 	if (!attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER] ||
2289a26c5fd7SAlexander Aring 	    !attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR])
2290a26c5fd7SAlexander Aring 		return -EINVAL;
2291a26c5fd7SAlexander Aring 
2292a26c5fd7SAlexander Aring 	/* TODO change key.id ? */
2293a26c5fd7SAlexander Aring 	if (ieee802154_llsec_parse_key_id(attrs[NL802154_DEVKEY_ATTR_ID],
2294a26c5fd7SAlexander Aring 					  &key.key_id) < 0)
2295a26c5fd7SAlexander Aring 		return -ENOBUFS;
2296a26c5fd7SAlexander Aring 
2297a26c5fd7SAlexander Aring 	/* TODO be32 */
2298a26c5fd7SAlexander Aring 	key.frame_counter = nla_get_u32(attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER]);
2299a26c5fd7SAlexander Aring 	/* TODO change naming hwaddr -> extended_addr
2300a26c5fd7SAlexander Aring 	 * check unique identifier short+pan OR extended_addr
2301a26c5fd7SAlexander Aring 	 */
2302a26c5fd7SAlexander Aring 	extended_addr = nla_get_le64(attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]);
2303a26c5fd7SAlexander Aring 	return rdev_add_devkey(rdev, wpan_dev, extended_addr, &key);
2304a26c5fd7SAlexander Aring }
2305a26c5fd7SAlexander Aring 
nl802154_del_llsec_devkey(struct sk_buff * skb,struct genl_info * info)2306a26c5fd7SAlexander Aring static int nl802154_del_llsec_devkey(struct sk_buff *skb, struct genl_info *info)
2307a26c5fd7SAlexander Aring {
2308a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
2309a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
2310a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
2311a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_DEVKEY_ATTR_MAX + 1];
2312a26c5fd7SAlexander Aring 	struct ieee802154_llsec_device_key key;
2313a26c5fd7SAlexander Aring 	__le64 extended_addr;
2314a26c5fd7SAlexander Aring 
23156fb80453SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
23166fb80453SAlexander Aring 		return -EOPNOTSUPP;
23176fb80453SAlexander Aring 
231827c74686SAlexander Aring 	if (!info->attrs[NL802154_ATTR_SEC_DEVKEY] ||
231927c74686SAlexander Aring 	    nla_parse_nested_deprecated(attrs, NL802154_DEVKEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVKEY], nl802154_devkey_policy, info->extack))
2320a26c5fd7SAlexander Aring 		return -EINVAL;
2321a26c5fd7SAlexander Aring 
2322a26c5fd7SAlexander Aring 	if (!attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR])
2323a26c5fd7SAlexander Aring 		return -EINVAL;
2324a26c5fd7SAlexander Aring 
2325a26c5fd7SAlexander Aring 	/* TODO change key.id ? */
2326a26c5fd7SAlexander Aring 	if (ieee802154_llsec_parse_key_id(attrs[NL802154_DEVKEY_ATTR_ID],
2327a26c5fd7SAlexander Aring 					  &key.key_id) < 0)
2328a26c5fd7SAlexander Aring 		return -ENOBUFS;
2329a26c5fd7SAlexander Aring 
2330a26c5fd7SAlexander Aring 	/* TODO change naming hwaddr -> extended_addr
2331a26c5fd7SAlexander Aring 	 * check unique identifier short+pan OR extended_addr
2332a26c5fd7SAlexander Aring 	 */
2333a26c5fd7SAlexander Aring 	extended_addr = nla_get_le64(attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]);
2334a26c5fd7SAlexander Aring 	return rdev_del_devkey(rdev, wpan_dev, extended_addr, &key);
2335a26c5fd7SAlexander Aring }
2336a26c5fd7SAlexander Aring 
nl802154_send_seclevel(struct sk_buff * msg,u32 cmd,u32 portid,u32 seq,int flags,struct cfg802154_registered_device * rdev,struct net_device * dev,const struct ieee802154_llsec_seclevel * sl)2337a26c5fd7SAlexander Aring static int nl802154_send_seclevel(struct sk_buff *msg, u32 cmd, u32 portid,
2338a26c5fd7SAlexander Aring 				  u32 seq, int flags,
2339a26c5fd7SAlexander Aring 				  struct cfg802154_registered_device *rdev,
2340a26c5fd7SAlexander Aring 				  struct net_device *dev,
2341a26c5fd7SAlexander Aring 				  const struct ieee802154_llsec_seclevel *sl)
2342a26c5fd7SAlexander Aring {
2343a26c5fd7SAlexander Aring 	void *hdr;
2344a26c5fd7SAlexander Aring 	struct nlattr *nl_seclevel;
2345a26c5fd7SAlexander Aring 
2346a26c5fd7SAlexander Aring 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
2347a26c5fd7SAlexander Aring 	if (!hdr)
234879c37ca7SMiquel Raynal 		return -ENOBUFS;
2349a26c5fd7SAlexander Aring 
2350a26c5fd7SAlexander Aring 	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
2351a26c5fd7SAlexander Aring 		goto nla_put_failure;
2352a26c5fd7SAlexander Aring 
2353ae0be8deSMichal Kubecek 	nl_seclevel = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_LEVEL);
2354a26c5fd7SAlexander Aring 	if (!nl_seclevel)
2355a26c5fd7SAlexander Aring 		goto nla_put_failure;
2356a26c5fd7SAlexander Aring 
2357a26c5fd7SAlexander Aring 	if (nla_put_u32(msg, NL802154_SECLEVEL_ATTR_FRAME, sl->frame_type) ||
2358a26c5fd7SAlexander Aring 	    nla_put_u32(msg, NL802154_SECLEVEL_ATTR_LEVELS, sl->sec_levels) ||
2359a26c5fd7SAlexander Aring 	    nla_put_u8(msg, NL802154_SECLEVEL_ATTR_DEV_OVERRIDE,
2360a26c5fd7SAlexander Aring 		       sl->device_override))
2361a26c5fd7SAlexander Aring 		goto nla_put_failure;
2362a26c5fd7SAlexander Aring 
2363a26c5fd7SAlexander Aring 	if (sl->frame_type == NL802154_FRAME_CMD) {
2364a26c5fd7SAlexander Aring 		if (nla_put_u32(msg, NL802154_SECLEVEL_ATTR_CMD_FRAME,
2365a26c5fd7SAlexander Aring 				sl->cmd_frame_id))
2366a26c5fd7SAlexander Aring 			goto nla_put_failure;
2367a26c5fd7SAlexander Aring 	}
2368a26c5fd7SAlexander Aring 
2369a26c5fd7SAlexander Aring 	nla_nest_end(msg, nl_seclevel);
2370a26c5fd7SAlexander Aring 	genlmsg_end(msg, hdr);
2371a26c5fd7SAlexander Aring 
2372a26c5fd7SAlexander Aring 	return 0;
2373a26c5fd7SAlexander Aring 
2374a26c5fd7SAlexander Aring nla_put_failure:
2375a26c5fd7SAlexander Aring 	genlmsg_cancel(msg, hdr);
2376a26c5fd7SAlexander Aring 	return -EMSGSIZE;
2377a26c5fd7SAlexander Aring }
2378a26c5fd7SAlexander Aring 
2379a26c5fd7SAlexander Aring static int
nl802154_dump_llsec_seclevel(struct sk_buff * skb,struct netlink_callback * cb)2380a26c5fd7SAlexander Aring nl802154_dump_llsec_seclevel(struct sk_buff *skb, struct netlink_callback *cb)
2381a26c5fd7SAlexander Aring {
2382a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = NULL;
2383a26c5fd7SAlexander Aring 	struct ieee802154_llsec_seclevel *sl;
2384a26c5fd7SAlexander Aring 	struct ieee802154_llsec_table *table;
2385a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev;
2386a26c5fd7SAlexander Aring 	int err;
2387a26c5fd7SAlexander Aring 
2388a26c5fd7SAlexander Aring 	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
2389a26c5fd7SAlexander Aring 	if (err)
2390a26c5fd7SAlexander Aring 		return err;
2391a26c5fd7SAlexander Aring 
23924c9b4f55SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) {
23934c9b4f55SAlexander Aring 		err = skb->len;
23944c9b4f55SAlexander Aring 		goto out_err;
23954c9b4f55SAlexander Aring 	}
23964c9b4f55SAlexander Aring 
2397a26c5fd7SAlexander Aring 	if (!wpan_dev->netdev) {
2398a26c5fd7SAlexander Aring 		err = -EINVAL;
2399a26c5fd7SAlexander Aring 		goto out_err;
2400a26c5fd7SAlexander Aring 	}
2401a26c5fd7SAlexander Aring 
2402a26c5fd7SAlexander Aring 	rdev_lock_llsec_table(rdev, wpan_dev);
2403a26c5fd7SAlexander Aring 	rdev_get_llsec_table(rdev, wpan_dev, &table);
2404a26c5fd7SAlexander Aring 
2405a26c5fd7SAlexander Aring 	/* TODO make it like station dump */
2406a26c5fd7SAlexander Aring 	if (cb->args[2])
2407a26c5fd7SAlexander Aring 		goto out;
2408a26c5fd7SAlexander Aring 
2409a26c5fd7SAlexander Aring 	list_for_each_entry(sl, &table->security_levels, list) {
2410a26c5fd7SAlexander Aring 		if (nl802154_send_seclevel(skb, NL802154_CMD_NEW_SEC_LEVEL,
2411a26c5fd7SAlexander Aring 					   NETLINK_CB(cb->skb).portid,
2412a26c5fd7SAlexander Aring 					   cb->nlh->nlmsg_seq, NLM_F_MULTI,
2413a26c5fd7SAlexander Aring 					   rdev, wpan_dev->netdev, sl) < 0) {
2414a26c5fd7SAlexander Aring 			/* TODO */
2415a26c5fd7SAlexander Aring 			err = -EIO;
2416a26c5fd7SAlexander Aring 			rdev_unlock_llsec_table(rdev, wpan_dev);
2417a26c5fd7SAlexander Aring 			goto out_err;
2418a26c5fd7SAlexander Aring 		}
2419a26c5fd7SAlexander Aring 	}
2420a26c5fd7SAlexander Aring 
2421a26c5fd7SAlexander Aring 	cb->args[2] = 1;
2422a26c5fd7SAlexander Aring 
2423a26c5fd7SAlexander Aring out:
2424a26c5fd7SAlexander Aring 	rdev_unlock_llsec_table(rdev, wpan_dev);
2425a26c5fd7SAlexander Aring 	err = skb->len;
2426a26c5fd7SAlexander Aring out_err:
2427a26c5fd7SAlexander Aring 	nl802154_finish_wpan_dev_dump(rdev);
2428a26c5fd7SAlexander Aring 
2429a26c5fd7SAlexander Aring 	return err;
2430a26c5fd7SAlexander Aring }
2431a26c5fd7SAlexander Aring 
2432a26c5fd7SAlexander Aring static const struct nla_policy nl802154_seclevel_policy[NL802154_SECLEVEL_ATTR_MAX + 1] = {
2433a26c5fd7SAlexander Aring 	[NL802154_SECLEVEL_ATTR_LEVELS] = { .type = NLA_U8 },
2434a26c5fd7SAlexander Aring 	[NL802154_SECLEVEL_ATTR_FRAME] = { .type = NLA_U32 },
2435a26c5fd7SAlexander Aring 	[NL802154_SECLEVEL_ATTR_CMD_FRAME] = { .type = NLA_U32 },
2436a26c5fd7SAlexander Aring 	[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE] = { .type = NLA_U8 },
2437a26c5fd7SAlexander Aring };
2438a26c5fd7SAlexander Aring 
2439a26c5fd7SAlexander Aring static int
llsec_parse_seclevel(struct nlattr * nla,struct ieee802154_llsec_seclevel * sl)2440a26c5fd7SAlexander Aring llsec_parse_seclevel(struct nlattr *nla, struct ieee802154_llsec_seclevel *sl)
2441a26c5fd7SAlexander Aring {
2442a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_SECLEVEL_ATTR_MAX + 1];
2443a26c5fd7SAlexander Aring 
24448cb08174SJohannes Berg 	if (!nla || nla_parse_nested_deprecated(attrs, NL802154_SECLEVEL_ATTR_MAX, nla, nl802154_seclevel_policy, NULL))
2445a26c5fd7SAlexander Aring 		return -EINVAL;
2446a26c5fd7SAlexander Aring 
2447a26c5fd7SAlexander Aring 	memset(sl, 0, sizeof(*sl));
2448a26c5fd7SAlexander Aring 
2449a26c5fd7SAlexander Aring 	if (!attrs[NL802154_SECLEVEL_ATTR_LEVELS] ||
2450a26c5fd7SAlexander Aring 	    !attrs[NL802154_SECLEVEL_ATTR_FRAME] ||
2451a26c5fd7SAlexander Aring 	    !attrs[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE])
2452a26c5fd7SAlexander Aring 		return -EINVAL;
2453a26c5fd7SAlexander Aring 
2454a26c5fd7SAlexander Aring 	sl->sec_levels = nla_get_u8(attrs[NL802154_SECLEVEL_ATTR_LEVELS]);
2455a26c5fd7SAlexander Aring 	sl->frame_type = nla_get_u32(attrs[NL802154_SECLEVEL_ATTR_FRAME]);
2456a26c5fd7SAlexander Aring 	sl->device_override = nla_get_u8(attrs[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE]);
2457a26c5fd7SAlexander Aring 	if (sl->frame_type > NL802154_FRAME_MAX ||
2458a26c5fd7SAlexander Aring 	    (sl->device_override != 0 && sl->device_override != 1))
2459a26c5fd7SAlexander Aring 		return -EINVAL;
2460a26c5fd7SAlexander Aring 
2461a26c5fd7SAlexander Aring 	if (sl->frame_type == NL802154_FRAME_CMD) {
2462a26c5fd7SAlexander Aring 		if (!attrs[NL802154_SECLEVEL_ATTR_CMD_FRAME])
2463a26c5fd7SAlexander Aring 			return -EINVAL;
2464a26c5fd7SAlexander Aring 
2465a26c5fd7SAlexander Aring 		sl->cmd_frame_id = nla_get_u32(attrs[NL802154_SECLEVEL_ATTR_CMD_FRAME]);
2466a26c5fd7SAlexander Aring 		if (sl->cmd_frame_id > NL802154_CMD_FRAME_MAX)
2467a26c5fd7SAlexander Aring 			return -EINVAL;
2468a26c5fd7SAlexander Aring 	}
2469a26c5fd7SAlexander Aring 
2470a26c5fd7SAlexander Aring 	return 0;
2471a26c5fd7SAlexander Aring }
2472a26c5fd7SAlexander Aring 
nl802154_add_llsec_seclevel(struct sk_buff * skb,struct genl_info * info)2473a26c5fd7SAlexander Aring static int nl802154_add_llsec_seclevel(struct sk_buff *skb,
2474a26c5fd7SAlexander Aring 				       struct genl_info *info)
2475a26c5fd7SAlexander Aring {
2476a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
2477a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
2478a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
2479a26c5fd7SAlexander Aring 	struct ieee802154_llsec_seclevel sl;
2480a26c5fd7SAlexander Aring 
24819ec87e32SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
24829ec87e32SAlexander Aring 		return -EOPNOTSUPP;
24839ec87e32SAlexander Aring 
2484a26c5fd7SAlexander Aring 	if (llsec_parse_seclevel(info->attrs[NL802154_ATTR_SEC_LEVEL],
2485a26c5fd7SAlexander Aring 				 &sl) < 0)
2486a26c5fd7SAlexander Aring 		return -EINVAL;
2487a26c5fd7SAlexander Aring 
2488a26c5fd7SAlexander Aring 	return rdev_add_seclevel(rdev, wpan_dev, &sl);
2489a26c5fd7SAlexander Aring }
2490a26c5fd7SAlexander Aring 
nl802154_del_llsec_seclevel(struct sk_buff * skb,struct genl_info * info)2491a26c5fd7SAlexander Aring static int nl802154_del_llsec_seclevel(struct sk_buff *skb,
2492a26c5fd7SAlexander Aring 				       struct genl_info *info)
2493a26c5fd7SAlexander Aring {
2494a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
2495a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
2496a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
2497a26c5fd7SAlexander Aring 	struct ieee802154_llsec_seclevel sl;
2498a26c5fd7SAlexander Aring 
24999dde1309SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
25009dde1309SAlexander Aring 		return -EOPNOTSUPP;
25019dde1309SAlexander Aring 
2502984cfd55SDongliang Mu 	if (llsec_parse_seclevel(info->attrs[NL802154_ATTR_SEC_LEVEL],
2503a26c5fd7SAlexander Aring 				 &sl) < 0)
2504a26c5fd7SAlexander Aring 		return -EINVAL;
2505a26c5fd7SAlexander Aring 
2506a26c5fd7SAlexander Aring 	return rdev_del_seclevel(rdev, wpan_dev, &sl);
2507a26c5fd7SAlexander Aring }
2508a26c5fd7SAlexander Aring #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
2509a26c5fd7SAlexander Aring 
251079fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_WPAN_PHY	0x01
251179fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_NETDEV	0x02
251279fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_RTNL		0x04
251379fe1a2aSAlexander Aring #define NL802154_FLAG_CHECK_NETDEV_UP	0x08
251479fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_WPAN_DEV	0x10
251579fe1a2aSAlexander Aring 
nl802154_pre_doit(const struct genl_split_ops * ops,struct sk_buff * skb,struct genl_info * info)251620b0b53aSJakub Kicinski static int nl802154_pre_doit(const struct genl_split_ops *ops,
251720b0b53aSJakub Kicinski 			     struct sk_buff *skb,
251879fe1a2aSAlexander Aring 			     struct genl_info *info)
251979fe1a2aSAlexander Aring {
252079fe1a2aSAlexander Aring 	struct cfg802154_registered_device *rdev;
252179fe1a2aSAlexander Aring 	struct wpan_dev *wpan_dev;
252279fe1a2aSAlexander Aring 	struct net_device *dev;
252379fe1a2aSAlexander Aring 	bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
252479fe1a2aSAlexander Aring 
252579fe1a2aSAlexander Aring 	if (rtnl)
252679fe1a2aSAlexander Aring 		rtnl_lock();
252779fe1a2aSAlexander Aring 
252879fe1a2aSAlexander Aring 	if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
252979fe1a2aSAlexander Aring 		rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
253079fe1a2aSAlexander Aring 		if (IS_ERR(rdev)) {
253179fe1a2aSAlexander Aring 			if (rtnl)
253279fe1a2aSAlexander Aring 				rtnl_unlock();
253379fe1a2aSAlexander Aring 			return PTR_ERR(rdev);
253479fe1a2aSAlexander Aring 		}
253579fe1a2aSAlexander Aring 		info->user_ptr[0] = rdev;
253679fe1a2aSAlexander Aring 	} else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
253779fe1a2aSAlexander Aring 		   ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
253879fe1a2aSAlexander Aring 		ASSERT_RTNL();
253979fe1a2aSAlexander Aring 		wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
254079fe1a2aSAlexander Aring 							   info->attrs);
254179fe1a2aSAlexander Aring 		if (IS_ERR(wpan_dev)) {
254279fe1a2aSAlexander Aring 			if (rtnl)
254379fe1a2aSAlexander Aring 				rtnl_unlock();
254479fe1a2aSAlexander Aring 			return PTR_ERR(wpan_dev);
254579fe1a2aSAlexander Aring 		}
254679fe1a2aSAlexander Aring 
254779fe1a2aSAlexander Aring 		dev = wpan_dev->netdev;
254879fe1a2aSAlexander Aring 		rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
254979fe1a2aSAlexander Aring 
255079fe1a2aSAlexander Aring 		if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
255179fe1a2aSAlexander Aring 			if (!dev) {
255279fe1a2aSAlexander Aring 				if (rtnl)
255379fe1a2aSAlexander Aring 					rtnl_unlock();
255479fe1a2aSAlexander Aring 				return -EINVAL;
255579fe1a2aSAlexander Aring 			}
255679fe1a2aSAlexander Aring 
255779fe1a2aSAlexander Aring 			info->user_ptr[1] = dev;
255879fe1a2aSAlexander Aring 		} else {
255979fe1a2aSAlexander Aring 			info->user_ptr[1] = wpan_dev;
256079fe1a2aSAlexander Aring 		}
256179fe1a2aSAlexander Aring 
256279fe1a2aSAlexander Aring 		if (dev) {
256379fe1a2aSAlexander Aring 			if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
256479fe1a2aSAlexander Aring 			    !netif_running(dev)) {
256579fe1a2aSAlexander Aring 				if (rtnl)
256679fe1a2aSAlexander Aring 					rtnl_unlock();
256779fe1a2aSAlexander Aring 				return -ENETDOWN;
256879fe1a2aSAlexander Aring 			}
256979fe1a2aSAlexander Aring 
257079fe1a2aSAlexander Aring 			dev_hold(dev);
257179fe1a2aSAlexander Aring 		}
257279fe1a2aSAlexander Aring 
257379fe1a2aSAlexander Aring 		info->user_ptr[0] = rdev;
257479fe1a2aSAlexander Aring 	}
257579fe1a2aSAlexander Aring 
257679fe1a2aSAlexander Aring 	return 0;
257779fe1a2aSAlexander Aring }
257879fe1a2aSAlexander Aring 
nl802154_post_doit(const struct genl_split_ops * ops,struct sk_buff * skb,struct genl_info * info)257920b0b53aSJakub Kicinski static void nl802154_post_doit(const struct genl_split_ops *ops,
258020b0b53aSJakub Kicinski 			       struct sk_buff *skb,
258179fe1a2aSAlexander Aring 			       struct genl_info *info)
258279fe1a2aSAlexander Aring {
258379fe1a2aSAlexander Aring 	if (info->user_ptr[1]) {
258479fe1a2aSAlexander Aring 		if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
258579fe1a2aSAlexander Aring 			struct wpan_dev *wpan_dev = info->user_ptr[1];
258679fe1a2aSAlexander Aring 
258779fe1a2aSAlexander Aring 			dev_put(wpan_dev->netdev);
258879fe1a2aSAlexander Aring 		} else {
258979fe1a2aSAlexander Aring 			dev_put(info->user_ptr[1]);
259079fe1a2aSAlexander Aring 		}
259179fe1a2aSAlexander Aring 	}
259279fe1a2aSAlexander Aring 
259379fe1a2aSAlexander Aring 	if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
259479fe1a2aSAlexander Aring 		rtnl_unlock();
259579fe1a2aSAlexander Aring }
259679fe1a2aSAlexander Aring 
259779fe1a2aSAlexander Aring static const struct genl_ops nl802154_ops[] = {
2598ca20ce20SAlexander Aring 	{
2599ca20ce20SAlexander Aring 		.cmd = NL802154_CMD_GET_WPAN_PHY,
260075cdbdd0SJiri Pirko 		.validate = GENL_DONT_VALIDATE_STRICT |
260175cdbdd0SJiri Pirko 			    GENL_DONT_VALIDATE_DUMP_STRICT,
2602ca20ce20SAlexander Aring 		.doit = nl802154_get_wpan_phy,
2603ca20ce20SAlexander Aring 		.dumpit = nl802154_dump_wpan_phy,
2604ca20ce20SAlexander Aring 		.done = nl802154_dump_wpan_phy_done,
2605ca20ce20SAlexander Aring 		/* can be retrieved by unprivileged users */
2606ca20ce20SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2607ca20ce20SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2608ca20ce20SAlexander Aring 	},
26094b96aea0SAlexander Aring 	{
26104b96aea0SAlexander Aring 		.cmd = NL802154_CMD_GET_INTERFACE,
2611ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
26124b96aea0SAlexander Aring 		.doit = nl802154_get_interface,
26134b96aea0SAlexander Aring 		.dumpit = nl802154_dump_interface,
26144b96aea0SAlexander Aring 		/* can be retrieved by unprivileged users */
26154b96aea0SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
26164b96aea0SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
26174b96aea0SAlexander Aring 	},
2618ab0bd561SAlexander Aring 	{
2619f3ea5e44SAlexander Aring 		.cmd = NL802154_CMD_NEW_INTERFACE,
2620ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2621f3ea5e44SAlexander Aring 		.doit = nl802154_new_interface,
2622f3ea5e44SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2623f3ea5e44SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2624f3ea5e44SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2625f3ea5e44SAlexander Aring 	},
2626f3ea5e44SAlexander Aring 	{
2627b821ecd4SAlexander Aring 		.cmd = NL802154_CMD_DEL_INTERFACE,
2628ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2629b821ecd4SAlexander Aring 		.doit = nl802154_del_interface,
2630b821ecd4SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2631b821ecd4SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
2632b821ecd4SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2633b821ecd4SAlexander Aring 	},
2634b821ecd4SAlexander Aring 	{
2635ab0bd561SAlexander Aring 		.cmd = NL802154_CMD_SET_CHANNEL,
2636ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2637ab0bd561SAlexander Aring 		.doit = nl802154_set_channel,
2638ab0bd561SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2639ab0bd561SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2640ab0bd561SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2641ab0bd561SAlexander Aring 	},
2642702bf371SAlexander Aring 	{
2643ba2a9506SAlexander Aring 		.cmd = NL802154_CMD_SET_CCA_MODE,
2644ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2645ba2a9506SAlexander Aring 		.doit = nl802154_set_cca_mode,
2646ba2a9506SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2647ba2a9506SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2648ba2a9506SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2649ba2a9506SAlexander Aring 	},
2650ba2a9506SAlexander Aring 	{
2651b69644c1SAlexander Aring 		.cmd = NL802154_CMD_SET_CCA_ED_LEVEL,
2652ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2653b69644c1SAlexander Aring 		.doit = nl802154_set_cca_ed_level,
2654b69644c1SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2655b69644c1SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2656b69644c1SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2657b69644c1SAlexander Aring 	},
2658b69644c1SAlexander Aring 	{
26590f999b09SVarka Bhadram 		.cmd = NL802154_CMD_SET_TX_POWER,
2660ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
26610f999b09SVarka Bhadram 		.doit = nl802154_set_tx_power,
26620f999b09SVarka Bhadram 		.flags = GENL_ADMIN_PERM,
26630f999b09SVarka Bhadram 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
26640f999b09SVarka Bhadram 				  NL802154_FLAG_NEED_RTNL,
26650f999b09SVarka Bhadram 	},
26660f999b09SVarka Bhadram 	{
266766e5c267SAlexander Aring 		.cmd = NL802154_CMD_SET_WPAN_PHY_NETNS,
2668ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
266966e5c267SAlexander Aring 		.doit = nl802154_wpan_phy_netns,
267066e5c267SAlexander Aring 		.flags = GENL_ADMIN_PERM,
267166e5c267SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
267266e5c267SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
267366e5c267SAlexander Aring 	},
267466e5c267SAlexander Aring 	{
2675702bf371SAlexander Aring 		.cmd = NL802154_CMD_SET_PAN_ID,
2676ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2677702bf371SAlexander Aring 		.doit = nl802154_set_pan_id,
2678702bf371SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2679702bf371SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2680702bf371SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2681702bf371SAlexander Aring 	},
26829830c62aSAlexander Aring 	{
26839830c62aSAlexander Aring 		.cmd = NL802154_CMD_SET_SHORT_ADDR,
2684ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
26859830c62aSAlexander Aring 		.doit = nl802154_set_short_addr,
26869830c62aSAlexander Aring 		.flags = GENL_ADMIN_PERM,
26879830c62aSAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
26889830c62aSAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
26899830c62aSAlexander Aring 	},
2690656a999eSAlexander Aring 	{
2691656a999eSAlexander Aring 		.cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
2692ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2693656a999eSAlexander Aring 		.doit = nl802154_set_backoff_exponent,
2694656a999eSAlexander Aring 		.flags = GENL_ADMIN_PERM,
2695656a999eSAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2696656a999eSAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2697656a999eSAlexander Aring 	},
2698a01ba765SAlexander Aring 	{
2699a01ba765SAlexander Aring 		.cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
2700ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2701a01ba765SAlexander Aring 		.doit = nl802154_set_max_csma_backoffs,
2702a01ba765SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2703a01ba765SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2704a01ba765SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2705a01ba765SAlexander Aring 	},
270617a3a46bSAlexander Aring 	{
270717a3a46bSAlexander Aring 		.cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
2708ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
270917a3a46bSAlexander Aring 		.doit = nl802154_set_max_frame_retries,
271017a3a46bSAlexander Aring 		.flags = GENL_ADMIN_PERM,
271117a3a46bSAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
271217a3a46bSAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
271317a3a46bSAlexander Aring 	},
2714c8937a1dSAlexander Aring 	{
2715c8937a1dSAlexander Aring 		.cmd = NL802154_CMD_SET_LBT_MODE,
2716ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2717c8937a1dSAlexander Aring 		.doit = nl802154_set_lbt_mode,
2718c8937a1dSAlexander Aring 		.flags = GENL_ADMIN_PERM,
2719c8937a1dSAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2720c8937a1dSAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2721c8937a1dSAlexander Aring 	},
2722c91208d8SAlexander Aring 	{
2723c91208d8SAlexander Aring 		.cmd = NL802154_CMD_SET_ACKREQ_DEFAULT,
2724ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2725c91208d8SAlexander Aring 		.doit = nl802154_set_ackreq_default,
2726c91208d8SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2727c91208d8SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2728c91208d8SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2729c91208d8SAlexander Aring 	},
2730ed3557c9SMiquel Raynal 	{
2731ed3557c9SMiquel Raynal 		.cmd = NL802154_CMD_TRIGGER_SCAN,
2732ed3557c9SMiquel Raynal 		.doit = nl802154_trigger_scan,
2733ed3557c9SMiquel Raynal 		.flags = GENL_ADMIN_PERM,
2734ed3557c9SMiquel Raynal 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2735ed3557c9SMiquel Raynal 				  NL802154_FLAG_CHECK_NETDEV_UP |
2736ed3557c9SMiquel Raynal 				  NL802154_FLAG_NEED_RTNL,
2737ed3557c9SMiquel Raynal 	},
2738ed3557c9SMiquel Raynal 	{
2739ed3557c9SMiquel Raynal 		.cmd = NL802154_CMD_ABORT_SCAN,
2740ed3557c9SMiquel Raynal 		.doit = nl802154_abort_scan,
2741ed3557c9SMiquel Raynal 		.flags = GENL_ADMIN_PERM,
2742ed3557c9SMiquel Raynal 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2743ed3557c9SMiquel Raynal 				  NL802154_FLAG_CHECK_NETDEV_UP |
2744ed3557c9SMiquel Raynal 				  NL802154_FLAG_NEED_RTNL,
2745ed3557c9SMiquel Raynal 	},
27469bc11450SMiquel Raynal 	{
27479bc11450SMiquel Raynal 		.cmd = NL802154_CMD_SEND_BEACONS,
27489bc11450SMiquel Raynal 		.doit = nl802154_send_beacons,
27499bc11450SMiquel Raynal 		.flags = GENL_ADMIN_PERM,
27509bc11450SMiquel Raynal 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
27519bc11450SMiquel Raynal 				  NL802154_FLAG_CHECK_NETDEV_UP |
27529bc11450SMiquel Raynal 				  NL802154_FLAG_NEED_RTNL,
27539bc11450SMiquel Raynal 	},
27549bc11450SMiquel Raynal 	{
27559bc11450SMiquel Raynal 		.cmd = NL802154_CMD_STOP_BEACONS,
27569bc11450SMiquel Raynal 		.doit = nl802154_stop_beacons,
27579bc11450SMiquel Raynal 		.flags = GENL_ADMIN_PERM,
27589bc11450SMiquel Raynal 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
27599bc11450SMiquel Raynal 				  NL802154_FLAG_CHECK_NETDEV_UP |
27609bc11450SMiquel Raynal 				  NL802154_FLAG_NEED_RTNL,
27619bc11450SMiquel Raynal 	},
2762a26c5fd7SAlexander Aring #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
2763a26c5fd7SAlexander Aring 	{
2764a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_SET_SEC_PARAMS,
2765ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2766a26c5fd7SAlexander Aring 		.doit = nl802154_set_llsec_params,
2767a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2768a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2769a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2770a26c5fd7SAlexander Aring 	},
2771a26c5fd7SAlexander Aring 	{
2772a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_GET_SEC_KEY,
277375cdbdd0SJiri Pirko 		.validate = GENL_DONT_VALIDATE_STRICT |
277475cdbdd0SJiri Pirko 			    GENL_DONT_VALIDATE_DUMP_STRICT,
2775a26c5fd7SAlexander Aring 		/* TODO .doit by matching key id? */
2776a26c5fd7SAlexander Aring 		.dumpit = nl802154_dump_llsec_key,
2777a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2778a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2779a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2780a26c5fd7SAlexander Aring 	},
2781a26c5fd7SAlexander Aring 	{
2782a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_NEW_SEC_KEY,
2783ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2784a26c5fd7SAlexander Aring 		.doit = nl802154_add_llsec_key,
2785a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2786a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2787a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2788a26c5fd7SAlexander Aring 	},
2789a26c5fd7SAlexander Aring 	{
2790a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_DEL_SEC_KEY,
2791ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2792a26c5fd7SAlexander Aring 		.doit = nl802154_del_llsec_key,
2793a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2794a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2795a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2796a26c5fd7SAlexander Aring 	},
2797a26c5fd7SAlexander Aring 	/* TODO unique identifier must short+pan OR extended_addr */
2798a26c5fd7SAlexander Aring 	{
2799a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_GET_SEC_DEV,
280075cdbdd0SJiri Pirko 		.validate = GENL_DONT_VALIDATE_STRICT |
280175cdbdd0SJiri Pirko 			    GENL_DONT_VALIDATE_DUMP_STRICT,
2802a26c5fd7SAlexander Aring 		/* TODO .doit by matching extended_addr? */
2803a26c5fd7SAlexander Aring 		.dumpit = nl802154_dump_llsec_dev,
2804a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2805a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2806a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2807a26c5fd7SAlexander Aring 	},
2808a26c5fd7SAlexander Aring 	{
2809a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_NEW_SEC_DEV,
2810ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2811a26c5fd7SAlexander Aring 		.doit = nl802154_add_llsec_dev,
2812a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2813a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2814a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2815a26c5fd7SAlexander Aring 	},
2816a26c5fd7SAlexander Aring 	{
2817a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_DEL_SEC_DEV,
2818ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2819a26c5fd7SAlexander Aring 		.doit = nl802154_del_llsec_dev,
2820a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2821a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2822a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2823a26c5fd7SAlexander Aring 	},
2824a26c5fd7SAlexander Aring 	/* TODO remove complete devkey, put it as nested? */
2825a26c5fd7SAlexander Aring 	{
2826a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_GET_SEC_DEVKEY,
282775cdbdd0SJiri Pirko 		.validate = GENL_DONT_VALIDATE_STRICT |
282875cdbdd0SJiri Pirko 			    GENL_DONT_VALIDATE_DUMP_STRICT,
2829a26c5fd7SAlexander Aring 		/* TODO doit by matching ??? */
2830a26c5fd7SAlexander Aring 		.dumpit = nl802154_dump_llsec_devkey,
2831a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2832a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2833a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2834a26c5fd7SAlexander Aring 	},
2835a26c5fd7SAlexander Aring 	{
2836a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_NEW_SEC_DEVKEY,
2837ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2838a26c5fd7SAlexander Aring 		.doit = nl802154_add_llsec_devkey,
2839a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2840a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2841a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2842a26c5fd7SAlexander Aring 	},
2843a26c5fd7SAlexander Aring 	{
2844a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_DEL_SEC_DEVKEY,
2845ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2846a26c5fd7SAlexander Aring 		.doit = nl802154_del_llsec_devkey,
2847a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2848a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2849a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2850a26c5fd7SAlexander Aring 	},
2851a26c5fd7SAlexander Aring 	{
2852a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_GET_SEC_LEVEL,
285375cdbdd0SJiri Pirko 		.validate = GENL_DONT_VALIDATE_STRICT |
285475cdbdd0SJiri Pirko 			    GENL_DONT_VALIDATE_DUMP_STRICT,
2855a26c5fd7SAlexander Aring 		/* TODO .doit by matching frame_type? */
2856a26c5fd7SAlexander Aring 		.dumpit = nl802154_dump_llsec_seclevel,
2857a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2858a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2859a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2860a26c5fd7SAlexander Aring 	},
2861a26c5fd7SAlexander Aring 	{
2862a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_NEW_SEC_LEVEL,
2863ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2864a26c5fd7SAlexander Aring 		.doit = nl802154_add_llsec_seclevel,
2865a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2866a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2867a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2868a26c5fd7SAlexander Aring 	},
2869a26c5fd7SAlexander Aring 	{
2870a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_DEL_SEC_LEVEL,
2871ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2872a26c5fd7SAlexander Aring 		/* TODO match frame_type only? */
2873a26c5fd7SAlexander Aring 		.doit = nl802154_del_llsec_seclevel,
2874a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2875a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2876a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2877a26c5fd7SAlexander Aring 	},
2878a26c5fd7SAlexander Aring #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
287979fe1a2aSAlexander Aring };
288079fe1a2aSAlexander Aring 
288156989f6dSJohannes Berg static struct genl_family nl802154_fam __ro_after_init = {
2882489111e5SJohannes Berg 	.name = NL802154_GENL_NAME,	/* have users key off the name instead */
2883489111e5SJohannes Berg 	.hdrsize = 0,			/* no private header */
2884489111e5SJohannes Berg 	.version = 1,			/* no particular meaning now */
2885489111e5SJohannes Berg 	.maxattr = NL802154_ATTR_MAX,
28863b0f31f2SJohannes Berg 	.policy = nl802154_policy,
2887489111e5SJohannes Berg 	.netnsok = true,
2888489111e5SJohannes Berg 	.pre_doit = nl802154_pre_doit,
2889489111e5SJohannes Berg 	.post_doit = nl802154_post_doit,
2890489111e5SJohannes Berg 	.module = THIS_MODULE,
2891489111e5SJohannes Berg 	.ops = nl802154_ops,
2892489111e5SJohannes Berg 	.n_ops = ARRAY_SIZE(nl802154_ops),
28939c5d03d3SJakub Kicinski 	.resv_start_op = NL802154_CMD_DEL_SEC_LEVEL + 1,
2894489111e5SJohannes Berg 	.mcgrps = nl802154_mcgrps,
2895489111e5SJohannes Berg 	.n_mcgrps = ARRAY_SIZE(nl802154_mcgrps),
2896489111e5SJohannes Berg };
2897489111e5SJohannes Berg 
289879fe1a2aSAlexander Aring /* initialisation/exit functions */
nl802154_init(void)289956989f6dSJohannes Berg int __init nl802154_init(void)
290079fe1a2aSAlexander Aring {
2901489111e5SJohannes Berg 	return genl_register_family(&nl802154_fam);
290279fe1a2aSAlexander Aring }
290379fe1a2aSAlexander Aring 
nl802154_exit(void)290479fe1a2aSAlexander Aring void nl802154_exit(void)
290579fe1a2aSAlexander Aring {
290679fe1a2aSAlexander Aring 	genl_unregister_family(&nl802154_fam);
290779fe1a2aSAlexander Aring }
2908