diff -ruN ethtool-1.2/AUTHORS ethtool-1.3/AUTHORS --- ethtool-1.2/AUTHORS Fri Mar 23 10:47:36 2001 +++ ethtool-1.3/AUTHORS Thu May 13 15:39:49 1976 @@ -1,3 +1,4 @@ David Miller Jakub Jelinek Jeff Garzik +Tim Hockin diff -ruN ethtool-1.2/NEWS ethtool-1.3/NEWS --- ethtool-1.2/NEWS Thu May 17 18:52:39 2001 +++ ethtool-1.3/NEWS Thu May 13 15:39:49 1976 @@ -4,3 +4,6 @@ information from the ethernet driver associated with the specified interface. +Version 1.3 - Aug 02, 2001 + + * Support Wake-on-LAN (ETHTOOL GWOL and ETHTOOL SWOL ioctl). diff -ruN ethtool-1.2/configure ethtool-1.3/configure --- ethtool-1.2/configure Thu May 17 18:58:44 2001 +++ ethtool-1.3/configure Thu May 13 22:53:17 1976 @@ -691,7 +691,7 @@ PACKAGE=ethtool -VERSION=1.2 +VERSION=1.3 if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then { echo "configure: error: source directory already configured; run "make distclean" there first" 1>&2; exit 1; } @@ -987,7 +987,7 @@ CFLAGS="$ac_save_CFLAGS" elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then - CFLAGS="-g -O2" + CFLAGS="-g -O2 -Wall" else CFLAGS="-g" fi diff -ruN ethtool-1.2/ethtool.8 ethtool-1.3/ethtool.8 --- ethtool-1.2/ethtool.8 Thu May 17 18:52:40 2001 +++ ethtool-1.3/ethtool.8 Fri May 14 14:58:46 1976 @@ -2,9 +2,9 @@ .\" Copyright 1999 by David S. Miller. All Rights Reserved. .\" This file may be copied under the terms of the GNU Public License. .\" -.TH ETHTOOL 8 "April 1999" "Ethtool version 1.0" +.TH ETHTOOL 8 "August 2001" "Ethtool version 1.3" .SH NAME -ethtool \- Display or change ethernet card setting +ethtool \- Display or change ethernet card settings .SH SYNOPSIS .B ethtool .I ethX @@ -50,9 +50,19 @@ .I internal,external } ] +[ +.B wol +[ +.I pumbagsd +]+ +] +[ +.B sopass +.I X:X:X:X:X:X +] .SH DESCRIPTION .BI ethtool -Is used for querying setting of an ethernet device and changing it. +Is used for querying settings of an ethernet device and changing them. .I ethX is the name of the ethernet device to work on. @@ -95,11 +105,49 @@ .I xcvr Select transceiver type. Currently only internal and external can be specified, in the future further types might be added. +.TP +.I wol +Set Wake-on-LAN options. Not all devices support this. The argument to +this option is a string of characters specifying which options to enable. +.PD 0 +.TP +.B p +Wake on phy activity +.TP +.B u +Wake on unicast messages +.TP +.B m +Wake on multicast messages +.TP +.B b +Wake on broadcast messages +.TP +.B a +Wake on ARP +.TP +.B g +Wake on MagicPacket(tm) +.TP +.B s +Enable SecureOn(tm) password for MagicPacket(tm) +.TP +.B d +Disable (wake on nothing). This option clears all previous options. +.TP +.TP +.I sopass +Set the SecureOn(tm) password. The argument to this option must be 6 +bytes in ethernet MAC hex format (xx:yy:zz:aa:bb:cc). .SH BUGS -Currently supported by Sun Happy Meal Ethernet only. +Not supported (in part or whole) on all ethernet drivers. .SH AUTHOR .B ethtool -has been written by David Miller . +was written by David Miller . + +Modifications by +Jeff Garzik , +Tim Hockin . Manual page written by Jakub Jelinek . .SH AVAILABILITY diff -ruN ethtool-1.2/ethtool.c ethtool-1.3/ethtool.c --- ethtool-1.2/ethtool.c Thu May 17 18:52:40 2001 +++ ethtool-1.3/ethtool.c Fri May 14 15:20:57 1976 @@ -3,6 +3,7 @@ * * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com) * Kernel 2.4 update Copyright 2001 Jeff Garzik + * Wake-on-LAN support by Tim Hockin */ #ifdef HAVE_CONFIG_H @@ -22,40 +23,59 @@ #include #include +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); /* Syntax: * * ethtool DEVNAME + * ethtool -i DEVNAME * ethtool -s DEVNAME [ speed {10,100,1000} ] \ * [ duplex {half,full} ] \ * [ port {tp,aui,mii,fibre} ] \ * [ autoneg {on,off} ] \ * [ phyad %d ] \ - * [ xcvr {internal,external} ] + * [ xcvr {internal,external} ] \ + * [ wol [pumbagsd]+ ] \ + * [ sopass %x:%x:%x:%x:%x:%x ] */ static char *devname = NULL; -static int cmd = ETHTOOL_GSET; +static enum { MODE_GSET=0, MODE_SSET, MODE_GDRV } mode = MODE_GSET; 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 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 void show_usage(int badarg) { fprintf(stderr, PACKAGE " version " VERSION "\n"); - fprintf(stderr, "Usage:\n"); - fprintf(stderr, " ethtool DEVNAME\n"); - fprintf(stderr, " ethtool -i DEVNAME\n"); - fprintf(stderr, " ethtool -s DEVNAME \\\n"); - fprintf(stderr, " [ speed {10,100,1000} ] \\\n"); - fprintf(stderr, " [ duplex {half,full} ] \\\n"); - fprintf(stderr, " [ port {tp,aui,mii,fibre} ] \\\n"); - fprintf(stderr, " [ autoneg {on,off} ] \\\n"); - fprintf(stderr, " [ phyad %%d ] \\\n"); - fprintf(stderr, " [ xcvr {internal,external} ] \\\n"); + fprintf(stderr, + "Usage:\n" + " ethtool DEVNAME\n" + " ethtool -i DEVNAME\n" + " ethtool -s DEVNAME \\\n" + " [ speed {10,100,1000} ] \\\n" + " [ duplex {half,full} ] \\\n" + " [ port {tp,aui,mii,fibre} ] \\\n" + " [ autoneg {on,off} ] \\\n" + " [ phyad %%d ] \\\n" + " [ xcvr {internal,external} ] \\\n" + " [ wol [pumbagsd]+ ] \\\n" + " [ sopass %%x:%%x:%%x:%%x:%%x:%%x ] \n" + ); exit(badarg); } @@ -67,25 +87,26 @@ switch(i) { case 1: if(!strcmp(argp[i], "-s")) - cmd = ETHTOOL_SSET; + mode = MODE_SSET; else if(!strcmp(argp[i], "-i")) - cmd = ETHTOOL_GDRVINFO; + mode = MODE_GDRV; else if(!strcmp(argp[i], "-h")) show_usage(0); else devname = argp[i]; break; case 2: - if ((cmd == ETHTOOL_SSET) || - (cmd == ETHTOOL_GDRVINFO)) { + if ((mode == MODE_SSET) || + (mode == MODE_GDRV)) { devname = argp[i]; break; } /* fallthrough */ default: - if(cmd != ETHTOOL_SSET) + if (mode != MODE_SSET) show_usage(1); if(!strcmp(argp[i], "speed")) { + gset_changed = 1; i += 1; if(i >= argc) show_usage(1); @@ -99,6 +120,7 @@ show_usage(1); break; } else if(!strcmp(argp[i], "duplex")) { + gset_changed = 1; i += 1; if(i >= argc) show_usage(1); @@ -110,6 +132,7 @@ show_usage(1); break; } else if(!strcmp(argp[i], "port")) { + gset_changed = 1; i += 1; if(i >= argc) show_usage(1); @@ -125,6 +148,7 @@ show_usage(1); break; } else if(!strcmp(argp[i], "autoneg")) { + gset_changed = 1; i += 1; if(i >= argc) show_usage(1); @@ -136,6 +160,7 @@ show_usage(1); break; } else if(!strcmp(argp[i], "phyad")) { + gset_changed = 1; i += 1; if(i >= argc) show_usage(1); @@ -144,6 +169,7 @@ show_usage(1); break; } else if(!strcmp(argp[i], "xcvr")) { + gset_changed = 1; i += 1; if(i >= argc) show_usage(1); @@ -154,6 +180,24 @@ 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; } show_usage(1); } @@ -165,7 +209,7 @@ static void dump_supported(struct ethtool_cmd *ep) { u_int32_t mask = ep->supported; - int cnt; + int did1; fprintf(stdout, " Supported ports: [ "); if(mask & SUPPORTED_TP) @@ -179,38 +223,36 @@ fprintf(stdout, "]\n"); fprintf(stdout, " Supported link modes: "); - cnt = 0; + did1 = 0; if(mask & SUPPORTED_10baseT_Half) { - cnt++; fprintf(stdout, "10baseT/Half "); + did1++; fprintf(stdout, "10baseT/Half "); } if(mask & SUPPORTED_10baseT_Full) { - cnt++; fprintf(stdout, "10baseT/Full "); + did1++; fprintf(stdout, "10baseT/Full "); } - if(cnt != 0) { + if(did1 && mask & (SUPPORTED_100baseT_Half|SUPPORTED_100baseT_Full)) { fprintf(stdout, "\n"); fprintf(stdout, " "); - cnt = 0; } if(mask & SUPPORTED_100baseT_Half) { - cnt++; fprintf(stdout, "100baseT/Half "); + did1++; fprintf(stdout, "100baseT/Half "); } if(mask & SUPPORTED_100baseT_Full) { - cnt++; fprintf(stdout, "100baseT/Full "); + did1++; fprintf(stdout, "100baseT/Full "); } - if(cnt != 0) { + if(did1 && mask & (SUPPORTED_1000baseT_Half|SUPPORTED_1000baseT_Full)) { fprintf(stdout, "\n"); fprintf(stdout, " "); - cnt = 0; } if(mask & SUPPORTED_1000baseT_Half) { - cnt++; fprintf(stdout, "1000baseT/Half "); + did1++; fprintf(stdout, "1000baseT/Half "); } if(mask & SUPPORTED_1000baseT_Full) { - cnt++; fprintf(stdout, "1000baseT/Full "); + did1++; fprintf(stdout, "1000baseT/Full "); } fprintf(stdout, "\n"); - fprintf(stdout, " Supports auto-negotiation? "); + fprintf(stdout, " Supports auto-negotiation: "); if(mask & SUPPORTED_Autoneg) fprintf(stdout, "Yes\n"); else @@ -293,10 +335,10 @@ static int dump_drvinfo(struct ethtool_drvinfo *info) { fprintf(stdout, -"driver: %s\n" -"version: %s\n" -"firmware-version: %s\n" -"bus-info: %s\n", + "driver: %s\n" + "version: %s\n" + "firmware-version: %s\n" + "bus-info: %s\n", info->driver, info->version, info->fw_version, @@ -305,23 +347,121 @@ 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 int doit(void) { - struct ethtool_cmd ecmd; struct ifreq ifr; - int fd, err; + int fd; char buf[1024]; /* Setup our control structures. */ - memset(&ecmd, 0, sizeof(ecmd)); + memset(buf, 0, sizeof(buf)); memset(&ifr, 0, sizeof(ifr)); - strcpy(&ifr.ifr_name[0], devname); + strcpy(ifr.ifr_name, devname); ifr.ifr_data = (caddr_t) &buf; - if (cmd == ETHTOOL_GDRVINFO) - ecmd.cmd = ETHTOOL_GDRVINFO; - else - ecmd.cmd = ETHTOOL_GSET; - memcpy(&buf, &ecmd, sizeof(ecmd.cmd)); /* Open control socket. */ fd = socket(AF_INET, SOCK_DGRAM, 0); @@ -330,45 +470,123 @@ return 70; } - /* Get current settings. */ - err = ioctl(fd, SIOCETHTOOL, &ifr); + 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); + } + + return 69; +} + +static int do_gdrv(int fd, struct ifreq *ifr) +{ + int err; + struct ethtool_cmd *ecmd = (struct ethtool_cmd *)ifr->ifr_data; + + ecmd->cmd = ETHTOOL_GDRVINFO; + err = ioctl(fd, SIOCETHTOOL, ifr); if(err < 0) { - perror("Cannot get current device settings"); + perror("Cannot get driver information"); return 71; } - if(cmd == ETHTOOL_GDRVINFO) - return dump_drvinfo((struct ethtool_drvinfo *) &buf); - else if(cmd == ETHTOOL_GSET) - return dump_ecmd((struct ethtool_cmd *) &buf); - else if(cmd == ETHTOOL_SSET) { - memcpy(&ecmd, &buf, sizeof(ecmd)); - ifr.ifr_data = (caddr_t) &ecmd; + return dump_drvinfo((struct ethtool_drvinfo *)ifr->ifr_data); +} + +static int do_gset(int fd, struct ifreq *ifr) +{ + int err; + struct ethtool_cmd *ecmd = (struct ethtool_cmd *)ifr->ifr_data; + + ecmd->cmd = ETHTOOL_GSET; + err = ioctl(fd, SIOCETHTOOL, ifr); + if(err < 0) { + perror("Cannot get device settings"); + return 71; + } + err = dump_ecmd(ecmd); + if (err) { + return err; + } + + ecmd->cmd = ETHTOOL_GWOL; + err = ioctl(fd, SIOCETHTOOL, ifr); + if(err < 0) { + perror("Cannot get wake-on-lan settings"); + return 72; + } + err = dump_wol((struct ethtool_wolinfo *)ifr->ifr_data); + return 0; +} + +static int do_sset(int fd, struct ifreq *ifr) +{ + int err; + struct ethtool_cmd *ecmd = (struct ethtool_cmd *)ifr->ifr_data; + struct ethtool_wolinfo *wol = (struct ethtool_wolinfo *)ifr->ifr_data; + + if (gset_changed) { + ecmd->cmd = ETHTOOL_GSET; + err = ioctl(fd, SIOCETHTOOL, ifr); + if(err < 0) { + perror("Cannot get current device settings"); + return 71; + } + /* Change everything the user specified. */ if(speed_wanted != -1) - ecmd.speed = speed_wanted; + ecmd->speed = speed_wanted; if(duplex_wanted != -1) - ecmd.duplex = duplex_wanted; + ecmd->duplex = duplex_wanted; if(port_wanted != -1) - ecmd.port = port_wanted; + ecmd->port = port_wanted; if(autoneg_wanted != -1) - ecmd.autoneg = autoneg_wanted; + ecmd->autoneg = autoneg_wanted; if(phyad_wanted != -1) - ecmd.phy_address = phyad_wanted; + ecmd->phy_address = phyad_wanted; if(xcvr_wanted != -1) - ecmd.transceiver = xcvr_wanted; - + ecmd->transceiver = xcvr_wanted; + /* Try to perform the update. */ - ecmd.cmd = ETHTOOL_SSET; - err = ioctl(fd, SIOCETHTOOL, &ifr); + ecmd->cmd = ETHTOOL_SSET; + err = ioctl(fd, SIOCETHTOOL, ifr); if(err < 0) { perror("Cannot update new settings"); return 72; } - return 0; } - /* XXX */ - return 69; + if (gwol_changed) { + ecmd->cmd = ETHTOOL_GWOL; + err = ioctl(fd, SIOCETHTOOL, ifr); + if (err < 0) { + perror("Cannot get current wake-on-lan settings"); + return 73; + } + + /* 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. */ + ecmd->cmd = ETHTOOL_SWOL; + err = ioctl(fd, SIOCETHTOOL, ifr); + if(err < 0) { + perror("Cannot update new wake-on-lan settings"); + return 74; + } + } + + return 0; } int main(int argc, char **argp, char **envp)