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