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