>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     [email protected]:/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:
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
To unsubscribe, send any mail to "[email protected]"

Reply via email to