/************************************************************* * File: lib/sonic.c * Purpose: A tftp driver for the Sonic Ethernet controller * Author: Phil Bunce (pjb@carmel.com) * Revision History: * 930507 Created * 960909 Made globals be malloc'd * 970221 Added ifdefs for SONIC_DW32 * 970325 Removed typedefs for Ulong etc. Now included by ether.h * 970325 ANDed byte with 0xff in printMem * 970509 Added arp support. * 970617 Added ping support * 970822 Changed RDASIZE to 16 to fix slow dl on large networks * 971115 Removed LE ifdefs around re_sonic code * 971115 Fixed n/4 prob in swap32n * 971118 Fixed bad ether_header messages from swapPkt * 980611 Added casts on free to supress warnings on Tasking cc3 * 980611 Removed cast in call to Qput4 in addRRrec (Tasking). * 980611 Removed assert in retry pack case. * 980615 Renamed re_sonic re_ether * 980615 Added ether_is_sonic. open etc via ptr now. * 980615 Made most stuff static * 981001 Moved all of the chip-independent stuff to ether.c * 981127 Move the GETRXREC swapPkt to ether.c */ #include #include #include #include #include /* * This file contains a very simple driver for the National Sonic * Ethernet controller. It is used to provide PMON with a method to * support download using tftp via Ethernet. It is intended to be * called from ether.c. */ /* * This module will not work if the global data that it uses (RRA etc) * crosses a 64KB boundary. i.e. The upper 16 address bits must not change. * That is the reason that these global data areas are malloc'd. */ /* All our boards have the Sonic at the same base address */ #ifndef ETHERADDR #define ETHERADDR 0xbc000000 #endif #ifdef MIPSEB #define SONIC_BASE ((volatile Ushort *)(ETHERADDR+2)) #else #define SONIC_BASE ((volatile Ushort *)ETHERADDR) #endif #define getlo(x) (((Ulong)(x))&0xffff) #define gethi(x) (((Ulong)(x))>>16) /*#define PROMISCUOUS /* enable promiscuous mode */ #define HDRONLY /* print headers only */ /*#define VERBOSE /* be verbose */ /*#define CHECKS_ON /* do runtime checks */ #ifdef CHECKS_ON #include #else #define assert(x) #endif #ifdef PROMISCUOUS #define VERBOSE #endif #define RRASIZE 8 /* min 4 */ #define RBSIZE 2048 /* receive buffer size (min 2048) */ #define RDASIZE 16 /* min 2. 970822 */ #define TDASIZE 4 /* min 1 */ #define TBSIZE 128 /* transmit buffer size */ #ifdef ETHERNET /* skip the entire file if ethernet is not enabled */ /*************** Ethernet stuff ******************************/ #include #include /* receive resource area */ static RRrec *RRA; /* receive resource area */ static char *RBA_data; /* receive descriptor area */ static RXrec *RDA; /* receive descriptor area */ /* transmit descriptor area */ static TXrec *TDA; /* transmit resource area */ static char *TBA_data; static Ulong CamData[5]; /* this scoreboard is used to determine whether an RBA is completely free */ typedef struct SBrec { int seq; int cnt; int tot; Ulong addr; } SBrec; static SBrec scorebd[RRASIZE]; static SBrec *putScorebd(),*getScorebd(); static RXrec *RDA_add; /* start adding new descriptors here */ static RXrec *RDA_new; /* start checking for FULL descriptors here */ static TXrec *TDA_done; /* start checking transmitted packets here */ static TXrec *TDA_next; /* next available packet for transmission */ /*************** Queues **************************************/ typedef struct Queue4 { volatile int ip; volatile int op; int size; Ulong *ptr; } Queue4; #define Qempty4(q) (((q)->ip == (q)->op)?1:0) static Ulong Qget4(); static Qput4(); static Queue4 Rxq,*rxq,Rbq,*rbq; static Ulong RxqD[RDASIZE+1],RbqD[RRASIZE+1]; /****************** Globals **********************************/ extern int vflag; /****************** Forward Declarations *********************/ static initHw(),initDA(); static pktrx(); static pkttx(); static rtnRXpkt(); static SBrec *putScorebd(); static SBrec *getScorebd(); static addRXrec(); static addRRrec(); /************************************************************* * pktrx() * Packet received. * Add it to the rxq queue. */ static pktrx() { #if 0 if (! IS_K1SEG(RDA_new)) printf("pktrx: %08x: not k1seg\n",RDA_new); #endif while (RDA_new->in_use == FULL) { Qput4(rxq,(Ulong)RDA_new); RDA_new = getRXlink(RDA_new); } SONIC_ISR = ISR_PKTRX; /* clear pkt rec'd bit */ } /************************************************************* * pkttx() * clear up after a pkt was transmitted */ static pkttx() { #if 0 if (! IS_K1SEG(TDA_done)) printf("pkttx: %08x: not k1seg\n",TDA_done); #endif while (TDA_done->status != 0) { if (vflag) printf("pkttx "); /* aught to check the status here */ TDA_done->status = 0; TDA_done = getTXlink(TDA_done); } SONIC_ISR = ISR_TXDN; /* clear pkt transmitted bit */ } /************************************************************* * rtnRXpkt(q) */ static rtnRXpkt(q) RXrec *q; { SBrec *p; Ulong rseq; assert(q != 0); rseq = getRXrseq(q); p = getScorebd(rseq); if (p) assert(p->addr != 0); if (!p) p = putScorebd(rseq,Qget4(rbq)); assert(p != 0); assert(p->addr != 0); if (q->status&LPKT) { /* last packet in buffer */ p->tot = getRXpseq(q)+1; } p->cnt++; if (p->tot && p->tot == p->cnt) { addRRrec((char *)p->addr,RBSIZE); p->addr = 0; } addRXrec(q); } /************************************************************* * SBrec *putScorebd(seq,addr) */ static SBrec *putScorebd(seq,addr) Ulong seq,addr; { int i; assert(addr != 0); for (i=0;ilink = EOL; q->in_use = EMPTY; if (RDA_add) putRXlink(RDA_add,q); else { putCRDA(q); RDA_new = q; } RDA_add = q; } /************************************************************* * addRRrec(p,size) */ static addRRrec(p,size) char *p; int size; { RRrec *q; int is_empty; assert(p != 0); p = (char *)k02k1(p); Qput4(rbq,p); q = getRWP(); if (q == getRRP()) is_empty = 1; else is_empty = 0; putRRptr(q,p); putRRwc(q,size); if (q+1 >= getREA()) q = getRSA(); else q++; putRWP(q); if (is_empty) SONIC_ISR = ISR_RBE; /* clear RBE bit */ } /************************************************************* * static Qput4(q,v) * put a word into a queue */ static Qput4(q,v) Queue4 *q; Ulong v; { assert(q != 0); q->ptr[q->ip] = v; if (q->ip+1 > q->size) q->ip = 0; else q->ip++; assert(q->ip != q->op); /* queue full */ } /************************************************************* * static Ulong Qget4(q) * get a word from a queue */ static Ulong Qget4(q) Queue4 *q; { Ulong v; assert(q != 0); while (q->ip == q->op) ; /* wait while empty */ v = q->ptr[q->op]; if (q->op+1 > q->size) q->op = 0; else q->op++; assert(v != 0); return(v); } /************************************************************* * static int initDA(void) * initialize all the data areas */ static int initDA(void) { int i; char *p,*fl[5]; Ulong t; rxq = &Rxq; Rxq.size = RDASIZE; Rxq.ip = Rxq.op = 0; Rxq.ptr = RxqD; rbq = &Rbq; Rbq.size = RRASIZE; Rbq.ip = Rbq.op = 0; Rbq.ptr = RbqD; /* * These data structures must not cross a 64KB boundary. Therefore * we request them again if they do. */ if (!RRA) { for (i=0;;i++) { RRA = (RRrec *)malloc(RRASIZE*sizeof(RRrec)); RBA_data = (char *)malloc(RBSIZE*(RRASIZE-1)); RDA = (RXrec *)malloc(RDASIZE*sizeof(RXrec)); TDA = (TXrec *)malloc(TDASIZE*sizeof(TXrec)); TBA_data = (char *)malloc(TBSIZE*TDASIZE); if (!RRA || !RBA_data || !RDA || !TDA || !TBA_data) { printf("initDA: malloc failure\n"); return(0); } /* check to see if it is ok */ t = (Ulong)malloc(4); free((void *)t); if ((t>>16) == (((Ulong)RRA)>>16)) break; /* not ok. so dealloc them */ free(RRA);free(RBA_data);free(RDA);free(TDA);free(TBA_data); if (i > 0) { /* too many attempts */ printf("initDA: 2nd malloc failure\n"); return(0); } /* malloc enough to take us to the 64KB boundary */ t = (Ulong)malloc(4); free((void *)t); malloc(0x10000-(t&0xffff)); } } #if 0 printf("RRA=%08x RBA_data=%08x RDA=%08x TDA=%08x TBA_data=%08x\n", RRA, RBA_data, RDA, TDA, TBA_data); #endif RDA_add = 0; putCRDA(RDA); for (i=0;i= 4000000) { printf("sonic: unable to load CAM\n"); return(0); } SONIC_ISR = ISR_LCD; /* Clear ISR bit */ #ifdef PROMISCUOUS /* Enable Promiscuous mode*/ SONIC_RCR = RCR_BRD | RCR_PRO; #else SONIC_RCR = RCR_BRD; /* 970509 */ #endif SONIC_MPT = 0xffff; SONIC_CRCT = 0xffff; SONIC_FAET = 0xffff; SONIC_CR = CR_RXEN; /* Turn on Receiver */ return(1); } /************************************************************* * void *sonic_driver(int op,void *vp1,void *vp2) * Main entry point for this driver. Everything else in this * module should be static. */ void *sonic_driver(int op,void *vp1,void *vp2) { RXrec *rr; char *rxptr; Ushort isr; Uchar *macAddr = vp1; isr = SONIC_ISR; if (isr&ISR_PKTRX) pktrx(); if (isr&ISR_TXDN) pkttx(); switch (op) { case ETHER_INIT : /* int ether_driver(ETHER_INIT,Uchar *macAddr,void) */ if (!initDA()) return(0); if (!initHw(macAddr)) return(0); SONIC_ISR = 0xffff; /* clear all current int requests */ return((void *)1); case ETHER_GETTBA : /* char *ether_driver(ETHER_GETTBA,int *len,void) */ TDA_next->pkt_size = TDA_next->frag_size = *((int *)vp2); return getTXptr(TDA_next); case ETHER_TBARDY : /* int ether_driver(ETHER_TBRDY,void,void) */ SONIC_CR=CR_TXP; TDA_next = getTXlink(TDA_next); assert(TDA_next != 0); return((void *)1); case ETHER_GETRXREC : /* RXrec *ether_driver(ETHER_GETRXREC,void,void) */ rr = (RXrec *)Qget4(rxq); rxptr = (char *)getRXptr(rr); if (re_ether) swap32n(rxptr,rr->byte_count); if (vflag) printMem(rxptr,rr->byte_count); #ifdef VERBOSE printPkt(rxptr,rr->byte_count); #endif return(rr); case ETHER_GETRBA : /* char *ether_driver(ETHER_GETRBA,RXrec *q,int *len) */ rr = (RXrec *)vp1; *((int *)vp2) = rr->byte_count; return(getRXptr(rr)); case ETHER_RXDONE : /* int ether_driver(ETHER_RXDONE,RXrec *q,void) */ rr = (RXrec *)vp1; rtnRXpkt(rr); return((void *)1); case ETHER_RXRDY : /* int ether_driver(ETHER_RXRDY,void,void) */ if (!Qempty4(rxq)) return((void *)1); return(0); default : return(0); } return(0); } #else /* Tasking tools don't like empty files (sigh) */ sonic_foobar() {} #endif /* ETHERNET */