summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>2000-10-20 12:57:46 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>2000-10-20 12:57:46 +0000
commit9142bf398b6de6ce6eaf8f04ffca47b55a3b49f1 (patch)
tree7161964615f3a58285e88f162c6bc5e35afedda6
parent2000-10-19 Joel Sherrill <joel@OARcorp.com> (diff)
downloadrtems-9142bf398b6de6ce6eaf8f04ffca47b55a3b49f1.tar.bz2
2000-10-20 Dmitry Kargapolov <dk@gentex.ru>
* ne2000/ne2000.c: Fix some errors in the driver. 1. There was no sufficient check of data in ethernet header. The code in ne_rx_daemon() was: inport_word (dport, len); ... len -= 4; ... if (len > 0) ne_read_data (sc, startaddr, len, p); Unfortunately, sometimes my NIC gave me too big len value, the result was memory override. To fix this, I added ethernet header data checking. 2. The way overrides were serviced was not good. It was complex but sometimes did not provide reliable continuing of NIC working. I had the situation of an endless loop in ne_check_status() after override processing. 3. There was conceptual error of porting. The old method of overrides curing was ported from the OS-s, where override-processing did start immediately. But RTEMS-version uses events, and cleaning of the overrides can start later. I selected the way of ne2000 programming that is used in freebsd kernel (v4.0). Because of both problems, incorrect data in header of raw packet and receiver override, it went through ne_reset() and fully reset the ne2000. So, in summary - added detecting of the incorrect data in ethernet header; - replaced handling of receiver overrides with new scheme, via resetting of NIC, this method is used also in case of invalid header detecting.
-rw-r--r--c/src/lib/libbsp/i386/pc386/ne2000/ne2000.c548
1 files changed, 385 insertions, 163 deletions
diff --git a/c/src/lib/libbsp/i386/pc386/ne2000/ne2000.c b/c/src/lib/libbsp/i386/pc386/ne2000/ne2000.c
index a65897763a..8e28bad5f3 100644
--- a/c/src/lib/libbsp/i386/pc386/ne2000/ne2000.c
+++ b/c/src/lib/libbsp/i386/pc386/ne2000/ne2000.c
@@ -23,12 +23,14 @@
* is any point to having more than two transmit buffers. However, the
* code does make it possible, by changing NE_TX_BUFS, although that
* would of course reduce the number of receive buffers.
- *
+ *
* I suspect that the wd80x3 driver would benefit slightly from copying
* the multiple transmit buffer code. However, I have no way to test
* that.
*/
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__
+
#include <bsp.h>
#include <wd80x3.h>
@@ -59,6 +61,7 @@
/* Define this to print debugging messages with printk. */
/* #define DEBUG_NE2000 */
+/* #define DEBUG_NE */
/* We expect to be able to read a complete packet into an mbuf. */
@@ -70,11 +73,11 @@
#define RO 0
/* Minimum size of Ethernet packet. */
-#define ET_MINLEN 60
+#define ET_MINLEN 60
/* The number of NE2000 devices supported by this driver. */
-#define NNEDRIVER 1
+#define NNEDRIVER 1
/* RTEMS event number used by the interrupt handler to signal the
driver task. This must not be any of the events used by the
@@ -186,10 +189,37 @@ struct ne_softc {
static struct ne_softc ne_softc[NNEDRIVER];
+/*
+ * receive ring descriptor
+ *
+ * The National Semiconductor DS8390 Network interface controller uses
+ * the following receive ring headers. The way this works is that the
+ * memory on the interface card is chopped up into 256 bytes blocks.
+ * A contiguous portion of those blocks are marked for receive packets
+ * by setting start and end block #'s in the NIC. For each packet that
+ * is put into the receive ring, one of these headers (4 bytes each) is
+ * tacked onto the front. The first byte is a copy of the receiver status
+ * register at the time the packet was received.
+ */
+struct ne_ring
+{
+ unsigned char rsr; /* receiver status */
+ unsigned char next; /* pointer to next packet */
+ unsigned short count; /* bytes in packet (length + 4) */
+};
+
/* Forward declarations to avoid warnings */
+static void ne_init_irq_handler (int irno);
static void ne_stop (struct ne_softc *sc);
+static void ne_stop_hardware (struct ne_softc *sc);
static void ne_init (void *arg);
+static void ne_init_hardware (struct ne_softc *sc);
+
+static void ne_reset(struct ne_softc *sc);
+#ifdef DEBUG_NE
+static void ne_dump(struct ne_softc *sc);
+#endif
/* Find the NE2000 device which is attached at a particular interrupt
vector. */
@@ -202,8 +232,8 @@ ne_device_for_irno (int irno)
for (i = 0; i < NNEDRIVER; ++i)
{
if (ne_softc[i].irno == irno
- && ne_softc[i].arpcom.ac_if.if_softc != NULL)
- return &ne_softc[i];
+ && ne_softc[i].arpcom.ac_if.if_softc != NULL)
+ return &ne_softc[i];
}
return NULL;
@@ -226,22 +256,31 @@ ne_read_data (struct ne_softc *sc, int addr, int len, unsigned char *p)
outport_byte (port + CMDR, MSK_PG0 | MSK_RRE | MSK_STA);
if (sc->byte_transfers)
- while (len > 0) {
- unsigned char d;
-
- inport_byte (dport, d);
+ {
+ unsigned char d;
+ while (len > 0)
+ {
+ inport_byte(dport, d);
*p++ = d;
len--;
}
+ }
else /* word transfers */
- while (len > 0) {
- unsigned short d;
-
- inport_word (dport, d);
+ {
+ unsigned short d;
+ while (len > 1)
+ {
+ inport_word(dport, d);
*p++ = d;
*p++ = d >> 8;
len -= 2;
}
+ if (len)
+ {
+ inport_word(dport, d);
+ *p++ = d;
+ }
+ }
outport_byte (port + ISR, MSK_RDC);
}
@@ -251,8 +290,9 @@ ne_read_data (struct ne_softc *sc, int addr, int len, unsigned char *p)
NE2000 interrupts have been disabled. */
static void
-ne_check_status (struct ne_softc *sc)
+ne_check_status (struct ne_softc *sc, int from_irq_handler)
{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
unsigned int port = sc->port;
unsigned char status;
@@ -263,66 +303,77 @@ ne_check_status (struct ne_softc *sc)
when interrupts are reenabled. (Based on the behaviour of the
Realtek 8019AS chip). */
- while (1) {
+ /* int count = 0; */
+ while (1)
+ {
inport_byte (port + ISR, status);
if (status == 0)
break;
+ /* ack */
+ outport_byte (port + ISR, status);
+
#ifdef DEBUG_NE2000
printk ("NE2000 status 0x%x (8259 enabled: %s; mask: %x)\n", status,
- i8259s_cache & (1 << sc->irno) ? "no" : "yes",
- i8259s_cache);
+ i8259s_cache & (1 << sc->irno) ? "no" : "yes",
+ i8259s_cache);
#endif
/* Check for incoming packet overwrite. */
- if (status & MSK_OVW) {
- unsigned char status2;
-
+ if (status & MSK_OVW)
+ {
+ ifp->if_timer = 0;
+#ifdef DEBUG_NE
+ printk("^");
+#endif
++sc->stats.overruns;
- outport_byte (port + CMDR, MSK_PG0 | MSK_STP | MSK_RD2);
- Wait_X_ms (2);
- outport_byte (port + RBCR0, 0);
- outport_byte (port + RBCR1, 0);
- inport_byte (port + ISR, status2);
- status |= status2 & (MSK_PTX | MSK_TXE);
- outport_byte (port + TCR, MSK_LOOP);
- outport_byte (port + CMDR, MSK_PG0 | MSK_STA | MSK_RD2);
- sc->overrun = 1;
- if ((status & (MSK_PTX | MSK_TXE)) == 0)
- sc->resend = 1;
- rtems_event_send (sc->rx_daemon_tid, INTERRUPT_EVENT);
+ ne_reset(sc);
+ /* Reenable device interrupts. */
+ if (from_irq_handler)
+ outport_byte(port + IMR, NE_INTERRUPTS);
+ return;
}
/* Check for transmitted packet. The transmit daemon may now be
able to send another packet to the device. */
- if ((status & (MSK_PTX | MSK_TXE)) != 0) {
+ if ((status & (MSK_PTX | MSK_TXE)) != 0)
+ {
+ ifp->if_timer = 0;
++sc->stats.tx_acks;
- outport_byte (port + ISR, status & (MSK_PTX | MSK_TXE));
--sc->inuse;
sc->transmitting = 0;
if (sc->inuse > 0 || (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) != 0)
- rtems_event_send (sc->tx_daemon_tid, START_TRANSMIT_EVENT);
+ rtems_event_send (sc->tx_daemon_tid, START_TRANSMIT_EVENT);
}
/* Check for received packet. */
- if ((status & (MSK_PRX | MSK_RXE)) != 0) {
+ if ((status & (MSK_PRX | MSK_RXE)) != 0)
+ {
++sc->stats.rx_acks;
- outport_byte (port + ISR, status & (MSK_PRX | MSK_RXE));
rtems_event_send (sc->rx_daemon_tid, INTERRUPT_EVENT);
}
/* Check for counter change. */
- if ((status & MSK_CNT) != 0) {
+ if ((status & MSK_CNT) != 0)
+ {
unsigned char add;
-
inport_byte (port + CNTR0, add);
sc->stats.rx_frame_errors += add;
inport_byte (port + CNTR1, add);
sc->stats.rx_crc_errors += add;
inport_byte (port + CNTR2, add);
sc->stats.rx_missed_errors += add;
- outport_byte (port + ISR, MSK_CNT);
}
+
+ break;
+ /* if (++count >= 1000)
+ {
+ printk("status: %x\n", status);
+ ne_reset(sc);
+ if (from_irq_handler)
+ outport_byte(port + IMR, NE_INTERRUPTS);
+ return;
+ } */
}
outport_byte (port + CMDR, MSK_PG0 | MSK_STA | MSK_RD2);
@@ -341,7 +392,10 @@ ne_interrupt_handler (rtems_vector_number v)
++sc->stats.interrupts;
- ne_check_status (sc);
+#ifdef DEBUG_NE
+ printk("!");
+#endif
+ ne_check_status(sc, 1);
}
/* Turn NE2000 interrupts on. */
@@ -351,8 +405,8 @@ ne_interrupt_on (const rtems_irq_connect_data *irq)
{
struct ne_softc *sc;
-#ifdef DEBUG_NE2000
- printk ("ne_interrupt_on\n");
+#ifdef DEBUG_NE
+ printk ("ne_interrupt_on()\n");
#endif
sc = ne_device_for_irno (irq->name);
if (sc != NULL)
@@ -366,8 +420,8 @@ ne_interrupt_off (const rtems_irq_connect_data *irq)
{
struct ne_softc *sc;
-#ifdef DEBUG_NE2000
- printk ("ne_interrupt_off\n");
+#ifdef DEBUG_NE
+ printk ("ne_interrupt_off()\n");
#endif
sc = ne_device_for_irno (irq->name);
if (sc != NULL)
@@ -389,108 +443,134 @@ ne_init_hardware (struct ne_softc *sc)
{
unsigned int port = sc->port;
int i;
- rtems_irq_connect_data irq;
#ifdef DEBUG_NE2000
- printk ("ne_init_hardware\n");
+ printk ("ne_init_hardware()\n");
#endif
/* Initialize registers. */
+ /* Set interface for page 0, Remote DMA complete, Stopped */
outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP);
- if (sc->byte_transfers) {
+ /* Set FIFO threshold to 8, No auto-init Remote DMA, byte order=80x86 */
+ /* byte-wide DMA xfers */
+ if (sc->byte_transfers)
outport_byte (port + DCR, MSK_FT10 | MSK_BMS);
- }
- else {
+ /* word-wide DMA xfers */
+ else
outport_byte (port + DCR, MSK_FT10 | MSK_BMS | MSK_WTS);
- }
+ /* Clear Remote Byte Count Registers */
outport_byte (port + RBCR0, 0);
outport_byte (port + RBCR1, 0);
+
+ /* For the moment, don't store incoming packets in memory. */
outport_byte (port + RCR, MSK_MON);
+
+ /* Place NIC in internal loopback mode */
outport_byte (port + TCR, MSK_LOOP);
- outport_byte (port + IMR, 0);
- outport_byte (port + ISR, 0xff);
- outport_byte (port + PSTOP, NE_STOP_PAGE);
+
+ /* Initialize transmit/receive (ring-buffer) Page Start */
+ outport_byte (port + TPSR, NE_FIRST_TX_PAGE);
outport_byte (port + PSTART, NE_FIRST_RX_PAGE);
+
+ /* Initialize Receiver (ring-buffer) Page Stop and Boundry */
+ outport_byte (port + PSTOP, NE_STOP_PAGE);
outport_byte (port + BNRY, NE_STOP_PAGE - 1);
- /* Set the Ethernet hardware address. */
+ /* Clear all interrupts */
+ outport_byte (port + ISR, 0xff);
+ /* Disable all interrupts */
+ outport_byte (port + IMR, 0);
+
+ /* Program Command Register for page 1 */
+ outport_byte (port + CMDR, MSK_PG1 | MSK_RD2 | MSK_STP);
- outport_byte (port + CMDR, MSK_PG1 | MSK_RD2);
+ /* Set the Ethernet hardware address. */
for (i = 0; i < ETHER_ADDR_LEN; ++i)
outport_byte (port + PAR + i, sc->arpcom.ac_enaddr[i]);
-#ifdef DEBUG_NE2000
- printk ("Using ethernet address: ");
- for (i = 0; i < ETHER_ADDR_LEN; ++i)
- printk("%x ",sc->arpcom.ac_enaddr[i]);
- printk ("\n");
-#endif
+ /* Set Current Page pointer to next_packet */
+ outport_byte (port + CURR, NE_FIRST_RX_PAGE);
/* Clear the multicast address. */
for (i = 0; i < MARsize; ++i)
outport_byte (port + MAR + i, 0);
- outport_byte (port + CURR, NE_FIRST_RX_PAGE);
+ /* Set page 0 registers */
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP);
- outport_byte (port + CMDR, MSK_PG0 | MSK_RD2);
+ /* accept broadcast */
+ outport_byte (port + RCR, (sc->accept_broadcasts ? MSK_AB : 0));
- /* Put the device on line. */
- outport_byte (port + CMDR, MSK_PG0 | MSK_STA | MSK_RD2);
+ /* Start interface */
+ outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STA);
- /* Set up interrupts. */
+ /* Take interface out of loopback */
+ outport_byte (port + TCR, 0);
+}
+
+/* Set up interrupts.
+*/
+static void
+ne_init_irq_handler(int irno)
+{
+ rtems_irq_connect_data irq;
- irq.name = sc->irno;
- irq.hdl = ne_interrupt_handler;
+#ifdef DEBUG_NE
+ printk("ne_init_irq_handler(%d)\n", irno);
+#endif
+ irq.name = irno;
+ irq.hdl = (rtems_irq_hdl)ne_interrupt_handler;
irq.on = ne_interrupt_on;
irq.off = ne_interrupt_off;
irq.isOn = ne_interrupt_is_on;
- if (! BSP_install_rtems_irq_handler (&irq))
- rtems_panic ("Can't attach NE interrupt handler for irq %d.\n",
- sc->irno);
-
- /* Prepare to receive packets. */
-
- outport_byte (port + TCR, 0);
- outport_byte (port + RCR, (sc->accept_broadcasts ? MSK_AB : 0));
+ if (!BSP_install_rtems_irq_handler (&irq))
+ rtems_panic ("Can't attach NE interrupt handler for irq %d\n", irno);
}
/* The NE2000 packet receive daemon. This task is started when the
NE2000 driver is initialized. */
+#ifdef DEBUG_NE
+static int ccc = 0; /* experinent! */
+#endif
+
static void
ne_rx_daemon (void *arg)
{
struct ne_softc *sc = (struct ne_softc *) arg;
struct ifnet *ifp = &sc->arpcom.ac_if;
unsigned int port = sc->port;
- unsigned int dport = port + DATAPORT;
- while (1) {
+ while (1)
+ {
rtems_event_set events;
/* Wait for the interrupt handler to tell us that there is a
packet ready to receive. */
rtems_bsdnet_event_receive (INTERRUPT_EVENT,
- RTEMS_WAIT | RTEMS_EVENT_ANY,
- RTEMS_NO_TIMEOUT,
- &events);
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
/* Don't let the device interrupt us now. */
outport_byte (port + IMR, 0);
- while (1) {
+ while (1)
+ {
unsigned char startpage, currpage;
- unsigned short statnext, len;
- int next;
+ unsigned short len;
+ unsigned char next, stat, cnt1, cnt2;
struct mbuf *m;
unsigned char *p;
int startaddr;
int toend;
struct ether_header *eh;
+ struct ne_ring hdr; /* ring buffer header */
+ int reset;
inport_byte (port + BNRY, startpage);
@@ -500,75 +580,109 @@ ne_rx_daemon (void *arg)
++startpage;
if (startpage >= NE_STOP_PAGE)
- startpage = NE_FIRST_RX_PAGE;
+ startpage = NE_FIRST_RX_PAGE;
if (startpage == currpage)
- break;
+ break;
#ifdef DEBUG_NE2000
printk ("ne_rx_daemon: start page %x; current page %x\n",
- startpage, currpage);
+ startpage, currpage);
#endif
- /* Read the buffer header. This is 1 byte receive status, 1
- byte page of next buffer, 2 bytes length. */
- outport_byte (port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STA);
- outport_byte (port + RBCR0, 4);
- outport_byte (port + RBCR1, 0);
- outport_byte (port + RSAR0, 0);
- outport_byte (port + RSAR1, startpage);
- outport_byte (port + CMDR, MSK_PG0 | MSK_RRE | MSK_STA);
-
- if (sc->byte_transfers) {
- unsigned char data;
-
- inport_byte (dport, data); /* Throw away status */
- inport_byte (dport, data);
- next = data;
+ reset = 0;
- inport_byte (dport, data);
- len = data;
- inport_byte (dport, data);
- len |= data << 8;
+ /* Read the buffer header */
+ startaddr = startpage * NE_PAGE_SIZE;
+ ne_read_data(sc, startaddr, sizeof(hdr), (unsigned char *)&hdr);
+ next = hdr.next;
+ if (next >= NE_STOP_PAGE)
+ next = NE_FIRST_RX_PAGE;
+
+ /* check packet length */
+ len = hdr.count;
+ if (currpage < startpage)
+ cnt1 = currpage + (NE_STOP_PAGE - NE_FIRST_RX_PAGE) - startpage;
+ else
+ cnt1 = currpage - startpage;
+ cnt2 = len / NE_PAGE_SIZE;
+ if (len % NE_PAGE_SIZE)
+ cnt2++;
+ if (cnt1 < cnt2)
+ {
+#ifdef DEBUG_NE
+ printk("(%x<%x:%x)", cnt1, cnt2, len);
+/*
+ printk("start page 0x%x; current page 0x%x\n",
+ startpage, currpage);
+ printk("cnt1 < cnt2 (0x%x, 0x%x); len 0x%x\n",
+ cnt1, cnt2, len);
+*/
+#endif
+ reset = 1;
}
- else { /* Word transfers */
- inport_word (dport, statnext);
- inport_word (dport, len);
-
- next = statnext >> 8;
+ if (len > (ETHER_MAX_LEN - ETHER_CRC_LEN + sizeof(struct ne_ring)) ||
+ len < (ETHER_MIN_LEN - ETHER_CRC_LEN + sizeof(struct ne_ring)) ||
+ len > MCLBYTES)
+ {
+#ifdef DEBUG_NE
+ printk("(%x)", len);
+/*
+ printk("start page 0x%x; current page 0x%x\n",
+ startpage, currpage);
+ printk("len out of range: 0x%x\n", len);
+ printk("stat: 0x%x, next: 0x%x\n", hdr.rsr, hdr.next);
+*/
+#endif
+ reset = 1;
+ }
+#ifdef DEBUG_NE
+ if (++ccc == 100)
+ { ccc = 0; reset = 1;
+ printk("T");
}
+#endif
- outport_byte (port + ISR, MSK_RDC);
+ /* reset interface */
+ if (reset)
+ {
+ ne_reset(sc);
+ goto Next;
+ }
- if (next >= NE_STOP_PAGE)
- next = NE_FIRST_RX_PAGE;
+ stat = hdr.rsr;
/* The first four bytes of the length are the buffer header. */
- len -= 4;
- startaddr = startpage * NE_PAGE_SIZE + 4;
+ len -= sizeof(struct ne_ring);
+ startaddr += sizeof(struct ne_ring);
MGETHDR (m, M_WAIT, MT_DATA);
MCLGET (m, M_WAIT);
m->m_pkthdr.rcvif = ifp;
p = mtod (m, unsigned char *);
- m->m_len = m->m_pkthdr.len = len - sizeof (struct ether_header);
+ m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header);
toend = NE_STOP_PAGE * NE_PAGE_SIZE - startaddr;
- if (toend < len) {
- ne_read_data (sc, startaddr, toend, p);
- p += toend;
- len -= toend;
- startaddr = NE_FIRST_RX_PAGE * NE_PAGE_SIZE;
+ if (toend < len)
+ {
+ ne_read_data (sc, startaddr, toend, p);
+ p += toend;
+ len -= toend;
+ startaddr = NE_FIRST_RX_PAGE * NE_PAGE_SIZE;
}
if (len > 0)
- ne_read_data (sc, startaddr, len, p);
+ ne_read_data (sc, startaddr, len, p);
eh = mtod (m, struct ether_header *);
m->m_data += sizeof (struct ether_header);
- ether_input (ifp, eh, m);
+#ifdef DEBUG_NE
+ /* printk("[r%d]", hdr.count - sizeof(hdr)); */
+ printk("<");
+#endif
+ ether_input (ifp, eh, m);
++sc->stats.rx_packets;
outport_byte (port + BNRY, next - 1);
@@ -578,11 +692,12 @@ ne_rx_daemon (void *arg)
outport_byte (port + ISR, MSK_OVW);
outport_byte (port + TCR, 0);
if (sc->resend)
- outport_byte (port + CMDR, MSK_PG0 | MSK_TXP | MSK_RD2 | MSK_STA);
+ outport_byte (port + CMDR, MSK_PG0 | MSK_TXP | MSK_RD2 | MSK_STA);
sc->resend = 0;
sc->overrun = 0;
}
+ Next:
/* Reenable device interrupts. */
outport_byte (port + IMR, NE_INTERRUPTS);
}
@@ -599,6 +714,7 @@ ne_loadpacket (struct ne_softc *sc, struct mbuf *m)
int leftover;
unsigned char leftover_data;
int timeout;
+ int send_cnt = 0;
#ifdef DEBUG_NE2000
printk ("Uploading NE2000 packet\n");
@@ -616,7 +732,7 @@ ne_loadpacket (struct ne_softc *sc, struct mbuf *m)
/* Tell the device which address we want to write to. */
outport_byte (port + RSAR0, 0);
outport_byte (port + RSAR1,
- NE_FIRST_TX_PAGE + (sc->nextavail * NE_TX_PAGES));
+ NE_FIRST_TX_PAGE + (sc->nextavail * NE_TX_PAGES));
/* Set up the write. */
outport_byte (port + CMDR, MSK_PG0 | MSK_RWR | MSK_STA);
@@ -645,34 +761,43 @@ ne_loadpacket (struct ne_softc *sc, struct mbuf *m)
next = *data++;
--len;
outport_word (dport, leftover_data | (next << 8));
+ send_cnt += 2;
leftover = 0;
}
- /* If using byte transfers, len always ends up as zero so
+ /* If using byte transfers, len always ends up as zero so
there are no leftovers. */
if (sc->byte_transfers)
while (len > 0) {
- outport_byte (dport, *data++);
- len--;
+ outport_byte (dport, *data++);
+ len--;
}
else
while (len > 1) {
- outport_word (dport, data[0] | (data[1] << 8));
- data += 2;
- len -= 2;
+ outport_word (dport, data[0] | (data[1] << 8));
+ data += 2;
+ len -= 2;
+ send_cnt += 2;
}
if (len > 0)
{
- leftover = 1;
- leftover_data = *data++;
+ leftover = 1;
+ leftover_data = *data++;
}
}
if (leftover)
+ {
outport_word (dport, leftover_data);
+ send_cnt += 2;
+ }
+#ifdef DEBUG_NE
+ /* printk("{l%d|%d}", send_cnt, sc->nextavail); */
+ printk("v");
+#endif
m_freem (mhold);
/* Wait for the device to complete accepting the data, with a
@@ -685,13 +810,13 @@ ne_loadpacket (struct ne_softc *sc, struct mbuf *m)
#ifdef DEBUG_NE2000
if ((status &~ MSK_RDC) != 0)
- printk ("Status 0x%x while waiting for acknowledgement of uploaded packet\n",
- status);
+ printk ("Status 0x%x while waiting for acknowledgement of uploaded packet\n",
+ status);
#endif
if ((status & MSK_RDC) != 0) {
- outport_byte (port + ISR, MSK_RDC);
- break;
+ outport_byte (port + ISR, MSK_RDC);
+ break;
}
}
@@ -709,6 +834,7 @@ ne_loadpacket (struct ne_softc *sc, struct mbuf *m)
static void
ne_transmit (struct ne_softc *sc)
{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
unsigned int port = sc->port;
int len;
@@ -726,11 +852,18 @@ ne_transmit (struct ne_softc *sc)
outport_byte (port + CMDR, MSK_PG0 | MSK_TXP | MSK_RD2 | MSK_STA);
+#ifdef DEBUG_NE
+ /* printk("{s%d|%d}", len, sc->nextsend); */
+ printk(">");
+#endif
++sc->nextsend;
if (sc->nextsend == NE_TX_BUFS)
sc->nextsend = 0;
++sc->stats.tx_packets;
+
+ /* set watchdog timer */
+ ifp->if_timer = 2;
}
/* The NE2000 packet transmit daemon. This task is started when the
@@ -749,9 +882,9 @@ ne_tx_daemon (void *arg)
/* Wait for a packet to be ready for sending, or for there to be
room for another packet in the device memory. */
rtems_bsdnet_event_receive (START_TRANSMIT_EVENT,
- RTEMS_EVENT_ANY | RTEMS_WAIT,
- RTEMS_NO_TIMEOUT,
- &events);
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &events);
#ifdef DEBUG_NE2000
printk ("ne_tx_daemon\n");
@@ -771,32 +904,32 @@ ne_tx_daemon (void *arg)
/* If the device is not transmitting a packet, and we have
uploaded a packet, tell the device to transmit it. */
if (! sc->transmitting && sc->inuse > 0) {
- sc->transmitting = 1;
- ne_transmit (sc);
+ sc->transmitting = 1;
+ ne_transmit (sc);
}
/* If we don't have any more buffers to send, quit now. */
if (ifp->if_snd.ifq_head == NULL) {
- ifp->if_flags &= ~IFF_OACTIVE;
- break;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ break;
}
/* Allocate a buffer to load data into. If there are none
available, quit until a buffer has been transmitted. */
if (sc->inuse >= NE_TX_BUFS)
- break;
+ break;
++sc->inuse;
IF_DEQUEUE (&ifp->if_snd, m);
if (m == NULL)
- panic ("ne_tx_daemon");
+ panic ("ne_tx_daemon");
ne_loadpacket (sc, m);
/* Check the device status. It may have finished transmitting
the last packet. */
- ne_check_status (sc);
+ ne_check_status(sc, 0);
}
/* Reenable device interrupts. */
@@ -811,6 +944,9 @@ ne_start (struct ifnet *ifp)
{
struct ne_softc *sc = ifp->if_softc;
+#ifdef DEBUG_NE
+ printk("S");
+#endif
/* Tell the transmit daemon to wake up and send a packet. */
rtems_event_send (sc->tx_daemon_tid, START_TRANSMIT_EVENT);
ifp->if_flags |= IFF_OACTIVE;
@@ -824,7 +960,14 @@ ne_init (void *arg)
struct ne_softc *sc = (struct ne_softc *) arg;
struct ifnet *ifp = &sc->arpcom.ac_if;
- if (sc->tx_daemon_tid == 0) {
+#ifdef DEBUG_NE
+ printk("ne_init()\n");
+ ne_dump(sc);
+#endif
+
+ /* only once... */
+ if (sc->tx_daemon_tid == 0)
+ {
sc->inuse = 0;
sc->nextavail = 0;
sc->nextsend = 0;
@@ -834,6 +977,9 @@ ne_init (void *arg)
sc->tx_daemon_tid = rtems_bsdnet_newproc ("SCtx", 4096, ne_tx_daemon, sc);
sc->rx_daemon_tid = rtems_bsdnet_newproc ("SCrx", 4096, ne_rx_daemon, sc);
+
+ /* install rtems irq handler */
+ ne_init_irq_handler(sc->irno);
}
ifp->if_flags |= IFF_RUNNING;
@@ -844,11 +990,24 @@ ne_init (void *arg)
static void
ne_stop (struct ne_softc *sc)
{
+ sc->arpcom.ac_if.if_flags &= ~IFF_RUNNING;
+
+ ne_stop_hardware(sc);
+
+ sc->inuse = 0;
+ sc->nextavail = 0;
+ sc->nextsend = 0;
+ sc->transmitting = 0;
+ sc->overrun = 0;
+ sc->resend = 0;
+}
+
+static void
+ne_stop_hardware (struct ne_softc *sc)
+{
unsigned int port = sc->port;
int i;
- sc->arpcom.ac_if.if_flags &= ~IFF_RUNNING;
-
/* Stop everything. */
outport_byte (port + CMDR, MSK_STP | MSK_RD2);
@@ -859,15 +1018,43 @@ ne_stop (struct ne_softc *sc)
inport_byte (port + ISR, status);
if ((status & MSK_RST) != 0)
- break;
+ break;
}
+}
- sc->inuse = 0;
- sc->nextavail = 0;
- sc->nextsend = 0;
- sc->transmitting = 0;
+/* reinitializing interface
+*/
+static void
+ne_reset(struct ne_softc *sc)
+{
+ ne_stop(sc);
+ ne_init_hardware(sc);
+ sc->arpcom.ac_if.if_flags |= IFF_RUNNING;
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+#ifdef DEBUG_NE
+ printk("*");
+#endif
}
+#ifdef DEBUG_NE
+/* show anything about ne
+*/
+static void
+ne_dump(struct ne_softc *sc)
+{
+ int i;
+ printk("\nne configuration:\n");
+ printk("ethernet addr:");
+ for (i=0; i<ETHER_ADDR_LEN; i++)
+ printk(" %x", sc->arpcom.ac_enaddr[i]);
+ printk("\n");
+ printk("irq = %d\n", sc->irno);
+ printk("port = 0x%x\n", sc->port);
+ printk("accept_broadcasts = %d\n", sc->accept_broadcasts);
+ printk("byte_transfers = %d\n", sc->byte_transfers);
+}
+#endif
+
/* Show NE2000 interface statistics. */
static void
@@ -932,16 +1119,41 @@ ne_ioctl (struct ifnet *ifp, int command, caddr_t data)
return error;
}
+/*
+ * Device timeout/watchdog routine. Entered if the device neglects to
+ * generate an interrupt after a transmit has been started on it.
+ */
+static void
+ne_watchdog(struct ifnet *ifp)
+{
+ struct ne_softc *sc = ifp->if_softc;
+
+ printk("ne2000: device timeout\n");
+ ifp->if_oerrors++;
+
+ ne_reset(sc);
+}
+
+static void
+print_byte(unsigned char b)
+{
+ printk("%x%x", b >> 4, b & 0x0f);
+}
+
/* Attach an NE2000 driver to the system. */
int
-rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *config)
+rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach)
{
int i;
struct ne_softc *sc;
struct ifnet *ifp;
int mtu;
+ /* dettach ... */
+ if (!attach)
+ return 0;
+
/* Find a free driver. */
sc = NULL;
for (i = 0; i < NNEDRIVER; ++i) {
@@ -959,7 +1171,7 @@ rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *config)
memset (sc, 0, sizeof *sc);
/* Check whether we do byte-wide or word-wide transfers. */
-
+
#ifdef NE2000_BYTE_TRANSFERS
sc->byte_transfers = TRUE;
#else
@@ -991,7 +1203,7 @@ rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *config)
if (config->hardware_address != NULL)
memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
- ETHER_ADDR_LEN);
+ ETHER_ADDR_LEN);
else
{
unsigned char prom[16];
@@ -1002,10 +1214,10 @@ rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *config)
outport_byte (sc->port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP);
if (sc->byte_transfers) {
- outport_byte (sc->port + DCR, MSK_FT10 | MSK_BMS);
+ outport_byte (sc->port + DCR, MSK_FT10 | MSK_BMS);
}
else {
- outport_byte (sc->port + DCR, MSK_FT10 | MSK_BMS | MSK_WTS);
+ outport_byte (sc->port + DCR, MSK_FT10 | MSK_BMS | MSK_WTS);
}
outport_byte (sc->port + RBCR0, 0);
@@ -1020,7 +1232,7 @@ rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *config)
outport_byte (sc->port + CMDR, MSK_PG0 | MSK_RD2 | MSK_STP);
for (ia = 0; ia < ETHER_ADDR_LEN; ++ia)
- sc->arpcom.ac_enaddr[ia] = prom[ia * 2];
+ sc->arpcom.ac_enaddr[ia] = prom[ia * 2];
}
/* Set up the network interface. */
@@ -1031,6 +1243,7 @@ rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *config)
ifp->if_mtu = mtu;
ifp->if_init = ne_init;
ifp->if_ioctl = ne_ioctl;
+ ifp->if_watchdog = ne_watchdog;
ifp->if_start = ne_start;
ifp->if_output = ether_output;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
@@ -1042,5 +1255,14 @@ rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *config)
if_attach (ifp);
ether_ifattach (ifp);
+ printk("network device '%s' <", config->name);
+ print_byte(sc->arpcom.ac_enaddr[0]);
+ for (i=1; i<ETHER_ADDR_LEN; i++)
+ { printk(":");
+ print_byte(sc->arpcom.ac_enaddr[i]);
+ }
+ printk("> initialized on port 0x%x, irq %d\n", sc->port, sc->irno);
+
return 1;
}
+