2017-09-19 11:57:00 -04:00
/*
* Handling of a master device , switching frames via its switch fabric CPU port
*
* Copyright ( c ) 2017 Savoir - faire Linux Inc .
* Vivien Didelot < vivien . didelot @ savoirfairelinux . com >
*
* 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 "dsa_priv.h"
static void dsa_master_get_ethtool_stats ( struct net_device * dev ,
struct ethtool_stats * stats ,
uint64_t * data )
{
2017-09-29 17:19:20 -04:00
struct dsa_port * cpu_dp = dev - > dsa_ptr ;
2017-09-29 17:19:16 -04:00
const struct ethtool_ops * ops = cpu_dp - > orig_ethtool_ops ;
struct dsa_switch * ds = cpu_dp - > ds ;
int port = cpu_dp - > index ;
2017-09-19 11:57:00 -04:00
int count = 0 ;
if ( ops & & ops - > get_sset_count & & ops - > get_ethtool_stats ) {
count = ops - > get_sset_count ( dev , ETH_SS_STATS ) ;
ops - > get_ethtool_stats ( dev , stats , data ) ;
}
if ( ds - > ops - > get_ethtool_stats )
2017-09-29 17:19:16 -04:00
ds - > ops - > get_ethtool_stats ( ds , port , data + count ) ;
2017-09-19 11:57:00 -04:00
}
static int dsa_master_get_sset_count ( struct net_device * dev , int sset )
{
2017-09-29 17:19:20 -04:00
struct dsa_port * cpu_dp = dev - > dsa_ptr ;
2017-09-29 17:19:16 -04:00
const struct ethtool_ops * ops = cpu_dp - > orig_ethtool_ops ;
struct dsa_switch * ds = cpu_dp - > ds ;
2017-09-19 11:57:00 -04:00
int count = 0 ;
if ( ops & & ops - > get_sset_count )
count + = ops - > get_sset_count ( dev , sset ) ;
if ( sset = = ETH_SS_STATS & & ds - > ops - > get_sset_count )
count + = ds - > ops - > get_sset_count ( ds ) ;
return count ;
}
static void dsa_master_get_strings ( struct net_device * dev , uint32_t stringset ,
uint8_t * data )
{
2017-09-29 17:19:20 -04:00
struct dsa_port * cpu_dp = dev - > dsa_ptr ;
2017-09-29 17:19:16 -04:00
const struct ethtool_ops * ops = cpu_dp - > orig_ethtool_ops ;
struct dsa_switch * ds = cpu_dp - > ds ;
int port = cpu_dp - > index ;
2017-09-19 11:57:00 -04:00
int len = ETH_GSTRING_LEN ;
int mcount = 0 , count ;
unsigned int i ;
uint8_t pfx [ 4 ] ;
uint8_t * ndata ;
2017-09-29 17:19:16 -04:00
snprintf ( pfx , sizeof ( pfx ) , " p%.2d " , port ) ;
2017-09-19 11:57:00 -04:00
/* We do not want to be NULL-terminated, since this is a prefix */
pfx [ sizeof ( pfx ) - 1 ] = ' _ ' ;
if ( ops & & ops - > get_sset_count & & ops - > get_strings ) {
mcount = ops - > get_sset_count ( dev , ETH_SS_STATS ) ;
ops - > get_strings ( dev , stringset , data ) ;
}
if ( stringset = = ETH_SS_STATS & & ds - > ops - > get_strings ) {
ndata = data + mcount * len ;
/* This function copies ETH_GSTRINGS_LEN bytes, we will mangle
* the output after to prepend our CPU port prefix we
* constructed earlier
*/
2017-09-29 17:19:16 -04:00
ds - > ops - > get_strings ( ds , port , ndata ) ;
2017-09-19 11:57:00 -04:00
count = ds - > ops - > get_sset_count ( ds ) ;
for ( i = 0 ; i < count ; i + + ) {
memmove ( ndata + ( i * len + sizeof ( pfx ) ) ,
ndata + i * len , len - sizeof ( pfx ) ) ;
memcpy ( ndata + i * len , pfx , sizeof ( pfx ) ) ;
}
}
}
2017-11-06 16:11:45 -05:00
static int dsa_master_ethtool_setup ( struct net_device * dev )
2017-09-19 11:57:00 -04:00
{
2017-09-29 17:19:20 -04:00
struct dsa_port * cpu_dp = dev - > dsa_ptr ;
2017-09-29 17:19:16 -04:00
struct dsa_switch * ds = cpu_dp - > ds ;
2017-09-19 11:57:00 -04:00
struct ethtool_ops * ops ;
ops = devm_kzalloc ( ds - > dev , sizeof ( * ops ) , GFP_KERNEL ) ;
if ( ! ops )
return - ENOMEM ;
2017-09-29 17:19:16 -04:00
cpu_dp - > orig_ethtool_ops = dev - > ethtool_ops ;
if ( cpu_dp - > orig_ethtool_ops )
memcpy ( ops , cpu_dp - > orig_ethtool_ops , sizeof ( * ops ) ) ;
2017-09-19 11:57:00 -04:00
ops - > get_sset_count = dsa_master_get_sset_count ;
ops - > get_ethtool_stats = dsa_master_get_ethtool_stats ;
ops - > get_strings = dsa_master_get_strings ;
dev - > ethtool_ops = ops ;
return 0 ;
}
2017-11-06 16:11:45 -05:00
static void dsa_master_ethtool_teardown ( struct net_device * dev )
2017-09-19 11:57:00 -04:00
{
2017-09-29 17:19:20 -04:00
struct dsa_port * cpu_dp = dev - > dsa_ptr ;
2017-09-19 11:57:00 -04:00
2017-09-29 17:19:16 -04:00
dev - > ethtool_ops = cpu_dp - > orig_ethtool_ops ;
cpu_dp - > orig_ethtool_ops = NULL ;
2017-09-19 11:57:00 -04:00
}
2017-11-06 16:11:45 -05:00
int dsa_master_setup ( struct net_device * dev , struct dsa_port * cpu_dp )
{
/* If we use a tagging format that doesn't have an ethertype
* field , make sure that all packets from this point on get
* sent to the tag format ' s receive function .
*/
wmb ( ) ;
dev - > dsa_ptr = cpu_dp ;
return dsa_master_ethtool_setup ( dev ) ;
}
void dsa_master_teardown ( struct net_device * dev )
{
dsa_master_ethtool_teardown ( dev ) ;
dev - > dsa_ptr = NULL ;
/* If we used a tagging format that doesn't have an ethertype
* field , make sure that all packets from this point get sent
* without the tag and go through the regular receive path .
*/
wmb ( ) ;
}