aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/dc/if_dc.c
diff options
context:
space:
mode:
authorBill Paul <wpaul@FreeBSD.org>2000-01-12 22:24:05 +0000
committerBill Paul <wpaul@FreeBSD.org>2000-01-12 22:24:05 +0000
commitfda39fd069929f21201ac095bc26faa561d5061d (patch)
tree5fd7ab0279a355af8d740ab490e7aba974036092 /sys/dev/dc/if_dc.c
parentae2a77e2bf77b76ccc7fa1228351b51265adcea2 (diff)
downloadsrc-fda39fd069929f21201ac095bc26faa561d5061d.tar.gz
src-fda39fd069929f21201ac095bc26faa561d5061d.zip
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
Notes
Notes: svn path=/head/; revision=55866
Diffstat (limited to 'sys/dev/dc/if_dc.c')
-rw-r--r--sys/dev/dc/if_dc.c44
1 files changed, 43 insertions, 1 deletions
diff --git a/sys/dev/dc/if_dc.c b/sys/dev/dc/if_dc.c
index 758554f2a970..b81ce58b0b02 100644
--- a/sys/dev/dc/if_dc.c
+++ b/sys/dev/dc/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;