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 { 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); 71955dc68cSSamuel Mendoza-Jonas if (ndp->force_channel == nc) 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 78955dc68cSSamuel Mendoza-Jonas vid_nest = nla_nest_start(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 103955dc68cSSamuel Mendoza-Jonas if (id > ndp->package_num) { 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; 112955dc68cSSamuel Mendoza-Jonas pnest = nla_nest_start(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); 116955dc68cSSamuel Mendoza-Jonas if (ndp->force_package == np) 117955dc68cSSamuel Mendoza-Jonas nla_put_flag(skb, NCSI_PKG_ATTR_FORCED); 118955dc68cSSamuel Mendoza-Jonas cnest = nla_nest_start(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) { 124955dc68cSSamuel Mendoza-Jonas nest = nla_nest_start(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 186955dc68cSSamuel Mendoza-Jonas attr = nla_nest_start(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: 204955dc68cSSamuel Mendoza-Jonas genlmsg_cancel(skb, hdr); 20550db64b0SDan Carpenter kfree_skb(skb); 206955dc68cSSamuel Mendoza-Jonas return rc; 207955dc68cSSamuel Mendoza-Jonas } 208955dc68cSSamuel Mendoza-Jonas 209955dc68cSSamuel Mendoza-Jonas static int ncsi_pkg_info_all_nl(struct sk_buff *skb, 210955dc68cSSamuel Mendoza-Jonas struct netlink_callback *cb) 211955dc68cSSamuel Mendoza-Jonas { 212955dc68cSSamuel Mendoza-Jonas struct nlattr *attrs[NCSI_ATTR_MAX]; 213955dc68cSSamuel Mendoza-Jonas struct ncsi_package *np, *package; 214955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 215955dc68cSSamuel Mendoza-Jonas unsigned int package_id; 216955dc68cSSamuel Mendoza-Jonas struct nlattr *attr; 217955dc68cSSamuel Mendoza-Jonas void *hdr; 218955dc68cSSamuel Mendoza-Jonas int rc; 219955dc68cSSamuel Mendoza-Jonas 220955dc68cSSamuel Mendoza-Jonas rc = genlmsg_parse(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX, 221955dc68cSSamuel Mendoza-Jonas ncsi_genl_policy, NULL); 222955dc68cSSamuel Mendoza-Jonas if (rc) 223955dc68cSSamuel Mendoza-Jonas return rc; 224955dc68cSSamuel Mendoza-Jonas 225955dc68cSSamuel Mendoza-Jonas if (!attrs[NCSI_ATTR_IFINDEX]) 226955dc68cSSamuel Mendoza-Jonas return -EINVAL; 227955dc68cSSamuel Mendoza-Jonas 228955dc68cSSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)), 229955dc68cSSamuel Mendoza-Jonas nla_get_u32(attrs[NCSI_ATTR_IFINDEX])); 230955dc68cSSamuel Mendoza-Jonas 231955dc68cSSamuel Mendoza-Jonas if (!ndp) 232955dc68cSSamuel Mendoza-Jonas return -ENODEV; 233955dc68cSSamuel Mendoza-Jonas 234955dc68cSSamuel Mendoza-Jonas package_id = cb->args[0]; 235955dc68cSSamuel Mendoza-Jonas package = NULL; 236955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) 237955dc68cSSamuel Mendoza-Jonas if (np->id == package_id) 238955dc68cSSamuel Mendoza-Jonas package = np; 239955dc68cSSamuel Mendoza-Jonas 240955dc68cSSamuel Mendoza-Jonas if (!package) 241955dc68cSSamuel Mendoza-Jonas return 0; /* done */ 242955dc68cSSamuel Mendoza-Jonas 243955dc68cSSamuel Mendoza-Jonas hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 244955dc68cSSamuel Mendoza-Jonas &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO); 245955dc68cSSamuel Mendoza-Jonas if (!hdr) { 246955dc68cSSamuel Mendoza-Jonas rc = -EMSGSIZE; 247955dc68cSSamuel Mendoza-Jonas goto err; 248955dc68cSSamuel Mendoza-Jonas } 249955dc68cSSamuel Mendoza-Jonas 250955dc68cSSamuel Mendoza-Jonas attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST); 251955dc68cSSamuel Mendoza-Jonas rc = ncsi_write_package_info(skb, ndp, package->id); 252955dc68cSSamuel Mendoza-Jonas if (rc) { 253955dc68cSSamuel Mendoza-Jonas nla_nest_cancel(skb, attr); 254955dc68cSSamuel Mendoza-Jonas goto err; 255955dc68cSSamuel Mendoza-Jonas } 256955dc68cSSamuel Mendoza-Jonas 257955dc68cSSamuel Mendoza-Jonas nla_nest_end(skb, attr); 258955dc68cSSamuel Mendoza-Jonas genlmsg_end(skb, hdr); 259955dc68cSSamuel Mendoza-Jonas 260955dc68cSSamuel Mendoza-Jonas cb->args[0] = package_id + 1; 261955dc68cSSamuel Mendoza-Jonas 262955dc68cSSamuel Mendoza-Jonas return skb->len; 263955dc68cSSamuel Mendoza-Jonas err: 264955dc68cSSamuel Mendoza-Jonas genlmsg_cancel(skb, hdr); 265955dc68cSSamuel Mendoza-Jonas return rc; 266955dc68cSSamuel Mendoza-Jonas } 267955dc68cSSamuel Mendoza-Jonas 268955dc68cSSamuel Mendoza-Jonas static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info) 269955dc68cSSamuel Mendoza-Jonas { 270955dc68cSSamuel Mendoza-Jonas struct ncsi_package *np, *package; 271955dc68cSSamuel Mendoza-Jonas struct ncsi_channel *nc, *channel; 272955dc68cSSamuel Mendoza-Jonas u32 package_id, channel_id; 273955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 274955dc68cSSamuel Mendoza-Jonas unsigned long flags; 275955dc68cSSamuel Mendoza-Jonas 276955dc68cSSamuel Mendoza-Jonas if (!info || !info->attrs) 277955dc68cSSamuel Mendoza-Jonas return -EINVAL; 278955dc68cSSamuel Mendoza-Jonas 279955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_IFINDEX]) 280955dc68cSSamuel Mendoza-Jonas return -EINVAL; 281955dc68cSSamuel Mendoza-Jonas 282955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) 283955dc68cSSamuel Mendoza-Jonas return -EINVAL; 284955dc68cSSamuel Mendoza-Jonas 285955dc68cSSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 286955dc68cSSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 287955dc68cSSamuel Mendoza-Jonas if (!ndp) 288955dc68cSSamuel Mendoza-Jonas return -ENODEV; 289955dc68cSSamuel Mendoza-Jonas 290955dc68cSSamuel Mendoza-Jonas package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); 291955dc68cSSamuel Mendoza-Jonas package = NULL; 292955dc68cSSamuel Mendoza-Jonas 293955dc68cSSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags); 294955dc68cSSamuel Mendoza-Jonas 295955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_PACKAGE(ndp, np) 296955dc68cSSamuel Mendoza-Jonas if (np->id == package_id) 297955dc68cSSamuel Mendoza-Jonas package = np; 298955dc68cSSamuel Mendoza-Jonas if (!package) { 299955dc68cSSamuel Mendoza-Jonas /* The user has set a package that does not exist */ 300054f34daSDan Carpenter spin_unlock_irqrestore(&ndp->lock, flags); 301955dc68cSSamuel Mendoza-Jonas return -ERANGE; 302955dc68cSSamuel Mendoza-Jonas } 303955dc68cSSamuel Mendoza-Jonas 304955dc68cSSamuel Mendoza-Jonas channel = NULL; 305955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { 306955dc68cSSamuel Mendoza-Jonas /* Allow any channel */ 307955dc68cSSamuel Mendoza-Jonas channel_id = NCSI_RESERVED_CHANNEL; 308955dc68cSSamuel Mendoza-Jonas } else { 309955dc68cSSamuel Mendoza-Jonas channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); 310955dc68cSSamuel Mendoza-Jonas NCSI_FOR_EACH_CHANNEL(package, nc) 311955dc68cSSamuel Mendoza-Jonas if (nc->id == channel_id) 312955dc68cSSamuel Mendoza-Jonas channel = nc; 313955dc68cSSamuel Mendoza-Jonas } 314955dc68cSSamuel Mendoza-Jonas 315955dc68cSSamuel Mendoza-Jonas if (channel_id != NCSI_RESERVED_CHANNEL && !channel) { 316955dc68cSSamuel Mendoza-Jonas /* The user has set a channel that does not exist on this 317955dc68cSSamuel Mendoza-Jonas * package 318955dc68cSSamuel Mendoza-Jonas */ 319054f34daSDan Carpenter spin_unlock_irqrestore(&ndp->lock, flags); 320955dc68cSSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n", 321955dc68cSSamuel Mendoza-Jonas channel_id); 322955dc68cSSamuel Mendoza-Jonas return -ERANGE; 323955dc68cSSamuel Mendoza-Jonas } 324955dc68cSSamuel Mendoza-Jonas 325955dc68cSSamuel Mendoza-Jonas ndp->force_package = package; 326955dc68cSSamuel Mendoza-Jonas ndp->force_channel = channel; 327955dc68cSSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags); 328955dc68cSSamuel Mendoza-Jonas 329955dc68cSSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n", 330955dc68cSSamuel Mendoza-Jonas package_id, channel_id, 331955dc68cSSamuel Mendoza-Jonas channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : ""); 332955dc68cSSamuel Mendoza-Jonas 333955dc68cSSamuel Mendoza-Jonas /* Bounce the NCSI channel to set changes */ 334955dc68cSSamuel Mendoza-Jonas ncsi_stop_dev(&ndp->ndev); 335955dc68cSSamuel Mendoza-Jonas ncsi_start_dev(&ndp->ndev); 336955dc68cSSamuel Mendoza-Jonas 337955dc68cSSamuel Mendoza-Jonas return 0; 338955dc68cSSamuel Mendoza-Jonas } 339955dc68cSSamuel Mendoza-Jonas 340955dc68cSSamuel Mendoza-Jonas static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) 341955dc68cSSamuel Mendoza-Jonas { 342955dc68cSSamuel Mendoza-Jonas struct ncsi_dev_priv *ndp; 343955dc68cSSamuel Mendoza-Jonas unsigned long flags; 344955dc68cSSamuel Mendoza-Jonas 345955dc68cSSamuel Mendoza-Jonas if (!info || !info->attrs) 346955dc68cSSamuel Mendoza-Jonas return -EINVAL; 347955dc68cSSamuel Mendoza-Jonas 348955dc68cSSamuel Mendoza-Jonas if (!info->attrs[NCSI_ATTR_IFINDEX]) 349955dc68cSSamuel Mendoza-Jonas return -EINVAL; 350955dc68cSSamuel Mendoza-Jonas 351955dc68cSSamuel Mendoza-Jonas ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), 352955dc68cSSamuel Mendoza-Jonas nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); 353955dc68cSSamuel Mendoza-Jonas if (!ndp) 354955dc68cSSamuel Mendoza-Jonas return -ENODEV; 355955dc68cSSamuel Mendoza-Jonas 356955dc68cSSamuel Mendoza-Jonas /* Clear any override */ 357955dc68cSSamuel Mendoza-Jonas spin_lock_irqsave(&ndp->lock, flags); 358955dc68cSSamuel Mendoza-Jonas ndp->force_package = NULL; 359955dc68cSSamuel Mendoza-Jonas ndp->force_channel = NULL; 360955dc68cSSamuel Mendoza-Jonas spin_unlock_irqrestore(&ndp->lock, flags); 361955dc68cSSamuel Mendoza-Jonas netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n"); 362955dc68cSSamuel Mendoza-Jonas 363955dc68cSSamuel Mendoza-Jonas /* Bounce the NCSI channel to set changes */ 364955dc68cSSamuel Mendoza-Jonas ncsi_stop_dev(&ndp->ndev); 365955dc68cSSamuel Mendoza-Jonas ncsi_start_dev(&ndp->ndev); 366955dc68cSSamuel Mendoza-Jonas 367955dc68cSSamuel Mendoza-Jonas return 0; 368955dc68cSSamuel Mendoza-Jonas } 369955dc68cSSamuel Mendoza-Jonas 370955dc68cSSamuel Mendoza-Jonas static const struct genl_ops ncsi_ops[] = { 371955dc68cSSamuel Mendoza-Jonas { 372955dc68cSSamuel Mendoza-Jonas .cmd = NCSI_CMD_PKG_INFO, 373955dc68cSSamuel Mendoza-Jonas .policy = ncsi_genl_policy, 374955dc68cSSamuel Mendoza-Jonas .doit = ncsi_pkg_info_nl, 375955dc68cSSamuel Mendoza-Jonas .dumpit = ncsi_pkg_info_all_nl, 376955dc68cSSamuel Mendoza-Jonas .flags = 0, 377955dc68cSSamuel Mendoza-Jonas }, 378955dc68cSSamuel Mendoza-Jonas { 379955dc68cSSamuel Mendoza-Jonas .cmd = NCSI_CMD_SET_INTERFACE, 380955dc68cSSamuel Mendoza-Jonas .policy = ncsi_genl_policy, 381955dc68cSSamuel Mendoza-Jonas .doit = ncsi_set_interface_nl, 382955dc68cSSamuel Mendoza-Jonas .flags = GENL_ADMIN_PERM, 383955dc68cSSamuel Mendoza-Jonas }, 384955dc68cSSamuel Mendoza-Jonas { 385955dc68cSSamuel Mendoza-Jonas .cmd = NCSI_CMD_CLEAR_INTERFACE, 386955dc68cSSamuel Mendoza-Jonas .policy = ncsi_genl_policy, 387955dc68cSSamuel Mendoza-Jonas .doit = ncsi_clear_interface_nl, 388955dc68cSSamuel Mendoza-Jonas .flags = GENL_ADMIN_PERM, 389955dc68cSSamuel Mendoza-Jonas }, 390955dc68cSSamuel Mendoza-Jonas }; 391955dc68cSSamuel Mendoza-Jonas 392955dc68cSSamuel Mendoza-Jonas static struct genl_family ncsi_genl_family __ro_after_init = { 393955dc68cSSamuel Mendoza-Jonas .name = "NCSI", 394955dc68cSSamuel Mendoza-Jonas .version = 0, 395955dc68cSSamuel Mendoza-Jonas .maxattr = NCSI_ATTR_MAX, 396955dc68cSSamuel Mendoza-Jonas .module = THIS_MODULE, 397955dc68cSSamuel Mendoza-Jonas .ops = ncsi_ops, 398955dc68cSSamuel Mendoza-Jonas .n_ops = ARRAY_SIZE(ncsi_ops), 399955dc68cSSamuel Mendoza-Jonas }; 400955dc68cSSamuel Mendoza-Jonas 401955dc68cSSamuel Mendoza-Jonas int ncsi_init_netlink(struct net_device *dev) 402955dc68cSSamuel Mendoza-Jonas { 403955dc68cSSamuel Mendoza-Jonas int rc; 404955dc68cSSamuel Mendoza-Jonas 405955dc68cSSamuel Mendoza-Jonas rc = genl_register_family(&ncsi_genl_family); 406955dc68cSSamuel Mendoza-Jonas if (rc) 407955dc68cSSamuel Mendoza-Jonas netdev_err(dev, "ncsi: failed to register netlink family\n"); 408955dc68cSSamuel Mendoza-Jonas 409955dc68cSSamuel Mendoza-Jonas return rc; 410955dc68cSSamuel Mendoza-Jonas } 411955dc68cSSamuel Mendoza-Jonas 412955dc68cSSamuel Mendoza-Jonas int ncsi_unregister_netlink(struct net_device *dev) 413955dc68cSSamuel Mendoza-Jonas { 414955dc68cSSamuel Mendoza-Jonas int rc; 415955dc68cSSamuel Mendoza-Jonas 416955dc68cSSamuel Mendoza-Jonas rc = genl_unregister_family(&ncsi_genl_family); 417955dc68cSSamuel Mendoza-Jonas if (rc) 418955dc68cSSamuel Mendoza-Jonas netdev_err(dev, "ncsi: failed to unregister netlink family\n"); 419955dc68cSSamuel Mendoza-Jonas 420955dc68cSSamuel Mendoza-Jonas return rc; 421955dc68cSSamuel Mendoza-Jonas } 422