diff options
Diffstat (limited to 'ethtool.c')
-rw-r--r-- | ethtool.c | 2003 |
1 files changed, 2003 insertions, 0 deletions
diff --git a/ethtool.c b/ethtool.c new file mode 100644 index 0000000..1eceb63 --- /dev/null +++ b/ethtool.c @@ -0,0 +1,2003 @@ +/* + * ethtool.c: Linux ethernet device configuration tool. + * + * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com) + * Portions Copyright 2001 Sun Microsystems + * Kernel 2.4 update Copyright 2001 Jeff Garzik <jgarzik@mandrakesoft.com> + * Wake-on-LAN,natsemi,misc support by Tim Hockin <thockin@sun.com> + * Portions Copyright 2002 Intel + * do_test support by Eli Kupermann <eli.kupermann@intel.com> + * ETHTOOL_PHYS_ID support by Chris Leech <christopher.leech@intel.com> + * e1000 support by Scott Feldman <scott.feldman@intel.com> + * e100 support by Wen Tao <wen-hwa.tao@intel.com> + * amd8111e support by Reeja John <reeja.john@amd.com> + * + * TODO: + * * no-args => summary of each device (mii-tool style) + * * better man page (steal from mii-tool?) + * * fall back on SIOCMII* ioctl()s and possibly SIOCDEVPRIVATE* + * * abstract ioctls to allow for fallback modes of data gathering + * * symbolic names for msglvl bitmask + */ + +#ifdef HAVE_CONFIG_H +# include "ethtool-config.h" +#endif + +#include <sys/types.h> +#include <string.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <net/if.h> + +#include <linux/sockios.h> +#include "ethtool-util.h" + +#ifndef SIOCETHTOOL +#define SIOCETHTOOL 0x8946 +#endif +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +static int parse_wolopts(char *optstr, int *data); +static char *unparse_wolopts(int wolopts); +static int parse_sopass(char *src, unsigned char *dest); +static int do_gdrv(int fd, struct ifreq *ifr); +static int do_gset(int fd, struct ifreq *ifr); +static int do_sset(int fd, struct ifreq *ifr); +static int do_gregs(int fd, struct ifreq *ifr); +static int do_nway_rst(int fd, struct ifreq *ifr); +static int do_geeprom(int fd, struct ifreq *ifr); +static int do_seeprom(int fd, struct ifreq *ifr); +static int do_test(int fd, struct ifreq *ifr); +static int do_phys_id(int fd, struct ifreq *ifr); +static int do_gpause(int fd, struct ifreq *ifr); +static int do_spause(int fd, struct ifreq *ifr); +static int do_gring(int fd, struct ifreq *ifr); +static int do_sring(int fd, struct ifreq *ifr); +static int do_gcoalesce(int fd, struct ifreq *ifr); +static int do_scoalesce(int fd, struct ifreq *ifr); +static int do_goffload(int fd, struct ifreq *ifr); +static int do_soffload(int fd, struct ifreq *ifr); +static int do_gstats(int fd, struct ifreq *ifr); + +/* Syntax: + * + * ethtool DEVNAME + * ethtool -a DEVNAME + * ethtool -A DEVNAME \ + * [ autoneg on|off ] \ + * [ rx on|off ] \ + * [ tx on|off ] + * ethtool -c DEVNAME + * ethtool -C DEVNAME \ + * [adaptive-rx on|off] \ + * [adaptive-tx on|off] \ + * [rx-usecs N] \ + * [rx-frames N] \ + * [rx-usecs-irq N] \ + * [rx-frames-irq N] \ + * [tx-usecs N] \ + * [tx-frames N] \ + * [tx-usecs-irq N] \ + * [tx-frames-irq N] \ + * [stats-block-usecs N] \ + * [pkt-rate-low N] \ + * [rx-usecs-low N] \ + * [rx-frames-low N] \ + * [tx-usecs-low N] \ + * [tx-frames-low N] \ + * [pkt-rate-high N] \ + * [rx-usecs-high N] \ + * [rx-frames-high N] \ + * [tx-usecs-high N] \ + * [tx-frames-high N] \ + * [sample-interval N] + * ethtool -g DEVNAME + * ethtool -G DEVNAME \ + * [ rx N ] \ + * [ rx-mini N ] \ + * [ rx-jumbo N ] \ + * [ tx N ] + * ethtool -i DEVNAME + * ethtool -d DEVNAME [ raw on|off ] + * ethtool -e DEVNAME \ + * [ raw on|off ] \ + * [ offset N ] \ + * [ len N ] + * ethtool -E DEVNAME \ + * [ magic N ] \ + * [ offset N ] \ + * [ value N ] \ + * ethtool -k DEVNAME + * ethtool -K DEVNAME \ + * [ rx on|off ] \ + * [ tx on|off ] \ + * [ sg on|off ] \ + * [ tso on|off ] + * ethtool -r DEVNAME + * ethtool -p DEVNAME [ %d ] + * ethtool -t DEVNAME [ online|offline ] + * ethtool -s DEVNAME [ speed 10|100|1000 ] \ + * [ duplex half|full ] \ + * [ port tp|aui|bnc|mii|fibre ] \ + * [ autoneg on|off ] \ + * [ phyad %d ] \ + * [ xcvr internal|external ] \ + * [ wol p|u|m|b|a|g|s|d... ] \ + * [ sopass %x:%x:%x:%x:%x:%x ] \ + * [ msglvl %d ] + * ethtool -S DEVNAME + */ + +static void show_usage(int badarg) +{ + fprintf(stderr, PACKAGE " version " VERSION "\n"); + fprintf(stderr, + "Usage:\n" + " ethtool DEVNAME\n" + " ethtool -a DEVNAME\n" + " ethtool -A DEVNAME \\\n" + " [ autoneg on|off ] \\\n" + " [ rx on|off ] \\\n" + " [ tx on|off ]\n" + " ethtool -c DEVNAME\n" + " ethtool -C DEVNAME \\\n" + " [adaptive-rx on|off] \\\n" + " [adaptive-tx on|off] \\\n" + " [rx-usecs N] \\\n" + " [rx-frames N] \\\n" + " [rx-usecs-irq N] \\\n" + " [rx-frames-irq N] \\\n" + " [tx-usecs N] \\\n" + " [tx-frames N] \\\n" + " [tx-usecs-irq N] \\\n" + " [tx-frames-irq N] \\\n" + " [stats-block-usecs N] \\\n" + " [pkt-rate-low N] \\\n" + " [rx-usecs-low N] \\\n" + " [rx-frames-low N] \\\n" + " [tx-usecs-low N] \\\n" + " [tx-frames-low N] \\\n" + " [pkt-rate-high N] \\\n" + " [rx-usecs-high N] \\\n" + " [rx-frames-high N] \\\n" + " [tx-usecs-high N] \\\n" + " [tx-frames-high N] \\\n" + " [sample-interval N]\n" + " ethtool -g DEVNAME\n" + " ethtool -G DEVNAME \\\n" + " [ rx N ] \\\n" + " [ rx-mini N ] \\\n" + " [ rx-jumbo N ] \\\n" + " [ tx N ]\n" + " ethtool -i DEVNAME\n" + " ethtool -d DEVNAME [ raw on|off ]\n" + " ethtool -e DEVNAME \\\n" + " [ raw on|off ] \\\n" + " [ offset N ] \\\n" + " [ length N ]\n" + " ethtool -E DEVNAME \\\n" + " [ magic N ] \\\n" + " [ offset N ] \\\n" + " [ value N ]\n" + " ethtool -k DEVNAME\n" + " ethtool -K DEVNAME \\\n" + " [ rx on|off ] \\\n" + " [ tx on|off ] \\\n" + " [ sg on|off ] \\\n" + " [ tso on|off ]\n" + " ethtool -r DEVNAME\n" + " ethtool -p DEVNAME [ %%d ]\n" + " ethtool -t DEVNAME [online|(offline)]\n" + " ethtool -s DEVNAME \\\n" + " [ speed 10|100|1000 ] \\\n" + " [ duplex half|full ] \\\n" + " [ port tp|aui|bnc|mii|fibre ] \\\n" + " [ autoneg on|off ] \\\n" + " [ phyad %%d ] \\\n" + " [ xcvr internal|external ] \\\n" + " [ wol p|u|m|b|a|g|s|d... ] \\\n" + " [ sopass %%x:%%x:%%x:%%x:%%x:%%x ] \\\n" + " [ msglvl %%d ] \n" + " ethtool -S DEVNAME\n" + ); + exit(badarg); +} + +static char *devname = NULL; +static enum { + MODE_GSET=0, + MODE_SSET, + MODE_GDRV, + MODE_GREGS, + MODE_NWAY_RST, + MODE_GEEPROM, + MODE_SEEPROM, + MODE_TEST, + MODE_PHYS_ID, + MODE_GPAUSE, + MODE_SPAUSE, + MODE_GCOALESCE, + MODE_SCOALESCE, + MODE_GRING, + MODE_SRING, + MODE_GOFFLOAD, + MODE_SOFFLOAD, + MODE_GSTATS, +} mode = MODE_GSET; + +static int goffload_changed = 0; +static int off_csum_rx_wanted = -1; +static int off_csum_tx_wanted = -1; +static int off_sg_wanted = -1; +static int off_tso_wanted = -1; + +static struct ethtool_pauseparam epause; +static int gpause_changed = 0; +static int pause_autoneg_wanted = -1; +static int pause_rx_wanted = -1; +static int pause_tx_wanted = -1; + +static struct ethtool_ringparam ering; +static int gring_changed = 0; +static int ring_rx_wanted = -1; +static int ring_rx_mini_wanted = -1; +static int ring_rx_jumbo_wanted = -1; +static int ring_tx_wanted = -1; + +static struct ethtool_coalesce ecoal; +static int gcoalesce_changed = 0; +static int coal_stats_wanted = -1; +static int coal_adaptive_rx_wanted = -1; +static int coal_adaptive_tx_wanted = -1; +static int coal_sample_rate_wanted = -1; +static int coal_pkt_rate_low_wanted = -1; +static int coal_pkt_rate_high_wanted = -1; +static int coal_rx_usec_wanted = -1; +static int coal_rx_frames_wanted = -1; +static int coal_rx_usec_irq_wanted = -1; +static int coal_rx_frames_irq_wanted = -1; +static int coal_tx_usec_wanted = -1; +static int coal_tx_frames_wanted = -1; +static int coal_tx_usec_irq_wanted = -1; +static int coal_tx_frames_irq_wanted = -1; +static int coal_rx_usec_low_wanted = -1; +static int coal_rx_frames_low_wanted = -1; +static int coal_tx_usec_low_wanted = -1; +static int coal_tx_frames_low_wanted = -1; +static int coal_rx_usec_high_wanted = -1; +static int coal_rx_frames_high_wanted = -1; +static int coal_tx_usec_high_wanted = -1; +static int coal_tx_frames_high_wanted = -1; + +static int speed_wanted = -1; +static int duplex_wanted = -1; +static int port_wanted = -1; +static int autoneg_wanted = -1; +static int phyad_wanted = -1; +static int xcvr_wanted = -1; +static int advertising_wanted = -1; +static int gset_changed = 0; /* did anything in GSET change? */ +static u32 wol_wanted = 0; +static int wol_change = 0; +static u8 sopass_wanted[SOPASS_MAX]; +static int sopass_change = 0; +static int gwol_changed = 0; /* did anything in GWOL change? */ +static int msglvl_wanted = -1; +static int phys_id_time = 0; +static int gregs_changed = 0; +static int gregs_dump_raw = 0; +static int geeprom_changed = 0; +static int geeprom_dump_raw = 0; +static int geeprom_offset = 0; +static int geeprom_length = -1; +static int seeprom_changed = 0; +static int seeprom_magic = 0; +static int seeprom_offset = -1; +static int seeprom_value = 0; +static enum { + ONLINE=0, + OFFLINE, +} test_type = OFFLINE; + +typedef enum { + CMDL_NONE, + CMDL_BOOL, + CMDL_INT, +} cmdline_type_t; + +struct cmdline_info { + const char *name; + cmdline_type_t type; + void *wanted_val; + void *ioctl_val; +}; + +static struct cmdline_info cmdline_gregs[] = { + { "raw", CMDL_BOOL, &gregs_dump_raw, NULL }, +}; + +static struct cmdline_info cmdline_geeprom[] = { + { "offset", CMDL_INT, &geeprom_offset, NULL }, + { "length", CMDL_INT, &geeprom_length, NULL }, + { "raw", CMDL_BOOL, &geeprom_dump_raw, NULL }, +}; + +static struct cmdline_info cmdline_seeprom[] = { + { "magic", CMDL_INT, &seeprom_magic, NULL }, + { "offset", CMDL_INT, &seeprom_offset, NULL }, + { "value", CMDL_INT, &seeprom_value, NULL }, +}; + +static struct cmdline_info cmdline_offload[] = { + { "rx", CMDL_BOOL, &off_csum_rx_wanted, NULL }, + { "tx", CMDL_BOOL, &off_csum_tx_wanted, NULL }, + { "sg", CMDL_BOOL, &off_sg_wanted, NULL }, + { "tso", CMDL_BOOL, &off_tso_wanted, NULL }, +}; + +static struct cmdline_info cmdline_pause[] = { + { "autoneg", CMDL_BOOL, &pause_autoneg_wanted, &epause.autoneg }, + { "rx", CMDL_BOOL, &pause_rx_wanted, &epause.rx_pause }, + { "tx", CMDL_BOOL, &pause_tx_wanted, &epause.tx_pause }, +}; + +static struct cmdline_info cmdline_ring[] = { + { "rx", CMDL_INT, &ring_rx_wanted, &ering.rx_pending }, + { "rx-mini", CMDL_INT, &ring_rx_mini_wanted, &ering.rx_mini_pending }, + { "rx-jumbo", CMDL_INT, &ring_rx_jumbo_wanted, &ering.rx_jumbo_pending }, + { "tx", CMDL_INT, &ring_tx_wanted, &ering.tx_pending }, +}; + +static struct cmdline_info cmdline_coalesce[] = { + { "adaptive-rx", CMDL_BOOL, &coal_adaptive_rx_wanted, &ecoal.use_adaptive_rx_coalesce }, + { "adaptive-tx", CMDL_BOOL, &coal_adaptive_tx_wanted, &ecoal.use_adaptive_tx_coalesce }, + { "sample-interval", CMDL_INT, &coal_sample_rate_wanted, &ecoal.rate_sample_interval }, + { "stats-block-usecs", CMDL_INT, &coal_stats_wanted, &ecoal.stats_block_coalesce_usecs }, + { "pkt-rate-low", CMDL_INT, &coal_pkt_rate_low_wanted, &ecoal.pkt_rate_low }, + { "pkt-rate-high", CMDL_INT, &coal_pkt_rate_high_wanted, &ecoal.pkt_rate_high }, + { "rx-usecs", CMDL_INT, &coal_rx_usec_wanted, &ecoal.rx_coalesce_usecs }, + { "rx-frames", CMDL_INT, &coal_rx_frames_wanted, &ecoal.rx_max_coalesced_frames }, + { "rx-usecs-irq", CMDL_INT, &coal_rx_usec_irq_wanted, &ecoal.rx_coalesce_usecs_irq }, + { "rx-frames-irq", CMDL_INT, &coal_rx_frames_irq_wanted, &ecoal.rx_max_coalesced_frames_irq }, + { "tx-usecs", CMDL_INT, &coal_tx_usec_wanted, &ecoal.tx_coalesce_usecs }, + { "tx-frames", CMDL_INT, &coal_tx_frames_wanted, &ecoal.tx_max_coalesced_frames }, + { "tx-usecs-irq", CMDL_INT, &coal_tx_usec_irq_wanted, &ecoal.tx_coalesce_usecs_irq }, + { "tx-frames-irq", CMDL_INT, &coal_tx_frames_irq_wanted, &ecoal.tx_max_coalesced_frames_irq }, + { "rx-usecs-low", CMDL_INT, &coal_rx_usec_low_wanted, &ecoal.rx_coalesce_usecs_low }, + { "rx-frames-low", CMDL_INT, &coal_rx_frames_low_wanted, &ecoal.rx_max_coalesced_frames_low }, + { "tx-usecs-low", CMDL_INT, &coal_tx_usec_low_wanted, &ecoal.tx_coalesce_usecs_low }, + { "tx-frames-low", CMDL_INT, &coal_tx_frames_low_wanted, &ecoal.tx_max_coalesced_frames_low }, + { "rx-usecs-high", CMDL_INT, &coal_rx_usec_high_wanted, &ecoal.rx_coalesce_usecs_high }, + { "rx-frames-high", CMDL_INT, &coal_rx_frames_high_wanted, &ecoal.rx_max_coalesced_frames_high }, + { "tx-usecs-high", CMDL_INT, &coal_tx_usec_high_wanted, &ecoal.tx_coalesce_usecs_high }, + { "tx-frames-high", CMDL_INT, &coal_tx_frames_high_wanted, &ecoal.tx_max_coalesced_frames_high }, +}; + +static void parse_generic_cmdline(int argc, char **argp, + int first_arg, int *changed, + struct cmdline_info *info, + unsigned int n_info) +{ + int i, idx, *p; + + for (i = first_arg; i < argc; i++) { + for (idx = 0; idx < n_info; idx++) { + if (!strcmp(info[idx].name, argp[i])) { + *changed = 1; + i += 1; + if (i >= argc) + show_usage(1); + p = info[idx].wanted_val; + if (info[idx].type == CMDL_BOOL) { + if (!strcmp(argp[i], "on")) + *p = 1; + else if (!strcmp(argp[i], "off")) + *p = 0; + else + show_usage(1); + } else if (info[idx].type == CMDL_INT) { + long v; + v = strtol(argp[i], NULL, 0); + if (v < 0) + show_usage(1); + *p = (int) v; + } else { + show_usage(1); + } + } + } + } +} + +static void parse_cmdline(int argc, char **argp) +{ + int i; + + for (i = 1; i < argc; i++) { + switch (i) { + case 1: + if (!strcmp(argp[i], "-s")) + mode = MODE_SSET; + else if (!strcmp(argp[i], "-a")) + mode = MODE_GPAUSE; + else if (!strcmp(argp[i], "-A")) + mode = MODE_SPAUSE; + else if (!strcmp(argp[i], "-c")) + mode = MODE_GCOALESCE; + else if (!strcmp(argp[i], "-C")) + mode = MODE_SCOALESCE; + else if (!strcmp(argp[i], "-g")) + mode = MODE_GRING; + else if (!strcmp(argp[i], "-G")) + mode = MODE_SRING; + else if (!strcmp(argp[i], "-k")) + mode = MODE_GOFFLOAD; + else if (!strcmp(argp[i], "-K")) + mode = MODE_SOFFLOAD; + else if (!strcmp(argp[i], "-i")) + mode = MODE_GDRV; + else if (!strcmp(argp[i], "-d")) + mode = MODE_GREGS; + else if (!strcmp(argp[i], "-e")) + mode = MODE_GEEPROM; + else if (!strcmp(argp[i], "-E")) + mode = MODE_SEEPROM; + else if (!strcmp(argp[i], "-r")) + mode = MODE_NWAY_RST; + else if (!strcmp(argp[i], "-p")) + mode = MODE_PHYS_ID; + else if (!strcmp(argp[i], "-t")) + mode = MODE_TEST; + else if (!strcmp(argp[i], "-S")) + mode = MODE_GSTATS; + else if (!strcmp(argp[i], "-h")) + show_usage(0); + else + devname = argp[i]; + break; + case 2: + if ((mode == MODE_SSET) || + (mode == MODE_GDRV) || + (mode == MODE_GREGS)|| + (mode == MODE_NWAY_RST) || + (mode == MODE_TEST) || + (mode == MODE_GEEPROM) || + (mode == MODE_SEEPROM) || + (mode == MODE_GPAUSE) || + (mode == MODE_SPAUSE) || + (mode == MODE_GCOALESCE) || + (mode == MODE_SCOALESCE) || + (mode == MODE_GRING) || + (mode == MODE_SRING) || + (mode == MODE_GOFFLOAD) || + (mode == MODE_SOFFLOAD) || + (mode == MODE_GSTATS) || + (mode == MODE_PHYS_ID)) { + devname = argp[i]; + break; + } + /* fallthrough */ + case 3: + if (mode == MODE_TEST) { + if (!strcmp(argp[i], "online")) { + test_type = ONLINE; + } else if (!strcmp(argp[i], "offline")) { + test_type = OFFLINE; + } else { + show_usage(1); + } + break; + } else if (mode == MODE_PHYS_ID) { + phys_id_time = strtol(argp[i], NULL, 0); + if (phys_id_time < 0) + show_usage(1); + break; + } + /* fallthrough */ + default: + if (mode == MODE_GREGS) { + parse_generic_cmdline(argc, argp, i, + &gregs_changed, + cmdline_gregs, + ARRAY_SIZE(cmdline_gregs)); + i = argc; + break; + } + if (mode == MODE_GEEPROM) { + parse_generic_cmdline(argc, argp, i, + &geeprom_changed, + cmdline_geeprom, + ARRAY_SIZE(cmdline_geeprom)); + i = argc; + break; + } + if (mode == MODE_SEEPROM) { + parse_generic_cmdline(argc, argp, i, + &seeprom_changed, + cmdline_seeprom, + ARRAY_SIZE(cmdline_seeprom)); + i = argc; + break; + } + if (mode == MODE_SPAUSE) { + parse_generic_cmdline(argc, argp, i, + &gpause_changed, + cmdline_pause, + ARRAY_SIZE(cmdline_pause)); + i = argc; + break; + } + if (mode == MODE_SRING) { + parse_generic_cmdline(argc, argp, i, + &gring_changed, + cmdline_ring, + ARRAY_SIZE(cmdline_ring)); + i = argc; + break; + } + if (mode == MODE_SCOALESCE) { + parse_generic_cmdline(argc, argp, i, + &gcoalesce_changed, + cmdline_coalesce, + ARRAY_SIZE(cmdline_coalesce)); + i = argc; + break; + } + if (mode == MODE_SOFFLOAD) { + parse_generic_cmdline(argc, argp, i, + &goffload_changed, + cmdline_offload, + ARRAY_SIZE(cmdline_offload)); + i = argc; + break; + } + if (mode != MODE_SSET) + show_usage(1); + if (!strcmp(argp[i], "speed")) { + gset_changed = 1; + i += 1; + if (i >= argc) + show_usage(1); + if (!strcmp(argp[i], "10")) + speed_wanted = SPEED_10; + else if (!strcmp(argp[i], "100")) + speed_wanted = SPEED_100; + else if (!strcmp(argp[i], "1000")) + speed_wanted = SPEED_1000; + else + show_usage(1); + break; + } else if (!strcmp(argp[i], "duplex")) { + gset_changed = 1; + i += 1; + if (i >= argc) + show_usage(1); + if (!strcmp(argp[i], "half")) + duplex_wanted = DUPLEX_HALF; + else if (!strcmp(argp[i], "full")) + duplex_wanted = DUPLEX_FULL; + else + show_usage(1); + break; + } else if (!strcmp(argp[i], "port")) { + gset_changed = 1; + i += 1; + if (i >= argc) + show_usage(1); + if (!strcmp(argp[i], "tp")) + port_wanted = PORT_TP; + else if (!strcmp(argp[i], "aui")) + port_wanted = PORT_AUI; + else if (!strcmp(argp[i], "bnc")) + port_wanted = PORT_BNC; + else if (!strcmp(argp[i], "mii")) + port_wanted = PORT_MII; + else if (!strcmp(argp[i], "fibre")) + port_wanted = PORT_FIBRE; + else + show_usage(1); + break; + } else if (!strcmp(argp[i], "autoneg")) { + i += 1; + if (i >= argc) + show_usage(1); + if (!strcmp(argp[i], "on")) { + gset_changed = 1; + autoneg_wanted = AUTONEG_ENABLE; + } else if (!strcmp(argp[i], "off")) { + gset_changed = 1; + autoneg_wanted = AUTONEG_DISABLE; + } else { + show_usage(1); + } + break; + } else if (!strcmp(argp[i], "phyad")) { + gset_changed = 1; + i += 1; + if (i >= argc) + show_usage(1); + phyad_wanted = strtol(argp[i], NULL, 0); + if (phyad_wanted < 0) + show_usage(1); + break; + } else if (!strcmp(argp[i], "xcvr")) { + gset_changed = 1; + i += 1; + if (i >= argc) + show_usage(1); + if (!strcmp(argp[i], "internal")) + xcvr_wanted = XCVR_INTERNAL; + else if (!strcmp(argp[i], "external")) + xcvr_wanted = XCVR_EXTERNAL; + else + show_usage(1); + break; + } else if (!strcmp(argp[i], "wol")) { + gwol_changed = 1; + i++; + if (i >= argc) + show_usage(1); + if (parse_wolopts(argp[i], &wol_wanted) < 0) + show_usage(1); + wol_change = 1; + break; + } else if (!strcmp(argp[i], "sopass")) { + gwol_changed = 1; + i++; + if (i >= argc) + show_usage(1); + if (parse_sopass(argp[i], sopass_wanted) < 0) + show_usage(1); + sopass_change = 1; + break; + } else if (!strcmp(argp[i], "msglvl")) { + i++; + if (i >= argc) + show_usage(1); + msglvl_wanted = strtol(argp[i], NULL, 0); + if (msglvl_wanted < 0) + show_usage(1); + break; + } + show_usage(1); + } + } + + if (autoneg_wanted == AUTONEG_ENABLE){ + if (speed_wanted == SPEED_10 && duplex_wanted == DUPLEX_HALF) + advertising_wanted = ADVERTISED_10baseT_Half; + else if (speed_wanted == SPEED_10 && + duplex_wanted == DUPLEX_FULL) + advertising_wanted = ADVERTISED_10baseT_Full; + else if (speed_wanted == SPEED_100 && + duplex_wanted == DUPLEX_HALF) + advertising_wanted = ADVERTISED_100baseT_Half; + else if (speed_wanted == SPEED_100 && + duplex_wanted == DUPLEX_FULL) + advertising_wanted = ADVERTISED_100baseT_Full; + else if (speed_wanted == SPEED_1000 && + duplex_wanted == DUPLEX_HALF) + advertising_wanted = ADVERTISED_1000baseT_Half; + else if (speed_wanted == SPEED_1000 && + duplex_wanted == DUPLEX_FULL) + advertising_wanted = ADVERTISED_1000baseT_Full; + else + /* auto negotiate without forcing */ + advertising_wanted = ADVERTISED_100baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_10baseT_Half | + ADVERTISED_1000baseT_Full | + ADVERTISED_1000baseT_Half; + + } + + if (devname == NULL) { + show_usage(1); + } +} + +static void dump_supported(struct ethtool_cmd *ep) +{ + u_int32_t mask = ep->supported; + int did1; + + fprintf(stdout, " Supported ports: [ "); + if (mask & SUPPORTED_TP) + fprintf(stdout, "TP "); + if (mask & SUPPORTED_AUI) + fprintf(stdout, "AUI "); + if (mask & SUPPORTED_BNC) + fprintf(stdout, "BNC "); + if (mask & SUPPORTED_MII) + fprintf(stdout, "MII "); + if (mask & SUPPORTED_FIBRE) + fprintf(stdout, "FIBRE "); + fprintf(stdout, "]\n"); + + fprintf(stdout, " Supported link modes: "); + did1 = 0; + if (mask & SUPPORTED_10baseT_Half) { + did1++; fprintf(stdout, "10baseT/Half "); + } + if (mask & SUPPORTED_10baseT_Full) { + did1++; fprintf(stdout, "10baseT/Full "); + } + if (did1 && (mask & (SUPPORTED_100baseT_Half|SUPPORTED_100baseT_Full))) { + fprintf(stdout, "\n"); + fprintf(stdout, " "); + } + if (mask & SUPPORTED_100baseT_Half) { + did1++; fprintf(stdout, "100baseT/Half "); + } + if (mask & SUPPORTED_100baseT_Full) { + did1++; fprintf(stdout, "100baseT/Full "); + } + if (did1 && (mask & (SUPPORTED_1000baseT_Half|SUPPORTED_1000baseT_Full))) { + fprintf(stdout, "\n"); + fprintf(stdout, " "); + } + if (mask & SUPPORTED_1000baseT_Half) { + did1++; fprintf(stdout, "1000baseT/Half "); + } + if (mask & SUPPORTED_1000baseT_Full) { + did1++; fprintf(stdout, "1000baseT/Full "); + } + fprintf(stdout, "\n"); + + fprintf(stdout, " Supports auto-negotiation: "); + if (mask & SUPPORTED_Autoneg) + fprintf(stdout, "Yes\n"); + else + fprintf(stdout, "No\n"); +} + +static void dump_advertised(struct ethtool_cmd *ep) +{ + u_int32_t mask = ep->advertising; + int did1; + + fprintf(stdout, " Advertised link modes: "); + did1 = 0; + if (mask & ADVERTISED_10baseT_Half) { + did1++; fprintf(stdout, "10baseT/Half "); + } + if (mask & ADVERTISED_10baseT_Full) { + did1++; fprintf(stdout, "10baseT/Full "); + } + if (did1 && (mask & (ADVERTISED_100baseT_Half|ADVERTISED_100baseT_Full))) { + fprintf(stdout, "\n"); + fprintf(stdout, " "); + } + if (mask & ADVERTISED_100baseT_Half) { + did1++; fprintf(stdout, "100baseT/Half "); + } + if (mask & ADVERTISED_100baseT_Full) { + did1++; fprintf(stdout, "100baseT/Full "); + } + if (did1 && (mask & (ADVERTISED_1000baseT_Half|ADVERTISED_1000baseT_Full))) { + fprintf(stdout, "\n"); + fprintf(stdout, " "); + } + if (mask & ADVERTISED_1000baseT_Half) { + did1++; fprintf(stdout, "1000baseT/Half "); + } + if (mask & ADVERTISED_1000baseT_Full) { + did1++; fprintf(stdout, "1000baseT/Full "); + } + if (did1 == 0) + fprintf(stdout, "Not reported"); + fprintf(stdout, "\n"); + + fprintf(stdout, " Advertised auto-negotiation: "); + if (mask & ADVERTISED_Autoneg) + fprintf(stdout, "Yes\n"); + else + fprintf(stdout, "No\n"); +} + +static int dump_ecmd(struct ethtool_cmd *ep) +{ + dump_supported(ep); + dump_advertised(ep); + + fprintf(stdout, " Speed: "); + switch (ep->speed) { + case SPEED_10: + fprintf(stdout, "10Mb/s\n"); + break; + case SPEED_100: + fprintf(stdout, "100Mb/s\n"); + break; + case SPEED_1000: + fprintf(stdout, "1000Mb/s\n"); + break; + default: + fprintf(stdout, "Unknown! (%i)\n", ep->speed); + break; + }; + + fprintf(stdout, " Duplex: "); + switch (ep->duplex) { + case DUPLEX_HALF: + fprintf(stdout, "Half\n"); + break; + case DUPLEX_FULL: + fprintf(stdout, "Full\n"); + break; + default: + fprintf(stdout, "Unknown! (%i)\n", ep->duplex); + break; + }; + + fprintf(stdout, " Port: "); + switch (ep->port) { + case PORT_TP: + fprintf(stdout, "Twisted Pair\n"); + break; + case PORT_AUI: + fprintf(stdout, "AUI\n"); + break; + case PORT_BNC: + fprintf(stdout, "BNC\n"); + break; + case PORT_MII: + fprintf(stdout, "MII\n"); + break; + case PORT_FIBRE: + fprintf(stdout, "FIBRE\n"); + break; + default: + fprintf(stdout, "Unknown! (%i)\n", ep->port); + break; + }; + + fprintf(stdout, " PHYAD: %d\n", ep->phy_address); + fprintf(stdout, " Transceiver: "); + switch (ep->transceiver) { + case XCVR_INTERNAL: + fprintf(stdout, "internal\n"); + break; + case XCVR_EXTERNAL: + fprintf(stdout, "external\n"); + break; + default: + fprintf(stdout, "Unknown!\n"); + break; + }; + + fprintf(stdout, " Auto-negotiation: %s\n", + (ep->autoneg == AUTONEG_DISABLE) ? + "off" : "on"); + return 0; +} + +static int dump_drvinfo(struct ethtool_drvinfo *info) +{ + fprintf(stdout, + "driver: %s\n" + "version: %s\n" + "firmware-version: %s\n" + "bus-info: %s\n", + info->driver, + info->version, + info->fw_version, + info->bus_info); + + return 0; +} + +static int dump_wol(struct ethtool_wolinfo *wol) +{ + fprintf(stdout, " Supports Wake-on: %s\n", + unparse_wolopts(wol->supported)); + fprintf(stdout, " Wake-on: %s\n", + unparse_wolopts(wol->wolopts)); + if (wol->supported & WAKE_MAGICSECURE) { + int i; + int delim = 0; + fprintf(stdout, " SecureOn password: "); + for (i = 0; i < SOPASS_MAX; i++) { + fprintf(stdout, "%s%02x", delim?":":"", wol->sopass[i]); + delim=1; + } + fprintf(stdout, "\n"); + } + + return 0; +} + +static int parse_wolopts(char *optstr, int *data) +{ + *data = 0; + while (*optstr) { + switch (*optstr) { + case 'p': + *data |= WAKE_PHY; + break; + case 'u': + *data |= WAKE_UCAST; + break; + case 'm': + *data |= WAKE_MCAST; + break; + case 'b': + *data |= WAKE_BCAST; + break; + case 'a': + *data |= WAKE_ARP; + break; + case 'g': + *data |= WAKE_MAGIC; + break; + case 's': + *data |= WAKE_MAGICSECURE; + break; + case 'd': + *data = 0; + break; + default: + return -1; + } + optstr++; + } + return 0; +} + +static char *unparse_wolopts(int wolopts) +{ + static char buf[16]; + char *p = buf; + + memset(buf, 0, sizeof(buf)); + + if (wolopts) { + if (wolopts & WAKE_PHY) + *p++ = 'p'; + if (wolopts & WAKE_UCAST) + *p++ = 'u'; + if (wolopts & WAKE_MCAST) + *p++ = 'm'; + if (wolopts & WAKE_BCAST) + *p++ = 'b'; + if (wolopts & WAKE_ARP) + *p++ = 'a'; + if (wolopts & WAKE_MAGIC) + *p++ = 'g'; + if (wolopts & WAKE_MAGICSECURE) + *p++ = 's'; + } else { + *p = 'd'; + } + + return buf; +} + +static int parse_sopass(char *src, unsigned char *dest) +{ + int count; + int i; + int buf[SOPASS_MAX]; + + count = sscanf(src, "%2x:%2x:%2x:%2x:%2x:%2x", + &buf[0], &buf[1], &buf[2], &buf[3], &buf[4], &buf[5]); + if (count != SOPASS_MAX) { + return -1; + } + + for (i = 0; i < count; i++) { + dest[i] = buf[i]; + } + return 0; +} + +static struct { + const char *name; + int (*func)(struct ethtool_drvinfo *info, struct ethtool_regs *regs); + +} driver_list[] = { + { "8139cp", realtek_dump_regs }, + { "8139too", realtek_dump_regs }, + { "r8169", realtek_dump_regs }, + { "de2104x", de2104x_dump_regs }, + { "e1000", e1000_dump_regs }, + { "natsemi", natsemi_dump_regs }, + { "e100", e100_dump_regs }, + { "amd8111e", amd8111e_dump_regs }, + { "pcnet32", pcnet32_dump_regs }, + { "fec_8xx", fec_8xx_dump_regs }, +}; + +static int dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs) +{ + int i; + + if (gregs_dump_raw) { + fwrite(regs->data, regs->len, 1, stdout); + return 0; + } + + for (i = 0; i < ARRAY_SIZE(driver_list); i++) + if (!strncmp(driver_list[i].name, info->driver, + ETHTOOL_BUSINFO_LEN)) + return driver_list[i].func(info, regs); + + fprintf(stdout, "Offset\tValue\n"); + fprintf(stdout, "--------\t-----\n"); + for (i = 0; i < regs->len; i++) + fprintf(stdout, "%02d\t0x%02x\n", i, regs->data[i]); + fprintf(stdout, "\n"); + return 0; +} + +static int dump_eeprom(struct ethtool_drvinfo *info, struct ethtool_eeprom *ee) +{ + int i; + + if (geeprom_dump_raw) { + fwrite(ee->data, 1, ee->len, stdout); + return 0; + } + + if (!strncmp("natsemi", info->driver, ETHTOOL_BUSINFO_LEN)) { + return natsemi_dump_eeprom(info, ee); + } else if (!strncmp("tg3", info->driver, ETHTOOL_BUSINFO_LEN)) { + return tg3_dump_eeprom(info, ee); + } + + fprintf(stdout, "Offset\t\tValues\n"); + fprintf(stdout, "------\t\t------"); + for (i = 0; i < ee->len; i++) { + if(!(i%16)) fprintf(stdout, "\n0x%04x\t\t", i + ee->offset); + fprintf(stdout, "%02x ", ee->data[i]); + } + fprintf(stdout, "\n"); + return 0; +} + +static int dump_test(struct ethtool_drvinfo *info, struct ethtool_test *test, + struct ethtool_gstrings *strings) +{ + int i, rc; + + rc = test->flags & ETH_TEST_FL_FAILED; + fprintf(stdout, "The test result is %s\n", rc ? "FAIL" : "PASS"); + + if (info->testinfo_len) + fprintf(stdout, "The test extra info:\n"); + + for (i = 0; i < info->testinfo_len; i++) { + fprintf(stdout, "%s\t %d\n", + (char *)(strings->data + i * ETH_GSTRING_LEN), + (u32) test->data[i]); + } + + fprintf(stdout, "\n"); + return rc; +} + +static int dump_pause(void) +{ + fprintf(stdout, + "Autonegotiate: %s\n" + "RX: %s\n" + "TX: %s\n", + epause.autoneg ? "on" : "off", + epause.rx_pause ? "on" : "off", + epause.tx_pause ? "on" : "off"); + + fprintf(stdout, "\n"); + return 0; +} + +static int dump_ring(void) +{ + fprintf(stdout, + "Pre-set maximums:\n" + "RX: %u\n" + "RX Mini: %u\n" + "RX Jumbo: %u\n" + "TX: %u\n", + ering.rx_max_pending, + ering.rx_mini_max_pending, + ering.rx_jumbo_max_pending, + ering.tx_max_pending); + + fprintf(stdout, + "Current hardware settings:\n" + "RX: %u\n" + "RX Mini: %u\n" + "RX Jumbo: %u\n" + "TX: %u\n", + ering.rx_pending, + ering.rx_mini_pending, + ering.rx_jumbo_pending, + ering.tx_pending); + + fprintf(stdout, "\n"); + return 0; +} + +static int dump_coalesce(void) +{ + fprintf(stdout, "Adaptive RX: %s TX: %s\n", + ecoal.use_adaptive_rx_coalesce ? "on" : "off", + ecoal.use_adaptive_tx_coalesce ? "on" : "off"); + + fprintf(stdout, + "stats-block-usecs: %u\n" + "sample-interval: %u\n" + "pkt-rate-low: %u\n" + "pkt-rate-high: %u\n" + "\n" + "rx-usecs: %u\n" + "rx-frames: %u\n" + "rx-usecs-irq: %u\n" + "rx-frames-irq: %u\n" + "\n" + "tx-usecs: %u\n" + "tx-frames: %u\n" + "tx-usecs-irq: %u\n" + "tx-frames-irq: %u\n" + "\n" + "rx-usecs-low: %u\n" + "rx-frame-low: %u\n" + "tx-usecs-low: %u\n" + "tx-frame-low: %u\n" + "\n" + "rx-usecs-high: %u\n" + "rx-frame-high: %u\n" + "tx-usecs-high: %u\n" + "tx-frame-high: %u\n" + "\n", + ecoal.stats_block_coalesce_usecs, + ecoal.rate_sample_interval, + ecoal.pkt_rate_low, + ecoal.pkt_rate_high, + + ecoal.rx_coalesce_usecs, + ecoal.rx_max_coalesced_frames, + ecoal.rx_coalesce_usecs_irq, + ecoal.rx_max_coalesced_frames_irq, + + ecoal.tx_coalesce_usecs, + ecoal.tx_max_coalesced_frames, + ecoal.tx_coalesce_usecs_irq, + ecoal.tx_max_coalesced_frames_irq, + + ecoal.rx_coalesce_usecs_low, + ecoal.rx_max_coalesced_frames_low, + ecoal.tx_coalesce_usecs_low, + ecoal.tx_max_coalesced_frames_low, + + ecoal.rx_coalesce_usecs_high, + ecoal.rx_max_coalesced_frames_high, + ecoal.tx_coalesce_usecs_high, + ecoal.tx_max_coalesced_frames_high); + + return 0; +} + +static int dump_offload (int rx, int tx, int sg, int tso) +{ + fprintf(stdout, + "rx-checksumming: %s\n" + "tx-checksumming: %s\n" + "scatter-gather: %s\n" + "tcp segmentation offload: %s\n", + rx ? "on" : "off", + tx ? "on" : "off", + sg ? "on" : "off", + tso ? "on" : "off"); + + return 0; +} + +static int doit(void) +{ + struct ifreq ifr; + int fd; + + /* Setup our control structures. */ + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, devname); + + /* Open control socket. */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("Cannot get control socket"); + return 70; + } + + /* all of these are expected to populate ifr->ifr_data as needed */ + if (mode == MODE_GDRV) { + return do_gdrv(fd, &ifr); + } else if (mode == MODE_GSET) { + return do_gset(fd, &ifr); + } else if (mode == MODE_SSET) { + return do_sset(fd, &ifr); + } else if (mode == MODE_GREGS) { + return do_gregs(fd, &ifr); + } else if (mode == MODE_NWAY_RST) { + return do_nway_rst(fd, &ifr); + } else if (mode == MODE_GEEPROM) { + return do_geeprom(fd, &ifr); + } else if (mode == MODE_SEEPROM) { + return do_seeprom(fd, &ifr); + } else if (mode == MODE_TEST) { + return do_test(fd, &ifr); + } else if (mode == MODE_PHYS_ID) { + return do_phys_id(fd, &ifr); + } else if (mode == MODE_GPAUSE) { + return do_gpause(fd, &ifr); + } else if (mode == MODE_SPAUSE) { + return do_spause(fd, &ifr); + } else if (mode == MODE_GCOALESCE) { + return do_gcoalesce(fd, &ifr); + } else if (mode == MODE_SCOALESCE) { + return do_scoalesce(fd, &ifr); + } else if (mode == MODE_GRING) { + return do_gring(fd, &ifr); + } else if (mode == MODE_SRING) { + return do_sring(fd, &ifr); + } else if (mode == MODE_GOFFLOAD) { + return do_goffload(fd, &ifr); + } else if (mode == MODE_SOFFLOAD) { + return do_soffload(fd, &ifr); + } else if (mode == MODE_GSTATS) { + return do_gstats(fd, &ifr); + } + + return 69; +} + +static int do_gdrv(int fd, struct ifreq *ifr) +{ + int err; + struct ethtool_drvinfo drvinfo; + + drvinfo.cmd = ETHTOOL_GDRVINFO; + ifr->ifr_data = (caddr_t)&drvinfo; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + perror("Cannot get driver information"); + return 71; + } + return dump_drvinfo(&drvinfo); +} + +static int do_gpause(int fd, struct ifreq *ifr) +{ + int err; + + fprintf(stdout, "Pause parameters for %s:\n", devname); + + epause.cmd = ETHTOOL_GPAUSEPARAM; + ifr->ifr_data = (caddr_t)&epause; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err == 0) { + err = dump_pause(); + if (err) + return err; + } else { + perror("Cannot get device pause settings"); + return 76; + } + + return 0; +} + +static void do_generic_set1(struct cmdline_info *info, int *changed_out) +{ + int wanted, *v1, *v2; + + v1 = info->wanted_val; + wanted = *v1; + + if (wanted < 0) + return; + + v2 = info->ioctl_val; + if (wanted == *v2) { + fprintf(stderr, "%s unmodified, ignoring\n", info->name); + } else { + *v2 = wanted; + *changed_out = 1; + } +} + +static void do_generic_set(struct cmdline_info *info, + unsigned int n_info, + int *changed_out) +{ + unsigned int i; + + for (i = 0; i < n_info; i++) + do_generic_set1(&info[i], changed_out); +} + +static int do_spause(int fd, struct ifreq *ifr) +{ + int err, changed = 0; + + epause.cmd = ETHTOOL_GPAUSEPARAM; + ifr->ifr_data = (caddr_t)&epause; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err) { + perror("Cannot get device pause settings"); + return 77; + } + + do_generic_set(cmdline_pause, ARRAY_SIZE(cmdline_pause), &changed); + + if (!changed) { + fprintf(stderr, "no pause parameters changed, aborting\n"); + return 78; + } + + epause.cmd = ETHTOOL_SPAUSEPARAM; + ifr->ifr_data = (caddr_t)&epause; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err) { + perror("Cannot set device pause parameters"); + return 79; + } + + return 0; +} + +static int do_sring(int fd, struct ifreq *ifr) +{ + int err, changed = 0; + + ering.cmd = ETHTOOL_GRINGPARAM; + ifr->ifr_data = (caddr_t)&ering; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err) { + perror("Cannot get device ring settings"); + return 76; + } + + do_generic_set(cmdline_ring, ARRAY_SIZE(cmdline_ring), &changed); + + if (!changed) { + fprintf(stderr, "no ring parameters changed, aborting\n"); + return 80; + } + + ering.cmd = ETHTOOL_SRINGPARAM; + ifr->ifr_data = (caddr_t)&ering; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err) { + perror("Cannot set device ring parameters"); + return 81; + } + + return 0; +} + +static int do_gring(int fd, struct ifreq *ifr) +{ + int err; + + fprintf(stdout, "Ring parameters for %s:\n", devname); + + ering.cmd = ETHTOOL_GRINGPARAM; + ifr->ifr_data = (caddr_t)&ering; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err == 0) { + err = dump_ring(); + if (err) + return err; + } else { + perror("Cannot get device ring settings"); + return 76; + } + + return 0; +} + +static int do_gcoalesce(int fd, struct ifreq *ifr) +{ + int err; + + fprintf(stdout, "Coalesce parameters for %s:\n", devname); + + ecoal.cmd = ETHTOOL_GCOALESCE; + ifr->ifr_data = (caddr_t)&ecoal; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err == 0) { + err = dump_coalesce(); + if (err) + return err; + } else { + perror("Cannot get device coalesce settings"); + return 82; + } + + return 0; +} + +static int do_scoalesce(int fd, struct ifreq *ifr) +{ + int err, changed = 0; + + ecoal.cmd = ETHTOOL_GCOALESCE; + ifr->ifr_data = (caddr_t)&ecoal; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err) { + perror("Cannot get device coalesce settings"); + return 76; + } + + do_generic_set(cmdline_coalesce, ARRAY_SIZE(cmdline_coalesce), + &changed); + + if (!changed) { + fprintf(stderr, "no ring parameters changed, aborting\n"); + return 80; + } + + ecoal.cmd = ETHTOOL_SCOALESCE; + ifr->ifr_data = (caddr_t)&ecoal; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err) { + perror("Cannot set device ring parameters"); + return 81; + } + + return 0; +} + +static int do_goffload(int fd, struct ifreq *ifr) +{ + struct ethtool_value eval; + int err, allfail = 1, rx = 0, tx = 0, sg = 0, tso = 0; + + fprintf(stdout, "Offload parameters for %s:\n", devname); + + eval.cmd = ETHTOOL_GRXCSUM; + ifr->ifr_data = (caddr_t)&eval; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err) + perror("Cannot get device rx csum settings"); + else { + rx = eval.data; + allfail = 0; + } + + eval.cmd = ETHTOOL_GTXCSUM; + ifr->ifr_data = (caddr_t)&eval; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err) + perror("Cannot get device tx csum settings"); + else { + tx = eval.data; + allfail = 0; + } + + eval.cmd = ETHTOOL_GSG; + ifr->ifr_data = (caddr_t)&eval; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err) + perror("Cannot get device scatter-gather settings"); + else { + sg = eval.data; + allfail = 0; + } + + eval.cmd = ETHTOOL_GTSO; + ifr->ifr_data = (caddr_t)&eval; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err) + perror("Cannot get device tcp segmentation offload settings"); + else { + tso = eval.data; + allfail = 0; + } + + if (allfail) { + fprintf(stdout, "no offload info available\n"); + return 83; + } + + return dump_offload(rx, tx, sg, tso); +} + +static int do_soffload(int fd, struct ifreq *ifr) +{ + struct ethtool_value eval; + int err, changed = 0; + + if (off_csum_rx_wanted >= 0) { + changed = 1; + eval.cmd = ETHTOOL_SRXCSUM; + eval.data = (off_csum_rx_wanted == 1); + ifr->ifr_data = (caddr_t)&eval; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err) { + perror("Cannot set device rx csum settings"); + return 84; + } + } + + if (off_csum_tx_wanted >= 0) { + changed = 1; + eval.cmd = ETHTOOL_STXCSUM; + eval.data = (off_csum_tx_wanted == 1); + ifr->ifr_data = (caddr_t)&eval; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err) { + perror("Cannot set device tx csum settings"); + return 85; + } + } + + if (off_sg_wanted >= 0) { + changed = 1; + eval.cmd = ETHTOOL_SSG; + eval.data = (off_sg_wanted == 1); + ifr->ifr_data = (caddr_t)&eval; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err) { + perror("Cannot set device scatter-gather settings"); + return 86; + } + } + + if (off_tso_wanted >= 0) { + changed = 1; + eval.cmd = ETHTOOL_STSO; + eval.data = (off_tso_wanted == 1); + ifr->ifr_data = (caddr_t)&eval; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err) { + perror("Cannot set device tcp segmentation offload settings"); + return 88; + } + } + if (!changed) { + fprintf(stdout, "no offload settings changed\n"); + } + + return 0; +} + +static int do_gset(int fd, struct ifreq *ifr) +{ + int err; + struct ethtool_cmd ecmd; + struct ethtool_wolinfo wolinfo; + struct ethtool_value edata; + int allfail = 1; + + fprintf(stdout, "Settings for %s:\n", devname); + + ecmd.cmd = ETHTOOL_GSET; + ifr->ifr_data = (caddr_t)&ecmd; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err == 0) { + err = dump_ecmd(&ecmd); + if (err) + return err; + allfail = 0; + } else if (errno != EOPNOTSUPP) { + perror("Cannot get device settings"); + } + + wolinfo.cmd = ETHTOOL_GWOL; + ifr->ifr_data = (caddr_t)&wolinfo; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err == 0) { + err = dump_wol(&wolinfo); + if (err) + return err; + allfail = 0; + } else if (errno != EOPNOTSUPP) { + perror("Cannot get wake-on-lan settings"); + } + + edata.cmd = ETHTOOL_GMSGLVL; + ifr->ifr_data = (caddr_t)&edata; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err == 0) { + fprintf(stdout, " Current message level: 0x%08x (%d)\n", + edata.data, edata.data); + allfail = 0; + } else if (errno != EOPNOTSUPP) { + perror("Cannot get message level"); + } + + edata.cmd = ETHTOOL_GLINK; + ifr->ifr_data = (caddr_t)&edata; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err == 0) { + fprintf(stdout, " Link detected: %s\n", + edata.data ? "yes":"no"); + allfail = 0; + } else if (errno != EOPNOTSUPP) { + perror("Cannot get link status"); + } + + if (allfail) { + fprintf(stdout, "No data available\n"); + return 75; + } + return 0; +} + +static int do_sset(int fd, struct ifreq *ifr) +{ + int err; + + if (gset_changed) { + struct ethtool_cmd ecmd; + + ecmd.cmd = ETHTOOL_GSET; + ifr->ifr_data = (caddr_t)&ecmd; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + perror("Cannot get current device settings"); + } else { + /* Change everything the user specified. */ + if (speed_wanted != -1) + ecmd.speed = speed_wanted; + if (duplex_wanted != -1) + ecmd.duplex = duplex_wanted; + if (port_wanted != -1) + ecmd.port = port_wanted; + if (autoneg_wanted != -1) + ecmd.autoneg = autoneg_wanted; + if (phyad_wanted != -1) + ecmd.phy_address = phyad_wanted; + if (xcvr_wanted != -1) + ecmd.transceiver = xcvr_wanted; + if (advertising_wanted != -1) + ecmd.advertising = advertising_wanted; + + /* Try to perform the update. */ + ecmd.cmd = ETHTOOL_SSET; + ifr->ifr_data = (caddr_t)&ecmd; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) + perror("Cannot set new settings"); + } + if (err < 0) { + if (speed_wanted != -1) + fprintf(stderr, " not setting speed\n"); + if (duplex_wanted != -1) + fprintf(stderr, " not setting duplex\n"); + if (port_wanted != -1) + fprintf(stderr, " not setting port\n"); + if (autoneg_wanted != -1) + fprintf(stderr, " not setting autoneg\n"); + if (phyad_wanted != -1) + fprintf(stderr, " not setting phy_address\n"); + if (xcvr_wanted != -1) + fprintf(stderr, " not setting transceiver\n"); + } + } + + if (gwol_changed) { + struct ethtool_wolinfo wol; + + wol.cmd = ETHTOOL_GWOL; + ifr->ifr_data = (caddr_t)&wol; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + perror("Cannot get current wake-on-lan settings"); + } else { + /* Change everything the user specified. */ + if (wol_change) { + wol.wolopts = wol_wanted; + } + if (sopass_change) { + int i; + for (i = 0; i < SOPASS_MAX; i++) { + wol.sopass[i] = sopass_wanted[i]; + } + } + + /* Try to perform the update. */ + wol.cmd = ETHTOOL_SWOL; + ifr->ifr_data = (caddr_t)&wol; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) + perror("Cannot set new wake-on-lan settings"); + } + if (err < 0) { + if (wol_change) + fprintf(stderr, " not setting wol\n"); + if (sopass_change) + fprintf(stderr, " not setting sopass\n"); + } + } + + if (msglvl_wanted != -1) { + struct ethtool_value edata; + + edata.cmd = ETHTOOL_SMSGLVL; + edata.data = msglvl_wanted; + ifr->ifr_data = (caddr_t)&edata;; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) + perror("Cannot set new msglvl"); + } + + return 0; +} + +static int do_gregs(int fd, struct ifreq *ifr) +{ + int err; + struct ethtool_drvinfo drvinfo; + struct ethtool_regs *regs; + + drvinfo.cmd = ETHTOOL_GDRVINFO; + ifr->ifr_data = (caddr_t)&drvinfo; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + perror("Cannot get driver information"); + return 72; + } + + regs = calloc(1, sizeof(*regs)+drvinfo.regdump_len); + if (!regs) { + perror("Cannot allocate memory for register dump"); + return 73; + } + regs->cmd = ETHTOOL_GREGS; + regs->len = drvinfo.regdump_len; + ifr->ifr_data = (caddr_t)regs; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + perror("Cannot get register dump"); + free(regs); + return 74; + } + if(dump_regs(&drvinfo, regs) < 0) { + perror("Cannot dump registers"); + free(regs); + return 75; + } + free(regs); + + return 0; +} + +static int do_nway_rst(int fd, struct ifreq *ifr) +{ + struct ethtool_value edata; + int err; + + edata.cmd = ETHTOOL_NWAY_RST; + ifr->ifr_data = (caddr_t)&edata; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) + perror("Cannot restart autonegotiation"); + + return err; +} + +static int do_geeprom(int fd, struct ifreq *ifr) +{ + int err; + struct ethtool_drvinfo drvinfo; + struct ethtool_eeprom *eeprom; + + drvinfo.cmd = ETHTOOL_GDRVINFO; + ifr->ifr_data = (caddr_t)&drvinfo; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + perror("Cannot get driver information"); + return 74; + } + + if (geeprom_length <= 0) + geeprom_length = drvinfo.eedump_len; + + if (drvinfo.eedump_len < geeprom_offset + geeprom_length) + geeprom_length = drvinfo.eedump_len - geeprom_offset; + + eeprom = calloc(1, sizeof(*eeprom)+geeprom_length); + if (!eeprom) { + perror("Cannot allocate memory for EEPROM data"); + return 75; + } + eeprom->cmd = ETHTOOL_GEEPROM; + eeprom->len = geeprom_length; + eeprom->offset = geeprom_offset; + ifr->ifr_data = (caddr_t)eeprom; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + perror("Cannot get EEPROM data"); + free(eeprom); + return 74; + } + err = dump_eeprom(&drvinfo, eeprom); + free(eeprom); + + return err; +} + +static int do_seeprom(int fd, struct ifreq *ifr) +{ + int err; + struct { + struct ethtool_eeprom eeprom; + u8 data; + } edata; + + edata.eeprom.cmd = ETHTOOL_SEEPROM; + edata.eeprom.len = 1; + edata.eeprom.offset = seeprom_offset; + edata.eeprom.magic = seeprom_magic; + edata.data = seeprom_value; + ifr->ifr_data = (caddr_t)&edata.eeprom; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + perror("Cannot set EEPROM data"); + return 87; + } + + return err; +} + +static int do_test(int fd, struct ifreq *ifr) +{ + int err; + struct ethtool_drvinfo drvinfo; + struct ethtool_test *test; + struct ethtool_gstrings *strings; + + drvinfo.cmd = ETHTOOL_GDRVINFO; + ifr->ifr_data = (caddr_t)&drvinfo; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + perror("Cannot get driver information"); + return 72; + } + + test = calloc(1, sizeof(*test) + drvinfo.testinfo_len * sizeof(u64)); + if (!test) { + perror("Cannot allocate memory for test info"); + return 73; + } + memset (test->data, 0, drvinfo.testinfo_len * sizeof(u64)); + test->cmd = ETHTOOL_TEST; + test->len = drvinfo.testinfo_len; + if (test_type == OFFLINE) + test->flags = ETH_TEST_FL_OFFLINE; + else + test->flags = 0; + ifr->ifr_data = (caddr_t)test; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + perror("Cannot test"); + free (test); + return 74; + } + + strings = calloc(1, sizeof(*strings) + + drvinfo.testinfo_len * ETH_GSTRING_LEN); + if (!strings) { + perror("Cannot allocate memory for strings"); + free(test); + return 73; + } + memset (strings->data, 0, drvinfo.testinfo_len * ETH_GSTRING_LEN); + strings->cmd = ETHTOOL_GSTRINGS; + strings->string_set = ETH_SS_TEST; + strings->len = drvinfo.testinfo_len; + ifr->ifr_data = (caddr_t)strings; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + perror("Cannot get strings"); + free (test); + free (strings); + return 74; + } + err = dump_test(&drvinfo, test, strings); + free(test); + free(strings); + + return err; +} + +static int do_phys_id(int fd, struct ifreq *ifr) +{ + int err; + struct ethtool_value edata; + + edata.cmd = ETHTOOL_PHYS_ID; + edata.data = phys_id_time; + ifr->ifr_data = (caddr_t)&edata; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) + perror("Cannot identify NIC"); + + return err; +} + +static int do_gstats(int fd, struct ifreq *ifr) +{ + struct ethtool_drvinfo drvinfo; + struct ethtool_gstrings *strings; + struct ethtool_stats *stats; + unsigned int n_stats, sz_str, sz_stats, i; + int err; + + drvinfo.cmd = ETHTOOL_GDRVINFO; + ifr->ifr_data = (caddr_t)&drvinfo; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + perror("Cannot get driver information"); + return 71; + } + + n_stats = drvinfo.n_stats; + if (n_stats < 1) { + fprintf(stderr, "no stats available\n"); + return 94; + } + + sz_str = n_stats * ETH_GSTRING_LEN; + sz_stats = n_stats * sizeof(u64); + + strings = calloc(1, sz_str + sizeof(struct ethtool_gstrings)); + stats = calloc(1, sz_stats + sizeof(struct ethtool_stats)); + if (!strings || !stats) { + fprintf(stderr, "no memory available\n"); + return 95; + } + + strings->cmd = ETHTOOL_GSTRINGS; + strings->string_set = ETH_SS_STATS; + strings->len = n_stats; + ifr->ifr_data = (caddr_t) strings; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + perror("Cannot get stats strings information"); + free(strings); + free(stats); + return 96; + } + + stats->cmd = ETHTOOL_GSTATS; + stats->n_stats = n_stats; + ifr->ifr_data = (caddr_t) stats; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + perror("Cannot get stats information"); + free(strings); + free(stats); + return 97; + } + + /* todo - pretty-print the strings per-driver */ + fprintf(stdout, "NIC statistics:\n"); + for (i = 0; i < n_stats; i++) { + char s[ETH_GSTRING_LEN]; + + strncpy(s, &strings->data[i * ETH_GSTRING_LEN], + ETH_GSTRING_LEN); + fprintf(stdout, " %s: %llu\n", + s, stats->data[i]); + } + free(strings); + free(stats); + + return 0; +} + +int main(int argc, char **argp, char **envp) +{ + parse_cmdline(argc, argp); + return doit(); +} |