162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/if_arp.h> 962306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1062306a36Sopenharmony_ci#include <linux/etherdevice.h> 1162306a36Sopenharmony_ci#include <net/genetlink.h> 1262306a36Sopenharmony_ci#include <net/ncsi.h> 1362306a36Sopenharmony_ci#include <linux/skbuff.h> 1462306a36Sopenharmony_ci#include <net/sock.h> 1562306a36Sopenharmony_ci#include <uapi/linux/ncsi.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "internal.h" 1862306a36Sopenharmony_ci#include "ncsi-pkt.h" 1962306a36Sopenharmony_ci#include "ncsi-netlink.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic struct genl_family ncsi_genl_family; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = { 2462306a36Sopenharmony_ci [NCSI_ATTR_IFINDEX] = { .type = NLA_U32 }, 2562306a36Sopenharmony_ci [NCSI_ATTR_PACKAGE_LIST] = { .type = NLA_NESTED }, 2662306a36Sopenharmony_ci [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, 2762306a36Sopenharmony_ci [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, 2862306a36Sopenharmony_ci [NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 }, 2962306a36Sopenharmony_ci [NCSI_ATTR_MULTI_FLAG] = { .type = NLA_FLAG }, 3062306a36Sopenharmony_ci [NCSI_ATTR_PACKAGE_MASK] = { .type = NLA_U32 }, 3162306a36Sopenharmony_ci [NCSI_ATTR_CHANNEL_MASK] = { .type = NLA_U32 }, 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct ncsi_dev_priv *ndp; 3762306a36Sopenharmony_ci struct net_device *dev; 3862306a36Sopenharmony_ci struct ncsi_dev *nd; 3962306a36Sopenharmony_ci struct ncsi_dev; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (!net) 4262306a36Sopenharmony_ci return NULL; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci dev = dev_get_by_index(net, ifindex); 4562306a36Sopenharmony_ci if (!dev) { 4662306a36Sopenharmony_ci pr_err("NCSI netlink: No device for ifindex %u\n", ifindex); 4762306a36Sopenharmony_ci return NULL; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci nd = ncsi_find_dev(dev); 5162306a36Sopenharmony_ci ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci dev_put(dev); 5462306a36Sopenharmony_ci return ndp; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int ncsi_write_channel_info(struct sk_buff *skb, 5862306a36Sopenharmony_ci struct ncsi_dev_priv *ndp, 5962306a36Sopenharmony_ci struct ncsi_channel *nc) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct ncsi_channel_vlan_filter *ncf; 6262306a36Sopenharmony_ci struct ncsi_channel_mode *m; 6362306a36Sopenharmony_ci struct nlattr *vid_nest; 6462306a36Sopenharmony_ci int i; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id); 6762306a36Sopenharmony_ci m = &nc->modes[NCSI_MODE_LINK]; 6862306a36Sopenharmony_ci nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]); 6962306a36Sopenharmony_ci if (nc->state == NCSI_CHANNEL_ACTIVE) 7062306a36Sopenharmony_ci nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE); 7162306a36Sopenharmony_ci if (nc == nc->package->preferred_channel) 7262306a36Sopenharmony_ci nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.major); 7562306a36Sopenharmony_ci nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MINOR, nc->version.minor); 7662306a36Sopenharmony_ci nla_put_string(skb, NCSI_CHANNEL_ATTR_VERSION_STR, nc->version.fw_name); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci vid_nest = nla_nest_start_noflag(skb, NCSI_CHANNEL_ATTR_VLAN_LIST); 7962306a36Sopenharmony_ci if (!vid_nest) 8062306a36Sopenharmony_ci return -ENOMEM; 8162306a36Sopenharmony_ci ncf = &nc->vlan_filter; 8262306a36Sopenharmony_ci i = -1; 8362306a36Sopenharmony_ci while ((i = find_next_bit((void *)&ncf->bitmap, ncf->n_vids, 8462306a36Sopenharmony_ci i + 1)) < ncf->n_vids) { 8562306a36Sopenharmony_ci if (ncf->vids[i]) 8662306a36Sopenharmony_ci nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID, 8762306a36Sopenharmony_ci ncf->vids[i]); 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci nla_nest_end(skb, vid_nest); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic int ncsi_write_package_info(struct sk_buff *skb, 9562306a36Sopenharmony_ci struct ncsi_dev_priv *ndp, unsigned int id) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct nlattr *pnest, *cnest, *nest; 9862306a36Sopenharmony_ci struct ncsi_package *np; 9962306a36Sopenharmony_ci struct ncsi_channel *nc; 10062306a36Sopenharmony_ci bool found; 10162306a36Sopenharmony_ci int rc; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (id > ndp->package_num - 1) { 10462306a36Sopenharmony_ci netdev_info(ndp->ndev.dev, "NCSI: No package with id %u\n", id); 10562306a36Sopenharmony_ci return -ENODEV; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci found = false; 10962306a36Sopenharmony_ci NCSI_FOR_EACH_PACKAGE(ndp, np) { 11062306a36Sopenharmony_ci if (np->id != id) 11162306a36Sopenharmony_ci continue; 11262306a36Sopenharmony_ci pnest = nla_nest_start_noflag(skb, NCSI_PKG_ATTR); 11362306a36Sopenharmony_ci if (!pnest) 11462306a36Sopenharmony_ci return -ENOMEM; 11562306a36Sopenharmony_ci rc = nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id); 11662306a36Sopenharmony_ci if (rc) { 11762306a36Sopenharmony_ci nla_nest_cancel(skb, pnest); 11862306a36Sopenharmony_ci return rc; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci if ((0x1 << np->id) == ndp->package_whitelist) 12162306a36Sopenharmony_ci nla_put_flag(skb, NCSI_PKG_ATTR_FORCED); 12262306a36Sopenharmony_ci cnest = nla_nest_start_noflag(skb, NCSI_PKG_ATTR_CHANNEL_LIST); 12362306a36Sopenharmony_ci if (!cnest) { 12462306a36Sopenharmony_ci nla_nest_cancel(skb, pnest); 12562306a36Sopenharmony_ci return -ENOMEM; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci NCSI_FOR_EACH_CHANNEL(np, nc) { 12862306a36Sopenharmony_ci nest = nla_nest_start_noflag(skb, NCSI_CHANNEL_ATTR); 12962306a36Sopenharmony_ci if (!nest) { 13062306a36Sopenharmony_ci nla_nest_cancel(skb, cnest); 13162306a36Sopenharmony_ci nla_nest_cancel(skb, pnest); 13262306a36Sopenharmony_ci return -ENOMEM; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci rc = ncsi_write_channel_info(skb, ndp, nc); 13562306a36Sopenharmony_ci if (rc) { 13662306a36Sopenharmony_ci nla_nest_cancel(skb, nest); 13762306a36Sopenharmony_ci nla_nest_cancel(skb, cnest); 13862306a36Sopenharmony_ci nla_nest_cancel(skb, pnest); 13962306a36Sopenharmony_ci return rc; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci nla_nest_end(skb, nest); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci nla_nest_end(skb, cnest); 14462306a36Sopenharmony_ci nla_nest_end(skb, pnest); 14562306a36Sopenharmony_ci found = true; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (!found) 14962306a36Sopenharmony_ci return -ENODEV; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct ncsi_dev_priv *ndp; 15762306a36Sopenharmony_ci unsigned int package_id; 15862306a36Sopenharmony_ci struct sk_buff *skb; 15962306a36Sopenharmony_ci struct nlattr *attr; 16062306a36Sopenharmony_ci void *hdr; 16162306a36Sopenharmony_ci int rc; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (!info || !info->attrs) 16462306a36Sopenharmony_ci return -EINVAL; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (!info->attrs[NCSI_ATTR_IFINDEX]) 16762306a36Sopenharmony_ci return -EINVAL; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) 17062306a36Sopenharmony_ci return -EINVAL; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci ndp = ndp_from_ifindex(genl_info_net(info), 17362306a36Sopenharmony_ci nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 17462306a36Sopenharmony_ci if (!ndp) 17562306a36Sopenharmony_ci return -ENODEV; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 17862306a36Sopenharmony_ci if (!skb) 17962306a36Sopenharmony_ci return -ENOMEM; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq, 18262306a36Sopenharmony_ci &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO); 18362306a36Sopenharmony_ci if (!hdr) { 18462306a36Sopenharmony_ci kfree_skb(skb); 18562306a36Sopenharmony_ci return -EMSGSIZE; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci attr = nla_nest_start_noflag(skb, NCSI_ATTR_PACKAGE_LIST); 19162306a36Sopenharmony_ci if (!attr) { 19262306a36Sopenharmony_ci kfree_skb(skb); 19362306a36Sopenharmony_ci return -EMSGSIZE; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci rc = ncsi_write_package_info(skb, ndp, package_id); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (rc) { 19862306a36Sopenharmony_ci nla_nest_cancel(skb, attr); 19962306a36Sopenharmony_ci goto err; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci nla_nest_end(skb, attr); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci genlmsg_end(skb, hdr); 20562306a36Sopenharmony_ci return genlmsg_reply(skb, info); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cierr: 20862306a36Sopenharmony_ci kfree_skb(skb); 20962306a36Sopenharmony_ci return rc; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int ncsi_pkg_info_all_nl(struct sk_buff *skb, 21362306a36Sopenharmony_ci struct netlink_callback *cb) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct nlattr *attrs[NCSI_ATTR_MAX + 1]; 21662306a36Sopenharmony_ci struct ncsi_package *np, *package; 21762306a36Sopenharmony_ci struct ncsi_dev_priv *ndp; 21862306a36Sopenharmony_ci unsigned int package_id; 21962306a36Sopenharmony_ci struct nlattr *attr; 22062306a36Sopenharmony_ci void *hdr; 22162306a36Sopenharmony_ci int rc; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci rc = genlmsg_parse_deprecated(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX, 22462306a36Sopenharmony_ci ncsi_genl_policy, NULL); 22562306a36Sopenharmony_ci if (rc) 22662306a36Sopenharmony_ci return rc; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (!attrs[NCSI_ATTR_IFINDEX]) 22962306a36Sopenharmony_ci return -EINVAL; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)), 23262306a36Sopenharmony_ci nla_get_u32(attrs[NCSI_ATTR_IFINDEX])); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (!ndp) 23562306a36Sopenharmony_ci return -ENODEV; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci package_id = cb->args[0]; 23862306a36Sopenharmony_ci package = NULL; 23962306a36Sopenharmony_ci NCSI_FOR_EACH_PACKAGE(ndp, np) 24062306a36Sopenharmony_ci if (np->id == package_id) 24162306a36Sopenharmony_ci package = np; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (!package) 24462306a36Sopenharmony_ci return 0; /* done */ 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 24762306a36Sopenharmony_ci &ncsi_genl_family, NLM_F_MULTI, NCSI_CMD_PKG_INFO); 24862306a36Sopenharmony_ci if (!hdr) { 24962306a36Sopenharmony_ci rc = -EMSGSIZE; 25062306a36Sopenharmony_ci goto err; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci attr = nla_nest_start_noflag(skb, NCSI_ATTR_PACKAGE_LIST); 25462306a36Sopenharmony_ci if (!attr) { 25562306a36Sopenharmony_ci rc = -EMSGSIZE; 25662306a36Sopenharmony_ci goto err; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci rc = ncsi_write_package_info(skb, ndp, package->id); 25962306a36Sopenharmony_ci if (rc) { 26062306a36Sopenharmony_ci nla_nest_cancel(skb, attr); 26162306a36Sopenharmony_ci goto err; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci nla_nest_end(skb, attr); 26562306a36Sopenharmony_ci genlmsg_end(skb, hdr); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci cb->args[0] = package_id + 1; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return skb->len; 27062306a36Sopenharmony_cierr: 27162306a36Sopenharmony_ci genlmsg_cancel(skb, hdr); 27262306a36Sopenharmony_ci return rc; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct ncsi_package *np, *package; 27862306a36Sopenharmony_ci struct ncsi_channel *nc, *channel; 27962306a36Sopenharmony_ci u32 package_id, channel_id; 28062306a36Sopenharmony_ci struct ncsi_dev_priv *ndp; 28162306a36Sopenharmony_ci unsigned long flags; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (!info || !info->attrs) 28462306a36Sopenharmony_ci return -EINVAL; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (!info->attrs[NCSI_ATTR_IFINDEX]) 28762306a36Sopenharmony_ci return -EINVAL; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) 29062306a36Sopenharmony_ci return -EINVAL; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 29362306a36Sopenharmony_ci nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 29462306a36Sopenharmony_ci if (!ndp) 29562306a36Sopenharmony_ci return -ENODEV; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 29862306a36Sopenharmony_ci package = NULL; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci NCSI_FOR_EACH_PACKAGE(ndp, np) 30162306a36Sopenharmony_ci if (np->id == package_id) 30262306a36Sopenharmony_ci package = np; 30362306a36Sopenharmony_ci if (!package) { 30462306a36Sopenharmony_ci /* The user has set a package that does not exist */ 30562306a36Sopenharmony_ci return -ERANGE; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci channel = NULL; 30962306a36Sopenharmony_ci if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { 31062306a36Sopenharmony_ci channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); 31162306a36Sopenharmony_ci NCSI_FOR_EACH_CHANNEL(package, nc) 31262306a36Sopenharmony_ci if (nc->id == channel_id) { 31362306a36Sopenharmony_ci channel = nc; 31462306a36Sopenharmony_ci break; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci if (!channel) { 31762306a36Sopenharmony_ci netdev_info(ndp->ndev.dev, 31862306a36Sopenharmony_ci "NCSI: Channel %u does not exist!\n", 31962306a36Sopenharmony_ci channel_id); 32062306a36Sopenharmony_ci return -ERANGE; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci spin_lock_irqsave(&ndp->lock, flags); 32562306a36Sopenharmony_ci ndp->package_whitelist = 0x1 << package->id; 32662306a36Sopenharmony_ci ndp->multi_package = false; 32762306a36Sopenharmony_ci spin_unlock_irqrestore(&ndp->lock, flags); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci spin_lock_irqsave(&package->lock, flags); 33062306a36Sopenharmony_ci package->multi_channel = false; 33162306a36Sopenharmony_ci if (channel) { 33262306a36Sopenharmony_ci package->channel_whitelist = 0x1 << channel->id; 33362306a36Sopenharmony_ci package->preferred_channel = channel; 33462306a36Sopenharmony_ci } else { 33562306a36Sopenharmony_ci /* Allow any channel */ 33662306a36Sopenharmony_ci package->channel_whitelist = UINT_MAX; 33762306a36Sopenharmony_ci package->preferred_channel = NULL; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci spin_unlock_irqrestore(&package->lock, flags); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (channel) 34262306a36Sopenharmony_ci netdev_info(ndp->ndev.dev, 34362306a36Sopenharmony_ci "Set package 0x%x, channel 0x%x as preferred\n", 34462306a36Sopenharmony_ci package_id, channel_id); 34562306a36Sopenharmony_ci else 34662306a36Sopenharmony_ci netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n", 34762306a36Sopenharmony_ci package_id); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* Update channel configuration */ 35062306a36Sopenharmony_ci if (!(ndp->flags & NCSI_DEV_RESET)) 35162306a36Sopenharmony_ci ncsi_reset_dev(&ndp->ndev); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct ncsi_dev_priv *ndp; 35962306a36Sopenharmony_ci struct ncsi_package *np; 36062306a36Sopenharmony_ci unsigned long flags; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (!info || !info->attrs) 36362306a36Sopenharmony_ci return -EINVAL; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (!info->attrs[NCSI_ATTR_IFINDEX]) 36662306a36Sopenharmony_ci return -EINVAL; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 36962306a36Sopenharmony_ci nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 37062306a36Sopenharmony_ci if (!ndp) 37162306a36Sopenharmony_ci return -ENODEV; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* Reset any whitelists and disable multi mode */ 37462306a36Sopenharmony_ci spin_lock_irqsave(&ndp->lock, flags); 37562306a36Sopenharmony_ci ndp->package_whitelist = UINT_MAX; 37662306a36Sopenharmony_ci ndp->multi_package = false; 37762306a36Sopenharmony_ci spin_unlock_irqrestore(&ndp->lock, flags); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci NCSI_FOR_EACH_PACKAGE(ndp, np) { 38062306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 38162306a36Sopenharmony_ci np->multi_channel = false; 38262306a36Sopenharmony_ci np->channel_whitelist = UINT_MAX; 38362306a36Sopenharmony_ci np->preferred_channel = NULL; 38462306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n"); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Update channel configuration */ 38962306a36Sopenharmony_ci if (!(ndp->flags & NCSI_DEV_RESET)) 39062306a36Sopenharmony_ci ncsi_reset_dev(&ndp->ndev); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return 0; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct ncsi_dev_priv *ndp; 39862306a36Sopenharmony_ci struct ncsi_pkt_hdr *hdr; 39962306a36Sopenharmony_ci struct ncsi_cmd_arg nca; 40062306a36Sopenharmony_ci unsigned char *data; 40162306a36Sopenharmony_ci u32 package_id; 40262306a36Sopenharmony_ci u32 channel_id; 40362306a36Sopenharmony_ci int len, ret; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!info || !info->attrs) { 40662306a36Sopenharmony_ci ret = -EINVAL; 40762306a36Sopenharmony_ci goto out; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (!info->attrs[NCSI_ATTR_IFINDEX]) { 41162306a36Sopenharmony_ci ret = -EINVAL; 41262306a36Sopenharmony_ci goto out; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) { 41662306a36Sopenharmony_ci ret = -EINVAL; 41762306a36Sopenharmony_ci goto out; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { 42162306a36Sopenharmony_ci ret = -EINVAL; 42262306a36Sopenharmony_ci goto out; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (!info->attrs[NCSI_ATTR_DATA]) { 42662306a36Sopenharmony_ci ret = -EINVAL; 42762306a36Sopenharmony_ci goto out; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 43162306a36Sopenharmony_ci nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 43262306a36Sopenharmony_ci if (!ndp) { 43362306a36Sopenharmony_ci ret = -ENODEV; 43462306a36Sopenharmony_ci goto out; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 43862306a36Sopenharmony_ci channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (package_id >= NCSI_MAX_PACKAGE || channel_id >= NCSI_MAX_CHANNEL) { 44162306a36Sopenharmony_ci ret = -ERANGE; 44262306a36Sopenharmony_ci goto out_netlink; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci len = nla_len(info->attrs[NCSI_ATTR_DATA]); 44662306a36Sopenharmony_ci if (len < sizeof(struct ncsi_pkt_hdr)) { 44762306a36Sopenharmony_ci netdev_info(ndp->ndev.dev, "NCSI: no command to send %u\n", 44862306a36Sopenharmony_ci package_id); 44962306a36Sopenharmony_ci ret = -EINVAL; 45062306a36Sopenharmony_ci goto out_netlink; 45162306a36Sopenharmony_ci } else { 45262306a36Sopenharmony_ci data = (unsigned char *)nla_data(info->attrs[NCSI_ATTR_DATA]); 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci hdr = (struct ncsi_pkt_hdr *)data; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci nca.ndp = ndp; 45862306a36Sopenharmony_ci nca.package = (unsigned char)package_id; 45962306a36Sopenharmony_ci nca.channel = (unsigned char)channel_id; 46062306a36Sopenharmony_ci nca.type = hdr->type; 46162306a36Sopenharmony_ci nca.req_flags = NCSI_REQ_FLAG_NETLINK_DRIVEN; 46262306a36Sopenharmony_ci nca.info = info; 46362306a36Sopenharmony_ci nca.payload = ntohs(hdr->length); 46462306a36Sopenharmony_ci nca.data = data + sizeof(*hdr); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci ret = ncsi_xmit_cmd(&nca); 46762306a36Sopenharmony_ciout_netlink: 46862306a36Sopenharmony_ci if (ret != 0) { 46962306a36Sopenharmony_ci netdev_err(ndp->ndev.dev, 47062306a36Sopenharmony_ci "NCSI: Error %d sending command\n", 47162306a36Sopenharmony_ci ret); 47262306a36Sopenharmony_ci ncsi_send_netlink_err(ndp->ndev.dev, 47362306a36Sopenharmony_ci info->snd_seq, 47462306a36Sopenharmony_ci info->snd_portid, 47562306a36Sopenharmony_ci info->nlhdr, 47662306a36Sopenharmony_ci ret); 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ciout: 47962306a36Sopenharmony_ci return ret; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ciint ncsi_send_netlink_rsp(struct ncsi_request *nr, 48362306a36Sopenharmony_ci struct ncsi_package *np, 48462306a36Sopenharmony_ci struct ncsi_channel *nc) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct sk_buff *skb; 48762306a36Sopenharmony_ci struct net *net; 48862306a36Sopenharmony_ci void *hdr; 48962306a36Sopenharmony_ci int rc; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci net = dev_net(nr->rsp->dev); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 49462306a36Sopenharmony_ci if (!skb) 49562306a36Sopenharmony_ci return -ENOMEM; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq, 49862306a36Sopenharmony_ci &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD); 49962306a36Sopenharmony_ci if (!hdr) { 50062306a36Sopenharmony_ci kfree_skb(skb); 50162306a36Sopenharmony_ci return -EMSGSIZE; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->rsp->dev->ifindex); 50562306a36Sopenharmony_ci if (np) 50662306a36Sopenharmony_ci nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id); 50762306a36Sopenharmony_ci if (nc) 50862306a36Sopenharmony_ci nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id); 50962306a36Sopenharmony_ci else 51062306a36Sopenharmony_ci nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci rc = nla_put(skb, NCSI_ATTR_DATA, nr->rsp->len, (void *)nr->rsp->data); 51362306a36Sopenharmony_ci if (rc) 51462306a36Sopenharmony_ci goto err; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci genlmsg_end(skb, hdr); 51762306a36Sopenharmony_ci return genlmsg_unicast(net, skb, nr->snd_portid); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cierr: 52062306a36Sopenharmony_ci kfree_skb(skb); 52162306a36Sopenharmony_ci return rc; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ciint ncsi_send_netlink_timeout(struct ncsi_request *nr, 52562306a36Sopenharmony_ci struct ncsi_package *np, 52662306a36Sopenharmony_ci struct ncsi_channel *nc) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct sk_buff *skb; 52962306a36Sopenharmony_ci struct net *net; 53062306a36Sopenharmony_ci void *hdr; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 53362306a36Sopenharmony_ci if (!skb) 53462306a36Sopenharmony_ci return -ENOMEM; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq, 53762306a36Sopenharmony_ci &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD); 53862306a36Sopenharmony_ci if (!hdr) { 53962306a36Sopenharmony_ci kfree_skb(skb); 54062306a36Sopenharmony_ci return -EMSGSIZE; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci net = dev_net(nr->cmd->dev); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->cmd->dev->ifindex); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (np) 54862306a36Sopenharmony_ci nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id); 54962306a36Sopenharmony_ci else 55062306a36Sopenharmony_ci nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, 55162306a36Sopenharmony_ci NCSI_PACKAGE_INDEX((((struct ncsi_pkt_hdr *) 55262306a36Sopenharmony_ci nr->cmd->data)->channel))); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (nc) 55562306a36Sopenharmony_ci nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id); 55662306a36Sopenharmony_ci else 55762306a36Sopenharmony_ci nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci genlmsg_end(skb, hdr); 56062306a36Sopenharmony_ci return genlmsg_unicast(net, skb, nr->snd_portid); 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ciint ncsi_send_netlink_err(struct net_device *dev, 56462306a36Sopenharmony_ci u32 snd_seq, 56562306a36Sopenharmony_ci u32 snd_portid, 56662306a36Sopenharmony_ci const struct nlmsghdr *nlhdr, 56762306a36Sopenharmony_ci int err) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct nlmsghdr *nlh; 57062306a36Sopenharmony_ci struct nlmsgerr *nle; 57162306a36Sopenharmony_ci struct sk_buff *skb; 57262306a36Sopenharmony_ci struct net *net; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 57562306a36Sopenharmony_ci if (!skb) 57662306a36Sopenharmony_ci return -ENOMEM; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci net = dev_net(dev); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci nlh = nlmsg_put(skb, snd_portid, snd_seq, 58162306a36Sopenharmony_ci NLMSG_ERROR, sizeof(*nle), 0); 58262306a36Sopenharmony_ci nle = (struct nlmsgerr *)nlmsg_data(nlh); 58362306a36Sopenharmony_ci nle->error = err; 58462306a36Sopenharmony_ci memcpy(&nle->msg, nlhdr, sizeof(*nlh)); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci nlmsg_end(skb, nlh); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return nlmsg_unicast(net->genl_sock, skb, snd_portid); 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic int ncsi_set_package_mask_nl(struct sk_buff *msg, 59262306a36Sopenharmony_ci struct genl_info *info) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct ncsi_dev_priv *ndp; 59562306a36Sopenharmony_ci unsigned long flags; 59662306a36Sopenharmony_ci int rc; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (!info || !info->attrs) 59962306a36Sopenharmony_ci return -EINVAL; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (!info->attrs[NCSI_ATTR_IFINDEX]) 60262306a36Sopenharmony_ci return -EINVAL; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (!info->attrs[NCSI_ATTR_PACKAGE_MASK]) 60562306a36Sopenharmony_ci return -EINVAL; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 60862306a36Sopenharmony_ci nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 60962306a36Sopenharmony_ci if (!ndp) 61062306a36Sopenharmony_ci return -ENODEV; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci spin_lock_irqsave(&ndp->lock, flags); 61362306a36Sopenharmony_ci if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { 61462306a36Sopenharmony_ci if (ndp->flags & NCSI_DEV_HWA) { 61562306a36Sopenharmony_ci ndp->multi_package = true; 61662306a36Sopenharmony_ci rc = 0; 61762306a36Sopenharmony_ci } else { 61862306a36Sopenharmony_ci netdev_err(ndp->ndev.dev, 61962306a36Sopenharmony_ci "NCSI: Can't use multiple packages without HWA\n"); 62062306a36Sopenharmony_ci rc = -EPERM; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci } else { 62362306a36Sopenharmony_ci ndp->multi_package = false; 62462306a36Sopenharmony_ci rc = 0; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (!rc) 62862306a36Sopenharmony_ci ndp->package_whitelist = 62962306a36Sopenharmony_ci nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]); 63062306a36Sopenharmony_ci spin_unlock_irqrestore(&ndp->lock, flags); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (!rc) { 63362306a36Sopenharmony_ci /* Update channel configuration */ 63462306a36Sopenharmony_ci if (!(ndp->flags & NCSI_DEV_RESET)) 63562306a36Sopenharmony_ci ncsi_reset_dev(&ndp->ndev); 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci return rc; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic int ncsi_set_channel_mask_nl(struct sk_buff *msg, 64262306a36Sopenharmony_ci struct genl_info *info) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct ncsi_package *np, *package; 64562306a36Sopenharmony_ci struct ncsi_channel *nc, *channel; 64662306a36Sopenharmony_ci u32 package_id, channel_id; 64762306a36Sopenharmony_ci struct ncsi_dev_priv *ndp; 64862306a36Sopenharmony_ci unsigned long flags; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (!info || !info->attrs) 65162306a36Sopenharmony_ci return -EINVAL; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (!info->attrs[NCSI_ATTR_IFINDEX]) 65462306a36Sopenharmony_ci return -EINVAL; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) 65762306a36Sopenharmony_ci return -EINVAL; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (!info->attrs[NCSI_ATTR_CHANNEL_MASK]) 66062306a36Sopenharmony_ci return -EINVAL; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 66362306a36Sopenharmony_ci nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 66462306a36Sopenharmony_ci if (!ndp) 66562306a36Sopenharmony_ci return -ENODEV; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 66862306a36Sopenharmony_ci package = NULL; 66962306a36Sopenharmony_ci NCSI_FOR_EACH_PACKAGE(ndp, np) 67062306a36Sopenharmony_ci if (np->id == package_id) { 67162306a36Sopenharmony_ci package = np; 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci if (!package) 67562306a36Sopenharmony_ci return -ERANGE; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci spin_lock_irqsave(&package->lock, flags); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci channel = NULL; 68062306a36Sopenharmony_ci if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { 68162306a36Sopenharmony_ci channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); 68262306a36Sopenharmony_ci NCSI_FOR_EACH_CHANNEL(np, nc) 68362306a36Sopenharmony_ci if (nc->id == channel_id) { 68462306a36Sopenharmony_ci channel = nc; 68562306a36Sopenharmony_ci break; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci if (!channel) { 68862306a36Sopenharmony_ci spin_unlock_irqrestore(&package->lock, flags); 68962306a36Sopenharmony_ci return -ERANGE; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci netdev_dbg(ndp->ndev.dev, 69262306a36Sopenharmony_ci "NCSI: Channel %u set as preferred channel\n", 69362306a36Sopenharmony_ci channel->id); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci package->channel_whitelist = 69762306a36Sopenharmony_ci nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]); 69862306a36Sopenharmony_ci if (package->channel_whitelist == 0) 69962306a36Sopenharmony_ci netdev_dbg(ndp->ndev.dev, 70062306a36Sopenharmony_ci "NCSI: Package %u set to all channels disabled\n", 70162306a36Sopenharmony_ci package->id); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci package->preferred_channel = channel; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { 70662306a36Sopenharmony_ci package->multi_channel = true; 70762306a36Sopenharmony_ci netdev_info(ndp->ndev.dev, 70862306a36Sopenharmony_ci "NCSI: Multi-channel enabled on package %u\n", 70962306a36Sopenharmony_ci package_id); 71062306a36Sopenharmony_ci } else { 71162306a36Sopenharmony_ci package->multi_channel = false; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci spin_unlock_irqrestore(&package->lock, flags); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* Update channel configuration */ 71762306a36Sopenharmony_ci if (!(ndp->flags & NCSI_DEV_RESET)) 71862306a36Sopenharmony_ci ncsi_reset_dev(&ndp->ndev); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci return 0; 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic const struct genl_small_ops ncsi_ops[] = { 72462306a36Sopenharmony_ci { 72562306a36Sopenharmony_ci .cmd = NCSI_CMD_PKG_INFO, 72662306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 72762306a36Sopenharmony_ci .doit = ncsi_pkg_info_nl, 72862306a36Sopenharmony_ci .dumpit = ncsi_pkg_info_all_nl, 72962306a36Sopenharmony_ci .flags = 0, 73062306a36Sopenharmony_ci }, 73162306a36Sopenharmony_ci { 73262306a36Sopenharmony_ci .cmd = NCSI_CMD_SET_INTERFACE, 73362306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 73462306a36Sopenharmony_ci .doit = ncsi_set_interface_nl, 73562306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 73662306a36Sopenharmony_ci }, 73762306a36Sopenharmony_ci { 73862306a36Sopenharmony_ci .cmd = NCSI_CMD_CLEAR_INTERFACE, 73962306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 74062306a36Sopenharmony_ci .doit = ncsi_clear_interface_nl, 74162306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 74262306a36Sopenharmony_ci }, 74362306a36Sopenharmony_ci { 74462306a36Sopenharmony_ci .cmd = NCSI_CMD_SEND_CMD, 74562306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 74662306a36Sopenharmony_ci .doit = ncsi_send_cmd_nl, 74762306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 74862306a36Sopenharmony_ci }, 74962306a36Sopenharmony_ci { 75062306a36Sopenharmony_ci .cmd = NCSI_CMD_SET_PACKAGE_MASK, 75162306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 75262306a36Sopenharmony_ci .doit = ncsi_set_package_mask_nl, 75362306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 75462306a36Sopenharmony_ci }, 75562306a36Sopenharmony_ci { 75662306a36Sopenharmony_ci .cmd = NCSI_CMD_SET_CHANNEL_MASK, 75762306a36Sopenharmony_ci .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 75862306a36Sopenharmony_ci .doit = ncsi_set_channel_mask_nl, 75962306a36Sopenharmony_ci .flags = GENL_ADMIN_PERM, 76062306a36Sopenharmony_ci }, 76162306a36Sopenharmony_ci}; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic struct genl_family ncsi_genl_family __ro_after_init = { 76462306a36Sopenharmony_ci .name = "NCSI", 76562306a36Sopenharmony_ci .version = 0, 76662306a36Sopenharmony_ci .maxattr = NCSI_ATTR_MAX, 76762306a36Sopenharmony_ci .policy = ncsi_genl_policy, 76862306a36Sopenharmony_ci .module = THIS_MODULE, 76962306a36Sopenharmony_ci .small_ops = ncsi_ops, 77062306a36Sopenharmony_ci .n_small_ops = ARRAY_SIZE(ncsi_ops), 77162306a36Sopenharmony_ci .resv_start_op = NCSI_CMD_SET_CHANNEL_MASK + 1, 77262306a36Sopenharmony_ci}; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic int __init ncsi_init_netlink(void) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci return genl_register_family(&ncsi_genl_family); 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_cisubsys_initcall(ncsi_init_netlink); 779