xref: /openbmc/linux/net/ieee802154/nl802154.c (revision 1edecbd0bd45c9c899e0f82b123342f28423468c)
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 *
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 *
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 *
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] =
236648324c9SMiquel Raynal 		NLA_POLICY_MAX(NLA_U8, IEEE802154_MAX_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
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),
26575cdbdd0SJiri Pirko 							    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
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 */
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
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
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
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 
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 
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);
57375cdbdd0SJiri Pirko 	struct nlattr **tb = 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
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 
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 
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 
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
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 
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
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
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 
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 
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 
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 
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 
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 
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 
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 
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 
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
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
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
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 
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
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 
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 
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 
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 
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");
1412*1edecbd0SMiquel Raynal 		return -EOPNOTSUPP;
1413a0b61066SMiquel Raynal 	}
1414a0b61066SMiquel Raynal 
1415a0b61066SMiquel Raynal 	if (!nla_get_u8(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 
1420ed3557c9SMiquel Raynal 	request = kzalloc(sizeof(*request), GFP_KERNEL);
1421ed3557c9SMiquel Raynal 	if (!request)
1422ed3557c9SMiquel Raynal 		return -ENOMEM;
1423ed3557c9SMiquel Raynal 
1424ed3557c9SMiquel Raynal 	request->wpan_dev = wpan_dev;
1425ed3557c9SMiquel Raynal 	request->wpan_phy = wpan_phy;
1426ed3557c9SMiquel Raynal 
1427ed3557c9SMiquel Raynal 	type = nla_get_u8(info->attrs[NL802154_ATTR_SCAN_TYPE]);
1428ed3557c9SMiquel Raynal 	switch (type) {
1429ed3557c9SMiquel Raynal 	case NL802154_SCAN_PASSIVE:
1430ed3557c9SMiquel Raynal 		request->type = type;
1431ed3557c9SMiquel Raynal 		break;
1432ed3557c9SMiquel Raynal 	default:
1433a0b61066SMiquel Raynal 		NL_SET_ERR_MSG_FMT(info->extack, "Unsupported scan type: %d", type);
1434ed3557c9SMiquel Raynal 		err = -EINVAL;
1435ed3557c9SMiquel Raynal 		goto free_request;
1436ed3557c9SMiquel Raynal 	}
1437ed3557c9SMiquel Raynal 
1438ed3557c9SMiquel Raynal 	/* Use current page by default */
1439648324c9SMiquel Raynal 	if (info->attrs[NL802154_ATTR_PAGE])
1440648324c9SMiquel Raynal 		request->page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
1441648324c9SMiquel Raynal 	else
1442ed3557c9SMiquel Raynal 		request->page = wpan_phy->current_page;
1443ed3557c9SMiquel Raynal 
1444ed3557c9SMiquel Raynal 	/* Scan all supported channels by default */
1445648324c9SMiquel Raynal 	if (info->attrs[NL802154_ATTR_SCAN_CHANNELS])
1446648324c9SMiquel Raynal 		request->channels = nla_get_u32(info->attrs[NL802154_ATTR_SCAN_CHANNELS]);
1447648324c9SMiquel Raynal 	else
1448ed3557c9SMiquel Raynal 		request->channels = wpan_phy->supported.channels[request->page];
1449ed3557c9SMiquel Raynal 
1450ed3557c9SMiquel Raynal 	/* Use maximum duration order by default */
1451648324c9SMiquel Raynal 	if (info->attrs[NL802154_ATTR_SCAN_DURATION])
1452648324c9SMiquel Raynal 		request->duration = nla_get_u8(info->attrs[NL802154_ATTR_SCAN_DURATION]);
1453648324c9SMiquel Raynal 	else
1454ed3557c9SMiquel Raynal 		request->duration = IEEE802154_MAX_SCAN_DURATION;
1455ed3557c9SMiquel Raynal 
1456ed3557c9SMiquel Raynal 	if (wpan_dev->netdev)
1457ed3557c9SMiquel Raynal 		dev_hold(wpan_dev->netdev);
1458ed3557c9SMiquel Raynal 
1459ed3557c9SMiquel Raynal 	err = rdev_trigger_scan(rdev, request);
1460ed3557c9SMiquel Raynal 	if (err) {
1461ed3557c9SMiquel Raynal 		pr_err("Failure starting scanning (%d)\n", err);
1462ed3557c9SMiquel Raynal 		goto free_device;
1463ed3557c9SMiquel Raynal 	}
1464ed3557c9SMiquel Raynal 
1465ed3557c9SMiquel Raynal 	return 0;
1466ed3557c9SMiquel Raynal 
1467ed3557c9SMiquel Raynal free_device:
1468ed3557c9SMiquel Raynal 	if (wpan_dev->netdev)
1469ed3557c9SMiquel Raynal 		dev_put(wpan_dev->netdev);
1470ed3557c9SMiquel Raynal free_request:
1471ed3557c9SMiquel Raynal 	kfree(request);
1472ed3557c9SMiquel Raynal 
1473ed3557c9SMiquel Raynal 	return err;
1474ed3557c9SMiquel Raynal }
1475ed3557c9SMiquel Raynal 
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 
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 
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 
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 	if (wpan_dev->netdev)
1559ed3557c9SMiquel Raynal 		dev_put(wpan_dev->netdev);
1560ed3557c9SMiquel Raynal 
1561ed3557c9SMiquel Raynal 	return err;
1562ed3557c9SMiquel Raynal }
1563ed3557c9SMiquel Raynal EXPORT_SYMBOL_GPL(nl802154_scan_done);
1564ed3557c9SMiquel Raynal 
1565ed3557c9SMiquel Raynal static int nl802154_abort_scan(struct sk_buff *skb, struct genl_info *info)
1566ed3557c9SMiquel Raynal {
1567ed3557c9SMiquel Raynal 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1568ed3557c9SMiquel Raynal 	struct net_device *dev = info->user_ptr[1];
1569ed3557c9SMiquel Raynal 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1570ed3557c9SMiquel Raynal 
1571ed3557c9SMiquel Raynal 	/* Resources are released in the notification helper above */
1572ed3557c9SMiquel Raynal 	return rdev_abort_scan(rdev, wpan_dev);
1573ed3557c9SMiquel Raynal }
1574ed3557c9SMiquel Raynal 
15759bc11450SMiquel Raynal static int
15769bc11450SMiquel Raynal nl802154_send_beacons(struct sk_buff *skb, struct genl_info *info)
15779bc11450SMiquel Raynal {
15789bc11450SMiquel Raynal 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
15799bc11450SMiquel Raynal 	struct net_device *dev = info->user_ptr[1];
15809bc11450SMiquel Raynal 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
15819bc11450SMiquel Raynal 	struct wpan_phy *wpan_phy = &rdev->wpan_phy;
15829bc11450SMiquel Raynal 	struct cfg802154_beacon_request *request;
15839bc11450SMiquel Raynal 	int err;
15849bc11450SMiquel Raynal 
1585a0b61066SMiquel Raynal 	if (wpan_dev->iftype != NL802154_IFTYPE_COORD) {
1586a0b61066SMiquel Raynal 		NL_SET_ERR_MSG(info->extack, "Only coordinators can send beacons");
15879bc11450SMiquel Raynal 		return -EOPNOTSUPP;
1588a0b61066SMiquel Raynal 	}
15899bc11450SMiquel Raynal 
15909bc11450SMiquel Raynal 	if (wpan_dev->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
1591a0b61066SMiquel Raynal 		NL_SET_ERR_MSG(info->extack, "Device is not part of any PAN");
15929bc11450SMiquel Raynal 		return -EPERM;
15939bc11450SMiquel Raynal 	}
15949bc11450SMiquel Raynal 
15959bc11450SMiquel Raynal 	request = kzalloc(sizeof(*request), GFP_KERNEL);
15969bc11450SMiquel Raynal 	if (!request)
15979bc11450SMiquel Raynal 		return -ENOMEM;
15989bc11450SMiquel Raynal 
15999bc11450SMiquel Raynal 	request->wpan_dev = wpan_dev;
16009bc11450SMiquel Raynal 	request->wpan_phy = wpan_phy;
16019bc11450SMiquel Raynal 
16029bc11450SMiquel Raynal 	/* Use maximum duration order by default */
1603648324c9SMiquel Raynal 	if (info->attrs[NL802154_ATTR_BEACON_INTERVAL])
1604648324c9SMiquel Raynal 		request->interval = nla_get_u8(info->attrs[NL802154_ATTR_BEACON_INTERVAL]);
1605648324c9SMiquel Raynal 	else
16069bc11450SMiquel Raynal 		request->interval = IEEE802154_MAX_SCAN_DURATION;
16079bc11450SMiquel Raynal 
16089bc11450SMiquel Raynal 	if (wpan_dev->netdev)
16099bc11450SMiquel Raynal 		dev_hold(wpan_dev->netdev);
16109bc11450SMiquel Raynal 
16119bc11450SMiquel Raynal 	err = rdev_send_beacons(rdev, request);
16129bc11450SMiquel Raynal 	if (err) {
16139bc11450SMiquel Raynal 		pr_err("Failure starting sending beacons (%d)\n", err);
16149bc11450SMiquel Raynal 		goto free_device;
16159bc11450SMiquel Raynal 	}
16169bc11450SMiquel Raynal 
16179bc11450SMiquel Raynal 	return 0;
16189bc11450SMiquel Raynal 
16199bc11450SMiquel Raynal free_device:
16209bc11450SMiquel Raynal 	if (wpan_dev->netdev)
16219bc11450SMiquel Raynal 		dev_put(wpan_dev->netdev);
1622648324c9SMiquel Raynal 
16239bc11450SMiquel Raynal 	kfree(request);
16249bc11450SMiquel Raynal 
16259bc11450SMiquel Raynal 	return err;
16269bc11450SMiquel Raynal }
16279bc11450SMiquel Raynal 
16289bc11450SMiquel Raynal void nl802154_beaconing_done(struct wpan_dev *wpan_dev)
16299bc11450SMiquel Raynal {
16309bc11450SMiquel Raynal 	if (wpan_dev->netdev)
16319bc11450SMiquel Raynal 		dev_put(wpan_dev->netdev);
16329bc11450SMiquel Raynal }
16339bc11450SMiquel Raynal EXPORT_SYMBOL_GPL(nl802154_beaconing_done);
16349bc11450SMiquel Raynal 
16359bc11450SMiquel Raynal static int
16369bc11450SMiquel Raynal nl802154_stop_beacons(struct sk_buff *skb, struct genl_info *info)
16379bc11450SMiquel Raynal {
16389bc11450SMiquel Raynal 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
16399bc11450SMiquel Raynal 	struct net_device *dev = info->user_ptr[1];
16409bc11450SMiquel Raynal 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
16419bc11450SMiquel Raynal 
16429bc11450SMiquel Raynal 	/* Resources are released in the notification helper above */
16439bc11450SMiquel Raynal 	return rdev_stop_beacons(rdev, wpan_dev);
16449bc11450SMiquel Raynal }
16459bc11450SMiquel Raynal 
1646a26c5fd7SAlexander Aring #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
1647a26c5fd7SAlexander Aring static const struct nla_policy nl802154_dev_addr_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = {
1648a26c5fd7SAlexander Aring 	[NL802154_DEV_ADDR_ATTR_PAN_ID] = { .type = NLA_U16 },
1649a26c5fd7SAlexander Aring 	[NL802154_DEV_ADDR_ATTR_MODE] = { .type = NLA_U32 },
1650a26c5fd7SAlexander Aring 	[NL802154_DEV_ADDR_ATTR_SHORT] = { .type = NLA_U16 },
1651a26c5fd7SAlexander Aring 	[NL802154_DEV_ADDR_ATTR_EXTENDED] = { .type = NLA_U64 },
1652a26c5fd7SAlexander Aring };
1653a26c5fd7SAlexander Aring 
1654a26c5fd7SAlexander Aring static int
1655a26c5fd7SAlexander Aring ieee802154_llsec_parse_dev_addr(struct nlattr *nla,
1656a26c5fd7SAlexander Aring 				struct ieee802154_addr *addr)
1657a26c5fd7SAlexander Aring {
1658a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_DEV_ADDR_ATTR_MAX + 1];
1659a26c5fd7SAlexander Aring 
16608cb08174SJohannes Berg 	if (!nla || nla_parse_nested_deprecated(attrs, NL802154_DEV_ADDR_ATTR_MAX, nla, nl802154_dev_addr_policy, NULL))
1661a26c5fd7SAlexander Aring 		return -EINVAL;
1662a26c5fd7SAlexander Aring 
16639fdd0491SDan Robertson 	if (!attrs[NL802154_DEV_ADDR_ATTR_PAN_ID] || !attrs[NL802154_DEV_ADDR_ATTR_MODE])
1664a26c5fd7SAlexander Aring 		return -EINVAL;
1665a26c5fd7SAlexander Aring 
1666a26c5fd7SAlexander Aring 	addr->pan_id = nla_get_le16(attrs[NL802154_DEV_ADDR_ATTR_PAN_ID]);
1667a26c5fd7SAlexander Aring 	addr->mode = nla_get_u32(attrs[NL802154_DEV_ADDR_ATTR_MODE]);
1668a26c5fd7SAlexander Aring 	switch (addr->mode) {
1669a26c5fd7SAlexander Aring 	case NL802154_DEV_ADDR_SHORT:
16709fdd0491SDan Robertson 		if (!attrs[NL802154_DEV_ADDR_ATTR_SHORT])
16719fdd0491SDan Robertson 			return -EINVAL;
1672a26c5fd7SAlexander Aring 		addr->short_addr = nla_get_le16(attrs[NL802154_DEV_ADDR_ATTR_SHORT]);
1673a26c5fd7SAlexander Aring 		break;
1674a26c5fd7SAlexander Aring 	case NL802154_DEV_ADDR_EXTENDED:
16759fdd0491SDan Robertson 		if (!attrs[NL802154_DEV_ADDR_ATTR_EXTENDED])
16769fdd0491SDan Robertson 			return -EINVAL;
1677a26c5fd7SAlexander Aring 		addr->extended_addr = nla_get_le64(attrs[NL802154_DEV_ADDR_ATTR_EXTENDED]);
1678a26c5fd7SAlexander Aring 		break;
1679a26c5fd7SAlexander Aring 	default:
1680a26c5fd7SAlexander Aring 		return -EINVAL;
1681a26c5fd7SAlexander Aring 	}
1682a26c5fd7SAlexander Aring 
1683a26c5fd7SAlexander Aring 	return 0;
1684a26c5fd7SAlexander Aring }
1685a26c5fd7SAlexander Aring 
1686a26c5fd7SAlexander Aring static const struct nla_policy nl802154_key_id_policy[NL802154_KEY_ID_ATTR_MAX + 1] = {
1687a26c5fd7SAlexander Aring 	[NL802154_KEY_ID_ATTR_MODE] = { .type = NLA_U32 },
1688a26c5fd7SAlexander Aring 	[NL802154_KEY_ID_ATTR_INDEX] = { .type = NLA_U8 },
1689a26c5fd7SAlexander Aring 	[NL802154_KEY_ID_ATTR_IMPLICIT] = { .type = NLA_NESTED },
1690a26c5fd7SAlexander Aring 	[NL802154_KEY_ID_ATTR_SOURCE_SHORT] = { .type = NLA_U32 },
1691a26c5fd7SAlexander Aring 	[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED] = { .type = NLA_U64 },
1692a26c5fd7SAlexander Aring };
1693a26c5fd7SAlexander Aring 
1694a26c5fd7SAlexander Aring static int
1695a26c5fd7SAlexander Aring ieee802154_llsec_parse_key_id(struct nlattr *nla,
1696a26c5fd7SAlexander Aring 			      struct ieee802154_llsec_key_id *desc)
1697a26c5fd7SAlexander Aring {
1698a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_KEY_ID_ATTR_MAX + 1];
1699a26c5fd7SAlexander Aring 
17008cb08174SJohannes Berg 	if (!nla || nla_parse_nested_deprecated(attrs, NL802154_KEY_ID_ATTR_MAX, nla, nl802154_key_id_policy, NULL))
1701a26c5fd7SAlexander Aring 		return -EINVAL;
1702a26c5fd7SAlexander Aring 
1703a26c5fd7SAlexander Aring 	if (!attrs[NL802154_KEY_ID_ATTR_MODE])
1704a26c5fd7SAlexander Aring 		return -EINVAL;
1705a26c5fd7SAlexander Aring 
1706a26c5fd7SAlexander Aring 	desc->mode = nla_get_u32(attrs[NL802154_KEY_ID_ATTR_MODE]);
1707a26c5fd7SAlexander Aring 	switch (desc->mode) {
1708a26c5fd7SAlexander Aring 	case NL802154_KEY_ID_MODE_IMPLICIT:
1709a26c5fd7SAlexander Aring 		if (!attrs[NL802154_KEY_ID_ATTR_IMPLICIT])
1710a26c5fd7SAlexander Aring 			return -EINVAL;
1711a26c5fd7SAlexander Aring 
1712a26c5fd7SAlexander Aring 		if (ieee802154_llsec_parse_dev_addr(attrs[NL802154_KEY_ID_ATTR_IMPLICIT],
1713a26c5fd7SAlexander Aring 						    &desc->device_addr) < 0)
1714a26c5fd7SAlexander Aring 			return -EINVAL;
1715a26c5fd7SAlexander Aring 		break;
1716a26c5fd7SAlexander Aring 	case NL802154_KEY_ID_MODE_INDEX:
1717a26c5fd7SAlexander Aring 		break;
1718a26c5fd7SAlexander Aring 	case NL802154_KEY_ID_MODE_INDEX_SHORT:
1719a26c5fd7SAlexander Aring 		if (!attrs[NL802154_KEY_ID_ATTR_SOURCE_SHORT])
1720a26c5fd7SAlexander Aring 			return -EINVAL;
1721a26c5fd7SAlexander Aring 
1722a26c5fd7SAlexander Aring 		desc->short_source = nla_get_le32(attrs[NL802154_KEY_ID_ATTR_SOURCE_SHORT]);
1723a26c5fd7SAlexander Aring 		break;
1724a26c5fd7SAlexander Aring 	case NL802154_KEY_ID_MODE_INDEX_EXTENDED:
1725a26c5fd7SAlexander Aring 		if (!attrs[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED])
1726a26c5fd7SAlexander Aring 			return -EINVAL;
1727a26c5fd7SAlexander Aring 
1728a26c5fd7SAlexander Aring 		desc->extended_source = nla_get_le64(attrs[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED]);
1729a26c5fd7SAlexander Aring 		break;
1730a26c5fd7SAlexander Aring 	default:
1731a26c5fd7SAlexander Aring 		return -EINVAL;
1732a26c5fd7SAlexander Aring 	}
1733a26c5fd7SAlexander Aring 
1734a26c5fd7SAlexander Aring 	if (desc->mode != NL802154_KEY_ID_MODE_IMPLICIT) {
1735a26c5fd7SAlexander Aring 		if (!attrs[NL802154_KEY_ID_ATTR_INDEX])
1736a26c5fd7SAlexander Aring 			return -EINVAL;
1737a26c5fd7SAlexander Aring 
1738a26c5fd7SAlexander Aring 		/* TODO change id to idx */
1739a26c5fd7SAlexander Aring 		desc->id = nla_get_u8(attrs[NL802154_KEY_ID_ATTR_INDEX]);
1740a26c5fd7SAlexander Aring 	}
1741a26c5fd7SAlexander Aring 
1742a26c5fd7SAlexander Aring 	return 0;
1743a26c5fd7SAlexander Aring }
1744a26c5fd7SAlexander Aring 
1745a26c5fd7SAlexander Aring static int nl802154_set_llsec_params(struct sk_buff *skb,
1746a26c5fd7SAlexander Aring 				     struct genl_info *info)
1747a26c5fd7SAlexander Aring {
1748a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1749a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
1750a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1751a26c5fd7SAlexander Aring 	struct ieee802154_llsec_params params;
1752a26c5fd7SAlexander Aring 	u32 changed = 0;
1753a26c5fd7SAlexander Aring 	int ret;
1754a26c5fd7SAlexander Aring 
175588c17855SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
175688c17855SAlexander Aring 		return -EOPNOTSUPP;
175788c17855SAlexander Aring 
1758a26c5fd7SAlexander Aring 	if (info->attrs[NL802154_ATTR_SEC_ENABLED]) {
1759a26c5fd7SAlexander Aring 		u8 enabled;
1760a26c5fd7SAlexander Aring 
1761a26c5fd7SAlexander Aring 		enabled = nla_get_u8(info->attrs[NL802154_ATTR_SEC_ENABLED]);
1762a26c5fd7SAlexander Aring 		if (enabled != 0 && enabled != 1)
1763a26c5fd7SAlexander Aring 			return -EINVAL;
1764a26c5fd7SAlexander Aring 
1765a26c5fd7SAlexander Aring 		params.enabled = nla_get_u8(info->attrs[NL802154_ATTR_SEC_ENABLED]);
1766a26c5fd7SAlexander Aring 		changed |= IEEE802154_LLSEC_PARAM_ENABLED;
1767a26c5fd7SAlexander Aring 	}
1768a26c5fd7SAlexander Aring 
1769a26c5fd7SAlexander Aring 	if (info->attrs[NL802154_ATTR_SEC_OUT_KEY_ID]) {
1770a26c5fd7SAlexander Aring 		ret = ieee802154_llsec_parse_key_id(info->attrs[NL802154_ATTR_SEC_OUT_KEY_ID],
1771a26c5fd7SAlexander Aring 						    &params.out_key);
1772a26c5fd7SAlexander Aring 		if (ret < 0)
1773a26c5fd7SAlexander Aring 			return ret;
1774a26c5fd7SAlexander Aring 
1775a26c5fd7SAlexander Aring 		changed |= IEEE802154_LLSEC_PARAM_OUT_KEY;
1776a26c5fd7SAlexander Aring 	}
1777a26c5fd7SAlexander Aring 
1778a26c5fd7SAlexander Aring 	if (info->attrs[NL802154_ATTR_SEC_OUT_LEVEL]) {
1779a26c5fd7SAlexander Aring 		params.out_level = nla_get_u32(info->attrs[NL802154_ATTR_SEC_OUT_LEVEL]);
1780a26c5fd7SAlexander Aring 		if (params.out_level > NL802154_SECLEVEL_MAX)
1781a26c5fd7SAlexander Aring 			return -EINVAL;
1782a26c5fd7SAlexander Aring 
1783a26c5fd7SAlexander Aring 		changed |= IEEE802154_LLSEC_PARAM_OUT_LEVEL;
1784a26c5fd7SAlexander Aring 	}
1785a26c5fd7SAlexander Aring 
1786a26c5fd7SAlexander Aring 	if (info->attrs[NL802154_ATTR_SEC_FRAME_COUNTER]) {
1787a26c5fd7SAlexander Aring 		params.frame_counter = nla_get_be32(info->attrs[NL802154_ATTR_SEC_FRAME_COUNTER]);
1788a26c5fd7SAlexander Aring 		changed |= IEEE802154_LLSEC_PARAM_FRAME_COUNTER;
1789a26c5fd7SAlexander Aring 	}
1790a26c5fd7SAlexander Aring 
1791a26c5fd7SAlexander Aring 	return rdev_set_llsec_params(rdev, wpan_dev, &params, changed);
1792a26c5fd7SAlexander Aring }
1793a26c5fd7SAlexander Aring 
1794a26c5fd7SAlexander Aring static int nl802154_send_key(struct sk_buff *msg, u32 cmd, u32 portid,
1795a26c5fd7SAlexander Aring 			     u32 seq, int flags,
1796a26c5fd7SAlexander Aring 			     struct cfg802154_registered_device *rdev,
1797a26c5fd7SAlexander Aring 			     struct net_device *dev,
1798a26c5fd7SAlexander Aring 			     const struct ieee802154_llsec_key_entry *key)
1799a26c5fd7SAlexander Aring {
1800a26c5fd7SAlexander Aring 	void *hdr;
1801a26c5fd7SAlexander Aring 	u32 commands[NL802154_CMD_FRAME_NR_IDS / 32];
1802a26c5fd7SAlexander Aring 	struct nlattr *nl_key, *nl_key_id;
1803a26c5fd7SAlexander Aring 
1804a26c5fd7SAlexander Aring 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
1805a26c5fd7SAlexander Aring 	if (!hdr)
180679c37ca7SMiquel Raynal 		return -ENOBUFS;
1807a26c5fd7SAlexander Aring 
1808a26c5fd7SAlexander Aring 	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
1809a26c5fd7SAlexander Aring 		goto nla_put_failure;
1810a26c5fd7SAlexander Aring 
1811ae0be8deSMichal Kubecek 	nl_key = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_KEY);
1812a26c5fd7SAlexander Aring 	if (!nl_key)
1813a26c5fd7SAlexander Aring 		goto nla_put_failure;
1814a26c5fd7SAlexander Aring 
1815ae0be8deSMichal Kubecek 	nl_key_id = nla_nest_start_noflag(msg, NL802154_KEY_ATTR_ID);
1816a26c5fd7SAlexander Aring 	if (!nl_key_id)
1817a26c5fd7SAlexander Aring 		goto nla_put_failure;
1818a26c5fd7SAlexander Aring 
1819a26c5fd7SAlexander Aring 	if (ieee802154_llsec_send_key_id(msg, &key->id) < 0)
1820a26c5fd7SAlexander Aring 		goto nla_put_failure;
1821a26c5fd7SAlexander Aring 
1822a26c5fd7SAlexander Aring 	nla_nest_end(msg, nl_key_id);
1823a26c5fd7SAlexander Aring 
1824a26c5fd7SAlexander Aring 	if (nla_put_u8(msg, NL802154_KEY_ATTR_USAGE_FRAMES,
1825a26c5fd7SAlexander Aring 		       key->key->frame_types))
1826a26c5fd7SAlexander Aring 		goto nla_put_failure;
1827a26c5fd7SAlexander Aring 
1828a26c5fd7SAlexander Aring 	if (key->key->frame_types & BIT(NL802154_FRAME_CMD)) {
1829a26c5fd7SAlexander Aring 		/* TODO for each nested */
1830a26c5fd7SAlexander Aring 		memset(commands, 0, sizeof(commands));
1831a26c5fd7SAlexander Aring 		commands[7] = key->key->cmd_frame_ids;
1832a26c5fd7SAlexander Aring 		if (nla_put(msg, NL802154_KEY_ATTR_USAGE_CMDS,
1833a26c5fd7SAlexander Aring 			    sizeof(commands), commands))
1834a26c5fd7SAlexander Aring 			goto nla_put_failure;
1835a26c5fd7SAlexander Aring 	}
1836a26c5fd7SAlexander Aring 
1837a26c5fd7SAlexander Aring 	if (nla_put(msg, NL802154_KEY_ATTR_BYTES, NL802154_KEY_SIZE,
1838a26c5fd7SAlexander Aring 		    key->key->key))
1839a26c5fd7SAlexander Aring 		goto nla_put_failure;
1840a26c5fd7SAlexander Aring 
1841a26c5fd7SAlexander Aring 	nla_nest_end(msg, nl_key);
1842a26c5fd7SAlexander Aring 	genlmsg_end(msg, hdr);
1843a26c5fd7SAlexander Aring 
1844a26c5fd7SAlexander Aring 	return 0;
1845a26c5fd7SAlexander Aring 
1846a26c5fd7SAlexander Aring nla_put_failure:
1847a26c5fd7SAlexander Aring 	genlmsg_cancel(msg, hdr);
1848a26c5fd7SAlexander Aring 	return -EMSGSIZE;
1849a26c5fd7SAlexander Aring }
1850a26c5fd7SAlexander Aring 
1851a26c5fd7SAlexander Aring static int
1852a26c5fd7SAlexander Aring nl802154_dump_llsec_key(struct sk_buff *skb, struct netlink_callback *cb)
1853a26c5fd7SAlexander Aring {
1854a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = NULL;
1855a26c5fd7SAlexander Aring 	struct ieee802154_llsec_key_entry *key;
1856a26c5fd7SAlexander Aring 	struct ieee802154_llsec_table *table;
1857a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev;
1858a26c5fd7SAlexander Aring 	int err;
1859a26c5fd7SAlexander Aring 
1860a26c5fd7SAlexander Aring 	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
1861a26c5fd7SAlexander Aring 	if (err)
1862a26c5fd7SAlexander Aring 		return err;
1863a26c5fd7SAlexander Aring 
1864fb3c5cdfSAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) {
1865fb3c5cdfSAlexander Aring 		err = skb->len;
1866fb3c5cdfSAlexander Aring 		goto out_err;
1867fb3c5cdfSAlexander Aring 	}
1868fb3c5cdfSAlexander Aring 
1869a26c5fd7SAlexander Aring 	if (!wpan_dev->netdev) {
1870a26c5fd7SAlexander Aring 		err = -EINVAL;
1871a26c5fd7SAlexander Aring 		goto out_err;
1872a26c5fd7SAlexander Aring 	}
1873a26c5fd7SAlexander Aring 
1874a26c5fd7SAlexander Aring 	rdev_lock_llsec_table(rdev, wpan_dev);
1875a26c5fd7SAlexander Aring 	rdev_get_llsec_table(rdev, wpan_dev, &table);
1876a26c5fd7SAlexander Aring 
1877a26c5fd7SAlexander Aring 	/* TODO make it like station dump */
1878a26c5fd7SAlexander Aring 	if (cb->args[2])
1879a26c5fd7SAlexander Aring 		goto out;
1880a26c5fd7SAlexander Aring 
1881a26c5fd7SAlexander Aring 	list_for_each_entry(key, &table->keys, list) {
1882a26c5fd7SAlexander Aring 		if (nl802154_send_key(skb, NL802154_CMD_NEW_SEC_KEY,
1883a26c5fd7SAlexander Aring 				      NETLINK_CB(cb->skb).portid,
1884a26c5fd7SAlexander Aring 				      cb->nlh->nlmsg_seq, NLM_F_MULTI,
1885a26c5fd7SAlexander Aring 				      rdev, wpan_dev->netdev, key) < 0) {
1886a26c5fd7SAlexander Aring 			/* TODO */
1887a26c5fd7SAlexander Aring 			err = -EIO;
1888a26c5fd7SAlexander Aring 			rdev_unlock_llsec_table(rdev, wpan_dev);
1889a26c5fd7SAlexander Aring 			goto out_err;
1890a26c5fd7SAlexander Aring 		}
1891a26c5fd7SAlexander Aring 	}
1892a26c5fd7SAlexander Aring 
1893a26c5fd7SAlexander Aring 	cb->args[2] = 1;
1894a26c5fd7SAlexander Aring 
1895a26c5fd7SAlexander Aring out:
1896a26c5fd7SAlexander Aring 	rdev_unlock_llsec_table(rdev, wpan_dev);
1897a26c5fd7SAlexander Aring 	err = skb->len;
1898a26c5fd7SAlexander Aring out_err:
1899a26c5fd7SAlexander Aring 	nl802154_finish_wpan_dev_dump(rdev);
1900a26c5fd7SAlexander Aring 
1901a26c5fd7SAlexander Aring 	return err;
1902a26c5fd7SAlexander Aring }
1903a26c5fd7SAlexander Aring 
1904a26c5fd7SAlexander Aring static const struct nla_policy nl802154_key_policy[NL802154_KEY_ATTR_MAX + 1] = {
1905a26c5fd7SAlexander Aring 	[NL802154_KEY_ATTR_ID] = { NLA_NESTED },
1906a26c5fd7SAlexander Aring 	/* TODO handle it as for_each_nested and NLA_FLAG? */
1907a26c5fd7SAlexander Aring 	[NL802154_KEY_ATTR_USAGE_FRAMES] = { NLA_U8 },
1908a26c5fd7SAlexander Aring 	/* TODO handle it as for_each_nested, not static array? */
1909a26c5fd7SAlexander Aring 	[NL802154_KEY_ATTR_USAGE_CMDS] = { .len = NL802154_CMD_FRAME_NR_IDS / 8 },
1910a26c5fd7SAlexander Aring 	[NL802154_KEY_ATTR_BYTES] = { .len = NL802154_KEY_SIZE },
1911a26c5fd7SAlexander Aring };
1912a26c5fd7SAlexander Aring 
1913a26c5fd7SAlexander Aring static int nl802154_add_llsec_key(struct sk_buff *skb, struct genl_info *info)
1914a26c5fd7SAlexander Aring {
1915a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1916a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
1917a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1918a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_KEY_ATTR_MAX + 1];
1919a26c5fd7SAlexander Aring 	struct ieee802154_llsec_key key = { };
1920a26c5fd7SAlexander Aring 	struct ieee802154_llsec_key_id id = { };
1921a26c5fd7SAlexander Aring 	u32 commands[NL802154_CMD_FRAME_NR_IDS / 32] = { };
1922a26c5fd7SAlexander Aring 
192308470c54SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
192408470c54SAlexander Aring 		return -EOPNOTSUPP;
192508470c54SAlexander Aring 
192620d5fe2dSAlexander Aring 	if (!info->attrs[NL802154_ATTR_SEC_KEY] ||
192720d5fe2dSAlexander Aring 	    nla_parse_nested_deprecated(attrs, NL802154_KEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_KEY], nl802154_key_policy, info->extack))
1928a26c5fd7SAlexander Aring 		return -EINVAL;
1929a26c5fd7SAlexander Aring 
1930a26c5fd7SAlexander Aring 	if (!attrs[NL802154_KEY_ATTR_USAGE_FRAMES] ||
1931a26c5fd7SAlexander Aring 	    !attrs[NL802154_KEY_ATTR_BYTES])
1932aa655562SDan Carpenter 		return -EINVAL;
1933a26c5fd7SAlexander Aring 
1934a26c5fd7SAlexander Aring 	if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0)
1935a26c5fd7SAlexander Aring 		return -ENOBUFS;
1936a26c5fd7SAlexander Aring 
1937a26c5fd7SAlexander Aring 	key.frame_types = nla_get_u8(attrs[NL802154_KEY_ATTR_USAGE_FRAMES]);
1938a26c5fd7SAlexander Aring 	if (key.frame_types > BIT(NL802154_FRAME_MAX) ||
1939a26c5fd7SAlexander Aring 	    ((key.frame_types & BIT(NL802154_FRAME_CMD)) &&
1940a26c5fd7SAlexander Aring 	     !attrs[NL802154_KEY_ATTR_USAGE_CMDS]))
1941a26c5fd7SAlexander Aring 		return -EINVAL;
1942a26c5fd7SAlexander Aring 
1943a26c5fd7SAlexander Aring 	if (attrs[NL802154_KEY_ATTR_USAGE_CMDS]) {
1944a26c5fd7SAlexander Aring 		/* TODO for each nested */
1945a26c5fd7SAlexander Aring 		nla_memcpy(commands, attrs[NL802154_KEY_ATTR_USAGE_CMDS],
1946a26c5fd7SAlexander Aring 			   NL802154_CMD_FRAME_NR_IDS / 8);
1947a26c5fd7SAlexander Aring 
1948a26c5fd7SAlexander Aring 		/* TODO understand the -EINVAL logic here? last condition */
1949a26c5fd7SAlexander Aring 		if (commands[0] || commands[1] || commands[2] || commands[3] ||
1950a26c5fd7SAlexander Aring 		    commands[4] || commands[5] || commands[6] ||
1951a26c5fd7SAlexander Aring 		    commands[7] > BIT(NL802154_CMD_FRAME_MAX))
1952a26c5fd7SAlexander Aring 			return -EINVAL;
1953a26c5fd7SAlexander Aring 
1954a26c5fd7SAlexander Aring 		key.cmd_frame_ids = commands[7];
1955a26c5fd7SAlexander Aring 	} else {
1956a26c5fd7SAlexander Aring 		key.cmd_frame_ids = 0;
1957a26c5fd7SAlexander Aring 	}
1958a26c5fd7SAlexander Aring 
1959a26c5fd7SAlexander Aring 	nla_memcpy(key.key, attrs[NL802154_KEY_ATTR_BYTES], NL802154_KEY_SIZE);
1960a26c5fd7SAlexander Aring 
1961a26c5fd7SAlexander Aring 	if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0)
1962a26c5fd7SAlexander Aring 		return -ENOBUFS;
1963a26c5fd7SAlexander Aring 
1964a26c5fd7SAlexander Aring 	return rdev_add_llsec_key(rdev, wpan_dev, &id, &key);
1965a26c5fd7SAlexander Aring }
1966a26c5fd7SAlexander Aring 
1967a26c5fd7SAlexander Aring static int nl802154_del_llsec_key(struct sk_buff *skb, struct genl_info *info)
1968a26c5fd7SAlexander Aring {
1969a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1970a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
1971a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1972a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_KEY_ATTR_MAX + 1];
1973a26c5fd7SAlexander Aring 	struct ieee802154_llsec_key_id id;
1974a26c5fd7SAlexander Aring 
1975b6e29495SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
1976b6e29495SAlexander Aring 		return -EOPNOTSUPP;
1977b6e29495SAlexander Aring 
197837feaaf5SAlexander Aring 	if (!info->attrs[NL802154_ATTR_SEC_KEY] ||
197937feaaf5SAlexander Aring 	    nla_parse_nested_deprecated(attrs, NL802154_KEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_KEY], nl802154_key_policy, info->extack))
1980a26c5fd7SAlexander Aring 		return -EINVAL;
1981a26c5fd7SAlexander Aring 
1982a26c5fd7SAlexander Aring 	if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0)
1983a26c5fd7SAlexander Aring 		return -ENOBUFS;
1984a26c5fd7SAlexander Aring 
1985a26c5fd7SAlexander Aring 	return rdev_del_llsec_key(rdev, wpan_dev, &id);
1986a26c5fd7SAlexander Aring }
1987a26c5fd7SAlexander Aring 
1988a26c5fd7SAlexander Aring static int nl802154_send_device(struct sk_buff *msg, u32 cmd, u32 portid,
1989a26c5fd7SAlexander Aring 				u32 seq, int flags,
1990a26c5fd7SAlexander Aring 				struct cfg802154_registered_device *rdev,
1991a26c5fd7SAlexander Aring 				struct net_device *dev,
1992a26c5fd7SAlexander Aring 				const struct ieee802154_llsec_device *dev_desc)
1993a26c5fd7SAlexander Aring {
1994a26c5fd7SAlexander Aring 	void *hdr;
1995a26c5fd7SAlexander Aring 	struct nlattr *nl_device;
1996a26c5fd7SAlexander Aring 
1997a26c5fd7SAlexander Aring 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
1998a26c5fd7SAlexander Aring 	if (!hdr)
199979c37ca7SMiquel Raynal 		return -ENOBUFS;
2000a26c5fd7SAlexander Aring 
2001a26c5fd7SAlexander Aring 	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
2002a26c5fd7SAlexander Aring 		goto nla_put_failure;
2003a26c5fd7SAlexander Aring 
2004ae0be8deSMichal Kubecek 	nl_device = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_DEVICE);
2005a26c5fd7SAlexander Aring 	if (!nl_device)
2006a26c5fd7SAlexander Aring 		goto nla_put_failure;
2007a26c5fd7SAlexander Aring 
2008a26c5fd7SAlexander Aring 	if (nla_put_u32(msg, NL802154_DEV_ATTR_FRAME_COUNTER,
2009a26c5fd7SAlexander Aring 			dev_desc->frame_counter) ||
2010a26c5fd7SAlexander Aring 	    nla_put_le16(msg, NL802154_DEV_ATTR_PAN_ID, dev_desc->pan_id) ||
2011a26c5fd7SAlexander Aring 	    nla_put_le16(msg, NL802154_DEV_ATTR_SHORT_ADDR,
2012a26c5fd7SAlexander Aring 			 dev_desc->short_addr) ||
2013a26c5fd7SAlexander Aring 	    nla_put_le64(msg, NL802154_DEV_ATTR_EXTENDED_ADDR,
2014e7479122SNicolas Dichtel 			 dev_desc->hwaddr, NL802154_DEV_ATTR_PAD) ||
2015a26c5fd7SAlexander Aring 	    nla_put_u8(msg, NL802154_DEV_ATTR_SECLEVEL_EXEMPT,
2016a26c5fd7SAlexander Aring 		       dev_desc->seclevel_exempt) ||
2017a26c5fd7SAlexander Aring 	    nla_put_u32(msg, NL802154_DEV_ATTR_KEY_MODE, dev_desc->key_mode))
2018a26c5fd7SAlexander Aring 		goto nla_put_failure;
2019a26c5fd7SAlexander Aring 
2020a26c5fd7SAlexander Aring 	nla_nest_end(msg, nl_device);
2021a26c5fd7SAlexander Aring 	genlmsg_end(msg, hdr);
2022a26c5fd7SAlexander Aring 
2023a26c5fd7SAlexander Aring 	return 0;
2024a26c5fd7SAlexander Aring 
2025a26c5fd7SAlexander Aring nla_put_failure:
2026a26c5fd7SAlexander Aring 	genlmsg_cancel(msg, hdr);
2027a26c5fd7SAlexander Aring 	return -EMSGSIZE;
2028a26c5fd7SAlexander Aring }
2029a26c5fd7SAlexander Aring 
2030a26c5fd7SAlexander Aring static int
2031a26c5fd7SAlexander Aring nl802154_dump_llsec_dev(struct sk_buff *skb, struct netlink_callback *cb)
2032a26c5fd7SAlexander Aring {
2033a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = NULL;
2034a26c5fd7SAlexander Aring 	struct ieee802154_llsec_device *dev;
2035a26c5fd7SAlexander Aring 	struct ieee802154_llsec_table *table;
2036a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev;
2037a26c5fd7SAlexander Aring 	int err;
2038a26c5fd7SAlexander Aring 
2039a26c5fd7SAlexander Aring 	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
2040a26c5fd7SAlexander Aring 	if (err)
2041a26c5fd7SAlexander Aring 		return err;
2042a26c5fd7SAlexander Aring 
20435582d641SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) {
20445582d641SAlexander Aring 		err = skb->len;
20455582d641SAlexander Aring 		goto out_err;
20465582d641SAlexander Aring 	}
20475582d641SAlexander Aring 
2048a26c5fd7SAlexander Aring 	if (!wpan_dev->netdev) {
2049a26c5fd7SAlexander Aring 		err = -EINVAL;
2050a26c5fd7SAlexander Aring 		goto out_err;
2051a26c5fd7SAlexander Aring 	}
2052a26c5fd7SAlexander Aring 
2053a26c5fd7SAlexander Aring 	rdev_lock_llsec_table(rdev, wpan_dev);
2054a26c5fd7SAlexander Aring 	rdev_get_llsec_table(rdev, wpan_dev, &table);
2055a26c5fd7SAlexander Aring 
2056a26c5fd7SAlexander Aring 	/* TODO make it like station dump */
2057a26c5fd7SAlexander Aring 	if (cb->args[2])
2058a26c5fd7SAlexander Aring 		goto out;
2059a26c5fd7SAlexander Aring 
2060a26c5fd7SAlexander Aring 	list_for_each_entry(dev, &table->devices, list) {
2061a26c5fd7SAlexander Aring 		if (nl802154_send_device(skb, NL802154_CMD_NEW_SEC_LEVEL,
2062a26c5fd7SAlexander Aring 					 NETLINK_CB(cb->skb).portid,
2063a26c5fd7SAlexander Aring 					 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2064a26c5fd7SAlexander Aring 					 rdev, wpan_dev->netdev, dev) < 0) {
2065a26c5fd7SAlexander Aring 			/* TODO */
2066a26c5fd7SAlexander Aring 			err = -EIO;
2067a26c5fd7SAlexander Aring 			rdev_unlock_llsec_table(rdev, wpan_dev);
2068a26c5fd7SAlexander Aring 			goto out_err;
2069a26c5fd7SAlexander Aring 		}
2070a26c5fd7SAlexander Aring 	}
2071a26c5fd7SAlexander Aring 
2072a26c5fd7SAlexander Aring 	cb->args[2] = 1;
2073a26c5fd7SAlexander Aring 
2074a26c5fd7SAlexander Aring out:
2075a26c5fd7SAlexander Aring 	rdev_unlock_llsec_table(rdev, wpan_dev);
2076a26c5fd7SAlexander Aring 	err = skb->len;
2077a26c5fd7SAlexander Aring out_err:
2078a26c5fd7SAlexander Aring 	nl802154_finish_wpan_dev_dump(rdev);
2079a26c5fd7SAlexander Aring 
2080a26c5fd7SAlexander Aring 	return err;
2081a26c5fd7SAlexander Aring }
2082a26c5fd7SAlexander Aring 
2083a26c5fd7SAlexander Aring static const struct nla_policy nl802154_dev_policy[NL802154_DEV_ATTR_MAX + 1] = {
2084a26c5fd7SAlexander Aring 	[NL802154_DEV_ATTR_FRAME_COUNTER] = { NLA_U32 },
2085a26c5fd7SAlexander Aring 	[NL802154_DEV_ATTR_PAN_ID] = { .type = NLA_U16 },
2086a26c5fd7SAlexander Aring 	[NL802154_DEV_ATTR_SHORT_ADDR] = { .type = NLA_U16 },
2087a26c5fd7SAlexander Aring 	[NL802154_DEV_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
2088a26c5fd7SAlexander Aring 	[NL802154_DEV_ATTR_SECLEVEL_EXEMPT] = { NLA_U8 },
2089a26c5fd7SAlexander Aring 	[NL802154_DEV_ATTR_KEY_MODE] = { NLA_U32 },
2090a26c5fd7SAlexander Aring };
2091a26c5fd7SAlexander Aring 
2092a26c5fd7SAlexander Aring static int
2093a26c5fd7SAlexander Aring ieee802154_llsec_parse_device(struct nlattr *nla,
2094a26c5fd7SAlexander Aring 			      struct ieee802154_llsec_device *dev)
2095a26c5fd7SAlexander Aring {
2096a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_DEV_ATTR_MAX + 1];
2097a26c5fd7SAlexander Aring 
20988cb08174SJohannes Berg 	if (!nla || nla_parse_nested_deprecated(attrs, NL802154_DEV_ATTR_MAX, nla, nl802154_dev_policy, NULL))
2099a26c5fd7SAlexander Aring 		return -EINVAL;
2100a26c5fd7SAlexander Aring 
2101a26c5fd7SAlexander Aring 	memset(dev, 0, sizeof(*dev));
2102a26c5fd7SAlexander Aring 
2103a26c5fd7SAlexander Aring 	if (!attrs[NL802154_DEV_ATTR_FRAME_COUNTER] ||
2104a26c5fd7SAlexander Aring 	    !attrs[NL802154_DEV_ATTR_PAN_ID] ||
2105a26c5fd7SAlexander Aring 	    !attrs[NL802154_DEV_ATTR_SHORT_ADDR] ||
2106a26c5fd7SAlexander Aring 	    !attrs[NL802154_DEV_ATTR_EXTENDED_ADDR] ||
2107a26c5fd7SAlexander Aring 	    !attrs[NL802154_DEV_ATTR_SECLEVEL_EXEMPT] ||
2108a26c5fd7SAlexander Aring 	    !attrs[NL802154_DEV_ATTR_KEY_MODE])
2109a26c5fd7SAlexander Aring 		return -EINVAL;
2110a26c5fd7SAlexander Aring 
2111a26c5fd7SAlexander Aring 	/* TODO be32 */
2112a26c5fd7SAlexander Aring 	dev->frame_counter = nla_get_u32(attrs[NL802154_DEV_ATTR_FRAME_COUNTER]);
2113a26c5fd7SAlexander Aring 	dev->pan_id = nla_get_le16(attrs[NL802154_DEV_ATTR_PAN_ID]);
2114a26c5fd7SAlexander Aring 	dev->short_addr = nla_get_le16(attrs[NL802154_DEV_ATTR_SHORT_ADDR]);
2115a26c5fd7SAlexander Aring 	/* TODO rename hwaddr to extended_addr */
2116a26c5fd7SAlexander Aring 	dev->hwaddr = nla_get_le64(attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]);
2117a26c5fd7SAlexander Aring 	dev->seclevel_exempt = nla_get_u8(attrs[NL802154_DEV_ATTR_SECLEVEL_EXEMPT]);
2118a26c5fd7SAlexander Aring 	dev->key_mode = nla_get_u32(attrs[NL802154_DEV_ATTR_KEY_MODE]);
2119a26c5fd7SAlexander Aring 
2120a26c5fd7SAlexander Aring 	if (dev->key_mode > NL802154_DEVKEY_MAX ||
2121a26c5fd7SAlexander Aring 	    (dev->seclevel_exempt != 0 && dev->seclevel_exempt != 1))
2122a26c5fd7SAlexander Aring 		return -EINVAL;
2123a26c5fd7SAlexander Aring 
2124a26c5fd7SAlexander Aring 	return 0;
2125a26c5fd7SAlexander Aring }
2126a26c5fd7SAlexander Aring 
2127a26c5fd7SAlexander Aring static int nl802154_add_llsec_dev(struct sk_buff *skb, struct genl_info *info)
2128a26c5fd7SAlexander Aring {
2129a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
2130a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
2131a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
2132a26c5fd7SAlexander Aring 	struct ieee802154_llsec_device dev_desc;
2133a26c5fd7SAlexander Aring 
21345303f956SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
21355303f956SAlexander Aring 		return -EOPNOTSUPP;
21365303f956SAlexander Aring 
2137a26c5fd7SAlexander Aring 	if (ieee802154_llsec_parse_device(info->attrs[NL802154_ATTR_SEC_DEVICE],
2138a26c5fd7SAlexander Aring 					  &dev_desc) < 0)
2139a26c5fd7SAlexander Aring 		return -EINVAL;
2140a26c5fd7SAlexander Aring 
2141a26c5fd7SAlexander Aring 	return rdev_add_device(rdev, wpan_dev, &dev_desc);
2142a26c5fd7SAlexander Aring }
2143a26c5fd7SAlexander Aring 
2144a26c5fd7SAlexander Aring static int nl802154_del_llsec_dev(struct sk_buff *skb, struct genl_info *info)
2145a26c5fd7SAlexander Aring {
2146a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
2147a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
2148a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
2149a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_DEV_ATTR_MAX + 1];
2150a26c5fd7SAlexander Aring 	__le64 extended_addr;
2151a26c5fd7SAlexander Aring 
2152ad8f9de1SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
2153ad8f9de1SAlexander Aring 		return -EOPNOTSUPP;
2154ad8f9de1SAlexander Aring 
21553d1eac2fSAlexander Aring 	if (!info->attrs[NL802154_ATTR_SEC_DEVICE] ||
21563d1eac2fSAlexander Aring 	    nla_parse_nested_deprecated(attrs, NL802154_DEV_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVICE], nl802154_dev_policy, info->extack))
2157a26c5fd7SAlexander Aring 		return -EINVAL;
2158a26c5fd7SAlexander Aring 
2159a26c5fd7SAlexander Aring 	if (!attrs[NL802154_DEV_ATTR_EXTENDED_ADDR])
2160a26c5fd7SAlexander Aring 		return -EINVAL;
2161a26c5fd7SAlexander Aring 
2162a26c5fd7SAlexander Aring 	extended_addr = nla_get_le64(attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]);
2163a26c5fd7SAlexander Aring 	return rdev_del_device(rdev, wpan_dev, extended_addr);
2164a26c5fd7SAlexander Aring }
2165a26c5fd7SAlexander Aring 
2166a26c5fd7SAlexander Aring static int nl802154_send_devkey(struct sk_buff *msg, u32 cmd, u32 portid,
2167a26c5fd7SAlexander Aring 				u32 seq, int flags,
2168a26c5fd7SAlexander Aring 				struct cfg802154_registered_device *rdev,
2169a26c5fd7SAlexander Aring 				struct net_device *dev, __le64 extended_addr,
2170a26c5fd7SAlexander Aring 				const struct ieee802154_llsec_device_key *devkey)
2171a26c5fd7SAlexander Aring {
2172a26c5fd7SAlexander Aring 	void *hdr;
2173a26c5fd7SAlexander Aring 	struct nlattr *nl_devkey, *nl_key_id;
2174a26c5fd7SAlexander Aring 
2175a26c5fd7SAlexander Aring 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
2176a26c5fd7SAlexander Aring 	if (!hdr)
217779c37ca7SMiquel Raynal 		return -ENOBUFS;
2178a26c5fd7SAlexander Aring 
2179a26c5fd7SAlexander Aring 	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
2180a26c5fd7SAlexander Aring 		goto nla_put_failure;
2181a26c5fd7SAlexander Aring 
2182ae0be8deSMichal Kubecek 	nl_devkey = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_DEVKEY);
2183a26c5fd7SAlexander Aring 	if (!nl_devkey)
2184a26c5fd7SAlexander Aring 		goto nla_put_failure;
2185a26c5fd7SAlexander Aring 
2186a26c5fd7SAlexander Aring 	if (nla_put_le64(msg, NL802154_DEVKEY_ATTR_EXTENDED_ADDR,
2187e7479122SNicolas Dichtel 			 extended_addr, NL802154_DEVKEY_ATTR_PAD) ||
2188a26c5fd7SAlexander Aring 	    nla_put_u32(msg, NL802154_DEVKEY_ATTR_FRAME_COUNTER,
2189a26c5fd7SAlexander Aring 			devkey->frame_counter))
2190a26c5fd7SAlexander Aring 		goto nla_put_failure;
2191a26c5fd7SAlexander Aring 
2192ae0be8deSMichal Kubecek 	nl_key_id = nla_nest_start_noflag(msg, NL802154_DEVKEY_ATTR_ID);
2193a26c5fd7SAlexander Aring 	if (!nl_key_id)
2194a26c5fd7SAlexander Aring 		goto nla_put_failure;
2195a26c5fd7SAlexander Aring 
2196a26c5fd7SAlexander Aring 	if (ieee802154_llsec_send_key_id(msg, &devkey->key_id) < 0)
2197a26c5fd7SAlexander Aring 		goto nla_put_failure;
2198a26c5fd7SAlexander Aring 
2199a26c5fd7SAlexander Aring 	nla_nest_end(msg, nl_key_id);
2200a26c5fd7SAlexander Aring 	nla_nest_end(msg, nl_devkey);
2201a26c5fd7SAlexander Aring 	genlmsg_end(msg, hdr);
2202a26c5fd7SAlexander Aring 
2203a26c5fd7SAlexander Aring 	return 0;
2204a26c5fd7SAlexander Aring 
2205a26c5fd7SAlexander Aring nla_put_failure:
2206a26c5fd7SAlexander Aring 	genlmsg_cancel(msg, hdr);
2207a26c5fd7SAlexander Aring 	return -EMSGSIZE;
2208a26c5fd7SAlexander Aring }
2209a26c5fd7SAlexander Aring 
2210a26c5fd7SAlexander Aring static int
2211a26c5fd7SAlexander Aring nl802154_dump_llsec_devkey(struct sk_buff *skb, struct netlink_callback *cb)
2212a26c5fd7SAlexander Aring {
2213a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = NULL;
2214a26c5fd7SAlexander Aring 	struct ieee802154_llsec_device_key *kpos;
2215a26c5fd7SAlexander Aring 	struct ieee802154_llsec_device *dpos;
2216a26c5fd7SAlexander Aring 	struct ieee802154_llsec_table *table;
2217a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev;
2218a26c5fd7SAlexander Aring 	int err;
2219a26c5fd7SAlexander Aring 
2220a26c5fd7SAlexander Aring 	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
2221a26c5fd7SAlexander Aring 	if (err)
2222a26c5fd7SAlexander Aring 		return err;
2223a26c5fd7SAlexander Aring 
2224080d1a57SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) {
2225080d1a57SAlexander Aring 		err = skb->len;
2226080d1a57SAlexander Aring 		goto out_err;
2227080d1a57SAlexander Aring 	}
2228080d1a57SAlexander Aring 
2229a26c5fd7SAlexander Aring 	if (!wpan_dev->netdev) {
2230a26c5fd7SAlexander Aring 		err = -EINVAL;
2231a26c5fd7SAlexander Aring 		goto out_err;
2232a26c5fd7SAlexander Aring 	}
2233a26c5fd7SAlexander Aring 
2234a26c5fd7SAlexander Aring 	rdev_lock_llsec_table(rdev, wpan_dev);
2235a26c5fd7SAlexander Aring 	rdev_get_llsec_table(rdev, wpan_dev, &table);
2236a26c5fd7SAlexander Aring 
2237a26c5fd7SAlexander Aring 	/* TODO make it like station dump */
2238a26c5fd7SAlexander Aring 	if (cb->args[2])
2239a26c5fd7SAlexander Aring 		goto out;
2240a26c5fd7SAlexander Aring 
2241a26c5fd7SAlexander Aring 	/* TODO look if remove devkey and do some nested attribute */
2242a26c5fd7SAlexander Aring 	list_for_each_entry(dpos, &table->devices, list) {
2243a26c5fd7SAlexander Aring 		list_for_each_entry(kpos, &dpos->keys, list) {
2244a26c5fd7SAlexander Aring 			if (nl802154_send_devkey(skb,
2245a26c5fd7SAlexander Aring 						 NL802154_CMD_NEW_SEC_LEVEL,
2246a26c5fd7SAlexander Aring 						 NETLINK_CB(cb->skb).portid,
2247a26c5fd7SAlexander Aring 						 cb->nlh->nlmsg_seq,
2248a26c5fd7SAlexander Aring 						 NLM_F_MULTI, rdev,
2249a26c5fd7SAlexander Aring 						 wpan_dev->netdev,
2250a26c5fd7SAlexander Aring 						 dpos->hwaddr,
2251a26c5fd7SAlexander Aring 						 kpos) < 0) {
2252a26c5fd7SAlexander Aring 				/* TODO */
2253a26c5fd7SAlexander Aring 				err = -EIO;
2254a26c5fd7SAlexander Aring 				rdev_unlock_llsec_table(rdev, wpan_dev);
2255a26c5fd7SAlexander Aring 				goto out_err;
2256a26c5fd7SAlexander Aring 			}
2257a26c5fd7SAlexander Aring 		}
2258a26c5fd7SAlexander Aring 	}
2259a26c5fd7SAlexander Aring 
2260a26c5fd7SAlexander Aring 	cb->args[2] = 1;
2261a26c5fd7SAlexander Aring 
2262a26c5fd7SAlexander Aring out:
2263a26c5fd7SAlexander Aring 	rdev_unlock_llsec_table(rdev, wpan_dev);
2264a26c5fd7SAlexander Aring 	err = skb->len;
2265a26c5fd7SAlexander Aring out_err:
2266a26c5fd7SAlexander Aring 	nl802154_finish_wpan_dev_dump(rdev);
2267a26c5fd7SAlexander Aring 
2268a26c5fd7SAlexander Aring 	return err;
2269a26c5fd7SAlexander Aring }
2270a26c5fd7SAlexander Aring 
2271a26c5fd7SAlexander Aring static const struct nla_policy nl802154_devkey_policy[NL802154_DEVKEY_ATTR_MAX + 1] = {
2272a26c5fd7SAlexander Aring 	[NL802154_DEVKEY_ATTR_FRAME_COUNTER] = { NLA_U32 },
2273a26c5fd7SAlexander Aring 	[NL802154_DEVKEY_ATTR_EXTENDED_ADDR] = { NLA_U64 },
2274a26c5fd7SAlexander Aring 	[NL802154_DEVKEY_ATTR_ID] = { NLA_NESTED },
2275a26c5fd7SAlexander Aring };
2276a26c5fd7SAlexander Aring 
2277a26c5fd7SAlexander Aring static int nl802154_add_llsec_devkey(struct sk_buff *skb, struct genl_info *info)
2278a26c5fd7SAlexander Aring {
2279a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
2280a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
2281a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
2282a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_DEVKEY_ATTR_MAX + 1];
2283a26c5fd7SAlexander Aring 	struct ieee802154_llsec_device_key key;
2284a26c5fd7SAlexander Aring 	__le64 extended_addr;
2285a26c5fd7SAlexander Aring 
2286a347b3b3SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
2287a347b3b3SAlexander Aring 		return -EOPNOTSUPP;
2288a347b3b3SAlexander Aring 
2289a26c5fd7SAlexander Aring 	if (!info->attrs[NL802154_ATTR_SEC_DEVKEY] ||
22908cb08174SJohannes Berg 	    nla_parse_nested_deprecated(attrs, NL802154_DEVKEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVKEY], nl802154_devkey_policy, info->extack) < 0)
2291a26c5fd7SAlexander Aring 		return -EINVAL;
2292a26c5fd7SAlexander Aring 
2293a26c5fd7SAlexander Aring 	if (!attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER] ||
2294a26c5fd7SAlexander Aring 	    !attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR])
2295a26c5fd7SAlexander Aring 		return -EINVAL;
2296a26c5fd7SAlexander Aring 
2297a26c5fd7SAlexander Aring 	/* TODO change key.id ? */
2298a26c5fd7SAlexander Aring 	if (ieee802154_llsec_parse_key_id(attrs[NL802154_DEVKEY_ATTR_ID],
2299a26c5fd7SAlexander Aring 					  &key.key_id) < 0)
2300a26c5fd7SAlexander Aring 		return -ENOBUFS;
2301a26c5fd7SAlexander Aring 
2302a26c5fd7SAlexander Aring 	/* TODO be32 */
2303a26c5fd7SAlexander Aring 	key.frame_counter = nla_get_u32(attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER]);
2304a26c5fd7SAlexander Aring 	/* TODO change naming hwaddr -> extended_addr
2305a26c5fd7SAlexander Aring 	 * check unique identifier short+pan OR extended_addr
2306a26c5fd7SAlexander Aring 	 */
2307a26c5fd7SAlexander Aring 	extended_addr = nla_get_le64(attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]);
2308a26c5fd7SAlexander Aring 	return rdev_add_devkey(rdev, wpan_dev, extended_addr, &key);
2309a26c5fd7SAlexander Aring }
2310a26c5fd7SAlexander Aring 
2311a26c5fd7SAlexander Aring static int nl802154_del_llsec_devkey(struct sk_buff *skb, struct genl_info *info)
2312a26c5fd7SAlexander Aring {
2313a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
2314a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
2315a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
2316a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_DEVKEY_ATTR_MAX + 1];
2317a26c5fd7SAlexander Aring 	struct ieee802154_llsec_device_key key;
2318a26c5fd7SAlexander Aring 	__le64 extended_addr;
2319a26c5fd7SAlexander Aring 
23206fb80453SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
23216fb80453SAlexander Aring 		return -EOPNOTSUPP;
23226fb80453SAlexander Aring 
232327c74686SAlexander Aring 	if (!info->attrs[NL802154_ATTR_SEC_DEVKEY] ||
232427c74686SAlexander Aring 	    nla_parse_nested_deprecated(attrs, NL802154_DEVKEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVKEY], nl802154_devkey_policy, info->extack))
2325a26c5fd7SAlexander Aring 		return -EINVAL;
2326a26c5fd7SAlexander Aring 
2327a26c5fd7SAlexander Aring 	if (!attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR])
2328a26c5fd7SAlexander Aring 		return -EINVAL;
2329a26c5fd7SAlexander Aring 
2330a26c5fd7SAlexander Aring 	/* TODO change key.id ? */
2331a26c5fd7SAlexander Aring 	if (ieee802154_llsec_parse_key_id(attrs[NL802154_DEVKEY_ATTR_ID],
2332a26c5fd7SAlexander Aring 					  &key.key_id) < 0)
2333a26c5fd7SAlexander Aring 		return -ENOBUFS;
2334a26c5fd7SAlexander Aring 
2335a26c5fd7SAlexander Aring 	/* TODO change naming hwaddr -> extended_addr
2336a26c5fd7SAlexander Aring 	 * check unique identifier short+pan OR extended_addr
2337a26c5fd7SAlexander Aring 	 */
2338a26c5fd7SAlexander Aring 	extended_addr = nla_get_le64(attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]);
2339a26c5fd7SAlexander Aring 	return rdev_del_devkey(rdev, wpan_dev, extended_addr, &key);
2340a26c5fd7SAlexander Aring }
2341a26c5fd7SAlexander Aring 
2342a26c5fd7SAlexander Aring static int nl802154_send_seclevel(struct sk_buff *msg, u32 cmd, u32 portid,
2343a26c5fd7SAlexander Aring 				  u32 seq, int flags,
2344a26c5fd7SAlexander Aring 				  struct cfg802154_registered_device *rdev,
2345a26c5fd7SAlexander Aring 				  struct net_device *dev,
2346a26c5fd7SAlexander Aring 				  const struct ieee802154_llsec_seclevel *sl)
2347a26c5fd7SAlexander Aring {
2348a26c5fd7SAlexander Aring 	void *hdr;
2349a26c5fd7SAlexander Aring 	struct nlattr *nl_seclevel;
2350a26c5fd7SAlexander Aring 
2351a26c5fd7SAlexander Aring 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
2352a26c5fd7SAlexander Aring 	if (!hdr)
235379c37ca7SMiquel Raynal 		return -ENOBUFS;
2354a26c5fd7SAlexander Aring 
2355a26c5fd7SAlexander Aring 	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
2356a26c5fd7SAlexander Aring 		goto nla_put_failure;
2357a26c5fd7SAlexander Aring 
2358ae0be8deSMichal Kubecek 	nl_seclevel = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_LEVEL);
2359a26c5fd7SAlexander Aring 	if (!nl_seclevel)
2360a26c5fd7SAlexander Aring 		goto nla_put_failure;
2361a26c5fd7SAlexander Aring 
2362a26c5fd7SAlexander Aring 	if (nla_put_u32(msg, NL802154_SECLEVEL_ATTR_FRAME, sl->frame_type) ||
2363a26c5fd7SAlexander Aring 	    nla_put_u32(msg, NL802154_SECLEVEL_ATTR_LEVELS, sl->sec_levels) ||
2364a26c5fd7SAlexander Aring 	    nla_put_u8(msg, NL802154_SECLEVEL_ATTR_DEV_OVERRIDE,
2365a26c5fd7SAlexander Aring 		       sl->device_override))
2366a26c5fd7SAlexander Aring 		goto nla_put_failure;
2367a26c5fd7SAlexander Aring 
2368a26c5fd7SAlexander Aring 	if (sl->frame_type == NL802154_FRAME_CMD) {
2369a26c5fd7SAlexander Aring 		if (nla_put_u32(msg, NL802154_SECLEVEL_ATTR_CMD_FRAME,
2370a26c5fd7SAlexander Aring 				sl->cmd_frame_id))
2371a26c5fd7SAlexander Aring 			goto nla_put_failure;
2372a26c5fd7SAlexander Aring 	}
2373a26c5fd7SAlexander Aring 
2374a26c5fd7SAlexander Aring 	nla_nest_end(msg, nl_seclevel);
2375a26c5fd7SAlexander Aring 	genlmsg_end(msg, hdr);
2376a26c5fd7SAlexander Aring 
2377a26c5fd7SAlexander Aring 	return 0;
2378a26c5fd7SAlexander Aring 
2379a26c5fd7SAlexander Aring nla_put_failure:
2380a26c5fd7SAlexander Aring 	genlmsg_cancel(msg, hdr);
2381a26c5fd7SAlexander Aring 	return -EMSGSIZE;
2382a26c5fd7SAlexander Aring }
2383a26c5fd7SAlexander Aring 
2384a26c5fd7SAlexander Aring static int
2385a26c5fd7SAlexander Aring nl802154_dump_llsec_seclevel(struct sk_buff *skb, struct netlink_callback *cb)
2386a26c5fd7SAlexander Aring {
2387a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = NULL;
2388a26c5fd7SAlexander Aring 	struct ieee802154_llsec_seclevel *sl;
2389a26c5fd7SAlexander Aring 	struct ieee802154_llsec_table *table;
2390a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev;
2391a26c5fd7SAlexander Aring 	int err;
2392a26c5fd7SAlexander Aring 
2393a26c5fd7SAlexander Aring 	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
2394a26c5fd7SAlexander Aring 	if (err)
2395a26c5fd7SAlexander Aring 		return err;
2396a26c5fd7SAlexander Aring 
23974c9b4f55SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) {
23984c9b4f55SAlexander Aring 		err = skb->len;
23994c9b4f55SAlexander Aring 		goto out_err;
24004c9b4f55SAlexander Aring 	}
24014c9b4f55SAlexander Aring 
2402a26c5fd7SAlexander Aring 	if (!wpan_dev->netdev) {
2403a26c5fd7SAlexander Aring 		err = -EINVAL;
2404a26c5fd7SAlexander Aring 		goto out_err;
2405a26c5fd7SAlexander Aring 	}
2406a26c5fd7SAlexander Aring 
2407a26c5fd7SAlexander Aring 	rdev_lock_llsec_table(rdev, wpan_dev);
2408a26c5fd7SAlexander Aring 	rdev_get_llsec_table(rdev, wpan_dev, &table);
2409a26c5fd7SAlexander Aring 
2410a26c5fd7SAlexander Aring 	/* TODO make it like station dump */
2411a26c5fd7SAlexander Aring 	if (cb->args[2])
2412a26c5fd7SAlexander Aring 		goto out;
2413a26c5fd7SAlexander Aring 
2414a26c5fd7SAlexander Aring 	list_for_each_entry(sl, &table->security_levels, list) {
2415a26c5fd7SAlexander Aring 		if (nl802154_send_seclevel(skb, NL802154_CMD_NEW_SEC_LEVEL,
2416a26c5fd7SAlexander Aring 					   NETLINK_CB(cb->skb).portid,
2417a26c5fd7SAlexander Aring 					   cb->nlh->nlmsg_seq, NLM_F_MULTI,
2418a26c5fd7SAlexander Aring 					   rdev, wpan_dev->netdev, sl) < 0) {
2419a26c5fd7SAlexander Aring 			/* TODO */
2420a26c5fd7SAlexander Aring 			err = -EIO;
2421a26c5fd7SAlexander Aring 			rdev_unlock_llsec_table(rdev, wpan_dev);
2422a26c5fd7SAlexander Aring 			goto out_err;
2423a26c5fd7SAlexander Aring 		}
2424a26c5fd7SAlexander Aring 	}
2425a26c5fd7SAlexander Aring 
2426a26c5fd7SAlexander Aring 	cb->args[2] = 1;
2427a26c5fd7SAlexander Aring 
2428a26c5fd7SAlexander Aring out:
2429a26c5fd7SAlexander Aring 	rdev_unlock_llsec_table(rdev, wpan_dev);
2430a26c5fd7SAlexander Aring 	err = skb->len;
2431a26c5fd7SAlexander Aring out_err:
2432a26c5fd7SAlexander Aring 	nl802154_finish_wpan_dev_dump(rdev);
2433a26c5fd7SAlexander Aring 
2434a26c5fd7SAlexander Aring 	return err;
2435a26c5fd7SAlexander Aring }
2436a26c5fd7SAlexander Aring 
2437a26c5fd7SAlexander Aring static const struct nla_policy nl802154_seclevel_policy[NL802154_SECLEVEL_ATTR_MAX + 1] = {
2438a26c5fd7SAlexander Aring 	[NL802154_SECLEVEL_ATTR_LEVELS] = { .type = NLA_U8 },
2439a26c5fd7SAlexander Aring 	[NL802154_SECLEVEL_ATTR_FRAME] = { .type = NLA_U32 },
2440a26c5fd7SAlexander Aring 	[NL802154_SECLEVEL_ATTR_CMD_FRAME] = { .type = NLA_U32 },
2441a26c5fd7SAlexander Aring 	[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE] = { .type = NLA_U8 },
2442a26c5fd7SAlexander Aring };
2443a26c5fd7SAlexander Aring 
2444a26c5fd7SAlexander Aring static int
2445a26c5fd7SAlexander Aring llsec_parse_seclevel(struct nlattr *nla, struct ieee802154_llsec_seclevel *sl)
2446a26c5fd7SAlexander Aring {
2447a26c5fd7SAlexander Aring 	struct nlattr *attrs[NL802154_SECLEVEL_ATTR_MAX + 1];
2448a26c5fd7SAlexander Aring 
24498cb08174SJohannes Berg 	if (!nla || nla_parse_nested_deprecated(attrs, NL802154_SECLEVEL_ATTR_MAX, nla, nl802154_seclevel_policy, NULL))
2450a26c5fd7SAlexander Aring 		return -EINVAL;
2451a26c5fd7SAlexander Aring 
2452a26c5fd7SAlexander Aring 	memset(sl, 0, sizeof(*sl));
2453a26c5fd7SAlexander Aring 
2454a26c5fd7SAlexander Aring 	if (!attrs[NL802154_SECLEVEL_ATTR_LEVELS] ||
2455a26c5fd7SAlexander Aring 	    !attrs[NL802154_SECLEVEL_ATTR_FRAME] ||
2456a26c5fd7SAlexander Aring 	    !attrs[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE])
2457a26c5fd7SAlexander Aring 		return -EINVAL;
2458a26c5fd7SAlexander Aring 
2459a26c5fd7SAlexander Aring 	sl->sec_levels = nla_get_u8(attrs[NL802154_SECLEVEL_ATTR_LEVELS]);
2460a26c5fd7SAlexander Aring 	sl->frame_type = nla_get_u32(attrs[NL802154_SECLEVEL_ATTR_FRAME]);
2461a26c5fd7SAlexander Aring 	sl->device_override = nla_get_u8(attrs[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE]);
2462a26c5fd7SAlexander Aring 	if (sl->frame_type > NL802154_FRAME_MAX ||
2463a26c5fd7SAlexander Aring 	    (sl->device_override != 0 && sl->device_override != 1))
2464a26c5fd7SAlexander Aring 		return -EINVAL;
2465a26c5fd7SAlexander Aring 
2466a26c5fd7SAlexander Aring 	if (sl->frame_type == NL802154_FRAME_CMD) {
2467a26c5fd7SAlexander Aring 		if (!attrs[NL802154_SECLEVEL_ATTR_CMD_FRAME])
2468a26c5fd7SAlexander Aring 			return -EINVAL;
2469a26c5fd7SAlexander Aring 
2470a26c5fd7SAlexander Aring 		sl->cmd_frame_id = nla_get_u32(attrs[NL802154_SECLEVEL_ATTR_CMD_FRAME]);
2471a26c5fd7SAlexander Aring 		if (sl->cmd_frame_id > NL802154_CMD_FRAME_MAX)
2472a26c5fd7SAlexander Aring 			return -EINVAL;
2473a26c5fd7SAlexander Aring 	}
2474a26c5fd7SAlexander Aring 
2475a26c5fd7SAlexander Aring 	return 0;
2476a26c5fd7SAlexander Aring }
2477a26c5fd7SAlexander Aring 
2478a26c5fd7SAlexander Aring static int nl802154_add_llsec_seclevel(struct sk_buff *skb,
2479a26c5fd7SAlexander Aring 				       struct genl_info *info)
2480a26c5fd7SAlexander Aring {
2481a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
2482a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
2483a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
2484a26c5fd7SAlexander Aring 	struct ieee802154_llsec_seclevel sl;
2485a26c5fd7SAlexander Aring 
24869ec87e32SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
24879ec87e32SAlexander Aring 		return -EOPNOTSUPP;
24889ec87e32SAlexander Aring 
2489a26c5fd7SAlexander Aring 	if (llsec_parse_seclevel(info->attrs[NL802154_ATTR_SEC_LEVEL],
2490a26c5fd7SAlexander Aring 				 &sl) < 0)
2491a26c5fd7SAlexander Aring 		return -EINVAL;
2492a26c5fd7SAlexander Aring 
2493a26c5fd7SAlexander Aring 	return rdev_add_seclevel(rdev, wpan_dev, &sl);
2494a26c5fd7SAlexander Aring }
2495a26c5fd7SAlexander Aring 
2496a26c5fd7SAlexander Aring static int nl802154_del_llsec_seclevel(struct sk_buff *skb,
2497a26c5fd7SAlexander Aring 				       struct genl_info *info)
2498a26c5fd7SAlexander Aring {
2499a26c5fd7SAlexander Aring 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
2500a26c5fd7SAlexander Aring 	struct net_device *dev = info->user_ptr[1];
2501a26c5fd7SAlexander Aring 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
2502a26c5fd7SAlexander Aring 	struct ieee802154_llsec_seclevel sl;
2503a26c5fd7SAlexander Aring 
25049dde1309SAlexander Aring 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
25059dde1309SAlexander Aring 		return -EOPNOTSUPP;
25069dde1309SAlexander Aring 
2507a26c5fd7SAlexander Aring 	if (!info->attrs[NL802154_ATTR_SEC_LEVEL] ||
2508a26c5fd7SAlexander Aring 	    llsec_parse_seclevel(info->attrs[NL802154_ATTR_SEC_LEVEL],
2509a26c5fd7SAlexander Aring 				 &sl) < 0)
2510a26c5fd7SAlexander Aring 		return -EINVAL;
2511a26c5fd7SAlexander Aring 
2512a26c5fd7SAlexander Aring 	return rdev_del_seclevel(rdev, wpan_dev, &sl);
2513a26c5fd7SAlexander Aring }
2514a26c5fd7SAlexander Aring #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
2515a26c5fd7SAlexander Aring 
251679fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_WPAN_PHY	0x01
251779fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_NETDEV	0x02
251879fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_RTNL		0x04
251979fe1a2aSAlexander Aring #define NL802154_FLAG_CHECK_NETDEV_UP	0x08
252079fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_WPAN_DEV	0x10
252179fe1a2aSAlexander Aring 
252220b0b53aSJakub Kicinski static int nl802154_pre_doit(const struct genl_split_ops *ops,
252320b0b53aSJakub Kicinski 			     struct sk_buff *skb,
252479fe1a2aSAlexander Aring 			     struct genl_info *info)
252579fe1a2aSAlexander Aring {
252679fe1a2aSAlexander Aring 	struct cfg802154_registered_device *rdev;
252779fe1a2aSAlexander Aring 	struct wpan_dev *wpan_dev;
252879fe1a2aSAlexander Aring 	struct net_device *dev;
252979fe1a2aSAlexander Aring 	bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
253079fe1a2aSAlexander Aring 
253179fe1a2aSAlexander Aring 	if (rtnl)
253279fe1a2aSAlexander Aring 		rtnl_lock();
253379fe1a2aSAlexander Aring 
253479fe1a2aSAlexander Aring 	if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
253579fe1a2aSAlexander Aring 		rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
253679fe1a2aSAlexander Aring 		if (IS_ERR(rdev)) {
253779fe1a2aSAlexander Aring 			if (rtnl)
253879fe1a2aSAlexander Aring 				rtnl_unlock();
253979fe1a2aSAlexander Aring 			return PTR_ERR(rdev);
254079fe1a2aSAlexander Aring 		}
254179fe1a2aSAlexander Aring 		info->user_ptr[0] = rdev;
254279fe1a2aSAlexander Aring 	} else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
254379fe1a2aSAlexander Aring 		   ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
254479fe1a2aSAlexander Aring 		ASSERT_RTNL();
254579fe1a2aSAlexander Aring 		wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
254679fe1a2aSAlexander Aring 							   info->attrs);
254779fe1a2aSAlexander Aring 		if (IS_ERR(wpan_dev)) {
254879fe1a2aSAlexander Aring 			if (rtnl)
254979fe1a2aSAlexander Aring 				rtnl_unlock();
255079fe1a2aSAlexander Aring 			return PTR_ERR(wpan_dev);
255179fe1a2aSAlexander Aring 		}
255279fe1a2aSAlexander Aring 
255379fe1a2aSAlexander Aring 		dev = wpan_dev->netdev;
255479fe1a2aSAlexander Aring 		rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
255579fe1a2aSAlexander Aring 
255679fe1a2aSAlexander Aring 		if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
255779fe1a2aSAlexander Aring 			if (!dev) {
255879fe1a2aSAlexander Aring 				if (rtnl)
255979fe1a2aSAlexander Aring 					rtnl_unlock();
256079fe1a2aSAlexander Aring 				return -EINVAL;
256179fe1a2aSAlexander Aring 			}
256279fe1a2aSAlexander Aring 
256379fe1a2aSAlexander Aring 			info->user_ptr[1] = dev;
256479fe1a2aSAlexander Aring 		} else {
256579fe1a2aSAlexander Aring 			info->user_ptr[1] = wpan_dev;
256679fe1a2aSAlexander Aring 		}
256779fe1a2aSAlexander Aring 
256879fe1a2aSAlexander Aring 		if (dev) {
256979fe1a2aSAlexander Aring 			if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
257079fe1a2aSAlexander Aring 			    !netif_running(dev)) {
257179fe1a2aSAlexander Aring 				if (rtnl)
257279fe1a2aSAlexander Aring 					rtnl_unlock();
257379fe1a2aSAlexander Aring 				return -ENETDOWN;
257479fe1a2aSAlexander Aring 			}
257579fe1a2aSAlexander Aring 
257679fe1a2aSAlexander Aring 			dev_hold(dev);
257779fe1a2aSAlexander Aring 		}
257879fe1a2aSAlexander Aring 
257979fe1a2aSAlexander Aring 		info->user_ptr[0] = rdev;
258079fe1a2aSAlexander Aring 	}
258179fe1a2aSAlexander Aring 
258279fe1a2aSAlexander Aring 	return 0;
258379fe1a2aSAlexander Aring }
258479fe1a2aSAlexander Aring 
258520b0b53aSJakub Kicinski static void nl802154_post_doit(const struct genl_split_ops *ops,
258620b0b53aSJakub Kicinski 			       struct sk_buff *skb,
258779fe1a2aSAlexander Aring 			       struct genl_info *info)
258879fe1a2aSAlexander Aring {
258979fe1a2aSAlexander Aring 	if (info->user_ptr[1]) {
259079fe1a2aSAlexander Aring 		if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
259179fe1a2aSAlexander Aring 			struct wpan_dev *wpan_dev = info->user_ptr[1];
259279fe1a2aSAlexander Aring 
259379fe1a2aSAlexander Aring 			dev_put(wpan_dev->netdev);
259479fe1a2aSAlexander Aring 		} else {
259579fe1a2aSAlexander Aring 			dev_put(info->user_ptr[1]);
259679fe1a2aSAlexander Aring 		}
259779fe1a2aSAlexander Aring 	}
259879fe1a2aSAlexander Aring 
259979fe1a2aSAlexander Aring 	if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
260079fe1a2aSAlexander Aring 		rtnl_unlock();
260179fe1a2aSAlexander Aring }
260279fe1a2aSAlexander Aring 
260379fe1a2aSAlexander Aring static const struct genl_ops nl802154_ops[] = {
2604ca20ce20SAlexander Aring 	{
2605ca20ce20SAlexander Aring 		.cmd = NL802154_CMD_GET_WPAN_PHY,
260675cdbdd0SJiri Pirko 		.validate = GENL_DONT_VALIDATE_STRICT |
260775cdbdd0SJiri Pirko 			    GENL_DONT_VALIDATE_DUMP_STRICT,
2608ca20ce20SAlexander Aring 		.doit = nl802154_get_wpan_phy,
2609ca20ce20SAlexander Aring 		.dumpit = nl802154_dump_wpan_phy,
2610ca20ce20SAlexander Aring 		.done = nl802154_dump_wpan_phy_done,
2611ca20ce20SAlexander Aring 		/* can be retrieved by unprivileged users */
2612ca20ce20SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2613ca20ce20SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2614ca20ce20SAlexander Aring 	},
26154b96aea0SAlexander Aring 	{
26164b96aea0SAlexander Aring 		.cmd = NL802154_CMD_GET_INTERFACE,
2617ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
26184b96aea0SAlexander Aring 		.doit = nl802154_get_interface,
26194b96aea0SAlexander Aring 		.dumpit = nl802154_dump_interface,
26204b96aea0SAlexander Aring 		/* can be retrieved by unprivileged users */
26214b96aea0SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
26224b96aea0SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
26234b96aea0SAlexander Aring 	},
2624ab0bd561SAlexander Aring 	{
2625f3ea5e44SAlexander Aring 		.cmd = NL802154_CMD_NEW_INTERFACE,
2626ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2627f3ea5e44SAlexander Aring 		.doit = nl802154_new_interface,
2628f3ea5e44SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2629f3ea5e44SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2630f3ea5e44SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2631f3ea5e44SAlexander Aring 	},
2632f3ea5e44SAlexander Aring 	{
2633b821ecd4SAlexander Aring 		.cmd = NL802154_CMD_DEL_INTERFACE,
2634ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2635b821ecd4SAlexander Aring 		.doit = nl802154_del_interface,
2636b821ecd4SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2637b821ecd4SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
2638b821ecd4SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2639b821ecd4SAlexander Aring 	},
2640b821ecd4SAlexander Aring 	{
2641ab0bd561SAlexander Aring 		.cmd = NL802154_CMD_SET_CHANNEL,
2642ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2643ab0bd561SAlexander Aring 		.doit = nl802154_set_channel,
2644ab0bd561SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2645ab0bd561SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2646ab0bd561SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2647ab0bd561SAlexander Aring 	},
2648702bf371SAlexander Aring 	{
2649ba2a9506SAlexander Aring 		.cmd = NL802154_CMD_SET_CCA_MODE,
2650ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2651ba2a9506SAlexander Aring 		.doit = nl802154_set_cca_mode,
2652ba2a9506SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2653ba2a9506SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2654ba2a9506SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2655ba2a9506SAlexander Aring 	},
2656ba2a9506SAlexander Aring 	{
2657b69644c1SAlexander Aring 		.cmd = NL802154_CMD_SET_CCA_ED_LEVEL,
2658ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2659b69644c1SAlexander Aring 		.doit = nl802154_set_cca_ed_level,
2660b69644c1SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2661b69644c1SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2662b69644c1SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2663b69644c1SAlexander Aring 	},
2664b69644c1SAlexander Aring 	{
26650f999b09SVarka Bhadram 		.cmd = NL802154_CMD_SET_TX_POWER,
2666ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
26670f999b09SVarka Bhadram 		.doit = nl802154_set_tx_power,
26680f999b09SVarka Bhadram 		.flags = GENL_ADMIN_PERM,
26690f999b09SVarka Bhadram 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
26700f999b09SVarka Bhadram 				  NL802154_FLAG_NEED_RTNL,
26710f999b09SVarka Bhadram 	},
26720f999b09SVarka Bhadram 	{
267366e5c267SAlexander Aring 		.cmd = NL802154_CMD_SET_WPAN_PHY_NETNS,
2674ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
267566e5c267SAlexander Aring 		.doit = nl802154_wpan_phy_netns,
267666e5c267SAlexander Aring 		.flags = GENL_ADMIN_PERM,
267766e5c267SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
267866e5c267SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
267966e5c267SAlexander Aring 	},
268066e5c267SAlexander Aring 	{
2681702bf371SAlexander Aring 		.cmd = NL802154_CMD_SET_PAN_ID,
2682ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2683702bf371SAlexander Aring 		.doit = nl802154_set_pan_id,
2684702bf371SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2685702bf371SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2686702bf371SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2687702bf371SAlexander Aring 	},
26889830c62aSAlexander Aring 	{
26899830c62aSAlexander Aring 		.cmd = NL802154_CMD_SET_SHORT_ADDR,
2690ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
26919830c62aSAlexander Aring 		.doit = nl802154_set_short_addr,
26929830c62aSAlexander Aring 		.flags = GENL_ADMIN_PERM,
26939830c62aSAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
26949830c62aSAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
26959830c62aSAlexander Aring 	},
2696656a999eSAlexander Aring 	{
2697656a999eSAlexander Aring 		.cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
2698ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2699656a999eSAlexander Aring 		.doit = nl802154_set_backoff_exponent,
2700656a999eSAlexander Aring 		.flags = GENL_ADMIN_PERM,
2701656a999eSAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2702656a999eSAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2703656a999eSAlexander Aring 	},
2704a01ba765SAlexander Aring 	{
2705a01ba765SAlexander Aring 		.cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
2706ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2707a01ba765SAlexander Aring 		.doit = nl802154_set_max_csma_backoffs,
2708a01ba765SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2709a01ba765SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2710a01ba765SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2711a01ba765SAlexander Aring 	},
271217a3a46bSAlexander Aring 	{
271317a3a46bSAlexander Aring 		.cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
2714ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
271517a3a46bSAlexander Aring 		.doit = nl802154_set_max_frame_retries,
271617a3a46bSAlexander Aring 		.flags = GENL_ADMIN_PERM,
271717a3a46bSAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
271817a3a46bSAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
271917a3a46bSAlexander Aring 	},
2720c8937a1dSAlexander Aring 	{
2721c8937a1dSAlexander Aring 		.cmd = NL802154_CMD_SET_LBT_MODE,
2722ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2723c8937a1dSAlexander Aring 		.doit = nl802154_set_lbt_mode,
2724c8937a1dSAlexander Aring 		.flags = GENL_ADMIN_PERM,
2725c8937a1dSAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2726c8937a1dSAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2727c8937a1dSAlexander Aring 	},
2728c91208d8SAlexander Aring 	{
2729c91208d8SAlexander Aring 		.cmd = NL802154_CMD_SET_ACKREQ_DEFAULT,
2730ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2731c91208d8SAlexander Aring 		.doit = nl802154_set_ackreq_default,
2732c91208d8SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2733c91208d8SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2734c91208d8SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2735c91208d8SAlexander Aring 	},
2736ed3557c9SMiquel Raynal 	{
2737ed3557c9SMiquel Raynal 		.cmd = NL802154_CMD_TRIGGER_SCAN,
2738ed3557c9SMiquel Raynal 		.doit = nl802154_trigger_scan,
2739ed3557c9SMiquel Raynal 		.flags = GENL_ADMIN_PERM,
2740ed3557c9SMiquel Raynal 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2741ed3557c9SMiquel Raynal 				  NL802154_FLAG_CHECK_NETDEV_UP |
2742ed3557c9SMiquel Raynal 				  NL802154_FLAG_NEED_RTNL,
2743ed3557c9SMiquel Raynal 	},
2744ed3557c9SMiquel Raynal 	{
2745ed3557c9SMiquel Raynal 		.cmd = NL802154_CMD_ABORT_SCAN,
2746ed3557c9SMiquel Raynal 		.doit = nl802154_abort_scan,
2747ed3557c9SMiquel Raynal 		.flags = GENL_ADMIN_PERM,
2748ed3557c9SMiquel Raynal 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2749ed3557c9SMiquel Raynal 				  NL802154_FLAG_CHECK_NETDEV_UP |
2750ed3557c9SMiquel Raynal 				  NL802154_FLAG_NEED_RTNL,
2751ed3557c9SMiquel Raynal 	},
27529bc11450SMiquel Raynal 	{
27539bc11450SMiquel Raynal 		.cmd = NL802154_CMD_SEND_BEACONS,
27549bc11450SMiquel Raynal 		.doit = nl802154_send_beacons,
27559bc11450SMiquel Raynal 		.flags = GENL_ADMIN_PERM,
27569bc11450SMiquel Raynal 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
27579bc11450SMiquel Raynal 				  NL802154_FLAG_CHECK_NETDEV_UP |
27589bc11450SMiquel Raynal 				  NL802154_FLAG_NEED_RTNL,
27599bc11450SMiquel Raynal 	},
27609bc11450SMiquel Raynal 	{
27619bc11450SMiquel Raynal 		.cmd = NL802154_CMD_STOP_BEACONS,
27629bc11450SMiquel Raynal 		.doit = nl802154_stop_beacons,
27639bc11450SMiquel Raynal 		.flags = GENL_ADMIN_PERM,
27649bc11450SMiquel Raynal 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
27659bc11450SMiquel Raynal 				  NL802154_FLAG_CHECK_NETDEV_UP |
27669bc11450SMiquel Raynal 				  NL802154_FLAG_NEED_RTNL,
27679bc11450SMiquel Raynal 	},
2768a26c5fd7SAlexander Aring #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
2769a26c5fd7SAlexander Aring 	{
2770a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_SET_SEC_PARAMS,
2771ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2772a26c5fd7SAlexander Aring 		.doit = nl802154_set_llsec_params,
2773a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2774a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2775a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2776a26c5fd7SAlexander Aring 	},
2777a26c5fd7SAlexander Aring 	{
2778a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_GET_SEC_KEY,
277975cdbdd0SJiri Pirko 		.validate = GENL_DONT_VALIDATE_STRICT |
278075cdbdd0SJiri Pirko 			    GENL_DONT_VALIDATE_DUMP_STRICT,
2781a26c5fd7SAlexander Aring 		/* TODO .doit by matching key id? */
2782a26c5fd7SAlexander Aring 		.dumpit = nl802154_dump_llsec_key,
2783a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2784a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2785a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2786a26c5fd7SAlexander Aring 	},
2787a26c5fd7SAlexander Aring 	{
2788a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_NEW_SEC_KEY,
2789ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2790a26c5fd7SAlexander Aring 		.doit = nl802154_add_llsec_key,
2791a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2792a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2793a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2794a26c5fd7SAlexander Aring 	},
2795a26c5fd7SAlexander Aring 	{
2796a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_DEL_SEC_KEY,
2797ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2798a26c5fd7SAlexander Aring 		.doit = nl802154_del_llsec_key,
2799a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2800a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2801a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2802a26c5fd7SAlexander Aring 	},
2803a26c5fd7SAlexander Aring 	/* TODO unique identifier must short+pan OR extended_addr */
2804a26c5fd7SAlexander Aring 	{
2805a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_GET_SEC_DEV,
280675cdbdd0SJiri Pirko 		.validate = GENL_DONT_VALIDATE_STRICT |
280775cdbdd0SJiri Pirko 			    GENL_DONT_VALIDATE_DUMP_STRICT,
2808a26c5fd7SAlexander Aring 		/* TODO .doit by matching extended_addr? */
2809a26c5fd7SAlexander Aring 		.dumpit = nl802154_dump_llsec_dev,
2810a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2811a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2812a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2813a26c5fd7SAlexander Aring 	},
2814a26c5fd7SAlexander Aring 	{
2815a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_NEW_SEC_DEV,
2816ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2817a26c5fd7SAlexander Aring 		.doit = nl802154_add_llsec_dev,
2818a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2819a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2820a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2821a26c5fd7SAlexander Aring 	},
2822a26c5fd7SAlexander Aring 	{
2823a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_DEL_SEC_DEV,
2824ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2825a26c5fd7SAlexander Aring 		.doit = nl802154_del_llsec_dev,
2826a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2827a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2828a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2829a26c5fd7SAlexander Aring 	},
2830a26c5fd7SAlexander Aring 	/* TODO remove complete devkey, put it as nested? */
2831a26c5fd7SAlexander Aring 	{
2832a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_GET_SEC_DEVKEY,
283375cdbdd0SJiri Pirko 		.validate = GENL_DONT_VALIDATE_STRICT |
283475cdbdd0SJiri Pirko 			    GENL_DONT_VALIDATE_DUMP_STRICT,
2835a26c5fd7SAlexander Aring 		/* TODO doit by matching ??? */
2836a26c5fd7SAlexander Aring 		.dumpit = nl802154_dump_llsec_devkey,
2837a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2838a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2839a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2840a26c5fd7SAlexander Aring 	},
2841a26c5fd7SAlexander Aring 	{
2842a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_NEW_SEC_DEVKEY,
2843ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2844a26c5fd7SAlexander Aring 		.doit = nl802154_add_llsec_devkey,
2845a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2846a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2847a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2848a26c5fd7SAlexander Aring 	},
2849a26c5fd7SAlexander Aring 	{
2850a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_DEL_SEC_DEVKEY,
2851ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2852a26c5fd7SAlexander Aring 		.doit = nl802154_del_llsec_devkey,
2853a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2854a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2855a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2856a26c5fd7SAlexander Aring 	},
2857a26c5fd7SAlexander Aring 	{
2858a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_GET_SEC_LEVEL,
285975cdbdd0SJiri Pirko 		.validate = GENL_DONT_VALIDATE_STRICT |
286075cdbdd0SJiri Pirko 			    GENL_DONT_VALIDATE_DUMP_STRICT,
2861a26c5fd7SAlexander Aring 		/* TODO .doit by matching frame_type? */
2862a26c5fd7SAlexander Aring 		.dumpit = nl802154_dump_llsec_seclevel,
2863a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2864a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2865a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2866a26c5fd7SAlexander Aring 	},
2867a26c5fd7SAlexander Aring 	{
2868a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_NEW_SEC_LEVEL,
2869ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2870a26c5fd7SAlexander Aring 		.doit = nl802154_add_llsec_seclevel,
2871a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2872a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2873a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2874a26c5fd7SAlexander Aring 	},
2875a26c5fd7SAlexander Aring 	{
2876a26c5fd7SAlexander Aring 		.cmd = NL802154_CMD_DEL_SEC_LEVEL,
2877ef6243acSJohannes Berg 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2878a26c5fd7SAlexander Aring 		/* TODO match frame_type only? */
2879a26c5fd7SAlexander Aring 		.doit = nl802154_del_llsec_seclevel,
2880a26c5fd7SAlexander Aring 		.flags = GENL_ADMIN_PERM,
2881a26c5fd7SAlexander Aring 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2882a26c5fd7SAlexander Aring 				  NL802154_FLAG_NEED_RTNL,
2883a26c5fd7SAlexander Aring 	},
2884a26c5fd7SAlexander Aring #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
288579fe1a2aSAlexander Aring };
288679fe1a2aSAlexander Aring 
288756989f6dSJohannes Berg static struct genl_family nl802154_fam __ro_after_init = {
2888489111e5SJohannes Berg 	.name = NL802154_GENL_NAME,	/* have users key off the name instead */
2889489111e5SJohannes Berg 	.hdrsize = 0,			/* no private header */
2890489111e5SJohannes Berg 	.version = 1,			/* no particular meaning now */
2891489111e5SJohannes Berg 	.maxattr = NL802154_ATTR_MAX,
28923b0f31f2SJohannes Berg 	.policy = nl802154_policy,
2893489111e5SJohannes Berg 	.netnsok = true,
2894489111e5SJohannes Berg 	.pre_doit = nl802154_pre_doit,
2895489111e5SJohannes Berg 	.post_doit = nl802154_post_doit,
2896489111e5SJohannes Berg 	.module = THIS_MODULE,
2897489111e5SJohannes Berg 	.ops = nl802154_ops,
2898489111e5SJohannes Berg 	.n_ops = ARRAY_SIZE(nl802154_ops),
28999c5d03d3SJakub Kicinski 	.resv_start_op = NL802154_CMD_DEL_SEC_LEVEL + 1,
2900489111e5SJohannes Berg 	.mcgrps = nl802154_mcgrps,
2901489111e5SJohannes Berg 	.n_mcgrps = ARRAY_SIZE(nl802154_mcgrps),
2902489111e5SJohannes Berg };
2903489111e5SJohannes Berg 
290479fe1a2aSAlexander Aring /* initialisation/exit functions */
290556989f6dSJohannes Berg int __init nl802154_init(void)
290679fe1a2aSAlexander Aring {
2907489111e5SJohannes Berg 	return genl_register_family(&nl802154_fam);
290879fe1a2aSAlexander Aring }
290979fe1a2aSAlexander Aring 
291079fe1a2aSAlexander Aring void nl802154_exit(void)
291179fe1a2aSAlexander Aring {
291279fe1a2aSAlexander Aring 	genl_unregister_family(&nl802154_fam);
291379fe1a2aSAlexander Aring }
2914