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, &reg));
+	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, &reg) == 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