Hi,
WOL is partially broken on forcedeth, at least until the v0.56 of the driver but if you read the link below, enhancements are in progress in the forcedeth driver to fix this.
First, check the version of your forcedeth driver (you'll see the version of the driver in /var/log/messages when loading the forcedeth module).
■
For forcedeth v0.56, there is a bug in the driver that requires to send the WOL packet with a reverse MAC address, eg:
Code:
piano:~# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 00:0C:76:5E:FE:3D
The wake-up command will be
Code:
wol -h 192.168.254.255 3d:fe:5e:76:0c:00
This is due to reverse byte orders of the forcedeth driver that is currently being investigated (see http://www.nvnews.net/vbulletin/showthread.php?t=70384).
■
For forcedeth version 0.49 and prior, you can try the following patch (at your own risk) (it did work for me with 0.49 within the 2.6.16 kernel):
This changes in the forcedeth have been performed to reproduce what had been done manually before by Carl-Daniel Hailfinger.
See:
-
http://www.oss.sgi.com/archives/netdev/2004-03/msg00245.html
-
http://atlas.et.tudelft.nl/verwei90/nforce2/wol.html
-
http://bugzilla.kernel.org/show_bug.cgi?id=1636
The only advantages compared to the pci-config method described by Carl-Daniel Hailfinger is that this fix directly updates the forcedeth and thus:
- The kernel does not complain anymore about timeouts on rx/tx when put in WOL mode... (even when this happened, it this didn't lead to a kernel crash though)
- There is no need to tweak DEV_NEED_TIMERIRQ anymore (we actually stop interrupts processing before moving the D3 sleep mode)
This patch worked on a debian with kernel 2.6.16 (forcedeth v0.49). It requires recompiling the module from the kernel sources (forcedeth.c)
Using the modified forcedeth driver, I just had to add the following script at shutdown (in /etc/rc0.d/S21nforce-wol for me)
Code:
#!/bin/sh
ethtool -s eth0 wol g
This allows the forcedeth driver to be set as WOL just before shutting down the network interface
Here is the patch for the kernel module:
[fixed]
--- drivers/net/forcedeth.c.orig 2007-08-17 10:25:00.000000000 +0200
+++ drivers/net/forcedeth.c 2007-08-17 10:55:54.000000000 +0200
@@ -102,8 +102,41 @@
* 0.47: 26 Oct 2005: Add phyaddr 0 in phy scan.
* 0.48: 24 Dec 2005: Disable TSO, bugfix for pci_map_single
* 0.49: 10 Dec 2005: Fix tso for large buffers.
+ * 10 Aug 2007: Fix WOL support (D3 PCI power save mode)
*
* Known bugs:
+ * Lionel Ains (wol fix):
+ * 1) Enable wol g mode with ethtool will only work if interface is up
+ * 2) When set to wol g mode, the NIC will be brought down immediately,
+ * so this is something you want to do only at shutdown (ie: around S2
+ * in /etc/rc0.d for debian)
+ * 2) If you put your NIC in wol mode, then rmmod forcedeth, modprobe forcedeth
+ * will always fail afterwards (until next reboot). The failure actually occurs
+ * within nv_probe() which fails detecting the nforce NIC when in D3 sleep mode
+ *
+ * This changes in the forcedeth have been performed to reproduce
+ * what had been done manually before by Carl-Daniel Hailfinger
+ * See:
+ *
http://www.oss.sgi.com/archives/netdev/2004-03/msg00245.html
+ *
http://atlas.et.tudelft.nl/verwei90/nforce2/wol.html
+ *
http://bugzilla.kernel.org/show_bug.cgi?id=1636
+ * The only advantages brought by the fact this fix directly updates the
+ * forcedeth module are that:
+ * - kernel does not complain anymore about timeouts on rx/tx when put
+ * in WOL mode... (even when this happened, it this didn't lead to a kernel
+ * crash though)
+ * - No need to tweak DEV_NEED_TIMERIRQ anymore (we actually stop interrupts
+ * processing before moving the D3 sleep mode)
+ *
+ * This worked on a debian with kernel 2.6.16 (forcedeth v0.49)
+ * Using the modified forcedeth driver, I just had to add the following
+ * script at shutdown (in /etc/rc0.d/S21nforce-wol for me)
+
+#!/bin/sh
+ethtool -s eth0 wol g
+
+ *
+ *
* We suspect that on some hardware no TX done interrupts are generated.
* This means recovery from netif_stop_queue only happens if the hw timer
* interrupt fires (100 times/second, configurable with NVREG_POLL_DEFAULT)
@@ -1014,6 +1047,34 @@
nv_drain_rx(dev);
}
+static void nv_powersave_D0(struct net_device *dev)
+{
+ u8 __iomem *base = get_hwbase(dev);
+
+ if ((readl(base + NvRegPowerState) & NVREG_POWERSTATE_D0) == 0) {
+ writel(NVREG_POWERSTATE_D0, base + NvRegPowerState);
+ pci_push(base);
+ udelay(10);
+ printk(KERN_INFO "%s: Switching to D0 active mode\n", dev->name);
+ }
+ writel(readl(base + NvRegPowerState) | NVREG_POWERSTATE_VALID, base + NvRegPowerState);
+ pci_push(base);
+}
+
+static void nv_powersave_D3(struct net_device *dev)
+{
+ u8 __iomem *base = get_hwbase(dev);
+
+ if ((readl(base + NvRegPowerState) & NVREG_POWERSTATE_D3) == 0) {
+ writel(NVREG_POWERSTATE_D3, base + NvRegPowerState);
+ pci_push(base);
+ udelay(10);
+ printk(KERN_INFO "%s: Switching to D3 sleep mode (WOL)\n", dev->name);
+ }
+ writel(readl(base + NvRegPowerState) | NVREG_POWERSTATE_VALID, base + NvRegPowerState);
+ pci_push(base);
+}
+
/*
* nv_start_xmit: dev->hard_start_xmit function
* Called with dev->xmit_lock held.
@@ -1974,11 +2035,21 @@
spin_lock_irq(&np->lock);
if (wolinfo->wolopts == 0) {
+ /* Disabling wol */
writel(0, base + NvRegWakeUpFlags);
+ if ((np->wolenabled) && (!(dev->flags & IFF_UP))) { /* wol was enabled, and interface is down, switch NIC to D0 power state right now */
+// printk(KERN_INFO "%s: Switching to D0 active while %s is down\n", dev->name, dev->name);
+ nv_powersave_D0(dev);
+ }
np->wolenabled = 0;
}
if (wolinfo->wolopts & WAKE_MAGIC) {
+ /* Enabling wol on magic */
writel(NVREG_WAKEUPFLAGS_ENABLE, base + NvRegWakeUpFlags);
+ if (!(np->wolenabled) && (!(dev->flags & IFF_UP))) { /* wol was disabled, and interface is down, switch to D3 power state right now */
+ printk(KERN_WARNING "%s: Switching to D3 sleep mode will only be effective at next up+down cycle for %s\n", dev->name, dev->name);
+// nv_powersave_D3(dev); /* Not working, because if must be listening on rx/tx before being put to sleep mode */
+ }
np->wolenabled = 1;
}
spin_unlock_irq(&np->lock);
@@ -2376,8 +2447,11 @@
netif_stop_queue(dev);
spin_lock_irq(&np->lock);
nv_stop_tx(dev);
- nv_stop_rx(dev);
- nv_txrx_reset(dev);
+ /* Lionel Ains: stop TX even if going to WOL */
+ if (!np->wolenabled) {
+ nv_stop_rx(dev);
+ nv_txrx_reset(dev);
+ }
/* disable interrupts on the nic or we will lock up */
base = get_hwbase(dev);
@@ -2392,7 +2466,8 @@
drain_ring(dev);
if (np->wolenabled)
- nv_start_rx(dev);
+// nv_start_rx(dev); /* Not needed... we skipped nv_stop_rx() above! */
+ nv_powersave_D3(dev);
/* special op: write back the misordered MAC address - otherwise
* the next nv_probe would see a wrong address.
@@ -2400,7 +2475,11 @@
writel(np->orig_mac[0], base + NvRegMacAddrA);
writel(np->orig_mac[1], base + NvRegMacAddrB);
- /* FIXME: power down nic */
+ /* Lionel Ains: NIC power down was done for WOL above */
+ /* We could power down NIC (to D3 mode) while bringing down interface by
+ uncommenting the lines below */
+// if (!np->wolenabled)
+// nv_powersave_D3(dev);
return 0;
}
[/fixed]
Lionel Ains