summaryrefslogtreecommitdiffstats
path: root/sys/pci
diff options
context:
space:
mode:
authorwpaul <wpaul@FreeBSD.org>2000-01-12 22:24:05 +0000
committerwpaul <wpaul@FreeBSD.org>2000-01-12 22:24:05 +0000
commit63989277ed024ba8b198a1947c05480dce176be8 (patch)
tree93868a6b47ec79bea8a06e01ac932f75ed9d4785 /sys/pci
parent1e3f91cfff7a8c5321271c132f7e4f705cb23bf3 (diff)
downloadFreeBSD-src-63989277ed024ba8b198a1947c05480dce176be8.zip
FreeBSD-src-63989277ed024ba8b198a1947c05480dce176be8.tar.gz
Reintroduce the dc_coal() workaround routine for coalescing outbound
packets into a single buffer, and set the DC_TX_COALESCE flag for the Davicom DM9102 chip. I thought I had escaped this problem, but... This chip appears to silently corrupt or discard transmitted frames when using scatter/gather DMA (i.e. DMAing each packet fragment in place with a separate descriptor). The only way to insure reliable transmission is to coalesce transmitted packets into a single cluster buffer. (There may also be an alignment constraint here, but mbuf cluster buffers are naturally aligned on 2K boundaries, which seems to be good enough.) The DM9102 driver for Linux written by Davicom also uses this workaround. Unfortunately, the Davicom datasheet has no errata section describing this or any other apparently known defect. Problem noted by: allan_chou@davicom.com.tw
Diffstat (limited to 'sys/pci')
-rw-r--r--sys/pci/if_dc.c44
1 files changed, 43 insertions, 1 deletions
diff --git a/sys/pci/if_dc.c b/sys/pci/if_dc.c
index 758554f..b81ce58 100644
--- a/sys/pci/if_dc.c
+++ b/sys/pci/if_dc.c
@@ -197,6 +197,7 @@ static struct dc_type *dc_devtype __P((device_t));
static int dc_newbuf __P((struct dc_softc *, int, struct mbuf *));
static int dc_encap __P((struct dc_softc *, struct mbuf *,
u_int32_t *));
+static int dc_coal __P((struct dc_softc *, struct mbuf **));
static void dc_pnic_rx_bug_war __P((struct dc_softc *, int));
static int dc_rx_resync __P((struct dc_softc *));
static void dc_rxeof __P((struct dc_softc *));
@@ -1454,7 +1455,7 @@ static int dc_attach(dev)
case DC_DEVICEID_DM9100:
case DC_DEVICEID_DM9102:
sc->dc_type = DC_TYPE_DM9102;
- sc->dc_flags |= DC_TX_USE_TX_INTR;
+ sc->dc_flags |= DC_TX_COALESCE|DC_TX_USE_TX_INTR;
sc->dc_flags |= DC_REDUCED_MII_POLL;
sc->dc_pmode = DC_PMODE_MII;
break;
@@ -2375,6 +2376,39 @@ static int dc_encap(sc, m_head, txidx)
}
/*
+ * Coalesce an mbuf chain into a single mbuf cluster buffer.
+ * Needed for some really badly behaved chips that just can't
+ * do scatter/gather correctly.
+ */
+static int dc_coal(sc, m_head)
+ struct dc_softc *sc;
+ struct mbuf **m_head;
+{
+ struct mbuf *m_new, *m;
+
+ m = *m_head;
+ MGETHDR(m_new, M_DONTWAIT, MT_DATA);
+ if (m_new == NULL) {
+ printf("dc%d: no memory for tx list", sc->dc_unit);
+ return(ENOBUFS);
+ }
+ if (m->m_pkthdr.len > MHLEN) {
+ MCLGET(m_new, M_DONTWAIT);
+ if (!(m_new->m_flags & M_EXT)) {
+ m_freem(m_new);
+ printf("dc%d: no memory for tx list", sc->dc_unit);
+ return(ENOBUFS);
+ }
+ }
+ m_copydata(m, 0, m->m_pkthdr.len, mtod(m_new, caddr_t));
+ m_new->m_pkthdr.len = m_new->m_len = m->m_pkthdr.len;
+ m_freem(m);
+ *m_head = m_new;
+
+ return(0);
+}
+
+/*
* Main transmit routine. To avoid having to do mbuf copies, we put pointers
* to the mbuf data regions directly in the transmit lists. We also save a
* copy of the pointers since the transmit list fragment pointers are
@@ -2403,6 +2437,14 @@ static void dc_start(ifp)
if (m_head == NULL)
break;
+ if (sc->dc_flags & DC_TX_COALESCE) {
+ if (dc_coal(sc, &m_head)) {
+ IF_PREPEND(&ifp->if_snd, m_head);
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+ }
+
if (dc_encap(sc, m_head, &idx)) {
IF_PREPEND(&ifp->if_snd, m_head);
ifp->if_flags |= IFF_OACTIVE;
OpenPOWER on IntegriCloud