1955dc68cSSamuel Mendoza-Jonas /* 2955dc68cSSamuel Mendoza-Jonas * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018. 3955dc68cSSamuel Mendoza-Jonas * 4955dc68cSSamuel Mendoza-Jonas * This program is free software; you can redistribute it and/or modify 5955dc68cSSamuel Mendoza-Jonas * it under the terms of the GNU General Public License as published by 6955dc68cSSamuel Mendoza-Jonas * the Free Software Foundation; either version 2 of the License, or 7955dc68cSSamuel Mendoza-Jonas * (at your option) any later version. 8955dc68cSSamuel Mendoza-Jonas */ 9955dc68cSSamuel Mendoza-Jonas 10955dc68cSSamuel Mendoza-Jonas #include <linux/module.h> 11955dc68cSSamuel Mendoza-Jonas #include <linux/kernel.h> 12955dc68cSSamuel Mendoza-Jonas #include <linux/if_arp.h> 13955dc68cSSamuel Mendoza-Jonas #include <linux/rtnetlink.h> 14955dc68cSSamuel Mendoza-Jonas #include <linux/etherdevice.h> 15955dc68cSSamuel Mendoza-Jonas #include <net/genetlink.h> 16955dc68cSSamuel Mendoza-Jonas #include <net/ncsi.h> 17955dc68cSSamuel Mendoza-Jonas #include <linux/skbuff.h> 18955dc68cSSamuel Mendoza-Jonas #include <net/sock.h> 19955dc68cSSamuel Mendoza-Jonas #include <uapi/linux/ncsi.h> 20955dc68cSSamuel Mendoza-Jonas 21955dc68cSSamuel Mendoza-Jonas #include "internal.h" 229771b8ccSJustin.Lee1@Dell.com #include "ncsi-pkt.h" 23955dc68cSSamuel Mendoza-Jonas #include "ncsi-netlink.h" 24955dc68cSSamuel Mendoza-Jonas 25955dc68cSSamuel Mendoza-Jonas static struct genl_family ncsi_genl_family; 26955dc68cSSamuel Mendoza-Jonas 27955dc68cSSamuel Mendoza-Jonas static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = { 28955dc68cSSamuel Mendoza-Jonas [NCSI_ATTR_IFINDEX] = { .type = NLA_U32 }, 29955dc68cSSamuel Mendoza-Jonas [NCSI_ATTR_PACKAGE_LIST] = { .type = NLA_NESTED }, 30955dc68cSSamuel Mendoza-Jonas [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, 31955dc68cSSamuel Mendoza-Jonas [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, 329771b8ccSJustin.Lee1@Dell.com [NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 }, 338d951a75SSamuel Mendoza-Jonas [NCSI_ATTR_MULTI_FLAG] = { .type = NLA_FLAG }, 348d951a75SSamuel Mendoza-Jonas [NCSI_ATTR_PACKAGE_MASK] = { .type = NLA_U32 }, 358d951a75SSamuel Mendoza-Jonas [NCSI_ATTR_CHANNEL_MASK] = { .type = NLA_U32 }, 36955dc68cSSamuel Mendoza-Jonas }; 37955dc68cSSamuel Mendoza-Jonas 38955dc68cSSamuel Mendoza-Jonas static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) 39955dc68cSSamuel Mendoza-Jonas { 40955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 41955dc68cSSamuel Mendoza-Jonas struct net_device *dev; 42955dc68cSSamuel Mendoza-Jonas struct ncsi_dev *nd; 43955dc68cSSamuel Mendoza-Jonas struct ncsi_dev; 44955dc68cSSamuel Mendoza-Jonas 45955dc68cSSamuel Mendoza-Jonas if (!net) 46955dc68cSSamuel Mendoza-Jonas return NULL; 47955dc68cSSamuel Mendoza-Jonas 48955dc68cSSamuel Mendoza-Jonas dev = dev_get_by_index(net, ifindex); 49955dc68cSSamuel Mendoza-Jonas if (!dev) { 50955dc68cSSamuel Mendoza-Jonas pr_err("NCSI netlink: No device for ifindex %u\n", ifindex); 51955dc68cSSamuel Mendoza-Jonas return NULL; 52955dc68cSSamuel Mendoza-Jonas } 53955dc68cSSamuel Mendoza-Jonas 54955dc68cSSamuel Mendoza-Jonas nd = ncsi_find_dev(dev); 55955dc68cSSamuel Mendoza-Jonas ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL; 56955dc68cSSamuel Mendoza-Jonas 57955dc68cSSamuel Mendoza-Jonas dev_put(dev); 58955dc68cSSamuel Mendoza-Jonas return ndp; 59955dc68cSSamuel Mendoza-Jonas } 60955dc68cSSamuel Mendoza-Jonas 61955dc68cSSamuel Mendoza-Jonas static int ncsi_write_channel_info(struct sk_buff *skb, 62955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp, 63955dc68cSSamuel Mendoza-Jonas struct ncsi_channel *nc) 64955dc68cSSamuel Mendoza-Jonas { 65062b3e1bSSamuel Mendoza-Jonas struct ncsi_channel_vlan_filter *ncf; 66955dc68cSSamuel Mendoza-Jonas struct ncsi_channel_mode *m; 67062b3e1bSSamuel Mendoza-Jonas struct nlattr *vid_nest; 68955dc68cSSamuel Mendoza-Jonas int i; 69955dc68cSSamuel Mendoza-Jonas 70955dc68cSSamuel Mendoza-Jonas nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id); 71955dc68cSSamuel Mendoza-Jonas m = &nc->modes[NCSI_MODE_LINK]; 72955dc68cSSamuel Mendoza-Jonas nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]); 73955dc68cSSamuel Mendoza-Jonas if (nc->state == NCSI_CHANNEL_ACTIVE) 74955dc68cSSamuel Mendoza-Jonas nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE); 758d951a75SSamuel Mendoza-Jonas if (nc == nc->package->preferred_channel) 76955dc68cSSamuel Mendoza-Jonas nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED); 77955dc68cSSamuel Mendoza-Jonas 78955dc68cSSamuel Mendoza-Jonas nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version); 79955dc68cSSamuel Mendoza-Jonas nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MINOR, nc->version.alpha2); 80955dc68cSSamuel Mendoza-Jonas nla_put_string(skb, NCSI_CHANNEL_ATTR_VERSION_STR, nc->version.fw_name); 81955dc68cSSamuel Mendoza-Jonas 82955dc68cSSamuel Mendoza-Jonas vid_nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR_VLAN_LIST); 83955dc68cSSamuel Mendoza-Jonas if (!vid_nest) 84955dc68cSSamuel Mendoza-Jonas return -ENOMEM; 85062b3e1bSSamuel Mendoza-Jonas ncf = &nc->vlan_filter; 86955dc68cSSamuel Mendoza-Jonas i = -1; 87062b3e1bSSamuel Mendoza-Jonas while ((i = find_next_bit((void *)&ncf->bitmap, ncf->n_vids, 88062b3e1bSSamuel Mendoza-Jonas i + 1)) < ncf->n_vids) { 89062b3e1bSSamuel Mendoza-Jonas if (ncf->vids[i]) 90955dc68cSSamuel Mendoza-Jonas nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID, 91062b3e1bSSamuel Mendoza-Jonas ncf->vids[i]); 92955dc68cSSamuel Mendoza-Jonas } 93955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, vid_nest); 94955dc68cSSamuel Mendoza-Jonas 95955dc68cSSamuel Mendoza-Jonas return 0; 96955dc68cSSamuel Mendoza-Jonas } 97955dc68cSSamuel Mendoza-Jonas 98955dc68cSSamuel Mendoza-Jonas static int ncsi_write_package_info(struct sk_buff *skb, 99955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp, unsigned int id) 100955dc68cSSamuel Mendoza-Jonas { 101955dc68cSSamuel Mendoza-Jonas struct nlattr *pnest, *cnest, *nest; 102955dc68cSSamuel Mendoza-Jonas struct ncsi_package *np; 103955dc68cSSamuel Mendoza-Jonas struct ncsi_channel *nc; 104955dc68cSSamuel Mendoza-Jonas bool found; 105955dc68cSSamuel Mendoza-Jonas int rc; 106955dc68cSSamuel Mendoza-Jonas 1073d0371b3SSamuel Mendoza-Jonas if (id > ndp->package_num - 1) { 108955dc68cSSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, "NCSI: No package with id %u\n", id); 109955dc68cSSamuel Mendoza-Jonas return -ENODEV; 110955dc68cSSamuel Mendoza-Jonas } 111955dc68cSSamuel Mendoza-Jonas 112955dc68cSSamuel Mendoza-Jonas found = false; 113955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) { 114955dc68cSSamuel Mendoza-Jonas if (np->id != id) 115955dc68cSSamuel Mendoza-Jonas continue; 116955dc68cSSamuel Mendoza-Jonas pnest = nla_nest_start(skb, NCSI_PKG_ATTR); 117955dc68cSSamuel Mendoza-Jonas if (!pnest) 118955dc68cSSamuel Mendoza-Jonas return -ENOMEM; 119955dc68cSSamuel Mendoza-Jonas nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id); 1208d951a75SSamuel Mendoza-Jonas if ((0x1 << np->id) == ndp->package_whitelist) 121955dc68cSSamuel Mendoza-Jonas nla_put_flag(skb, NCSI_PKG_ATTR_FORCED); 122955dc68cSSamuel Mendoza-Jonas cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST); 123955dc68cSSamuel Mendoza-Jonas if (!cnest) { 124955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, pnest); 125955dc68cSSamuel Mendoza-Jonas return -ENOMEM; 126955dc68cSSamuel Mendoza-Jonas } 127955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(np, nc) { 128955dc68cSSamuel Mendoza-Jonas nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR); 129955dc68cSSamuel Mendoza-Jonas if (!nest) { 130955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, cnest); 131955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, pnest); 132955dc68cSSamuel Mendoza-Jonas return -ENOMEM; 133955dc68cSSamuel Mendoza-Jonas } 134955dc68cSSamuel Mendoza-Jonas rc = ncsi_write_channel_info(skb, ndp, nc); 135955dc68cSSamuel Mendoza-Jonas if (rc) { 136955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, nest); 137955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, cnest); 138955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, pnest); 139955dc68cSSamuel Mendoza-Jonas return rc; 140955dc68cSSamuel Mendoza-Jonas } 141955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, nest); 142955dc68cSSamuel Mendoza-Jonas } 143955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, cnest); 144955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, pnest); 145955dc68cSSamuel Mendoza-Jonas found = true; 146955dc68cSSamuel Mendoza-Jonas } 147955dc68cSSamuel Mendoza-Jonas 148955dc68cSSamuel Mendoza-Jonas if (!found) 149955dc68cSSamuel Mendoza-Jonas return -ENODEV; 150955dc68cSSamuel Mendoza-Jonas 151955dc68cSSamuel Mendoza-Jonas return 0; 152955dc68cSSamuel Mendoza-Jonas } 153955dc68cSSamuel Mendoza-Jonas 154955dc68cSSamuel Mendoza-Jonas static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info) 155955dc68cSSamuel Mendoza-Jonas { 156955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 157955dc68cSSamuel Mendoza-Jonas unsigned int package_id; 158955dc68cSSamuel Mendoza-Jonas struct sk_buff *skb; 159955dc68cSSamuel Mendoza-Jonas struct nlattr *attr; 160955dc68cSSamuel Mendoza-Jonas void *hdr; 161955dc68cSSamuel Mendoza-Jonas int rc; 162955dc68cSSamuel Mendoza-Jonas 163955dc68cSSamuel Mendoza-Jonas if (!info || !info->attrs) 164955dc68cSSamuel Mendoza-Jonas return -EINVAL; 165955dc68cSSamuel Mendoza-Jonas 166955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_IFINDEX]) 167955dc68cSSamuel Mendoza-Jonas return -EINVAL; 168955dc68cSSamuel Mendoza-Jonas 169955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) 170955dc68cSSamuel Mendoza-Jonas return -EINVAL; 171955dc68cSSamuel Mendoza-Jonas 172955dc68cSSamuel Mendoza-Jonas ndp = ndp_from_ifindex(genl_info_net(info), 173955dc68cSSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 174955dc68cSSamuel Mendoza-Jonas if (!ndp) 175955dc68cSSamuel Mendoza-Jonas return -ENODEV; 176955dc68cSSamuel Mendoza-Jonas 177955dc68cSSamuel Mendoza-Jonas skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 178955dc68cSSamuel Mendoza-Jonas if (!skb) 179955dc68cSSamuel Mendoza-Jonas return -ENOMEM; 180955dc68cSSamuel Mendoza-Jonas 181955dc68cSSamuel Mendoza-Jonas hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq, 182955dc68cSSamuel Mendoza-Jonas &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO); 183955dc68cSSamuel Mendoza-Jonas if (!hdr) { 18450db64b0SDan Carpenter kfree_skb(skb); 185955dc68cSSamuel Mendoza-Jonas return -EMSGSIZE; 186955dc68cSSamuel Mendoza-Jonas } 187955dc68cSSamuel Mendoza-Jonas 188955dc68cSSamuel Mendoza-Jonas package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 189955dc68cSSamuel Mendoza-Jonas 190955dc68cSSamuel Mendoza-Jonas attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST); 1918daf1a2dSColin Ian King if (!attr) { 1928daf1a2dSColin Ian King kfree_skb(skb); 1938daf1a2dSColin Ian King return -EMSGSIZE; 1948daf1a2dSColin Ian King } 195955dc68cSSamuel Mendoza-Jonas rc = ncsi_write_package_info(skb, ndp, package_id); 196955dc68cSSamuel Mendoza-Jonas 197955dc68cSSamuel Mendoza-Jonas if (rc) { 198955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, attr); 199955dc68cSSamuel Mendoza-Jonas goto err; 200955dc68cSSamuel Mendoza-Jonas } 201955dc68cSSamuel Mendoza-Jonas 202955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, attr); 203955dc68cSSamuel Mendoza-Jonas 204955dc68cSSamuel Mendoza-Jonas genlmsg_end(skb, hdr); 205955dc68cSSamuel Mendoza-Jonas return genlmsg_reply(skb, info); 206955dc68cSSamuel Mendoza-Jonas 207955dc68cSSamuel Mendoza-Jonas err: 20850db64b0SDan Carpenter kfree_skb(skb); 209955dc68cSSamuel Mendoza-Jonas return rc; 210955dc68cSSamuel Mendoza-Jonas } 211955dc68cSSamuel Mendoza-Jonas 212955dc68cSSamuel Mendoza-Jonas static int ncsi_pkg_info_all_nl(struct sk_buff *skb, 213955dc68cSSamuel Mendoza-Jonas struct netlink_callback *cb) 214955dc68cSSamuel Mendoza-Jonas { 2150f51f358SSamuel Mendoza-Jonas struct nlattr *attrs[NCSI_ATTR_MAX + 1]; 216955dc68cSSamuel Mendoza-Jonas struct ncsi_package *np, *package; 217955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 218955dc68cSSamuel Mendoza-Jonas unsigned int package_id; 219955dc68cSSamuel Mendoza-Jonas struct nlattr *attr; 220955dc68cSSamuel Mendoza-Jonas void *hdr; 221955dc68cSSamuel Mendoza-Jonas int rc; 222955dc68cSSamuel Mendoza-Jonas 223955dc68cSSamuel Mendoza-Jonas rc = genlmsg_parse(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX, 224955dc68cSSamuel Mendoza-Jonas ncsi_genl_policy, NULL); 225955dc68cSSamuel Mendoza-Jonas if (rc) 226955dc68cSSamuel Mendoza-Jonas return rc; 227955dc68cSSamuel Mendoza-Jonas 228955dc68cSSamuel Mendoza-Jonas if (!attrs[NCSI_ATTR_IFINDEX]) 229955dc68cSSamuel Mendoza-Jonas return -EINVAL; 230955dc68cSSamuel Mendoza-Jonas 231955dc68cSSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)), 232955dc68cSSamuel Mendoza-Jonas nla_get_u32(attrs[NCSI_ATTR_IFINDEX])); 233955dc68cSSamuel Mendoza-Jonas 234955dc68cSSamuel Mendoza-Jonas if (!ndp) 235955dc68cSSamuel Mendoza-Jonas return -ENODEV; 236955dc68cSSamuel Mendoza-Jonas 237955dc68cSSamuel Mendoza-Jonas package_id = cb->args[0]; 238955dc68cSSamuel Mendoza-Jonas package = NULL; 239955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) 240955dc68cSSamuel Mendoza-Jonas if (np->id == package_id) 241955dc68cSSamuel Mendoza-Jonas package = np; 242955dc68cSSamuel Mendoza-Jonas 243955dc68cSSamuel Mendoza-Jonas if (!package) 244955dc68cSSamuel Mendoza-Jonas return 0; /* done */ 245955dc68cSSamuel Mendoza-Jonas 246955dc68cSSamuel Mendoza-Jonas hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 2473d0371b3SSamuel Mendoza-Jonas &ncsi_genl_family, NLM_F_MULTI, NCSI_CMD_PKG_INFO); 248955dc68cSSamuel Mendoza-Jonas if (!hdr) { 249955dc68cSSamuel Mendoza-Jonas rc = -EMSGSIZE; 250955dc68cSSamuel Mendoza-Jonas goto err; 251955dc68cSSamuel Mendoza-Jonas } 252955dc68cSSamuel Mendoza-Jonas 253955dc68cSSamuel Mendoza-Jonas attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST); 25407660ca6SKangjie Lu if (!attr) { 25507660ca6SKangjie Lu rc = -EMSGSIZE; 25607660ca6SKangjie Lu goto err; 25707660ca6SKangjie Lu } 258955dc68cSSamuel Mendoza-Jonas rc = ncsi_write_package_info(skb, ndp, package->id); 259955dc68cSSamuel Mendoza-Jonas if (rc) { 260955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, attr); 261955dc68cSSamuel Mendoza-Jonas goto err; 262955dc68cSSamuel Mendoza-Jonas } 263955dc68cSSamuel Mendoza-Jonas 264955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, attr); 265955dc68cSSamuel Mendoza-Jonas genlmsg_end(skb, hdr); 266955dc68cSSamuel Mendoza-Jonas 267955dc68cSSamuel Mendoza-Jonas cb->args[0] = package_id + 1; 268955dc68cSSamuel Mendoza-Jonas 269955dc68cSSamuel Mendoza-Jonas return skb->len; 270955dc68cSSamuel Mendoza-Jonas err: 271955dc68cSSamuel Mendoza-Jonas genlmsg_cancel(skb, hdr); 272955dc68cSSamuel Mendoza-Jonas return rc; 273955dc68cSSamuel Mendoza-Jonas } 274955dc68cSSamuel Mendoza-Jonas 275955dc68cSSamuel Mendoza-Jonas static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info) 276955dc68cSSamuel Mendoza-Jonas { 277955dc68cSSamuel Mendoza-Jonas struct ncsi_package *np, *package; 278955dc68cSSamuel Mendoza-Jonas struct ncsi_channel *nc, *channel; 279955dc68cSSamuel Mendoza-Jonas u32 package_id, channel_id; 280955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 281955dc68cSSamuel Mendoza-Jonas unsigned long flags; 282955dc68cSSamuel Mendoza-Jonas 283955dc68cSSamuel Mendoza-Jonas if (!info || !info->attrs) 284955dc68cSSamuel Mendoza-Jonas return -EINVAL; 285955dc68cSSamuel Mendoza-Jonas 286955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_IFINDEX]) 287955dc68cSSamuel Mendoza-Jonas return -EINVAL; 288955dc68cSSamuel Mendoza-Jonas 289955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) 290955dc68cSSamuel Mendoza-Jonas return -EINVAL; 291955dc68cSSamuel Mendoza-Jonas 292955dc68cSSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 293955dc68cSSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 294955dc68cSSamuel Mendoza-Jonas if (!ndp) 295955dc68cSSamuel Mendoza-Jonas return -ENODEV; 296955dc68cSSamuel Mendoza-Jonas 297955dc68cSSamuel Mendoza-Jonas package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 298955dc68cSSamuel Mendoza-Jonas package = NULL; 299955dc68cSSamuel Mendoza-Jonas 300955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) 301955dc68cSSamuel Mendoza-Jonas if (np->id == package_id) 302955dc68cSSamuel Mendoza-Jonas package = np; 303955dc68cSSamuel Mendoza-Jonas if (!package) { 304955dc68cSSamuel Mendoza-Jonas /* The user has set a package that does not exist */ 305955dc68cSSamuel Mendoza-Jonas return -ERANGE; 306955dc68cSSamuel Mendoza-Jonas } 307955dc68cSSamuel Mendoza-Jonas 308955dc68cSSamuel Mendoza-Jonas channel = NULL; 3098d951a75SSamuel Mendoza-Jonas if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { 310955dc68cSSamuel Mendoza-Jonas channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); 311955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(package, nc) 3128d951a75SSamuel Mendoza-Jonas if (nc->id == channel_id) { 313955dc68cSSamuel Mendoza-Jonas channel = nc; 3148d951a75SSamuel Mendoza-Jonas break; 315955dc68cSSamuel Mendoza-Jonas } 3168d951a75SSamuel Mendoza-Jonas if (!channel) { 3178d951a75SSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, 3188d951a75SSamuel Mendoza-Jonas "NCSI: Channel %u does not exist!\n", 319955dc68cSSamuel Mendoza-Jonas channel_id); 320955dc68cSSamuel Mendoza-Jonas return -ERANGE; 321955dc68cSSamuel Mendoza-Jonas } 3228d951a75SSamuel Mendoza-Jonas } 323955dc68cSSamuel Mendoza-Jonas 3248d951a75SSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags); 3258d951a75SSamuel Mendoza-Jonas ndp->package_whitelist = 0x1 << package->id; 3268d951a75SSamuel Mendoza-Jonas ndp->multi_package = false; 327955dc68cSSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags); 328955dc68cSSamuel Mendoza-Jonas 3298d951a75SSamuel Mendoza-Jonas spin_lock_irqsave(&package->lock, flags); 3308d951a75SSamuel Mendoza-Jonas package->multi_channel = false; 3318d951a75SSamuel Mendoza-Jonas if (channel) { 3328d951a75SSamuel Mendoza-Jonas package->channel_whitelist = 0x1 << channel->id; 3338d951a75SSamuel Mendoza-Jonas package->preferred_channel = channel; 3348d951a75SSamuel Mendoza-Jonas } else { 3358d951a75SSamuel Mendoza-Jonas /* Allow any channel */ 3368d951a75SSamuel Mendoza-Jonas package->channel_whitelist = UINT_MAX; 3378d951a75SSamuel Mendoza-Jonas package->preferred_channel = NULL; 3388d951a75SSamuel Mendoza-Jonas } 3398d951a75SSamuel Mendoza-Jonas spin_unlock_irqrestore(&package->lock, flags); 3408d951a75SSamuel Mendoza-Jonas 3418d951a75SSamuel Mendoza-Jonas if (channel) 3428d951a75SSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, 3438d951a75SSamuel Mendoza-Jonas "Set package 0x%x, channel 0x%x as preferred\n", 3448d951a75SSamuel Mendoza-Jonas package_id, channel_id); 3458d951a75SSamuel Mendoza-Jonas else 3468d951a75SSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n", 3478d951a75SSamuel Mendoza-Jonas package_id); 348955dc68cSSamuel Mendoza-Jonas 3492878a2cfSSamuel Mendoza-Jonas /* Update channel configuration */ 3502878a2cfSSamuel Mendoza-Jonas if (!(ndp->flags & NCSI_DEV_RESET)) 3512878a2cfSSamuel Mendoza-Jonas ncsi_reset_dev(&ndp->ndev); 352955dc68cSSamuel Mendoza-Jonas 353955dc68cSSamuel Mendoza-Jonas return 0; 354955dc68cSSamuel Mendoza-Jonas } 355955dc68cSSamuel Mendoza-Jonas 356955dc68cSSamuel Mendoza-Jonas static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) 357955dc68cSSamuel Mendoza-Jonas { 358955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 3598d951a75SSamuel Mendoza-Jonas struct ncsi_package *np; 360955dc68cSSamuel Mendoza-Jonas unsigned long flags; 361955dc68cSSamuel Mendoza-Jonas 362955dc68cSSamuel Mendoza-Jonas if (!info || !info->attrs) 363955dc68cSSamuel Mendoza-Jonas return -EINVAL; 364955dc68cSSamuel Mendoza-Jonas 365955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_IFINDEX]) 366955dc68cSSamuel Mendoza-Jonas return -EINVAL; 367955dc68cSSamuel Mendoza-Jonas 368955dc68cSSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 369955dc68cSSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 370955dc68cSSamuel Mendoza-Jonas if (!ndp) 371955dc68cSSamuel Mendoza-Jonas return -ENODEV; 372955dc68cSSamuel Mendoza-Jonas 3738d951a75SSamuel Mendoza-Jonas /* Reset any whitelists and disable multi mode */ 374955dc68cSSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags); 3758d951a75SSamuel Mendoza-Jonas ndp->package_whitelist = UINT_MAX; 3768d951a75SSamuel Mendoza-Jonas ndp->multi_package = false; 377955dc68cSSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags); 3788d951a75SSamuel Mendoza-Jonas 3798d951a75SSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) { 3808d951a75SSamuel Mendoza-Jonas spin_lock_irqsave(&np->lock, flags); 3818d951a75SSamuel Mendoza-Jonas np->multi_channel = false; 3828d951a75SSamuel Mendoza-Jonas np->channel_whitelist = UINT_MAX; 3838d951a75SSamuel Mendoza-Jonas np->preferred_channel = NULL; 3848d951a75SSamuel Mendoza-Jonas spin_unlock_irqrestore(&np->lock, flags); 3858d951a75SSamuel Mendoza-Jonas } 386955dc68cSSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n"); 387955dc68cSSamuel Mendoza-Jonas 3882878a2cfSSamuel Mendoza-Jonas /* Update channel configuration */ 3892878a2cfSSamuel Mendoza-Jonas if (!(ndp->flags & NCSI_DEV_RESET)) 3902878a2cfSSamuel Mendoza-Jonas ncsi_reset_dev(&ndp->ndev); 391955dc68cSSamuel Mendoza-Jonas 392955dc68cSSamuel Mendoza-Jonas return 0; 393955dc68cSSamuel Mendoza-Jonas } 394955dc68cSSamuel Mendoza-Jonas 3959771b8ccSJustin.Lee1@Dell.com static int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info) 3969771b8ccSJustin.Lee1@Dell.com { 3979771b8ccSJustin.Lee1@Dell.com struct ncsi_dev_priv *ndp; 3989771b8ccSJustin.Lee1@Dell.com struct ncsi_pkt_hdr *hdr; 3999771b8ccSJustin.Lee1@Dell.com struct ncsi_cmd_arg nca; 4009771b8ccSJustin.Lee1@Dell.com unsigned char *data; 4019771b8ccSJustin.Lee1@Dell.com u32 package_id; 4029771b8ccSJustin.Lee1@Dell.com u32 channel_id; 4039771b8ccSJustin.Lee1@Dell.com int len, ret; 4049771b8ccSJustin.Lee1@Dell.com 4059771b8ccSJustin.Lee1@Dell.com if (!info || !info->attrs) { 4069771b8ccSJustin.Lee1@Dell.com ret = -EINVAL; 4079771b8ccSJustin.Lee1@Dell.com goto out; 4089771b8ccSJustin.Lee1@Dell.com } 4099771b8ccSJustin.Lee1@Dell.com 4109771b8ccSJustin.Lee1@Dell.com if (!info->attrs[NCSI_ATTR_IFINDEX]) { 4119771b8ccSJustin.Lee1@Dell.com ret = -EINVAL; 4129771b8ccSJustin.Lee1@Dell.com goto out; 4139771b8ccSJustin.Lee1@Dell.com } 4149771b8ccSJustin.Lee1@Dell.com 4159771b8ccSJustin.Lee1@Dell.com if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) { 4169771b8ccSJustin.Lee1@Dell.com ret = -EINVAL; 4179771b8ccSJustin.Lee1@Dell.com goto out; 4189771b8ccSJustin.Lee1@Dell.com } 4199771b8ccSJustin.Lee1@Dell.com 4209771b8ccSJustin.Lee1@Dell.com if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { 4219771b8ccSJustin.Lee1@Dell.com ret = -EINVAL; 4229771b8ccSJustin.Lee1@Dell.com goto out; 4239771b8ccSJustin.Lee1@Dell.com } 4249771b8ccSJustin.Lee1@Dell.com 4259771b8ccSJustin.Lee1@Dell.com if (!info->attrs[NCSI_ATTR_DATA]) { 4269771b8ccSJustin.Lee1@Dell.com ret = -EINVAL; 4279771b8ccSJustin.Lee1@Dell.com goto out; 4289771b8ccSJustin.Lee1@Dell.com } 4299771b8ccSJustin.Lee1@Dell.com 4309771b8ccSJustin.Lee1@Dell.com ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 4319771b8ccSJustin.Lee1@Dell.com nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 4329771b8ccSJustin.Lee1@Dell.com if (!ndp) { 4339771b8ccSJustin.Lee1@Dell.com ret = -ENODEV; 4349771b8ccSJustin.Lee1@Dell.com goto out; 4359771b8ccSJustin.Lee1@Dell.com } 4369771b8ccSJustin.Lee1@Dell.com 4379771b8ccSJustin.Lee1@Dell.com package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 4389771b8ccSJustin.Lee1@Dell.com channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); 4399771b8ccSJustin.Lee1@Dell.com 4409771b8ccSJustin.Lee1@Dell.com if (package_id >= NCSI_MAX_PACKAGE || channel_id >= NCSI_MAX_CHANNEL) { 4419771b8ccSJustin.Lee1@Dell.com ret = -ERANGE; 4429771b8ccSJustin.Lee1@Dell.com goto out_netlink; 4439771b8ccSJustin.Lee1@Dell.com } 4449771b8ccSJustin.Lee1@Dell.com 4459771b8ccSJustin.Lee1@Dell.com len = nla_len(info->attrs[NCSI_ATTR_DATA]); 4469771b8ccSJustin.Lee1@Dell.com if (len < sizeof(struct ncsi_pkt_hdr)) { 4479771b8ccSJustin.Lee1@Dell.com netdev_info(ndp->ndev.dev, "NCSI: no command to send %u\n", 4489771b8ccSJustin.Lee1@Dell.com package_id); 4499771b8ccSJustin.Lee1@Dell.com ret = -EINVAL; 4509771b8ccSJustin.Lee1@Dell.com goto out_netlink; 4519771b8ccSJustin.Lee1@Dell.com } else { 4529771b8ccSJustin.Lee1@Dell.com data = (unsigned char *)nla_data(info->attrs[NCSI_ATTR_DATA]); 4539771b8ccSJustin.Lee1@Dell.com } 4549771b8ccSJustin.Lee1@Dell.com 4559771b8ccSJustin.Lee1@Dell.com hdr = (struct ncsi_pkt_hdr *)data; 4569771b8ccSJustin.Lee1@Dell.com 4579771b8ccSJustin.Lee1@Dell.com nca.ndp = ndp; 4589771b8ccSJustin.Lee1@Dell.com nca.package = (unsigned char)package_id; 4599771b8ccSJustin.Lee1@Dell.com nca.channel = (unsigned char)channel_id; 4609771b8ccSJustin.Lee1@Dell.com nca.type = hdr->type; 4619771b8ccSJustin.Lee1@Dell.com nca.req_flags = NCSI_REQ_FLAG_NETLINK_DRIVEN; 4629771b8ccSJustin.Lee1@Dell.com nca.info = info; 4639771b8ccSJustin.Lee1@Dell.com nca.payload = ntohs(hdr->length); 4649771b8ccSJustin.Lee1@Dell.com nca.data = data + sizeof(*hdr); 4659771b8ccSJustin.Lee1@Dell.com 4669771b8ccSJustin.Lee1@Dell.com ret = ncsi_xmit_cmd(&nca); 4679771b8ccSJustin.Lee1@Dell.com out_netlink: 4689771b8ccSJustin.Lee1@Dell.com if (ret != 0) { 4699771b8ccSJustin.Lee1@Dell.com netdev_err(ndp->ndev.dev, 4709771b8ccSJustin.Lee1@Dell.com "NCSI: Error %d sending command\n", 4719771b8ccSJustin.Lee1@Dell.com ret); 4729771b8ccSJustin.Lee1@Dell.com ncsi_send_netlink_err(ndp->ndev.dev, 4739771b8ccSJustin.Lee1@Dell.com info->snd_seq, 4749771b8ccSJustin.Lee1@Dell.com info->snd_portid, 4759771b8ccSJustin.Lee1@Dell.com info->nlhdr, 4769771b8ccSJustin.Lee1@Dell.com ret); 4779771b8ccSJustin.Lee1@Dell.com } 4789771b8ccSJustin.Lee1@Dell.com out: 4799771b8ccSJustin.Lee1@Dell.com return ret; 4809771b8ccSJustin.Lee1@Dell.com } 4819771b8ccSJustin.Lee1@Dell.com 4829771b8ccSJustin.Lee1@Dell.com int ncsi_send_netlink_rsp(struct ncsi_request *nr, 4839771b8ccSJustin.Lee1@Dell.com struct ncsi_package *np, 4849771b8ccSJustin.Lee1@Dell.com struct ncsi_channel *nc) 4859771b8ccSJustin.Lee1@Dell.com { 4869771b8ccSJustin.Lee1@Dell.com struct sk_buff *skb; 4879771b8ccSJustin.Lee1@Dell.com struct net *net; 4889771b8ccSJustin.Lee1@Dell.com void *hdr; 4899771b8ccSJustin.Lee1@Dell.com int rc; 4909771b8ccSJustin.Lee1@Dell.com 4919771b8ccSJustin.Lee1@Dell.com net = dev_net(nr->rsp->dev); 4929771b8ccSJustin.Lee1@Dell.com 4939771b8ccSJustin.Lee1@Dell.com skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 4949771b8ccSJustin.Lee1@Dell.com if (!skb) 4959771b8ccSJustin.Lee1@Dell.com return -ENOMEM; 4969771b8ccSJustin.Lee1@Dell.com 4979771b8ccSJustin.Lee1@Dell.com hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq, 4989771b8ccSJustin.Lee1@Dell.com &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD); 4999771b8ccSJustin.Lee1@Dell.com if (!hdr) { 5009771b8ccSJustin.Lee1@Dell.com kfree_skb(skb); 5019771b8ccSJustin.Lee1@Dell.com return -EMSGSIZE; 5029771b8ccSJustin.Lee1@Dell.com } 5039771b8ccSJustin.Lee1@Dell.com 5049771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->rsp->dev->ifindex); 5059771b8ccSJustin.Lee1@Dell.com if (np) 5069771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id); 5079771b8ccSJustin.Lee1@Dell.com if (nc) 5089771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id); 5099771b8ccSJustin.Lee1@Dell.com else 5109771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL); 5119771b8ccSJustin.Lee1@Dell.com 5129771b8ccSJustin.Lee1@Dell.com rc = nla_put(skb, NCSI_ATTR_DATA, nr->rsp->len, (void *)nr->rsp->data); 5139771b8ccSJustin.Lee1@Dell.com if (rc) 5149771b8ccSJustin.Lee1@Dell.com goto err; 5159771b8ccSJustin.Lee1@Dell.com 5169771b8ccSJustin.Lee1@Dell.com genlmsg_end(skb, hdr); 5179771b8ccSJustin.Lee1@Dell.com return genlmsg_unicast(net, skb, nr->snd_portid); 5189771b8ccSJustin.Lee1@Dell.com 5199771b8ccSJustin.Lee1@Dell.com err: 5209771b8ccSJustin.Lee1@Dell.com kfree_skb(skb); 5219771b8ccSJustin.Lee1@Dell.com return rc; 5229771b8ccSJustin.Lee1@Dell.com } 5239771b8ccSJustin.Lee1@Dell.com 5249771b8ccSJustin.Lee1@Dell.com int ncsi_send_netlink_timeout(struct ncsi_request *nr, 5259771b8ccSJustin.Lee1@Dell.com struct ncsi_package *np, 5269771b8ccSJustin.Lee1@Dell.com struct ncsi_channel *nc) 5279771b8ccSJustin.Lee1@Dell.com { 5289771b8ccSJustin.Lee1@Dell.com struct sk_buff *skb; 5299771b8ccSJustin.Lee1@Dell.com struct net *net; 5309771b8ccSJustin.Lee1@Dell.com void *hdr; 5319771b8ccSJustin.Lee1@Dell.com 5329771b8ccSJustin.Lee1@Dell.com skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 5339771b8ccSJustin.Lee1@Dell.com if (!skb) 5349771b8ccSJustin.Lee1@Dell.com return -ENOMEM; 5359771b8ccSJustin.Lee1@Dell.com 5369771b8ccSJustin.Lee1@Dell.com hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq, 5379771b8ccSJustin.Lee1@Dell.com &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD); 5389771b8ccSJustin.Lee1@Dell.com if (!hdr) { 5399771b8ccSJustin.Lee1@Dell.com kfree_skb(skb); 5409771b8ccSJustin.Lee1@Dell.com return -EMSGSIZE; 5419771b8ccSJustin.Lee1@Dell.com } 5429771b8ccSJustin.Lee1@Dell.com 5439771b8ccSJustin.Lee1@Dell.com net = dev_net(nr->cmd->dev); 5449771b8ccSJustin.Lee1@Dell.com 5459771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->cmd->dev->ifindex); 5469771b8ccSJustin.Lee1@Dell.com 5479771b8ccSJustin.Lee1@Dell.com if (np) 5489771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id); 5499771b8ccSJustin.Lee1@Dell.com else 5509771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, 5519771b8ccSJustin.Lee1@Dell.com NCSI_PACKAGE_INDEX((((struct ncsi_pkt_hdr *) 5529771b8ccSJustin.Lee1@Dell.com nr->cmd->data)->channel))); 5539771b8ccSJustin.Lee1@Dell.com 5549771b8ccSJustin.Lee1@Dell.com if (nc) 5559771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id); 5569771b8ccSJustin.Lee1@Dell.com else 5579771b8ccSJustin.Lee1@Dell.com nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL); 5589771b8ccSJustin.Lee1@Dell.com 5599771b8ccSJustin.Lee1@Dell.com genlmsg_end(skb, hdr); 5609771b8ccSJustin.Lee1@Dell.com return genlmsg_unicast(net, skb, nr->snd_portid); 5619771b8ccSJustin.Lee1@Dell.com } 5629771b8ccSJustin.Lee1@Dell.com 5639771b8ccSJustin.Lee1@Dell.com int ncsi_send_netlink_err(struct net_device *dev, 5649771b8ccSJustin.Lee1@Dell.com u32 snd_seq, 5659771b8ccSJustin.Lee1@Dell.com u32 snd_portid, 5669771b8ccSJustin.Lee1@Dell.com struct nlmsghdr *nlhdr, 5679771b8ccSJustin.Lee1@Dell.com int err) 5689771b8ccSJustin.Lee1@Dell.com { 5699771b8ccSJustin.Lee1@Dell.com struct nlmsghdr *nlh; 5709771b8ccSJustin.Lee1@Dell.com struct nlmsgerr *nle; 5719771b8ccSJustin.Lee1@Dell.com struct sk_buff *skb; 5729771b8ccSJustin.Lee1@Dell.com struct net *net; 5739771b8ccSJustin.Lee1@Dell.com 5749771b8ccSJustin.Lee1@Dell.com skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 5759771b8ccSJustin.Lee1@Dell.com if (!skb) 5769771b8ccSJustin.Lee1@Dell.com return -ENOMEM; 5779771b8ccSJustin.Lee1@Dell.com 5789771b8ccSJustin.Lee1@Dell.com net = dev_net(dev); 5799771b8ccSJustin.Lee1@Dell.com 5809771b8ccSJustin.Lee1@Dell.com nlh = nlmsg_put(skb, snd_portid, snd_seq, 5819771b8ccSJustin.Lee1@Dell.com NLMSG_ERROR, sizeof(*nle), 0); 5829771b8ccSJustin.Lee1@Dell.com nle = (struct nlmsgerr *)nlmsg_data(nlh); 5839771b8ccSJustin.Lee1@Dell.com nle->error = err; 5849771b8ccSJustin.Lee1@Dell.com memcpy(&nle->msg, nlhdr, sizeof(*nlh)); 5859771b8ccSJustin.Lee1@Dell.com 5869771b8ccSJustin.Lee1@Dell.com nlmsg_end(skb, nlh); 5879771b8ccSJustin.Lee1@Dell.com 5889771b8ccSJustin.Lee1@Dell.com return nlmsg_unicast(net->genl_sock, skb, snd_portid); 5899771b8ccSJustin.Lee1@Dell.com } 5909771b8ccSJustin.Lee1@Dell.com 5918d951a75SSamuel Mendoza-Jonas static int ncsi_set_package_mask_nl(struct sk_buff *msg, 5928d951a75SSamuel Mendoza-Jonas struct genl_info *info) 5938d951a75SSamuel Mendoza-Jonas { 5948d951a75SSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 5958d951a75SSamuel Mendoza-Jonas unsigned long flags; 5968d951a75SSamuel Mendoza-Jonas int rc; 5978d951a75SSamuel Mendoza-Jonas 5988d951a75SSamuel Mendoza-Jonas if (!info || !info->attrs) 5998d951a75SSamuel Mendoza-Jonas return -EINVAL; 6008d951a75SSamuel Mendoza-Jonas 6018d951a75SSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_IFINDEX]) 6028d951a75SSamuel Mendoza-Jonas return -EINVAL; 6038d951a75SSamuel Mendoza-Jonas 6048d951a75SSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_PACKAGE_MASK]) 6058d951a75SSamuel Mendoza-Jonas return -EINVAL; 6068d951a75SSamuel Mendoza-Jonas 6078d951a75SSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 6088d951a75SSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 6098d951a75SSamuel Mendoza-Jonas if (!ndp) 6108d951a75SSamuel Mendoza-Jonas return -ENODEV; 6118d951a75SSamuel Mendoza-Jonas 6128d951a75SSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags); 6138d951a75SSamuel Mendoza-Jonas if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { 6148d951a75SSamuel Mendoza-Jonas if (ndp->flags & NCSI_DEV_HWA) { 6158d951a75SSamuel Mendoza-Jonas ndp->multi_package = true; 6168d951a75SSamuel Mendoza-Jonas rc = 0; 6178d951a75SSamuel Mendoza-Jonas } else { 6188d951a75SSamuel Mendoza-Jonas netdev_err(ndp->ndev.dev, 6198d951a75SSamuel Mendoza-Jonas "NCSI: Can't use multiple packages without HWA\n"); 6208d951a75SSamuel Mendoza-Jonas rc = -EPERM; 6218d951a75SSamuel Mendoza-Jonas } 6228d951a75SSamuel Mendoza-Jonas } else { 6238d951a75SSamuel Mendoza-Jonas ndp->multi_package = false; 6248d951a75SSamuel Mendoza-Jonas rc = 0; 6258d951a75SSamuel Mendoza-Jonas } 6268d951a75SSamuel Mendoza-Jonas 6278d951a75SSamuel Mendoza-Jonas if (!rc) 6288d951a75SSamuel Mendoza-Jonas ndp->package_whitelist = 6298d951a75SSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]); 6308d951a75SSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags); 6318d951a75SSamuel Mendoza-Jonas 6328d951a75SSamuel Mendoza-Jonas if (!rc) { 6338d951a75SSamuel Mendoza-Jonas /* Update channel configuration */ 6348d951a75SSamuel Mendoza-Jonas if (!(ndp->flags & NCSI_DEV_RESET)) 6358d951a75SSamuel Mendoza-Jonas ncsi_reset_dev(&ndp->ndev); 6368d951a75SSamuel Mendoza-Jonas } 6378d951a75SSamuel Mendoza-Jonas 6388d951a75SSamuel Mendoza-Jonas return rc; 6398d951a75SSamuel Mendoza-Jonas } 6408d951a75SSamuel Mendoza-Jonas 6418d951a75SSamuel Mendoza-Jonas static int ncsi_set_channel_mask_nl(struct sk_buff *msg, 6428d951a75SSamuel Mendoza-Jonas struct genl_info *info) 6438d951a75SSamuel Mendoza-Jonas { 6448d951a75SSamuel Mendoza-Jonas struct ncsi_package *np, *package; 6458d951a75SSamuel Mendoza-Jonas struct ncsi_channel *nc, *channel; 6468d951a75SSamuel Mendoza-Jonas u32 package_id, channel_id; 6478d951a75SSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 6488d951a75SSamuel Mendoza-Jonas unsigned long flags; 6498d951a75SSamuel Mendoza-Jonas 6508d951a75SSamuel Mendoza-Jonas if (!info || !info->attrs) 6518d951a75SSamuel Mendoza-Jonas return -EINVAL; 6528d951a75SSamuel Mendoza-Jonas 6538d951a75SSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_IFINDEX]) 6548d951a75SSamuel Mendoza-Jonas return -EINVAL; 6558d951a75SSamuel Mendoza-Jonas 6568d951a75SSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) 6578d951a75SSamuel Mendoza-Jonas return -EINVAL; 6588d951a75SSamuel Mendoza-Jonas 6598d951a75SSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_CHANNEL_MASK]) 6608d951a75SSamuel Mendoza-Jonas return -EINVAL; 6618d951a75SSamuel Mendoza-Jonas 6628d951a75SSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 6638d951a75SSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 6648d951a75SSamuel Mendoza-Jonas if (!ndp) 6658d951a75SSamuel Mendoza-Jonas return -ENODEV; 6668d951a75SSamuel Mendoza-Jonas 6678d951a75SSamuel Mendoza-Jonas package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 6688d951a75SSamuel Mendoza-Jonas package = NULL; 6698d951a75SSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) 6708d951a75SSamuel Mendoza-Jonas if (np->id == package_id) { 6718d951a75SSamuel Mendoza-Jonas package = np; 6728d951a75SSamuel Mendoza-Jonas break; 6738d951a75SSamuel Mendoza-Jonas } 6748d951a75SSamuel Mendoza-Jonas if (!package) 6758d951a75SSamuel Mendoza-Jonas return -ERANGE; 6768d951a75SSamuel Mendoza-Jonas 6778d951a75SSamuel Mendoza-Jonas spin_lock_irqsave(&package->lock, flags); 6788d951a75SSamuel Mendoza-Jonas 6798d951a75SSamuel Mendoza-Jonas channel = NULL; 6808d951a75SSamuel Mendoza-Jonas if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { 6818d951a75SSamuel Mendoza-Jonas channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); 6828d951a75SSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(np, nc) 6838d951a75SSamuel Mendoza-Jonas if (nc->id == channel_id) { 6848d951a75SSamuel Mendoza-Jonas channel = nc; 6858d951a75SSamuel Mendoza-Jonas break; 6868d951a75SSamuel Mendoza-Jonas } 6878d951a75SSamuel Mendoza-Jonas if (!channel) { 6888d951a75SSamuel Mendoza-Jonas spin_unlock_irqrestore(&package->lock, flags); 6898d951a75SSamuel Mendoza-Jonas return -ERANGE; 6908d951a75SSamuel Mendoza-Jonas } 6918d951a75SSamuel Mendoza-Jonas netdev_dbg(ndp->ndev.dev, 6928d951a75SSamuel Mendoza-Jonas "NCSI: Channel %u set as preferred channel\n", 6938d951a75SSamuel Mendoza-Jonas channel->id); 6948d951a75SSamuel Mendoza-Jonas } 6958d951a75SSamuel Mendoza-Jonas 6968d951a75SSamuel Mendoza-Jonas package->channel_whitelist = 6978d951a75SSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]); 6988d951a75SSamuel Mendoza-Jonas if (package->channel_whitelist == 0) 6998d951a75SSamuel Mendoza-Jonas netdev_dbg(ndp->ndev.dev, 7008d951a75SSamuel Mendoza-Jonas "NCSI: Package %u set to all channels disabled\n", 7018d951a75SSamuel Mendoza-Jonas package->id); 7028d951a75SSamuel Mendoza-Jonas 7038d951a75SSamuel Mendoza-Jonas package->preferred_channel = channel; 7048d951a75SSamuel Mendoza-Jonas 7058d951a75SSamuel Mendoza-Jonas if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { 7068d951a75SSamuel Mendoza-Jonas package->multi_channel = true; 7078d951a75SSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, 7088d951a75SSamuel Mendoza-Jonas "NCSI: Multi-channel enabled on package %u\n", 7098d951a75SSamuel Mendoza-Jonas package_id); 7108d951a75SSamuel Mendoza-Jonas } else { 7118d951a75SSamuel Mendoza-Jonas package->multi_channel = false; 7128d951a75SSamuel Mendoza-Jonas } 7138d951a75SSamuel Mendoza-Jonas 7148d951a75SSamuel Mendoza-Jonas spin_unlock_irqrestore(&package->lock, flags); 7158d951a75SSamuel Mendoza-Jonas 7168d951a75SSamuel Mendoza-Jonas /* Update channel configuration */ 7178d951a75SSamuel Mendoza-Jonas if (!(ndp->flags & NCSI_DEV_RESET)) 7188d951a75SSamuel Mendoza-Jonas ncsi_reset_dev(&ndp->ndev); 7198d951a75SSamuel Mendoza-Jonas 7208d951a75SSamuel Mendoza-Jonas return 0; 7218d951a75SSamuel Mendoza-Jonas } 7228d951a75SSamuel Mendoza-Jonas 723955dc68cSSamuel Mendoza-Jonas static const struct genl_ops ncsi_ops[] = { 724955dc68cSSamuel Mendoza-Jonas { 725955dc68cSSamuel Mendoza-Jonas .cmd = NCSI_CMD_PKG_INFO, 726955dc68cSSamuel Mendoza-Jonas .doit = ncsi_pkg_info_nl, 727955dc68cSSamuel Mendoza-Jonas .dumpit = ncsi_pkg_info_all_nl, 728955dc68cSSamuel Mendoza-Jonas .flags = 0, 729955dc68cSSamuel Mendoza-Jonas }, 730955dc68cSSamuel Mendoza-Jonas { 731955dc68cSSamuel Mendoza-Jonas .cmd = NCSI_CMD_SET_INTERFACE, 732955dc68cSSamuel Mendoza-Jonas .doit = ncsi_set_interface_nl, 733955dc68cSSamuel Mendoza-Jonas .flags = GENL_ADMIN_PERM, 734955dc68cSSamuel Mendoza-Jonas }, 735955dc68cSSamuel Mendoza-Jonas { 736955dc68cSSamuel Mendoza-Jonas .cmd = NCSI_CMD_CLEAR_INTERFACE, 737955dc68cSSamuel Mendoza-Jonas .doit = ncsi_clear_interface_nl, 738955dc68cSSamuel Mendoza-Jonas .flags = GENL_ADMIN_PERM, 739955dc68cSSamuel Mendoza-Jonas }, 7409771b8ccSJustin.Lee1@Dell.com { 7419771b8ccSJustin.Lee1@Dell.com .cmd = NCSI_CMD_SEND_CMD, 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, 7478d951a75SSamuel Mendoza-Jonas .doit = ncsi_set_package_mask_nl, 7488d951a75SSamuel Mendoza-Jonas .flags = GENL_ADMIN_PERM, 7498d951a75SSamuel Mendoza-Jonas }, 7508d951a75SSamuel Mendoza-Jonas { 7518d951a75SSamuel Mendoza-Jonas .cmd = NCSI_CMD_SET_CHANNEL_MASK, 7528d951a75SSamuel Mendoza-Jonas .doit = ncsi_set_channel_mask_nl, 7538d951a75SSamuel Mendoza-Jonas .flags = GENL_ADMIN_PERM, 7548d951a75SSamuel Mendoza-Jonas }, 755955dc68cSSamuel Mendoza-Jonas }; 756955dc68cSSamuel Mendoza-Jonas 757955dc68cSSamuel Mendoza-Jonas static struct genl_family ncsi_genl_family __ro_after_init = { 758955dc68cSSamuel Mendoza-Jonas .name = "NCSI", 759955dc68cSSamuel Mendoza-Jonas .version = 0, 760955dc68cSSamuel Mendoza-Jonas .maxattr = NCSI_ATTR_MAX, 7613b0f31f2SJohannes Berg .policy = ncsi_genl_policy, 762955dc68cSSamuel Mendoza-Jonas .module = THIS_MODULE, 763955dc68cSSamuel Mendoza-Jonas .ops = ncsi_ops, 764955dc68cSSamuel Mendoza-Jonas .n_ops = ARRAY_SIZE(ncsi_ops), 765955dc68cSSamuel Mendoza-Jonas }; 766955dc68cSSamuel Mendoza-Jonas 767955dc68cSSamuel Mendoza-Jonas int ncsi_init_netlink(struct net_device *dev) 768955dc68cSSamuel Mendoza-Jonas { 769955dc68cSSamuel Mendoza-Jonas int rc; 770955dc68cSSamuel Mendoza-Jonas 771955dc68cSSamuel Mendoza-Jonas rc = genl_register_family(&ncsi_genl_family); 772955dc68cSSamuel Mendoza-Jonas if (rc) 773955dc68cSSamuel Mendoza-Jonas netdev_err(dev, "ncsi: failed to register netlink family\n"); 774955dc68cSSamuel Mendoza-Jonas 775955dc68cSSamuel Mendoza-Jonas return rc; 776955dc68cSSamuel Mendoza-Jonas } 777955dc68cSSamuel Mendoza-Jonas 778955dc68cSSamuel Mendoza-Jonas int ncsi_unregister_netlink(struct net_device *dev) 779955dc68cSSamuel Mendoza-Jonas { 780955dc68cSSamuel Mendoza-Jonas int rc; 781955dc68cSSamuel Mendoza-Jonas 782955dc68cSSamuel Mendoza-Jonas rc = genl_unregister_family(&ncsi_genl_family); 783955dc68cSSamuel Mendoza-Jonas if (rc) 784955dc68cSSamuel Mendoza-Jonas netdev_err(dev, "ncsi: failed to unregister netlink family\n"); 785955dc68cSSamuel Mendoza-Jonas 786955dc68cSSamuel Mendoza-Jonas return rc; 787955dc68cSSamuel Mendoza-Jonas } 788