2023-01-09 19:59:39 +03:00
// SPDX-License-Identifier: GPL-2.0-only
# include <linux/phy.h>
# include <linux/ethtool_netlink.h>
# include "netlink.h"
# include "common.h"
struct plca_req_info {
struct ethnl_req_info base ;
} ;
struct plca_reply_data {
struct ethnl_reply_data base ;
struct phy_plca_cfg plca_cfg ;
struct phy_plca_status plca_st ;
} ;
// Helpers ------------------------------------------------------------------ //
# define PLCA_REPDATA(__reply_base) \
container_of ( __reply_base , struct plca_reply_data , base )
// PLCA get configuration message ------------------------------------------- //
const struct nla_policy ethnl_plca_get_cfg_policy [ ] = {
[ ETHTOOL_A_PLCA_HEADER ] =
NLA_POLICY_NESTED ( ethnl_header_policy ) ,
} ;
2023-09-08 07:45:48 +03:00
static void plca_update_sint ( int * dst , struct nlattr * * tb , u32 attrid ,
bool * mod )
{
const struct nlattr * attr = tb [ attrid ] ;
if ( ! attr | |
WARN_ON_ONCE ( attrid > = ARRAY_SIZE ( ethnl_plca_set_cfg_policy ) ) )
return ;
switch ( ethnl_plca_set_cfg_policy [ attrid ] . type ) {
case NLA_U8 :
* dst = nla_get_u8 ( attr ) ;
break ;
case NLA_U32 :
* dst = nla_get_u32 ( attr ) ;
break ;
default :
WARN_ON_ONCE ( 1 ) ;
}
* mod = true ;
}
2023-01-09 19:59:39 +03:00
static int plca_get_cfg_prepare_data ( const struct ethnl_req_info * req_base ,
struct ethnl_reply_data * reply_base ,
2023-08-15 00:47:23 +03:00
const struct genl_info * info )
2023-01-09 19:59:39 +03:00
{
struct plca_reply_data * data = PLCA_REPDATA ( reply_base ) ;
struct net_device * dev = reply_base - > dev ;
const struct ethtool_phy_ops * ops ;
int ret ;
// check that the PHY device is available and connected
if ( ! dev - > phydev ) {
ret = - EOPNOTSUPP ;
goto out ;
}
// note: rtnl_lock is held already by ethnl_default_doit
ops = ethtool_phy_ops ;
if ( ! ops | | ! ops - > get_plca_cfg ) {
ret = - EOPNOTSUPP ;
goto out ;
}
ret = ethnl_ops_begin ( dev ) ;
2023-01-13 16:26:35 +03:00
if ( ret < 0 )
2023-01-09 19:59:39 +03:00
goto out ;
memset ( & data - > plca_cfg , 0xff ,
sizeof_field ( struct plca_reply_data , plca_cfg ) ) ;
ret = ops - > get_plca_cfg ( dev - > phydev , & data - > plca_cfg ) ;
ethnl_ops_complete ( dev ) ;
out :
return ret ;
}
static int plca_get_cfg_reply_size ( const struct ethnl_req_info * req_base ,
const struct ethnl_reply_data * reply_base )
{
return nla_total_size ( sizeof ( u16 ) ) + /* _VERSION */
nla_total_size ( sizeof ( u8 ) ) + /* _ENABLED */
nla_total_size ( sizeof ( u32 ) ) + /* _NODE_CNT */
nla_total_size ( sizeof ( u32 ) ) + /* _NODE_ID */
nla_total_size ( sizeof ( u32 ) ) + /* _TO_TIMER */
nla_total_size ( sizeof ( u32 ) ) + /* _BURST_COUNT */
nla_total_size ( sizeof ( u32 ) ) ; /* _BURST_TIMER */
}
static int plca_get_cfg_fill_reply ( struct sk_buff * skb ,
const struct ethnl_req_info * req_base ,
const struct ethnl_reply_data * reply_base )
{
const struct plca_reply_data * data = PLCA_REPDATA ( reply_base ) ;
const struct phy_plca_cfg * plca = & data - > plca_cfg ;
if ( ( plca - > version > = 0 & &
nla_put_u16 ( skb , ETHTOOL_A_PLCA_VERSION , plca - > version ) ) | |
( plca - > enabled > = 0 & &
nla_put_u8 ( skb , ETHTOOL_A_PLCA_ENABLED , ! ! plca - > enabled ) ) | |
( plca - > node_id > = 0 & &
nla_put_u32 ( skb , ETHTOOL_A_PLCA_NODE_ID , plca - > node_id ) ) | |
( plca - > node_cnt > = 0 & &
nla_put_u32 ( skb , ETHTOOL_A_PLCA_NODE_CNT , plca - > node_cnt ) ) | |
( plca - > to_tmr > = 0 & &
nla_put_u32 ( skb , ETHTOOL_A_PLCA_TO_TMR , plca - > to_tmr ) ) | |
( plca - > burst_cnt > = 0 & &
nla_put_u32 ( skb , ETHTOOL_A_PLCA_BURST_CNT , plca - > burst_cnt ) ) | |
( plca - > burst_tmr > = 0 & &
nla_put_u32 ( skb , ETHTOOL_A_PLCA_BURST_TMR , plca - > burst_tmr ) ) )
return - EMSGSIZE ;
return 0 ;
} ;
// PLCA set configuration message ------------------------------------------- //
const struct nla_policy ethnl_plca_set_cfg_policy [ ] = {
[ ETHTOOL_A_PLCA_HEADER ] =
NLA_POLICY_NESTED ( ethnl_header_policy ) ,
[ ETHTOOL_A_PLCA_ENABLED ] = NLA_POLICY_MAX ( NLA_U8 , 1 ) ,
[ ETHTOOL_A_PLCA_NODE_ID ] = NLA_POLICY_MAX ( NLA_U32 , 255 ) ,
[ ETHTOOL_A_PLCA_NODE_CNT ] = NLA_POLICY_RANGE ( NLA_U32 , 1 , 255 ) ,
[ ETHTOOL_A_PLCA_TO_TMR ] = NLA_POLICY_MAX ( NLA_U32 , 255 ) ,
[ ETHTOOL_A_PLCA_BURST_CNT ] = NLA_POLICY_MAX ( NLA_U32 , 255 ) ,
[ ETHTOOL_A_PLCA_BURST_TMR ] = NLA_POLICY_MAX ( NLA_U32 , 255 ) ,
} ;
2023-01-26 02:05:19 +03:00
static int
ethnl_set_plca ( struct ethnl_req_info * req_info , struct genl_info * info )
2023-01-09 19:59:39 +03:00
{
2023-01-26 02:05:19 +03:00
struct net_device * dev = req_info - > dev ;
2023-01-09 19:59:39 +03:00
const struct ethtool_phy_ops * ops ;
2023-01-26 02:05:19 +03:00
struct nlattr * * tb = info - > attrs ;
2023-01-09 19:59:39 +03:00
struct phy_plca_cfg plca_cfg ;
bool mod = false ;
int ret ;
// check that the PHY device is available and connected
2023-01-26 02:05:19 +03:00
if ( ! dev - > phydev )
return - EOPNOTSUPP ;
2023-01-09 19:59:39 +03:00
ops = ethtool_phy_ops ;
2023-01-26 02:05:19 +03:00
if ( ! ops | | ! ops - > set_plca_cfg )
return - EOPNOTSUPP ;
2023-01-09 19:59:39 +03:00
memset ( & plca_cfg , 0xff , sizeof ( plca_cfg ) ) ;
2023-09-08 07:45:48 +03:00
plca_update_sint ( & plca_cfg . enabled , tb , ETHTOOL_A_PLCA_ENABLED , & mod ) ;
plca_update_sint ( & plca_cfg . node_id , tb , ETHTOOL_A_PLCA_NODE_ID , & mod ) ;
plca_update_sint ( & plca_cfg . node_cnt , tb , ETHTOOL_A_PLCA_NODE_CNT , & mod ) ;
plca_update_sint ( & plca_cfg . to_tmr , tb , ETHTOOL_A_PLCA_TO_TMR , & mod ) ;
plca_update_sint ( & plca_cfg . burst_cnt , tb , ETHTOOL_A_PLCA_BURST_CNT ,
2023-01-09 19:59:39 +03:00
& mod ) ;
2023-09-08 07:45:48 +03:00
plca_update_sint ( & plca_cfg . burst_tmr , tb , ETHTOOL_A_PLCA_BURST_TMR ,
2023-01-09 19:59:39 +03:00
& mod ) ;
if ( ! mod )
2023-01-26 02:05:19 +03:00
return 0 ;
2023-01-09 19:59:39 +03:00
ret = ops - > set_plca_cfg ( dev - > phydev , & plca_cfg , info - > extack ) ;
2023-01-26 02:05:19 +03:00
return ret < 0 ? ret : 1 ;
}
2023-01-09 19:59:39 +03:00
2023-01-26 02:05:19 +03:00
const struct ethnl_request_ops ethnl_plca_cfg_request_ops = {
. request_cmd = ETHTOOL_MSG_PLCA_GET_CFG ,
. reply_cmd = ETHTOOL_MSG_PLCA_GET_CFG_REPLY ,
. hdr_attr = ETHTOOL_A_PLCA_HEADER ,
. req_info_size = sizeof ( struct plca_req_info ) ,
. reply_data_size = sizeof ( struct plca_reply_data ) ,
2023-01-09 19:59:39 +03:00
2023-01-26 02:05:19 +03:00
. prepare_data = plca_get_cfg_prepare_data ,
. reply_size = plca_get_cfg_reply_size ,
. fill_reply = plca_get_cfg_fill_reply ,
2023-01-09 19:59:39 +03:00
2023-01-26 02:05:19 +03:00
. set = ethnl_set_plca ,
. set_ntf_cmd = ETHTOOL_MSG_PLCA_NTF ,
} ;
2023-01-09 19:59:39 +03:00
// PLCA get status message -------------------------------------------------- //
const struct nla_policy ethnl_plca_get_status_policy [ ] = {
[ ETHTOOL_A_PLCA_HEADER ] =
NLA_POLICY_NESTED ( ethnl_header_policy ) ,
} ;
static int plca_get_status_prepare_data ( const struct ethnl_req_info * req_base ,
struct ethnl_reply_data * reply_base ,
2023-08-15 00:47:23 +03:00
const struct genl_info * info )
2023-01-09 19:59:39 +03:00
{
struct plca_reply_data * data = PLCA_REPDATA ( reply_base ) ;
struct net_device * dev = reply_base - > dev ;
const struct ethtool_phy_ops * ops ;
int ret ;
// check that the PHY device is available and connected
if ( ! dev - > phydev ) {
ret = - EOPNOTSUPP ;
goto out ;
}
// note: rtnl_lock is held already by ethnl_default_doit
ops = ethtool_phy_ops ;
if ( ! ops | | ! ops - > get_plca_status ) {
ret = - EOPNOTSUPP ;
goto out ;
}
ret = ethnl_ops_begin ( dev ) ;
2023-01-13 16:26:35 +03:00
if ( ret < 0 )
2023-01-09 19:59:39 +03:00
goto out ;
memset ( & data - > plca_st , 0xff ,
sizeof_field ( struct plca_reply_data , plca_st ) ) ;
ret = ops - > get_plca_status ( dev - > phydev , & data - > plca_st ) ;
ethnl_ops_complete ( dev ) ;
out :
return ret ;
}
static int plca_get_status_reply_size ( const struct ethnl_req_info * req_base ,
const struct ethnl_reply_data * reply_base )
{
return nla_total_size ( sizeof ( u8 ) ) ; /* _STATUS */
}
static int plca_get_status_fill_reply ( struct sk_buff * skb ,
const struct ethnl_req_info * req_base ,
const struct ethnl_reply_data * reply_base )
{
const struct plca_reply_data * data = PLCA_REPDATA ( reply_base ) ;
const u8 status = data - > plca_st . pst ;
if ( nla_put_u8 ( skb , ETHTOOL_A_PLCA_STATUS , ! ! status ) )
return - EMSGSIZE ;
return 0 ;
} ;
const struct ethnl_request_ops ethnl_plca_status_request_ops = {
. request_cmd = ETHTOOL_MSG_PLCA_GET_STATUS ,
. reply_cmd = ETHTOOL_MSG_PLCA_GET_STATUS_REPLY ,
. hdr_attr = ETHTOOL_A_PLCA_HEADER ,
. req_info_size = sizeof ( struct plca_req_info ) ,
. reply_data_size = sizeof ( struct plca_reply_data ) ,
. prepare_data = plca_get_status_prepare_data ,
. reply_size = plca_get_status_reply_size ,
. fill_reply = plca_get_status_fill_reply ,
} ;