2018-03-05 11:39:05 +11:00
/*
* Copyright Samuel Mendoza - Jonas , IBM Corporation 2018.
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/if_arp.h>
# include <linux/rtnetlink.h>
# include <linux/etherdevice.h>
# include <net/genetlink.h>
# include <net/ncsi.h>
# include <linux/skbuff.h>
# include <net/sock.h>
# include <uapi/linux/ncsi.h>
# include "internal.h"
2018-10-11 18:07:37 +00:00
# include "ncsi-pkt.h"
2018-03-05 11:39:05 +11:00
# include "ncsi-netlink.h"
static struct genl_family ncsi_genl_family ;
static const struct nla_policy ncsi_genl_policy [ NCSI_ATTR_MAX + 1 ] = {
[ NCSI_ATTR_IFINDEX ] = { . type = NLA_U32 } ,
[ NCSI_ATTR_PACKAGE_LIST ] = { . type = NLA_NESTED } ,
[ NCSI_ATTR_PACKAGE_ID ] = { . type = NLA_U32 } ,
[ NCSI_ATTR_CHANNEL_ID ] = { . type = NLA_U32 } ,
2018-10-11 18:07:37 +00:00
[ NCSI_ATTR_DATA ] = { . type = NLA_BINARY , . len = 2048 } ,
2018-11-16 15:51:59 +11:00
[ NCSI_ATTR_MULTI_FLAG ] = { . type = NLA_FLAG } ,
[ NCSI_ATTR_PACKAGE_MASK ] = { . type = NLA_U32 } ,
[ NCSI_ATTR_CHANNEL_MASK ] = { . type = NLA_U32 } ,
2018-03-05 11:39:05 +11:00
} ;
static struct ncsi_dev_priv * ndp_from_ifindex ( struct net * net , u32 ifindex )
{
struct ncsi_dev_priv * ndp ;
struct net_device * dev ;
struct ncsi_dev * nd ;
struct ncsi_dev ;
if ( ! net )
return NULL ;
dev = dev_get_by_index ( net , ifindex ) ;
if ( ! dev ) {
pr_err ( " NCSI netlink: No device for ifindex %u \n " , ifindex ) ;
return NULL ;
}
nd = ncsi_find_dev ( dev ) ;
ndp = nd ? TO_NCSI_DEV_PRIV ( nd ) : NULL ;
dev_put ( dev ) ;
return ndp ;
}
static int ncsi_write_channel_info ( struct sk_buff * skb ,
struct ncsi_dev_priv * ndp ,
struct ncsi_channel * nc )
{
2018-04-17 14:23:23 +10:00
struct ncsi_channel_vlan_filter * ncf ;
2018-03-05 11:39:05 +11:00
struct ncsi_channel_mode * m ;
2018-04-17 14:23:23 +10:00
struct nlattr * vid_nest ;
2018-03-05 11:39:05 +11:00
int i ;
nla_put_u32 ( skb , NCSI_CHANNEL_ATTR_ID , nc - > id ) ;
m = & nc - > modes [ NCSI_MODE_LINK ] ;
nla_put_u32 ( skb , NCSI_CHANNEL_ATTR_LINK_STATE , m - > data [ 2 ] ) ;
if ( nc - > state = = NCSI_CHANNEL_ACTIVE )
nla_put_flag ( skb , NCSI_CHANNEL_ATTR_ACTIVE ) ;
2018-11-16 15:51:59 +11:00
if ( nc = = nc - > package - > preferred_channel )
2018-03-05 11:39:05 +11:00
nla_put_flag ( skb , NCSI_CHANNEL_ATTR_FORCED ) ;
nla_put_u32 ( skb , NCSI_CHANNEL_ATTR_VERSION_MAJOR , nc - > version . version ) ;
nla_put_u32 ( skb , NCSI_CHANNEL_ATTR_VERSION_MINOR , nc - > version . alpha2 ) ;
nla_put_string ( skb , NCSI_CHANNEL_ATTR_VERSION_STR , nc - > version . fw_name ) ;
vid_nest = nla_nest_start ( skb , NCSI_CHANNEL_ATTR_VLAN_LIST ) ;
if ( ! vid_nest )
return - ENOMEM ;
2018-04-17 14:23:23 +10:00
ncf = & nc - > vlan_filter ;
2018-03-05 11:39:05 +11:00
i = - 1 ;
2018-04-17 14:23:23 +10:00
while ( ( i = find_next_bit ( ( void * ) & ncf - > bitmap , ncf - > n_vids ,
i + 1 ) ) < ncf - > n_vids ) {
if ( ncf - > vids [ i ] )
2018-03-05 11:39:05 +11:00
nla_put_u16 ( skb , NCSI_CHANNEL_ATTR_VLAN_ID ,
2018-04-17 14:23:23 +10:00
ncf - > vids [ i ] ) ;
2018-03-05 11:39:05 +11:00
}
nla_nest_end ( skb , vid_nest ) ;
return 0 ;
}
static int ncsi_write_package_info ( struct sk_buff * skb ,
struct ncsi_dev_priv * ndp , unsigned int id )
{
struct nlattr * pnest , * cnest , * nest ;
struct ncsi_package * np ;
struct ncsi_channel * nc ;
bool found ;
int rc ;
2018-08-22 14:57:44 +10:00
if ( id > ndp - > package_num - 1 ) {
2018-03-05 11:39:05 +11:00
netdev_info ( ndp - > ndev . dev , " NCSI: No package with id %u \n " , id ) ;
return - ENODEV ;
}
found = false ;
NCSI_FOR_EACH_PACKAGE ( ndp , np ) {
if ( np - > id ! = id )
continue ;
pnest = nla_nest_start ( skb , NCSI_PKG_ATTR ) ;
if ( ! pnest )
return - ENOMEM ;
nla_put_u32 ( skb , NCSI_PKG_ATTR_ID , np - > id ) ;
2018-11-16 15:51:59 +11:00
if ( ( 0x1 < < np - > id ) = = ndp - > package_whitelist )
2018-03-05 11:39:05 +11:00
nla_put_flag ( skb , NCSI_PKG_ATTR_FORCED ) ;
cnest = nla_nest_start ( skb , NCSI_PKG_ATTR_CHANNEL_LIST ) ;
if ( ! cnest ) {
nla_nest_cancel ( skb , pnest ) ;
return - ENOMEM ;
}
NCSI_FOR_EACH_CHANNEL ( np , nc ) {
nest = nla_nest_start ( skb , NCSI_CHANNEL_ATTR ) ;
if ( ! nest ) {
nla_nest_cancel ( skb , cnest ) ;
nla_nest_cancel ( skb , pnest ) ;
return - ENOMEM ;
}
rc = ncsi_write_channel_info ( skb , ndp , nc ) ;
if ( rc ) {
nla_nest_cancel ( skb , nest ) ;
nla_nest_cancel ( skb , cnest ) ;
nla_nest_cancel ( skb , pnest ) ;
return rc ;
}
nla_nest_end ( skb , nest ) ;
}
nla_nest_end ( skb , cnest ) ;
nla_nest_end ( skb , pnest ) ;
found = true ;
}
if ( ! found )
return - ENODEV ;
return 0 ;
}
static int ncsi_pkg_info_nl ( struct sk_buff * msg , struct genl_info * info )
{
struct ncsi_dev_priv * ndp ;
unsigned int package_id ;
struct sk_buff * skb ;
struct nlattr * attr ;
void * hdr ;
int rc ;
if ( ! info | | ! info - > attrs )
return - EINVAL ;
if ( ! info - > attrs [ NCSI_ATTR_IFINDEX ] )
return - EINVAL ;
if ( ! info - > attrs [ NCSI_ATTR_PACKAGE_ID ] )
return - EINVAL ;
ndp = ndp_from_ifindex ( genl_info_net ( info ) ,
nla_get_u32 ( info - > attrs [ NCSI_ATTR_IFINDEX ] ) ) ;
if ( ! ndp )
return - ENODEV ;
skb = genlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_KERNEL ) ;
if ( ! skb )
return - ENOMEM ;
hdr = genlmsg_put ( skb , info - > snd_portid , info - > snd_seq ,
& ncsi_genl_family , 0 , NCSI_CMD_PKG_INFO ) ;
if ( ! hdr ) {
2018-03-08 12:36:04 +03:00
kfree_skb ( skb ) ;
2018-03-05 11:39:05 +11:00
return - EMSGSIZE ;
}
package_id = nla_get_u32 ( info - > attrs [ NCSI_ATTR_PACKAGE_ID ] ) ;
attr = nla_nest_start ( skb , NCSI_ATTR_PACKAGE_LIST ) ;
2018-03-26 12:27:12 +01:00
if ( ! attr ) {
kfree_skb ( skb ) ;
return - EMSGSIZE ;
}
2018-03-05 11:39:05 +11:00
rc = ncsi_write_package_info ( skb , ndp , package_id ) ;
if ( rc ) {
nla_nest_cancel ( skb , attr ) ;
goto err ;
}
nla_nest_end ( skb , attr ) ;
genlmsg_end ( skb , hdr ) ;
return genlmsg_reply ( skb , info ) ;
err :
2018-03-08 12:36:04 +03:00
kfree_skb ( skb ) ;
2018-03-05 11:39:05 +11:00
return rc ;
}
static int ncsi_pkg_info_all_nl ( struct sk_buff * skb ,
struct netlink_callback * cb )
{
2018-05-31 14:10:04 +10:00
struct nlattr * attrs [ NCSI_ATTR_MAX + 1 ] ;
2018-03-05 11:39:05 +11:00
struct ncsi_package * np , * package ;
struct ncsi_dev_priv * ndp ;
unsigned int package_id ;
struct nlattr * attr ;
void * hdr ;
int rc ;
rc = genlmsg_parse ( cb - > nlh , & ncsi_genl_family , attrs , NCSI_ATTR_MAX ,
ncsi_genl_policy , NULL ) ;
if ( rc )
return rc ;
if ( ! attrs [ NCSI_ATTR_IFINDEX ] )
return - EINVAL ;
ndp = ndp_from_ifindex ( get_net ( sock_net ( skb - > sk ) ) ,
nla_get_u32 ( attrs [ NCSI_ATTR_IFINDEX ] ) ) ;
if ( ! ndp )
return - ENODEV ;
package_id = cb - > args [ 0 ] ;
package = NULL ;
NCSI_FOR_EACH_PACKAGE ( ndp , np )
if ( np - > id = = package_id )
package = np ;
if ( ! package )
return 0 ; /* done */
hdr = genlmsg_put ( skb , NETLINK_CB ( cb - > skb ) . portid , cb - > nlh - > nlmsg_seq ,
2018-08-22 14:57:44 +10:00
& ncsi_genl_family , NLM_F_MULTI , NCSI_CMD_PKG_INFO ) ;
2018-03-05 11:39:05 +11:00
if ( ! hdr ) {
rc = - EMSGSIZE ;
goto err ;
}
attr = nla_nest_start ( skb , NCSI_ATTR_PACKAGE_LIST ) ;
rc = ncsi_write_package_info ( skb , ndp , package - > id ) ;
if ( rc ) {
nla_nest_cancel ( skb , attr ) ;
goto err ;
}
nla_nest_end ( skb , attr ) ;
genlmsg_end ( skb , hdr ) ;
cb - > args [ 0 ] = package_id + 1 ;
return skb - > len ;
err :
genlmsg_cancel ( skb , hdr ) ;
return rc ;
}
static int ncsi_set_interface_nl ( struct sk_buff * msg , struct genl_info * info )
{
struct ncsi_package * np , * package ;
struct ncsi_channel * nc , * channel ;
u32 package_id , channel_id ;
struct ncsi_dev_priv * ndp ;
unsigned long flags ;
if ( ! info | | ! info - > attrs )
return - EINVAL ;
if ( ! info - > attrs [ NCSI_ATTR_IFINDEX ] )
return - EINVAL ;
if ( ! info - > attrs [ NCSI_ATTR_PACKAGE_ID ] )
return - EINVAL ;
ndp = ndp_from_ifindex ( get_net ( sock_net ( msg - > sk ) ) ,
nla_get_u32 ( info - > attrs [ NCSI_ATTR_IFINDEX ] ) ) ;
if ( ! ndp )
return - ENODEV ;
package_id = nla_get_u32 ( info - > attrs [ NCSI_ATTR_PACKAGE_ID ] ) ;
package = NULL ;
NCSI_FOR_EACH_PACKAGE ( ndp , np )
if ( np - > id = = package_id )
package = np ;
if ( ! package ) {
/* The user has set a package that does not exist */
return - ERANGE ;
}
channel = NULL ;
2018-11-16 15:51:59 +11:00
if ( info - > attrs [ NCSI_ATTR_CHANNEL_ID ] ) {
2018-03-05 11:39:05 +11:00
channel_id = nla_get_u32 ( info - > attrs [ NCSI_ATTR_CHANNEL_ID ] ) ;
NCSI_FOR_EACH_CHANNEL ( package , nc )
2018-11-16 15:51:59 +11:00
if ( nc - > id = = channel_id ) {
2018-03-05 11:39:05 +11:00
channel = nc ;
2018-11-16 15:51:59 +11:00
break ;
}
if ( ! channel ) {
netdev_info ( ndp - > ndev . dev ,
" NCSI: Channel %u does not exist! \n " ,
channel_id ) ;
return - ERANGE ;
}
2018-03-05 11:39:05 +11:00
}
2018-11-16 15:51:59 +11:00
spin_lock_irqsave ( & ndp - > lock , flags ) ;
ndp - > package_whitelist = 0x1 < < package - > id ;
ndp - > multi_package = false ;
2018-03-05 11:39:05 +11:00
spin_unlock_irqrestore ( & ndp - > lock , flags ) ;
2018-11-16 15:51:59 +11:00
spin_lock_irqsave ( & package - > lock , flags ) ;
package - > multi_channel = false ;
if ( channel ) {
package - > channel_whitelist = 0x1 < < channel - > id ;
package - > preferred_channel = channel ;
} else {
/* Allow any channel */
package - > channel_whitelist = UINT_MAX ;
package - > preferred_channel = NULL ;
}
spin_unlock_irqrestore ( & package - > lock , flags ) ;
if ( channel )
netdev_info ( ndp - > ndev . dev ,
" Set package 0x%x, channel 0x%x as preferred \n " ,
package_id , channel_id ) ;
else
netdev_info ( ndp - > ndev . dev , " Set package 0x%x as preferred \n " ,
package_id ) ;
2018-03-05 11:39:05 +11:00
2018-11-16 15:51:58 +11:00
/* Update channel configuration */
if ( ! ( ndp - > flags & NCSI_DEV_RESET ) )
ncsi_reset_dev ( & ndp - > ndev ) ;
2018-03-05 11:39:05 +11:00
return 0 ;
}
static int ncsi_clear_interface_nl ( struct sk_buff * msg , struct genl_info * info )
{
struct ncsi_dev_priv * ndp ;
2018-11-16 15:51:59 +11:00
struct ncsi_package * np ;
2018-03-05 11:39:05 +11:00
unsigned long flags ;
if ( ! info | | ! info - > attrs )
return - EINVAL ;
if ( ! info - > attrs [ NCSI_ATTR_IFINDEX ] )
return - EINVAL ;
ndp = ndp_from_ifindex ( get_net ( sock_net ( msg - > sk ) ) ,
nla_get_u32 ( info - > attrs [ NCSI_ATTR_IFINDEX ] ) ) ;
if ( ! ndp )
return - ENODEV ;
2018-11-16 15:51:59 +11:00
/* Reset any whitelists and disable multi mode */
2018-03-05 11:39:05 +11:00
spin_lock_irqsave ( & ndp - > lock , flags ) ;
2018-11-16 15:51:59 +11:00
ndp - > package_whitelist = UINT_MAX ;
ndp - > multi_package = false ;
2018-03-05 11:39:05 +11:00
spin_unlock_irqrestore ( & ndp - > lock , flags ) ;
2018-11-16 15:51:59 +11:00
NCSI_FOR_EACH_PACKAGE ( ndp , np ) {
spin_lock_irqsave ( & np - > lock , flags ) ;
np - > multi_channel = false ;
np - > channel_whitelist = UINT_MAX ;
np - > preferred_channel = NULL ;
spin_unlock_irqrestore ( & np - > lock , flags ) ;
}
2018-03-05 11:39:05 +11:00
netdev_info ( ndp - > ndev . dev , " NCSI: Cleared preferred package/channel \n " ) ;
2018-11-16 15:51:58 +11:00
/* Update channel configuration */
if ( ! ( ndp - > flags & NCSI_DEV_RESET ) )
ncsi_reset_dev ( & ndp - > ndev ) ;
2018-03-05 11:39:05 +11:00
return 0 ;
}
2018-10-11 18:07:37 +00:00
static int ncsi_send_cmd_nl ( struct sk_buff * msg , struct genl_info * info )
{
struct ncsi_dev_priv * ndp ;
struct ncsi_pkt_hdr * hdr ;
struct ncsi_cmd_arg nca ;
unsigned char * data ;
u32 package_id ;
u32 channel_id ;
int len , ret ;
if ( ! info | | ! info - > attrs ) {
ret = - EINVAL ;
goto out ;
}
if ( ! info - > attrs [ NCSI_ATTR_IFINDEX ] ) {
ret = - EINVAL ;
goto out ;
}
if ( ! info - > attrs [ NCSI_ATTR_PACKAGE_ID ] ) {
ret = - EINVAL ;
goto out ;
}
if ( ! info - > attrs [ NCSI_ATTR_CHANNEL_ID ] ) {
ret = - EINVAL ;
goto out ;
}
if ( ! info - > attrs [ NCSI_ATTR_DATA ] ) {
ret = - EINVAL ;
goto out ;
}
ndp = ndp_from_ifindex ( get_net ( sock_net ( msg - > sk ) ) ,
nla_get_u32 ( info - > attrs [ NCSI_ATTR_IFINDEX ] ) ) ;
if ( ! ndp ) {
ret = - ENODEV ;
goto out ;
}
package_id = nla_get_u32 ( info - > attrs [ NCSI_ATTR_PACKAGE_ID ] ) ;
channel_id = nla_get_u32 ( info - > attrs [ NCSI_ATTR_CHANNEL_ID ] ) ;
if ( package_id > = NCSI_MAX_PACKAGE | | channel_id > = NCSI_MAX_CHANNEL ) {
ret = - ERANGE ;
goto out_netlink ;
}
len = nla_len ( info - > attrs [ NCSI_ATTR_DATA ] ) ;
if ( len < sizeof ( struct ncsi_pkt_hdr ) ) {
netdev_info ( ndp - > ndev . dev , " NCSI: no command to send %u \n " ,
package_id ) ;
ret = - EINVAL ;
goto out_netlink ;
} else {
data = ( unsigned char * ) nla_data ( info - > attrs [ NCSI_ATTR_DATA ] ) ;
}
hdr = ( struct ncsi_pkt_hdr * ) data ;
nca . ndp = ndp ;
nca . package = ( unsigned char ) package_id ;
nca . channel = ( unsigned char ) channel_id ;
nca . type = hdr - > type ;
nca . req_flags = NCSI_REQ_FLAG_NETLINK_DRIVEN ;
nca . info = info ;
nca . payload = ntohs ( hdr - > length ) ;
nca . data = data + sizeof ( * hdr ) ;
ret = ncsi_xmit_cmd ( & nca ) ;
out_netlink :
if ( ret ! = 0 ) {
netdev_err ( ndp - > ndev . dev ,
" NCSI: Error %d sending command \n " ,
ret ) ;
ncsi_send_netlink_err ( ndp - > ndev . dev ,
info - > snd_seq ,
info - > snd_portid ,
info - > nlhdr ,
ret ) ;
}
out :
return ret ;
}
int ncsi_send_netlink_rsp ( struct ncsi_request * nr ,
struct ncsi_package * np ,
struct ncsi_channel * nc )
{
struct sk_buff * skb ;
struct net * net ;
void * hdr ;
int rc ;
net = dev_net ( nr - > rsp - > dev ) ;
skb = genlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_ATOMIC ) ;
if ( ! skb )
return - ENOMEM ;
hdr = genlmsg_put ( skb , nr - > snd_portid , nr - > snd_seq ,
& ncsi_genl_family , 0 , NCSI_CMD_SEND_CMD ) ;
if ( ! hdr ) {
kfree_skb ( skb ) ;
return - EMSGSIZE ;
}
nla_put_u32 ( skb , NCSI_ATTR_IFINDEX , nr - > rsp - > dev - > ifindex ) ;
if ( np )
nla_put_u32 ( skb , NCSI_ATTR_PACKAGE_ID , np - > id ) ;
if ( nc )
nla_put_u32 ( skb , NCSI_ATTR_CHANNEL_ID , nc - > id ) ;
else
nla_put_u32 ( skb , NCSI_ATTR_CHANNEL_ID , NCSI_RESERVED_CHANNEL ) ;
rc = nla_put ( skb , NCSI_ATTR_DATA , nr - > rsp - > len , ( void * ) nr - > rsp - > data ) ;
if ( rc )
goto err ;
genlmsg_end ( skb , hdr ) ;
return genlmsg_unicast ( net , skb , nr - > snd_portid ) ;
err :
kfree_skb ( skb ) ;
return rc ;
}
int ncsi_send_netlink_timeout ( struct ncsi_request * nr ,
struct ncsi_package * np ,
struct ncsi_channel * nc )
{
struct sk_buff * skb ;
struct net * net ;
void * hdr ;
skb = genlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_ATOMIC ) ;
if ( ! skb )
return - ENOMEM ;
hdr = genlmsg_put ( skb , nr - > snd_portid , nr - > snd_seq ,
& ncsi_genl_family , 0 , NCSI_CMD_SEND_CMD ) ;
if ( ! hdr ) {
kfree_skb ( skb ) ;
return - EMSGSIZE ;
}
net = dev_net ( nr - > cmd - > dev ) ;
nla_put_u32 ( skb , NCSI_ATTR_IFINDEX , nr - > cmd - > dev - > ifindex ) ;
if ( np )
nla_put_u32 ( skb , NCSI_ATTR_PACKAGE_ID , np - > id ) ;
else
nla_put_u32 ( skb , NCSI_ATTR_PACKAGE_ID ,
NCSI_PACKAGE_INDEX ( ( ( ( struct ncsi_pkt_hdr * )
nr - > cmd - > data ) - > channel ) ) ) ;
if ( nc )
nla_put_u32 ( skb , NCSI_ATTR_CHANNEL_ID , nc - > id ) ;
else
nla_put_u32 ( skb , NCSI_ATTR_CHANNEL_ID , NCSI_RESERVED_CHANNEL ) ;
genlmsg_end ( skb , hdr ) ;
return genlmsg_unicast ( net , skb , nr - > snd_portid ) ;
}
int ncsi_send_netlink_err ( struct net_device * dev ,
u32 snd_seq ,
u32 snd_portid ,
struct nlmsghdr * nlhdr ,
int err )
{
struct nlmsghdr * nlh ;
struct nlmsgerr * nle ;
struct sk_buff * skb ;
struct net * net ;
skb = nlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_ATOMIC ) ;
if ( ! skb )
return - ENOMEM ;
net = dev_net ( dev ) ;
nlh = nlmsg_put ( skb , snd_portid , snd_seq ,
NLMSG_ERROR , sizeof ( * nle ) , 0 ) ;
nle = ( struct nlmsgerr * ) nlmsg_data ( nlh ) ;
nle - > error = err ;
memcpy ( & nle - > msg , nlhdr , sizeof ( * nlh ) ) ;
nlmsg_end ( skb , nlh ) ;
return nlmsg_unicast ( net - > genl_sock , skb , snd_portid ) ;
}
2018-11-16 15:51:59 +11:00
static int ncsi_set_package_mask_nl ( struct sk_buff * msg ,
struct genl_info * info )
{
struct ncsi_dev_priv * ndp ;
unsigned long flags ;
int rc ;
if ( ! info | | ! info - > attrs )
return - EINVAL ;
if ( ! info - > attrs [ NCSI_ATTR_IFINDEX ] )
return - EINVAL ;
if ( ! info - > attrs [ NCSI_ATTR_PACKAGE_MASK ] )
return - EINVAL ;
ndp = ndp_from_ifindex ( get_net ( sock_net ( msg - > sk ) ) ,
nla_get_u32 ( info - > attrs [ NCSI_ATTR_IFINDEX ] ) ) ;
if ( ! ndp )
return - ENODEV ;
spin_lock_irqsave ( & ndp - > lock , flags ) ;
if ( nla_get_flag ( info - > attrs [ NCSI_ATTR_MULTI_FLAG ] ) ) {
if ( ndp - > flags & NCSI_DEV_HWA ) {
ndp - > multi_package = true ;
rc = 0 ;
} else {
netdev_err ( ndp - > ndev . dev ,
" NCSI: Can't use multiple packages without HWA \n " ) ;
rc = - EPERM ;
}
} else {
ndp - > multi_package = false ;
rc = 0 ;
}
if ( ! rc )
ndp - > package_whitelist =
nla_get_u32 ( info - > attrs [ NCSI_ATTR_PACKAGE_MASK ] ) ;
spin_unlock_irqrestore ( & ndp - > lock , flags ) ;
if ( ! rc ) {
/* Update channel configuration */
if ( ! ( ndp - > flags & NCSI_DEV_RESET ) )
ncsi_reset_dev ( & ndp - > ndev ) ;
}
return rc ;
}
static int ncsi_set_channel_mask_nl ( struct sk_buff * msg ,
struct genl_info * info )
{
struct ncsi_package * np , * package ;
struct ncsi_channel * nc , * channel ;
u32 package_id , channel_id ;
struct ncsi_dev_priv * ndp ;
unsigned long flags ;
if ( ! info | | ! info - > attrs )
return - EINVAL ;
if ( ! info - > attrs [ NCSI_ATTR_IFINDEX ] )
return - EINVAL ;
if ( ! info - > attrs [ NCSI_ATTR_PACKAGE_ID ] )
return - EINVAL ;
if ( ! info - > attrs [ NCSI_ATTR_CHANNEL_MASK ] )
return - EINVAL ;
ndp = ndp_from_ifindex ( get_net ( sock_net ( msg - > sk ) ) ,
nla_get_u32 ( info - > attrs [ NCSI_ATTR_IFINDEX ] ) ) ;
if ( ! ndp )
return - ENODEV ;
package_id = nla_get_u32 ( info - > attrs [ NCSI_ATTR_PACKAGE_ID ] ) ;
package = NULL ;
NCSI_FOR_EACH_PACKAGE ( ndp , np )
if ( np - > id = = package_id ) {
package = np ;
break ;
}
if ( ! package )
return - ERANGE ;
spin_lock_irqsave ( & package - > lock , flags ) ;
channel = NULL ;
if ( info - > attrs [ NCSI_ATTR_CHANNEL_ID ] ) {
channel_id = nla_get_u32 ( info - > attrs [ NCSI_ATTR_CHANNEL_ID ] ) ;
NCSI_FOR_EACH_CHANNEL ( np , nc )
if ( nc - > id = = channel_id ) {
channel = nc ;
break ;
}
if ( ! channel ) {
spin_unlock_irqrestore ( & package - > lock , flags ) ;
return - ERANGE ;
}
netdev_dbg ( ndp - > ndev . dev ,
" NCSI: Channel %u set as preferred channel \n " ,
channel - > id ) ;
}
package - > channel_whitelist =
nla_get_u32 ( info - > attrs [ NCSI_ATTR_CHANNEL_MASK ] ) ;
if ( package - > channel_whitelist = = 0 )
netdev_dbg ( ndp - > ndev . dev ,
" NCSI: Package %u set to all channels disabled \n " ,
package - > id ) ;
package - > preferred_channel = channel ;
if ( nla_get_flag ( info - > attrs [ NCSI_ATTR_MULTI_FLAG ] ) ) {
package - > multi_channel = true ;
netdev_info ( ndp - > ndev . dev ,
" NCSI: Multi-channel enabled on package %u \n " ,
package_id ) ;
} else {
package - > multi_channel = false ;
}
spin_unlock_irqrestore ( & package - > lock , flags ) ;
/* Update channel configuration */
if ( ! ( ndp - > flags & NCSI_DEV_RESET ) )
ncsi_reset_dev ( & ndp - > ndev ) ;
return 0 ;
}
2018-03-05 11:39:05 +11:00
static const struct genl_ops ncsi_ops [ ] = {
{
. cmd = NCSI_CMD_PKG_INFO ,
. policy = ncsi_genl_policy ,
. doit = ncsi_pkg_info_nl ,
. dumpit = ncsi_pkg_info_all_nl ,
. flags = 0 ,
} ,
{
. cmd = NCSI_CMD_SET_INTERFACE ,
. policy = ncsi_genl_policy ,
. doit = ncsi_set_interface_nl ,
. flags = GENL_ADMIN_PERM ,
} ,
{
. cmd = NCSI_CMD_CLEAR_INTERFACE ,
. policy = ncsi_genl_policy ,
. doit = ncsi_clear_interface_nl ,
. flags = GENL_ADMIN_PERM ,
} ,
2018-10-11 18:07:37 +00:00
{
. cmd = NCSI_CMD_SEND_CMD ,
. policy = ncsi_genl_policy ,
. doit = ncsi_send_cmd_nl ,
. flags = GENL_ADMIN_PERM ,
} ,
2018-11-16 15:51:59 +11:00
{
. cmd = NCSI_CMD_SET_PACKAGE_MASK ,
. policy = ncsi_genl_policy ,
. doit = ncsi_set_package_mask_nl ,
. flags = GENL_ADMIN_PERM ,
} ,
{
. cmd = NCSI_CMD_SET_CHANNEL_MASK ,
. policy = ncsi_genl_policy ,
. doit = ncsi_set_channel_mask_nl ,
. flags = GENL_ADMIN_PERM ,
} ,
2018-03-05 11:39:05 +11:00
} ;
static struct genl_family ncsi_genl_family __ro_after_init = {
. name = " NCSI " ,
. version = 0 ,
. maxattr = NCSI_ATTR_MAX ,
. module = THIS_MODULE ,
. ops = ncsi_ops ,
. n_ops = ARRAY_SIZE ( ncsi_ops ) ,
} ;
int ncsi_init_netlink ( struct net_device * dev )
{
int rc ;
rc = genl_register_family ( & ncsi_genl_family ) ;
if ( rc )
netdev_err ( dev , " ncsi: failed to register netlink family \n " ) ;
return rc ;
}
int ncsi_unregister_netlink ( struct net_device * dev )
{
int rc ;
rc = genl_unregister_family ( & ncsi_genl_family ) ;
if ( rc )
netdev_err ( dev , " ncsi: failed to unregister netlink family \n " ) ;
return rc ;
}