patch-2.1.23 linux/drivers/ap1000/mac.c

Next file: linux/drivers/ap1000/mac.h
Previous file: linux/drivers/ap1000/ddv_util.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.22/linux/drivers/ap1000/mac.c linux/drivers/ap1000/mac.c
@@ -0,0 +1,1177 @@
+  /*
+   * Copyright 1996 The Australian National University.
+   * Copyright 1996 Fujitsu Laboratories Limited
+   * 
+   * This software may be distributed under the terms of the Gnu
+   * Public License version 2 or later
+  */
+/*
+ * Routines for controlling the FORMAC+
+ */
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>	/* For the statistics structure. */
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/inet.h>
+#include <net/sock.h>
+
+#include <asm/ap1000/apreg.h>
+#include <asm/ap1000/apservice.h>
+#include <asm/pgtable.h>
+
+#include "apfddi.h"
+#include "smt-types.h"
+#include "am79c830.h"
+#include "mac.h"
+#include "plc.h"
+#include "apfddi-reg.h"
+
+#define MAC_DEBUG 0
+
+/* Values for dma_state */
+#define IDLE            0
+#define XMITTING	1
+#define RECVING		2
+
+/*
+ * Messages greater than this value are transferred to the FDDI send buffer
+ * using DMA.
+ */
+#define DMA_XMIT_THRESHOLD 64
+#define DMA_RECV_THRESHOLD 64
+
+/*
+ * If the FDDI receive buffer is occupied by less than this value, then
+ * sending has priority.
+ */
+#define RECV_THRESHOLD (20*1024)
+
+#define DMA_RESET_MASKS ((AP_CLR_INTR_MASK<<DMA_INTR_NORMAL_SH) | \
+			 (AP_CLR_INTR_MASK<<DMA_INTR_ERROR_SH))
+
+#define DMA_INTR_REQS ((AP_INTR_REQ<<DMA_INTR_NORMAL_SH) | \
+		       (AP_INTR_REQ<<DMA_INTR_ERROR_SH))
+
+static void mac_print_state(void);
+
+typedef unsigned int	mac_status_t;
+
+static volatile struct mac_queue *mac_queue_top = NULL;
+static volatile struct mac_queue *mac_queue_bottom = NULL;
+
+struct formac_state {
+    LoopbackType loopback;
+    int	ring_op;
+    int	recv_ptr;
+    int	recv_empty;
+    int	recv_ovf;
+    int	xmit_ptr;
+    int	xmit_free;
+    int	xmit_start;
+    int	xmit_chains;
+    int	xmit_more_ptr;
+    int	frames_xmitted;
+    int	xmit_chain_start[3];
+    int frames_recvd;
+    int recv_aborted;
+    int	xmit_aborted;
+    int	wrong_bb;
+    int recv_error;
+    volatile struct mac_queue *cur_macq; /* Current queue el for send DMA   */
+    volatile struct mac_buf cur_mbuf;    /* Current mac_buf for send DMA    */
+    struct sk_buff *cur_skb;             /* skb for received packets by DMA */
+    int dma_state;
+};
+
+#define SPFRAMES_SIZE	64		/* # words for special frames area */
+#define RECV_BUF_START	SPFRAMES_SIZE
+#define RECV_BUF_END	(BUFFER_SIZE / 2 + 2048)
+#define RECV_BUF_SIZE	(RECV_BUF_END - RECV_BUF_START)
+#define XMIT_BUF_START	RECV_BUF_END
+#define XMIT_BUF_END	BUFFER_SIZE
+
+#define S2_RMT_EVENTS	(S2_CLAIM_STATE | S2_MY_CLAIM | S2_HIGHER_CLAIM | \
+			 S2_LOWER_CLAIM | S2_BEACON_STATE | S2_MY_BEACON | \
+			 S2_OTHER_BEACON | S2_RING_OP | S2_MULTIPLE_DA | \
+			 S2_TOKEN_ERR | S2_DUPL_CLAIM | S2_TRT_EXP_RECOV)
+
+struct mac_info *this_mac_info;
+struct formac_state this_mac_state;
+
+int
+mac_init(struct mac_info *mip)
+{
+    struct formac_state *msp = &this_mac_state;
+
+    bif_add_debug_key('f',mac_print_state,"show FDDI mac state");
+
+    this_mac_info = mip;
+
+    mac->cmdreg1 = C1_SOFTWARE_RESET;
+    mac->said = (mip->s_address[0] << 8) + mip->s_address[1];
+    mac->laim = (mip->l_address[0] << 8) + mip->l_address[1];
+    mac->laic = (mip->l_address[2] << 8) + mip->l_address[3];
+    mac->lail = (mip->l_address[4] << 8) + mip->l_address[5];
+    mac->sagp = (mip->s_group_adrs[0] << 8) + mip->s_group_adrs[1];
+    mac->lagm = (mip->l_group_adrs[0] << 8) + mip->l_group_adrs[1];
+    mac->lagc = (mip->l_group_adrs[2] << 8) + mip->l_group_adrs[3];
+    mac->lagl = (mip->l_group_adrs[4] << 8) + mip->l_group_adrs[5];
+    mac->tmax = mip->tmax >> 5;
+    mac->tvx = (mip->tvx - 254) / 255;	/* it's -ve, round downwards */
+    mac->treq0 = mip->treq;
+    mac->treq1 = mip->treq >> 16;
+    mac->pri0 = ~0;
+    mac->pri1 = ~0;
+    mac->pri2 = ~0;
+    mac->mdreg2 = /*M2_STRIP_FCS +*/ M2_CHECK_PARITY + M2_EVEN_PARITY
+	+ 3 * M2_RCV_BYTE_BDRY + M2_ENABLE_HSREQ
+	+ M2_ENABLE_NPDMA + M2_SYNC_NPDMA + M2_RECV_BAD_FRAMES;
+    mac->eacb = RECV_BUF_START - 1;
+    mac->earv = XMIT_BUF_START - 1;
+    mac->eas = mac->earv;
+    mac->eaa0 = BUFFER_SIZE - 1;
+    mac->eaa1 = mac->eaa0;
+    mac->eaa2 = mac->eaa1;
+    mac->wpxsf = 0;
+    mac->rpr = RECV_BUF_START;
+    mac->wpr = RECV_BUF_START + 1;
+    mac->swpr = RECV_BUF_START;
+    mac->wpxs = mac->eas;
+    mac->swpxs = mac->eas;
+    mac->rpxs = mac->eas;
+    mac->wpxa0 = XMIT_BUF_START;
+    mac->rpxa0 = XMIT_BUF_START;
+
+    memset(msp, 0, sizeof(*msp));
+    msp->recv_ptr = RECV_BUF_START;
+    msp->recv_empty = 1;
+    msp->xmit_ptr = XMIT_BUF_START;
+    msp->xmit_free = XMIT_BUF_START + 1;
+    msp->xmit_start = XMIT_BUF_START;
+    msp->xmit_chains = 0;
+    msp->frames_xmitted = 0;
+    msp->frames_recvd = 0;
+    msp->recv_aborted = 0;
+
+    mac->mdreg1 = M1_MODE_MEMORY;
+
+    mac_make_spframes();
+
+    return 0;
+}
+
+int
+mac_inited(struct mac_info *mip)
+{
+    struct formac_state *msp = &this_mac_state;
+    mac_status_t st1, st2;
+
+    if (mac->said != (mip->s_address[0] << 8) + mip->s_address[1]
+	|| mac->laim != (mip->l_address[0] << 8) + mip->l_address[1]
+	|| mac->laic != (mip->l_address[2] << 8) + mip->l_address[3]
+	|| mac->lail != (mip->l_address[4] << 8) + mip->l_address[5]
+	|| mac->sagp != (mip->s_group_adrs[0] << 8) + mip->s_group_adrs[1]
+	|| mac->lagm != (mip->l_group_adrs[0] << 8) + mip->l_group_adrs[1]
+	|| mac->lagc != (mip->l_group_adrs[2] << 8) + mip->l_group_adrs[3]
+	|| mac->lagl != (mip->l_group_adrs[4] << 8) + mip->l_group_adrs[5])
+	return 1;
+    if ((mac->mdreg1 & ~M1_ADDET) != (M1_MODE_ONLINE | M1_SELECT_RA
+				      | M1_FULL_DUPLEX))
+	return 3;
+    if (mac->treq0 != (mip->treq & 0xffff)
+	|| mac->treq1 != ((unsigned)mip->treq >> 16))
+	return 4;
+
+    st1 = (mac->st1u << 16) + mac->st1l;
+    st2 = (mac->st2u << 16) + mac->st2l;
+    if ((st2 & S2_RING_OP) == 0)
+	return 5;
+
+    /* It's probably OK, reset some things to be safe. */
+    this_mac_info = mip;
+    *csr0 &= ~CS0_HREQ;
+    mac->tmax = mip->tmax >> 5;
+    mac->tvx = (mip->tvx - 254) / 255;	/* it's -ve, round downwards */
+    mac->pri0 = ~0;
+    mac->pri1 = ~0;
+    mac->pri2 = ~0;
+    mac->mdreg2 = /*M2_STRIP_FCS +*/ M2_CHECK_PARITY + M2_EVEN_PARITY
+	+ 3 * M2_RCV_BYTE_BDRY + M2_ENABLE_HSREQ
+	+ M2_ENABLE_NPDMA + M2_SYNC_NPDMA + M2_RECV_BAD_FRAMES;
+
+    /* clear out the receive queue */
+    mac->mdreg1 = (mac->mdreg1 & ~M1_ADDET) | M1_ADDET_DISABLE_RECV;
+    mac->rpr = RECV_BUF_START;
+    mac->wpr = RECV_BUF_START + 1;
+    mac->swpr = RECV_BUF_START;
+
+    memset(msp, 0, sizeof(*msp));
+    msp->recv_ptr = RECV_BUF_START;
+    msp->recv_empty = 1;
+
+    /* XXX reset transmit pointers */
+    mac->cmdreg2 = C2_ABORT_XMIT;
+    mac->cmdreg2 = C2_RESET_XMITQS;
+    mac->wpxa0 = XMIT_BUF_START;
+    mac->rpxa0 = XMIT_BUF_START;
+    msp->xmit_ptr = XMIT_BUF_START;
+    msp->xmit_free = XMIT_BUF_START + 1;
+    msp->xmit_start = XMIT_BUF_START;
+    msp->xmit_chains = 0;
+
+    mac_make_spframes();
+    mac->cmdreg1 = C1_CLR_ALL_LOCKS;
+
+    msp->frames_xmitted = 0;
+    msp->frames_recvd = 0;
+    msp->recv_aborted = 0;
+    msp->ring_op = 1;
+
+    mac->mdreg1 = (mac->mdreg1 & ~M1_ADDET) | M1_ADDET_NSA;
+    mac->imsk1u = ~(S1_XMIT_ABORT | S1_END_FRAME_ASYNC0) >> 16;
+    mac->imsk1l = ~(S1_PAR_ERROR_ASYNC0 | S1_QUEUE_LOCK_ASYNC0);
+    mac->imsk2u = ~(S2_RECV_COMPLETE | S2_RECV_BUF_FULL | S2_RECV_FIFO_OVF
+		    | S2_ERR_SPECIAL_FR | S2_RMT_EVENTS
+		    | S2_NP_SIMULT_LOAD) >> 16;
+    mac->imsk2l = ~(S2_RMT_EVENTS | S2_MISSED_FRAME);
+
+    return 0;
+}
+
+void mac_make_spframes(void)
+{
+    volatile int *bp;
+    struct mac_info *mip = this_mac_info;
+    int sa;
+    struct formac_state *msp = &this_mac_state;
+
+    /* initialize memory to avoid parity errors */
+    *csr0 &= ~CS0_HREQ;
+    *csr1 &= ~CS1_BUF_WR_TAG;
+    for (bp = &buffer_mem[BUFFER_SIZE]; bp > &buffer_mem[XMIT_BUF_START];)
+	*--bp = 0xdeadbeef;
+    for (; bp > buffer_mem;)
+	*--bp = 0xfeedf00d;
+    buffer_mem[msp->recv_ptr] = 0;
+
+    bp = buffer_mem;
+    *bp++ = 0;			/* auto-void frame pointer (not used) */
+
+    /* make claim frame */
+    sa = bp - buffer_mem;
+    *bp++ = 0xd8000011;		/* claim frame descr. + length */
+    *bp++ = 0xc3;		/* FC value for claim frame, long addr */
+    *bp++ = (mip->l_address[0] << 24) + (mip->l_address[1] << 16)
+	+ (mip->l_address[2] << 8) + mip->l_address[3];
+    *bp++ = (mip->l_address[4] << 24) + (mip->l_address[5] << 16)
+	+ (mip->l_address[0] << 8) + mip->l_address[1];
+    *bp++ = (mip->l_address[2] << 24) + (mip->l_address[3] << 16)
+	+ (mip->l_address[4] << 8) + mip->l_address[5];
+    *bp++ = mip->treq;
+    mac->sacl = bp - buffer_mem; /* points to pointer to claim frame */
+    *bp++ = 0xa0000000 + sa;	/* pointer to start of claim frame */
+
+    /* make beacon frame */
+    sa = bp - buffer_mem;
+    *bp++ = 0xd8000011;		/* beacon frame descr. + length */
+    *bp++ = 0xc2;		/* FC value for beacon frame, long addr */
+    *bp++ = 0;			/* DA = 0 */
+    *bp++ = (mip->l_address[0] << 8) + mip->l_address[1];
+    *bp++ = (mip->l_address[2] << 24) + (mip->l_address[3] << 16)
+	+ (mip->l_address[4] << 8) + mip->l_address[5];
+    *bp++ = 0;			/* beacon reason = failed claim */
+    mac->sabc = bp - buffer_mem;
+    *bp++ = 0xa0000000 + sa;	/* pointer to start of beacon frame */
+}
+
+void mac_reset(LoopbackType loopback)
+{
+    int mode;
+    struct formac_state *msp = &this_mac_state;
+
+    msp->loopback = loopback;
+    switch (loopback) {
+    case loop_none:
+	mode = M1_MODE_ONLINE;
+	break;
+    case loop_formac:
+	mode = M1_MODE_INT_LOOP;
+	break;
+    default:
+	mode = M1_MODE_EXT_LOOP;
+	break;
+    }
+    mac->mdreg1 = mode | M1_ADDET_NSA | M1_SELECT_RA | M1_FULL_DUPLEX;
+    mac->cmdreg1 = C1_IDLE_LISTEN;
+    mac->cmdreg1 = C1_CLR_ALL_LOCKS;
+    mac->imsk1u = ~(S1_XMIT_ABORT | S1_END_FRAME_ASYNC0) >> 16;
+    mac->imsk1l = ~(S1_PAR_ERROR_ASYNC0 | S1_QUEUE_LOCK_ASYNC0);
+    mac->imsk2u = ~(S2_RECV_COMPLETE | S2_RECV_BUF_FULL | S2_RECV_FIFO_OVF
+		    | S2_ERR_SPECIAL_FR | S2_RMT_EVENTS
+		    | S2_NP_SIMULT_LOAD) >> 16;
+    mac->imsk2l = ~(S2_RMT_EVENTS | S2_MISSED_FRAME);
+}
+
+void mac_claim(void)
+{
+    mac->cmdreg1 = C1_CLAIM_LISTEN;
+}
+
+void mac_disable(void)
+{
+    mac->mdreg1 = M1_MODE_MEMORY;
+    mac->imsk1u = ~0;
+    mac->imsk1l = ~0;
+    mac->imsk2u = ~0;
+    mac->imsk2l = ~0;
+    mac->wpr = mac->swpr + 1;
+    if (mac->wpr > mac->earv)
+	mac->wpr = mac->eacb + 1;
+    buffer_mem[mac->swpr] = 0;
+}
+
+void mac_stats(void)
+{
+    struct formac_state *msp = &this_mac_state;
+
+    if (msp->recv_ovf)
+	printk("%d receive buffer overflows\n", msp->recv_ovf);
+    if (msp->wrong_bb)
+	printk("%d frames on wrong byte bdry\n", msp->wrong_bb);
+    printk("%d frames transmitted, %d aborted\n", msp->frames_xmitted,
+	   msp->xmit_aborted);
+    printk("%d frames received, %d aborted\n", msp->frames_recvd,
+	   msp->recv_aborted);
+    printk("%d frames received with errors\n", msp->recv_error);
+}
+
+void mac_sleep(void)
+{
+    /* disable the receiver */
+    mac->mdreg1 = (mac->mdreg1 & ~M1_ADDET) | M1_ADDET_DISABLE_RECV;
+}
+
+void mac_poll(void)
+{
+    mac_status_t st1, st2;
+    struct formac_state *msp = &this_mac_state;
+    int up, f, d, l, r, e, i;
+
+    st1 = (mac->st1u << 16) + mac->st1l;
+    st2 = (mac->st2u << 16) + mac->st2l;
+
+    if (st2 & S2_NP_SIMULT_LOAD)
+	panic("NP/formac simultaneous load!!!");
+
+    up = (st2 & S2_RING_OP) != 0;
+    if (up != msp->ring_op) {
+	/* ring has come up or down */
+	msp->ring_op = up;
+	printk("mac: ring %s\n", up? "up": "down");
+	set_ring_op(up);
+    }
+
+    if (up) {
+	if (st1 & S1_XMIT_ABORT) {
+	    ++msp->xmit_aborted;
+	    if (st1 & S1_QUEUE_LOCK_ASYNC0) {
+		printk("mac: xmit queue locked, resetting xmit buffer\n");
+		mac->cmdreg2 = C2_RESET_XMITQS;	/* XXX bit gross */
+		mac->rpxa0 = XMIT_BUF_START;
+		buffer_mem[XMIT_BUF_START] = 0;
+		msp->xmit_ptr = XMIT_BUF_START;
+		msp->xmit_start = XMIT_BUF_START;
+		msp->xmit_chains = 0;
+		mac->cmdreg1 = C1_CLR_ASYNCQ0_LOCK;
+		st1 &= ~(S1_END_CHAIN_ASYNC0 | S1_END_FRAME_ASYNC0
+			 | S1_XINSTR_FULL_ASYNC0);
+	    } else
+		st1 |= S1_END_FRAME_ASYNC0;
+	} else if (st1 & S1_QUEUE_LOCK_ASYNC0) {
+	    printk("mac: xmit queue locked, why?\n");
+	    mac->cmdreg1 = C1_CLR_ASYNCQ0_LOCK;
+	}
+
+	if (st1 & S1_END_FRAME_ASYNC0) {
+	    /* advance xmit_start */
+	    e = msp->xmit_start;
+	    while (e != msp->xmit_ptr) {
+		/* find the end of the current frame */
+		f = buffer_mem[e]; /* read pointer */
+		if (f == 0)
+		    break;	/* huh?? */
+		f &= 0xffff;
+		d = buffer_mem[f]; /* read descriptor */
+		l = ((d & 0xffff) + ((d >> TD_BYTE_BDRY_LG) & 3) + 3) >> 2;
+		e = f + 1 + l;	/* index of ptr at end of frame */
+		r = mac->rpxa0;
+		if ((r <= msp->xmit_ptr && r < e && e <= msp->xmit_ptr)
+		    || (r > msp->xmit_ptr && (r < e || e <= msp->xmit_ptr)))
+		    break;	/* up to current frame */
+		/* printk("frame @ %x done\n", msp->xmit_start); */
+		msp->xmit_start = e;
+		if ((st1 & S1_XMIT_ABORT) == 0)
+		    ++msp->frames_xmitted;
+		if ((msp->xmit_chains == 1 && e == msp->xmit_ptr) ||
+		    (msp->xmit_chains > 1 && e == msp->xmit_chain_start[1])) {
+		    /* we've finished chain 0 */
+		    --msp->xmit_chains;
+		    for (i = 0; i < msp->xmit_chains; ++i)
+			msp->xmit_chain_start[i] = msp->xmit_chain_start[i+1];
+		    if (msp->xmit_chains >= 2) {
+			mac->cmdreg2 = C2_XMIT_ASYNCQ0;
+			/* printk("mac_poll: xmit chain\n"); */
+		    }
+		    if (msp->xmit_chains == 0)
+			*csr0 &= ~CS0_LED1;
+		}
+	    }
+	    /*
+	     * Now that we have a bit more space in the transmit buffer,
+	     * see if we want to put another frame in.
+	     */
+#if MAC_DEBUG
+	    printk("Removed space in transmit buffer.\n");
+#endif
+	    mac_process();
+	}
+    }
+
+    if (st2 & S2_RMT_EVENTS) {
+	rmt_event(st2);
+    }
+
+    if (st2 & S2_RECV_COMPLETE) {
+	/*
+	 * A frame has just finished arriving in the receive buffer.
+	 */
+	*csr0 |= CS0_LED2;
+	msp->recv_empty = 0;
+#if MAC_DEBUG
+	printk("Frame has just trickled in...\n");
+#endif
+	mac_process();
+    }
+
+    if (st2 & S2_RECV_BUF_FULL) {
+	/*
+	 * receive buffer overflow: reset and unlock the receive buffer.
+	 */
+/*	printk("mac: receive buffer full\n"); */
+	mac->rpr = RECV_BUF_START;
+	mac->wpr = RECV_BUF_START + 1;
+	mac->swpr = RECV_BUF_START;
+	msp->recv_ptr = RECV_BUF_START;
+	msp->recv_empty = 1;
+	buffer_mem[RECV_BUF_START] = 0;
+	mac->cmdreg1 = C1_CLR_RECVQ_LOCK;
+	++msp->recv_ovf;
+
+#if 0
+    } else if (st2 & S2_RECV_FIFO_OVF) {
+	printk("mac: receive FIFO overflow\n");
+	/* any further action required here? */
+
+    } else if (st2 & S2_MISSED_FRAME) {
+	printk("mac: missed frame\n");
+#endif
+    }
+
+    if (st2 & S2_ERR_SPECIAL_FR) {
+	printk("mac: bug: error in special frame\n");
+	mac_disable();
+    }
+}
+
+void
+mac_xmit_alloc(sp, bb)
+    struct mac_buf *sp;
+    int bb;
+{
+    int nwords;
+
+    nwords = (sp->length + bb + 3) >> 2;
+    sp->fr_start = mac_xalloc(nwords + 2);
+    sp->fr_end = sp->fr_start + nwords + 1;
+    sp->ptr = (char *) &buffer_mem[sp->fr_start + 1] + bb;
+    buffer_mem[sp->fr_start] = TD_MAGIC + (bb << TD_BYTE_BDRY_LG) + sp->length;
+}
+
+void
+mac_queue_frame(sp)
+    struct mac_buf *sp;
+{
+    struct formac_state *msp = &this_mac_state;
+
+    buffer_mem[sp->fr_end] = 0;	/* null pointer at end of frame */
+    buffer_mem[msp->xmit_ptr] = PT_MAGIC + sp->fr_start;
+    if (msp->xmit_chains <= 2) {
+	msp->xmit_chain_start[msp->xmit_chains] = msp->xmit_ptr;
+	if (msp->xmit_chains < 2)
+	    mac->cmdreg2 = C2_XMIT_ASYNCQ0;
+	++msp->xmit_chains;
+    } else {
+	buffer_mem[msp->xmit_more_ptr] |= TD_MORE;
+    }
+    msp->xmit_ptr = sp->fr_end;
+    msp->xmit_more_ptr = sp->fr_start;
+    *csr0 |= CS0_LED1;
+}
+
+int
+mac_xalloc(int nwords)
+{
+    int fr_start;
+    struct formac_state *msp = &this_mac_state;
+
+    /*
+     * Find some room in the transmit buffer.
+     */
+    fr_start = msp->xmit_free;
+    if (fr_start > msp->xmit_start) {
+	if (fr_start + nwords > XMIT_BUF_END) {
+	    /* no space at end - see if we can start again from the front */
+	    fr_start = XMIT_BUF_START;
+	    if (fr_start + nwords > msp->xmit_start)
+		panic("no space in xmit buffer (1)");
+	}
+    } else {
+	if (fr_start + nwords > msp->xmit_start)
+	    panic("no space in xmit buffer (2)");
+    }
+
+    msp->xmit_free = fr_start + nwords;
+
+    return fr_start;
+}
+
+int
+mac_recv_frame(sp)
+    struct mac_buf *sp;
+{
+    struct formac_state *msp = &this_mac_state;
+    int status, bb, orig_recv_ptr;
+
+    orig_recv_ptr = msp->recv_ptr;
+    for (;;) {
+	status = buffer_mem[msp->recv_ptr];
+	if ((status & RS_VALID) == 0) {
+	    if (status != 0) {
+		printk("recv buf out of sync: recv_ptr=%x status=%x\n",
+		       msp->recv_ptr, status);
+		printk(" rpr=%x swpr=%x, buf[rpr]=%x\n", mac->rpr, mac->swpr,
+		       buffer_mem[mac->rpr]);
+		msp->recv_ptr = mac->swpr;
+	    }
+	    *csr0 &= ~CS0_LED2;
+	    msp->recv_empty = 1;
+	    if (mac->rpr == orig_recv_ptr)
+		mac->rpr = msp->recv_ptr;
+	    return 0;
+	}
+	if (status & RS_ABORTED)
+	    ++msp->recv_aborted;
+	else {
+	    bb = (status >> RS_BYTE_BDRY_LG) & 3;
+	    if (bb != 3) {
+		++msp->wrong_bb;
+		bb = 3;
+	    }
+	    if ((status & RS_ERROR) == 0)
+		break;
+	    ++msp->recv_error;
+	    msp->recv_ptr += NWORDS((status & RS_LENGTH) + bb);
+	}
+	if (++msp->recv_ptr >= RECV_BUF_END)
+	    msp->recv_ptr -= RECV_BUF_SIZE;
+    }
+    ++msp->frames_recvd;
+    if (mac->rpr == orig_recv_ptr)
+	mac->rpr = msp->recv_ptr;
+
+    sp->fr_start = msp->recv_ptr;
+    sp->length = (status & RS_LENGTH) + bb;	/* + 4 (status) - 4 (FCS) */
+    sp->ptr = (void *) &buffer_mem[sp->fr_start];
+    if ((msp->recv_ptr += NWORDS(sp->length) + 1) >= RECV_BUF_END)
+	msp->recv_ptr -= RECV_BUF_SIZE;
+    sp->fr_end = msp->recv_ptr;
+    sp->wraplen = (RECV_BUF_END - sp->fr_start) * 4;
+    sp->wrapptr = (void *) &buffer_mem[RECV_BUF_START];
+
+    return 1;
+}
+
+void
+mac_discard_frame(sp)
+    struct mac_buf *sp;
+{
+    mac->rpr = sp->fr_end;
+}
+
+/*
+ * Return the number of bytes free in the async 0 transmit queue.
+ */
+int
+mac_xmit_space(void)
+{
+    struct formac_state *msp = &this_mac_state;
+    int nw;
+
+    if (msp->xmit_free > msp->xmit_start) {
+	nw = XMIT_BUF_END - msp->xmit_free;
+	if (nw < msp->xmit_start - XMIT_BUF_START)
+	    nw = msp->xmit_start - XMIT_BUF_START;
+    } else
+	nw = msp->xmit_start - msp->xmit_free;
+    return nw <= 2? 0: (nw - 2) << 2;
+}
+
+/*
+ * Return the number of bytes of frames available in the receive queue.
+ */
+int
+mac_recv_level(void)
+{
+    int nw;
+
+    nw = mac->swpr - mac->rpr;
+    if (nw < 0)
+	nw += mac->earv - mac->eacb;
+    return nw << 2;
+}
+
+/*
+ * Return 1 iff all transmission has been completed, 0 otherwise.
+ */
+int mac_xmit_done(void)
+{
+    struct formac_state *msp = &this_mac_state;
+
+    return msp->xmit_chains == 0;
+}
+
+/*
+ * Append skbuff packet to queue.
+ */
+int mac_queue_append (struct sk_buff *skb)
+{
+    struct mac_queue *el;
+    unsigned flags;
+    save_flags(flags); cli();
+
+#if MAC_DEBUG
+    printk("Appending queue element skb 0x%x\n", skb);
+#endif
+
+    if ((el = (struct mac_queue *)kmalloc(sizeof(*el), GFP_ATOMIC)) == NULL) {
+	restore_flags(flags);
+	return 1;
+    }
+    el->next = NULL;
+    el->skb = skb;
+    
+    if (mac_queue_top == NULL) {
+	mac_queue_top = mac_queue_bottom = el;
+    }
+    else {
+	mac_queue_bottom->next = el;
+	mac_queue_bottom = el;
+    }
+    restore_flags(flags);
+    return 0;
+}
+
+/*
+ * If the packet originated from the same FDDI subnet as we are on,
+ * there is no need to perform checksumming as FDDI will does this
+ * us.  
+ */
+#define CHECK_IF_CHECKSUM_REQUIRED(skb) \
+    if ((skb)->protocol == ETH_P_IP) { \
+	extern struct cap_init cap_init; \
+	int *from_ip = (int *)((skb)->data+12); \
+	int *to_ip = (int *)((skb)->data+16); \
+	if ((*from_ip & cap_init.netmask) == (*to_ip & cap_init.netmask)) \
+	    (skb)->ip_summed = CHECKSUM_UNNECESSARY; \
+    }
+
+/*
+ * Try to send and/or recv frames.
+ */
+void mac_process(void)
+{
+    volatile struct dma_chan *dma = (volatile struct dma_chan *) DMA3;
+    struct formac_state *msp = &this_mac_state;
+    struct mac_queue *el;
+    int nw=0, mrl = 0, fstart, send_buffer_full = 0;
+    unsigned flags;
+
+    save_flags(flags); cli();
+
+#if MAC_DEBUG
+    printk("In mac_process()\n");
+#endif
+
+    /*
+     * Check if the DMA is being used.
+     */
+    if (msp->dma_state != IDLE) {
+	restore_flags(flags);
+	return;
+    }
+
+    while (mac_queue_top != NULL  || /* Something to transmit */
+	   (mrl = mac_recv_level()) > 0) {  /* Frames in receive buffer */
+	send_buffer_full = 0;
+#if MAC_DEBUG
+	printk("mac_process(): something to do... mqt %x mrl is %d\n", 
+	       mac_queue_top, mrl);
+#endif
+	if (mac_queue_top != NULL && mrl < RECV_THRESHOLD) {
+	    el = (struct mac_queue *)mac_queue_top;
+
+	    /*
+	     * Check there is enough space in the FDDI send buffer.
+	     */
+	    if (mac_xmit_space() < el->skb->len) {
+#if MAC_DEBUG
+		printk("process_queue(): FDDI send buffer is full\n");
+#endif
+		send_buffer_full = 1;
+	    }
+	    else {
+#if MAC_DEBUG
+		printk("mac_process(): sending a frame\n");
+#endif
+		/*
+		 * Update mac_queue_top.
+		 */
+		mac_queue_top = mac_queue_top->next;
+
+		/*
+		 * Allocate space in the FDDI send buffer.
+		 */
+		msp->cur_mbuf.length = el->skb->len-3;
+		mac_xmit_alloc((struct mac_buf *)&msp->cur_mbuf, 3);
+
+		/*
+		 * If message size is greater than DMA_XMIT_THRESHOLD, send
+		 * using DMA, otherwise use memcpy().
+		 */
+		if (el->skb->len > DMA_XMIT_THRESHOLD) {
+		    /*
+		     * Start the DMA.
+		     */
+#if MAC_DEBUG
+		    printk("mac_process(): Starting send DMA...\n");
+#endif
+		    nw = msp->cur_mbuf.fr_end - msp->cur_mbuf.fr_start + 1;
+		    mac->wpxa0 = msp->cur_mbuf.fr_start + 1;
+		    
+		    *csr0 |= CS0_HREQ_WA0;
+		    
+		    msp->cur_macq = el;
+		    msp->dma_state = XMITTING;
+		    dma->st = DMA_DMST_RST;
+		    dma->st = DMA_RESET_MASKS;
+		    dma->hskip = 1;		/* skip = 0, count = 1 */
+		    dma->vskip = 1;		/* skip = 0, count = 1 */
+		    dma->maddr = (u_char *)
+			mmu_v2p((unsigned long)el->skb->data);
+		    dma->cmd = DMA_DCMD_ST + DMA_DCMD_TYP_AUTO + 
+			DMA_DCMD_TD_MD + nw;
+		    *csr0 &= ~CS0_DMA_RECV;
+		    *csr0 |= CS0_DMA_ENABLE;
+
+		    /*
+		     * Don't process any more packets since the DMA is 
+		     * being used.
+		     */
+		    break;
+		}
+		else {   /* el->skb->len <= DMA_XMIT_THRESHOLD */
+		    /*
+		     * Copy the data directly into the FDDI buffer.
+		     */
+#if MAC_DEBUG
+		    printk("mac_proces(): Copying send data...\n");
+#endif
+		    memcpy(msp->cur_mbuf.ptr - 3, el->skb->data, 
+			   ROUND4(el->skb->len));
+		    mac_queue_frame((struct mac_buf *)&msp->cur_mbuf);
+		    dev_kfree_skb(el->skb, FREE_WRITE);
+		    kfree_s(el, sizeof(*el));
+		    continue;
+		}
+	    }
+
+	    /*
+	     * We have reached here if there is not enough space in the
+	     * send buffer.  Try to receive some packets instead.
+	     */
+	}
+
+	if (mac_recv_frame((struct mac_buf *)&msp->cur_mbuf)) {
+	    volatile int fc, llc_header_word2;
+	    int pkt_len = 0;
+
+#if MAC_DEBUG
+	    printk("mac_process(): Receiving frames...\n");
+#endif
+	    /* 
+	     * Get the fc, note only word accesses are allowed from the
+	     * FDDI buffers.
+	     */
+	    if (msp->cur_mbuf.wraplen > 4) {
+		fc = *(int *)(msp->cur_mbuf.ptr+4);
+	    }
+	    else {
+		/*
+		 * fc_word must be at the start of the FDDI buffer.
+		 */
+#if MAC_DEBUG
+		printk("Grabbed fc_word from wrapptr, wraplen %d\n", 
+		       msp->cur_mbuf.wraplen);
+#endif
+		fc = *(int *)msp->cur_mbuf.wrapptr;
+	    }
+	    fc &= 0xff;
+	    
+#if MAC_DEBUG
+	    printk("fc is 0x%x\n", fc);
+#endif
+	    if (fc < 0x50 || fc > 0x57) {
+		mac_discard_frame((struct mac_buf *)&msp->cur_mbuf);
+		continue;
+	    }
+
+	    /*
+	     * Determine the size of the packet data and allocate a socket
+	     * buffer.
+	     */
+	    pkt_len = msp->cur_mbuf.length - FDDI_HARDHDR_LEN;
+#if MAC_DEBUG
+	    printk("Packet of length %d\n", pkt_len);
+#endif
+	    msp->cur_skb = dev_alloc_skb(ROUND4(pkt_len));
+	    
+	    if (msp->cur_skb == NULL) {
+		printk("mac_process(): Memory squeeze, dropping packet.\n");
+		apfddi_stats->rx_dropped++;
+		restore_flags(flags);
+		return;
+	    }
+	    msp->cur_skb->dev = apfddi_device;
+
+	    /*
+	     * Hardware header isn't copied to skbuff.
+	     */
+	    msp->cur_skb->mac.raw = msp->cur_skb->data;
+	    apfddi_stats->rx_packets++;
+
+	    /*
+	     * Determine protocol from llc header.
+	     */
+	    if (msp->cur_mbuf.wraplen < FDDI_HARDHDR_LEN) {
+		llc_header_word2 = *(int *)(msp->cur_mbuf.wrapptr + 
+					    (FDDI_HARDHDR_LEN - 
+					     msp->cur_mbuf.wraplen - 4));
+	    }
+	    else {
+		llc_header_word2 = *(int *)(msp->cur_mbuf.ptr + 
+					    FDDI_HARDHDR_LEN - 4);
+	    }
+	    msp->cur_skb->protocol = llc_header_word2 & 0xFFFF;
+#if MAC_DEBUG
+	    printk("Got protocol 0x%x\n", msp->cur_skb->protocol);
+#endif
+	    
+	    /*
+	     * Copy data into socket buffer, which may be wrapped around the 
+	     * FDDI buffer.  Use memcpy if the size of the data is less
+	     * than DMA_RECV_THRESHOLD.  Note if DMA is used, then wrap-
+	     * arounds are handled automatically.
+	     */
+	    if (pkt_len < DMA_RECV_THRESHOLD) {
+		if (msp->cur_mbuf.length < msp->cur_mbuf.wraplen) {
+		    memcpy(skb_put(msp->cur_skb, ROUND4(pkt_len)), 
+			   msp->cur_mbuf.ptr + FDDI_HARDHDR_LEN, 
+			   ROUND4(pkt_len));
+		} 
+		else if (msp->cur_mbuf.wraplen < FDDI_HARDHDR_LEN) {
+#if MAC_DEBUG
+		    printk("Wrap case 2\n");
+#endif
+		    memcpy(skb_put(msp->cur_skb, ROUND4(pkt_len)), 
+			   msp->cur_mbuf.wrapptr + 
+			   (FDDI_HARDHDR_LEN - msp->cur_mbuf.wraplen),
+			   ROUND4(pkt_len));
+		} 
+		else {
+#if MAC_DEBUG
+		    printk("wrap case 3\n");
+#endif
+		    memcpy(skb_put(msp->cur_skb, 
+				   ROUND4(msp->cur_mbuf.wraplen-
+					  FDDI_HARDHDR_LEN)),
+			   msp->cur_mbuf.ptr + FDDI_HARDHDR_LEN, 
+			   ROUND4(msp->cur_mbuf.wraplen - FDDI_HARDHDR_LEN));
+		    memcpy(skb_put(msp->cur_skb, 
+				   ROUND4(msp->cur_mbuf.length - 
+					  msp->cur_mbuf.wraplen)),
+			   msp->cur_mbuf.wrapptr, 
+			   ROUND4(msp->cur_mbuf.length - 
+				  msp->cur_mbuf.wraplen));
+		}
+
+#if MAC_DEBUG
+		if (msp->cur_skb->protocol == ETH_P_IP) {
+		    dump_packet("apfddi_rx:", msp->cur_skb->data, pkt_len, 0);
+		}
+		else if (msp->cur_skb->protocol == ETH_P_ARP) {
+		    struct arphdr *arp = (struct arphdr *)msp->cur_skb->data;
+		    printk("arp->ar_op is 0x%x ar_hrd %d ar_pro 0x%x ar_hln %d ar_ln %d\n", 
+			   arp->ar_op, arp->ar_hrd, arp->ar_pro, arp->ar_hln, 
+			   arp->ar_pln);
+		    printk("sender hardware address: %x:%x:%x:%x:%x:%x\n",
+			   *((u_char *)msp->cur_skb->data+8),
+			   *((u_char *)msp->cur_skb->data+9),
+			   *((u_char *)msp->cur_skb->data+10),
+			   *((u_char *)msp->cur_skb->data+11),
+			   *((u_char *)msp->cur_skb->data+12),
+			   *((u_char *)msp->cur_skb->data+13));
+		    printk("sender IP number %d.%d.%d.%d\n", 
+			   *((u_char *)msp->cur_skb->data+14),
+			   *((u_char *)msp->cur_skb->data+15),
+			   *((u_char *)msp->cur_skb->data+16),
+			   *((u_char *)msp->cur_skb->data+17));
+		    printk("receiver hardware address: %x:%x:%x:%x:%x:%x\n",
+			   *((u_char *)msp->cur_skb->data+18),
+			   *((u_char *)msp->cur_skb->data+19),
+			   *((u_char *)msp->cur_skb->data+20),
+			   *((u_char *)msp->cur_skb->data+21),
+			   *((u_char *)msp->cur_skb->data+22),
+			   *((u_char *)msp->cur_skb->data+23));
+		    printk("receiver IP number %d.%d.%d.%d\n", 
+			   *((u_char *)msp->cur_skb->data+24),
+			   *((u_char *)msp->cur_skb->data+25),
+			   *((u_char *)msp->cur_skb->data+26),
+			   *((u_char *)msp->cur_skb->data+27));
+		}
+#endif
+		CHECK_IF_CHECKSUM_REQUIRED(msp->cur_skb);
+
+		/*
+		 * Inform the network layer of the new packet.
+		 */
+#if MAC_DEBUG
+		printk("Calling netif_rx()\n");
+#endif
+		netif_rx(msp->cur_skb);
+
+		/*
+		 * Remove frame from FDDI buffer.
+		 */
+		mac_discard_frame((struct mac_buf *)&msp->cur_mbuf);
+		continue;
+	    }
+	    else {
+		/*
+		 * Set up dma and break.
+		 */
+#if MAC_DEBUG
+		printk("mac_process(): Starting receive DMA...\n");
+#endif
+		nw = NWORDS(pkt_len);
+		msp->dma_state = RECVING;
+		*csr0 &= ~(CS0_HREQ | CS0_DMA_ENABLE);
+/*		*csr1 |= CS1_RESET_FIFO;
+		*csr1 &= ~CS1_RESET_FIFO; */
+		if ((*csr1 & CS1_FIFO_LEVEL) != 0) {
+		    int x;
+		    printk("fifo not empty! (csr1 = 0x%x) emptying...", *csr1);
+		    do {
+			x = *fifo;
+		    } while ((*csr1 & CS1_FIFO_LEVEL) != 0);
+		    printk("done\n");
+		}
+		fstart = msp->cur_mbuf.fr_start + NWORDS(FDDI_HARDHDR_LEN);
+		if (fstart >= RECV_BUF_END)
+		    fstart -= RECV_BUF_SIZE;
+		mac->rpr = fstart;
+#if MAC_DEBUG
+		printk("rpr=0x%x, nw=0x%x, stat=0x%x\n",
+		       mac->rpr, nw, buffer_mem[msp->cur_mbuf.fr_start]);
+#endif
+		dma->st = DMA_DMST_RST;
+		dma->st = DMA_RESET_MASKS;
+		dma->hskip = 1;         /* skip = 0, count = 1 */
+		dma->vskip = 1;         /* skip = 0, count = 1 */
+		dma->maddr = (u_char *)
+		    mmu_v2p((unsigned long)
+			    skb_put(msp->cur_skb, ROUND4(pkt_len)));
+		dma->cmd = DMA_DCMD_ST + DMA_DCMD_TYP_AUTO + DMA_DCMD_TD_DM
+		    + nw - 4;
+		*csr0 |= CS0_HREQ_RECV | CS0_DMA_RECV;
+		*csr0 |= CS0_DMA_ENABLE;
+#if MAC_DEBUG
+		printk("mac_process(): DMA is away!\n");
+#endif
+		break;
+	    }
+	}
+	else {
+#if MAC_DEBUG
+	    printk("mac_recv_frame failed\n");
+#endif
+	    if (msp->recv_empty && send_buffer_full)
+		break;
+	}
+    }
+    /*
+     * Update mac_queue_bottom.
+     */
+    if (mac_queue_top == NULL)
+	mac_queue_bottom = NULL;
+
+#if MAC_DEBUG
+    printk("End of mac_process()\n");
+#endif
+    restore_flags(flags);
+}
+    
+
+#define DMA_IN(reg) (*(volatile unsigned *)(reg))
+#define DMA_OUT(reg,v) (*(volatile unsigned *)(reg) = (v))
+
+/*
+ * DMA completion handler.
+ */
+void mac_dma_complete(void)
+{
+    volatile struct dma_chan *dma;
+    struct formac_state *msp = &this_mac_state;
+    unsigned a;
+
+    a = DMA_IN(DMA3_DMST);
+    if (!(a & DMA_INTR_REQS)) {
+	if (msp->dma_state != IDLE && (a & DMA_DMST_AC) == 0) {
+	    printk("dma completed but no interrupt!\n");
+	    msp->dma_state = IDLE;
+	}
+	return;
+    }
+
+    DMA_OUT(DMA3_DMST,AP_CLR_INTR_REQ<<DMA_INTR_NORMAL_SH);
+    DMA_OUT(DMA3_DMST,AP_CLR_INTR_REQ<<DMA_INTR_ERROR_SH);
+
+    dma = (volatile struct dma_chan *) DMA3;
+
+#if MAC_DEBUG
+    printk("In mac_dma_complete\n");
+#endif
+
+    if (msp->dma_state == XMITTING && ((dma->st & DMA_DMST_AC) == 0)) {
+	/*
+	 * Transmit DMA finished.
+	 */
+	int i = 20;
+#if MAC_DEBUG
+	printk("In mac_dma_complete for transmit complete\n");
+#endif
+	while (*csr1 & CS1_FIFO_LEVEL) {
+	    if (--i <= 0) {
+		printk("csr0=0x%x csr1=0x%x: fifo not emptying\n", *csr0,
+		       *csr1);
+		return;
+	    }
+	}
+	*csr0 &= ~(CS0_HREQ | CS0_DMA_ENABLE);
+	msp->dma_state = IDLE;
+#if MAC_DEBUG
+	printk("mac_dma_complete(): Calling mac_queue_frame\n");
+#endif
+	mac_queue_frame((struct mac_buf *)&msp->cur_mbuf);
+	dev_kfree_skb(msp->cur_macq->skb, FREE_WRITE);
+	kfree_s((struct mac_buf *)msp->cur_macq, sizeof(*(msp->cur_macq)));
+	msp->cur_macq = NULL;
+#if MAC_DEBUG
+	printk("mac_dma_complete(): Calling mac_process()\n");
+#endif
+	mac_process();
+#if MAC_DEBUG
+	printk("End of mac_dma_complete transmitting\n");
+#endif
+    }
+    else if (msp->dma_state == RECVING && ((dma->st & DMA_DMST_AC) == 0)) {
+	/*
+	 * Receive DMA finished.  Copy the last four words from the
+	 * fifo into the buffer, after turning off the host requests.
+	 * We do this to avoid reading past the end of frame.
+	 */
+	int *ip, i;
+
+#if MAC_DEBUG
+	printk("In mac_dma_complete for receive complete\n");
+#endif
+	msp->dma_state = IDLE;
+	ip = (int *)mmu_p2v((unsigned long)dma->cmaddr);
+
+#if MAC_DEBUG
+	printk("ip is 0x%x, skb->data is 0x%x\n", ip, msp->cur_skb->data);
+#endif
+
+	*csr0 &= ~(CS0_DMA_ENABLE | CS0_HREQ);
+
+	for (i = 0; (*csr1 & CS1_FIFO_LEVEL); ++i)
+	    ip[i] = *fifo;
+	if (i != 4)
+	    printk("mac_dma_complete(): not four words remaining in fifo?\n");
+#if MAC_DEBUG
+	printk("Copied last four words out of fifo\n");
+#endif
+	
+	/*
+	 * Remove the frame from the FDDI receive buffer.
+	 */
+	mac_discard_frame((struct mac_buf *)&msp->cur_mbuf);
+
+	CHECK_IF_CHECKSUM_REQUIRED(msp->cur_skb);
+
+	/*
+	 * Now inject the packet into the network system.
+	 */
+	netif_rx(msp->cur_skb);
+
+#if MAC_DEBUG
+	dump_packet("mac_dma_complete:", msp->cur_skb->data, 0, 0);
+#endif
+
+	/*
+	 * Check if any more frames can be processed.
+	 */
+	mac_process();
+
+#if MAC_DEBUG
+	printk("End of mac_dma_complete receiving\n");
+#endif
+    }
+#if MAC_DEBUG
+    printk("End of mac_dma_complete()\n");
+#endif
+}
+    
+static void mac_print_state(void)
+{
+    struct formac_state *msp = &this_mac_state;
+
+    printk("DMA3_DMST is 0x%x dma_state is %d\n", DMA_IN(DMA3_DMST),
+	   msp->dma_state);
+    printk("csr0 = 0x%x, csr1 = 0x%x\n", *csr0, *csr1);
+}
+
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov