18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
98c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
108c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
118c2ecf20Sopenharmony_ci#include <net/genetlink.h>
128c2ecf20Sopenharmony_ci#include <net/ncsi.h>
138c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
148c2ecf20Sopenharmony_ci#include <net/sock.h>
158c2ecf20Sopenharmony_ci#include <uapi/linux/ncsi.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "internal.h"
188c2ecf20Sopenharmony_ci#include "ncsi-pkt.h"
198c2ecf20Sopenharmony_ci#include "ncsi-netlink.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic struct genl_family ncsi_genl_family;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = {
248c2ecf20Sopenharmony_ci	[NCSI_ATTR_IFINDEX] =		{ .type = NLA_U32 },
258c2ecf20Sopenharmony_ci	[NCSI_ATTR_PACKAGE_LIST] =	{ .type = NLA_NESTED },
268c2ecf20Sopenharmony_ci	[NCSI_ATTR_PACKAGE_ID] =	{ .type = NLA_U32 },
278c2ecf20Sopenharmony_ci	[NCSI_ATTR_CHANNEL_ID] =	{ .type = NLA_U32 },
288c2ecf20Sopenharmony_ci	[NCSI_ATTR_DATA] =		{ .type = NLA_BINARY, .len = 2048 },
298c2ecf20Sopenharmony_ci	[NCSI_ATTR_MULTI_FLAG] =	{ .type = NLA_FLAG },
308c2ecf20Sopenharmony_ci	[NCSI_ATTR_PACKAGE_MASK] =	{ .type = NLA_U32 },
318c2ecf20Sopenharmony_ci	[NCSI_ATTR_CHANNEL_MASK] =	{ .type = NLA_U32 },
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp;
378c2ecf20Sopenharmony_ci	struct net_device *dev;
388c2ecf20Sopenharmony_ci	struct ncsi_dev *nd;
398c2ecf20Sopenharmony_ci	struct ncsi_dev;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	if (!net)
428c2ecf20Sopenharmony_ci		return NULL;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	dev = dev_get_by_index(net, ifindex);
458c2ecf20Sopenharmony_ci	if (!dev) {
468c2ecf20Sopenharmony_ci		pr_err("NCSI netlink: No device for ifindex %u\n", ifindex);
478c2ecf20Sopenharmony_ci		return NULL;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	nd = ncsi_find_dev(dev);
518c2ecf20Sopenharmony_ci	ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	dev_put(dev);
548c2ecf20Sopenharmony_ci	return ndp;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int ncsi_write_channel_info(struct sk_buff *skb,
588c2ecf20Sopenharmony_ci				   struct ncsi_dev_priv *ndp,
598c2ecf20Sopenharmony_ci				   struct ncsi_channel *nc)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct ncsi_channel_vlan_filter *ncf;
628c2ecf20Sopenharmony_ci	struct ncsi_channel_mode *m;
638c2ecf20Sopenharmony_ci	struct nlattr *vid_nest;
648c2ecf20Sopenharmony_ci	int i;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id);
678c2ecf20Sopenharmony_ci	m = &nc->modes[NCSI_MODE_LINK];
688c2ecf20Sopenharmony_ci	nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]);
698c2ecf20Sopenharmony_ci	if (nc->state == NCSI_CHANNEL_ACTIVE)
708c2ecf20Sopenharmony_ci		nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE);
718c2ecf20Sopenharmony_ci	if (nc == nc->package->preferred_channel)
728c2ecf20Sopenharmony_ci		nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.major);
758c2ecf20Sopenharmony_ci	nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MINOR, nc->version.minor);
768c2ecf20Sopenharmony_ci	nla_put_string(skb, NCSI_CHANNEL_ATTR_VERSION_STR, nc->version.fw_name);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	vid_nest = nla_nest_start_noflag(skb, NCSI_CHANNEL_ATTR_VLAN_LIST);
798c2ecf20Sopenharmony_ci	if (!vid_nest)
808c2ecf20Sopenharmony_ci		return -ENOMEM;
818c2ecf20Sopenharmony_ci	ncf = &nc->vlan_filter;
828c2ecf20Sopenharmony_ci	i = -1;
838c2ecf20Sopenharmony_ci	while ((i = find_next_bit((void *)&ncf->bitmap, ncf->n_vids,
848c2ecf20Sopenharmony_ci				  i + 1)) < ncf->n_vids) {
858c2ecf20Sopenharmony_ci		if (ncf->vids[i])
868c2ecf20Sopenharmony_ci			nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID,
878c2ecf20Sopenharmony_ci				    ncf->vids[i]);
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci	nla_nest_end(skb, vid_nest);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int ncsi_write_package_info(struct sk_buff *skb,
958c2ecf20Sopenharmony_ci				   struct ncsi_dev_priv *ndp, unsigned int id)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct nlattr *pnest, *cnest, *nest;
988c2ecf20Sopenharmony_ci	struct ncsi_package *np;
998c2ecf20Sopenharmony_ci	struct ncsi_channel *nc;
1008c2ecf20Sopenharmony_ci	bool found;
1018c2ecf20Sopenharmony_ci	int rc;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (id > ndp->package_num - 1) {
1048c2ecf20Sopenharmony_ci		netdev_info(ndp->ndev.dev, "NCSI: No package with id %u\n", id);
1058c2ecf20Sopenharmony_ci		return -ENODEV;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	found = false;
1098c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np) {
1108c2ecf20Sopenharmony_ci		if (np->id != id)
1118c2ecf20Sopenharmony_ci			continue;
1128c2ecf20Sopenharmony_ci		pnest = nla_nest_start_noflag(skb, NCSI_PKG_ATTR);
1138c2ecf20Sopenharmony_ci		if (!pnest)
1148c2ecf20Sopenharmony_ci			return -ENOMEM;
1158c2ecf20Sopenharmony_ci		rc = nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id);
1168c2ecf20Sopenharmony_ci		if (rc) {
1178c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, pnest);
1188c2ecf20Sopenharmony_ci			return rc;
1198c2ecf20Sopenharmony_ci		}
1208c2ecf20Sopenharmony_ci		if ((0x1 << np->id) == ndp->package_whitelist)
1218c2ecf20Sopenharmony_ci			nla_put_flag(skb, NCSI_PKG_ATTR_FORCED);
1228c2ecf20Sopenharmony_ci		cnest = nla_nest_start_noflag(skb, NCSI_PKG_ATTR_CHANNEL_LIST);
1238c2ecf20Sopenharmony_ci		if (!cnest) {
1248c2ecf20Sopenharmony_ci			nla_nest_cancel(skb, pnest);
1258c2ecf20Sopenharmony_ci			return -ENOMEM;
1268c2ecf20Sopenharmony_ci		}
1278c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(np, nc) {
1288c2ecf20Sopenharmony_ci			nest = nla_nest_start_noflag(skb, NCSI_CHANNEL_ATTR);
1298c2ecf20Sopenharmony_ci			if (!nest) {
1308c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, cnest);
1318c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, pnest);
1328c2ecf20Sopenharmony_ci				return -ENOMEM;
1338c2ecf20Sopenharmony_ci			}
1348c2ecf20Sopenharmony_ci			rc = ncsi_write_channel_info(skb, ndp, nc);
1358c2ecf20Sopenharmony_ci			if (rc) {
1368c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, nest);
1378c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, cnest);
1388c2ecf20Sopenharmony_ci				nla_nest_cancel(skb, pnest);
1398c2ecf20Sopenharmony_ci				return rc;
1408c2ecf20Sopenharmony_ci			}
1418c2ecf20Sopenharmony_ci			nla_nest_end(skb, nest);
1428c2ecf20Sopenharmony_ci		}
1438c2ecf20Sopenharmony_ci		nla_nest_end(skb, cnest);
1448c2ecf20Sopenharmony_ci		nla_nest_end(skb, pnest);
1458c2ecf20Sopenharmony_ci		found = true;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (!found)
1498c2ecf20Sopenharmony_ci		return -ENODEV;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	return 0;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp;
1578c2ecf20Sopenharmony_ci	unsigned int package_id;
1588c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1598c2ecf20Sopenharmony_ci	struct nlattr *attr;
1608c2ecf20Sopenharmony_ci	void *hdr;
1618c2ecf20Sopenharmony_ci	int rc;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (!info || !info->attrs)
1648c2ecf20Sopenharmony_ci		return -EINVAL;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (!info->attrs[NCSI_ATTR_IFINDEX])
1678c2ecf20Sopenharmony_ci		return -EINVAL;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
1708c2ecf20Sopenharmony_ci		return -EINVAL;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	ndp = ndp_from_ifindex(genl_info_net(info),
1738c2ecf20Sopenharmony_ci			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
1748c2ecf20Sopenharmony_ci	if (!ndp)
1758c2ecf20Sopenharmony_ci		return -ENODEV;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
1788c2ecf20Sopenharmony_ci	if (!skb)
1798c2ecf20Sopenharmony_ci		return -ENOMEM;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
1828c2ecf20Sopenharmony_ci			  &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO);
1838c2ecf20Sopenharmony_ci	if (!hdr) {
1848c2ecf20Sopenharmony_ci		kfree_skb(skb);
1858c2ecf20Sopenharmony_ci		return -EMSGSIZE;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	attr = nla_nest_start_noflag(skb, NCSI_ATTR_PACKAGE_LIST);
1918c2ecf20Sopenharmony_ci	if (!attr) {
1928c2ecf20Sopenharmony_ci		kfree_skb(skb);
1938c2ecf20Sopenharmony_ci		return -EMSGSIZE;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci	rc = ncsi_write_package_info(skb, ndp, package_id);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (rc) {
1988c2ecf20Sopenharmony_ci		nla_nest_cancel(skb, attr);
1998c2ecf20Sopenharmony_ci		goto err;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	nla_nest_end(skb, attr);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	genlmsg_end(skb, hdr);
2058c2ecf20Sopenharmony_ci	return genlmsg_reply(skb, info);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cierr:
2088c2ecf20Sopenharmony_ci	kfree_skb(skb);
2098c2ecf20Sopenharmony_ci	return rc;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic int ncsi_pkg_info_all_nl(struct sk_buff *skb,
2138c2ecf20Sopenharmony_ci				struct netlink_callback *cb)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct nlattr *attrs[NCSI_ATTR_MAX + 1];
2168c2ecf20Sopenharmony_ci	struct ncsi_package *np, *package;
2178c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp;
2188c2ecf20Sopenharmony_ci	unsigned int package_id;
2198c2ecf20Sopenharmony_ci	struct nlattr *attr;
2208c2ecf20Sopenharmony_ci	void *hdr;
2218c2ecf20Sopenharmony_ci	int rc;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	rc = genlmsg_parse_deprecated(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX,
2248c2ecf20Sopenharmony_ci				      ncsi_genl_policy, NULL);
2258c2ecf20Sopenharmony_ci	if (rc)
2268c2ecf20Sopenharmony_ci		return rc;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (!attrs[NCSI_ATTR_IFINDEX])
2298c2ecf20Sopenharmony_ci		return -EINVAL;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)),
2328c2ecf20Sopenharmony_ci			       nla_get_u32(attrs[NCSI_ATTR_IFINDEX]));
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (!ndp)
2358c2ecf20Sopenharmony_ci		return -ENODEV;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	package_id = cb->args[0];
2388c2ecf20Sopenharmony_ci	package = NULL;
2398c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np)
2408c2ecf20Sopenharmony_ci		if (np->id == package_id)
2418c2ecf20Sopenharmony_ci			package = np;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (!package)
2448c2ecf20Sopenharmony_ci		return 0; /* done */
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
2478c2ecf20Sopenharmony_ci			  &ncsi_genl_family, NLM_F_MULTI,  NCSI_CMD_PKG_INFO);
2488c2ecf20Sopenharmony_ci	if (!hdr) {
2498c2ecf20Sopenharmony_ci		rc = -EMSGSIZE;
2508c2ecf20Sopenharmony_ci		goto err;
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	attr = nla_nest_start_noflag(skb, NCSI_ATTR_PACKAGE_LIST);
2548c2ecf20Sopenharmony_ci	if (!attr) {
2558c2ecf20Sopenharmony_ci		rc = -EMSGSIZE;
2568c2ecf20Sopenharmony_ci		goto err;
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci	rc = ncsi_write_package_info(skb, ndp, package->id);
2598c2ecf20Sopenharmony_ci	if (rc) {
2608c2ecf20Sopenharmony_ci		nla_nest_cancel(skb, attr);
2618c2ecf20Sopenharmony_ci		goto err;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	nla_nest_end(skb, attr);
2658c2ecf20Sopenharmony_ci	genlmsg_end(skb, hdr);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	cb->args[0] = package_id + 1;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	return skb->len;
2708c2ecf20Sopenharmony_cierr:
2718c2ecf20Sopenharmony_ci	genlmsg_cancel(skb, hdr);
2728c2ecf20Sopenharmony_ci	return rc;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct ncsi_package *np, *package;
2788c2ecf20Sopenharmony_ci	struct ncsi_channel *nc, *channel;
2798c2ecf20Sopenharmony_ci	u32 package_id, channel_id;
2808c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp;
2818c2ecf20Sopenharmony_ci	unsigned long flags;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (!info || !info->attrs)
2848c2ecf20Sopenharmony_ci		return -EINVAL;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	if (!info->attrs[NCSI_ATTR_IFINDEX])
2878c2ecf20Sopenharmony_ci		return -EINVAL;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
2908c2ecf20Sopenharmony_ci		return -EINVAL;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
2938c2ecf20Sopenharmony_ci			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
2948c2ecf20Sopenharmony_ci	if (!ndp)
2958c2ecf20Sopenharmony_ci		return -ENODEV;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
2988c2ecf20Sopenharmony_ci	package = NULL;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np)
3018c2ecf20Sopenharmony_ci		if (np->id == package_id)
3028c2ecf20Sopenharmony_ci			package = np;
3038c2ecf20Sopenharmony_ci	if (!package) {
3048c2ecf20Sopenharmony_ci		/* The user has set a package that does not exist */
3058c2ecf20Sopenharmony_ci		return -ERANGE;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	channel = NULL;
3098c2ecf20Sopenharmony_ci	if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
3108c2ecf20Sopenharmony_ci		channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
3118c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(package, nc)
3128c2ecf20Sopenharmony_ci			if (nc->id == channel_id) {
3138c2ecf20Sopenharmony_ci				channel = nc;
3148c2ecf20Sopenharmony_ci				break;
3158c2ecf20Sopenharmony_ci			}
3168c2ecf20Sopenharmony_ci		if (!channel) {
3178c2ecf20Sopenharmony_ci			netdev_info(ndp->ndev.dev,
3188c2ecf20Sopenharmony_ci				    "NCSI: Channel %u does not exist!\n",
3198c2ecf20Sopenharmony_ci				    channel_id);
3208c2ecf20Sopenharmony_ci			return -ERANGE;
3218c2ecf20Sopenharmony_ci		}
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndp->lock, flags);
3258c2ecf20Sopenharmony_ci	ndp->package_whitelist = 0x1 << package->id;
3268c2ecf20Sopenharmony_ci	ndp->multi_package = false;
3278c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndp->lock, flags);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	spin_lock_irqsave(&package->lock, flags);
3308c2ecf20Sopenharmony_ci	package->multi_channel = false;
3318c2ecf20Sopenharmony_ci	if (channel) {
3328c2ecf20Sopenharmony_ci		package->channel_whitelist = 0x1 << channel->id;
3338c2ecf20Sopenharmony_ci		package->preferred_channel = channel;
3348c2ecf20Sopenharmony_ci	} else {
3358c2ecf20Sopenharmony_ci		/* Allow any channel */
3368c2ecf20Sopenharmony_ci		package->channel_whitelist = UINT_MAX;
3378c2ecf20Sopenharmony_ci		package->preferred_channel = NULL;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&package->lock, flags);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	if (channel)
3428c2ecf20Sopenharmony_ci		netdev_info(ndp->ndev.dev,
3438c2ecf20Sopenharmony_ci			    "Set package 0x%x, channel 0x%x as preferred\n",
3448c2ecf20Sopenharmony_ci			    package_id, channel_id);
3458c2ecf20Sopenharmony_ci	else
3468c2ecf20Sopenharmony_ci		netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n",
3478c2ecf20Sopenharmony_ci			    package_id);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* Update channel configuration */
3508c2ecf20Sopenharmony_ci	if (!(ndp->flags & NCSI_DEV_RESET))
3518c2ecf20Sopenharmony_ci		ncsi_reset_dev(&ndp->ndev);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	return 0;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp;
3598c2ecf20Sopenharmony_ci	struct ncsi_package *np;
3608c2ecf20Sopenharmony_ci	unsigned long flags;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (!info || !info->attrs)
3638c2ecf20Sopenharmony_ci		return -EINVAL;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (!info->attrs[NCSI_ATTR_IFINDEX])
3668c2ecf20Sopenharmony_ci		return -EINVAL;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
3698c2ecf20Sopenharmony_ci			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
3708c2ecf20Sopenharmony_ci	if (!ndp)
3718c2ecf20Sopenharmony_ci		return -ENODEV;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	/* Reset any whitelists and disable multi mode */
3748c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndp->lock, flags);
3758c2ecf20Sopenharmony_ci	ndp->package_whitelist = UINT_MAX;
3768c2ecf20Sopenharmony_ci	ndp->multi_package = false;
3778c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndp->lock, flags);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np) {
3808c2ecf20Sopenharmony_ci		spin_lock_irqsave(&np->lock, flags);
3818c2ecf20Sopenharmony_ci		np->multi_channel = false;
3828c2ecf20Sopenharmony_ci		np->channel_whitelist = UINT_MAX;
3838c2ecf20Sopenharmony_ci		np->preferred_channel = NULL;
3848c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&np->lock, flags);
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci	netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n");
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/* Update channel configuration */
3898c2ecf20Sopenharmony_ci	if (!(ndp->flags & NCSI_DEV_RESET))
3908c2ecf20Sopenharmony_ci		ncsi_reset_dev(&ndp->ndev);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	return 0;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp;
3988c2ecf20Sopenharmony_ci	struct ncsi_pkt_hdr *hdr;
3998c2ecf20Sopenharmony_ci	struct ncsi_cmd_arg nca;
4008c2ecf20Sopenharmony_ci	unsigned char *data;
4018c2ecf20Sopenharmony_ci	u32 package_id;
4028c2ecf20Sopenharmony_ci	u32 channel_id;
4038c2ecf20Sopenharmony_ci	int len, ret;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (!info || !info->attrs) {
4068c2ecf20Sopenharmony_ci		ret = -EINVAL;
4078c2ecf20Sopenharmony_ci		goto out;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	if (!info->attrs[NCSI_ATTR_IFINDEX]) {
4118c2ecf20Sopenharmony_ci		ret = -EINVAL;
4128c2ecf20Sopenharmony_ci		goto out;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) {
4168c2ecf20Sopenharmony_ci		ret = -EINVAL;
4178c2ecf20Sopenharmony_ci		goto out;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) {
4218c2ecf20Sopenharmony_ci		ret = -EINVAL;
4228c2ecf20Sopenharmony_ci		goto out;
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (!info->attrs[NCSI_ATTR_DATA]) {
4268c2ecf20Sopenharmony_ci		ret = -EINVAL;
4278c2ecf20Sopenharmony_ci		goto out;
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
4318c2ecf20Sopenharmony_ci			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
4328c2ecf20Sopenharmony_ci	if (!ndp) {
4338c2ecf20Sopenharmony_ci		ret = -ENODEV;
4348c2ecf20Sopenharmony_ci		goto out;
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
4388c2ecf20Sopenharmony_ci	channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if (package_id >= NCSI_MAX_PACKAGE || channel_id >= NCSI_MAX_CHANNEL) {
4418c2ecf20Sopenharmony_ci		ret = -ERANGE;
4428c2ecf20Sopenharmony_ci		goto out_netlink;
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	len = nla_len(info->attrs[NCSI_ATTR_DATA]);
4468c2ecf20Sopenharmony_ci	if (len < sizeof(struct ncsi_pkt_hdr)) {
4478c2ecf20Sopenharmony_ci		netdev_info(ndp->ndev.dev, "NCSI: no command to send %u\n",
4488c2ecf20Sopenharmony_ci			    package_id);
4498c2ecf20Sopenharmony_ci		ret = -EINVAL;
4508c2ecf20Sopenharmony_ci		goto out_netlink;
4518c2ecf20Sopenharmony_ci	} else {
4528c2ecf20Sopenharmony_ci		data = (unsigned char *)nla_data(info->attrs[NCSI_ATTR_DATA]);
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	hdr = (struct ncsi_pkt_hdr *)data;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	nca.ndp = ndp;
4588c2ecf20Sopenharmony_ci	nca.package = (unsigned char)package_id;
4598c2ecf20Sopenharmony_ci	nca.channel = (unsigned char)channel_id;
4608c2ecf20Sopenharmony_ci	nca.type = hdr->type;
4618c2ecf20Sopenharmony_ci	nca.req_flags = NCSI_REQ_FLAG_NETLINK_DRIVEN;
4628c2ecf20Sopenharmony_ci	nca.info = info;
4638c2ecf20Sopenharmony_ci	nca.payload = ntohs(hdr->length);
4648c2ecf20Sopenharmony_ci	nca.data = data + sizeof(*hdr);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	ret = ncsi_xmit_cmd(&nca);
4678c2ecf20Sopenharmony_ciout_netlink:
4688c2ecf20Sopenharmony_ci	if (ret != 0) {
4698c2ecf20Sopenharmony_ci		netdev_err(ndp->ndev.dev,
4708c2ecf20Sopenharmony_ci			   "NCSI: Error %d sending command\n",
4718c2ecf20Sopenharmony_ci			   ret);
4728c2ecf20Sopenharmony_ci		ncsi_send_netlink_err(ndp->ndev.dev,
4738c2ecf20Sopenharmony_ci				      info->snd_seq,
4748c2ecf20Sopenharmony_ci				      info->snd_portid,
4758c2ecf20Sopenharmony_ci				      info->nlhdr,
4768c2ecf20Sopenharmony_ci				      ret);
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ciout:
4798c2ecf20Sopenharmony_ci	return ret;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ciint ncsi_send_netlink_rsp(struct ncsi_request *nr,
4838c2ecf20Sopenharmony_ci			  struct ncsi_package *np,
4848c2ecf20Sopenharmony_ci			  struct ncsi_channel *nc)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	struct sk_buff *skb;
4878c2ecf20Sopenharmony_ci	struct net *net;
4888c2ecf20Sopenharmony_ci	void *hdr;
4898c2ecf20Sopenharmony_ci	int rc;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	net = dev_net(nr->rsp->dev);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
4948c2ecf20Sopenharmony_ci	if (!skb)
4958c2ecf20Sopenharmony_ci		return -ENOMEM;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq,
4988c2ecf20Sopenharmony_ci			  &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD);
4998c2ecf20Sopenharmony_ci	if (!hdr) {
5008c2ecf20Sopenharmony_ci		kfree_skb(skb);
5018c2ecf20Sopenharmony_ci		return -EMSGSIZE;
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->rsp->dev->ifindex);
5058c2ecf20Sopenharmony_ci	if (np)
5068c2ecf20Sopenharmony_ci		nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id);
5078c2ecf20Sopenharmony_ci	if (nc)
5088c2ecf20Sopenharmony_ci		nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id);
5098c2ecf20Sopenharmony_ci	else
5108c2ecf20Sopenharmony_ci		nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	rc = nla_put(skb, NCSI_ATTR_DATA, nr->rsp->len, (void *)nr->rsp->data);
5138c2ecf20Sopenharmony_ci	if (rc)
5148c2ecf20Sopenharmony_ci		goto err;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	genlmsg_end(skb, hdr);
5178c2ecf20Sopenharmony_ci	return genlmsg_unicast(net, skb, nr->snd_portid);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cierr:
5208c2ecf20Sopenharmony_ci	kfree_skb(skb);
5218c2ecf20Sopenharmony_ci	return rc;
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ciint ncsi_send_netlink_timeout(struct ncsi_request *nr,
5258c2ecf20Sopenharmony_ci			      struct ncsi_package *np,
5268c2ecf20Sopenharmony_ci			      struct ncsi_channel *nc)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5298c2ecf20Sopenharmony_ci	struct net *net;
5308c2ecf20Sopenharmony_ci	void *hdr;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
5338c2ecf20Sopenharmony_ci	if (!skb)
5348c2ecf20Sopenharmony_ci		return -ENOMEM;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq,
5378c2ecf20Sopenharmony_ci			  &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD);
5388c2ecf20Sopenharmony_ci	if (!hdr) {
5398c2ecf20Sopenharmony_ci		kfree_skb(skb);
5408c2ecf20Sopenharmony_ci		return -EMSGSIZE;
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	net = dev_net(nr->cmd->dev);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->cmd->dev->ifindex);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	if (np)
5488c2ecf20Sopenharmony_ci		nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id);
5498c2ecf20Sopenharmony_ci	else
5508c2ecf20Sopenharmony_ci		nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID,
5518c2ecf20Sopenharmony_ci			    NCSI_PACKAGE_INDEX((((struct ncsi_pkt_hdr *)
5528c2ecf20Sopenharmony_ci						 nr->cmd->data)->channel)));
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if (nc)
5558c2ecf20Sopenharmony_ci		nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id);
5568c2ecf20Sopenharmony_ci	else
5578c2ecf20Sopenharmony_ci		nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	genlmsg_end(skb, hdr);
5608c2ecf20Sopenharmony_ci	return genlmsg_unicast(net, skb, nr->snd_portid);
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ciint ncsi_send_netlink_err(struct net_device *dev,
5648c2ecf20Sopenharmony_ci			  u32 snd_seq,
5658c2ecf20Sopenharmony_ci			  u32 snd_portid,
5668c2ecf20Sopenharmony_ci			  struct nlmsghdr *nlhdr,
5678c2ecf20Sopenharmony_ci			  int err)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
5708c2ecf20Sopenharmony_ci	struct nlmsgerr *nle;
5718c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5728c2ecf20Sopenharmony_ci	struct net *net;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
5758c2ecf20Sopenharmony_ci	if (!skb)
5768c2ecf20Sopenharmony_ci		return -ENOMEM;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	net = dev_net(dev);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, snd_portid, snd_seq,
5818c2ecf20Sopenharmony_ci			NLMSG_ERROR, sizeof(*nle), 0);
5828c2ecf20Sopenharmony_ci	nle = (struct nlmsgerr *)nlmsg_data(nlh);
5838c2ecf20Sopenharmony_ci	nle->error = err;
5848c2ecf20Sopenharmony_ci	memcpy(&nle->msg, nlhdr, sizeof(*nlh));
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	return nlmsg_unicast(net->genl_sock, skb, snd_portid);
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_cistatic int ncsi_set_package_mask_nl(struct sk_buff *msg,
5928c2ecf20Sopenharmony_ci				    struct genl_info *info)
5938c2ecf20Sopenharmony_ci{
5948c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp;
5958c2ecf20Sopenharmony_ci	unsigned long flags;
5968c2ecf20Sopenharmony_ci	int rc;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	if (!info || !info->attrs)
5998c2ecf20Sopenharmony_ci		return -EINVAL;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	if (!info->attrs[NCSI_ATTR_IFINDEX])
6028c2ecf20Sopenharmony_ci		return -EINVAL;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	if (!info->attrs[NCSI_ATTR_PACKAGE_MASK])
6058c2ecf20Sopenharmony_ci		return -EINVAL;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
6088c2ecf20Sopenharmony_ci			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
6098c2ecf20Sopenharmony_ci	if (!ndp)
6108c2ecf20Sopenharmony_ci		return -ENODEV;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ndp->lock, flags);
6138c2ecf20Sopenharmony_ci	if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
6148c2ecf20Sopenharmony_ci		if (ndp->flags & NCSI_DEV_HWA) {
6158c2ecf20Sopenharmony_ci			ndp->multi_package = true;
6168c2ecf20Sopenharmony_ci			rc = 0;
6178c2ecf20Sopenharmony_ci		} else {
6188c2ecf20Sopenharmony_ci			netdev_err(ndp->ndev.dev,
6198c2ecf20Sopenharmony_ci				   "NCSI: Can't use multiple packages without HWA\n");
6208c2ecf20Sopenharmony_ci			rc = -EPERM;
6218c2ecf20Sopenharmony_ci		}
6228c2ecf20Sopenharmony_ci	} else {
6238c2ecf20Sopenharmony_ci		ndp->multi_package = false;
6248c2ecf20Sopenharmony_ci		rc = 0;
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	if (!rc)
6288c2ecf20Sopenharmony_ci		ndp->package_whitelist =
6298c2ecf20Sopenharmony_ci			nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]);
6308c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ndp->lock, flags);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	if (!rc) {
6338c2ecf20Sopenharmony_ci		/* Update channel configuration */
6348c2ecf20Sopenharmony_ci		if (!(ndp->flags & NCSI_DEV_RESET))
6358c2ecf20Sopenharmony_ci			ncsi_reset_dev(&ndp->ndev);
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	return rc;
6398c2ecf20Sopenharmony_ci}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_cistatic int ncsi_set_channel_mask_nl(struct sk_buff *msg,
6428c2ecf20Sopenharmony_ci				    struct genl_info *info)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	struct ncsi_package *np, *package;
6458c2ecf20Sopenharmony_ci	struct ncsi_channel *nc, *channel;
6468c2ecf20Sopenharmony_ci	u32 package_id, channel_id;
6478c2ecf20Sopenharmony_ci	struct ncsi_dev_priv *ndp;
6488c2ecf20Sopenharmony_ci	unsigned long flags;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	if (!info || !info->attrs)
6518c2ecf20Sopenharmony_ci		return -EINVAL;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	if (!info->attrs[NCSI_ATTR_IFINDEX])
6548c2ecf20Sopenharmony_ci		return -EINVAL;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
6578c2ecf20Sopenharmony_ci		return -EINVAL;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	if (!info->attrs[NCSI_ATTR_CHANNEL_MASK])
6608c2ecf20Sopenharmony_ci		return -EINVAL;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
6638c2ecf20Sopenharmony_ci			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
6648c2ecf20Sopenharmony_ci	if (!ndp)
6658c2ecf20Sopenharmony_ci		return -ENODEV;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
6688c2ecf20Sopenharmony_ci	package = NULL;
6698c2ecf20Sopenharmony_ci	NCSI_FOR_EACH_PACKAGE(ndp, np)
6708c2ecf20Sopenharmony_ci		if (np->id == package_id) {
6718c2ecf20Sopenharmony_ci			package = np;
6728c2ecf20Sopenharmony_ci			break;
6738c2ecf20Sopenharmony_ci		}
6748c2ecf20Sopenharmony_ci	if (!package)
6758c2ecf20Sopenharmony_ci		return -ERANGE;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	spin_lock_irqsave(&package->lock, flags);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	channel = NULL;
6808c2ecf20Sopenharmony_ci	if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
6818c2ecf20Sopenharmony_ci		channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
6828c2ecf20Sopenharmony_ci		NCSI_FOR_EACH_CHANNEL(np, nc)
6838c2ecf20Sopenharmony_ci			if (nc->id == channel_id) {
6848c2ecf20Sopenharmony_ci				channel = nc;
6858c2ecf20Sopenharmony_ci				break;
6868c2ecf20Sopenharmony_ci			}
6878c2ecf20Sopenharmony_ci		if (!channel) {
6888c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&package->lock, flags);
6898c2ecf20Sopenharmony_ci			return -ERANGE;
6908c2ecf20Sopenharmony_ci		}
6918c2ecf20Sopenharmony_ci		netdev_dbg(ndp->ndev.dev,
6928c2ecf20Sopenharmony_ci			   "NCSI: Channel %u set as preferred channel\n",
6938c2ecf20Sopenharmony_ci			   channel->id);
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	package->channel_whitelist =
6978c2ecf20Sopenharmony_ci		nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]);
6988c2ecf20Sopenharmony_ci	if (package->channel_whitelist == 0)
6998c2ecf20Sopenharmony_ci		netdev_dbg(ndp->ndev.dev,
7008c2ecf20Sopenharmony_ci			   "NCSI: Package %u set to all channels disabled\n",
7018c2ecf20Sopenharmony_ci			   package->id);
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	package->preferred_channel = channel;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
7068c2ecf20Sopenharmony_ci		package->multi_channel = true;
7078c2ecf20Sopenharmony_ci		netdev_info(ndp->ndev.dev,
7088c2ecf20Sopenharmony_ci			    "NCSI: Multi-channel enabled on package %u\n",
7098c2ecf20Sopenharmony_ci			    package_id);
7108c2ecf20Sopenharmony_ci	} else {
7118c2ecf20Sopenharmony_ci		package->multi_channel = false;
7128c2ecf20Sopenharmony_ci	}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&package->lock, flags);
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	/* Update channel configuration */
7178c2ecf20Sopenharmony_ci	if (!(ndp->flags & NCSI_DEV_RESET))
7188c2ecf20Sopenharmony_ci		ncsi_reset_dev(&ndp->ndev);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	return 0;
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_cistatic const struct genl_small_ops ncsi_ops[] = {
7248c2ecf20Sopenharmony_ci	{
7258c2ecf20Sopenharmony_ci		.cmd = NCSI_CMD_PKG_INFO,
7268c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
7278c2ecf20Sopenharmony_ci		.doit = ncsi_pkg_info_nl,
7288c2ecf20Sopenharmony_ci		.dumpit = ncsi_pkg_info_all_nl,
7298c2ecf20Sopenharmony_ci		.flags = 0,
7308c2ecf20Sopenharmony_ci	},
7318c2ecf20Sopenharmony_ci	{
7328c2ecf20Sopenharmony_ci		.cmd = NCSI_CMD_SET_INTERFACE,
7338c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
7348c2ecf20Sopenharmony_ci		.doit = ncsi_set_interface_nl,
7358c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
7368c2ecf20Sopenharmony_ci	},
7378c2ecf20Sopenharmony_ci	{
7388c2ecf20Sopenharmony_ci		.cmd = NCSI_CMD_CLEAR_INTERFACE,
7398c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
7408c2ecf20Sopenharmony_ci		.doit = ncsi_clear_interface_nl,
7418c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
7428c2ecf20Sopenharmony_ci	},
7438c2ecf20Sopenharmony_ci	{
7448c2ecf20Sopenharmony_ci		.cmd = NCSI_CMD_SEND_CMD,
7458c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
7468c2ecf20Sopenharmony_ci		.doit = ncsi_send_cmd_nl,
7478c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
7488c2ecf20Sopenharmony_ci	},
7498c2ecf20Sopenharmony_ci	{
7508c2ecf20Sopenharmony_ci		.cmd = NCSI_CMD_SET_PACKAGE_MASK,
7518c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
7528c2ecf20Sopenharmony_ci		.doit = ncsi_set_package_mask_nl,
7538c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
7548c2ecf20Sopenharmony_ci	},
7558c2ecf20Sopenharmony_ci	{
7568c2ecf20Sopenharmony_ci		.cmd = NCSI_CMD_SET_CHANNEL_MASK,
7578c2ecf20Sopenharmony_ci		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
7588c2ecf20Sopenharmony_ci		.doit = ncsi_set_channel_mask_nl,
7598c2ecf20Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
7608c2ecf20Sopenharmony_ci	},
7618c2ecf20Sopenharmony_ci};
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_cistatic struct genl_family ncsi_genl_family __ro_after_init = {
7648c2ecf20Sopenharmony_ci	.name = "NCSI",
7658c2ecf20Sopenharmony_ci	.version = 0,
7668c2ecf20Sopenharmony_ci	.maxattr = NCSI_ATTR_MAX,
7678c2ecf20Sopenharmony_ci	.policy = ncsi_genl_policy,
7688c2ecf20Sopenharmony_ci	.module = THIS_MODULE,
7698c2ecf20Sopenharmony_ci	.small_ops = ncsi_ops,
7708c2ecf20Sopenharmony_ci	.n_small_ops = ARRAY_SIZE(ncsi_ops),
7718c2ecf20Sopenharmony_ci};
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_cistatic int __init ncsi_init_netlink(void)
7748c2ecf20Sopenharmony_ci{
7758c2ecf20Sopenharmony_ci	return genl_register_family(&ncsi_genl_family);
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_cisubsys_initcall(ncsi_init_netlink);
778