Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 21 Jan 2012 03:05:47 GMT
From:      Jakub Wojciech Klama <jceel@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 204933 for review
Message-ID:  <201201210305.q0L35lov052003@skunkworks.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://p4web.freebsd.org/@@204933?ac=10

Change 204933 by jceel@jceel_cyclone on 2012/01/21 03:05:31

	Introduce RX filtering based on built-in hash filter. Turn off promiscuous mode by default.

Affected files ...

.. //depot/projects/soc2011/jceel_lpc/sys/arm/lpc/if_lpe.c#10 edit
.. //depot/projects/soc2011/jceel_lpc/sys/arm/lpc/if_lpereg.h#6 edit

Differences ...

==== //depot/projects/soc2011/jceel_lpc/sys/arm/lpc/if_lpe.c#10 (text+ko) ====

@@ -133,8 +133,8 @@
 	bus_space_handle_t	lpe_bsh;
 #define	LPE_FLAG_LINK		(1 << 0)
 	uint32_t		lpe_flags;
+	int			lpe_watchdog_timer;
 	struct callout		lpe_tick;
-
 	struct lpe_chain_data	lpe_cdata;
 	struct lpe_ring_data	lpe_rdata;
 };
@@ -154,16 +154,20 @@
 static void lpe_stop(struct lpe_softc *);
 static void lpe_stop_locked(struct lpe_softc *);
 static int lpe_ioctl(struct ifnet *, u_long, caddr_t);
+static void lpe_set_rxmode(struct lpe_softc *);
+static void lpe_set_rxfilter(struct lpe_softc *);
 static void lpe_intr(void *);
 static void lpe_rxintr(struct lpe_softc *);
 static void lpe_txintr(struct lpe_softc *);
 static void lpe_tick(void *);
+static void lpe_watchdog(struct lpe_softc *);
 static int lpe_encap(struct lpe_softc *, struct mbuf **);
 static int lpe_dma_alloc(struct lpe_softc *);
 static int lpe_dma_alloc_rx(struct lpe_softc *);
 static int lpe_dma_alloc_tx(struct lpe_softc *);
 static int lpe_init_rx(struct lpe_softc *);
 static int lpe_init_rxbuf(struct lpe_softc *, int);
+static void lpe_discard_rxbuf(struct lpe_softc *, int);
 static void lpe_dmamap_cb(void *, bus_dma_segment_t *, int, int);
 static int lpe_ifmedia_upd(struct ifnet *);
 static void lpe_ifmedia_sts(struct ifnet *, struct ifmediareq *);
@@ -177,6 +181,13 @@
 #define	lpe_write_4(_sc, _reg, _val)	\
     bus_space_write_4((_sc)->lpe_bst, (_sc)->lpe_bsh, (_reg), (_val))
 
+#define	LPE_HWDESC_RXERRS	(LPE_HWDESC_CRCERROR | LPE_HWDESC_SYMBOLERROR | \
+    LPE_HWDESC_LENGTHERROR | LPE_HWDESC_ALIGNERROR | LPE_HWDESC_OVERRUN | \
+    LPE_HWDESC_RXNODESCR)
+
+#define	LPE_HWDESC_TXERRS	(LPE_HWDESC_EXCDEFER | LPE_HWDESC_EXCCOLL | \
+    LPE_HWDESC_LATECOLL | LPE_HWDESC_UNDERRUN | LPE_HWDESC_TXNODESCR)
+
 static int
 lpe_probe(device_t dev)
 {
@@ -305,6 +316,12 @@
 	struct lpe_softc *sc = device_get_softc(dev);
 
 	lpe_stop(sc);
+
+	if_free(sc->lpe_ifp);
+	bus_teardown_intr(dev, sc->lpe_irq_res, sc->lpe_intrhand);
+	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lpe_irq_res);
+	bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lpe_mem_res);
+
 	return (0);
 }
 
@@ -313,6 +330,7 @@
 {
 	struct lpe_softc *sc = device_get_softc(dev);
 	uint32_t val;
+	int result;
 
 	lpe_write_4(sc, LPE_MCMD, LPE_MCMD_READ);
 	lpe_write_4(sc, LPE_MADR, 
@@ -331,12 +349,10 @@
 		return (0);
 
 	lpe_write_4(sc, LPE_MCMD, 0);
+	result = (lpe_read_4(sc, LPE_MRDD) & LPE_MRDD_DATAMASK);
+	debugf("phy=%d reg=%d result=0x%04x\n", phy, reg, result);
 
-	int x = (lpe_read_4(sc, LPE_MRDD) & LPE_MRDD_DATAMASK);
-
-	
-	debugf("phy=%d reg=%d result=0x%04x\n", phy, reg, x);
-	return (x);
+	return (result);
 }
 
 static int
@@ -397,9 +413,9 @@
 	    LPE_COMMAND_TXRESET | LPE_COMMAND_RXRESET);
 
 	/* Set station address */
-	lpe_write_4(sc, LPE_SA0, sc->lpe_enaddr[0] << 8 | sc->lpe_enaddr[1]);
-	lpe_write_4(sc, LPE_SA1, sc->lpe_enaddr[2] << 8 | sc->lpe_enaddr[3]);
-	lpe_write_4(sc, LPE_SA2, sc->lpe_enaddr[4] << 8 | sc->lpe_enaddr[5]);
+	lpe_write_4(sc, LPE_SA2, sc->lpe_enaddr[1] << 8 | sc->lpe_enaddr[0]);
+	lpe_write_4(sc, LPE_SA1, sc->lpe_enaddr[3] << 8 | sc->lpe_enaddr[2]);
+	lpe_write_4(sc, LPE_SA0, sc->lpe_enaddr[5] << 8 | sc->lpe_enaddr[4]);
 
 	/* Leave soft reset mode */
 	mac1 = lpe_read_4(sc, LPE_MAC1);
@@ -433,8 +449,7 @@
 	/* Enable Tx and Rx */
 	cmd = lpe_read_4(sc, LPE_COMMAND);
 	lpe_write_4(sc, LPE_COMMAND, cmd | LPE_COMMAND_RXENABLE |
-	    LPE_COMMAND_TXENABLE | LPE_COMMAND_PASSRUNTFRAME | 
-	    LPE_COMMAND_PASSRXFILTER);
+	    LPE_COMMAND_TXENABLE | LPE_COMMAND_PASSRUNTFRAME);
 
 	/* Enable receive */
 	mac1 = lpe_read_4(sc, LPE_MAC1);
@@ -446,7 +461,7 @@
 	lpe_write_4(sc, LPE_MCFG, LPE_MCFG_CLKSEL(7));
 
 	/* Set up Rx filter */
-	lpe_write_4(sc, LPE_RXFILTER_CTRL, 0xffffffff);
+	lpe_set_rxmode(sc);
 
 	/* Enable interrupts */
 	lpe_write_4(sc, LPE_INTENABLE, LPE_INT_RXOVERRUN | LPE_INT_RXERROR |
@@ -503,8 +518,6 @@
 
 	lpe_lock_assert(sc);
 
-	debugf("entry");
-
 	while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
 		if (lpe_read_4(sc, LPE_TXDESC_PROD) ==
 		    lpe_read_4(sc, LPE_TXDESC_CONS) - 5)
@@ -523,6 +536,7 @@
 	/* Submit new descriptor list */
 	if (encap) {
 		lpe_write_4(sc, LPE_TXDESC_PROD, sc->lpe_cdata.lpe_tx_prod);
+		sc->lpe_watchdog_timer = 5;
 	}
 	
 }
@@ -541,7 +555,7 @@
 	prod = sc->lpe_cdata.lpe_tx_prod;
 	txd = &sc->lpe_cdata.lpe_tx_desc[prod];
 
-	debugf("lpe_encap: starting with prod=%d\n", prod);
+	debugf("starting with prod=%d\n", prod);
 
 	err = bus_dmamap_load_mbuf_sg(sc->lpe_cdata.lpe_tx_buf_tag,
 	    txd->lpe_txdesc_dmamap, *m_head, segs, &nsegs, BUS_DMA_NOWAIT);
@@ -599,6 +613,19 @@
 lpe_stop_locked(struct lpe_softc *sc)
 {
 	lpe_lock_assert(sc);
+
+	callout_stop(&sc->lpe_tick);
+
+	/* Disable interrupts */
+	lpe_write_4(sc, LPE_INTCLEAR, 0xffffffff);
+
+	/* Stop EMAC */
+	lpe_write_4(sc, LPE_MAC1, 0);
+	lpe_write_4(sc, LPE_MAC2, 0);
+	lpe_write_4(sc, LPE_COMMAND, 0);
+
+	sc->lpe_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+	sc->lpe_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
 }
 
 static int
@@ -607,9 +634,29 @@
 	struct lpe_softc *sc = ifp->if_softc;
 	struct mii_data *mii = device_get_softc(sc->lpe_miibus);
 	struct ifreq *ifr = (struct ifreq *)data;
-	int err;
+	int err = 0;
 
 	switch (cmd) {
+	case SIOCSIFFLAGS:
+		lpe_lock(sc);
+		if (ifp->if_flags & IFF_UP) {
+			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+				lpe_set_rxmode(sc);
+				lpe_set_rxfilter(sc);
+			} else
+				lpe_init_locked(sc);
+		} else
+			lpe_stop(sc);
+		lpe_unlock(sc);
+		break;
+	case SIOCADDMULTI:
+	case SIOCDELMULTI:
+		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+			lpe_lock(sc);
+			lpe_set_rxfilter(sc);
+			lpe_unlock(sc);
+		}
+		break;
 	case SIOCGIFMEDIA:
 	case SIOCSIFMEDIA:
 		err = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
@@ -622,6 +669,57 @@
 	return (err);
 }
 
+static void lpe_set_rxmode(struct lpe_softc *sc)
+{
+	struct ifnet *ifp = sc->lpe_ifp;
+	uint32_t rxfilt;
+
+	rxfilt = LPE_RXFILTER_UNIHASH | LPE_RXFILTER_MULTIHASH | LPE_RXFILTER_PERFECT;
+
+	if (ifp->if_flags & IFF_BROADCAST)
+		rxfilt |= LPE_RXFILTER_BROADCAST;
+
+	if (ifp->if_flags & IFF_PROMISC)
+		rxfilt |= LPE_RXFILTER_UNICAST | LPE_RXFILTER_MULTICAST;
+
+	if (ifp->if_flags & IFF_ALLMULTI)
+		rxfilt |= LPE_RXFILTER_MULTICAST;
+
+	lpe_write_4(sc, LPE_RXFILTER_CTRL, rxfilt);
+}
+
+static void lpe_set_rxfilter(struct lpe_softc *sc)
+{
+	struct ifnet *ifp = sc->lpe_ifp;
+	struct ifmultiaddr *ifma;
+	int index;
+	uint32_t hashl, hashh;
+
+	hashl = 0;
+	hashh = 0;
+
+	if_maddr_rlock(ifp);
+	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+		if (ifma->ifma_addr->sa_family != AF_LINK)
+			continue;
+
+		device_printf(sc->lpe_dev, "rx filter add: %6D\n", LLADDR((struct sockaddr_dl *)ifma->ifma_addr), ":");
+
+		index = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+		    ifma->ifma_addr), ETHER_ADDR_LEN) >> 23 & 0x3f;
+
+		if (index > 31)
+			hashh |= (1 << (index - 32));
+		else
+			hashl |= (1 << index);
+	}
+	if_maddr_runlock(ifp);
+
+	/* Program new hash filter */
+	lpe_write_4(sc, LPE_HASHFILTER_L, hashl);
+	lpe_write_4(sc, LPE_HASHFILTER_H, hashh);
+}
+
 static void
 lpe_intr(void *arg)
 {
@@ -666,16 +764,26 @@
 		hwd = &sc->lpe_rdata.lpe_rx_ring[cons];
 		hws = &sc->lpe_rdata.lpe_rx_status[cons];
 
+		/* Check received frame for errors */
+		if (hws->lhs_info & LPE_HWDESC_RXERRS) {
+			ifp->if_ierrors++;
+			lpe_discard_rxbuf(sc, cons);
+			lpe_init_rxbuf(sc, cons);
+			goto skip;
+		}
+
 		m = rxd->lpe_rxdesc_mbuf;
 		m->m_pkthdr.rcvif = ifp;
 		m->m_data += 2;
 
+		ifp->if_ipackets++;
+
 		lpe_unlock(sc);
 		(*ifp->if_input)(ifp, m);	
 		lpe_lock(sc);
 
 		lpe_init_rxbuf(sc, cons);
-
+skip:
 		LPE_INC(cons, LPE_RXDESC_NUM);
 		lpe_write_4(sc, LPE_RXDESC_CONS, cons);
 	}
@@ -684,13 +792,12 @@
 static void
 lpe_txintr(struct lpe_softc *sc)
 {
+	struct ifnet *ifp = sc->lpe_ifp;
 	struct lpe_hwdesc *hwd;
 	struct lpe_hwstatus *hws;
 	struct lpe_txdesc *txd;
 	int cons, last;
 
-	debugf("transmit interrupt\n");
-
 	for (;;) {
 		cons = lpe_read_4(sc, LPE_TXDESC_CONS);
 		last = sc->lpe_cdata.lpe_tx_last;
@@ -705,6 +812,14 @@
 		bus_dmamap_sync(sc->lpe_cdata.lpe_tx_buf_tag,
 		    txd->lpe_txdesc_dmamap, BUS_DMASYNC_POSTWRITE);
 
+		ifp->if_collisions += LPE_HWDESC_COLLISIONS(hws->lhs_info);
+
+		/* XXX tylko dla ostatniego fragmentu */
+		if (hws->lhs_info & LPE_HWDESC_TXERRS)
+			ifp->if_oerrors++;
+		else
+			ifp->if_opackets++;
+
 		if (txd->lpe_txdesc_first) {
 			bus_dmamap_unload(sc->lpe_cdata.lpe_tx_buf_tag,
 			    txd->lpe_txdesc_dmamap);	
@@ -714,8 +829,12 @@
 			txd->lpe_txdesc_first = 0;
 		}
 
+		sc->lpe_cdata.lpe_tx_used--;
 		LPE_INC(sc->lpe_cdata.lpe_tx_last, LPE_TXDESC_NUM);
 	}
+
+	if (!sc->lpe_cdata.lpe_tx_used)
+		sc->lpe_watchdog_timer = 0;
 }
 
 static void
@@ -725,11 +844,33 @@
 	struct mii_data *mii = device_get_softc(sc->lpe_miibus);
 
 	lpe_lock_assert(sc);
+	
 	mii_tick(mii);
+	lpe_watchdog(sc);
 
 	callout_reset(&sc->lpe_tick, hz, lpe_tick, sc);
 }
 
+static void
+lpe_watchdog(struct lpe_softc *sc)
+{
+	struct ifnet *ifp = sc->lpe_ifp;
+
+	lpe_lock_assert(sc);
+
+	if (sc->lpe_watchdog_timer == 0 || sc->lpe_watchdog_timer--)
+		return;
+
+	/* Chip has stopped responding */
+	device_printf(sc->lpe_dev, "WARNING: chip hangup, restarting...\n");
+	lpe_stop_locked(sc);
+	lpe_init_locked(sc);
+
+	/* Try to resend packets */
+	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+		lpe_start_locked(ifp);
+}
+
 static int
 lpe_dma_alloc(struct lpe_softc *sc)
 {
@@ -1022,7 +1163,6 @@
 	return (0);
 }
 
-#if 0
 static void
 lpe_discard_rxbuf(struct lpe_softc *sc, int n)
 {
@@ -1032,16 +1172,16 @@
 	rxd = &sc->lpe_cdata.lpe_rx_desc[n];
 	hwd = &sc->lpe_rdata.lpe_rx_ring[n];
 
-	bus_dmamap_unload(rxch->dve_rx_buf_tag, rxd->dve_rxdesc_dmamap);
+	bus_dmamap_unload(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap);
 
-	hwd->hw_flagslen = 0;
+	hwd->lhr_data = 0;
+	hwd->lhr_control = 0;
 
-	if (rxd->dve_rxdesc_mbuf) {
-		m_freem(rxd->dve_rxdesc_mbuf); 
-		rxd->dve_rxdesc_mbuf = NULL;
+	if (rxd->lpe_rxdesc_mbuf) {
+		m_freem(rxd->lpe_rxdesc_mbuf); 
+		rxd->lpe_rxdesc_mbuf = NULL;
 	}
 }
-#endif
 
 static void
 lpe_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)

==== //depot/projects/soc2011/jceel_lpc/sys/arm/lpc/if_lpereg.h#6 (text+ko) ====

@@ -116,6 +116,14 @@
 #define	LPE_FLOWCONTROL_COUNTER	0x170
 #define	LPE_FLOWCONTROL_STATUS	0x174
 #define	LPE_RXFILTER_CTRL	0x200
+#define	LPE_RXFILTER_UNICAST	(1 << 0)
+#define	LPE_RXFILTER_BROADCAST	(1 << 1)
+#define LPE_RXFILTER_MULTICAST	(1 << 2)
+#define	LPE_RXFILTER_UNIHASH	(1 << 3)
+#define	LPE_RXFILTER_MULTIHASH	(1 << 4)
+#define	LPE_RXFILTER_PERFECT	(1 << 5)
+#define	LPE_RXFILTER_WOL	(1 << 12)
+#define	LPE_RXFILTER_FILTWOL	(1 << 13)
 #define	LPE_RXFILTER_WOL_STATUS	0x204
 #define	LPE_RXFILTER_WOL_CLEAR	0x208
 #define	LPE_HASHFILTER_L	0x210
@@ -159,13 +167,26 @@
 
 /* These are valid for both Rx and Tx descriptors */
 #define	LPE_HWDESC_SIZE_MASK	(1 << 10)
+#define	LPE_HWDESC_INTERRUPT	(1 << 31)
 
-/* These are valid for Tx descriptors only */
-#define	LPE_HWDESC_INTERRUPT	(1 << 31)
+/* These are valid for Tx descriptors */
+#define	LPE_HWDESC_LAST		(1 << 30)
 #define	LPE_HWDESC_CRC		(1 << 29)
 #define	LPE_HWDESC_PAD		(1 << 28)
+#define	LPE_HWDESC_HUGE		(1 << 27)
+#define	LPE_HWDESC_OVERRIDE	(1 << 26)
 
-/* These are valid for Rx descriptors only */
+/* These are valid for Tx status descriptors */
+#define	LPE_HWDESC_COLLISIONS(_n) (((_n) >> 21) & 0x7)
+#define	LPE_HWDESC_DEFER	(1 << 25)
+#define	LPE_HWDESC_EXCDEFER	(1 << 26)
+#define	LPE_HWDESC_EXCCOLL	(1 << 27)
+#define	LPE_HWDESC_LATECOLL	(1 << 28)
+#define	LPE_HWDESC_UNDERRUN	(1 << 29)
+#define	LPE_HWDESC_TXNODESCR	(1 << 30)
+#define	LPE_HWDESC_ERROR	(1 << 31)
+
+/* These are valid for Rx status descriptors */
 #define	LPE_HWDESC_CONTROL	(1 << 18)
 #define	LPE_HWDESC_VLAN		(1 << 19)
 #define	LPE_HWDESC_FAILFILTER	(1 << 20)
@@ -177,7 +198,7 @@
 #define	LPE_HWDESC_RANGEERROR	(1 << 26)
 #define	LPE_HWDESC_ALIGNERROR	(1 << 27)
 #define	LPE_HWDESC_OVERRUN	(1 << 28)
-#define	LPE_HWDESC_NODESCR	(1 << 29)
+#define	LPE_HWDESC_RXNODESCR	(1 << 29)
 #define	LPE_HWDESC_LASTFLAG	(1 << 30)
 #define	LPE_HWDESC_ERROR	(1 << 31)
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201201210305.q0L35lov052003>