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 <linux/module.h> 16955dc68cSSamuel Mendoza-Jonas #include <net/genetlink.h> 17955dc68cSSamuel Mendoza-Jonas #include <net/ncsi.h> 18955dc68cSSamuel Mendoza-Jonas #include <linux/skbuff.h> 19955dc68cSSamuel Mendoza-Jonas #include <net/sock.h> 20955dc68cSSamuel Mendoza-Jonas #include <uapi/linux/ncsi.h> 21955dc68cSSamuel Mendoza-Jonas 22955dc68cSSamuel Mendoza-Jonas #include "internal.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 }, 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 { 61955dc68cSSamuel Mendoza-Jonas struct nlattr *vid_nest; 62955dc68cSSamuel Mendoza-Jonas struct ncsi_channel_filter *ncf; 63955dc68cSSamuel Mendoza-Jonas struct ncsi_channel_mode *m; 64955dc68cSSamuel Mendoza-Jonas u32 *data; 65955dc68cSSamuel Mendoza-Jonas int i; 66955dc68cSSamuel Mendoza-Jonas 67955dc68cSSamuel Mendoza-Jonas nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id); 68955dc68cSSamuel Mendoza-Jonas m = &nc->modes[NCSI_MODE_LINK]; 69955dc68cSSamuel Mendoza-Jonas nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]); 70955dc68cSSamuel Mendoza-Jonas if (nc->state == NCSI_CHANNEL_ACTIVE) 71955dc68cSSamuel Mendoza-Jonas nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE); 72955dc68cSSamuel Mendoza-Jonas if (ndp->force_channel == nc) 73955dc68cSSamuel Mendoza-Jonas nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED); 74955dc68cSSamuel Mendoza-Jonas 75955dc68cSSamuel Mendoza-Jonas nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version); 76955dc68cSSamuel Mendoza-Jonas nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MINOR, nc->version.alpha2); 77955dc68cSSamuel Mendoza-Jonas nla_put_string(skb, NCSI_CHANNEL_ATTR_VERSION_STR, nc->version.fw_name); 78955dc68cSSamuel Mendoza-Jonas 79955dc68cSSamuel Mendoza-Jonas vid_nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR_VLAN_LIST); 80955dc68cSSamuel Mendoza-Jonas if (!vid_nest) 81955dc68cSSamuel Mendoza-Jonas return -ENOMEM; 82955dc68cSSamuel Mendoza-Jonas ncf = nc->filters[NCSI_FILTER_VLAN]; 83955dc68cSSamuel Mendoza-Jonas i = -1; 84955dc68cSSamuel Mendoza-Jonas if (ncf) { 85955dc68cSSamuel Mendoza-Jonas while ((i = find_next_bit((void *)&ncf->bitmap, ncf->total, 86955dc68cSSamuel Mendoza-Jonas i + 1)) < ncf->total) { 87955dc68cSSamuel Mendoza-Jonas data = ncsi_get_filter(nc, NCSI_FILTER_VLAN, i); 88955dc68cSSamuel Mendoza-Jonas /* Uninitialised channels will have 'zero' vlan ids */ 89955dc68cSSamuel Mendoza-Jonas if (!data || !*data) 90955dc68cSSamuel Mendoza-Jonas continue; 91955dc68cSSamuel Mendoza-Jonas nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID, 92955dc68cSSamuel Mendoza-Jonas *(u16 *)data); 93955dc68cSSamuel Mendoza-Jonas } 94955dc68cSSamuel Mendoza-Jonas } 95955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, vid_nest); 96955dc68cSSamuel Mendoza-Jonas 97955dc68cSSamuel Mendoza-Jonas return 0; 98955dc68cSSamuel Mendoza-Jonas } 99955dc68cSSamuel Mendoza-Jonas 100955dc68cSSamuel Mendoza-Jonas static int ncsi_write_package_info(struct sk_buff *skb, 101955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp, unsigned int id) 102955dc68cSSamuel Mendoza-Jonas { 103955dc68cSSamuel Mendoza-Jonas struct nlattr *pnest, *cnest, *nest; 104955dc68cSSamuel Mendoza-Jonas struct ncsi_package *np; 105955dc68cSSamuel Mendoza-Jonas struct ncsi_channel *nc; 106955dc68cSSamuel Mendoza-Jonas bool found; 107955dc68cSSamuel Mendoza-Jonas int rc; 108955dc68cSSamuel Mendoza-Jonas 109955dc68cSSamuel Mendoza-Jonas if (id > ndp->package_num) { 110955dc68cSSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, "NCSI: No package with id %u\n", id); 111955dc68cSSamuel Mendoza-Jonas return -ENODEV; 112955dc68cSSamuel Mendoza-Jonas } 113955dc68cSSamuel Mendoza-Jonas 114955dc68cSSamuel Mendoza-Jonas found = false; 115955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) { 116955dc68cSSamuel Mendoza-Jonas if (np->id != id) 117955dc68cSSamuel Mendoza-Jonas continue; 118955dc68cSSamuel Mendoza-Jonas pnest = nla_nest_start(skb, NCSI_PKG_ATTR); 119955dc68cSSamuel Mendoza-Jonas if (!pnest) 120955dc68cSSamuel Mendoza-Jonas return -ENOMEM; 121955dc68cSSamuel Mendoza-Jonas nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id); 122955dc68cSSamuel Mendoza-Jonas if (ndp->force_package == np) 123955dc68cSSamuel Mendoza-Jonas nla_put_flag(skb, NCSI_PKG_ATTR_FORCED); 124955dc68cSSamuel Mendoza-Jonas cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST); 125955dc68cSSamuel Mendoza-Jonas if (!cnest) { 126955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, pnest); 127955dc68cSSamuel Mendoza-Jonas return -ENOMEM; 128955dc68cSSamuel Mendoza-Jonas } 129955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(np, nc) { 130955dc68cSSamuel Mendoza-Jonas nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR); 131955dc68cSSamuel Mendoza-Jonas if (!nest) { 132955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, cnest); 133955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, pnest); 134955dc68cSSamuel Mendoza-Jonas return -ENOMEM; 135955dc68cSSamuel Mendoza-Jonas } 136955dc68cSSamuel Mendoza-Jonas rc = ncsi_write_channel_info(skb, ndp, nc); 137955dc68cSSamuel Mendoza-Jonas if (rc) { 138955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, nest); 139955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, cnest); 140955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, pnest); 141955dc68cSSamuel Mendoza-Jonas return rc; 142955dc68cSSamuel Mendoza-Jonas } 143955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, nest); 144955dc68cSSamuel Mendoza-Jonas } 145955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, cnest); 146955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, pnest); 147955dc68cSSamuel Mendoza-Jonas found = true; 148955dc68cSSamuel Mendoza-Jonas } 149955dc68cSSamuel Mendoza-Jonas 150955dc68cSSamuel Mendoza-Jonas if (!found) 151955dc68cSSamuel Mendoza-Jonas return -ENODEV; 152955dc68cSSamuel Mendoza-Jonas 153955dc68cSSamuel Mendoza-Jonas return 0; 154955dc68cSSamuel Mendoza-Jonas } 155955dc68cSSamuel Mendoza-Jonas 156955dc68cSSamuel Mendoza-Jonas static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info) 157955dc68cSSamuel Mendoza-Jonas { 158955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 159955dc68cSSamuel Mendoza-Jonas unsigned int package_id; 160955dc68cSSamuel Mendoza-Jonas struct sk_buff *skb; 161955dc68cSSamuel Mendoza-Jonas struct nlattr *attr; 162955dc68cSSamuel Mendoza-Jonas void *hdr; 163955dc68cSSamuel Mendoza-Jonas int rc; 164955dc68cSSamuel Mendoza-Jonas 165955dc68cSSamuel Mendoza-Jonas if (!info || !info->attrs) 166955dc68cSSamuel Mendoza-Jonas return -EINVAL; 167955dc68cSSamuel Mendoza-Jonas 168955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_IFINDEX]) 169955dc68cSSamuel Mendoza-Jonas return -EINVAL; 170955dc68cSSamuel Mendoza-Jonas 171955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) 172955dc68cSSamuel Mendoza-Jonas return -EINVAL; 173955dc68cSSamuel Mendoza-Jonas 174955dc68cSSamuel Mendoza-Jonas ndp = ndp_from_ifindex(genl_info_net(info), 175955dc68cSSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 176955dc68cSSamuel Mendoza-Jonas if (!ndp) 177955dc68cSSamuel Mendoza-Jonas return -ENODEV; 178955dc68cSSamuel Mendoza-Jonas 179955dc68cSSamuel Mendoza-Jonas skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 180955dc68cSSamuel Mendoza-Jonas if (!skb) 181955dc68cSSamuel Mendoza-Jonas return -ENOMEM; 182955dc68cSSamuel Mendoza-Jonas 183955dc68cSSamuel Mendoza-Jonas hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq, 184955dc68cSSamuel Mendoza-Jonas &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO); 185955dc68cSSamuel Mendoza-Jonas if (!hdr) { 18650db64b0SDan Carpenter kfree_skb(skb); 187955dc68cSSamuel Mendoza-Jonas return -EMSGSIZE; 188955dc68cSSamuel Mendoza-Jonas } 189955dc68cSSamuel Mendoza-Jonas 190955dc68cSSamuel Mendoza-Jonas package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 191955dc68cSSamuel Mendoza-Jonas 192955dc68cSSamuel Mendoza-Jonas attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST); 193955dc68cSSamuel Mendoza-Jonas rc = ncsi_write_package_info(skb, ndp, package_id); 194955dc68cSSamuel Mendoza-Jonas 195955dc68cSSamuel Mendoza-Jonas if (rc) { 196955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, attr); 197955dc68cSSamuel Mendoza-Jonas goto err; 198955dc68cSSamuel Mendoza-Jonas } 199955dc68cSSamuel Mendoza-Jonas 200955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, attr); 201955dc68cSSamuel Mendoza-Jonas 202955dc68cSSamuel Mendoza-Jonas genlmsg_end(skb, hdr); 203955dc68cSSamuel Mendoza-Jonas return genlmsg_reply(skb, info); 204955dc68cSSamuel Mendoza-Jonas 205955dc68cSSamuel Mendoza-Jonas err: 206955dc68cSSamuel Mendoza-Jonas genlmsg_cancel(skb, hdr); 20750db64b0SDan Carpenter kfree_skb(skb); 208955dc68cSSamuel Mendoza-Jonas return rc; 209955dc68cSSamuel Mendoza-Jonas } 210955dc68cSSamuel Mendoza-Jonas 211955dc68cSSamuel Mendoza-Jonas static int ncsi_pkg_info_all_nl(struct sk_buff *skb, 212955dc68cSSamuel Mendoza-Jonas struct netlink_callback *cb) 213955dc68cSSamuel Mendoza-Jonas { 214955dc68cSSamuel Mendoza-Jonas struct nlattr *attrs[NCSI_ATTR_MAX]; 215955dc68cSSamuel Mendoza-Jonas struct ncsi_package *np, *package; 216955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 217955dc68cSSamuel Mendoza-Jonas unsigned int package_id; 218955dc68cSSamuel Mendoza-Jonas struct nlattr *attr; 219955dc68cSSamuel Mendoza-Jonas void *hdr; 220955dc68cSSamuel Mendoza-Jonas int rc; 221955dc68cSSamuel Mendoza-Jonas 222955dc68cSSamuel Mendoza-Jonas rc = genlmsg_parse(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX, 223955dc68cSSamuel Mendoza-Jonas ncsi_genl_policy, NULL); 224955dc68cSSamuel Mendoza-Jonas if (rc) 225955dc68cSSamuel Mendoza-Jonas return rc; 226955dc68cSSamuel Mendoza-Jonas 227955dc68cSSamuel Mendoza-Jonas if (!attrs[NCSI_ATTR_IFINDEX]) 228955dc68cSSamuel Mendoza-Jonas return -EINVAL; 229955dc68cSSamuel Mendoza-Jonas 230955dc68cSSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)), 231955dc68cSSamuel Mendoza-Jonas nla_get_u32(attrs[NCSI_ATTR_IFINDEX])); 232955dc68cSSamuel Mendoza-Jonas 233955dc68cSSamuel Mendoza-Jonas if (!ndp) 234955dc68cSSamuel Mendoza-Jonas return -ENODEV; 235955dc68cSSamuel Mendoza-Jonas 236955dc68cSSamuel Mendoza-Jonas package_id = cb->args[0]; 237955dc68cSSamuel Mendoza-Jonas package = NULL; 238955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) 239955dc68cSSamuel Mendoza-Jonas if (np->id == package_id) 240955dc68cSSamuel Mendoza-Jonas package = np; 241955dc68cSSamuel Mendoza-Jonas 242955dc68cSSamuel Mendoza-Jonas if (!package) 243955dc68cSSamuel Mendoza-Jonas return 0; /* done */ 244955dc68cSSamuel Mendoza-Jonas 245955dc68cSSamuel Mendoza-Jonas hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 246955dc68cSSamuel Mendoza-Jonas &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO); 247955dc68cSSamuel Mendoza-Jonas if (!hdr) { 248955dc68cSSamuel Mendoza-Jonas rc = -EMSGSIZE; 249955dc68cSSamuel Mendoza-Jonas goto err; 250955dc68cSSamuel Mendoza-Jonas } 251955dc68cSSamuel Mendoza-Jonas 252955dc68cSSamuel Mendoza-Jonas attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST); 253955dc68cSSamuel Mendoza-Jonas rc = ncsi_write_package_info(skb, ndp, package->id); 254955dc68cSSamuel Mendoza-Jonas if (rc) { 255955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, attr); 256955dc68cSSamuel Mendoza-Jonas goto err; 257955dc68cSSamuel Mendoza-Jonas } 258955dc68cSSamuel Mendoza-Jonas 259955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, attr); 260955dc68cSSamuel Mendoza-Jonas genlmsg_end(skb, hdr); 261955dc68cSSamuel Mendoza-Jonas 262955dc68cSSamuel Mendoza-Jonas cb->args[0] = package_id + 1; 263955dc68cSSamuel Mendoza-Jonas 264955dc68cSSamuel Mendoza-Jonas return skb->len; 265955dc68cSSamuel Mendoza-Jonas err: 266955dc68cSSamuel Mendoza-Jonas genlmsg_cancel(skb, hdr); 267955dc68cSSamuel Mendoza-Jonas return rc; 268955dc68cSSamuel Mendoza-Jonas } 269955dc68cSSamuel Mendoza-Jonas 270955dc68cSSamuel Mendoza-Jonas static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info) 271955dc68cSSamuel Mendoza-Jonas { 272955dc68cSSamuel Mendoza-Jonas struct ncsi_package *np, *package; 273955dc68cSSamuel Mendoza-Jonas struct ncsi_channel *nc, *channel; 274955dc68cSSamuel Mendoza-Jonas u32 package_id, channel_id; 275955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 276955dc68cSSamuel Mendoza-Jonas unsigned long flags; 277955dc68cSSamuel Mendoza-Jonas 278955dc68cSSamuel Mendoza-Jonas if (!info || !info->attrs) 279955dc68cSSamuel Mendoza-Jonas return -EINVAL; 280955dc68cSSamuel Mendoza-Jonas 281955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_IFINDEX]) 282955dc68cSSamuel Mendoza-Jonas return -EINVAL; 283955dc68cSSamuel Mendoza-Jonas 284955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) 285955dc68cSSamuel Mendoza-Jonas return -EINVAL; 286955dc68cSSamuel Mendoza-Jonas 287955dc68cSSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 288955dc68cSSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 289955dc68cSSamuel Mendoza-Jonas if (!ndp) 290955dc68cSSamuel Mendoza-Jonas return -ENODEV; 291955dc68cSSamuel Mendoza-Jonas 292955dc68cSSamuel Mendoza-Jonas package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 293955dc68cSSamuel Mendoza-Jonas package = NULL; 294955dc68cSSamuel Mendoza-Jonas 295955dc68cSSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags); 296955dc68cSSamuel Mendoza-Jonas 297955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) 298955dc68cSSamuel Mendoza-Jonas if (np->id == package_id) 299955dc68cSSamuel Mendoza-Jonas package = np; 300955dc68cSSamuel Mendoza-Jonas if (!package) { 301955dc68cSSamuel Mendoza-Jonas /* The user has set a package that does not exist */ 302054f34daSDan Carpenter spin_unlock_irqrestore(&ndp->lock, flags); 303955dc68cSSamuel Mendoza-Jonas return -ERANGE; 304955dc68cSSamuel Mendoza-Jonas } 305955dc68cSSamuel Mendoza-Jonas 306955dc68cSSamuel Mendoza-Jonas channel = NULL; 307955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { 308955dc68cSSamuel Mendoza-Jonas /* Allow any channel */ 309955dc68cSSamuel Mendoza-Jonas channel_id = NCSI_RESERVED_CHANNEL; 310955dc68cSSamuel Mendoza-Jonas } else { 311955dc68cSSamuel Mendoza-Jonas channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); 312955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(package, nc) 313955dc68cSSamuel Mendoza-Jonas if (nc->id == channel_id) 314955dc68cSSamuel Mendoza-Jonas channel = nc; 315955dc68cSSamuel Mendoza-Jonas } 316955dc68cSSamuel Mendoza-Jonas 317955dc68cSSamuel Mendoza-Jonas if (channel_id != NCSI_RESERVED_CHANNEL && !channel) { 318955dc68cSSamuel Mendoza-Jonas /* The user has set a channel that does not exist on this 319955dc68cSSamuel Mendoza-Jonas * package 320955dc68cSSamuel Mendoza-Jonas */ 321054f34daSDan Carpenter spin_unlock_irqrestore(&ndp->lock, flags); 322955dc68cSSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n", 323955dc68cSSamuel Mendoza-Jonas channel_id); 324955dc68cSSamuel Mendoza-Jonas return -ERANGE; 325955dc68cSSamuel Mendoza-Jonas } 326955dc68cSSamuel Mendoza-Jonas 327955dc68cSSamuel Mendoza-Jonas ndp->force_package = package; 328955dc68cSSamuel Mendoza-Jonas ndp->force_channel = channel; 329955dc68cSSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags); 330955dc68cSSamuel Mendoza-Jonas 331955dc68cSSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n", 332955dc68cSSamuel Mendoza-Jonas package_id, channel_id, 333955dc68cSSamuel Mendoza-Jonas channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : ""); 334955dc68cSSamuel Mendoza-Jonas 335955dc68cSSamuel Mendoza-Jonas /* Bounce the NCSI channel to set changes */ 336955dc68cSSamuel Mendoza-Jonas ncsi_stop_dev(&ndp->ndev); 337955dc68cSSamuel Mendoza-Jonas ncsi_start_dev(&ndp->ndev); 338955dc68cSSamuel Mendoza-Jonas 339955dc68cSSamuel Mendoza-Jonas return 0; 340955dc68cSSamuel Mendoza-Jonas } 341955dc68cSSamuel Mendoza-Jonas 342955dc68cSSamuel Mendoza-Jonas static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) 343955dc68cSSamuel Mendoza-Jonas { 344955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 345955dc68cSSamuel Mendoza-Jonas unsigned long flags; 346955dc68cSSamuel Mendoza-Jonas 347955dc68cSSamuel Mendoza-Jonas if (!info || !info->attrs) 348955dc68cSSamuel Mendoza-Jonas return -EINVAL; 349955dc68cSSamuel Mendoza-Jonas 350955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_IFINDEX]) 351955dc68cSSamuel Mendoza-Jonas return -EINVAL; 352955dc68cSSamuel Mendoza-Jonas 353955dc68cSSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 354955dc68cSSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 355955dc68cSSamuel Mendoza-Jonas if (!ndp) 356955dc68cSSamuel Mendoza-Jonas return -ENODEV; 357955dc68cSSamuel Mendoza-Jonas 358955dc68cSSamuel Mendoza-Jonas /* Clear any override */ 359955dc68cSSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags); 360955dc68cSSamuel Mendoza-Jonas ndp->force_package = NULL; 361955dc68cSSamuel Mendoza-Jonas ndp->force_channel = NULL; 362955dc68cSSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags); 363955dc68cSSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n"); 364955dc68cSSamuel Mendoza-Jonas 365955dc68cSSamuel Mendoza-Jonas /* Bounce the NCSI channel to set changes */ 366955dc68cSSamuel Mendoza-Jonas ncsi_stop_dev(&ndp->ndev); 367955dc68cSSamuel Mendoza-Jonas ncsi_start_dev(&ndp->ndev); 368955dc68cSSamuel Mendoza-Jonas 369955dc68cSSamuel Mendoza-Jonas return 0; 370955dc68cSSamuel Mendoza-Jonas } 371955dc68cSSamuel Mendoza-Jonas 372955dc68cSSamuel Mendoza-Jonas static const struct genl_ops ncsi_ops[] = { 373955dc68cSSamuel Mendoza-Jonas { 374955dc68cSSamuel Mendoza-Jonas .cmd = NCSI_CMD_PKG_INFO, 375955dc68cSSamuel Mendoza-Jonas .policy = ncsi_genl_policy, 376955dc68cSSamuel Mendoza-Jonas .doit = ncsi_pkg_info_nl, 377955dc68cSSamuel Mendoza-Jonas .dumpit = ncsi_pkg_info_all_nl, 378955dc68cSSamuel Mendoza-Jonas .flags = 0, 379955dc68cSSamuel Mendoza-Jonas }, 380955dc68cSSamuel Mendoza-Jonas { 381955dc68cSSamuel Mendoza-Jonas .cmd = NCSI_CMD_SET_INTERFACE, 382955dc68cSSamuel Mendoza-Jonas .policy = ncsi_genl_policy, 383955dc68cSSamuel Mendoza-Jonas .doit = ncsi_set_interface_nl, 384955dc68cSSamuel Mendoza-Jonas .flags = GENL_ADMIN_PERM, 385955dc68cSSamuel Mendoza-Jonas }, 386955dc68cSSamuel Mendoza-Jonas { 387955dc68cSSamuel Mendoza-Jonas .cmd = NCSI_CMD_CLEAR_INTERFACE, 388955dc68cSSamuel Mendoza-Jonas .policy = ncsi_genl_policy, 389955dc68cSSamuel Mendoza-Jonas .doit = ncsi_clear_interface_nl, 390955dc68cSSamuel Mendoza-Jonas .flags = GENL_ADMIN_PERM, 391955dc68cSSamuel Mendoza-Jonas }, 392955dc68cSSamuel Mendoza-Jonas }; 393955dc68cSSamuel Mendoza-Jonas 394955dc68cSSamuel Mendoza-Jonas static struct genl_family ncsi_genl_family __ro_after_init = { 395955dc68cSSamuel Mendoza-Jonas .name = "NCSI", 396955dc68cSSamuel Mendoza-Jonas .version = 0, 397955dc68cSSamuel Mendoza-Jonas .maxattr = NCSI_ATTR_MAX, 398955dc68cSSamuel Mendoza-Jonas .module = THIS_MODULE, 399955dc68cSSamuel Mendoza-Jonas .ops = ncsi_ops, 400955dc68cSSamuel Mendoza-Jonas .n_ops = ARRAY_SIZE(ncsi_ops), 401955dc68cSSamuel Mendoza-Jonas }; 402955dc68cSSamuel Mendoza-Jonas 403955dc68cSSamuel Mendoza-Jonas int ncsi_init_netlink(struct net_device *dev) 404955dc68cSSamuel Mendoza-Jonas { 405955dc68cSSamuel Mendoza-Jonas int rc; 406955dc68cSSamuel Mendoza-Jonas 407955dc68cSSamuel Mendoza-Jonas rc = genl_register_family(&ncsi_genl_family); 408955dc68cSSamuel Mendoza-Jonas if (rc) 409955dc68cSSamuel Mendoza-Jonas netdev_err(dev, "ncsi: failed to register netlink family\n"); 410955dc68cSSamuel Mendoza-Jonas 411955dc68cSSamuel Mendoza-Jonas return rc; 412955dc68cSSamuel Mendoza-Jonas } 413955dc68cSSamuel Mendoza-Jonas 414955dc68cSSamuel Mendoza-Jonas int ncsi_unregister_netlink(struct net_device *dev) 415955dc68cSSamuel Mendoza-Jonas { 416955dc68cSSamuel Mendoza-Jonas int rc; 417955dc68cSSamuel Mendoza-Jonas 418955dc68cSSamuel Mendoza-Jonas rc = genl_unregister_family(&ncsi_genl_family); 419955dc68cSSamuel Mendoza-Jonas if (rc) 420955dc68cSSamuel Mendoza-Jonas netdev_err(dev, "ncsi: failed to unregister netlink family\n"); 421955dc68cSSamuel Mendoza-Jonas 422955dc68cSSamuel Mendoza-Jonas return rc; 423955dc68cSSamuel Mendoza-Jonas } 424