summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/i386/pc386/wd8003
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>1999-02-18 21:09:25 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>1999-02-18 21:09:25 +0000
commitc0438adde978a8b66e82ce9509e338dc1d714ccc (patch)
tree88ac871282c0274aaa0aa96c468098734c375c9f /c/src/lib/libbsp/i386/pc386/wd8003
parentPatch from Emmanuel Raguet <raguet@crf.canon.fr>. Missed adding this file (diff)
downloadrtems-c0438adde978a8b66e82ce9509e338dc1d714ccc.tar.bz2
Renamed network to wd8003.
Diffstat (limited to 'c/src/lib/libbsp/i386/pc386/wd8003')
-rw-r--r--c/src/lib/libbsp/i386/pc386/wd8003/Makefile.in57
-rw-r--r--c/src/lib/libbsp/i386/pc386/wd8003/wd8003.c657
2 files changed, 714 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/i386/pc386/wd8003/Makefile.in b/c/src/lib/libbsp/i386/pc386/wd8003/Makefile.in
new file mode 100644
index 0000000000..b02e724894
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/wd8003/Makefile.in
@@ -0,0 +1,57 @@
+#
+# $Id$
+#
+
+@SET_MAKE@
+srcdir = @srcdir@
+VPATH = @srcdir@
+RTEMS_ROOT = @top_srcdir@
+PROJECT_ROOT = @PROJECT_ROOT@
+
+INSTALL = @INSTALL@
+
+PGM=${ARCH}/wd8003.rel
+
+# C source names, if any, go here -- minus the .c
+C_PIECES=wd8003
+C_FILES=$(C_PIECES:%=%.c)
+C_O_FILES=$(C_PIECES:%=${ARCH}/%.o)
+
+H_FILES=
+
+SRCS=$(C_FILES) $(H_FILES)
+OBJS=$(C_O_FILES)
+
+include $(RTEMS_ROOT)/make/custom/$(RTEMS_BSP).cfg
+include $(RTEMS_ROOT)/make/leaf.cfg
+
+#
+# (OPTIONAL) Add local stuff here using +=
+#
+
+DEFINES += -D_COMPILING_BSD_KERNEL_ -DKERNEL -DINET -DNFS \
+ -DDIAGNOSTIC -DBOOTP_COMPAT
+CPPFLAGS +=
+CFLAGS +=
+
+LD_PATHS +=
+LD_LIBS +=
+LDFLAGS +=
+
+#
+# Add your list of files to delete here. The config files
+# already know how to delete some stuff, so you may want
+# to just run 'make clean' first to see what gets missed.
+# 'make clobber' already includes 'make clean'
+#
+
+CLEAN_ADDITIONS +=
+CLOBBER_ADDITIONS +=
+
+${PGM}: ${SRCS} ${OBJS}
+ $(make-rel)
+
+all: ${ARCH} $(SRCS) $(PGM)
+
+# the .rel file built here will be put into libbsp.a by ../wrapup/Makefile
+install: all
diff --git a/c/src/lib/libbsp/i386/pc386/wd8003/wd8003.c b/c/src/lib/libbsp/i386/pc386/wd8003/wd8003.c
new file mode 100644
index 0000000000..e897435f7a
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/wd8003/wd8003.c
@@ -0,0 +1,657 @@
+/*
+ * RTEMS driver for WD800x
+ *
+ * Based on the 68360 Network Driver by:
+ * W. Eric Norum
+ * Saskatchewan Accelerator Laboratory
+ * University of Saskatchewan
+ * Saskatoon, Saskatchewan, CANADA
+ * eric@skatter.usask.ca
+ *
+ * $Id$
+ */
+
+#include <bsp.h>
+#include <wd80x3.h>
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <irq.h>
+
+#define ET_MINLEN 60 /* minimum message length */
+
+/*
+ * Number of WDs supported by this driver
+ */
+#define NWDDRIVER 1
+
+/*
+ * Default number of buffer descriptors set aside for this driver.
+ * The number of transmit buffer descriptors has to be quite large
+ * since a single frame often uses four or more buffer descriptors.
+ */
+#define RX_BUF_COUNT 15
+#define TX_BUF_COUNT 4
+#define TX_BD_PER_BUF 4
+
+/*
+ * RTEMS event used by interrupt handler to signal driver tasks.
+ * This must not be any of the events used by the network task synchronization.
+ */
+#define INTERRUPT_EVENT RTEMS_EVENT_1
+
+/*
+ * RTEMS event used to start transmit daemon.
+ * This must not be the same as INTERRUPT_EVENT.
+ */
+#define START_TRANSMIT_EVENT RTEMS_EVENT_2
+
+/*
+ * Receive buffer size -- Allow for a full ethernet packet including CRC
+ */
+#define RBUF_SIZE 1520
+
+#if (MCLBYTES < RBUF_SIZE)
+# error "Driver must have MCLBYTES > RBUF_SIZE"
+#endif
+
+/*
+ * Per-device data
+ */
+struct wd_softc {
+ struct arpcom arpcom;
+ rtems_irq_connect_data irqInfo;
+ struct mbuf **rxMbuf;
+ struct mbuf **txMbuf;
+ int acceptBroadcast;
+ int rxBdCount;
+ int txBdCount;
+ int txBdHead;
+ int txBdTail;
+ int txBdActiveCount;
+ rtems_id rxDaemonTid;
+ rtems_id txDaemonTid;
+
+ unsigned int port;
+ unsigned char *base;
+ unsigned long bpar;
+
+ /*
+ * Statistics
+ */
+ unsigned long rxInterrupts;
+ unsigned long rxNotFirst;
+ unsigned long rxNotLast;
+ unsigned long rxGiant;
+ unsigned long rxNonOctet;
+ unsigned long rxRunt;
+ unsigned long rxBadCRC;
+ unsigned long rxOverrun;
+ unsigned long rxCollision;
+
+ unsigned long txInterrupts;
+ unsigned long txDeferred;
+ unsigned long txHeartbeat;
+ unsigned long txLateCollision;
+ unsigned long txRetryLimit;
+ unsigned long txUnderrun;
+ unsigned long txLostCarrier;
+ unsigned long txRawWait;
+};
+
+#define RO 0x10
+
+#define SHATOT (8*1024) /* size of shared memory */
+#define SHAPAGE 256 /* shared memory information */
+#define MAXSIZ 1536 /*(MAXBUF - MESSH_SZ)*/
+#define OUTPAGE ((SHATOT-(MAXSIZ+SHAPAGE-1))/SHAPAGE)
+
+static volatile unsigned long overrun;
+static volatile unsigned long resend;
+static struct wd_softc wd_softc[NWDDRIVER];
+
+/*
+ * WD interrupt handler
+ */
+static rtems_isr
+wd8003Enet_interrupt_handler (rtems_vector_number v)
+{
+ unsigned int tport;
+ unsigned char status, status2;
+
+ tport = wd_softc[0].port ;
+
+ /*
+ * Read status
+ */
+ inport_byte(tport+ISR, status);
+ outport_byte(tport+IMR, 0x00);
+
+ /*
+ * Ring overwrite
+ */
+
+ if (status & MSK_OVW){
+ outport_byte(tport+CMDR, MSK_STP + MSK_RD2); /* stop 8390 */
+ Wait_X_ms(2);
+ outport_byte(tport+RBCR0, 0); /* clear byte count */
+ outport_byte(tport+RBCR1, 0);
+ inport_byte(tport+ISR, status2);
+ status |= (status2 & (MSK_PTX+MSK_TXE)) ; /* TX status */
+ outport_byte(tport+TCR, MSK_LOOP); /* loopback mode */
+ outport_byte(tport+CMDR, MSK_STA + MSK_RD2); /* start */
+ overrun = 1 ;
+ if ((status & (MSK_PTX+MSK_TXE)) == 0)
+ resend = 1;
+ }
+
+ /*
+ * Frame received?
+ */
+ if (status & (MSK_PRX+MSK_RXE)) {
+ outport_byte(tport+ISR, status & (MSK_PRX+MSK_RXE));
+ wd_softc[0].rxInterrupts++;
+ rtems_event_send (wd_softc[0].rxDaemonTid, INTERRUPT_EVENT);
+ }
+
+}
+
+static void nopOn(const rtems_irq_connect_data* notUsed)
+{
+ /*
+ * code should be moved from wd8003Enet_initialize_hardware
+ * to this location
+ */
+}
+
+static int wdIsOn(const rtems_irq_connect_data* irq)
+{
+ return BSP_irq_enabled_at_i8259s (irq->name);
+}
+
+/*
+ * Initialize the ethernet hardware
+ */
+static void
+wd8003Enet_initialize_hardware (struct wd_softc *sc)
+{
+ int i1, ultra;
+ char cc1, cc2;
+ unsigned char temp;
+ rtems_status_code st;
+ unsigned int tport;
+ unsigned char *hwaddr;
+
+ tport = sc->port;
+
+ /* address from board ROM */
+ inport_byte(tport+0x04, temp);
+ outport_byte(tport+0x04, temp & 0x7f);
+
+ hwaddr = sc->arpcom.ac_enaddr;
+ for (i1=cc2=0; i1<8; i1++) {
+ inport_byte(tport + ADDROM + i1, cc1);
+ cc2 += cc1;
+ if (i1 < 6)
+ hwaddr[i1] = cc1;
+ }
+
+ inport_byte(tport+0x04, temp);
+ outport_byte(tport+0x04, temp | 0x80); /* alternate registers */
+ outport_byte(tport+W83CREG, MSK_RESET); /* reset board, set buffer */
+ outport_byte(tport+W83CREG, 0);
+ outport_byte(tport+W83CREG, MSK_ENASH + (int)((sc->bpar>>13)&0x3f));
+
+ outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
+ cc1 = MSK_BMS + MSK_FT10; /* configure 8 or 16 bits */
+
+ inport_byte(tport+0x07, temp) ;
+
+ ultra = ((temp & 0xf0) == 0x20 || (temp & 0xf0) == 0x40);
+ if (ultra)
+ cc1 = MSK_WTS + MSK_BMS + MSK_FT10;
+ outport_byte(tport+DCR, cc1);
+ outport_byte(tport+RBCR0, 0);
+ outport_byte(tport+RBCR1, 0);
+ outport_byte(tport+RCR, MSK_MON); /* disable the rxer */
+ outport_byte(tport+TCR, 0); /* normal operation */
+ outport_byte(tport+PSTOP, OUTPAGE); /* init PSTOP */
+ outport_byte(tport+PSTART, 0); /* init PSTART */
+ outport_byte(tport+BNRY, -1); /* init BNRY */
+ outport_byte(tport+ISR, -1); /* clear IR's */
+ outport_byte(tport+IMR, 0x15); /* enable interrupt */
+
+ outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
+
+ for (i1=0; i1<6; i1++) /* initial physical addr */
+ outport_byte(tport+PAR+i1, hwaddr[i1]);
+
+ for (i1=0; i1<MARsize; i1++) /* clear multicast */
+ outport_byte(tport+MAR+i1, 0);
+ outport_byte(tport+CURR, 0); /* init current packet */
+
+ outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
+ outport_byte(tport+CMDR, MSK_STA + MSK_RD2); /* put 8390 on line */
+ outport_byte(tport+RCR, MSK_AB); /* MSK_AB accept broadcast */
+
+ if (ultra) {
+ inport_byte(tport+0x0c, temp);
+ outport_byte(tport+0x0c, temp | 0x80);
+ outport_byte(tport+0x05, 0x80);
+ outport_byte(tport+0x06, 0x01);
+ }
+
+ /*
+ * Set up interrupts
+ */
+ sc->irqInfo.hdl = wd8003Enet_interrupt_handler;
+ sc->irqInfo.on = nopOn;
+ sc->irqInfo.off = nopOn;
+ sc->irqInfo.isOn = wdIsOn;
+
+ st = BSP_install_rtems_irq_handler (&sc->irqInfo);
+ if (!st)
+ rtems_panic ("Can't attach WD interrupt handler for irq %d\n",
+ sc->irqInfo.name);
+}
+
+static void
+wd_rxDaemon (void *arg)
+{
+ unsigned int tport;
+ struct ether_header *eh;
+ struct wd_softc *dp = (struct wd_softc *)&wd_softc[0];
+ struct ifnet *ifp = &dp->arpcom.ac_if;
+ struct mbuf *m;
+ unsigned int i2;
+ unsigned int len;
+ volatile unsigned char start, next, current;
+ char *shp, *temp;
+ rtems_event_set events;
+
+ tport = wd_softc[0].port ;
+
+ for (;;){
+
+
+ rtems_bsdnet_event_receive (INTERRUPT_EVENT,
+ RTEMS_WAIT|RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events);
+
+ for (;;){
+ inport_byte(tport+BNRY, start);
+
+ outport_byte(tport+CMDR, MSK_PG1 + MSK_RD2);
+ inport_byte(tport+CURR, current);
+ outport_byte(tport+CMDR, MSK_PG0 + MSK_RD2);
+
+ start += 1;
+ if (start >= OUTPAGE){
+ start = 0;
+ }
+
+ if (current == start)
+ break;
+
+ shp = dp->base + 1 + (SHAPAGE * start);
+ next = *shp++;
+ len = *((short *)shp)++ - 4;
+
+ if (next >= OUTPAGE){
+ next = 0;
+ }
+
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = ifp;
+
+ temp = m->m_data;
+ m->m_len = m->m_pkthdr.len = len - sizeof(struct ether_header);
+
+ if ((i2 = (OUTPAGE - start) * SHAPAGE - 4) < len){
+ memcpy(temp, shp, i2);
+ len -= i2;
+ temp += i2;
+ shp = dp->base;
+ }
+ memcpy(temp, shp, len);
+
+ eh = mtod (m, struct ether_header *);
+ m->m_data += sizeof(struct ether_header);
+ ether_input (ifp, eh, m);
+
+ outport_byte(tport+BNRY, next-1);
+ }
+
+ /*
+ * Ring overwrite
+ */
+ if (overrun){
+ outport_byte(tport+ISR, MSK_OVW); /* reset IR */
+ outport_byte(tport+TCR, 0); /* out of loopback */
+ if (resend == 1)
+ outport_byte(tport+CMDR, MSK_TXP + MSK_RD2); /* resend */
+ resend = 0;
+ overrun = 0;
+ }
+
+ outport_byte(tport+IMR, 0x15); /* re-enable IT rx */
+ }
+}
+
+static void
+sendpacket (struct ifnet *ifp, struct mbuf *m)
+{
+ struct wd_softc *dp = ifp->if_softc;
+ struct mbuf *n;
+ unsigned int len, tport;
+ char *shp, txReady;
+
+ tport = dp->port;
+
+ /*
+ * Waiting for Transmitter ready
+ */
+ inport_byte(tport+CMDR, txReady);
+ while(txReady & MSK_TXP)
+ inport_byte(tport+CMDR, txReady);
+
+ len = 0;
+ shp = dp->base + (SHAPAGE * OUTPAGE);
+
+ n = m;
+
+ for (;;){
+ len += m->m_len;
+ memcpy(shp, (char *)m->m_data, m->m_len);
+ shp += m->m_len ;
+ if ((m = m->m_next) == NULL)
+ break;
+ }
+
+ m_freem(n);
+
+ if (len < ET_MINLEN) len = ET_MINLEN;
+ outport_byte(tport+TBCR0, len);
+ outport_byte(tport+TBCR1, (len >> 8) );
+ outport_byte(tport+TPSR, OUTPAGE);
+ outport_byte(tport+CMDR, MSK_TXP + MSK_RD2);
+}
+
+/*
+ * Driver transmit daemon
+ */
+void
+wd_txDaemon (void *arg)
+{
+ struct wd_softc *sc = (struct wd_softc *)arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct mbuf *m;
+ rtems_event_set events;
+
+ for (;;) {
+ /*
+ * Wait for packet
+ */
+ rtems_bsdnet_event_receive (START_TRANSMIT_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events);
+
+ /*
+ * Send packets till queue is empty
+ */
+ for (;;) {
+ /*
+ * Get the next mbuf chain to transmit.
+ */
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (!m)
+ break;
+ sendpacket (ifp, m);
+ }
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+}
+
+/*
+ * Send packet (caller provides header).
+ */
+static void
+wd_start (struct ifnet *ifp)
+{
+ struct wd_softc *sc = ifp->if_softc;
+
+ rtems_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT);
+ ifp->if_flags |= IFF_OACTIVE;
+}
+
+/*
+ * Initialize and start the device
+ */
+static void
+wd_init (void *arg)
+{
+ struct wd_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ if (sc->txDaemonTid == 0) {
+
+ /*
+ * Set up WD hardware
+ */
+ wd8003Enet_initialize_hardware (sc);
+
+ /*
+ * Start driver tasks
+ */
+ sc->txDaemonTid = rtems_bsdnet_newproc ("SCtx", 4096, wd_txDaemon, sc);
+ sc->rxDaemonTid = rtems_bsdnet_newproc ("SCrx", 4096, wd_rxDaemon, sc);
+ }
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+
+}
+
+/*
+ * Stop the device
+ */
+static void
+wd_stop (struct wd_softc *sc)
+{
+ unsigned int tport;
+ unsigned char temp;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /*
+ * Stop the transmitter
+ */
+ tport=wd_softc[0].port ;
+ inport_byte(tport+0x04,temp);
+ outport_byte(tport+0x04, temp & 0x7f);
+ outport_byte(tport + CMDR, MSK_STP + MSK_RD2);
+
+}
+
+
+/*
+ * Show interface statistics
+ */
+static void
+wd_stats (struct wd_softc *sc)
+{
+ printf (" Rx Interrupts:%-8lu", sc->rxInterrupts);
+ printf (" Not First:%-8lu", sc->rxNotFirst);
+ printf (" Not Last:%-8lu\n", sc->rxNotLast);
+ printf (" Giant:%-8lu", sc->rxGiant);
+ printf (" Runt:%-8lu", sc->rxRunt);
+ printf (" Non-octet:%-8lu\n", sc->rxNonOctet);
+ printf (" Bad CRC:%-8lu", sc->rxBadCRC);
+ printf (" Overrun:%-8lu", sc->rxOverrun);
+ printf (" Collision:%-8lu\n", sc->rxCollision);
+
+ printf (" Tx Interrupts:%-8lu", sc->txInterrupts);
+ printf (" Deferred:%-8lu", sc->txDeferred);
+ printf (" Missed Hearbeat:%-8lu\n", sc->txHeartbeat);
+ printf (" No Carrier:%-8lu", sc->txLostCarrier);
+ printf ("Retransmit Limit:%-8lu", sc->txRetryLimit);
+ printf (" Late Collision:%-8lu\n", sc->txLateCollision);
+ printf (" Underrun:%-8lu", sc->txUnderrun);
+ printf (" Raw output wait:%-8lu\n", sc->txRawWait);
+}
+
+/*
+ * Driver ioctl handler
+ */
+static int
+wd_ioctl (struct ifnet *ifp, int command, caddr_t data)
+{
+ struct wd_softc *sc = ifp->if_softc;
+ int error = 0;
+
+ switch (command) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ ether_ioctl (ifp, command, data);
+ break;
+
+ case SIOCSIFFLAGS:
+ switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
+ case IFF_RUNNING:
+ wd_stop (sc);
+ break;
+
+ case IFF_UP:
+ wd_init (sc);
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+ wd_stop (sc);
+ wd_init (sc);
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+ wd_stats (sc);
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+/*
+ * Attach an WD driver to the system
+ */
+int
+rtems_wd_driver_attach (struct rtems_bsdnet_ifconfig *config)
+{
+ struct wd_softc *sc;
+ struct ifnet *ifp;
+ int mtu;
+ int i;
+
+ /*
+ * Find a free driver
+ */
+ for (i = 0 ; i < NWDDRIVER ; i++) {
+ sc = &wd_softc[i];
+ ifp = &sc->arpcom.ac_if;
+ if (ifp->if_softc == NULL)
+ break;
+ }
+ if (i >= NWDDRIVER) {
+ printf ("Too many WD drivers.\n");
+ return 0;
+ }
+
+ /*
+ * Process options
+ */
+ if (config->hardware_address) {
+ memcpy (sc->arpcom.ac_enaddr, config->hardware_address,
+ ETHER_ADDR_LEN);
+ }
+ else {
+ memset (sc->arpcom.ac_enaddr, 0x08,ETHER_ADDR_LEN);
+ }
+ if (config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+
+ if (config->irno)
+ sc->irqInfo.name = config->irno;
+ else
+ sc->irqInfo.name = 5;
+
+ if (config->port)
+ sc->port = config->port;
+ else
+ sc->port = 0x240;
+
+ if (config->bpar) {
+ sc->bpar = config->bpar;
+ sc->base = (unsigned char*) config->bpar;
+ }
+ else {
+ sc->bpar = 0xD0000;
+ sc->base = (unsigned char*) 0xD0000;
+ }
+
+ sc->acceptBroadcast = !config->ignore_broadcast;
+
+ /*
+ * Set up network interface values
+ */
+ ifp->if_softc = sc;
+ ifp->if_unit = i + 1;
+ ifp->if_name = "wd";
+ ifp->if_mtu = mtu;
+ ifp->if_init = wd_init;
+ ifp->if_ioctl = wd_ioctl;
+ ifp->if_start = wd_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
+ if (ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /*
+ * init some variables
+ */
+ overrun = 0;
+ resend = 0;
+
+ /*
+ * Attach the interface
+ */
+ if_attach (ifp);
+ ether_ifattach (ifp);
+ return 1;
+};