12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2955dc68cSSamuel Mendoza-Jonas /* 3955dc68cSSamuel Mendoza-Jonas * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018. 4955dc68cSSamuel Mendoza-Jonas */ 5955dc68cSSamuel Mendoza-Jonas 6955dc68cSSamuel Mendoza-Jonas #include <linux/module.h> 7955dc68cSSamuel Mendoza-Jonas #include <linux/kernel.h> 8955dc68cSSamuel Mendoza-Jonas #include <linux/if_arp.h> 9955dc68cSSamuel Mendoza-Jonas #include <linux/rtnetlink.h> 10955dc68cSSamuel Mendoza-Jonas #include <linux/etherdevice.h> 11955dc68cSSamuel Mendoza-Jonas #include <net/genetlink.h> 12955dc68cSSamuel Mendoza-Jonas #include <net/ncsi.h> 13955dc68cSSamuel Mendoza-Jonas #include <linux/skbuff.h> 14955dc68cSSamuel Mendoza-Jonas #include <net/sock.h> 15955dc68cSSamuel Mendoza-Jonas #include <uapi/linux/ncsi.h> 16955dc68cSSamuel Mendoza-Jonas 17955dc68cSSamuel Mendoza-Jonas #include "internal.h" 189771b8ccSJustin.Lee1@Dell.com #include "ncsi-pkt.h" 19955dc68cSSamuel Mendoza-Jonas #include "ncsi-netlink.h" 20955dc68cSSamuel Mendoza-Jonas 21955dc68cSSamuel Mendoza-Jonas static struct genl_family ncsi_genl_family; 22955dc68cSSamuel Mendoza-Jonas 23955dc68cSSamuel Mendoza-Jonas static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = { 24955dc68cSSamuel Mendoza-Jonas [NCSI_ATTR_IFINDEX] = { .type = NLA_U32 }, 25955dc68cSSamuel Mendoza-Jonas [NCSI_ATTR_PACKAGE_LIST] = { .type = NLA_NESTED }, 26955dc68cSSamuel Mendoza-Jonas [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, 27955dc68cSSamuel Mendoza-Jonas [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, 289771b8ccSJustin.Lee1@Dell.com [NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 }, 298d951a75SSamuel Mendoza-Jonas [NCSI_ATTR_MULTI_FLAG] = { .type = NLA_FLAG }, 308d951a75SSamuel Mendoza-Jonas [NCSI_ATTR_PACKAGE_MASK] = { .type = NLA_U32 }, 318d951a75SSamuel Mendoza-Jonas [NCSI_ATTR_CHANNEL_MASK] = { .type = NLA_U32 }, 32955dc68cSSamuel Mendoza-Jonas }; 33955dc68cSSamuel Mendoza-Jonas 34955dc68cSSamuel Mendoza-Jonas static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) 35955dc68cSSamuel Mendoza-Jonas { 36955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 37955dc68cSSamuel Mendoza-Jonas struct net_device *dev; 38955dc68cSSamuel Mendoza-Jonas struct ncsi_dev *nd; 39955dc68cSSamuel Mendoza-Jonas struct ncsi_dev; 40955dc68cSSamuel Mendoza-Jonas 41955dc68cSSamuel Mendoza-Jonas if (!net) 42955dc68cSSamuel Mendoza-Jonas return NULL; 43955dc68cSSamuel Mendoza-Jonas 44955dc68cSSamuel Mendoza-Jonas dev = dev_get_by_index(net, ifindex); 45955dc68cSSamuel Mendoza-Jonas if (!dev) { 46955dc68cSSamuel Mendoza-Jonas pr_err("NCSI netlink: No device for ifindex %u\n", ifindex); 47955dc68cSSamuel Mendoza-Jonas return NULL; 48955dc68cSSamuel Mendoza-Jonas } 49955dc68cSSamuel Mendoza-Jonas 50955dc68cSSamuel Mendoza-Jonas nd = ncsi_find_dev(dev); 51955dc68cSSamuel Mendoza-Jonas ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL; 52955dc68cSSamuel Mendoza-Jonas 53955dc68cSSamuel Mendoza-Jonas dev_put(dev); 54955dc68cSSamuel Mendoza-Jonas return ndp; 55955dc68cSSamuel Mendoza-Jonas } 56955dc68cSSamuel Mendoza-Jonas 57955dc68cSSamuel Mendoza-Jonas static int ncsi_write_channel_info(struct sk_buff *skb, 58955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp, 59955dc68cSSamuel Mendoza-Jonas struct ncsi_channel *nc) 60955dc68cSSamuel Mendoza-Jonas { 61062b3e1bSSamuel Mendoza-Jonas struct ncsi_channel_vlan_filter *ncf; 62955dc68cSSamuel Mendoza-Jonas struct ncsi_channel_mode *m; 63062b3e1bSSamuel Mendoza-Jonas struct nlattr *vid_nest; 64955dc68cSSamuel Mendoza-Jonas int i; 65955dc68cSSamuel Mendoza-Jonas 66955dc68cSSamuel Mendoza-Jonas nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id); 67955dc68cSSamuel Mendoza-Jonas m = &nc->modes[NCSI_MODE_LINK]; 68955dc68cSSamuel Mendoza-Jonas nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]); 69955dc68cSSamuel Mendoza-Jonas if (nc->state == NCSI_CHANNEL_ACTIVE) 70955dc68cSSamuel Mendoza-Jonas nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE); 718d951a75SSamuel Mendoza-Jonas if (nc == nc->package->preferred_channel) 72955dc68cSSamuel Mendoza-Jonas nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED); 73955dc68cSSamuel Mendoza-Jonas 74955dc68cSSamuel Mendoza-Jonas nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version); 75955dc68cSSamuel Mendoza-Jonas nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MINOR, nc->version.alpha2); 76955dc68cSSamuel Mendoza-Jonas nla_put_string(skb, NCSI_CHANNEL_ATTR_VERSION_STR, nc->version.fw_name); 77955dc68cSSamuel Mendoza-Jonas 78ae0be8deSMichal Kubecek vid_nest = nla_nest_start_noflag(skb, NCSI_CHANNEL_ATTR_VLAN_LIST); 79955dc68cSSamuel Mendoza-Jonas if (!vid_nest) 80955dc68cSSamuel Mendoza-Jonas return -ENOMEM; 81062b3e1bSSamuel Mendoza-Jonas ncf = &nc->vlan_filter; 82955dc68cSSamuel Mendoza-Jonas i = -1; 83062b3e1bSSamuel Mendoza-Jonas while ((i = find_next_bit((void *)&ncf->bitmap, ncf->n_vids, 84062b3e1bSSamuel Mendoza-Jonas i + 1)) < ncf->n_vids) { 85062b3e1bSSamuel Mendoza-Jonas if (ncf->vids[i]) 86955dc68cSSamuel Mendoza-Jonas nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID, 87062b3e1bSSamuel Mendoza-Jonas ncf->vids[i]); 88955dc68cSSamuel Mendoza-Jonas } 89955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, vid_nest); 90955dc68cSSamuel Mendoza-Jonas 91955dc68cSSamuel Mendoza-Jonas return 0; 92955dc68cSSamuel Mendoza-Jonas } 93955dc68cSSamuel Mendoza-Jonas 94955dc68cSSamuel Mendoza-Jonas static int ncsi_write_package_info(struct sk_buff *skb, 95955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp, unsigned int id) 96955dc68cSSamuel Mendoza-Jonas { 97955dc68cSSamuel Mendoza-Jonas struct nlattr *pnest, *cnest, *nest; 98955dc68cSSamuel Mendoza-Jonas struct ncsi_package *np; 99955dc68cSSamuel Mendoza-Jonas struct ncsi_channel *nc; 100955dc68cSSamuel Mendoza-Jonas bool found; 101955dc68cSSamuel Mendoza-Jonas int rc; 102955dc68cSSamuel Mendoza-Jonas 1033d0371b3SSamuel Mendoza-Jonas if (id > ndp->package_num - 1) { 104955dc68cSSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, "NCSI: No package with id %u\n", id); 105955dc68cSSamuel Mendoza-Jonas return -ENODEV; 106955dc68cSSamuel Mendoza-Jonas } 107955dc68cSSamuel Mendoza-Jonas 108955dc68cSSamuel Mendoza-Jonas found = false; 109955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) { 110955dc68cSSamuel Mendoza-Jonas if (np->id != id) 111955dc68cSSamuel Mendoza-Jonas continue; 112ae0be8deSMichal Kubecek pnest = nla_nest_start_noflag(skb, NCSI_PKG_ATTR); 113955dc68cSSamuel Mendoza-Jonas if (!pnest) 114955dc68cSSamuel Mendoza-Jonas return -ENOMEM; 115955dc68cSSamuel Mendoza-Jonas nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id); 1168d951a75SSamuel Mendoza-Jonas if ((0x1 << np->id) == ndp->package_whitelist) 117955dc68cSSamuel Mendoza-Jonas nla_put_flag(skb, NCSI_PKG_ATTR_FORCED); 118ae0be8deSMichal Kubecek cnest = nla_nest_start_noflag(skb, NCSI_PKG_ATTR_CHANNEL_LIST); 119955dc68cSSamuel Mendoza-Jonas if (!cnest) { 120955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, pnest); 121955dc68cSSamuel Mendoza-Jonas return -ENOMEM; 122955dc68cSSamuel Mendoza-Jonas } 123955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(np, nc) { 124ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, NCSI_CHANNEL_ATTR); 125955dc68cSSamuel Mendoza-Jonas if (!nest) { 126955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, cnest); 127955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, pnest); 128955dc68cSSamuel Mendoza-Jonas return -ENOMEM; 129955dc68cSSamuel Mendoza-Jonas } 130955dc68cSSamuel Mendoza-Jonas rc = ncsi_write_channel_info(skb, ndp, nc); 131955dc68cSSamuel Mendoza-Jonas if (rc) { 132955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, nest); 133955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, cnest); 134955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, pnest); 135955dc68cSSamuel Mendoza-Jonas return rc; 136955dc68cSSamuel Mendoza-Jonas } 137955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, nest); 138955dc68cSSamuel Mendoza-Jonas } 139955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, cnest); 140955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, pnest); 141955dc68cSSamuel Mendoza-Jonas found = true; 142955dc68cSSamuel Mendoza-Jonas } 143955dc68cSSamuel Mendoza-Jonas 144955dc68cSSamuel Mendoza-Jonas if (!found) 145955dc68cSSamuel Mendoza-Jonas return -ENODEV; 146955dc68cSSamuel Mendoza-Jonas 147955dc68cSSamuel Mendoza-Jonas return 0; 148955dc68cSSamuel Mendoza-Jonas } 149955dc68cSSamuel Mendoza-Jonas 150955dc68cSSamuel Mendoza-Jonas static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info) 151955dc68cSSamuel Mendoza-Jonas { 152955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 153955dc68cSSamuel Mendoza-Jonas unsigned int package_id; 154955dc68cSSamuel Mendoza-Jonas struct sk_buff *skb; 155955dc68cSSamuel Mendoza-Jonas struct nlattr *attr; 156955dc68cSSamuel Mendoza-Jonas void *hdr; 157955dc68cSSamuel Mendoza-Jonas int rc; 158955dc68cSSamuel Mendoza-Jonas 159955dc68cSSamuel Mendoza-Jonas if (!info || !info->attrs) 160955dc68cSSamuel Mendoza-Jonas return -EINVAL; 161955dc68cSSamuel Mendoza-Jonas 162955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_IFINDEX]) 163955dc68cSSamuel Mendoza-Jonas return -EINVAL; 164955dc68cSSamuel Mendoza-Jonas 165955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) 166955dc68cSSamuel Mendoza-Jonas return -EINVAL; 167955dc68cSSamuel Mendoza-Jonas 168955dc68cSSamuel Mendoza-Jonas ndp = ndp_from_ifindex(genl_info_net(info), 169955dc68cSSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 170955dc68cSSamuel Mendoza-Jonas if (!ndp) 171955dc68cSSamuel Mendoza-Jonas return -ENODEV; 172955dc68cSSamuel Mendoza-Jonas 173955dc68cSSamuel Mendoza-Jonas skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 174955dc68cSSamuel Mendoza-Jonas if (!skb) 175955dc68cSSamuel Mendoza-Jonas return -ENOMEM; 176955dc68cSSamuel Mendoza-Jonas 177955dc68cSSamuel Mendoza-Jonas hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq, 178955dc68cSSamuel Mendoza-Jonas &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO); 179955dc68cSSamuel Mendoza-Jonas if (!hdr) { 18050db64b0SDan Carpenter kfree_skb(skb); 181955dc68cSSamuel Mendoza-Jonas return -EMSGSIZE; 182955dc68cSSamuel Mendoza-Jonas } 183955dc68cSSamuel Mendoza-Jonas 184955dc68cSSamuel Mendoza-Jonas package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 185955dc68cSSamuel Mendoza-Jonas 186ae0be8deSMichal Kubecek attr = nla_nest_start_noflag(skb, NCSI_ATTR_PACKAGE_LIST); 1878daf1a2dSColin Ian King if (!attr) { 1888daf1a2dSColin Ian King kfree_skb(skb); 1898daf1a2dSColin Ian King return -EMSGSIZE; 1908daf1a2dSColin Ian King } 191955dc68cSSamuel Mendoza-Jonas rc = ncsi_write_package_info(skb, ndp, package_id); 192955dc68cSSamuel Mendoza-Jonas 193955dc68cSSamuel Mendoza-Jonas if (rc) { 194955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, attr); 195955dc68cSSamuel Mendoza-Jonas goto err; 196955dc68cSSamuel Mendoza-Jonas } 197955dc68cSSamuel Mendoza-Jonas 198955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, attr); 199955dc68cSSamuel Mendoza-Jonas 200955dc68cSSamuel Mendoza-Jonas genlmsg_end(skb, hdr); 201955dc68cSSamuel Mendoza-Jonas return genlmsg_reply(skb, info); 202955dc68cSSamuel Mendoza-Jonas 203955dc68cSSamuel Mendoza-Jonas err: 20450db64b0SDan Carpenter kfree_skb(skb); 205955dc68cSSamuel Mendoza-Jonas return rc; 206955dc68cSSamuel Mendoza-Jonas } 207955dc68cSSamuel Mendoza-Jonas 208955dc68cSSamuel Mendoza-Jonas static int ncsi_pkg_info_all_nl(struct sk_buff *skb, 209955dc68cSSamuel Mendoza-Jonas struct netlink_callback *cb) 210955dc68cSSamuel Mendoza-Jonas { 2110f51f358SSamuel Mendoza-Jonas struct nlattr *attrs[NCSI_ATTR_MAX + 1]; 212955dc68cSSamuel Mendoza-Jonas struct ncsi_package *np, *package; 213955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 214955dc68cSSamuel Mendoza-Jonas unsigned int package_id; 215955dc68cSSamuel Mendoza-Jonas struct nlattr *attr; 216955dc68cSSamuel Mendoza-Jonas void *hdr; 217955dc68cSSamuel Mendoza-Jonas int rc; 218955dc68cSSamuel Mendoza-Jonas 2198cb08174SJohannes Berg rc = genlmsg_parse_deprecated(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX, 220955dc68cSSamuel Mendoza-Jonas ncsi_genl_policy, NULL); 221955dc68cSSamuel Mendoza-Jonas if (rc) 222955dc68cSSamuel Mendoza-Jonas return rc; 223955dc68cSSamuel Mendoza-Jonas 224955dc68cSSamuel Mendoza-Jonas if (!attrs[NCSI_ATTR_IFINDEX]) 225955dc68cSSamuel Mendoza-Jonas return -EINVAL; 226955dc68cSSamuel Mendoza-Jonas 227955dc68cSSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)), 228955dc68cSSamuel Mendoza-Jonas nla_get_u32(attrs[NCSI_ATTR_IFINDEX])); 229955dc68cSSamuel Mendoza-Jonas 230955dc68cSSamuel Mendoza-Jonas if (!ndp) 231955dc68cSSamuel Mendoza-Jonas return -ENODEV; 232955dc68cSSamuel Mendoza-Jonas 233955dc68cSSamuel Mendoza-Jonas package_id = cb->args[0]; 234955dc68cSSamuel Mendoza-Jonas package = NULL; 235955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) 236955dc68cSSamuel Mendoza-Jonas if (np->id == package_id) 237955dc68cSSamuel Mendoza-Jonas package = np; 238955dc68cSSamuel Mendoza-Jonas 239955dc68cSSamuel Mendoza-Jonas if (!package) 240955dc68cSSamuel Mendoza-Jonas return 0; /* done */ 241955dc68cSSamuel Mendoza-Jonas 242955dc68cSSamuel Mendoza-Jonas hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 2433d0371b3SSamuel Mendoza-Jonas &ncsi_genl_family, NLM_F_MULTI, NCSI_CMD_PKG_INFO); 244955dc68cSSamuel Mendoza-Jonas if (!hdr) { 245955dc68cSSamuel Mendoza-Jonas rc = -EMSGSIZE; 246955dc68cSSamuel Mendoza-Jonas goto err; 247955dc68cSSamuel Mendoza-Jonas } 248955dc68cSSamuel Mendoza-Jonas 249ae0be8deSMichal Kubecek attr = nla_nest_start_noflag(skb, NCSI_ATTR_PACKAGE_LIST); 25007660ca6SKangjie Lu if (!attr) { 25107660ca6SKangjie Lu rc = -EMSGSIZE; 25207660ca6SKangjie Lu goto err; 25307660ca6SKangjie Lu } 254955dc68cSSamuel Mendoza-Jonas rc = ncsi_write_package_info(skb, ndp, package->id); 255955dc68cSSamuel Mendoza-Jonas if (rc) { 256955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, attr); 257955dc68cSSamuel Mendoza-Jonas goto err; 258955dc68cSSamuel Mendoza-Jonas } 259955dc68cSSamuel Mendoza-Jonas 260955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, attr); 261955dc68cSSamuel Mendoza-Jonas genlmsg_end(skb, hdr); 262955dc68cSSamuel Mendoza-Jonas 263955dc68cSSamuel Mendoza-Jonas cb->args[0] = package_id + 1; 264955dc68cSSamuel Mendoza-Jonas 265955dc68cSSamuel Mendoza-Jonas return skb->len; 266955dc68cSSamuel Mendoza-Jonas err: 267955dc68cSSamuel Mendoza-Jonas genlmsg_cancel(skb, hdr); 268955dc68cSSamuel Mendoza-Jonas return rc; 269955dc68cSSamuel Mendoza-Jonas } 270955dc68cSSamuel Mendoza-Jonas 271955dc68cSSamuel Mendoza-Jonas static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info) 272955dc68cSSamuel Mendoza-Jonas { 273955dc68cSSamuel Mendoza-Jonas struct ncsi_package *np, *package; 274955dc68cSSamuel Mendoza-Jonas struct ncsi_channel *nc, *channel; 275955dc68cSSamuel Mendoza-Jonas u32 package_id, channel_id; 276955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 277955dc68cSSamuel Mendoza-Jonas unsigned long flags; 278955dc68cSSamuel Mendoza-Jonas 279955dc68cSSamuel Mendoza-Jonas if (!info || !info->attrs) 280955dc68cSSamuel Mendoza-Jonas return -EINVAL; 281955dc68cSSamuel Mendoza-Jonas 282955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_IFINDEX]) 283955dc68cSSamuel Mendoza-Jonas return -EINVAL; 284955dc68cSSamuel Mendoza-Jonas 285955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) 286955dc68cSSamuel Mendoza-Jonas return -EINVAL; 287955dc68cSSamuel Mendoza-Jonas 288955dc68cSSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 289955dc68cSSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 290955dc68cSSamuel Mendoza-Jonas if (!ndp) 291955dc68cSSamuel Mendoza-Jonas return -ENODEV; 292955dc68cSSamuel Mendoza-Jonas 293955dc68cSSamuel Mendoza-Jonas package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 294955dc68cSSamuel Mendoza-Jonas package = NULL; 295955dc68cSSamuel Mendoza-Jonas 296955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) 297955dc68cSSamuel Mendoza-Jonas if (np->id == package_id) 298955dc68cSSamuel Mendoza-Jonas package = np; 299955dc68cSSamuel Mendoza-Jonas if (!package) { 300955dc68cSSamuel Mendoza-Jonas /* The user has set a package that does not exist */ 301955dc68cSSamuel Mendoza-Jonas return -ERANGE; 302955dc68cSSamuel Mendoza-Jonas } 303955dc68cSSamuel Mendoza-Jonas 304955dc68cSSamuel Mendoza-Jonas channel = NULL; 3058d951a75SSamuel Mendoza-Jonas if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { 306955dc68cSSamuel Mendoza-Jonas channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); 307955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(package, nc) 3088d951a75SSamuel Mendoza-Jonas if (nc->id == channel_id) { 309955dc68cSSamuel Mendoza-Jonas channel = nc; 3108d951a75SSamuel Mendoza-Jonas break; 311955dc68cSSamuel Mendoza-Jonas } 3128d951a75SSamuel Mendoza-Jonas if (!channel) { 3138d951a75SSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, 3148d951a75SSamuel Mendoza-Jonas "NCSI: Channel %u does not exist!\n", 315955dc68cSSamuel Mendoza-Jonas channel_id); 316955dc68cSSamuel Mendoza-Jonas return -ERANGE; 317955dc68cSSamuel Mendoza-Jonas } 3188d951a75SSamuel Mendoza-Jonas } 319955dc68cSSamuel Mendoza-Jonas 3208d951a75SSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags); 3218d951a75SSamuel Mendoza-Jonas ndp->package_whitelist = 0x1 << package->id; 3228d951a75SSamuel Mendoza-Jonas ndp->multi_package = false; 323955dc68cSSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags); 324955dc68cSSamuel Mendoza-Jonas 3258d951a75SSamuel Mendoza-Jonas spin_lock_irqsave(&package->lock, flags); 3268d951a75SSamuel Mendoza-Jonas package->multi_channel = false; 3278d951a75SSamuel Mendoza-Jonas if (channel) { 3288d951a75SSamuel Mendoza-Jonas package->channel_whitelist = 0x1 << channel->id; 3298d951a75SSamuel Mendoza-Jonas package->preferred_channel = channel; 3308d951a75SSamuel Mendoza-Jonas } else { 3318d951a75SSamuel Mendoza-Jonas /* Allow any channel */ 3328d951a75SSamuel Mendoza-Jonas package->channel_whitelist = UINT_MAX; 3338d951a75SSamuel Mendoza-Jonas package->preferred_channel = NULL; 3348d951a75SSamuel Mendoza-Jonas } 3358d951a75SSamuel Mendoza-Jonas spin_unlock_irqrestore(&package->lock, flags); 3368d951a75SSamuel Mendoza-Jonas 3378d951a75SSamuel Mendoza-Jonas if (channel) 3388d951a75SSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, 3398d951a75SSamuel Mendoza-Jonas "Set package 0x%x, channel 0x%x as preferred\n", 3408d951a75SSamuel Mendoza-Jonas package_id, channel_id); 3418d951a75SSamuel Mendoza-Jonas else 3428d951a75SSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n", 3438d951a75SSamuel Mendoza-Jonas package_id); 344955dc68cSSamuel Mendoza-Jonas 3452878a2cfSSamuel Mendoza-Jonas /* Update channel configuration */ 3462878a2cfSSamuel Mendoza-Jonas if (!(ndp->flags & NCSI_DEV_RESET)) 3472878a2cfSSamuel Mendoza-Jonas ncsi_reset_dev(&ndp->ndev); 348955dc68cSSamuel Mendoza-Jonas 349955dc68cSSamuel Mendoza-Jonas return 0; 350955dc68cSSamuel Mendoza-Jonas } 351955dc68cSSamuel Mendoza-Jonas 352955dc68cSSamuel Mendoza-Jonas static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) 353955dc68cSSamuel Mendoza-Jonas { 354955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 3558d951a75SSamuel Mendoza-Jonas struct ncsi_package *np; 356955dc68cSSamuel Mendoza-Jonas unsigned long flags; 357955dc68cSSamuel Mendoza-Jonas 358955dc68cSSamuel Mendoza-Jonas if (!info || !info->attrs) 359955dc68cSSamuel Mendoza-Jonas return -EINVAL; 360955dc68cSSamuel Mendoza-Jonas 361955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_IFINDEX]) 362955dc68cSSamuel Mendoza-Jonas return -EINVAL; 363955dc68cSSamuel Mendoza-Jonas 364955dc68cSSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 365955dc68cSSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 366955dc68cSSamuel Mendoza-Jonas if (!ndp) 367955dc68cSSamuel Mendoza-Jonas return -ENODEV; 368955dc68cSSamuel Mendoza-Jonas 3698d951a75SSamuel Mendoza-Jonas /* Reset any whitelists and disable multi mode */ 370955dc68cSSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags); 3718d951a75SSamuel Mendoza-Jonas ndp->package_whitelist = UINT_MAX; 3728d951a75SSamuel Mendoza-Jonas ndp->multi_package = false; 373955dc68cSSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags); 3748d951a75SSamuel Mendoza-Jonas 3758d951a75SSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) { 3768d951a75SSamuel Mendoza-Jonas spin_lock_irqsave(&np->lock, flags); 3778d951a75SSamuel Mendoza-Jonas np->multi_channel = false; 3788d951a75SSamuel Mendoza-Jonas np->channel_whitelist = UINT_MAX; 3798d951a75SSamuel Mendoza-Jonas np->preferred_channel = NULL; 3808d951a75SSamuel Mendoza-Jonas spin_unlock_irqrestore(&np->lock, flags); 3818d951a75SSamuel Mendoza-Jonas } 382955dc68cSSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n"); 383955dc68cSSamuel Mendoza-Jonas 3842878a2cfSSamuel Mendoza-Jonas /* Update channel configuration */ 3852878a2cfSSamuel Mendoza-Jonas if (!(ndp->flags & NCSI_DEV_RESET)) 3862878a2cfSSamuel Mendoza-Jonas ncsi_reset_dev(&ndp->ndev); 387955dc68cSSamuel Mendoza-Jonas 388955dc68cSSamuel Mendoza-Jonas return 0; 389955dc68cSSamuel Mendoza-Jonas } 390955dc68cSSamuel Mendoza-Jonas 3919771b8ccSJustin.Lee1@Dell.com static int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info) 3929771b8ccSJustin.Lee1@Dell.com { 3939771b8ccSJustin.Lee1@Dell.com struct ncsi_dev_priv *ndp; 3949771b8ccSJustin.Lee1@Dell.com struct ncsi_pkt_hdr *hdr; 3959771b8ccSJustin.Lee1@Dell.com struct ncsi_cmd_arg nca; 3969771b8ccSJustin.Lee1@Dell.com unsigned char *data; 3979771b8ccSJustin.Lee1@Dell.com u32 package_id; 3989771b8ccSJustin.Lee1@Dell.com u32 channel_id; 3999771b8ccSJustin.Lee1@Dell.com int len, ret; 4009771b8ccSJustin.Lee1@Dell.com 4019771b8ccSJustin.Lee1@Dell.com if (!info || !info->attrs) { 4029771b8ccSJustin.Lee1@Dell.com ret = -EINVAL; 4039771b8ccSJustin.Lee1@Dell.com goto out; 4049771b8ccSJustin.Lee1@Dell.com } 4059771b8ccSJustin.Lee1@Dell.com 4069771b8ccSJustin.Lee1@Dell.com if (!info->attrs[NCSI_ATTR_IFINDEX]) { 4079771b8ccSJustin.Lee1@Dell.com ret = -EINVAL; 4089771b8ccSJustin.Lee1@Dell.com goto out; 4099771b8ccSJustin.Lee1@Dell.com } 4109771b8ccSJustin.Lee1@Dell.com 4119771b8ccSJustin.Lee1@Dell.com if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) { 4129771b8ccSJustin.Lee1@Dell.com ret = -EINVAL; 4139771b8ccSJustin.Lee1@Dell.com goto out; 4149771b8ccSJustin.Lee1@Dell.com } 4159771b8ccSJustin.Lee1@Dell.com 4169771b8ccSJustin.Lee1@Dell.com if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { 4179771b8ccSJustin.Lee1@Dell.com ret = -EINVAL; 4189771b8ccSJustin.Lee1@Dell.com goto out; 4199771b8ccSJustin.Lee1@Dell.com } 4209771b8ccSJustin.Lee1@Dell.com 4219771b8ccSJustin.Lee1@Dell.com if (!info->attrs[NCSI_ATTR_DATA]) { 4229771b8ccSJustin.Lee1@Dell.com ret = -EINVAL; 4239771b8ccSJustin.Lee1@Dell.com goto out; 4249771b8ccSJustin.Lee1@Dell.com } 4259771b8ccSJustin.Lee1@Dell.com 4269771b8ccSJustin.Lee1@Dell.com ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 4279771b8ccSJustin.Lee1@Dell.com nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 4289771b8ccSJustin.Lee1@Dell.com if (!ndp) { 4299771b8ccSJustin.Lee1@Dell.com ret = -ENODEV; 4309771b8ccSJustin.Lee1@Dell.com goto out; 4319771b8ccSJustin.Lee1@Dell.com } 4329771b8ccSJustin.Lee1@Dell.com 4339771b8ccSJustin.Lee1@Dell.com package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 4349771b8ccSJustin.Lee1@Dell.com channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); 4359771b8ccSJustin.Lee1@Dell.com 4369771b8ccSJustin.Lee1@Dell.com if (package_id >= NCSI_MAX_PACKAGE || channel_id >= NCSI_MAX_CHANNEL) { 4379771b8ccSJustin.Lee1@Dell.com ret = -ERANGE; 4389771b8ccSJustin.Lee1@Dell.com goto out_netlink; 4399771b8ccSJustin.Lee1@Dell.com } 4409771b8ccSJustin.Lee1@Dell.com 4419771b8ccSJustin.Lee1@Dell.com len = nla_len(info->attrs[NCSI_ATTR_DATA]); 4429771b8ccSJustin.Lee1@Dell.com if (len < sizeof(struct ncsi_pkt_hdr)) { 4439771b8ccSJustin.Lee1@Dell.com netdev_info(ndp->ndev.dev, "NCSI: no command to send %u\n", 4449771b8ccSJustin.Lee1@Dell.com package_id); 4459771b8ccSJustin.Lee1@Dell.com ret = -EINVAL; 4469771b8ccSJustin.Lee1@Dell.com goto out_netlink; 4479771b8ccSJustin.Lee1@Dell.com } else { 4489771b8ccSJustin.Lee1@Dell.com data = (unsigned char *)nla_data(info->attrs[NCSI_ATTR_DATA]); 4499771b8ccSJustin.Lee1@Dell.com } 4509771b8ccSJustin.Lee1@Dell.com 4519771b8ccSJustin.Lee1@Dell.com hdr = (struct ncsi_pkt_hdr *)data; 4529771b8ccSJustin.Lee1@Dell.com 4539771b8ccSJustin.Lee1@Dell.com nca.ndp = ndp; 4549771b8ccSJustin.Lee1@Dell.com nca.package = (unsigned char)package_id; 4559771b8ccSJustin.Lee1@Dell.com nca.channel = (unsigned char)channel_id; 4569771b8ccSJustin.Lee1@Dell.com nca.type = hdr->type; 4579771b8ccSJustin.Lee1@Dell.com nca.req_flags = NCSI_REQ_FLAG_NETLINK_DRIVEN; 4589771b8ccSJustin.Lee1@Dell.com nca.info = info; 4599771b8ccSJustin.Lee1@Dell.com nca.payload = ntohs(hdr->length); 4609771b8ccSJustin.Lee1@Dell.com nca.data = data + sizeof(*hdr); 4619771b8ccSJustin.Lee1@Dell.com 4629771b8ccSJustin.Lee1@Dell.com ret = ncsi_xmit_cmd(&nca); 4639771b8ccSJustin.Lee1@Dell.com out_netlink: 4649771b8ccSJustin.Lee1@Dell.com if (ret != 0) { 4659771b8ccSJustin.Lee1@Dell.com netdev_err(ndp->ndev.dev, 4669771b8ccSJustin.Lee1@Dell.com "NCSI: Error %d sending command\n", 4679771b8ccSJustin.Lee1@Dell.com ret); 4689771b8ccSJustin.Lee1@Dell.com ncsi_send_netlink_err(ndp->ndev.dev, 4699771b8ccSJustin.Lee1@Dell.com info->snd_seq, 4709771b8ccSJustin.Lee1@Dell.com info->snd_portid, 4719771b8ccSJustin.Lee1@Dell.com info->nlhdr, 4729771b8ccSJustin.Lee1@Dell.com ret); 4739771b8ccSJustin.Lee1@Dell.com } 4749771b8ccSJustin.Lee1@Dell.com out: 4759771b8ccSJustin.Lee1@Dell.com return ret; 4769771b8ccSJustin.Lee1@Dell.com } 4779771b8ccSJustin.Lee1@Dell.com 4789771b8ccSJustin.Lee1@Dell.com int ncsi_send_netlink_rsp(struct ncsi_request *nr, 4799771b8ccSJustin.Lee1@Dell.com struct ncsi_package *np, 4809771b8ccSJustin.Lee1@Dell.com struct ncsi_channel *nc) 4819771b8ccSJustin.Lee1@Dell.com { 4829771b8ccSJustin.Lee1@Dell.com struct sk_buff *skb; 4839771b8ccSJustin.Lee1@Dell.com struct net *net; 4849771b8ccSJustin.Lee1@Dell.com void *hdr; 4859771b8ccSJustin.Lee1@Dell.com int rc; 4869771b8ccSJustin.Lee1@Dell.com 4879771b8ccSJustin.Lee1@Dell.com net = dev_net(nr->rsp->dev); 4889771b8ccSJustin.Lee1@Dell.com 4899771b8ccSJustin.Lee1@Dell.com skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 4909771b8ccSJustin.Lee1@Dell.com if (!skb) 4919771b8ccSJustin.Lee1@Dell.com return -ENOMEM; 4929771b8ccSJustin.Lee1@Dell.com 4939771b8ccSJustin.Lee1@Dell.com hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq, 4949771b8ccSJustin.Lee1@Dell.com &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD); 4959771b8ccSJustin.Lee1@Dell.com if (!hdr) { 4969771b8ccSJustin.Lee1@Dell.com kfree_skb(skb); 4979771b8ccSJustin.Lee1@Dell.com return -EMSGSIZE; 4989771b8ccSJustin.Lee1@Dell.com } 4999771b8ccSJustin.Lee1@Dell.com 5009771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->rsp->dev->ifindex); 5019771b8ccSJustin.Lee1@Dell.com if (np) 5029771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id); 5039771b8ccSJustin.Lee1@Dell.com if (nc) 5049771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id); 5059771b8ccSJustin.Lee1@Dell.com else 5069771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL); 5079771b8ccSJustin.Lee1@Dell.com 5089771b8ccSJustin.Lee1@Dell.com rc = nla_put(skb, NCSI_ATTR_DATA, nr->rsp->len, (void *)nr->rsp->data); 5099771b8ccSJustin.Lee1@Dell.com if (rc) 5109771b8ccSJustin.Lee1@Dell.com goto err; 5119771b8ccSJustin.Lee1@Dell.com 5129771b8ccSJustin.Lee1@Dell.com genlmsg_end(skb, hdr); 5139771b8ccSJustin.Lee1@Dell.com return genlmsg_unicast(net, skb, nr->snd_portid); 5149771b8ccSJustin.Lee1@Dell.com 5159771b8ccSJustin.Lee1@Dell.com err: 5169771b8ccSJustin.Lee1@Dell.com kfree_skb(skb); 5179771b8ccSJustin.Lee1@Dell.com return rc; 5189771b8ccSJustin.Lee1@Dell.com } 5199771b8ccSJustin.Lee1@Dell.com 5209771b8ccSJustin.Lee1@Dell.com int ncsi_send_netlink_timeout(struct ncsi_request *nr, 5219771b8ccSJustin.Lee1@Dell.com struct ncsi_package *np, 5229771b8ccSJustin.Lee1@Dell.com struct ncsi_channel *nc) 5239771b8ccSJustin.Lee1@Dell.com { 5249771b8ccSJustin.Lee1@Dell.com struct sk_buff *skb; 5259771b8ccSJustin.Lee1@Dell.com struct net *net; 5269771b8ccSJustin.Lee1@Dell.com void *hdr; 5279771b8ccSJustin.Lee1@Dell.com 5289771b8ccSJustin.Lee1@Dell.com skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 5299771b8ccSJustin.Lee1@Dell.com if (!skb) 5309771b8ccSJustin.Lee1@Dell.com return -ENOMEM; 5319771b8ccSJustin.Lee1@Dell.com 5329771b8ccSJustin.Lee1@Dell.com hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq, 5339771b8ccSJustin.Lee1@Dell.com &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD); 5349771b8ccSJustin.Lee1@Dell.com if (!hdr) { 5359771b8ccSJustin.Lee1@Dell.com kfree_skb(skb); 5369771b8ccSJustin.Lee1@Dell.com return -EMSGSIZE; 5379771b8ccSJustin.Lee1@Dell.com } 5389771b8ccSJustin.Lee1@Dell.com 5399771b8ccSJustin.Lee1@Dell.com net = dev_net(nr->cmd->dev); 5409771b8ccSJustin.Lee1@Dell.com 5419771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->cmd->dev->ifindex); 5429771b8ccSJustin.Lee1@Dell.com 5439771b8ccSJustin.Lee1@Dell.com if (np) 5449771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id); 5459771b8ccSJustin.Lee1@Dell.com else 5469771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, 5479771b8ccSJustin.Lee1@Dell.com NCSI_PACKAGE_INDEX((((struct ncsi_pkt_hdr *) 5489771b8ccSJustin.Lee1@Dell.com nr->cmd->data)->channel))); 5499771b8ccSJustin.Lee1@Dell.com 5509771b8ccSJustin.Lee1@Dell.com if (nc) 5519771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id); 5529771b8ccSJustin.Lee1@Dell.com else 5539771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL); 5549771b8ccSJustin.Lee1@Dell.com 5559771b8ccSJustin.Lee1@Dell.com genlmsg_end(skb, hdr); 5569771b8ccSJustin.Lee1@Dell.com return genlmsg_unicast(net, skb, nr->snd_portid); 5579771b8ccSJustin.Lee1@Dell.com } 5589771b8ccSJustin.Lee1@Dell.com 5599771b8ccSJustin.Lee1@Dell.com int ncsi_send_netlink_err(struct net_device *dev, 5609771b8ccSJustin.Lee1@Dell.com u32 snd_seq, 5619771b8ccSJustin.Lee1@Dell.com u32 snd_portid, 5629771b8ccSJustin.Lee1@Dell.com struct nlmsghdr *nlhdr, 5639771b8ccSJustin.Lee1@Dell.com int err) 5649771b8ccSJustin.Lee1@Dell.com { 5659771b8ccSJustin.Lee1@Dell.com struct nlmsghdr *nlh; 5669771b8ccSJustin.Lee1@Dell.com struct nlmsgerr *nle; 5679771b8ccSJustin.Lee1@Dell.com struct sk_buff *skb; 5689771b8ccSJustin.Lee1@Dell.com struct net *net; 5699771b8ccSJustin.Lee1@Dell.com 5709771b8ccSJustin.Lee1@Dell.com skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 5719771b8ccSJustin.Lee1@Dell.com if (!skb) 5729771b8ccSJustin.Lee1@Dell.com return -ENOMEM; 5739771b8ccSJustin.Lee1@Dell.com 5749771b8ccSJustin.Lee1@Dell.com net = dev_net(dev); 5759771b8ccSJustin.Lee1@Dell.com 5769771b8ccSJustin.Lee1@Dell.com nlh = nlmsg_put(skb, snd_portid, snd_seq, 5779771b8ccSJustin.Lee1@Dell.com NLMSG_ERROR, sizeof(*nle), 0); 5789771b8ccSJustin.Lee1@Dell.com nle = (struct nlmsgerr *)nlmsg_data(nlh); 5799771b8ccSJustin.Lee1@Dell.com nle->error = err; 5809771b8ccSJustin.Lee1@Dell.com memcpy(&nle->msg, nlhdr, sizeof(*nlh)); 5819771b8ccSJustin.Lee1@Dell.com 5829771b8ccSJustin.Lee1@Dell.com nlmsg_end(skb, nlh); 5839771b8ccSJustin.Lee1@Dell.com 5849771b8ccSJustin.Lee1@Dell.com return nlmsg_unicast(net->genl_sock, skb, snd_portid); 5859771b8ccSJustin.Lee1@Dell.com } 5869771b8ccSJustin.Lee1@Dell.com 5878d951a75SSamuel Mendoza-Jonas static int ncsi_set_package_mask_nl(struct sk_buff *msg, 5888d951a75SSamuel Mendoza-Jonas struct genl_info *info) 5898d951a75SSamuel Mendoza-Jonas { 5908d951a75SSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 5918d951a75SSamuel Mendoza-Jonas unsigned long flags; 5928d951a75SSamuel Mendoza-Jonas int rc; 5938d951a75SSamuel Mendoza-Jonas 5948d951a75SSamuel Mendoza-Jonas if (!info || !info->attrs) 5958d951a75SSamuel Mendoza-Jonas return -EINVAL; 5968d951a75SSamuel Mendoza-Jonas 5978d951a75SSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_IFINDEX]) 5988d951a75SSamuel Mendoza-Jonas return -EINVAL; 5998d951a75SSamuel Mendoza-Jonas 6008d951a75SSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_PACKAGE_MASK]) 6018d951a75SSamuel Mendoza-Jonas return -EINVAL; 6028d951a75SSamuel Mendoza-Jonas 6038d951a75SSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 6048d951a75SSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 6058d951a75SSamuel Mendoza-Jonas if (!ndp) 6068d951a75SSamuel Mendoza-Jonas return -ENODEV; 6078d951a75SSamuel Mendoza-Jonas 6088d951a75SSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags); 6098d951a75SSamuel Mendoza-Jonas if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { 6108d951a75SSamuel Mendoza-Jonas if (ndp->flags & NCSI_DEV_HWA) { 6118d951a75SSamuel Mendoza-Jonas ndp->multi_package = true; 6128d951a75SSamuel Mendoza-Jonas rc = 0; 6138d951a75SSamuel Mendoza-Jonas } else { 6148d951a75SSamuel Mendoza-Jonas netdev_err(ndp->ndev.dev, 6158d951a75SSamuel Mendoza-Jonas "NCSI: Can't use multiple packages without HWA\n"); 6168d951a75SSamuel Mendoza-Jonas rc = -EPERM; 6178d951a75SSamuel Mendoza-Jonas } 6188d951a75SSamuel Mendoza-Jonas } else { 6198d951a75SSamuel Mendoza-Jonas ndp->multi_package = false; 6208d951a75SSamuel Mendoza-Jonas rc = 0; 6218d951a75SSamuel Mendoza-Jonas } 6228d951a75SSamuel Mendoza-Jonas 6238d951a75SSamuel Mendoza-Jonas if (!rc) 6248d951a75SSamuel Mendoza-Jonas ndp->package_whitelist = 6258d951a75SSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]); 6268d951a75SSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags); 6278d951a75SSamuel Mendoza-Jonas 6288d951a75SSamuel Mendoza-Jonas if (!rc) { 6298d951a75SSamuel Mendoza-Jonas /* Update channel configuration */ 6308d951a75SSamuel Mendoza-Jonas if (!(ndp->flags & NCSI_DEV_RESET)) 6318d951a75SSamuel Mendoza-Jonas ncsi_reset_dev(&ndp->ndev); 6328d951a75SSamuel Mendoza-Jonas } 6338d951a75SSamuel Mendoza-Jonas 6348d951a75SSamuel Mendoza-Jonas return rc; 6358d951a75SSamuel Mendoza-Jonas } 6368d951a75SSamuel Mendoza-Jonas 6378d951a75SSamuel Mendoza-Jonas static int ncsi_set_channel_mask_nl(struct sk_buff *msg, 6388d951a75SSamuel Mendoza-Jonas struct genl_info *info) 6398d951a75SSamuel Mendoza-Jonas { 6408d951a75SSamuel Mendoza-Jonas struct ncsi_package *np, *package; 6418d951a75SSamuel Mendoza-Jonas struct ncsi_channel *nc, *channel; 6428d951a75SSamuel Mendoza-Jonas u32 package_id, channel_id; 6438d951a75SSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 6448d951a75SSamuel Mendoza-Jonas unsigned long flags; 6458d951a75SSamuel Mendoza-Jonas 6468d951a75SSamuel Mendoza-Jonas if (!info || !info->attrs) 6478d951a75SSamuel Mendoza-Jonas return -EINVAL; 6488d951a75SSamuel Mendoza-Jonas 6498d951a75SSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_IFINDEX]) 6508d951a75SSamuel Mendoza-Jonas return -EINVAL; 6518d951a75SSamuel Mendoza-Jonas 6528d951a75SSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) 6538d951a75SSamuel Mendoza-Jonas return -EINVAL; 6548d951a75SSamuel Mendoza-Jonas 6558d951a75SSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_CHANNEL_MASK]) 6568d951a75SSamuel Mendoza-Jonas return -EINVAL; 6578d951a75SSamuel Mendoza-Jonas 6588d951a75SSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 6598d951a75SSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 6608d951a75SSamuel Mendoza-Jonas if (!ndp) 6618d951a75SSamuel Mendoza-Jonas return -ENODEV; 6628d951a75SSamuel Mendoza-Jonas 6638d951a75SSamuel Mendoza-Jonas package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 6648d951a75SSamuel Mendoza-Jonas package = NULL; 6658d951a75SSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) 6668d951a75SSamuel Mendoza-Jonas if (np->id == package_id) { 6678d951a75SSamuel Mendoza-Jonas package = np; 6688d951a75SSamuel Mendoza-Jonas break; 6698d951a75SSamuel Mendoza-Jonas } 6708d951a75SSamuel Mendoza-Jonas if (!package) 6718d951a75SSamuel Mendoza-Jonas return -ERANGE; 6728d951a75SSamuel Mendoza-Jonas 6738d951a75SSamuel Mendoza-Jonas spin_lock_irqsave(&package->lock, flags); 6748d951a75SSamuel Mendoza-Jonas 6758d951a75SSamuel Mendoza-Jonas channel = NULL; 6768d951a75SSamuel Mendoza-Jonas if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { 6778d951a75SSamuel Mendoza-Jonas channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); 6788d951a75SSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(np, nc) 6798d951a75SSamuel Mendoza-Jonas if (nc->id == channel_id) { 6808d951a75SSamuel Mendoza-Jonas channel = nc; 6818d951a75SSamuel Mendoza-Jonas break; 6828d951a75SSamuel Mendoza-Jonas } 6838d951a75SSamuel Mendoza-Jonas if (!channel) { 6848d951a75SSamuel Mendoza-Jonas spin_unlock_irqrestore(&package->lock, flags); 6858d951a75SSamuel Mendoza-Jonas return -ERANGE; 6868d951a75SSamuel Mendoza-Jonas } 6878d951a75SSamuel Mendoza-Jonas netdev_dbg(ndp->ndev.dev, 6888d951a75SSamuel Mendoza-Jonas "NCSI: Channel %u set as preferred channel\n", 6898d951a75SSamuel Mendoza-Jonas channel->id); 6908d951a75SSamuel Mendoza-Jonas } 6918d951a75SSamuel Mendoza-Jonas 6928d951a75SSamuel Mendoza-Jonas package->channel_whitelist = 6938d951a75SSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]); 6948d951a75SSamuel Mendoza-Jonas if (package->channel_whitelist == 0) 6958d951a75SSamuel Mendoza-Jonas netdev_dbg(ndp->ndev.dev, 6968d951a75SSamuel Mendoza-Jonas "NCSI: Package %u set to all channels disabled\n", 6978d951a75SSamuel Mendoza-Jonas package->id); 6988d951a75SSamuel Mendoza-Jonas 6998d951a75SSamuel Mendoza-Jonas package->preferred_channel = channel; 7008d951a75SSamuel Mendoza-Jonas 7018d951a75SSamuel Mendoza-Jonas if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { 7028d951a75SSamuel Mendoza-Jonas package->multi_channel = true; 7038d951a75SSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, 7048d951a75SSamuel Mendoza-Jonas "NCSI: Multi-channel enabled on package %u\n", 7058d951a75SSamuel Mendoza-Jonas package_id); 7068d951a75SSamuel Mendoza-Jonas } else { 7078d951a75SSamuel Mendoza-Jonas package->multi_channel = false; 7088d951a75SSamuel Mendoza-Jonas } 7098d951a75SSamuel Mendoza-Jonas 7108d951a75SSamuel Mendoza-Jonas spin_unlock_irqrestore(&package->lock, flags); 7118d951a75SSamuel Mendoza-Jonas 7128d951a75SSamuel Mendoza-Jonas /* Update channel configuration */ 7138d951a75SSamuel Mendoza-Jonas if (!(ndp->flags & NCSI_DEV_RESET)) 7148d951a75SSamuel Mendoza-Jonas ncsi_reset_dev(&ndp->ndev); 7158d951a75SSamuel Mendoza-Jonas 7168d951a75SSamuel Mendoza-Jonas return 0; 7178d951a75SSamuel Mendoza-Jonas } 7188d951a75SSamuel Mendoza-Jonas 719955dc68cSSamuel Mendoza-Jonas static const struct genl_ops ncsi_ops[] = { 720955dc68cSSamuel Mendoza-Jonas { 721955dc68cSSamuel Mendoza-Jonas .cmd = NCSI_CMD_PKG_INFO, 722ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 723955dc68cSSamuel Mendoza-Jonas .doit = ncsi_pkg_info_nl, 724955dc68cSSamuel Mendoza-Jonas .dumpit = ncsi_pkg_info_all_nl, 725955dc68cSSamuel Mendoza-Jonas .flags = 0, 726955dc68cSSamuel Mendoza-Jonas }, 727955dc68cSSamuel Mendoza-Jonas { 728955dc68cSSamuel Mendoza-Jonas .cmd = NCSI_CMD_SET_INTERFACE, 729ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 730955dc68cSSamuel Mendoza-Jonas .doit = ncsi_set_interface_nl, 731955dc68cSSamuel Mendoza-Jonas .flags = GENL_ADMIN_PERM, 732955dc68cSSamuel Mendoza-Jonas }, 733955dc68cSSamuel Mendoza-Jonas { 734955dc68cSSamuel Mendoza-Jonas .cmd = NCSI_CMD_CLEAR_INTERFACE, 735ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 736955dc68cSSamuel Mendoza-Jonas .doit = ncsi_clear_interface_nl, 737955dc68cSSamuel Mendoza-Jonas .flags = GENL_ADMIN_PERM, 738955dc68cSSamuel Mendoza-Jonas }, 7399771b8ccSJustin.Lee1@Dell.com { 7409771b8ccSJustin.Lee1@Dell.com .cmd = NCSI_CMD_SEND_CMD, 741ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 7429771b8ccSJustin.Lee1@Dell.com .doit = ncsi_send_cmd_nl, 7439771b8ccSJustin.Lee1@Dell.com .flags = GENL_ADMIN_PERM, 7449771b8ccSJustin.Lee1@Dell.com }, 7458d951a75SSamuel Mendoza-Jonas { 7468d951a75SSamuel Mendoza-Jonas .cmd = NCSI_CMD_SET_PACKAGE_MASK, 747ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 7488d951a75SSamuel Mendoza-Jonas .doit = ncsi_set_package_mask_nl, 7498d951a75SSamuel Mendoza-Jonas .flags = GENL_ADMIN_PERM, 7508d951a75SSamuel Mendoza-Jonas }, 7518d951a75SSamuel Mendoza-Jonas { 7528d951a75SSamuel Mendoza-Jonas .cmd = NCSI_CMD_SET_CHANNEL_MASK, 753ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 7548d951a75SSamuel Mendoza-Jonas .doit = ncsi_set_channel_mask_nl, 7558d951a75SSamuel Mendoza-Jonas .flags = GENL_ADMIN_PERM, 7568d951a75SSamuel Mendoza-Jonas }, 757955dc68cSSamuel Mendoza-Jonas }; 758955dc68cSSamuel Mendoza-Jonas 759955dc68cSSamuel Mendoza-Jonas static struct genl_family ncsi_genl_family __ro_after_init = { 760955dc68cSSamuel Mendoza-Jonas .name = "NCSI", 761955dc68cSSamuel Mendoza-Jonas .version = 0, 762955dc68cSSamuel Mendoza-Jonas .maxattr = NCSI_ATTR_MAX, 7633b0f31f2SJohannes Berg .policy = ncsi_genl_policy, 764955dc68cSSamuel Mendoza-Jonas .module = THIS_MODULE, 765955dc68cSSamuel Mendoza-Jonas .ops = ncsi_ops, 766955dc68cSSamuel Mendoza-Jonas .n_ops = ARRAY_SIZE(ncsi_ops), 767955dc68cSSamuel Mendoza-Jonas }; 768955dc68cSSamuel Mendoza-Jonas 769955dc68cSSamuel Mendoza-Jonas int ncsi_init_netlink(struct net_device *dev) 770955dc68cSSamuel Mendoza-Jonas { 771955dc68cSSamuel Mendoza-Jonas int rc; 772955dc68cSSamuel Mendoza-Jonas 773955dc68cSSamuel Mendoza-Jonas rc = genl_register_family(&ncsi_genl_family); 774955dc68cSSamuel Mendoza-Jonas if (rc) 775955dc68cSSamuel Mendoza-Jonas netdev_err(dev, "ncsi: failed to register netlink family\n"); 776955dc68cSSamuel Mendoza-Jonas 777955dc68cSSamuel Mendoza-Jonas return rc; 778955dc68cSSamuel Mendoza-Jonas } 779955dc68cSSamuel Mendoza-Jonas 780955dc68cSSamuel Mendoza-Jonas int ncsi_unregister_netlink(struct net_device *dev) 781955dc68cSSamuel Mendoza-Jonas { 782955dc68cSSamuel Mendoza-Jonas int rc; 783955dc68cSSamuel Mendoza-Jonas 784955dc68cSSamuel Mendoza-Jonas rc = genl_unregister_family(&ncsi_genl_family); 785955dc68cSSamuel Mendoza-Jonas if (rc) 786955dc68cSSamuel Mendoza-Jonas netdev_err(dev, "ncsi: failed to unregister netlink family\n"); 787955dc68cSSamuel Mendoza-Jonas 788955dc68cSSamuel Mendoza-Jonas return rc; 789955dc68cSSamuel Mendoza-Jonas } 790