Now that arp(8) can send Wake On Lan frames (a.k.a. magic packets),
it would be nice to have a way to configure WOL from the operating system.

Some network cards need help from the OS to do WOL.
Some already do WOL without help from the OS.
For the latter it's nice to have a way to disable it.

This diff adds code to enable/disable WOL.
It's based on earlier diffs I've written for WOL which never went in,
and some discussion with Theo and Claudio.

If you have an re(4) or vr(4), you are lucky because those are the only
drivers supported so far. More drivers will be added later.

More often than not, WOL depends on BIOS settings.
Sometimes BIOS settings override what the OS wants, and vice versa.

Please test if you're interested in WOL. I'm particularly interested in
test reports with vr(4). The on-board vr(4) I have doesn't listen to the OS.
It only listens to the BIOS. Could someone owning a vr(4) verify that this
diff can enable and disable WOL regardless of (or in some combination with)
BIOS settings?

After booting the patched kernel, don't forget to run 'make includes'
in /usr/src before trying to compile ifconfig. Else, the if.h changes
won't be seen by ifconfig and it will fail to build.

Because this changes struct ifnet, sbin/route and usr.bin/netstat
may need to be recompiled, too.

Index: sbin/ifconfig/brconfig.h
===================================================================
RCS file: /cvs/src/sbin/ifconfig/brconfig.h,v
retrieving revision 1.3
diff -u -p -r1.3 brconfig.h
--- sbin/ifconfig/brconfig.h    7 Jun 2010 15:05:42 -0000       1.3
+++ sbin/ifconfig/brconfig.h    12 Mar 2011 22:48:05 -0000
@@ -68,7 +68,7 @@ int bridge_rule(int, char **, int);
 
 #define        IFFBITS \
 
"\024\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6NOTRAILERS\7RUNNING\10NOARP\
-\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2\20MULTICAST\21TXREADY\22NOINET6\23INET6_PRIVACY\24MPLS"
+\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2\20MULTICAST\21TXREADY\22NOINET6\23INET6_PRIVACY\24MPLS\25WOL"
 
 void printb(char *, unsigned int, char *);
 
Index: sbin/ifconfig/ifconfig.8
===================================================================
RCS file: /cvs/src/sbin/ifconfig/ifconfig.8,v
retrieving revision 1.213
diff -u -p -r1.213 ifconfig.8
--- sbin/ifconfig/ifconfig.8    17 Feb 2011 08:32:29 -0000      1.213
+++ sbin/ifconfig/ifconfig.8    12 Mar 2011 22:55:55 -0000
@@ -435,6 +435,27 @@ This may be used to enable an interface 
 It happens automatically when setting the first address on an interface.
 If the interface was reset when previously marked down,
 the hardware will be re-initialized.
+.Pp
+.It Cm wol
+Enable Wake On Lan.
+When enabled, reception of a Wake On Lan frame (a.k.a. Magic Packet)
+will cause the network card to power up the system from standby or
+suspend mode.
+Wake On Lan frames can be sent with
+.Xr arp 8 .
+Support for Wake On Lan depends on various factors,
+some of which may not be under control of the operating system.
+Configuration parameters in the system BIOS or firmware can affect Wake On Lan.
+.Pp
+Wake On Lan should not be enabled on interfaces that can receive traffic
+from the internet.
+It should only be enabled on dedicated management interfaces connected to
+networks where no packets can be injected by untrusted parties.
+Wake On Lan frames can be sent in routable IP packets and are not 
authenticated.
+.It Fl wol
+Disable Wake On Lan.
+Wake On Lan is disabled by default if possible.
+The operating system cannot disable Wake On Lan on some machines.
 .El
 .Pp
 .Nm
Index: share/man/man4/re.4
===================================================================
RCS file: /cvs/src/share/man/man4/re.4,v
retrieving revision 1.44
diff -u -p -r1.44 re.4
--- share/man/man4/re.4 8 Jul 2010 09:19:11 -0000       1.44
+++ share/man/man4/re.4 12 Mar 2011 23:35:47 -0000
@@ -168,6 +168,10 @@ Force full duplex operation.
 Force half duplex operation.
 .El
 .Pp
+The
+.Nm
+driver supports Wake On Lan.
+.Pp
 For more information on configuring this device, see
 .Xr ifconfig 8 .
 .Sh SEE ALSO
Index: share/man/man4/vr.4
===================================================================
RCS file: /cvs/src/share/man/man4/vr.4,v
retrieving revision 1.22
diff -u -p -r1.22 vr.4
--- share/man/man4/vr.4 16 Mar 2009 22:47:45 -0000      1.22
+++ share/man/man4/vr.4 12 Mar 2011 23:36:21 -0000
@@ -104,6 +104,11 @@ Force half duplex operation.
 .Pp
 Note that the 100baseTX media type is only available if supported
 by the adapter.
+.Pp
+The
+.Nm
+driver supports Wake On Lan.
+.Pp
 For more information on configuring this device, see
 .Xr ifconfig 8 .
 .Sh DIAGNOSTICS
Index: sbin/ifconfig/ifconfig.c
===================================================================
RCS file: /cvs/src/sbin/ifconfig/ifconfig.c,v
retrieving revision 1.244
diff -u -p -r1.244 ifconfig.c
--- sbin/ifconfig/ifconfig.c    1 Mar 2011 09:37:31 -0000       1.244
+++ sbin/ifconfig/ifconfig.c    12 Mar 2011 23:10:43 -0000
@@ -461,6 +461,8 @@ const struct        cmd {
        { "descr",      NEXTARG,        0,              setifdesc },
        { "-description", 1,            0,              unsetifdesc },
        { "-descr",     1,              0,              unsetifdesc },
+       { "wol",        IFXF_WOL,       0,              setifxflags },
+       { "-wol",       -IFXF_WOL,      0,              setifxflags },
 #else /* SMALL */
        { "group",      NEXTARG,        0,              setignore },
        { "powersave",  NEXTARG0,       0,              setignore },
@@ -474,6 +476,8 @@ const struct        cmd {
        { "-inet6",     IFXF_NOINET6,   0,              setignore } ,
        { "description", NEXTARG,       0,              setignore },
        { "descr",      NEXTARG,        0,              setignore },
+       { "wol",        IFXF_WOL,       0,              setignore },
+       { "-wol",       -IFXF_WOL,      0,              setignore },
 #endif /* SMALL */
 #if 0
        /* XXX `create' special-cased below */
Index: sys/dev/ic/re.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/re.c,v
retrieving revision 1.132
diff -u -p -r1.132 re.c
--- sys/dev/ic/re.c     28 Nov 2010 22:13:48 -0000      1.132
+++ sys/dev/ic/re.c     12 Mar 2011 22:44:08 -0000
@@ -194,6 +194,9 @@ void        re_disable_hw_im(struct rl_softc *)
 void   re_disable_sim_im(struct rl_softc *);
 void   re_config_imtype(struct rl_softc *, int);
 void   re_setup_intr(struct rl_softc *, int, int);
+#ifndef SMALL_KERNEL
+int    re_wol(struct ifnet*, int);
+#endif
 
 #ifdef RE_DIAG
 int    re_diag(struct rl_softc *);
@@ -1118,6 +1121,11 @@ re_attach(struct rl_softc *sc, const cha
        ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
 #endif
 
+#ifndef SMALL_KERNEL
+       ifp->if_capabilities |= IFCAP_WOL;
+       ifp->if_wol = re_wol;
+       re_wol(ifp, 0);
+#endif
        timeout_set(&sc->timer_handle, re_tick, sc);
 
        /* Take PHY out of power down mode. */
@@ -2335,3 +2343,53 @@ re_setup_intr(struct rl_softc *sc, int e
                      sc->sc_dev.dv_xname, imtype);
        }
 }
+
+#ifndef SMALL_KERNEL
+struct re_wolcfg {
+       u_int8_t        enable;
+       u_int8_t        reg;
+       u_int8_t        bit;
+} re_wolcfg[] = {
+       /* Always disable all wake events expect magic packet. */
+       {0,     RL_CFG5,        RL_CFG5_WOL_UCAST},
+       {0,     RL_CFG5,        RL_CFG5_WOL_MCAST},
+       {0,     RL_CFG5,        RL_CFG5_WOL_BCAST},
+       {1,     RL_CFG3,        RL_CFG3_WOL_MAGIC},
+       {0,     RL_CFG3,        RL_CFG3_WOL_LINK}
+};
+
+int
+re_wol(struct ifnet *ifp, int enable)
+{
+       struct rl_softc *sc = ifp->if_softc;
+       int i;
+       u_int8_t val;
+
+       if (enable) {
+               if ((CSR_READ_1(sc, RL_CFG1) & RL_CFG1_PME) == 0) {
+                       printf("%s: power management is disabled, "
+                           "cannot do WOL\n", sc->sc_dev.dv_xname);
+                       return (EINVAL);
+               }
+               if ((CSR_READ_1(sc, RL_CFG2) & RL_CFG2_AUXPWR) == 0)
+                       printf("%s: no auxiliary power, cannot do WOL from D3 "
+                           "(power-off) state\n", sc->sc_dev.dv_xname);
+       }
+
+       /* Temporarily enable write to configuration registers. */
+       CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_WRITECFG);
+
+       for (i = 0; i < nitems(re_wolcfg); i++) {
+               val = CSR_READ_1(sc, re_wolcfg[i].reg);
+               if (enable && re_wolcfg[i].enable)
+                       val |= re_wolcfg[i].bit;
+               else
+                       val &= ~re_wolcfg[i].bit;
+               CSR_WRITE_1(sc, re_wolcfg[i].reg, val);
+       }
+
+       CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
+
+       return (0);
+}
+#endif
Index: sys/dev/ic/rtl81x9reg.h
===================================================================
RCS file: /cvs/src/sys/dev/ic/rtl81x9reg.h,v
retrieving revision 1.72
diff -u -p -r1.72 rtl81x9reg.h
--- sys/dev/ic/rtl81x9reg.h     28 Nov 2010 22:08:59 -0000      1.72
+++ sys/dev/ic/rtl81x9reg.h     8 Dec 2010 12:57:59 -0000
@@ -987,6 +987,7 @@ struct rl_softc {
 #define RL_PCI_INTPIN          0x3D
 #define RL_PCI_MINGNT          0x3E
 #define RL_PCI_MINLAT          0x0F
+#define RL_PCI_PMCSR           0x44
 #define RL_PCI_RESETOPT                0x48
 #define RL_PCI_EEPROM_DATA     0x4C
 
@@ -1000,7 +1001,7 @@ struct rl_softc {
 #define RL_PSTATE_D1           0x0001
 #define RL_PSTATE_D2           0x0002
 #define RL_PSTATE_D3           0x0003
-#define RL_PME_EN              0x0010
+#define RL_PME_EN              0x0100
 #define RL_PME_STATUS          0x8000
 
 extern int rl_attach(struct rl_softc *);
Index: sys/dev/pci/if_re_pci.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_re_pci.c,v
retrieving revision 1.30
diff -u -p -r1.30 if_re_pci.c
--- sys/dev/pci/if_re_pci.c     7 Sep 2010 16:21:45 -0000       1.30
+++ sys/dev/pci/if_re_pci.c     21 Sep 2010 08:19:42 -0000
@@ -163,6 +163,11 @@ re_pci_attach(struct device *parent, str
                pci_conf_write(pc, pa->pa_tag, RL_PCI_INTLINE, irq);
        }
 
+#ifndef SMALL_KERNEL
+       /* Enable power management for wake on lan. */
+       pci_conf_write(pc, pa->pa_tag, RL_PCI_PMCSR, RL_PME_EN);
+#endif
+
        /*
         * Map control/status registers.
         */
Index: sys/dev/pci/if_vr.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_vr.c,v
retrieving revision 1.107
diff -u -p -r1.107 if_vr.c
--- sys/dev/pci/if_vr.c 13 Jan 2011 11:28:14 -0000      1.107
+++ sys/dev/pci/if_vr.c 12 Mar 2011 22:42:47 -0000
@@ -137,6 +137,9 @@ void vr_reset(struct vr_softc *);
 int vr_list_rx_init(struct vr_softc *);
 void vr_fill_rx_ring(struct vr_softc *);
 int vr_list_tx_init(struct vr_softc *);
+#ifndef SMALL_KERNEL
+int vr_wol(struct ifnet *, int);
+#endif
 
 int vr_alloc_mbuf(struct vr_softc *, struct vr_chain_onefrag *);
 
@@ -643,6 +646,13 @@ vr_attach(struct device *parent, struct 
        if (sc->vr_quirks & VR_Q_CSUM)
                ifp->if_capabilities |= IFCAP_CSUM_IPv4|IFCAP_CSUM_TCPv4|
                                        IFCAP_CSUM_UDPv4;
+#ifndef SMALL_KERNEL
+       if (sc->vr_revid >= REV_ID_VT3065_A) {
+               ifp->if_capabilities |= IFCAP_WOL;
+               ifp->if_wol = vr_wol;
+               vr_wol(ifp, 0);
+       }
+#endif
 
        /*
         * Do MII setup.
@@ -1528,6 +1538,34 @@ vr_stop(struct vr_softc *sc)
        bzero((char *)&sc->vr_ldata->vr_tx_list,
                sizeof(sc->vr_ldata->vr_tx_list));
 }
+
+#ifndef SMALL_KERNEL
+int
+vr_wol(struct ifnet *ifp, int enable)
+{
+       struct vr_softc *sc = ifp->if_softc;
+
+       /* Clear WOL configuration */
+       CSR_WRITE_1(sc, VR_WOLCRCLR, 0xFF);
+
+       /* Clear event status bits. */
+       CSR_WRITE_1(sc, VR_PWRCSRCLR, 0xFF);
+
+       /* Disable PME# assertion upon wake event. */
+       VR_CLRBIT(sc, VR_STICKHW, VR_STICKHW_WOL_ENB);
+       VR_SETBIT(sc, VR_WOLCFGCLR, VR_WOLCFG_PMEOVR);
+
+       if (enable) {
+               VR_SETBIT(sc, VR_WOLCRSET, VR_WOLCR_MAGIC);
+
+               /* Enable PME# assertion upon wake event. */
+               VR_SETBIT(sc, VR_STICKHW, VR_STICKHW_WOL_ENB);
+               VR_SETBIT(sc, VR_WOLCFGSET, VR_WOLCFG_PMEOVR);
+       }
+
+       return (0);
+}
+#endif
 
 int
 vr_alloc_mbuf(struct vr_softc *sc, struct vr_chain_onefrag *r)
Index: sys/dev/pci/if_vrreg.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_vrreg.h,v
retrieving revision 1.27
diff -u -p -r1.27 if_vrreg.h
--- sys/dev/pci/if_vrreg.h      18 Jun 2009 17:48:15 -0000      1.27
+++ sys/dev/pci/if_vrreg.h      12 Mar 2011 23:00:27 -0000
@@ -285,6 +285,21 @@
 #define VR_STICKHW_WOL_STS     0x08
 #define VR_STICKHW_LEGWOL_ENB  0x80
 
+/* Wake on Lan */
+#define VR_WOLCRSET            0xA0
+#define VR_PWRCFGSET           0xA1
+#define VR_WOLCFGSET           0xA3
+#define VR_WOLCRCLR            0xA4
+#define VR_PWRCFGCLR           0xA5
+#define VR_WOLCFGCLR           0xA7
+#define VR_PWRCSRSET           0xA8
+#define VR_PWRCSRCLR           0xAC
+#define VR_WOLCR_UCAST         0x10
+#define VR_WOLCR_MAGIC         0x20
+#define VR_WOLCR_LINKON                0x40
+#define VR_WOLCR_LINKOFF       0x80
+#define VR_WOLCFG_PMEOVR       0x80
+
 /*
  * BCR0 register bits. (At least for the VT6102 chip.)
  */
Index: sys/net/if.c
===================================================================
RCS file: /cvs/src/sys/net/if.c,v
retrieving revision 1.233
diff -u -p -r1.233 if.c
--- sys/net/if.c        25 Jan 2011 05:44:05 -0000      1.233
+++ sys/net/if.c        12 Mar 2011 23:00:48 -0000
@@ -1356,7 +1356,28 @@ ifioctl(struct socket *so, u_long cmd, c
                        splx(s);
                }
 #endif
-
+#ifndef SMALL_KERNEL
+               if (ifp->if_capabilities & IFCAP_WOL) {
+                       if (ISSET(ifr->ifr_flags, IFXF_WOL) &&
+                           !ISSET(ifp->if_xflags, IFXF_WOL)) {
+                               int s = splnet();
+                               ifp->if_xflags |= IFXF_WOL;
+                               error = ifp->if_wol(ifp, 1);
+                               splx(s);
+                               if (error)
+                                       return (error);
+                       }
+                       if (ISSET(ifp->if_xflags, IFXF_WOL) &&
+                           !ISSET(ifr->ifr_flags, IFXF_WOL)) {
+                               int s = splnet();
+                               ifp->if_xflags &= ~IFXF_WOL;
+                               error = ifp->if_wol(ifp, 0);
+                               splx(s);
+                               if (error)
+                                       return (error);
+                       }
+               }
+#endif
 
                ifp->if_xflags = (ifp->if_xflags & IFXF_CANTCHANGE) |
                        (ifr->ifr_flags & ~IFXF_CANTCHANGE);
Index: sys/net/if.h
===================================================================
RCS file: /cvs/src/sys/net/if.h,v
retrieving revision 1.121
diff -u -p -r1.121 if.h
--- sys/net/if.h        17 Nov 2010 18:51:57 -0000      1.121
+++ sys/net/if.h        12 Mar 2011 22:24:52 -0000
@@ -278,6 +278,7 @@ struct ifnet {                              /* and the 
entries */
        int     (*if_stop)(struct ifnet *, int);
                                        /* timer routine */
        void    (*if_watchdog)(struct ifnet *);
+       int     (*if_wol)(struct ifnet *, int);
        struct  ifaltq if_snd;          /* output queue (includes altq) */
        struct sockaddr_dl *if_sadl;    /* pointer to our sockaddr_dl */
 
@@ -329,6 +330,7 @@ struct ifnet {                              /* and the 
entries */
 #define        IFXF_NOINET6            0x2             /* don't do inet6 */
 #define        IFXF_INET6_PRIVACY      0x4             /* autoconf privacy 
extension */
 #define        IFXF_MPLS               0x8             /* supports MPLS */
+#define        IFXF_WOL                0x10            /* wake on lan enabled 
*/
 
 #define        IFXF_CANTCHANGE \
        (IFXF_TXREADY)
@@ -352,6 +354,7 @@ struct ifnet {                              /* and the 
entries */
 #define        IFCAP_CSUM_UDPv6        0x00000100      /* can do IPv6/UDP 
checksums */
 #define        IFCAP_CSUM_TCPv4_Rx     0x00000200      /* can do IPv4/TCP (Rx 
only) */
 #define        IFCAP_CSUM_UDPv4_Rx     0x00000400      /* can do IPv4/UDP (Rx 
only) */
+#define        IFCAP_WOL               0x00008000      /* can do wake on lan */
 
 /*
  * Output queues (ifp->if_snd) and internetwork datagram level (pup level 1)

Reply via email to