The code I wrote to write mbufs out using PIO had a bug in the handling

of mb_offset given the right sequence of 1 and 0 byte mbufs. This bug
was discovered by John Hood who also provided this fix -  which is a
rewrite of the routine (and is easier to understand than the code I wrote).

Submitted by:	John Hood <cgull@smoke.marlboro.vt.us>
This commit is contained in:
dg 1994-10-14 11:56:36 +00:00
parent ee66141a00
commit 786cb986e2
2 changed files with 110 additions and 104 deletions

View File

@ -13,7 +13,7 @@
* the SMC Elite Ultra (8216), the 3Com 3c503, the NE1000 and NE2000,
* and a variety of similar clones.
*
* $Id: if_ed.c,v 1.48 1994/09/16 13:33:40 davidg Exp $
* $Id: if_ed.c,v 1.49 1994/10/08 09:24:20 davidg Exp $
*/
#include "ed.h"
@ -2223,9 +2223,8 @@ ed_pio_write_mbufs(sc, m, dst)
struct mbuf *m;
unsigned short dst;
{
unsigned short len, mb_offset;
unsigned short len;
struct mbuf *mp;
unsigned char residual[2];
int maxwait = 100; /* about 120us */
/* First, count up the total number of bytes to copy */
@ -2249,57 +2248,61 @@ ed_pio_write_mbufs(sc, m, dst)
/* set remote DMA write */
outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA);
mb_offset = 0;
/*
* Transfer the mbuf chain to the NIC memory. The following code isn't
* too pretty. The problem is that we can only transfer words to the
* board, and if an mbuf has an odd number of bytes in it, this is a
* problem. It's not a simple matter of just removing a byte from the
* next mbuf (adjusting data++ and len--) because this will hose-over
* the mbuf chain which might be needed later for BPF. Instead, we
* maintain an offset (mb_offset) which let's us skip over the first
* byte in the following mbuf.
*/
while (m) {
if (m->m_len - mb_offset) {
if (sc->isa16bit) {
if ((m->m_len - mb_offset) > 1)
outsw(sc->asic_addr + ED_NOVELL_DATA,
mtod(m, caddr_t) + mb_offset,
(m->m_len - mb_offset) / 2);
/*
* if odd number of bytes, get the odd byte
* from the next mbuf with data
*/
if ((m->m_len - mb_offset) & 1) {
/* first the last byte in current mbuf */
residual[0] = *(mtod(m, caddr_t) +
m->m_len - 1);
/* advance past any empty mbufs */
while (m->m_next && (m->m_next->m_len == 0))
m = m->m_next;
if (m->m_next) {
/*
* remove first byte in next
* mbuf
*/
residual[1] = *(mtod(m->m_next, caddr_t));
mb_offset = 1;
}
outw(sc->asic_addr + ED_NOVELL_DATA,
*((unsigned short *) residual));
} else
mb_offset = 0;
} else
outsb(sc->asic_addr + ED_NOVELL_DATA, m->m_data, m->m_len);
/*
* Transfer the mbuf chain to the NIC memory.
* 16-bit cards require that data be transferred as words, and only words.
* So that case requires some extra code to patch over odd-length mbufs.
*/
if (!sc->isa16bit) {
/* NE1000s are easy */
while (m) {
if (m->m_len) {
outsb(sc->asic_addr + ED_NOVELL_DATA,
m->m_data, m->m_len);
}
m = m->m_next;
}
m = m->m_next;
} else {
/* NE2000s are a pain */
unsigned char *data;
int len, wantbyte;
unsigned char savebyte[2];
wantbyte = 0;
while (m) {
data = mtod(m, caddr_t);
len = m->m_len;
if (len) {
/* finish the last word */
if (wantbyte) {
savebyte[1] = *data;
outw(sc->asic_addr + ED_NOVELL_DATA,
*((unsigned short *) savebyte));
data++;
len--;
wantbyte = 0;
}
/* output contiguous words */
if (len > 1) {
outsw(sc->asic_addr + ED_NOVELL_DATA,
data, len >> 1);
data += len & ~1;
len &= 1;
}
/* save last byte, if necessary */
if (len == 1) {
savebyte[0] = *data;
wantbyte = 1;
}
}
m = m->m_next;
}
/* spit last byte */
if (wantbyte)
outw(sc->asic_addr + ED_NOVELL_DATA,
*((unsigned short *) savebyte));
}
/*

View File

@ -13,7 +13,7 @@
* the SMC Elite Ultra (8216), the 3Com 3c503, the NE1000 and NE2000,
* and a variety of similar clones.
*
* $Id: if_ed.c,v 1.48 1994/09/16 13:33:40 davidg Exp $
* $Id: if_ed.c,v 1.49 1994/10/08 09:24:20 davidg Exp $
*/
#include "ed.h"
@ -2223,9 +2223,8 @@ ed_pio_write_mbufs(sc, m, dst)
struct mbuf *m;
unsigned short dst;
{
unsigned short len, mb_offset;
unsigned short len;
struct mbuf *mp;
unsigned char residual[2];
int maxwait = 100; /* about 120us */
/* First, count up the total number of bytes to copy */
@ -2249,57 +2248,61 @@ ed_pio_write_mbufs(sc, m, dst)
/* set remote DMA write */
outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA);
mb_offset = 0;
/*
* Transfer the mbuf chain to the NIC memory. The following code isn't
* too pretty. The problem is that we can only transfer words to the
* board, and if an mbuf has an odd number of bytes in it, this is a
* problem. It's not a simple matter of just removing a byte from the
* next mbuf (adjusting data++ and len--) because this will hose-over
* the mbuf chain which might be needed later for BPF. Instead, we
* maintain an offset (mb_offset) which let's us skip over the first
* byte in the following mbuf.
*/
while (m) {
if (m->m_len - mb_offset) {
if (sc->isa16bit) {
if ((m->m_len - mb_offset) > 1)
outsw(sc->asic_addr + ED_NOVELL_DATA,
mtod(m, caddr_t) + mb_offset,
(m->m_len - mb_offset) / 2);
/*
* if odd number of bytes, get the odd byte
* from the next mbuf with data
*/
if ((m->m_len - mb_offset) & 1) {
/* first the last byte in current mbuf */
residual[0] = *(mtod(m, caddr_t) +
m->m_len - 1);
/* advance past any empty mbufs */
while (m->m_next && (m->m_next->m_len == 0))
m = m->m_next;
if (m->m_next) {
/*
* remove first byte in next
* mbuf
*/
residual[1] = *(mtod(m->m_next, caddr_t));
mb_offset = 1;
}
outw(sc->asic_addr + ED_NOVELL_DATA,
*((unsigned short *) residual));
} else
mb_offset = 0;
} else
outsb(sc->asic_addr + ED_NOVELL_DATA, m->m_data, m->m_len);
/*
* Transfer the mbuf chain to the NIC memory.
* 16-bit cards require that data be transferred as words, and only words.
* So that case requires some extra code to patch over odd-length mbufs.
*/
if (!sc->isa16bit) {
/* NE1000s are easy */
while (m) {
if (m->m_len) {
outsb(sc->asic_addr + ED_NOVELL_DATA,
m->m_data, m->m_len);
}
m = m->m_next;
}
m = m->m_next;
} else {
/* NE2000s are a pain */
unsigned char *data;
int len, wantbyte;
unsigned char savebyte[2];
wantbyte = 0;
while (m) {
data = mtod(m, caddr_t);
len = m->m_len;
if (len) {
/* finish the last word */
if (wantbyte) {
savebyte[1] = *data;
outw(sc->asic_addr + ED_NOVELL_DATA,
*((unsigned short *) savebyte));
data++;
len--;
wantbyte = 0;
}
/* output contiguous words */
if (len > 1) {
outsw(sc->asic_addr + ED_NOVELL_DATA,
data, len >> 1);
data += len & ~1;
len &= 1;
}
/* save last byte, if necessary */
if (len == 1) {
savebyte[0] = *data;
wantbyte = 1;
}
}
m = m->m_next;
}
/* spit last byte */
if (wantbyte)
outw(sc->asic_addr + ED_NOVELL_DATA,
*((unsigned short *) savebyte));
}
/*