kern/148013: [patch] add WoL support to rl(4)
David Naylor
naylor.b.david at gmail.com
Sun Jun 20 16:50:02 UTC 2010
>Number: 148013
>Category: kern
>Synopsis: [patch] add WoL support to rl(4)
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: update
>Submitter-Id: current-users
>Arrival-Date: Sun Jun 20 16:50:01 UTC 2010
>Closed-Date:
>Last-Modified:
>Originator: David Naylor
>Release: FreeBSD-9
>Organization:
Private
>Environment:
FreeBSD dragon.dg 9.0-CURRENT FreeBSD 9.0-CURRENT #0: Sat Jun 19 19:08:38 SAST 2010 root at dragon.dg:/tmp/home/freebsd9/src/sys/DRAGON amd64
>Description:
Add WoL support for the rl(4) driver. The relevant portions were taken from the re(4) driver (the Linux drivers [1] [2]) also had the same implementations. I removed what I considered irrelevant code for the rl(4) driver.
I've tested this with 8139D. DISCLAIMER: I have no experience with coding drivers (or anything in the kernel).
[1] rl(4) equivalent*: http://git.kernel.org/?p=linux/kernel/git/stable/linux-2.6.34.y.git;a=blob_plain;f=drivers/net/8139too.c;hb=HEAD
[2] re(4) equivalent*: http://git.kernel.org/?p=linux/kernel/git/stable/linux-2.6.34.y.git;a=blob_plain;f=drivers/net/8139cp.c;hb=HEAD
*) see the *_set_wol() function.
>How-To-Repeat:
n/a
>Fix:
n/a
Patch attached with submission follows:
--- /usr/src/sys/pci/if_rl.c 2010-06-20 18:25:03.000000000 +0200
+++ if_rl.c 2010-06-20 15:08:32.000000000 +0200
@@ -182,6 +182,7 @@
};
static int rl_attach(device_t);
+static void rl_clrwol(struct rl_softc *);
static int rl_detach(device_t);
static void rl_dmamap_cb(void *, bus_dma_segment_t *, int, int);
static int rl_dma_alloc(struct rl_softc *);
@@ -214,6 +215,7 @@
static int rl_resume(device_t);
static int rl_rxeof(struct rl_softc *);
static void rl_setmulti(struct rl_softc *);
+static void rl_setwol(struct rl_softc *);
static int rl_shutdown(device_t);
static void rl_start(struct ifnet *);
static void rl_start_locked(struct ifnet *);
@@ -804,7 +806,7 @@
struct sysctl_ctx_list *ctx;
struct sysctl_oid_list *children;
int error = 0, i, rid;
- int unit;
+ int unit, reg;
uint16_t rl_did = 0;
char tn[32];
@@ -938,6 +940,16 @@
ifp->if_start = rl_start;
ifp->if_init = rl_init;
ifp->if_capabilities = IFCAP_VLAN_MTU;
+ /* Enable WOL if PM and chipset (810[01]/8130B?/8139[BCD]) supported. */
+ device_printf(sc->rl_dev, "RL_TYPE=0x%x\n", sc->rl_type);
+ device_printf(sc->rl_dev, "RL_HWREV=0x%x\n", CSR_READ_4(sc, RL_TXCFG) & RL_TXCFG_HWREV);
+ device_printf(sc->rl_dev, "PM=%d\n", pci_find_extcap(sc->rl_dev, PCIY_PMG, ®));
+ if ((sc->rl_type == RL_8139) &&
+ ((CSR_READ_4(sc, RL_TXCFG) & RL_TXCFG_HWREV) > RL_HWREV_8139AG) &&
+ (pci_find_extcap(sc->rl_dev, PCIY_PMG, ®) == 0)) {
+ ifp->if_capabilities |= IFCAP_WOL;
+ device_printf(sc->rl_dev, "IFCAP_WOL\n");
+ }
ifp->if_capenable = ifp->if_capabilities;
#ifdef DEVICE_POLLING
ifp->if_capabilities |= IFCAP_POLLING;
@@ -2066,6 +2078,7 @@
RL_LOCK(sc);
rl_stop(sc);
+ rl_setwol(sc);
sc->suspended = 1;
RL_UNLOCK(sc);
@@ -2087,6 +2100,12 @@
ifp = sc->rl_ifp;
RL_LOCK(sc);
+
+ /*
+ * Clear WOL matching such that normal Rx filtering
+ * wouldn't interfere with WOL patterns.
+ */
+ rl_clrwol(sc);
/* reinitialize interface if necessary */
if (ifp->if_flags & IFF_UP)
@@ -2112,7 +2131,87 @@
RL_LOCK(sc);
rl_stop(sc);
+ /*
+ * Mark interface as down since otherwise we will panic if
+ * interrupt comes in later on, which can happen in some
+ * cases.
+ */
+ sc->rl_ifp->if_flags &= ~IFF_UP;
+ rl_setwol(sc);
RL_UNLOCK(sc);
return (0);
}
+
+
+static void
+rl_setwol(struct rl_softc *sc)
+{
+ struct ifnet *ifp;
+ int pmc;
+ uint8_t v;
+
+ RL_LOCK_ASSERT(sc);
+
+ if (pci_find_extcap(sc->rl_dev, PCIY_PMG, &pmc) != 0)
+ return;
+
+ ifp = sc->rl_ifp;
+
+ /* Enable PME. */
+ v = CSR_READ_1(sc, RL_CFG1);
+ v &= ~RL_CFG1_PME;
+ if ((ifp->if_capenable & IFCAP_WOL) != 0)
+ v |= RL_CFG1_PME;
+ CSR_WRITE_1(sc, RL_CFG1, v);
+
+ v = CSR_READ_1(sc, RL_CFG3);
+ v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC);
+ if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0)
+ v |= RL_CFG3_WOL_MAGIC;
+ CSR_WRITE_1(sc, RL_CFG3, v);
+
+ /* Config register write done. */
+ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
+
+ v = CSR_READ_1(sc, RL_CFG5);
+ v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST);
+ v &= ~RL_CFG5_WOL_LANWAKE;
+ if ((ifp->if_capenable & IFCAP_WOL_UCAST) != 0)
+ v |= RL_CFG5_WOL_UCAST;
+ if ((ifp->if_capenable & IFCAP_WOL_MCAST) != 0)
+ v |= RL_CFG5_WOL_MCAST | RL_CFG5_WOL_BCAST;
+ if ((ifp->if_capenable & IFCAP_WOL) != 0)
+ v |= RL_CFG5_WOL_LANWAKE;
+ CSR_WRITE_1(sc, RL_CFG5, v);
+ device_printf(sc->rl_dev, "WOL set: 0x%x\n", v);
+}
+
+static void
+rl_clrwol(struct rl_softc *sc)
+{
+ int pmc;
+ uint8_t v;
+
+ RL_LOCK_ASSERT(sc);
+
+ if (pci_find_extcap(sc->rl_dev, PCIY_PMG, &pmc) != 0)
+ return;
+
+ /* Enable config register write. */
+ CSR_WRITE_1(sc, RL_EECMD, RL_EE_MODE);
+
+ v = CSR_READ_1(sc, RL_CFG3);
+ v &= ~(RL_CFG3_WOL_LINK | RL_CFG3_WOL_MAGIC);
+ CSR_WRITE_1(sc, RL_CFG3, v);
+
+ /* Config register write done. */
+ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF);
+
+ v = CSR_READ_1(sc, RL_CFG5);
+ v &= ~(RL_CFG5_WOL_BCAST | RL_CFG5_WOL_MCAST | RL_CFG5_WOL_UCAST);
+ v &= ~RL_CFG5_WOL_LANWAKE;
+ CSR_WRITE_1(sc, RL_CFG5, v);
+
+ device_printf(sc->rl_dev, "WOL cleared\n");
+}
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list