2014-11-09 08:36:53 +01:00
/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* Authors :
* Alexander Aring < aar @ pengutronix . de >
*
* Based on : net / wireless / nl80211 . c
*/
# include <linux/rtnetlink.h>
# include <net/cfg802154.h>
# include <net/genetlink.h>
# include <net/mac802154.h>
# include <net/netlink.h>
# include <net/nl802154.h>
# include <net/sock.h>
# include "nl802154.h"
2014-11-12 03:36:55 +01:00
# include "rdev-ops.h"
2014-11-09 08:36:53 +01:00
# include "core.h"
static int nl802154_pre_doit ( const struct genl_ops * ops , struct sk_buff * skb ,
struct genl_info * info ) ;
static void nl802154_post_doit ( const struct genl_ops * ops , struct sk_buff * skb ,
struct genl_info * info ) ;
/* the netlink family */
static struct genl_family nl802154_fam = {
. id = GENL_ID_GENERATE , /* don't bother with a hardcoded ID */
. name = NL802154_GENL_NAME , /* have users key off the name instead */
. hdrsize = 0 , /* no private header */
. version = 1 , /* no particular meaning now */
. maxattr = NL802154_ATTR_MAX ,
. netnsok = true ,
. pre_doit = nl802154_pre_doit ,
. post_doit = nl802154_post_doit ,
} ;
/* multicast groups */
enum nl802154_multicast_groups {
NL802154_MCGRP_CONFIG ,
} ;
static const struct genl_multicast_group nl802154_mcgrps [ ] = {
[ NL802154_MCGRP_CONFIG ] = { . name = " config " , } ,
} ;
/* returns ERR_PTR values */
static struct wpan_dev *
__cfg802154_wpan_dev_from_attrs ( struct net * netns , struct nlattr * * attrs )
{
struct cfg802154_registered_device * rdev ;
struct wpan_dev * result = NULL ;
bool have_ifidx = attrs [ NL802154_ATTR_IFINDEX ] ;
bool have_wpan_dev_id = attrs [ NL802154_ATTR_WPAN_DEV ] ;
u64 wpan_dev_id ;
int wpan_phy_idx = - 1 ;
int ifidx = - 1 ;
ASSERT_RTNL ( ) ;
if ( ! have_ifidx & & ! have_wpan_dev_id )
return ERR_PTR ( - EINVAL ) ;
if ( have_ifidx )
ifidx = nla_get_u32 ( attrs [ NL802154_ATTR_IFINDEX ] ) ;
if ( have_wpan_dev_id ) {
wpan_dev_id = nla_get_u64 ( attrs [ NL802154_ATTR_WPAN_DEV ] ) ;
wpan_phy_idx = wpan_dev_id > > 32 ;
}
list_for_each_entry ( rdev , & cfg802154_rdev_list , list ) {
struct wpan_dev * wpan_dev ;
/* TODO netns compare */
if ( have_wpan_dev_id & & rdev - > wpan_phy_idx ! = wpan_phy_idx )
continue ;
list_for_each_entry ( wpan_dev , & rdev - > wpan_dev_list , list ) {
if ( have_ifidx & & wpan_dev - > netdev & &
wpan_dev - > netdev - > ifindex = = ifidx ) {
result = wpan_dev ;
break ;
}
if ( have_wpan_dev_id & &
wpan_dev - > identifier = = ( u32 ) wpan_dev_id ) {
result = wpan_dev ;
break ;
}
}
if ( result )
break ;
}
if ( result )
return result ;
return ERR_PTR ( - ENODEV ) ;
}
static struct cfg802154_registered_device *
__cfg802154_rdev_from_attrs ( struct net * netns , struct nlattr * * attrs )
{
struct cfg802154_registered_device * rdev = NULL , * tmp ;
struct net_device * netdev ;
ASSERT_RTNL ( ) ;
if ( ! attrs [ NL802154_ATTR_WPAN_PHY ] & &
! attrs [ NL802154_ATTR_IFINDEX ] & &
! attrs [ NL802154_ATTR_WPAN_DEV ] )
return ERR_PTR ( - EINVAL ) ;
if ( attrs [ NL802154_ATTR_WPAN_PHY ] )
rdev = cfg802154_rdev_by_wpan_phy_idx (
nla_get_u32 ( attrs [ NL802154_ATTR_WPAN_PHY ] ) ) ;
if ( attrs [ NL802154_ATTR_WPAN_DEV ] ) {
u64 wpan_dev_id = nla_get_u64 ( attrs [ NL802154_ATTR_WPAN_DEV ] ) ;
struct wpan_dev * wpan_dev ;
bool found = false ;
tmp = cfg802154_rdev_by_wpan_phy_idx ( wpan_dev_id > > 32 ) ;
if ( tmp ) {
/* make sure wpan_dev exists */
list_for_each_entry ( wpan_dev , & tmp - > wpan_dev_list , list ) {
if ( wpan_dev - > identifier ! = ( u32 ) wpan_dev_id )
continue ;
found = true ;
break ;
}
if ( ! found )
tmp = NULL ;
if ( rdev & & tmp ! = rdev )
return ERR_PTR ( - EINVAL ) ;
rdev = tmp ;
}
}
if ( attrs [ NL802154_ATTR_IFINDEX ] ) {
int ifindex = nla_get_u32 ( attrs [ NL802154_ATTR_IFINDEX ] ) ;
netdev = __dev_get_by_index ( netns , ifindex ) ;
if ( netdev ) {
if ( netdev - > ieee802154_ptr )
tmp = wpan_phy_to_rdev (
netdev - > ieee802154_ptr - > wpan_phy ) ;
else
tmp = NULL ;
/* not wireless device -- return error */
if ( ! tmp )
return ERR_PTR ( - EINVAL ) ;
/* mismatch -- return error */
if ( rdev & & tmp ! = rdev )
return ERR_PTR ( - EINVAL ) ;
rdev = tmp ;
}
}
if ( ! rdev )
return ERR_PTR ( - ENODEV ) ;
/* TODO netns compare */
return rdev ;
}
/* This function returns a pointer to the driver
* that the genl_info item that is passed refers to .
*
* The result of this can be a PTR_ERR and hence must
* be checked with IS_ERR ( ) for errors .
*/
static struct cfg802154_registered_device *
cfg802154_get_dev_from_info ( struct net * netns , struct genl_info * info )
{
return __cfg802154_rdev_from_attrs ( netns , info - > attrs ) ;
}
/* policy for the attributes */
static const struct nla_policy nl802154_policy [ NL802154_ATTR_MAX + 1 ] = {
2014-11-09 08:36:54 +01:00
[ NL802154_ATTR_WPAN_PHY ] = { . type = NLA_U32 } ,
[ NL802154_ATTR_WPAN_PHY_NAME ] = { . type = NLA_NUL_STRING ,
. len = 20 - 1 } ,
[ NL802154_ATTR_IFINDEX ] = { . type = NLA_U32 } ,
2014-11-09 08:36:55 +01:00
[ NL802154_ATTR_IFTYPE ] = { . type = NLA_U32 } ,
[ NL802154_ATTR_IFNAME ] = { . type = NLA_NUL_STRING , . len = IFNAMSIZ - 1 } ,
2014-11-09 08:36:54 +01:00
[ NL802154_ATTR_WPAN_DEV ] = { . type = NLA_U64 } ,
[ NL802154_ATTR_PAGE ] = { . type = NLA_U8 , } ,
[ NL802154_ATTR_CHANNEL ] = { . type = NLA_U8 , } ,
2015-05-17 21:44:39 +02:00
[ NL802154_ATTR_TX_POWER ] = { . type = NLA_S32 , } ,
2014-11-09 08:36:54 +01:00
2014-12-10 15:33:13 +01:00
[ NL802154_ATTR_CCA_MODE ] = { . type = NLA_U32 , } ,
[ NL802154_ATTR_CCA_OPT ] = { . type = NLA_U32 , } ,
2015-05-27 13:42:09 +02:00
[ NL802154_ATTR_CCA_ED_LEVEL ] = { . type = NLA_S32 , } ,
2014-11-09 08:36:54 +01:00
[ NL802154_ATTR_SUPPORTED_CHANNEL ] = { . type = NLA_U32 , } ,
2014-11-09 08:36:55 +01:00
[ NL802154_ATTR_PAN_ID ] = { . type = NLA_U16 , } ,
[ NL802154_ATTR_EXTENDED_ADDR ] = { . type = NLA_U64 } ,
[ NL802154_ATTR_SHORT_ADDR ] = { . type = NLA_U16 , } ,
[ NL802154_ATTR_MIN_BE ] = { . type = NLA_U8 , } ,
[ NL802154_ATTR_MAX_BE ] = { . type = NLA_U8 , } ,
[ NL802154_ATTR_MAX_CSMA_BACKOFFS ] = { . type = NLA_U8 , } ,
[ NL802154_ATTR_MAX_FRAME_RETRIES ] = { . type = NLA_S8 , } ,
[ NL802154_ATTR_LBT_MODE ] = { . type = NLA_U8 , } ,
2015-05-17 21:44:53 +02:00
[ NL802154_ATTR_WPAN_PHY_CAPS ] = { . type = NLA_NESTED } ,
2015-06-04 13:07:36 +05:30
[ NL802154_ATTR_SUPPORTED_COMMANDS ] = { . type = NLA_NESTED } ,
2015-08-10 21:15:58 +02:00
[ NL802154_ATTR_ACKREQ_DEFAULT ] = { . type = NLA_U8 } ,
2014-11-09 08:36:53 +01:00
} ;
/* message building helper */
static inline void * nl802154hdr_put ( struct sk_buff * skb , u32 portid , u32 seq ,
int flags , u8 cmd )
{
/* since there is no private header just add the generic one */
return genlmsg_put ( skb , portid , seq , & nl802154_fam , flags , cmd ) ;
}
2015-05-17 21:44:53 +02:00
static int
nl802154_put_flags ( struct sk_buff * msg , int attr , u32 mask )
{
struct nlattr * nl_flags = nla_nest_start ( msg , attr ) ;
int i ;
if ( ! nl_flags )
return - ENOBUFS ;
i = 0 ;
while ( mask ) {
if ( ( mask & 1 ) & & nla_put_flag ( msg , i ) )
return - ENOBUFS ;
mask > > = 1 ;
i + + ;
}
nla_nest_end ( msg , nl_flags ) ;
return 0 ;
}
2014-11-09 08:36:54 +01:00
static int
nl802154_send_wpan_phy_channels ( struct cfg802154_registered_device * rdev ,
struct sk_buff * msg )
{
struct nlattr * nl_page ;
unsigned long page ;
nl_page = nla_nest_start ( msg , NL802154_ATTR_CHANNELS_SUPPORTED ) ;
if ( ! nl_page )
return - ENOBUFS ;
2014-11-17 08:20:54 +01:00
for ( page = 0 ; page < = IEEE802154_MAX_PAGE ; page + + ) {
2014-11-09 08:36:54 +01:00
if ( nla_put_u32 ( msg , NL802154_ATTR_SUPPORTED_CHANNEL ,
2015-05-17 21:44:42 +02:00
rdev - > wpan_phy . supported . channels [ page ] ) )
2014-11-09 08:36:54 +01:00
return - ENOBUFS ;
}
nla_nest_end ( msg , nl_page ) ;
return 0 ;
}
2015-05-17 21:44:53 +02:00
static int
nl802154_put_capabilities ( struct sk_buff * msg ,
struct cfg802154_registered_device * rdev )
{
const struct wpan_phy_supported * caps = & rdev - > wpan_phy . supported ;
struct nlattr * nl_caps , * nl_channels ;
int i ;
nl_caps = nla_nest_start ( msg , NL802154_ATTR_WPAN_PHY_CAPS ) ;
if ( ! nl_caps )
return - ENOBUFS ;
nl_channels = nla_nest_start ( msg , NL802154_CAP_ATTR_CHANNELS ) ;
if ( ! nl_channels )
return - ENOBUFS ;
for ( i = 0 ; i < = IEEE802154_MAX_PAGE ; i + + ) {
if ( caps - > channels [ i ] ) {
if ( nl802154_put_flags ( msg , i , caps - > channels [ i ] ) )
return - ENOBUFS ;
}
}
nla_nest_end ( msg , nl_channels ) ;
if ( rdev - > wpan_phy . flags & WPAN_PHY_FLAG_CCA_ED_LEVEL ) {
struct nlattr * nl_ed_lvls ;
nl_ed_lvls = nla_nest_start ( msg ,
NL802154_CAP_ATTR_CCA_ED_LEVELS ) ;
if ( ! nl_ed_lvls )
return - ENOBUFS ;
for ( i = 0 ; i < caps - > cca_ed_levels_size ; i + + ) {
if ( nla_put_s32 ( msg , i , caps - > cca_ed_levels [ i ] ) )
return - ENOBUFS ;
}
nla_nest_end ( msg , nl_ed_lvls ) ;
}
if ( rdev - > wpan_phy . flags & WPAN_PHY_FLAG_TXPOWER ) {
struct nlattr * nl_tx_pwrs ;
nl_tx_pwrs = nla_nest_start ( msg , NL802154_CAP_ATTR_TX_POWERS ) ;
if ( ! nl_tx_pwrs )
return - ENOBUFS ;
for ( i = 0 ; i < caps - > tx_powers_size ; i + + ) {
if ( nla_put_s32 ( msg , i , caps - > tx_powers [ i ] ) )
return - ENOBUFS ;
}
nla_nest_end ( msg , nl_tx_pwrs ) ;
}
if ( rdev - > wpan_phy . flags & WPAN_PHY_FLAG_CCA_MODE ) {
if ( nl802154_put_flags ( msg , NL802154_CAP_ATTR_CCA_MODES ,
caps - > cca_modes ) | |
nl802154_put_flags ( msg , NL802154_CAP_ATTR_CCA_OPTS ,
caps - > cca_opts ) )
return - ENOBUFS ;
}
if ( nla_put_u8 ( msg , NL802154_CAP_ATTR_MIN_MINBE , caps - > min_minbe ) | |
nla_put_u8 ( msg , NL802154_CAP_ATTR_MAX_MINBE , caps - > max_minbe ) | |
nla_put_u8 ( msg , NL802154_CAP_ATTR_MIN_MAXBE , caps - > min_maxbe ) | |
nla_put_u8 ( msg , NL802154_CAP_ATTR_MAX_MAXBE , caps - > max_maxbe ) | |
nla_put_u8 ( msg , NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS ,
caps - > min_csma_backoffs ) | |
nla_put_u8 ( msg , NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS ,
caps - > max_csma_backoffs ) | |
nla_put_s8 ( msg , NL802154_CAP_ATTR_MIN_FRAME_RETRIES ,
caps - > min_frame_retries ) | |
nla_put_s8 ( msg , NL802154_CAP_ATTR_MAX_FRAME_RETRIES ,
caps - > max_frame_retries ) | |
nl802154_put_flags ( msg , NL802154_CAP_ATTR_IFTYPES ,
caps - > iftypes ) | |
nla_put_u32 ( msg , NL802154_CAP_ATTR_LBT , caps - > lbt ) )
return - ENOBUFS ;
nla_nest_end ( msg , nl_caps ) ;
return 0 ;
}
2014-11-09 08:36:54 +01:00
static int nl802154_send_wpan_phy ( struct cfg802154_registered_device * rdev ,
enum nl802154_commands cmd ,
struct sk_buff * msg , u32 portid , u32 seq ,
int flags )
{
2015-06-04 13:07:36 +05:30
struct nlattr * nl_cmds ;
2014-11-09 08:36:54 +01:00
void * hdr ;
2015-06-04 13:07:36 +05:30
int i ;
2014-11-09 08:36:54 +01:00
hdr = nl802154hdr_put ( msg , portid , seq , flags , cmd ) ;
if ( ! hdr )
return - ENOBUFS ;
if ( nla_put_u32 ( msg , NL802154_ATTR_WPAN_PHY , rdev - > wpan_phy_idx ) | |
nla_put_string ( msg , NL802154_ATTR_WPAN_PHY_NAME ,
wpan_phy_name ( & rdev - > wpan_phy ) ) | |
nla_put_u32 ( msg , NL802154_ATTR_GENERATION ,
cfg802154_rdev_list_generation ) )
goto nla_put_failure ;
if ( cmd ! = NL802154_CMD_NEW_WPAN_PHY )
goto finish ;
/* DUMP PHY PIB */
/* current channel settings */
if ( nla_put_u8 ( msg , NL802154_ATTR_PAGE ,
rdev - > wpan_phy . current_page ) | |
nla_put_u8 ( msg , NL802154_ATTR_CHANNEL ,
rdev - > wpan_phy . current_channel ) )
goto nla_put_failure ;
2015-05-17 21:44:53 +02:00
/* TODO remove this behaviour, we still keep support it for a while
* so users can change the behaviour to the new one .
*/
2014-11-09 08:36:54 +01:00
if ( nl802154_send_wpan_phy_channels ( rdev , msg ) )
goto nla_put_failure ;
/* cca mode */
2015-05-17 21:44:46 +02:00
if ( rdev - > wpan_phy . flags & WPAN_PHY_FLAG_CCA_MODE ) {
if ( nla_put_u32 ( msg , NL802154_ATTR_CCA_MODE ,
rdev - > wpan_phy . cca . mode ) )
2014-12-10 15:33:13 +01:00
goto nla_put_failure ;
2015-05-17 21:44:46 +02:00
if ( rdev - > wpan_phy . cca . mode = = NL802154_CCA_ENERGY_CARRIER ) {
if ( nla_put_u32 ( msg , NL802154_ATTR_CCA_OPT ,
rdev - > wpan_phy . cca . opt ) )
goto nla_put_failure ;
}
2014-12-10 15:33:13 +01:00
}
2015-05-17 21:44:46 +02:00
if ( rdev - > wpan_phy . flags & WPAN_PHY_FLAG_TXPOWER ) {
if ( nla_put_s32 ( msg , NL802154_ATTR_TX_POWER ,
rdev - > wpan_phy . transmit_power ) )
goto nla_put_failure ;
}
2014-11-09 08:36:54 +01:00
2015-05-27 13:42:09 +02:00
if ( rdev - > wpan_phy . flags & WPAN_PHY_FLAG_CCA_ED_LEVEL ) {
if ( nla_put_s32 ( msg , NL802154_ATTR_CCA_ED_LEVEL ,
rdev - > wpan_phy . cca_ed_level ) )
goto nla_put_failure ;
}
2015-05-17 21:44:53 +02:00
if ( nl802154_put_capabilities ( msg , rdev ) )
goto nla_put_failure ;
2015-06-04 13:07:36 +05:30
nl_cmds = nla_nest_start ( msg , NL802154_ATTR_SUPPORTED_COMMANDS ) ;
if ( ! nl_cmds )
goto nla_put_failure ;
i = 0 ;
# define CMD(op, n) \
do { \
if ( rdev - > ops - > op ) { \
i + + ; \
if ( nla_put_u32 ( msg , i , NL802154_CMD_ # # n ) ) \
goto nla_put_failure ; \
} \
} while ( 0 )
CMD ( add_virtual_intf , NEW_INTERFACE ) ;
CMD ( del_virtual_intf , DEL_INTERFACE ) ;
CMD ( set_channel , SET_CHANNEL ) ;
CMD ( set_pan_id , SET_PAN_ID ) ;
CMD ( set_short_addr , SET_SHORT_ADDR ) ;
CMD ( set_backoff_exponent , SET_BACKOFF_EXPONENT ) ;
CMD ( set_max_csma_backoffs , SET_MAX_CSMA_BACKOFFS ) ;
CMD ( set_max_frame_retries , SET_MAX_FRAME_RETRIES ) ;
CMD ( set_lbt_mode , SET_LBT_MODE ) ;
2015-08-10 21:15:58 +02:00
CMD ( set_ackreq_default , SET_ACKREQ_DEFAULT ) ;
2015-06-04 13:07:36 +05:30
if ( rdev - > wpan_phy . flags & WPAN_PHY_FLAG_TXPOWER )
CMD ( set_tx_power , SET_TX_POWER ) ;
if ( rdev - > wpan_phy . flags & WPAN_PHY_FLAG_CCA_ED_LEVEL )
CMD ( set_cca_ed_level , SET_CCA_ED_LEVEL ) ;
if ( rdev - > wpan_phy . flags & WPAN_PHY_FLAG_CCA_MODE )
CMD ( set_cca_mode , SET_CCA_MODE ) ;
# undef CMD
nla_nest_end ( msg , nl_cmds ) ;
2014-11-09 08:36:54 +01:00
finish :
2015-01-16 22:09:00 +01:00
genlmsg_end ( msg , hdr ) ;
return 0 ;
2014-11-09 08:36:54 +01:00
nla_put_failure :
genlmsg_cancel ( msg , hdr ) ;
return - EMSGSIZE ;
}
struct nl802154_dump_wpan_phy_state {
s64 filter_wpan_phy ;
long start ;
} ;
static int nl802154_dump_wpan_phy_parse ( struct sk_buff * skb ,
struct netlink_callback * cb ,
struct nl802154_dump_wpan_phy_state * state )
{
struct nlattr * * tb = nl802154_fam . attrbuf ;
int ret = nlmsg_parse ( cb - > nlh , GENL_HDRLEN + nl802154_fam . hdrsize ,
tb , nl802154_fam . maxattr , nl802154_policy ) ;
/* TODO check if we can handle error here,
* we have no backward compatibility
*/
if ( ret )
return 0 ;
if ( tb [ NL802154_ATTR_WPAN_PHY ] )
state - > filter_wpan_phy = nla_get_u32 ( tb [ NL802154_ATTR_WPAN_PHY ] ) ;
if ( tb [ NL802154_ATTR_WPAN_DEV ] )
state - > filter_wpan_phy = nla_get_u64 ( tb [ NL802154_ATTR_WPAN_DEV ] ) > > 32 ;
if ( tb [ NL802154_ATTR_IFINDEX ] ) {
struct net_device * netdev ;
struct cfg802154_registered_device * rdev ;
int ifidx = nla_get_u32 ( tb [ NL802154_ATTR_IFINDEX ] ) ;
/* TODO netns */
netdev = __dev_get_by_index ( & init_net , ifidx ) ;
if ( ! netdev )
return - ENODEV ;
if ( netdev - > ieee802154_ptr ) {
rdev = wpan_phy_to_rdev (
netdev - > ieee802154_ptr - > wpan_phy ) ;
state - > filter_wpan_phy = rdev - > wpan_phy_idx ;
}
}
return 0 ;
}
static int
nl802154_dump_wpan_phy ( struct sk_buff * skb , struct netlink_callback * cb )
{
int idx = 0 , ret ;
struct nl802154_dump_wpan_phy_state * state = ( void * ) cb - > args [ 0 ] ;
struct cfg802154_registered_device * rdev ;
rtnl_lock ( ) ;
if ( ! state ) {
state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
if ( ! state ) {
rtnl_unlock ( ) ;
return - ENOMEM ;
}
state - > filter_wpan_phy = - 1 ;
ret = nl802154_dump_wpan_phy_parse ( skb , cb , state ) ;
if ( ret ) {
kfree ( state ) ;
rtnl_unlock ( ) ;
return ret ;
}
cb - > args [ 0 ] = ( long ) state ;
}
list_for_each_entry ( rdev , & cfg802154_rdev_list , list ) {
/* TODO net ns compare */
if ( + + idx < = state - > start )
continue ;
if ( state - > filter_wpan_phy ! = - 1 & &
state - > filter_wpan_phy ! = rdev - > wpan_phy_idx )
continue ;
/* attempt to fit multiple wpan_phy data chunks into the skb */
ret = nl802154_send_wpan_phy ( rdev ,
NL802154_CMD_NEW_WPAN_PHY ,
skb ,
NETLINK_CB ( cb - > skb ) . portid ,
cb - > nlh - > nlmsg_seq , NLM_F_MULTI ) ;
if ( ret < 0 ) {
if ( ( ret = = - ENOBUFS | | ret = = - EMSGSIZE ) & &
! skb - > len & & cb - > min_dump_alloc < 4096 ) {
cb - > min_dump_alloc = 4096 ;
rtnl_unlock ( ) ;
return 1 ;
}
idx - - ;
break ;
}
break ;
}
rtnl_unlock ( ) ;
state - > start = idx ;
return skb - > len ;
}
static int nl802154_dump_wpan_phy_done ( struct netlink_callback * cb )
{
kfree ( ( void * ) cb - > args [ 0 ] ) ;
return 0 ;
}
static int nl802154_get_wpan_phy ( struct sk_buff * skb , struct genl_info * info )
{
struct sk_buff * msg ;
struct cfg802154_registered_device * rdev = info - > user_ptr [ 0 ] ;
msg = nlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
if ( nl802154_send_wpan_phy ( rdev , NL802154_CMD_NEW_WPAN_PHY , msg ,
info - > snd_portid , info - > snd_seq , 0 ) < 0 ) {
nlmsg_free ( msg ) ;
return - ENOBUFS ;
}
return genlmsg_reply ( msg , info ) ;
}
2014-11-09 08:36:55 +01:00
static inline u64 wpan_dev_id ( struct wpan_dev * wpan_dev )
{
return ( u64 ) wpan_dev - > identifier |
( ( u64 ) wpan_phy_to_rdev ( wpan_dev - > wpan_phy ) - > wpan_phy_idx < < 32 ) ;
}
static int
nl802154_send_iface ( struct sk_buff * msg , u32 portid , u32 seq , int flags ,
struct cfg802154_registered_device * rdev ,
struct wpan_dev * wpan_dev )
{
struct net_device * dev = wpan_dev - > netdev ;
void * hdr ;
hdr = nl802154hdr_put ( msg , portid , seq , flags ,
NL802154_CMD_NEW_INTERFACE ) ;
if ( ! hdr )
return - 1 ;
if ( dev & &
( nla_put_u32 ( msg , NL802154_ATTR_IFINDEX , dev - > ifindex ) | |
nla_put_string ( msg , NL802154_ATTR_IFNAME , dev - > name ) ) )
goto nla_put_failure ;
if ( nla_put_u32 ( msg , NL802154_ATTR_WPAN_PHY , rdev - > wpan_phy_idx ) | |
nla_put_u32 ( msg , NL802154_ATTR_IFTYPE , wpan_dev - > iftype ) | |
nla_put_u64 ( msg , NL802154_ATTR_WPAN_DEV , wpan_dev_id ( wpan_dev ) ) | |
nla_put_u32 ( msg , NL802154_ATTR_GENERATION ,
rdev - > devlist_generation ^
( cfg802154_rdev_list_generation < < 2 ) ) )
goto nla_put_failure ;
/* address settings */
if ( nla_put_le64 ( msg , NL802154_ATTR_EXTENDED_ADDR ,
wpan_dev - > extended_addr ) | |
nla_put_le16 ( msg , NL802154_ATTR_SHORT_ADDR ,
wpan_dev - > short_addr ) | |
nla_put_le16 ( msg , NL802154_ATTR_PAN_ID , wpan_dev - > pan_id ) )
goto nla_put_failure ;
/* ARET handling */
if ( nla_put_s8 ( msg , NL802154_ATTR_MAX_FRAME_RETRIES ,
wpan_dev - > frame_retries ) | |
nla_put_u8 ( msg , NL802154_ATTR_MAX_BE , wpan_dev - > max_be ) | |
nla_put_u8 ( msg , NL802154_ATTR_MAX_CSMA_BACKOFFS ,
wpan_dev - > csma_retries ) | |
nla_put_u8 ( msg , NL802154_ATTR_MIN_BE , wpan_dev - > min_be ) )
goto nla_put_failure ;
/* listen before transmit */
if ( nla_put_u8 ( msg , NL802154_ATTR_LBT_MODE , wpan_dev - > lbt ) )
goto nla_put_failure ;
2015-08-10 21:15:58 +02:00
/* ackreq default behaviour */
if ( nla_put_u8 ( msg , NL802154_ATTR_ACKREQ_DEFAULT , wpan_dev - > ackreq ) )
goto nla_put_failure ;
2015-01-16 22:09:00 +01:00
genlmsg_end ( msg , hdr ) ;
return 0 ;
2014-11-09 08:36:55 +01:00
nla_put_failure :
genlmsg_cancel ( msg , hdr ) ;
return - EMSGSIZE ;
}
static int
nl802154_dump_interface ( struct sk_buff * skb , struct netlink_callback * cb )
{
int wp_idx = 0 ;
int if_idx = 0 ;
int wp_start = cb - > args [ 0 ] ;
int if_start = cb - > args [ 1 ] ;
struct cfg802154_registered_device * rdev ;
struct wpan_dev * wpan_dev ;
rtnl_lock ( ) ;
list_for_each_entry ( rdev , & cfg802154_rdev_list , list ) {
/* TODO netns compare */
if ( wp_idx < wp_start ) {
wp_idx + + ;
continue ;
}
if_idx = 0 ;
list_for_each_entry ( wpan_dev , & rdev - > wpan_dev_list , list ) {
if ( if_idx < if_start ) {
if_idx + + ;
continue ;
}
if ( nl802154_send_iface ( skb , NETLINK_CB ( cb - > skb ) . portid ,
cb - > nlh - > nlmsg_seq , NLM_F_MULTI ,
rdev , wpan_dev ) < 0 ) {
goto out ;
}
if_idx + + ;
}
wp_idx + + ;
}
out :
rtnl_unlock ( ) ;
cb - > args [ 0 ] = wp_idx ;
cb - > args [ 1 ] = if_idx ;
return skb - > len ;
}
static int nl802154_get_interface ( struct sk_buff * skb , struct genl_info * info )
{
struct sk_buff * msg ;
struct cfg802154_registered_device * rdev = info - > user_ptr [ 0 ] ;
struct wpan_dev * wdev = info - > user_ptr [ 1 ] ;
msg = nlmsg_new ( NLMSG_DEFAULT_SIZE , GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
if ( nl802154_send_iface ( msg , info - > snd_portid , info - > snd_seq , 0 ,
rdev , wdev ) < 0 ) {
nlmsg_free ( msg ) ;
return - ENOBUFS ;
}
return genlmsg_reply ( msg , info ) ;
}
2014-11-17 08:20:51 +01:00
static int nl802154_new_interface ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg802154_registered_device * rdev = info - > user_ptr [ 0 ] ;
enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC ;
2014-11-17 08:20:52 +01:00
__le64 extended_addr = cpu_to_le64 ( 0x0000000000000000ULL ) ;
2014-11-17 08:20:51 +01:00
/* TODO avoid failing a new interface
* creation due to pending removal ?
*/
if ( ! info - > attrs [ NL802154_ATTR_IFNAME ] )
return - EINVAL ;
if ( info - > attrs [ NL802154_ATTR_IFTYPE ] ) {
type = nla_get_u32 ( info - > attrs [ NL802154_ATTR_IFTYPE ] ) ;
2015-05-17 21:44:47 +02:00
if ( type > NL802154_IFTYPE_MAX | |
! ( rdev - > wpan_phy . supported . iftypes & BIT ( type ) ) )
2014-11-17 08:20:51 +01:00
return - EINVAL ;
}
2014-11-17 08:20:52 +01:00
/* TODO add nla_get_le64 to netlink */
if ( info - > attrs [ NL802154_ATTR_EXTENDED_ADDR ] )
extended_addr = ( __force __le64 ) nla_get_u64 (
info - > attrs [ NL802154_ATTR_EXTENDED_ADDR ] ) ;
2014-11-17 08:20:51 +01:00
if ( ! rdev - > ops - > add_virtual_intf )
return - EOPNOTSUPP ;
return rdev_add_virtual_intf ( rdev ,
nla_data ( info - > attrs [ NL802154_ATTR_IFNAME ] ) ,
2015-04-30 17:44:57 +02:00
NET_NAME_USER , type , extended_addr ) ;
2014-11-17 08:20:51 +01:00
}
2014-11-17 08:20:53 +01:00
static int nl802154_del_interface ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg802154_registered_device * rdev = info - > user_ptr [ 0 ] ;
struct wpan_dev * wpan_dev = info - > user_ptr [ 1 ] ;
if ( ! rdev - > ops - > del_virtual_intf )
return - EOPNOTSUPP ;
/* If we remove a wpan device without a netdev then clear
* user_ptr [ 1 ] so that nl802154_post_doit won ' t dereference it
* to check if it needs to do dev_put ( ) . Otherwise it crashes
* since the wpan_dev has been freed , unlike with a netdev where
* we need the dev_put ( ) for the netdev to really be freed .
*/
if ( ! wpan_dev - > netdev )
info - > user_ptr [ 1 ] = NULL ;
return rdev_del_virtual_intf ( rdev , wpan_dev ) ;
}
2014-11-12 03:36:55 +01:00
static int nl802154_set_channel ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg802154_registered_device * rdev = info - > user_ptr [ 0 ] ;
u8 channel , page ;
if ( ! info - > attrs [ NL802154_ATTR_PAGE ] | |
! info - > attrs [ NL802154_ATTR_CHANNEL ] )
return - EINVAL ;
page = nla_get_u8 ( info - > attrs [ NL802154_ATTR_PAGE ] ) ;
channel = nla_get_u8 ( info - > attrs [ NL802154_ATTR_CHANNEL ] ) ;
/* check 802.15.4 constraints */
2015-05-17 21:44:38 +02:00
if ( page > IEEE802154_MAX_PAGE | | channel > IEEE802154_MAX_CHANNEL | |
2015-05-17 21:44:42 +02:00
! ( rdev - > wpan_phy . supported . channels [ page ] & BIT ( channel ) ) )
2014-11-12 03:36:55 +01:00
return - EINVAL ;
return rdev_set_channel ( rdev , page , channel ) ;
}
2014-12-10 15:33:13 +01:00
static int nl802154_set_cca_mode ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg802154_registered_device * rdev = info - > user_ptr [ 0 ] ;
struct wpan_phy_cca cca ;
2015-05-26 23:11:31 +02:00
if ( ! ( rdev - > wpan_phy . flags & WPAN_PHY_FLAG_CCA_MODE ) )
2015-05-17 21:44:46 +02:00
return - EOPNOTSUPP ;
2014-12-10 15:33:13 +01:00
if ( ! info - > attrs [ NL802154_ATTR_CCA_MODE ] )
return - EINVAL ;
cca . mode = nla_get_u32 ( info - > attrs [ NL802154_ATTR_CCA_MODE ] ) ;
/* checking 802.15.4 constraints */
2015-05-17 21:44:43 +02:00
if ( cca . mode < NL802154_CCA_ENERGY | |
cca . mode > NL802154_CCA_ATTR_MAX | |
! ( rdev - > wpan_phy . supported . cca_modes & BIT ( cca . mode ) ) )
2014-12-10 15:33:13 +01:00
return - EINVAL ;
if ( cca . mode = = NL802154_CCA_ENERGY_CARRIER ) {
if ( ! info - > attrs [ NL802154_ATTR_CCA_OPT ] )
return - EINVAL ;
cca . opt = nla_get_u32 ( info - > attrs [ NL802154_ATTR_CCA_OPT ] ) ;
2015-05-17 21:44:43 +02:00
if ( cca . opt > NL802154_CCA_OPT_ATTR_MAX | |
! ( rdev - > wpan_phy . supported . cca_opts & BIT ( cca . opt ) ) )
2014-12-10 15:33:13 +01:00
return - EINVAL ;
}
return rdev_set_cca_mode ( rdev , & cca ) ;
}
2015-05-27 13:42:10 +02:00
static int nl802154_set_cca_ed_level ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg802154_registered_device * rdev = info - > user_ptr [ 0 ] ;
s32 ed_level ;
int i ;
if ( ! ( rdev - > wpan_phy . flags & WPAN_PHY_FLAG_CCA_ED_LEVEL ) )
return - EOPNOTSUPP ;
if ( ! info - > attrs [ NL802154_ATTR_CCA_ED_LEVEL ] )
return - EINVAL ;
ed_level = nla_get_s32 ( info - > attrs [ NL802154_ATTR_CCA_ED_LEVEL ] ) ;
for ( i = 0 ; i < rdev - > wpan_phy . supported . cca_ed_levels_size ; i + + ) {
if ( ed_level = = rdev - > wpan_phy . supported . cca_ed_levels [ i ] )
return rdev_set_cca_ed_level ( rdev , ed_level ) ;
}
return - EINVAL ;
}
2015-05-27 09:10:54 +05:30
static int nl802154_set_tx_power ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg802154_registered_device * rdev = info - > user_ptr [ 0 ] ;
s32 power ;
int i ;
if ( ! ( rdev - > wpan_phy . flags & WPAN_PHY_FLAG_TXPOWER ) )
return - EOPNOTSUPP ;
if ( ! info - > attrs [ NL802154_ATTR_TX_POWER ] )
return - EINVAL ;
power = nla_get_s32 ( info - > attrs [ NL802154_ATTR_TX_POWER ] ) ;
for ( i = 0 ; i < rdev - > wpan_phy . supported . tx_powers_size ; i + + ) {
if ( power = = rdev - > wpan_phy . supported . tx_powers [ i ] )
return rdev_set_tx_power ( rdev , power ) ;
}
return - EINVAL ;
}
2014-11-12 03:36:57 +01:00
static int nl802154_set_pan_id ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg802154_registered_device * rdev = info - > user_ptr [ 0 ] ;
struct net_device * dev = info - > user_ptr [ 1 ] ;
struct wpan_dev * wpan_dev = dev - > ieee802154_ptr ;
2014-11-17 08:20:55 +01:00
__le16 pan_id ;
2014-11-12 03:36:57 +01:00
/* conflict here while tx/rx calls */
if ( netif_running ( dev ) )
return - EBUSY ;
/* don't change address fields on monitor */
2015-05-17 21:44:37 +02:00
if ( wpan_dev - > iftype = = NL802154_IFTYPE_MONITOR | |
! info - > attrs [ NL802154_ATTR_PAN_ID ] )
2014-11-12 03:36:57 +01:00
return - EINVAL ;
2014-11-17 08:20:55 +01:00
pan_id = nla_get_le16 ( info - > attrs [ NL802154_ATTR_PAN_ID ] ) ;
2014-11-12 03:36:57 +01:00
2015-05-17 21:44:38 +02:00
/* TODO
* I am not sure about to check here on broadcast pan_id .
* Broadcast is a valid setting , comment from 802.15 .4 :
* If this value is 0xffff , the device is not associated .
*
* This could useful to simple deassociate an device .
*/
if ( pan_id = = cpu_to_le16 ( IEEE802154_PAN_ID_BROADCAST ) )
return - EINVAL ;
2014-11-12 03:36:57 +01:00
return rdev_set_pan_id ( rdev , wpan_dev , pan_id ) ;
}
2014-11-12 03:36:58 +01:00
static int nl802154_set_short_addr ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg802154_registered_device * rdev = info - > user_ptr [ 0 ] ;
struct net_device * dev = info - > user_ptr [ 1 ] ;
struct wpan_dev * wpan_dev = dev - > ieee802154_ptr ;
2014-11-17 08:20:55 +01:00
__le16 short_addr ;
2014-11-12 03:36:58 +01:00
/* conflict here while tx/rx calls */
if ( netif_running ( dev ) )
return - EBUSY ;
/* don't change address fields on monitor */
2015-05-17 21:44:37 +02:00
if ( wpan_dev - > iftype = = NL802154_IFTYPE_MONITOR | |
! info - > attrs [ NL802154_ATTR_SHORT_ADDR ] )
2014-11-12 03:36:58 +01:00
return - EINVAL ;
2014-11-17 08:20:55 +01:00
short_addr = nla_get_le16 ( info - > attrs [ NL802154_ATTR_SHORT_ADDR ] ) ;
2014-11-12 03:36:58 +01:00
2015-05-17 21:44:38 +02:00
/* TODO
* I am not sure about to check here on broadcast short_addr .
* Broadcast is a valid setting , comment from 802.15 .4 :
* A value of 0xfffe indicates that the device has
* associated but has not been allocated an address . A
* value of 0xffff indicates that the device does not
* have a short address .
*
* I think we should allow to set these settings but
* don ' t allow to allow socket communication with it .
*/
if ( short_addr = = cpu_to_le16 ( IEEE802154_ADDR_SHORT_UNSPEC ) | |
short_addr = = cpu_to_le16 ( IEEE802154_ADDR_SHORT_BROADCAST ) )
return - EINVAL ;
2014-11-12 03:36:58 +01:00
return rdev_set_short_addr ( rdev , wpan_dev , short_addr ) ;
}
2014-11-12 03:36:59 +01:00
static int
nl802154_set_backoff_exponent ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg802154_registered_device * rdev = info - > user_ptr [ 0 ] ;
struct net_device * dev = info - > user_ptr [ 1 ] ;
struct wpan_dev * wpan_dev = dev - > ieee802154_ptr ;
u8 min_be , max_be ;
/* should be set on netif open inside phy settings */
if ( netif_running ( dev ) )
return - EBUSY ;
if ( ! info - > attrs [ NL802154_ATTR_MIN_BE ] | |
! info - > attrs [ NL802154_ATTR_MAX_BE ] )
return - EINVAL ;
min_be = nla_get_u8 ( info - > attrs [ NL802154_ATTR_MIN_BE ] ) ;
max_be = nla_get_u8 ( info - > attrs [ NL802154_ATTR_MAX_BE ] ) ;
/* check 802.15.4 constraints */
2015-05-17 21:44:43 +02:00
if ( min_be < rdev - > wpan_phy . supported . min_minbe | |
min_be > rdev - > wpan_phy . supported . max_minbe | |
max_be < rdev - > wpan_phy . supported . min_maxbe | |
max_be > rdev - > wpan_phy . supported . max_maxbe | |
min_be > max_be )
2014-11-12 03:36:59 +01:00
return - EINVAL ;
return rdev_set_backoff_exponent ( rdev , wpan_dev , min_be , max_be ) ;
}
2014-11-12 03:37:01 +01:00
static int
nl802154_set_max_csma_backoffs ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg802154_registered_device * rdev = info - > user_ptr [ 0 ] ;
struct net_device * dev = info - > user_ptr [ 1 ] ;
struct wpan_dev * wpan_dev = dev - > ieee802154_ptr ;
u8 max_csma_backoffs ;
/* conflict here while other running iface settings */
if ( netif_running ( dev ) )
return - EBUSY ;
if ( ! info - > attrs [ NL802154_ATTR_MAX_CSMA_BACKOFFS ] )
return - EINVAL ;
max_csma_backoffs = nla_get_u8 (
info - > attrs [ NL802154_ATTR_MAX_CSMA_BACKOFFS ] ) ;
/* check 802.15.4 constraints */
2015-05-17 21:44:43 +02:00
if ( max_csma_backoffs < rdev - > wpan_phy . supported . min_csma_backoffs | |
max_csma_backoffs > rdev - > wpan_phy . supported . max_csma_backoffs )
2014-11-12 03:37:01 +01:00
return - EINVAL ;
return rdev_set_max_csma_backoffs ( rdev , wpan_dev , max_csma_backoffs ) ;
}
2014-11-12 03:37:03 +01:00
static int
nl802154_set_max_frame_retries ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg802154_registered_device * rdev = info - > user_ptr [ 0 ] ;
struct net_device * dev = info - > user_ptr [ 1 ] ;
struct wpan_dev * wpan_dev = dev - > ieee802154_ptr ;
s8 max_frame_retries ;
if ( netif_running ( dev ) )
return - EBUSY ;
if ( ! info - > attrs [ NL802154_ATTR_MAX_FRAME_RETRIES ] )
return - EINVAL ;
max_frame_retries = nla_get_s8 (
info - > attrs [ NL802154_ATTR_MAX_FRAME_RETRIES ] ) ;
/* check 802.15.4 constraints */
2015-05-17 21:44:43 +02:00
if ( max_frame_retries < rdev - > wpan_phy . supported . min_frame_retries | |
max_frame_retries > rdev - > wpan_phy . supported . max_frame_retries )
2014-11-12 03:37:03 +01:00
return - EINVAL ;
return rdev_set_max_frame_retries ( rdev , wpan_dev , max_frame_retries ) ;
}
2014-11-12 03:37:05 +01:00
static int nl802154_set_lbt_mode ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg802154_registered_device * rdev = info - > user_ptr [ 0 ] ;
struct net_device * dev = info - > user_ptr [ 1 ] ;
struct wpan_dev * wpan_dev = dev - > ieee802154_ptr ;
2015-08-20 12:09:47 +02:00
int mode ;
2014-11-12 03:37:05 +01:00
if ( netif_running ( dev ) )
return - EBUSY ;
if ( ! info - > attrs [ NL802154_ATTR_LBT_MODE ] )
return - EINVAL ;
2015-08-20 12:09:47 +02:00
mode = nla_get_u8 ( info - > attrs [ NL802154_ATTR_LBT_MODE ] ) ;
if ( mode ! = 0 & & mode ! = 1 )
return - EINVAL ;
2015-05-17 21:44:43 +02:00
if ( ! wpan_phy_supported_bool ( mode , rdev - > wpan_phy . supported . lbt ) )
return - EINVAL ;
2014-11-12 03:37:05 +01:00
return rdev_set_lbt_mode ( rdev , wpan_dev , mode ) ;
}
2015-08-10 21:15:58 +02:00
static int
nl802154_set_ackreq_default ( struct sk_buff * skb , struct genl_info * info )
{
struct cfg802154_registered_device * rdev = info - > user_ptr [ 0 ] ;
struct net_device * dev = info - > user_ptr [ 1 ] ;
struct wpan_dev * wpan_dev = dev - > ieee802154_ptr ;
2015-08-20 12:09:47 +02:00
int ackreq ;
2015-08-10 21:15:58 +02:00
if ( netif_running ( dev ) )
return - EBUSY ;
if ( ! info - > attrs [ NL802154_ATTR_ACKREQ_DEFAULT ] )
return - EINVAL ;
2015-08-20 12:09:47 +02:00
ackreq = nla_get_u8 ( info - > attrs [ NL802154_ATTR_ACKREQ_DEFAULT ] ) ;
if ( ackreq ! = 0 & & ackreq ! = 1 )
return - EINVAL ;
2015-08-10 21:15:58 +02:00
return rdev_set_ackreq_default ( rdev , wpan_dev , ackreq ) ;
}
2014-11-09 08:36:53 +01:00
# define NL802154_FLAG_NEED_WPAN_PHY 0x01
# define NL802154_FLAG_NEED_NETDEV 0x02
# define NL802154_FLAG_NEED_RTNL 0x04
# define NL802154_FLAG_CHECK_NETDEV_UP 0x08
# define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\
NL802154_FLAG_CHECK_NETDEV_UP )
# define NL802154_FLAG_NEED_WPAN_DEV 0x10
# define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\
NL802154_FLAG_CHECK_NETDEV_UP )
static int nl802154_pre_doit ( const struct genl_ops * ops , struct sk_buff * skb ,
struct genl_info * info )
{
struct cfg802154_registered_device * rdev ;
struct wpan_dev * wpan_dev ;
struct net_device * dev ;
bool rtnl = ops - > internal_flags & NL802154_FLAG_NEED_RTNL ;
if ( rtnl )
rtnl_lock ( ) ;
if ( ops - > internal_flags & NL802154_FLAG_NEED_WPAN_PHY ) {
rdev = cfg802154_get_dev_from_info ( genl_info_net ( info ) , info ) ;
if ( IS_ERR ( rdev ) ) {
if ( rtnl )
rtnl_unlock ( ) ;
return PTR_ERR ( rdev ) ;
}
info - > user_ptr [ 0 ] = rdev ;
} else if ( ops - > internal_flags & NL802154_FLAG_NEED_NETDEV | |
ops - > internal_flags & NL802154_FLAG_NEED_WPAN_DEV ) {
ASSERT_RTNL ( ) ;
wpan_dev = __cfg802154_wpan_dev_from_attrs ( genl_info_net ( info ) ,
info - > attrs ) ;
if ( IS_ERR ( wpan_dev ) ) {
if ( rtnl )
rtnl_unlock ( ) ;
return PTR_ERR ( wpan_dev ) ;
}
dev = wpan_dev - > netdev ;
rdev = wpan_phy_to_rdev ( wpan_dev - > wpan_phy ) ;
if ( ops - > internal_flags & NL802154_FLAG_NEED_NETDEV ) {
if ( ! dev ) {
if ( rtnl )
rtnl_unlock ( ) ;
return - EINVAL ;
}
info - > user_ptr [ 1 ] = dev ;
} else {
info - > user_ptr [ 1 ] = wpan_dev ;
}
if ( dev ) {
if ( ops - > internal_flags & NL802154_FLAG_CHECK_NETDEV_UP & &
! netif_running ( dev ) ) {
if ( rtnl )
rtnl_unlock ( ) ;
return - ENETDOWN ;
}
dev_hold ( dev ) ;
}
info - > user_ptr [ 0 ] = rdev ;
}
return 0 ;
}
static void nl802154_post_doit ( const struct genl_ops * ops , struct sk_buff * skb ,
struct genl_info * info )
{
if ( info - > user_ptr [ 1 ] ) {
if ( ops - > internal_flags & NL802154_FLAG_NEED_WPAN_DEV ) {
struct wpan_dev * wpan_dev = info - > user_ptr [ 1 ] ;
if ( wpan_dev - > netdev )
dev_put ( wpan_dev - > netdev ) ;
} else {
dev_put ( info - > user_ptr [ 1 ] ) ;
}
}
if ( ops - > internal_flags & NL802154_FLAG_NEED_RTNL )
rtnl_unlock ( ) ;
}
static const struct genl_ops nl802154_ops [ ] = {
2014-11-09 08:36:54 +01:00
{
. cmd = NL802154_CMD_GET_WPAN_PHY ,
. doit = nl802154_get_wpan_phy ,
. dumpit = nl802154_dump_wpan_phy ,
. done = nl802154_dump_wpan_phy_done ,
. policy = nl802154_policy ,
/* can be retrieved by unprivileged users */
. internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
NL802154_FLAG_NEED_RTNL ,
} ,
2014-11-09 08:36:55 +01:00
{
. cmd = NL802154_CMD_GET_INTERFACE ,
. doit = nl802154_get_interface ,
. dumpit = nl802154_dump_interface ,
. policy = nl802154_policy ,
/* can be retrieved by unprivileged users */
. internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
NL802154_FLAG_NEED_RTNL ,
} ,
2014-11-17 08:20:51 +01:00
{
. cmd = NL802154_CMD_NEW_INTERFACE ,
. doit = nl802154_new_interface ,
. policy = nl802154_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
NL802154_FLAG_NEED_RTNL ,
} ,
2014-11-17 08:20:53 +01:00
{
. cmd = NL802154_CMD_DEL_INTERFACE ,
. doit = nl802154_del_interface ,
. policy = nl802154_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
NL802154_FLAG_NEED_RTNL ,
} ,
2014-11-12 03:36:55 +01:00
{
. cmd = NL802154_CMD_SET_CHANNEL ,
. doit = nl802154_set_channel ,
. policy = nl802154_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
NL802154_FLAG_NEED_RTNL ,
} ,
2014-12-10 15:33:13 +01:00
{
. cmd = NL802154_CMD_SET_CCA_MODE ,
. doit = nl802154_set_cca_mode ,
. policy = nl802154_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
NL802154_FLAG_NEED_RTNL ,
} ,
2015-05-27 13:42:10 +02:00
{
. cmd = NL802154_CMD_SET_CCA_ED_LEVEL ,
. doit = nl802154_set_cca_ed_level ,
. policy = nl802154_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
NL802154_FLAG_NEED_RTNL ,
} ,
2015-05-27 09:10:54 +05:30
{
. cmd = NL802154_CMD_SET_TX_POWER ,
. doit = nl802154_set_tx_power ,
. policy = nl802154_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
NL802154_FLAG_NEED_RTNL ,
} ,
2014-11-12 03:36:57 +01:00
{
. cmd = NL802154_CMD_SET_PAN_ID ,
. doit = nl802154_set_pan_id ,
. policy = nl802154_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = NL802154_FLAG_NEED_NETDEV |
NL802154_FLAG_NEED_RTNL ,
} ,
2014-11-12 03:36:58 +01:00
{
. cmd = NL802154_CMD_SET_SHORT_ADDR ,
. doit = nl802154_set_short_addr ,
. policy = nl802154_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = NL802154_FLAG_NEED_NETDEV |
NL802154_FLAG_NEED_RTNL ,
} ,
2014-11-12 03:36:59 +01:00
{
. cmd = NL802154_CMD_SET_BACKOFF_EXPONENT ,
. doit = nl802154_set_backoff_exponent ,
. policy = nl802154_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = NL802154_FLAG_NEED_NETDEV |
NL802154_FLAG_NEED_RTNL ,
} ,
2014-11-12 03:37:01 +01:00
{
. cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS ,
. doit = nl802154_set_max_csma_backoffs ,
. policy = nl802154_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = NL802154_FLAG_NEED_NETDEV |
NL802154_FLAG_NEED_RTNL ,
} ,
2014-11-12 03:37:03 +01:00
{
. cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES ,
. doit = nl802154_set_max_frame_retries ,
. policy = nl802154_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = NL802154_FLAG_NEED_NETDEV |
NL802154_FLAG_NEED_RTNL ,
} ,
2014-11-12 03:37:05 +01:00
{
. cmd = NL802154_CMD_SET_LBT_MODE ,
. doit = nl802154_set_lbt_mode ,
. policy = nl802154_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = NL802154_FLAG_NEED_NETDEV |
NL802154_FLAG_NEED_RTNL ,
} ,
2015-08-10 21:15:58 +02:00
{
. cmd = NL802154_CMD_SET_ACKREQ_DEFAULT ,
. doit = nl802154_set_ackreq_default ,
. policy = nl802154_policy ,
. flags = GENL_ADMIN_PERM ,
. internal_flags = NL802154_FLAG_NEED_NETDEV |
NL802154_FLAG_NEED_RTNL ,
} ,
2014-11-09 08:36:53 +01:00
} ;
/* initialisation/exit functions */
int nl802154_init ( void )
{
return genl_register_family_with_ops_groups ( & nl802154_fam , nl802154_ops ,
nl802154_mcgrps ) ;
}
void nl802154_exit ( void )
{
genl_unregister_family ( & nl802154_fam ) ;
}