diff options
author | Bill Paul <wpaul@FreeBSD.org> | 2000-01-12 22:24:05 +0000 |
---|---|---|
committer | Bill Paul <wpaul@FreeBSD.org> | 2000-01-12 22:24:05 +0000 |
commit | fda39fd069929f21201ac095bc26faa561d5061d (patch) | |
tree | 5fd7ab0279a355af8d740ab490e7aba974036092 /sys/dev/dc/if_dc.c | |
parent | ae2a77e2bf77b76ccc7fa1228351b51265adcea2 (diff) | |
download | src-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.c | 44 |
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; |