Path: utzoo!attcan!uunet!mcsun!ukc!keele!nott-cs!ucl-cs!J.Crowcroft From: J.Crowcroft@ucl-cs.UUCP Newsgroups: comp.unix.aix Subject: (none) Message-ID: <1088@ucl-cs.UUCP> Date: 3 Jul 90 13:11:49 GMT Sender: J.Crowcroft@ucl-cs.UUCP Lines: 1577 From: J.Crowcroft@uk.ac.ucl.cs >But didn't you say it was a simple program? I don't really see the point >of your posting if you can't give any more information. Before IBM >will accept it as a bug, you will have to reduce it to its smallest >reproducible level. >A. Lester Buck buck@siswat.lonestar.org ...!texbell!moray!siswat!buck okay...small is relative... what follows is a non working driver for a BICC (lance based) ethernet card for a PS/2 under AIX 1.1. interrupts and packet reception come and go depending which buffers/descriptor rings/initialisation blocks are kmemalloc'd and which are statics...it occasionally crashes the machine...the compiler problems referred to in my earlier message are the appearance of *warnings* only when syntax errors are also detected (I havnt time or energy to reproduce the smallest program that produces this effect). If you can make the driver work, you are welcome to it( modulo the usual saying where it came from). its fairly close to working ... but very untidy...a variant works under a real time executive on the same hardware base...if i get it working (and you are interested) i can repost it... cheers jon --------------------bicc.h----------------- /* * (c) Bloomsbury Computer Consortium */ /* * Lance init Block */ typedef struct { u_short mode; u_char paddr[6]; /* phys address */ u_char ladrf[8]; /* Logicali addr filter */ u_char raddr[3]; /* rx desriptor addr */ u_char rlen; /* rx descriptor length */ u_char taddr[3]; /* tx descriptor addr */ u_char tlen; /* tx descriptor length */ } LanInit; /* * Descriptor Ring entry */ typedef struct { u_char addr[3]; /* high 8 bits of buffer addr */ u_char flags; short bcnt; /* buffer length in bytes */ u_short mcnt; /* message byte count */ } LanDesc; /* * Constants for LANCE CSR */ #define ERR 0x8000 /* error */ #define BABL 0x4000 /* babble */ #define CERR 0x2000 /* Collision */ #define MISS 0x1000 /* Missed */ #define MERR 0x0800 /* memory error */ #define RINT 0x0400 /* rx interrupt */ #define TINT 0x0200 /* tx interrupt */ #define IDON 0x0100 /* initialisation done */ #define INTR 0x0080 /* interrupt */ #define INEA 0x0040 /* interrupt enable */ #define RXON 0x0020 /* rx on */ #define TXON 0x0010 /* tx on */ #define TDMD 0x0008 /* tx demand */ #define STOP 0x0004 /* stop */ #define STRT 0x0002 /* start */ #define INIT 0x0001 /* initialise */ /* * Lance Rx Descriptor ring flag bits */ #define RxOWN 0x80 /* Lance's */ #define RxERR 0x40 /* Error in rx'd pkt */ #define RxFRAM 0x20 /* Framing Error */ #define RxOFLO 0x10 /* Overflow in rxd pkt */ #define RxCRC 0x08 /* CRC Error - Noize */ #define RxBUFF 0x04 /* Buffer Error */ #define RxSTP 0x02 /* Start of Pkt */ #define RxENP 0x01 /* End of Pkt */ /* * Lance TX Descriptor ring flag bits */ #define TxOWN 0x80 /* Lance's */ #define TxERR 0x40 /* Error in rx'd pkt */ #define TxMORE 0x10 /* More in next descriptor */ #define TxONE 0x08 /* One retry needed to send */ #define TxDEF 0x04 /* Deferred */ #define TxSTP 0x02 /* Start of Pkt */ #define TxENP 0x01 /* End of Pkt */ /* * Tx Descriptor 3 bits */ #define TxBUFF 0x8000 /* Buffer error */ #define TxUFLO 0x4000 /* Underflow */ #define TxLCOL 0x1000 /* Late Collision */ #define TxLCAR 0x0800 /* Carrier sense loss */ #define TxRTRY 0x0400 /* Retry error */ /* * max ethernet frame size */ #define MAXPKTSIZE 1540 #define MINPKTSIZE 60 #define SAFEPKTSIZE 2048 #define EthernetHeaderLength 14 /* * useful bits */ #define BIT0 0x01 #define BIT1 0x02 #define BIT2 0x04 #define BIT3 0x08 #define BIT4 0x010 #define BIT5 0x020 #define BIT6 0x040 #define BIT7 0x080 extern unsigned short ioin(); extern u_long kvtophys(); /* * Configuration specific ... */ /* The Bicc ethernet card is from 0x8280 up to 0x828f */ #define BICCADDR 0x8280 #define CARDID 0x0808; /* Should be around 8 for decent performance */ #define NRXDS 8 /* and 2**3 = 8, but shift up 5 places for lance... */ #define LN2NRXDS (3 << 5) /* Force Quad word align (not nec needed...) */ #define QUAD(x) (((u_long)(x) + 3) & 0xfffffffc) /* use kmemalloc rather than statics for buffers: */ /* #define DYNBUF */ #define DEBUG 1 #define DEBUGI 1 #define DEBUGRI 1 /* #define DEBUGTI 1 #define DEBUG2 1 */ ---------------bicc.c-------------------- /* * BICC ISOLANN MAC Ethernet Devicve Driver * Under AIX PS/2 1.1 * * Version 1.1 * * J Crowcroft * jon@cs.ucl.ac.uk * jon@uk.ac.ucl.cs * ccaajac@uk.ac.ucl * * (c) Bloomsbury Computer Consortium */ /* * TODO * Add multiple device support properly * Add LLC support (at least LLC1). * Add err logging instead of printfs... * Deal with lance freeeze... * Add LPP support...(external to this) */ /* We ARE of course in Kernel mode */ #ifndef KERNEL #define KERNEL #endif #ifndef i386 #define i386 #endif /* And get all required definitions */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bicc.h" /* * Notes: * * POS (programmable option select) registers accessed thru I/O * see posread/poswrite call * ioin(b) and ioout(b) on PS/2 AIX * * buffer allocations etc use * kmemalloc for internal structures * m_get, m_getclr, m_free, mtod, dtom, m_copy, m_cat, m_pullup * * queuing to IF uses * IF_ENQUEUE and IF_DEQUEUE * called on input and output ifq's in * if device we learn about in lainit()... * * critical network code protected by calling * splnet * critical mbuf code protected by * splimp * */ /* ****************** GLOBS ****************** */ /* * Stats gathering - inited in lainit */ struct laststats { u_long txd; /* snet */ u_long rxd; /* got */ u_long ovr; /* overrruns */ u_long bad; /* bad frames */ u_long sht; /* short frames */ u_long mst; /* missed frames */ u_long bab; /* babbles */ u_long crc; /* Collisions */ u_long ukn; /* unk type frames */ } Lastats; #ifdef DYNBUF #else /* * declare static space for init, rings and buffers */ static char sbuff[NRXDS][SAFEPKTSIZE]; static char naff0[16]; static char stxbuff[SAFEPKTSIZE]; static char naff1[16]; static LanDesc stxring; static char naff2[16]; static LanDesc srxring[NRXDS]; static char naff3[16]; static LanInit slaninit; static char naff4[16]; #endif /* Save where we kmemalloc space for buffs (for later checks) */ static char *rxbuff[NRXDS]; /* hang onto the tx buff too */ static char *txbuff; /* Lance Descriptor rings and buffers */ static LanDesc *txring, *rxring; /* Base addr of BICC card */ static u_long labase = BICCADDR; /* Cardid we read back on MCA for BICC */ static int cardid = CARDID; static int initted = 0; /* Visible Device Driver routines */ int lainit(), laoutput(), laioctl(), lawatchdog(), lareset(), laintr(); /* following not defined or used - could be for etherfind (NIT) interface laopen(), laread(), lawrite(), */ /* Internal to driver... */ static void la_rxintr(), la_txintr(), larx(); static void lacsrwrite(), ilacsrwrite(); static u_short lacsread(), ilacsread(); static u_long posfind(); static u_short posread(); static void poswrite(); /* Misc things required */ struct iobuf labuf; int lalevl; /* interrupt level of BICC lance device */ u_short bdn; /* Ether board number */ extern struct devdata devdata[];/* GLobal Device Data structure */ extern struct ifqueue ipintrq; /* The IP s/w interrupt queue */ /* * Herein should be an Struct arpcom... * ( which includes an ifnet...) */ struct arpcom ether_ifs[1]; /* IFPs */ /* * our addr - need more than one for multiple bicc support */ u_char lanaddr[6]; u_long outipaddr; /* For timing out a frozen lance... */ static struct callout *freezer = NULL; /* ******************************************************************* * Find and Initiailize the BICC card * Set up any globs for the driver * * Here we learn about IF and we tel IF structure about all our entry * points * * Called by Kernel at boot time ******************************************************************* */ lainit(dev) dev_t dev; { struct ifnet *ifp; LanInit *laninit; LanDesc *rdre; int i, j, status; u_long addr; u_short tmp; char *data, *buf; int unit, maj, slot; /* for multiple ether board support */ int s; #ifdef PROMISC int promisc = 0; #endif #ifdef DEBUG2 printf("lainit\n"); #endif /* No ints while doin this please */ s = splimp(); unit = minor(dev); maj = major(dev); ifp = &(ether_ifs[0].ac_if); /* should be unit */ if ((slot = devexist(cardid, maj, 1, 0)) == -1) { printf("lainit: no bicc device\n"); splx(s); return -1; } if (!initted) { DEV_INSTALL(maj, lainit, lareset, nulldev, nulldev, laintr, ISNOTATTY); BDEV_INSTALL(maj, nulldev, nulldev, &labuf); CDEV_INSTALL(maj, nulldev, nulldev, laioctl, nulldev, ISNOTATTY); } #ifdef NVRAMBICC /* AIXPS2 doesnt know about bicc... yet */ lalevl = devdata[slot].pd_pos3; #else lalevl = 3; #endif #ifdef DEBUG2 printf("lalvl %d\n", lalevl); #endif /* And stop the beast before we get it going */ lacsrwrite(0, STOP); if (!initted) intrattach(laintr, lalevl, SPL_IMP); /* * zero internal stats */ Lastats.txd = 0L; Lastats.rxd = 0L; Lastats.ovr = 0L; Lastats.bad = 0L; Lastats.sht = 0L; Lastats.mst = 0L; Lastats.bab = 0L; Lastats.crc = 0L; Lastats.ukn = 0L; /* POS stuff to find BDN BICC/Lance - panics if not present */ bdn = posfind(cardid); poswrite(bdn, 0x102, 0x11); /* Enable card and sampled timings */ poswrite(bdn, 0x104, 0x88); /* IRQ, Fairness etc */ /* Get mem for lance descriptor, panic if fail */ #ifdef DYNBUF if (!initted) laninit = (LanInit *)kmemalloc(sizeof(LanInit), MA_DBLWD|MA_LONGTERM|MA_OK2SLEEP); if(laninit == NULL) panic("lainit: kmemalloc failed"); #else laninit = QUAD(&slaninit); #endif bzero(laninit, sizeof(LanInit)); /* get tx and rxrings */ #ifdef DYNBUF if (!initted) txring = (LanDesc *)kmemalloc(sizeof(LanDesc), MA_DBLWD|MA_LONGTERM|MA_OK2SLEEP); if(txring == NULL) panic("lainit: kmemalloc failed"); #else txring = QUAD(&stxring); #endif #ifdef DYNBUF if (!initted) rxring = (LanDesc *)kmemalloc(NRXDS*sizeof(LanDesc), MA_DBLWD|MA_LONGTERM|MA_OK2SLEEP); if(rxring == NULL) panic("lainit: kmemalloc failed"); #else rxring = QUAD(&srxring[0]); #endif /* Setup Lance Initialisation bloc */ for(i=0; i<8; i++) laninit->ladrf[i] = 0; laninit->tlen = 0; /* 1 entry in tx ring */ laninit->rlen = LN2NRXDS; /* 8 entries in rx ring (2^3)*/ /* Flatten addr of rx and tx descriptor and put in init bloc */ addr = kvtophys(txring); for(i=0; i<3; i++) laninit->taddr[i] = ((u_long)(addr >> (8 * i))) & 0xff; addr = kvtophys(rxring); for(i=0; i<3; i++) laninit->raddr[i] = ((u_long)(addr >> (8 * i))) & 0xff; #ifdef PROMISC if (promisc) laninit->mode = 0x8000; else laninit->mode = 0; #else laninit->mode = 0; /* Normal operation mode */ #endif txring->flags = 0; /* setup rx ring */ for(i=0, rdre = rxring; iaddr[j] = ((u_long)(addr >> (8 * j))) & 0xff; rdre->bcnt = -(SAFEPKTSIZE); rdre->mcnt = 0; rdre->flags = RxOWN; /* Give to the Lance */ } /* Receive Descriptor Ring ptr at head of list */ rdre = rxring; /* Get mem for transmit buffer */ #ifdef DYNBUF txbuff = (char *)kmemalloc(SAFEPKTSIZE, MA_DBLWD|MA_LONGTERM|MA_OK2SLEEP); if(txbuff == NULL) panic("lainit: kmemalloc failed\n"); #else txbuff = QUAD(&stxbuff[0]); #endif addr = kvtophys(txbuff); /* map to phys */ for(j=0; j<3; j++) txring->addr[j] = ((u_long)(addr >> (8 * j))) & 0xff; /* get phys addr somewhere more useful */ for(i=0; i<6; i++) { lanaddr[i] = ioin((u_short)(labase + (i * 2))) & 0xff; laninit->paddr[i] = lanaddr[i]; ifp->ac_lanaddr[i] = lanaddr[i]; } #ifdef DEBUG printf("lainit ea: "); for(i=0; i<6; i++) printf("%x.", lanaddr[i]); printf("\n"); #endif /* and give it the init block */ addr = kvtophys(laninit); tmp = (u_short)(addr & 0xffff); lacsrwrite(1, tmp); tmp = ((u_long)addr >> 16) & 0xff; lacsrwrite(2, tmp); lacsrwrite(3, 0); lacsrwrite(0, INIT); /* wait for it to live... */ while(((status = lacsread(0)) & IDON) == 0) { if (status & ERR) { printf("lainit err: 0x%x\n", status); panic("lance initialisation error"); } } /* fill in IF information.. */ ifp->if_unit = unit; ifp->if_name = "la"; ifp->if_mtu = ETHERMTU; ifp->if_init = lainit; ifp->if_output = laoutput; ifp->if_ioctl = laioctl; ifp->if_reset = lareset; ifp->if_watchdog = lawatchdog; ifp->if_timer = 0; /* Do broadcasts, dont do trailers, am ethenet IEEE */ ifp->if_flags = IFF_BROADCAST | IFF_NOTRAILERS | IFF_ETHERNET; /* | IFF_IEEE; */ /* Set stats */ ifp->if_ipackets = 0; ifp->if_opackets = 0; ifp->if_ierrors = 0; ifp->if_oerrors = 0; ifp->if_collisions = 0; /* tell aix we've started lance... */ if(!initted) if_attach(ifp); if(!initted) /* avoid wraparound */ initted++; lacsrwrite(0, IDON); /* Clear IDON */ lacsrwrite(0, STRT|INEA); /* Start and enable interrupts... */ splx(s); return 0; } /* ******************************************************************* * output to ethernet * ******************************************************************* */ laoutput(ifp, m0, dst) struct ifnet *ifp; struct mbuf *m0; struct sockaddr *dst; { int error; u_char edst[6], *destn = edst; struct ip *pip; struct in_addr idst; struct mbuf *m = m0; spl_t s; int type = 0, usetrailors; int off; int hdr_len, mac_len; u_char *macp; struct ifqueue *opq; /* The IP output queue */ #ifdef IEEE int llc_len; struct ie2_llc_hdr *llcp; #else struct eth_header *dix; struct arphdr *arph; #endif #ifdef DEBUG2 printf("laoutput\n"); #endif /* * is interface up and running ?? */ if((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { #ifdef DEBUG2 printf("laoutput %x\n", ifp->if_flags); #endif error = ENETDOWN; goto bad; } /* The BIG SWITCH - see AIX Tech Ref Vol 2 */ switch(dst->sa_family) { case AF_INET: idst = ((struct sockaddr_in *)(dst))->sin_addr; if(!arpresolve(ifp, m, &idst, destn, &usetrailors)) { #ifdef DEBUG2 printf("laoutput: arp failed\n"); #endif return 0; } pip = mtod(m, struct ip *); off = ntohs(pip->ip_len) - m->m_len; /* Trailer stuff */ if (usetrailors && off > 0 && (off & 0x1ff) == 0 && m->m_off >= MMINOFF + 2 * sizeof(u_short)) { type = ETHERTYPE_TRAIL + (off>>9); m->m_off -= 2 * sizeof(u_short); m->m_len += 2 * sizeof(u_short); *mtod(m, u_short *) = htons((u_short)LANTYPE_IP); *(mtod(m, u_short *) +1) = htons((u_short)m->m_len); goto gottrailortype; } else { type = LANTYPE_IP; off = 0; goto gottype; } break; case AF_ARP: type = LANTYPE_ARP; /* DROP THRU */ case AF_UNSPEC: bcopy((caddr_t)dst, (caddr_t)edst, 6); goto gottype; break; default: #ifdef DEBUG2 printf("laoutput: non supported type\n"); #endif error = EAFNOSUPPORT; return; break; } gottrailortype: #ifdef DEBUG2 printf("laoutput: trailer\n"); #endif while(m->m_next) /* pkt going at end of packet!! */ m = m->m_next; m->m_next = m0; m = m0->m_next; m0->m_next = 0; m0 = m; gottype: mac_len = 2 * sizeof(edst) + sizeof(u_short); #ifdef IEEE802 /* not llc yet */ llc_len = sizeof(struct ie2_llc_hdr); hdr_len = mac_len + llc_len; #endif hdr_len = mac_len; arph = mtod(m, struct arphdr *); /* find header space */ if ((m0->m_off > MMAXOFF) || (MMINOFF + hdr_len > m0->m_off)){ m = m_get(M_DONTWAIT, MT_HEADER); if (m == 0) { error = ENOBUFS; goto bad; } m->m_next = m0; m0 = m; m0->m_off = MMINOFF; m0->m_len = hdr_len; } else { m0->m_off -= hdr_len; m0->m_len += hdr_len; } /* Fill in mac header, dst, src, type. */ /* THIS IS HEAVILY DIX SPEFICIX */ macp = mtod(m, u_char *); dix = mtod(m, struct eth_header *); /* dst */ if (type == LANTYPE_ARP) { bcopy((char *)arph + sizeof(struct arphdr) + 6 + 4, (caddr_t)&(dix->eth_dest[0]), ETH_ADDR_SIZE); bcopy(lanaddr, (char *)arph+sizeof(struct arphdr), ETH_ADDR_SIZE); /* * OVERWRITE 802 part of arp packet */ arph->ar_hrd = ntohs(ARPHRD_ETHER); } else bcopy((caddr_t)edst, (caddr_t)&(dix->eth_dest[0]), ETH_ADDR_SIZE); /* src */ bcopy((caddr_t)&lanaddr[0], (caddr_t)&(dix->eth_srce[0]), ETH_ADDR_SIZE); /* type */ if (type != 0) { type = htons(type); bcopy((caddr_t)&type, (caddr_t)&(dix->eth_type[0]), sizeof(u_short)); } /* * Otherwise we'd do the following... * fill LLC header (only if IEEE ... ) llcp = mac_to_llc(macp); bcopy((caddr_t)&sap->sa_llc, (caddr_t)llcp, lc_len); * and we'd need to set length rather than type (suitably swapped?) */ /* * watch out critics region about * see if we can queue pkt for output, if so start it... */ s = splimp(); opq = &(ifp->if_snd); if (IF_QFULL(opq)) { IF_DROP(opq); error = ENOBUFS; splx(s); #ifdef DEBUG printf("la: IP oq full\n"); #endif goto qfull; } IF_ENQUEUE(opq, m); laostart(ifp); splx(s); return 0; qfull: m0 = m; bad: m_freem(m0); return error; } /* ******************************************************************* * called if timeout happens (lance frozen? ******************************************************************* */ laorestart(ifp) struct ifnet *ifp; { #ifdef DEBUG printf("laorestart\n"); #endif if (freezer == NULL) return; to_cancel(freezer); printf("la: frozen lance\n"); /* FOR NOW XXX */ return; #ifdef DEBUG lacsrwrite(0, RINT|TINT|INEA); /* Start and enable interrupts... */ #else lainit(makedev(25,0)); #endif } /* ******************************************************************* * called whenever more work to be done... * dequue from ifp->if_snd then get lance going... * Currently called only from la_output, could * call from lainit & laintr if we want - note * splimp should have been called before this is... ******************************************************************* */ laostart(ifp) struct ifnet *ifp; { /* * Find from args... */ char *dstadr, *data; int olen, len, i; u_long addr; struct mbuf *m; char *buf; #ifdef DEBUG2 printf("laostart\n"); #endif /* dequeue safely packet */ IF_DEQUEUE(&(ifp->if_snd), m); if (m == NULL) { printf("laostart: deq null m\n"); return; } /* copy in from mbuf, as much as there was */ len = copy_from_mbufs(txbuff, m); /* fixup min pkt size...else lance wont send */ if (len < MINPKTSIZE) len = MINPKTSIZE; else if (len & 1) len++; #ifdef DEBUG2 ethdump(txbuff, len); #endif addr = kvtophys(txbuff); for(i=0; i<3; i++) txring->addr[i] = ((u_long)(addr >> (8 * i))) & 0xff; txring->bcnt = -(len); txring->mcnt = 0; /* Set a timer to deal with LANCE freezing here... */ freezer = ctimeout(laorestart, ifp, 50); /* Give to Lance and set STP/ENP */ txring->flags = TxOWN|TxSTP|TxENP; lacsrwrite(0, INEA|TDMD); if (txring->flags & TxERR) { txerrp(txring->mcnt); if (txring->mcnt & TxLCOL) ifp->if_collisions++; } m_freem(m); Lastats.txd++; ifp->if_opackets++; #ifdef DEBUGTI printf("laostart ret\n"); #endif } static txerrp(txerr) { #ifdef DEBUG printf("La_txintr error 0x%x", txerr); if (txerr & TxBUFF) printf("TxBuff"); if (txerr & TxUFLO) printf("TxUFLO"); if (txerr & TxLCOL) { printf("TxLCOL"); } if (txerr & TxLCAR) printf("TxLCAR"); if (txerr & TxRTRY) printf("TxRTRY"); printf("\n"); #endif } /* ******************************************************************* * deal with ioctl sys calls to device... ******************************************************************* */ laioctl(ifp, cmd, data) struct ifnet *ifp; int cmd; caddr_t data; { struct ifaddr *ifa = (struct ifaddr *)data; struct arpcom *arper = (struct arpcom *)ifp; int error = 0; short flags = ifp->if_flags; spl_t s = splimp(); #ifdef DEBUG2 printf("laioctl: "); lainfo(); #endif switch(cmd) { case (SIOCSIFADDR): ifp->if_flags |= IFF_UP; switch(ifa->ifa_addr.sa_family) { case AF_INET: arper->ac_ipaddr = IA_SIN(ifa)->sin_addr; arpwhohas(arper, &IA_SIN(ifa)->sin_addr); break; default: #ifdef DEBUG printf("laioctl: arp on non IP?\n"); #endif break; } #ifdef DEBUG printf("laioctl: %x\n", ntohl(((struct arpcom *)ifp)->ac_ipaddr)); #endif /* * fall thru */ case (SIOCSIFFLAGS): if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING) break; else { /* * Bring up the link... */ ifp->if_flags |= IFF_RUNNING; /* * Set anything else */ #ifdef DEBUG if (flags & IFF_DEBUG) lainit(makedev(25,0)); #endif } } else { if(ifp->if_flags & IFF_RUNNING) { /* * Close down link */ ifp->if_flags &= ~IFF_RUNNING; /* * Unset anything else */ } } break; default: error = EINVAL; break; } splx(s); return error; } /* ******************************************************************* * deal with reset * eg at reboot ******************************************************************* */ lareset() { #ifdef DEBUG printf("lareset: called\n"); #endif /* stop lance */ lacsrwrite(0, STOP); lacsrwrite(0, MERR|INEA); } /* * watchdog timer - not used... */ lawatchdog() { #ifdef DEBUG printf("lawatchdog: called\n"); #endif lacsrwrite(0, STOP); lainit(makedev(25,0)); } /* ******************************************************************* * The interrupt handler - field in and out... ******************************************************************* */ laintr(vec_num) int vec_num; { u_short status; int done = 0; struct ifnet *ifp = ðer_ifs[0]; /* should derive from int vec */ /* Should then derive labase from ether_ifs to support multiple i/fs */ ioout(labase + 0x0e, 0); /* get right CSR */ status = ioin(labase + 0x0c); /* read data */ #ifdef DEBUG if (!initted) { printf("laintr: not initted\n"); return done; } #endif if (!(status & INTR)) { printf("la: spurious interrupt\n"); return done; } /* * which kind of interrupt?? * Note lance keeps internal track of rx buffer chain * need to check own bit then clear rint bit, else * packet can arrive between own and rint and we miss intr */ #ifdef DEBUG if (status & RINT) { #else if ((status & RINT) && (status & RXON)) { #endif ilacsrwrite(0, RINT&INEA); /* clr rx int and INEA */ la_rxintr(ifp); done++; } #ifdef DEBUG if (status & TINT) { #else if ((status & TINT) && (status & TXON)) { #endif ilacsrwrite(0, TINT&INEA); /* clr tx int and INEA */ la_txintr(ifp); done++; } if ( (status & (BABL | MERR | MISS | TXON | RXON)) != (RXON|TXON) ) la_errintr(status, ifp); return done; } static la_errintr(status, ifp) u_short status; struct ifnet *ifp; { int restart = 0; #ifdef DEBUG printf("laintr "); #endif #ifdef DEBUG2 printf("(%x) ", status); #endif if (status & MISS) { printf("miss "); ilacsrwrite(0, MISS|INEA); Lastats.mst++; } if (status & BABL) { printf("babl "); ilacsrwrite(0, BABL|INEA); Lastats.bab++; } if (status & CERR) { printf("crc "); ilacsrwrite(0, CERR|INEA); Lastats.crc++; } /* Next one is bad lose!! */ if (status & MERR) { printf("mem "); /* maybe ilacsrwrite(0, STOP); ??? */ ilacsrwrite(0, MERR|INEA); } /* Assert the next one never happens :-) */ if (status & IDON) { printf("ini done "); ilacsrwrite(0, IDON|INEA); } if (!(status & RXON)) { printf("rx stopped "); restart++; } if (!(status & TXON)) { printf("tx stopped "); restart++; } printf("\n"); if (restart) { printf("la: restarting...csr %x\n", status); lainit(makedev(25,0)); } } /* ******************************************************************* * rx interrupt handler - field in ******************************************************************* */ static void la_rxintr(ifp) struct ifnet *ifp; { register LanDesc *rdre; register int cnt; int len; char *pkt; #ifdef DEBUG int j; u_long addr; #endif /* Go round all the rx ring... check which are ours, */ /* Should check STP and ENP in flags... */ for(rdre = rxring, cnt=0; cntflags & RxOWN) != RxOWN) { /* If its ours... */ len = rdre->mcnt; pkt = rxbuff[cnt]; if (rdre->flags & RxERR) rdreerr(rdre->flags); else larx(pkt, len, ifp); rdre->bcnt = -(SAFEPKTSIZE); rdre->mcnt = 0; rdre->flags = RxOWN; /* and give the lance ownership */ #ifdef DEBUG /* convert phys addr back to virt and check lance and we agree!! */ addr = (u_long)(rdre->addr[2] << 16) | (u_long)(rdre->addr[1] << 8) | (u_long)(rdre->addr[0]); if (addr != kvtophys(pkt)) { printf("la: bad rx buffer %x!=%x\n", addr, kvtophys(pkt)); addr = kvtophys(pkt); for(j=0; j<3; j++) rdre->addr[j] = ((u_long)(addr >> (8 * j))) & 0xff; } #endif } } ifp->if_ipackets++; Lastats.rxd++; #ifdef DEBUG2 printf("r"); lainfo(); #endif } /* ******************************************************************* * Demux frame types ******************************************************************* */ static void larx(pkt, len, ifp) char *pkt; int len; struct ifnet *ifp; { struct arpcom *arper = (struct arpcom *)ifp; int data_len; char *addr_of_data; u_short type; struct mbuf *m, *copy_to_mbufs(); struct ifqueue *ipq; /* The IP s/w interrupt queue */ if ((len < MINPKTSIZE) || (len > MAXPKTSIZE)) { Lastats.sht++; #ifdef DEBUGRI printf("la: rx bad size pkt: %d\n", len); #endif return; } /* * the other BIG SWITCH - Find Client for packet type (? LSAP?) * Now do the mbuf and queuing stuff... */ type = ((u_short *)(pkt))[6]; type = htons(type); addr_of_data = pkt + sizeof(struct eth_header); data_len = len - sizeof(struct eth_header); switch(type) { case LANTYPE_IP: printf("I"); m = copy_to_mbufs(addr_of_data, data_len, ifp); if (m == NULL) break; ipq = &ipintrq; if (IF_QFULL(ipq)) { #ifdef DEBUGRI printf("larx: IP inq full\n"); #endif IF_DROP(ipq); m_freem(m); return; } #ifdef DEBUGRI printf("larx: IP enq %d\n", data_len); #endif IF_ENQUEUE(ipq, m); schednetisr(NETISR_IP); break; case LANTYPE_ARP: printf("A"); m = copy_to_mbufs(addr_of_data, data_len, ifp); if (m == NULL) break; arpinput(arper, m); #ifdef DEBUGRI printf("larx: ARP enq\n"); #endif break; default: /* * Could cope with IEEE LLC frames here... * need a registry of LSAPs etc etc... */ Lastats.ukn++; #ifdef DEBUG2 printf("larx unknwn frm type %x len %d\n", type, len); #endif break; } } static rdreerr(flags) u_short flags; { #ifdef DEBUG printf("laintr: err 0x%x", flags); if (flags & RxCRC) printf("CRC"); if (flags & RxBUFF) printf("BUFF"); if (flags & RxOFLO) printf("OFLO"); if (flags & RxFRAM) printf("FRAM"); printf("\n"); #endif } /* ******************************************************************* * handle tx interrupts... ******************************************************************* */ static void la_txintr(ifp) struct ifnet *ifp; { #ifdef DEBUGTI printf("t"); #endif if (freezer != NULL) { to_cancel(freezer); freezer = NULL; } #ifdef DEBUG if (txring->flags & TxERR) { txerrp(txring->mcnt); if (txring->mcnt & TxLCOL) ifp->if_collisions++; } #endif } /* ******************************************************************* * general service routines... ******************************************************************* */ /* * service routine copy from mbuf to contig lance buffer */ static copy_from_mbufs(buf, m) unsigned char *buf; struct mbuf *m; { int offset; struct mbuf *mp; for(offset=0, mp = m; mp; mp = mp->m_next) { unsigned int len = mp->m_len; unsigned char *mcp; if (len != 0) { mcp = mtod(mp, unsigned char *); bcopy((caddr_t)mcp, (caddr_t)buf, len); offset += len; buf += len; } } return offset; } /* * service routine for la_rxintr */ static struct mbuf * copy_to_mbufs(addr, totlen, ifp) char *addr; int totlen; struct ifnet *ifp; { int len; struct mbuf *m, *top=NULL, **mp = ⊤ char *mcp; spl_t s = splimp(); ifp->if_ipackets++; while(totlen > 0) { MGET(m, M_DONTWAIT, MT_DATA); if (m == NULL) goto bad; len = totlen; if (ifp != NULL) len += sizeof(ifp); if (len >= mincluster) { printf("ctm: >mcl"); MCLGET(m); if (m->m_len == CLBYTES) m->m_len = len = MIN(CLBYTES, len); else m->m_len = len = MIN(MLEN, len); } else { m->m_len = len = MIN(MLEN, len); m->m_off = MMINOFF; } mcp = mtod(m, u_char *); if(ifp != NULL) { *(mtod(m, struct ifnet **)) = ifp; mcp += sizeof(ifp); len -= sizeof(ifp); ifp = NULL; } bcopy(addr, mcp, len); addr += len; *mp = m; mp = &m->m_next; totlen -= len; } splx(s); return top; bad: #ifdef DEBUGI printf("copy_to_mbufs failed, no mbuf\n"); #endif if (top != NULL) m_freem(top); splx(s); return NULL; } ethdump(pkt, len) char *pkt; { struct eth_header *dix = (struct eth_header *)pkt; int i; for(i=0; i<6; i++) printf("%2x:", dix->eth_srce[i] & 0x0ff); printf("->"); for(i=0; i<6; i++) printf("%2x:", dix->eth_dest[i] & 0x0ff); i = *(u_short *)dix->eth_type; printf("(%x)\n", i); pkt = pkt + sizeof(struct eth_header); pktdump(pkt, len); } pktdump(pkt, len) char *pkt; { if (len > 80) len = 80; while(len-- > 0) { printf("%2x ", (int)(*pkt) & 0xff); pkt ++; } printf("\n"); } /* ************************************* * * talk to the lance direct */ /* *Write lance csr with intrs off */ static void lacsrwrite(csr, data) u_short csr, data; { spl_t i = splnet(); ioout(labase + 0x0e, csr); ioout(labase +0x0c, data); splx(i); } /* *write lance csr with intrs as are */ static void ilacsrwrite(csr, data) u_short csr, data; { ioout(labase + 0x0e, csr); ioout(labase +0x0c, data); } /* * read lance CSR */ static u_short lacsread(csr) u_short csr; { u_short ret; spl_t i = splnet(); ioout(labase + 0x0e, csr); ret = ioin(labase + 0x0c); splx(i); return ret; } /* *************************************** * * POS things to do */ static void poswrite(card,reg,val) u_short card, reg, val; { spl_t i = splnet(); iooutb(0x94, BIT5|BIT7); iooutb(0x96, BIT3|card); ioout(0x100|reg, val); /* was iooutb */ splx(i); } static u_short posread(card, reg) u_short card, reg; { u_short ret; spl_t i = splnet(); iooutb(0x94, BIT5|BIT7); iooutb(0x96, BIT3|card); ret = ioin(0x100|reg); /* was ioinb */ splx(i); return ret; } static u_long posfind(id) u_short id; { u_long i; for(i=0; i<8; i++) { if((posread(i, 0x100) << 8) | posread(i, 0x0101) == id) return i; } /* * else no such slot... */ panic("cannot BICC Ether card slot"); /* NOTREACHED */ return -1; } /* * Debugging prints... */ #ifdef DEBUG static lainfo() { int i, j; LanDesc *rdre; u_short stat; stat = lacsread(0); printf("\nla stat %x\n", stat); #ifdef DEBUG2 for(i=0; i< 6; i++) printf("%x.", lanaddr[i]& 0xff); printf("\n"); printf("txt\trxd\tovr\tbad\tsht\tmst\tbab\n"); printf("%d\t%d\t%d\t%d\t%d\t%d\t%d\n", Lastats.txd, Lastats.rxd, Lastats.ovr, Lastats.bad, Lastats.sht, Lastats.mst, Lastats.bab ); for(j=0, rdre = rxring; jaddr[0], rdre->addr[1], rdre->addr[2], rdre->flags, rdre->bcnt, rdre->mcnt); } #endif } #endif