xref: /OK3568_Linux_fs/kernel/net/ncsi/ncsi-netlink.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018.
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include <linux/module.h>
7*4882a593Smuzhiyun #include <linux/kernel.h>
8*4882a593Smuzhiyun #include <linux/if_arp.h>
9*4882a593Smuzhiyun #include <linux/rtnetlink.h>
10*4882a593Smuzhiyun #include <linux/etherdevice.h>
11*4882a593Smuzhiyun #include <net/genetlink.h>
12*4882a593Smuzhiyun #include <net/ncsi.h>
13*4882a593Smuzhiyun #include <linux/skbuff.h>
14*4882a593Smuzhiyun #include <net/sock.h>
15*4882a593Smuzhiyun #include <uapi/linux/ncsi.h>
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #include "internal.h"
18*4882a593Smuzhiyun #include "ncsi-pkt.h"
19*4882a593Smuzhiyun #include "ncsi-netlink.h"
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun static struct genl_family ncsi_genl_family;
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = {
24*4882a593Smuzhiyun 	[NCSI_ATTR_IFINDEX] =		{ .type = NLA_U32 },
25*4882a593Smuzhiyun 	[NCSI_ATTR_PACKAGE_LIST] =	{ .type = NLA_NESTED },
26*4882a593Smuzhiyun 	[NCSI_ATTR_PACKAGE_ID] =	{ .type = NLA_U32 },
27*4882a593Smuzhiyun 	[NCSI_ATTR_CHANNEL_ID] =	{ .type = NLA_U32 },
28*4882a593Smuzhiyun 	[NCSI_ATTR_DATA] =		{ .type = NLA_BINARY, .len = 2048 },
29*4882a593Smuzhiyun 	[NCSI_ATTR_MULTI_FLAG] =	{ .type = NLA_FLAG },
30*4882a593Smuzhiyun 	[NCSI_ATTR_PACKAGE_MASK] =	{ .type = NLA_U32 },
31*4882a593Smuzhiyun 	[NCSI_ATTR_CHANNEL_MASK] =	{ .type = NLA_U32 },
32*4882a593Smuzhiyun };
33*4882a593Smuzhiyun 
ndp_from_ifindex(struct net * net,u32 ifindex)34*4882a593Smuzhiyun static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
35*4882a593Smuzhiyun {
36*4882a593Smuzhiyun 	struct ncsi_dev_priv *ndp;
37*4882a593Smuzhiyun 	struct net_device *dev;
38*4882a593Smuzhiyun 	struct ncsi_dev *nd;
39*4882a593Smuzhiyun 	struct ncsi_dev;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	if (!net)
42*4882a593Smuzhiyun 		return NULL;
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	dev = dev_get_by_index(net, ifindex);
45*4882a593Smuzhiyun 	if (!dev) {
46*4882a593Smuzhiyun 		pr_err("NCSI netlink: No device for ifindex %u\n", ifindex);
47*4882a593Smuzhiyun 		return NULL;
48*4882a593Smuzhiyun 	}
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	nd = ncsi_find_dev(dev);
51*4882a593Smuzhiyun 	ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	dev_put(dev);
54*4882a593Smuzhiyun 	return ndp;
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun 
ncsi_write_channel_info(struct sk_buff * skb,struct ncsi_dev_priv * ndp,struct ncsi_channel * nc)57*4882a593Smuzhiyun static int ncsi_write_channel_info(struct sk_buff *skb,
58*4882a593Smuzhiyun 				   struct ncsi_dev_priv *ndp,
59*4882a593Smuzhiyun 				   struct ncsi_channel *nc)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun 	struct ncsi_channel_vlan_filter *ncf;
62*4882a593Smuzhiyun 	struct ncsi_channel_mode *m;
63*4882a593Smuzhiyun 	struct nlattr *vid_nest;
64*4882a593Smuzhiyun 	int i;
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id);
67*4882a593Smuzhiyun 	m = &nc->modes[NCSI_MODE_LINK];
68*4882a593Smuzhiyun 	nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]);
69*4882a593Smuzhiyun 	if (nc->state == NCSI_CHANNEL_ACTIVE)
70*4882a593Smuzhiyun 		nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE);
71*4882a593Smuzhiyun 	if (nc == nc->package->preferred_channel)
72*4882a593Smuzhiyun 		nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED);
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version);
75*4882a593Smuzhiyun 	nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MINOR, nc->version.alpha2);
76*4882a593Smuzhiyun 	nla_put_string(skb, NCSI_CHANNEL_ATTR_VERSION_STR, nc->version.fw_name);
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	vid_nest = nla_nest_start_noflag(skb, NCSI_CHANNEL_ATTR_VLAN_LIST);
79*4882a593Smuzhiyun 	if (!vid_nest)
80*4882a593Smuzhiyun 		return -ENOMEM;
81*4882a593Smuzhiyun 	ncf = &nc->vlan_filter;
82*4882a593Smuzhiyun 	i = -1;
83*4882a593Smuzhiyun 	while ((i = find_next_bit((void *)&ncf->bitmap, ncf->n_vids,
84*4882a593Smuzhiyun 				  i + 1)) < ncf->n_vids) {
85*4882a593Smuzhiyun 		if (ncf->vids[i])
86*4882a593Smuzhiyun 			nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID,
87*4882a593Smuzhiyun 				    ncf->vids[i]);
88*4882a593Smuzhiyun 	}
89*4882a593Smuzhiyun 	nla_nest_end(skb, vid_nest);
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	return 0;
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun 
ncsi_write_package_info(struct sk_buff * skb,struct ncsi_dev_priv * ndp,unsigned int id)94*4882a593Smuzhiyun static int ncsi_write_package_info(struct sk_buff *skb,
95*4882a593Smuzhiyun 				   struct ncsi_dev_priv *ndp, unsigned int id)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun 	struct nlattr *pnest, *cnest, *nest;
98*4882a593Smuzhiyun 	struct ncsi_package *np;
99*4882a593Smuzhiyun 	struct ncsi_channel *nc;
100*4882a593Smuzhiyun 	bool found;
101*4882a593Smuzhiyun 	int rc;
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	if (id > ndp->package_num - 1) {
104*4882a593Smuzhiyun 		netdev_info(ndp->ndev.dev, "NCSI: No package with id %u\n", id);
105*4882a593Smuzhiyun 		return -ENODEV;
106*4882a593Smuzhiyun 	}
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	found = false;
109*4882a593Smuzhiyun 	NCSI_FOR_EACH_PACKAGE(ndp, np) {
110*4882a593Smuzhiyun 		if (np->id != id)
111*4882a593Smuzhiyun 			continue;
112*4882a593Smuzhiyun 		pnest = nla_nest_start_noflag(skb, NCSI_PKG_ATTR);
113*4882a593Smuzhiyun 		if (!pnest)
114*4882a593Smuzhiyun 			return -ENOMEM;
115*4882a593Smuzhiyun 		rc = nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id);
116*4882a593Smuzhiyun 		if (rc) {
117*4882a593Smuzhiyun 			nla_nest_cancel(skb, pnest);
118*4882a593Smuzhiyun 			return rc;
119*4882a593Smuzhiyun 		}
120*4882a593Smuzhiyun 		if ((0x1 << np->id) == ndp->package_whitelist)
121*4882a593Smuzhiyun 			nla_put_flag(skb, NCSI_PKG_ATTR_FORCED);
122*4882a593Smuzhiyun 		cnest = nla_nest_start_noflag(skb, NCSI_PKG_ATTR_CHANNEL_LIST);
123*4882a593Smuzhiyun 		if (!cnest) {
124*4882a593Smuzhiyun 			nla_nest_cancel(skb, pnest);
125*4882a593Smuzhiyun 			return -ENOMEM;
126*4882a593Smuzhiyun 		}
127*4882a593Smuzhiyun 		NCSI_FOR_EACH_CHANNEL(np, nc) {
128*4882a593Smuzhiyun 			nest = nla_nest_start_noflag(skb, NCSI_CHANNEL_ATTR);
129*4882a593Smuzhiyun 			if (!nest) {
130*4882a593Smuzhiyun 				nla_nest_cancel(skb, cnest);
131*4882a593Smuzhiyun 				nla_nest_cancel(skb, pnest);
132*4882a593Smuzhiyun 				return -ENOMEM;
133*4882a593Smuzhiyun 			}
134*4882a593Smuzhiyun 			rc = ncsi_write_channel_info(skb, ndp, nc);
135*4882a593Smuzhiyun 			if (rc) {
136*4882a593Smuzhiyun 				nla_nest_cancel(skb, nest);
137*4882a593Smuzhiyun 				nla_nest_cancel(skb, cnest);
138*4882a593Smuzhiyun 				nla_nest_cancel(skb, pnest);
139*4882a593Smuzhiyun 				return rc;
140*4882a593Smuzhiyun 			}
141*4882a593Smuzhiyun 			nla_nest_end(skb, nest);
142*4882a593Smuzhiyun 		}
143*4882a593Smuzhiyun 		nla_nest_end(skb, cnest);
144*4882a593Smuzhiyun 		nla_nest_end(skb, pnest);
145*4882a593Smuzhiyun 		found = true;
146*4882a593Smuzhiyun 	}
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	if (!found)
149*4882a593Smuzhiyun 		return -ENODEV;
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	return 0;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun 
ncsi_pkg_info_nl(struct sk_buff * msg,struct genl_info * info)154*4882a593Smuzhiyun static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun 	struct ncsi_dev_priv *ndp;
157*4882a593Smuzhiyun 	unsigned int package_id;
158*4882a593Smuzhiyun 	struct sk_buff *skb;
159*4882a593Smuzhiyun 	struct nlattr *attr;
160*4882a593Smuzhiyun 	void *hdr;
161*4882a593Smuzhiyun 	int rc;
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	if (!info || !info->attrs)
164*4882a593Smuzhiyun 		return -EINVAL;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	if (!info->attrs[NCSI_ATTR_IFINDEX])
167*4882a593Smuzhiyun 		return -EINVAL;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
170*4882a593Smuzhiyun 		return -EINVAL;
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	ndp = ndp_from_ifindex(genl_info_net(info),
173*4882a593Smuzhiyun 			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
174*4882a593Smuzhiyun 	if (!ndp)
175*4882a593Smuzhiyun 		return -ENODEV;
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
178*4882a593Smuzhiyun 	if (!skb)
179*4882a593Smuzhiyun 		return -ENOMEM;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
182*4882a593Smuzhiyun 			  &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO);
183*4882a593Smuzhiyun 	if (!hdr) {
184*4882a593Smuzhiyun 		kfree_skb(skb);
185*4882a593Smuzhiyun 		return -EMSGSIZE;
186*4882a593Smuzhiyun 	}
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	attr = nla_nest_start_noflag(skb, NCSI_ATTR_PACKAGE_LIST);
191*4882a593Smuzhiyun 	if (!attr) {
192*4882a593Smuzhiyun 		kfree_skb(skb);
193*4882a593Smuzhiyun 		return -EMSGSIZE;
194*4882a593Smuzhiyun 	}
195*4882a593Smuzhiyun 	rc = ncsi_write_package_info(skb, ndp, package_id);
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	if (rc) {
198*4882a593Smuzhiyun 		nla_nest_cancel(skb, attr);
199*4882a593Smuzhiyun 		goto err;
200*4882a593Smuzhiyun 	}
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	nla_nest_end(skb, attr);
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	genlmsg_end(skb, hdr);
205*4882a593Smuzhiyun 	return genlmsg_reply(skb, info);
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun err:
208*4882a593Smuzhiyun 	kfree_skb(skb);
209*4882a593Smuzhiyun 	return rc;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun 
ncsi_pkg_info_all_nl(struct sk_buff * skb,struct netlink_callback * cb)212*4882a593Smuzhiyun static int ncsi_pkg_info_all_nl(struct sk_buff *skb,
213*4882a593Smuzhiyun 				struct netlink_callback *cb)
214*4882a593Smuzhiyun {
215*4882a593Smuzhiyun 	struct nlattr *attrs[NCSI_ATTR_MAX + 1];
216*4882a593Smuzhiyun 	struct ncsi_package *np, *package;
217*4882a593Smuzhiyun 	struct ncsi_dev_priv *ndp;
218*4882a593Smuzhiyun 	unsigned int package_id;
219*4882a593Smuzhiyun 	struct nlattr *attr;
220*4882a593Smuzhiyun 	void *hdr;
221*4882a593Smuzhiyun 	int rc;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	rc = genlmsg_parse_deprecated(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX,
224*4882a593Smuzhiyun 				      ncsi_genl_policy, NULL);
225*4882a593Smuzhiyun 	if (rc)
226*4882a593Smuzhiyun 		return rc;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	if (!attrs[NCSI_ATTR_IFINDEX])
229*4882a593Smuzhiyun 		return -EINVAL;
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)),
232*4882a593Smuzhiyun 			       nla_get_u32(attrs[NCSI_ATTR_IFINDEX]));
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	if (!ndp)
235*4882a593Smuzhiyun 		return -ENODEV;
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	package_id = cb->args[0];
238*4882a593Smuzhiyun 	package = NULL;
239*4882a593Smuzhiyun 	NCSI_FOR_EACH_PACKAGE(ndp, np)
240*4882a593Smuzhiyun 		if (np->id == package_id)
241*4882a593Smuzhiyun 			package = np;
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	if (!package)
244*4882a593Smuzhiyun 		return 0; /* done */
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
247*4882a593Smuzhiyun 			  &ncsi_genl_family, NLM_F_MULTI,  NCSI_CMD_PKG_INFO);
248*4882a593Smuzhiyun 	if (!hdr) {
249*4882a593Smuzhiyun 		rc = -EMSGSIZE;
250*4882a593Smuzhiyun 		goto err;
251*4882a593Smuzhiyun 	}
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	attr = nla_nest_start_noflag(skb, NCSI_ATTR_PACKAGE_LIST);
254*4882a593Smuzhiyun 	if (!attr) {
255*4882a593Smuzhiyun 		rc = -EMSGSIZE;
256*4882a593Smuzhiyun 		goto err;
257*4882a593Smuzhiyun 	}
258*4882a593Smuzhiyun 	rc = ncsi_write_package_info(skb, ndp, package->id);
259*4882a593Smuzhiyun 	if (rc) {
260*4882a593Smuzhiyun 		nla_nest_cancel(skb, attr);
261*4882a593Smuzhiyun 		goto err;
262*4882a593Smuzhiyun 	}
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	nla_nest_end(skb, attr);
265*4882a593Smuzhiyun 	genlmsg_end(skb, hdr);
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun 	cb->args[0] = package_id + 1;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	return skb->len;
270*4882a593Smuzhiyun err:
271*4882a593Smuzhiyun 	genlmsg_cancel(skb, hdr);
272*4882a593Smuzhiyun 	return rc;
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun 
ncsi_set_interface_nl(struct sk_buff * msg,struct genl_info * info)275*4882a593Smuzhiyun static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun 	struct ncsi_package *np, *package;
278*4882a593Smuzhiyun 	struct ncsi_channel *nc, *channel;
279*4882a593Smuzhiyun 	u32 package_id, channel_id;
280*4882a593Smuzhiyun 	struct ncsi_dev_priv *ndp;
281*4882a593Smuzhiyun 	unsigned long flags;
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	if (!info || !info->attrs)
284*4882a593Smuzhiyun 		return -EINVAL;
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	if (!info->attrs[NCSI_ATTR_IFINDEX])
287*4882a593Smuzhiyun 		return -EINVAL;
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 	if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
290*4882a593Smuzhiyun 		return -EINVAL;
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
293*4882a593Smuzhiyun 			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
294*4882a593Smuzhiyun 	if (!ndp)
295*4882a593Smuzhiyun 		return -ENODEV;
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
298*4882a593Smuzhiyun 	package = NULL;
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	NCSI_FOR_EACH_PACKAGE(ndp, np)
301*4882a593Smuzhiyun 		if (np->id == package_id)
302*4882a593Smuzhiyun 			package = np;
303*4882a593Smuzhiyun 	if (!package) {
304*4882a593Smuzhiyun 		/* The user has set a package that does not exist */
305*4882a593Smuzhiyun 		return -ERANGE;
306*4882a593Smuzhiyun 	}
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	channel = NULL;
309*4882a593Smuzhiyun 	if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
310*4882a593Smuzhiyun 		channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
311*4882a593Smuzhiyun 		NCSI_FOR_EACH_CHANNEL(package, nc)
312*4882a593Smuzhiyun 			if (nc->id == channel_id) {
313*4882a593Smuzhiyun 				channel = nc;
314*4882a593Smuzhiyun 				break;
315*4882a593Smuzhiyun 			}
316*4882a593Smuzhiyun 		if (!channel) {
317*4882a593Smuzhiyun 			netdev_info(ndp->ndev.dev,
318*4882a593Smuzhiyun 				    "NCSI: Channel %u does not exist!\n",
319*4882a593Smuzhiyun 				    channel_id);
320*4882a593Smuzhiyun 			return -ERANGE;
321*4882a593Smuzhiyun 		}
322*4882a593Smuzhiyun 	}
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	spin_lock_irqsave(&ndp->lock, flags);
325*4882a593Smuzhiyun 	ndp->package_whitelist = 0x1 << package->id;
326*4882a593Smuzhiyun 	ndp->multi_package = false;
327*4882a593Smuzhiyun 	spin_unlock_irqrestore(&ndp->lock, flags);
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	spin_lock_irqsave(&package->lock, flags);
330*4882a593Smuzhiyun 	package->multi_channel = false;
331*4882a593Smuzhiyun 	if (channel) {
332*4882a593Smuzhiyun 		package->channel_whitelist = 0x1 << channel->id;
333*4882a593Smuzhiyun 		package->preferred_channel = channel;
334*4882a593Smuzhiyun 	} else {
335*4882a593Smuzhiyun 		/* Allow any channel */
336*4882a593Smuzhiyun 		package->channel_whitelist = UINT_MAX;
337*4882a593Smuzhiyun 		package->preferred_channel = NULL;
338*4882a593Smuzhiyun 	}
339*4882a593Smuzhiyun 	spin_unlock_irqrestore(&package->lock, flags);
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	if (channel)
342*4882a593Smuzhiyun 		netdev_info(ndp->ndev.dev,
343*4882a593Smuzhiyun 			    "Set package 0x%x, channel 0x%x as preferred\n",
344*4882a593Smuzhiyun 			    package_id, channel_id);
345*4882a593Smuzhiyun 	else
346*4882a593Smuzhiyun 		netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n",
347*4882a593Smuzhiyun 			    package_id);
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	/* Update channel configuration */
350*4882a593Smuzhiyun 	if (!(ndp->flags & NCSI_DEV_RESET))
351*4882a593Smuzhiyun 		ncsi_reset_dev(&ndp->ndev);
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun 	return 0;
354*4882a593Smuzhiyun }
355*4882a593Smuzhiyun 
ncsi_clear_interface_nl(struct sk_buff * msg,struct genl_info * info)356*4882a593Smuzhiyun static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
357*4882a593Smuzhiyun {
358*4882a593Smuzhiyun 	struct ncsi_dev_priv *ndp;
359*4882a593Smuzhiyun 	struct ncsi_package *np;
360*4882a593Smuzhiyun 	unsigned long flags;
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	if (!info || !info->attrs)
363*4882a593Smuzhiyun 		return -EINVAL;
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 	if (!info->attrs[NCSI_ATTR_IFINDEX])
366*4882a593Smuzhiyun 		return -EINVAL;
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
369*4882a593Smuzhiyun 			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
370*4882a593Smuzhiyun 	if (!ndp)
371*4882a593Smuzhiyun 		return -ENODEV;
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	/* Reset any whitelists and disable multi mode */
374*4882a593Smuzhiyun 	spin_lock_irqsave(&ndp->lock, flags);
375*4882a593Smuzhiyun 	ndp->package_whitelist = UINT_MAX;
376*4882a593Smuzhiyun 	ndp->multi_package = false;
377*4882a593Smuzhiyun 	spin_unlock_irqrestore(&ndp->lock, flags);
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	NCSI_FOR_EACH_PACKAGE(ndp, np) {
380*4882a593Smuzhiyun 		spin_lock_irqsave(&np->lock, flags);
381*4882a593Smuzhiyun 		np->multi_channel = false;
382*4882a593Smuzhiyun 		np->channel_whitelist = UINT_MAX;
383*4882a593Smuzhiyun 		np->preferred_channel = NULL;
384*4882a593Smuzhiyun 		spin_unlock_irqrestore(&np->lock, flags);
385*4882a593Smuzhiyun 	}
386*4882a593Smuzhiyun 	netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n");
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	/* Update channel configuration */
389*4882a593Smuzhiyun 	if (!(ndp->flags & NCSI_DEV_RESET))
390*4882a593Smuzhiyun 		ncsi_reset_dev(&ndp->ndev);
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 	return 0;
393*4882a593Smuzhiyun }
394*4882a593Smuzhiyun 
ncsi_send_cmd_nl(struct sk_buff * msg,struct genl_info * info)395*4882a593Smuzhiyun static int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info)
396*4882a593Smuzhiyun {
397*4882a593Smuzhiyun 	struct ncsi_dev_priv *ndp;
398*4882a593Smuzhiyun 	struct ncsi_pkt_hdr *hdr;
399*4882a593Smuzhiyun 	struct ncsi_cmd_arg nca;
400*4882a593Smuzhiyun 	unsigned char *data;
401*4882a593Smuzhiyun 	u32 package_id;
402*4882a593Smuzhiyun 	u32 channel_id;
403*4882a593Smuzhiyun 	int len, ret;
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	if (!info || !info->attrs) {
406*4882a593Smuzhiyun 		ret = -EINVAL;
407*4882a593Smuzhiyun 		goto out;
408*4882a593Smuzhiyun 	}
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun 	if (!info->attrs[NCSI_ATTR_IFINDEX]) {
411*4882a593Smuzhiyun 		ret = -EINVAL;
412*4882a593Smuzhiyun 		goto out;
413*4882a593Smuzhiyun 	}
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) {
416*4882a593Smuzhiyun 		ret = -EINVAL;
417*4882a593Smuzhiyun 		goto out;
418*4882a593Smuzhiyun 	}
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 	if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) {
421*4882a593Smuzhiyun 		ret = -EINVAL;
422*4882a593Smuzhiyun 		goto out;
423*4882a593Smuzhiyun 	}
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun 	if (!info->attrs[NCSI_ATTR_DATA]) {
426*4882a593Smuzhiyun 		ret = -EINVAL;
427*4882a593Smuzhiyun 		goto out;
428*4882a593Smuzhiyun 	}
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 	ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
431*4882a593Smuzhiyun 			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
432*4882a593Smuzhiyun 	if (!ndp) {
433*4882a593Smuzhiyun 		ret = -ENODEV;
434*4882a593Smuzhiyun 		goto out;
435*4882a593Smuzhiyun 	}
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
438*4882a593Smuzhiyun 	channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	if (package_id >= NCSI_MAX_PACKAGE || channel_id >= NCSI_MAX_CHANNEL) {
441*4882a593Smuzhiyun 		ret = -ERANGE;
442*4882a593Smuzhiyun 		goto out_netlink;
443*4882a593Smuzhiyun 	}
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	len = nla_len(info->attrs[NCSI_ATTR_DATA]);
446*4882a593Smuzhiyun 	if (len < sizeof(struct ncsi_pkt_hdr)) {
447*4882a593Smuzhiyun 		netdev_info(ndp->ndev.dev, "NCSI: no command to send %u\n",
448*4882a593Smuzhiyun 			    package_id);
449*4882a593Smuzhiyun 		ret = -EINVAL;
450*4882a593Smuzhiyun 		goto out_netlink;
451*4882a593Smuzhiyun 	} else {
452*4882a593Smuzhiyun 		data = (unsigned char *)nla_data(info->attrs[NCSI_ATTR_DATA]);
453*4882a593Smuzhiyun 	}
454*4882a593Smuzhiyun 
455*4882a593Smuzhiyun 	hdr = (struct ncsi_pkt_hdr *)data;
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	nca.ndp = ndp;
458*4882a593Smuzhiyun 	nca.package = (unsigned char)package_id;
459*4882a593Smuzhiyun 	nca.channel = (unsigned char)channel_id;
460*4882a593Smuzhiyun 	nca.type = hdr->type;
461*4882a593Smuzhiyun 	nca.req_flags = NCSI_REQ_FLAG_NETLINK_DRIVEN;
462*4882a593Smuzhiyun 	nca.info = info;
463*4882a593Smuzhiyun 	nca.payload = ntohs(hdr->length);
464*4882a593Smuzhiyun 	nca.data = data + sizeof(*hdr);
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	ret = ncsi_xmit_cmd(&nca);
467*4882a593Smuzhiyun out_netlink:
468*4882a593Smuzhiyun 	if (ret != 0) {
469*4882a593Smuzhiyun 		netdev_err(ndp->ndev.dev,
470*4882a593Smuzhiyun 			   "NCSI: Error %d sending command\n",
471*4882a593Smuzhiyun 			   ret);
472*4882a593Smuzhiyun 		ncsi_send_netlink_err(ndp->ndev.dev,
473*4882a593Smuzhiyun 				      info->snd_seq,
474*4882a593Smuzhiyun 				      info->snd_portid,
475*4882a593Smuzhiyun 				      info->nlhdr,
476*4882a593Smuzhiyun 				      ret);
477*4882a593Smuzhiyun 	}
478*4882a593Smuzhiyun out:
479*4882a593Smuzhiyun 	return ret;
480*4882a593Smuzhiyun }
481*4882a593Smuzhiyun 
ncsi_send_netlink_rsp(struct ncsi_request * nr,struct ncsi_package * np,struct ncsi_channel * nc)482*4882a593Smuzhiyun int ncsi_send_netlink_rsp(struct ncsi_request *nr,
483*4882a593Smuzhiyun 			  struct ncsi_package *np,
484*4882a593Smuzhiyun 			  struct ncsi_channel *nc)
485*4882a593Smuzhiyun {
486*4882a593Smuzhiyun 	struct sk_buff *skb;
487*4882a593Smuzhiyun 	struct net *net;
488*4882a593Smuzhiyun 	void *hdr;
489*4882a593Smuzhiyun 	int rc;
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	net = dev_net(nr->rsp->dev);
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun 	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
494*4882a593Smuzhiyun 	if (!skb)
495*4882a593Smuzhiyun 		return -ENOMEM;
496*4882a593Smuzhiyun 
497*4882a593Smuzhiyun 	hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq,
498*4882a593Smuzhiyun 			  &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD);
499*4882a593Smuzhiyun 	if (!hdr) {
500*4882a593Smuzhiyun 		kfree_skb(skb);
501*4882a593Smuzhiyun 		return -EMSGSIZE;
502*4882a593Smuzhiyun 	}
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 	nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->rsp->dev->ifindex);
505*4882a593Smuzhiyun 	if (np)
506*4882a593Smuzhiyun 		nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id);
507*4882a593Smuzhiyun 	if (nc)
508*4882a593Smuzhiyun 		nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id);
509*4882a593Smuzhiyun 	else
510*4882a593Smuzhiyun 		nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL);
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun 	rc = nla_put(skb, NCSI_ATTR_DATA, nr->rsp->len, (void *)nr->rsp->data);
513*4882a593Smuzhiyun 	if (rc)
514*4882a593Smuzhiyun 		goto err;
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun 	genlmsg_end(skb, hdr);
517*4882a593Smuzhiyun 	return genlmsg_unicast(net, skb, nr->snd_portid);
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun err:
520*4882a593Smuzhiyun 	kfree_skb(skb);
521*4882a593Smuzhiyun 	return rc;
522*4882a593Smuzhiyun }
523*4882a593Smuzhiyun 
ncsi_send_netlink_timeout(struct ncsi_request * nr,struct ncsi_package * np,struct ncsi_channel * nc)524*4882a593Smuzhiyun int ncsi_send_netlink_timeout(struct ncsi_request *nr,
525*4882a593Smuzhiyun 			      struct ncsi_package *np,
526*4882a593Smuzhiyun 			      struct ncsi_channel *nc)
527*4882a593Smuzhiyun {
528*4882a593Smuzhiyun 	struct sk_buff *skb;
529*4882a593Smuzhiyun 	struct net *net;
530*4882a593Smuzhiyun 	void *hdr;
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
533*4882a593Smuzhiyun 	if (!skb)
534*4882a593Smuzhiyun 		return -ENOMEM;
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq,
537*4882a593Smuzhiyun 			  &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD);
538*4882a593Smuzhiyun 	if (!hdr) {
539*4882a593Smuzhiyun 		kfree_skb(skb);
540*4882a593Smuzhiyun 		return -EMSGSIZE;
541*4882a593Smuzhiyun 	}
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 	net = dev_net(nr->cmd->dev);
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 	nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->cmd->dev->ifindex);
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun 	if (np)
548*4882a593Smuzhiyun 		nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id);
549*4882a593Smuzhiyun 	else
550*4882a593Smuzhiyun 		nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID,
551*4882a593Smuzhiyun 			    NCSI_PACKAGE_INDEX((((struct ncsi_pkt_hdr *)
552*4882a593Smuzhiyun 						 nr->cmd->data)->channel)));
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun 	if (nc)
555*4882a593Smuzhiyun 		nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id);
556*4882a593Smuzhiyun 	else
557*4882a593Smuzhiyun 		nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL);
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 	genlmsg_end(skb, hdr);
560*4882a593Smuzhiyun 	return genlmsg_unicast(net, skb, nr->snd_portid);
561*4882a593Smuzhiyun }
562*4882a593Smuzhiyun 
ncsi_send_netlink_err(struct net_device * dev,u32 snd_seq,u32 snd_portid,struct nlmsghdr * nlhdr,int err)563*4882a593Smuzhiyun int ncsi_send_netlink_err(struct net_device *dev,
564*4882a593Smuzhiyun 			  u32 snd_seq,
565*4882a593Smuzhiyun 			  u32 snd_portid,
566*4882a593Smuzhiyun 			  struct nlmsghdr *nlhdr,
567*4882a593Smuzhiyun 			  int err)
568*4882a593Smuzhiyun {
569*4882a593Smuzhiyun 	struct nlmsghdr *nlh;
570*4882a593Smuzhiyun 	struct nlmsgerr *nle;
571*4882a593Smuzhiyun 	struct sk_buff *skb;
572*4882a593Smuzhiyun 	struct net *net;
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
575*4882a593Smuzhiyun 	if (!skb)
576*4882a593Smuzhiyun 		return -ENOMEM;
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 	net = dev_net(dev);
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	nlh = nlmsg_put(skb, snd_portid, snd_seq,
581*4882a593Smuzhiyun 			NLMSG_ERROR, sizeof(*nle), 0);
582*4882a593Smuzhiyun 	nle = (struct nlmsgerr *)nlmsg_data(nlh);
583*4882a593Smuzhiyun 	nle->error = err;
584*4882a593Smuzhiyun 	memcpy(&nle->msg, nlhdr, sizeof(*nlh));
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun 	nlmsg_end(skb, nlh);
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun 	return nlmsg_unicast(net->genl_sock, skb, snd_portid);
589*4882a593Smuzhiyun }
590*4882a593Smuzhiyun 
ncsi_set_package_mask_nl(struct sk_buff * msg,struct genl_info * info)591*4882a593Smuzhiyun static int ncsi_set_package_mask_nl(struct sk_buff *msg,
592*4882a593Smuzhiyun 				    struct genl_info *info)
593*4882a593Smuzhiyun {
594*4882a593Smuzhiyun 	struct ncsi_dev_priv *ndp;
595*4882a593Smuzhiyun 	unsigned long flags;
596*4882a593Smuzhiyun 	int rc;
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun 	if (!info || !info->attrs)
599*4882a593Smuzhiyun 		return -EINVAL;
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	if (!info->attrs[NCSI_ATTR_IFINDEX])
602*4882a593Smuzhiyun 		return -EINVAL;
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun 	if (!info->attrs[NCSI_ATTR_PACKAGE_MASK])
605*4882a593Smuzhiyun 		return -EINVAL;
606*4882a593Smuzhiyun 
607*4882a593Smuzhiyun 	ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
608*4882a593Smuzhiyun 			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
609*4882a593Smuzhiyun 	if (!ndp)
610*4882a593Smuzhiyun 		return -ENODEV;
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun 	spin_lock_irqsave(&ndp->lock, flags);
613*4882a593Smuzhiyun 	if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
614*4882a593Smuzhiyun 		if (ndp->flags & NCSI_DEV_HWA) {
615*4882a593Smuzhiyun 			ndp->multi_package = true;
616*4882a593Smuzhiyun 			rc = 0;
617*4882a593Smuzhiyun 		} else {
618*4882a593Smuzhiyun 			netdev_err(ndp->ndev.dev,
619*4882a593Smuzhiyun 				   "NCSI: Can't use multiple packages without HWA\n");
620*4882a593Smuzhiyun 			rc = -EPERM;
621*4882a593Smuzhiyun 		}
622*4882a593Smuzhiyun 	} else {
623*4882a593Smuzhiyun 		ndp->multi_package = false;
624*4882a593Smuzhiyun 		rc = 0;
625*4882a593Smuzhiyun 	}
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun 	if (!rc)
628*4882a593Smuzhiyun 		ndp->package_whitelist =
629*4882a593Smuzhiyun 			nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]);
630*4882a593Smuzhiyun 	spin_unlock_irqrestore(&ndp->lock, flags);
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 	if (!rc) {
633*4882a593Smuzhiyun 		/* Update channel configuration */
634*4882a593Smuzhiyun 		if (!(ndp->flags & NCSI_DEV_RESET))
635*4882a593Smuzhiyun 			ncsi_reset_dev(&ndp->ndev);
636*4882a593Smuzhiyun 	}
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun 	return rc;
639*4882a593Smuzhiyun }
640*4882a593Smuzhiyun 
ncsi_set_channel_mask_nl(struct sk_buff * msg,struct genl_info * info)641*4882a593Smuzhiyun static int ncsi_set_channel_mask_nl(struct sk_buff *msg,
642*4882a593Smuzhiyun 				    struct genl_info *info)
643*4882a593Smuzhiyun {
644*4882a593Smuzhiyun 	struct ncsi_package *np, *package;
645*4882a593Smuzhiyun 	struct ncsi_channel *nc, *channel;
646*4882a593Smuzhiyun 	u32 package_id, channel_id;
647*4882a593Smuzhiyun 	struct ncsi_dev_priv *ndp;
648*4882a593Smuzhiyun 	unsigned long flags;
649*4882a593Smuzhiyun 
650*4882a593Smuzhiyun 	if (!info || !info->attrs)
651*4882a593Smuzhiyun 		return -EINVAL;
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	if (!info->attrs[NCSI_ATTR_IFINDEX])
654*4882a593Smuzhiyun 		return -EINVAL;
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun 	if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
657*4882a593Smuzhiyun 		return -EINVAL;
658*4882a593Smuzhiyun 
659*4882a593Smuzhiyun 	if (!info->attrs[NCSI_ATTR_CHANNEL_MASK])
660*4882a593Smuzhiyun 		return -EINVAL;
661*4882a593Smuzhiyun 
662*4882a593Smuzhiyun 	ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
663*4882a593Smuzhiyun 			       nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
664*4882a593Smuzhiyun 	if (!ndp)
665*4882a593Smuzhiyun 		return -ENODEV;
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun 	package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
668*4882a593Smuzhiyun 	package = NULL;
669*4882a593Smuzhiyun 	NCSI_FOR_EACH_PACKAGE(ndp, np)
670*4882a593Smuzhiyun 		if (np->id == package_id) {
671*4882a593Smuzhiyun 			package = np;
672*4882a593Smuzhiyun 			break;
673*4882a593Smuzhiyun 		}
674*4882a593Smuzhiyun 	if (!package)
675*4882a593Smuzhiyun 		return -ERANGE;
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	spin_lock_irqsave(&package->lock, flags);
678*4882a593Smuzhiyun 
679*4882a593Smuzhiyun 	channel = NULL;
680*4882a593Smuzhiyun 	if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
681*4882a593Smuzhiyun 		channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
682*4882a593Smuzhiyun 		NCSI_FOR_EACH_CHANNEL(np, nc)
683*4882a593Smuzhiyun 			if (nc->id == channel_id) {
684*4882a593Smuzhiyun 				channel = nc;
685*4882a593Smuzhiyun 				break;
686*4882a593Smuzhiyun 			}
687*4882a593Smuzhiyun 		if (!channel) {
688*4882a593Smuzhiyun 			spin_unlock_irqrestore(&package->lock, flags);
689*4882a593Smuzhiyun 			return -ERANGE;
690*4882a593Smuzhiyun 		}
691*4882a593Smuzhiyun 		netdev_dbg(ndp->ndev.dev,
692*4882a593Smuzhiyun 			   "NCSI: Channel %u set as preferred channel\n",
693*4882a593Smuzhiyun 			   channel->id);
694*4882a593Smuzhiyun 	}
695*4882a593Smuzhiyun 
696*4882a593Smuzhiyun 	package->channel_whitelist =
697*4882a593Smuzhiyun 		nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]);
698*4882a593Smuzhiyun 	if (package->channel_whitelist == 0)
699*4882a593Smuzhiyun 		netdev_dbg(ndp->ndev.dev,
700*4882a593Smuzhiyun 			   "NCSI: Package %u set to all channels disabled\n",
701*4882a593Smuzhiyun 			   package->id);
702*4882a593Smuzhiyun 
703*4882a593Smuzhiyun 	package->preferred_channel = channel;
704*4882a593Smuzhiyun 
705*4882a593Smuzhiyun 	if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
706*4882a593Smuzhiyun 		package->multi_channel = true;
707*4882a593Smuzhiyun 		netdev_info(ndp->ndev.dev,
708*4882a593Smuzhiyun 			    "NCSI: Multi-channel enabled on package %u\n",
709*4882a593Smuzhiyun 			    package_id);
710*4882a593Smuzhiyun 	} else {
711*4882a593Smuzhiyun 		package->multi_channel = false;
712*4882a593Smuzhiyun 	}
713*4882a593Smuzhiyun 
714*4882a593Smuzhiyun 	spin_unlock_irqrestore(&package->lock, flags);
715*4882a593Smuzhiyun 
716*4882a593Smuzhiyun 	/* Update channel configuration */
717*4882a593Smuzhiyun 	if (!(ndp->flags & NCSI_DEV_RESET))
718*4882a593Smuzhiyun 		ncsi_reset_dev(&ndp->ndev);
719*4882a593Smuzhiyun 
720*4882a593Smuzhiyun 	return 0;
721*4882a593Smuzhiyun }
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun static const struct genl_small_ops ncsi_ops[] = {
724*4882a593Smuzhiyun 	{
725*4882a593Smuzhiyun 		.cmd = NCSI_CMD_PKG_INFO,
726*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
727*4882a593Smuzhiyun 		.doit = ncsi_pkg_info_nl,
728*4882a593Smuzhiyun 		.dumpit = ncsi_pkg_info_all_nl,
729*4882a593Smuzhiyun 		.flags = 0,
730*4882a593Smuzhiyun 	},
731*4882a593Smuzhiyun 	{
732*4882a593Smuzhiyun 		.cmd = NCSI_CMD_SET_INTERFACE,
733*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
734*4882a593Smuzhiyun 		.doit = ncsi_set_interface_nl,
735*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
736*4882a593Smuzhiyun 	},
737*4882a593Smuzhiyun 	{
738*4882a593Smuzhiyun 		.cmd = NCSI_CMD_CLEAR_INTERFACE,
739*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
740*4882a593Smuzhiyun 		.doit = ncsi_clear_interface_nl,
741*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
742*4882a593Smuzhiyun 	},
743*4882a593Smuzhiyun 	{
744*4882a593Smuzhiyun 		.cmd = NCSI_CMD_SEND_CMD,
745*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
746*4882a593Smuzhiyun 		.doit = ncsi_send_cmd_nl,
747*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
748*4882a593Smuzhiyun 	},
749*4882a593Smuzhiyun 	{
750*4882a593Smuzhiyun 		.cmd = NCSI_CMD_SET_PACKAGE_MASK,
751*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
752*4882a593Smuzhiyun 		.doit = ncsi_set_package_mask_nl,
753*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
754*4882a593Smuzhiyun 	},
755*4882a593Smuzhiyun 	{
756*4882a593Smuzhiyun 		.cmd = NCSI_CMD_SET_CHANNEL_MASK,
757*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
758*4882a593Smuzhiyun 		.doit = ncsi_set_channel_mask_nl,
759*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
760*4882a593Smuzhiyun 	},
761*4882a593Smuzhiyun };
762*4882a593Smuzhiyun 
763*4882a593Smuzhiyun static struct genl_family ncsi_genl_family __ro_after_init = {
764*4882a593Smuzhiyun 	.name = "NCSI",
765*4882a593Smuzhiyun 	.version = 0,
766*4882a593Smuzhiyun 	.maxattr = NCSI_ATTR_MAX,
767*4882a593Smuzhiyun 	.policy = ncsi_genl_policy,
768*4882a593Smuzhiyun 	.module = THIS_MODULE,
769*4882a593Smuzhiyun 	.small_ops = ncsi_ops,
770*4882a593Smuzhiyun 	.n_small_ops = ARRAY_SIZE(ncsi_ops),
771*4882a593Smuzhiyun };
772*4882a593Smuzhiyun 
ncsi_init_netlink(void)773*4882a593Smuzhiyun static int __init ncsi_init_netlink(void)
774*4882a593Smuzhiyun {
775*4882a593Smuzhiyun 	return genl_register_family(&ncsi_genl_family);
776*4882a593Smuzhiyun }
777*4882a593Smuzhiyun subsys_initcall(ncsi_init_netlink);
778