mlewis@ibmpa.UUCP (Mark S. Lewis) (08/17/89)
Subject: IBM/4.3 6152 Ethernet driver (V1.12) Index: sys/ca_atr/if_un.c Description: Included is a new 6152 ethernet driver for the Ungermann-Bass adapter. It replaces the file sys/ca_atr/if_un.c. It fixes several problems related to the DEC 88 release of the ethernet driver. Fix: Copy sys/ca_atr/if_un.c to sys/ca_atr/if_un.c.orig. Copy this file less the header over sys/ca_atr/if_un.c and build an ATR kernel. In your AUTOEXEC.BAT (or your unix start-up) file, you can comment out (REM) the call to NICPSHH. Also, you must have "IRQ +3" on your DOS call to UNIX.EXE. This makes sure all ethernet interrupts are passed to the kernel. Reboot the new kernel with the modified start-up file. ----------------------- cut here ---------------------------------- /* * P_R_P_Q_# (C) COPYRIGHT IBM CORPORATION 1987 * LICENSED MATERIALS - PROPERTY OF IBM * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083 */ /* $Header: if_un.c,v 12.3 89/08/14 16:41:21 mlewis Exp $ */ #ifndef lint static char *rcsid = "$Header: if_un.c,v 12.3 89/08/14 16:41:21 mlewis Exp $"; #endif lint /* * this driver supports the NICPS2 adapter from Ungermann Bass * which is based on the Intel 82586 chip. See the Intel Microcomunications * handbook for hardware details. */ #define inb(port) IN(port) #define outb(port,value) OUT(port,value) #define swaph(value) ((((value)>>8)&0xff)|(((value)&0xff)<<8)) #define UNIRQ 3 #include "un.h" #include "../machine/pte.h" #include "param.h" #include "systm.h" #include "mbuf.h" #include "buf.h" #include "protosw.h" #include "socket.h" #include "vmmac.h" #include "ioctl.h" #include "errno.h" #include "../net/if.h" #include "../net/netisr.h" #include "../net/route.h" #ifdef INET #include "../netinet/in.h" #include "../netinet/in_systm.h" #include "../netinet/in_var.h" #include "../netinet/ip.h" #include "../netinet/if_ether.h" #endif INET #ifdef NS #include "../netns/ns.h" #include "../netns/ns_if.h" #endif NS #include "../machine/io.h" #include "../machineio/ioccvar.h" #include "time.h" #include "kernel.h" #include "../machine/debug.h" #include "../ca_atr/pcif.h" #define ETH_ADDR_SIZE 6 #define UNCARDID 0xEFF5 /* POS register ID */ /* bits in the CSR registers */ #define INTPNDG 0x80 #define CHINTENB 0x08 #define TMINTENB 0x04 #define LOOPBACK 0x02 #define RESET 0x01 /* Bits in the Transmit Control Blocks */ #define BBIT 0x4000 /* Busy executing command */ #define DONEBIT 0x8000 /* command completed */ #define OKBIT 0x2000 /* error-free completion */ #define ABORTED 0x1000 /* command aborted */ #define NOCARRIER 0x0400 /* no carrier sense */ #define XMIT_BAD 0x0200 /* transmit failed */ #define XMIT_BAD_DMA 0x0100 /* transmit failed (DMA) */ #define XMIT_DEFER 0x0080 /* transmit failed (traffic) */ #define XMIT_HEART 0x0040 /* transmit failed (heartbeat) */ #define XMIT_MAX_COL_16 0x0020 /* more than 16 collisions */ #define XMIT_NCOL(status) ((((status)&XMIT_MAX_COL_16)>>1)|((status)&0xf)) /* number of colisions */ /* Bits in the System Control Block */ #define CX 0x8000 /* the CU finished a command */ #define FR 0x4000 /* the RU got a frame */ #define CNA 0x2000 /* the CU left the ACT state */ #define RNR 0x1000 /* the RU left the RDY state */ #define NO_RESOURCES 0x0020 /* we got an illegal packet */ /* command bits. */ #define CUCSTART 0x100 #define RUCSTART 0x10 #define RUCRESUME 0x20 /* Bits in the Receive Buffer Descriptor (rbd) */ #define ACT_MASK 0x3fff /* received count */ #define EOFBIT 0x8000 /* End of a Frame */ /* Bits in the Frame descriptor command */ #define FDSUSPEND 0x4000 /* Tell receive unit to suspend */ #define FDEOL 0x8000 /* States of Chip */ #define CRESET 0x0001 /* we just reset the chip */ #define CREADY 0x0002 /* turned on rcvr */ #define CBROKEN 0x8000 /* something's screwed */ #define FREE 0 #define BUSY 1 #define RBUFFS 15 /* number of rcvr buffers */ #if NUN > 0 /* * Ethernet software status. */ struct un_softc { struct arpcom es_ac; /* common Ethernet structures */ #define es_if es_ac.ac_if /* network-visable interfaces */ #define es_addr es_ac.ac_enaddr /* hardware ethernet addresses */ long if_buf0; /* number of uses of buf0 */ long if_buf1; /* number of uses of buf0 */ long if_nobuf; /* number of times no buffer */ unsigned short state; /* state of card */ unsigned short tbuffstate[2]; /* transmit buffer state */ unsigned short rec_nm_buf; /* next receive buffer to fill */ unsigned short csr; /* port address */ short us_owatch; /* output watch timer */ short us_iwatch; /* input watch timer */ short us_watch; /* watchdog running */ unsigned int rom_addr; /* address of ROM */ unsigned int ram_addr; /* RAM address */ unsigned int ram_seg; /* RAM segment */ struct scb *scbptr; /* pointer to our scb */ struct buff *tbufptr[2]; /* transmit buffer pointer */ struct buff *rbufptr[RBUFFS]; /* recieve buffer pointers */ struct fd *fdptr[RBUFFS]; /* data pointer */ struct tcb *tcbptr[2]; struct rbd *rbdptr[RBUFFS]; struct tbd *tbdptr[2]; int fill[2]; /* align it conveniently */ } un_softc[NUN]; int un_size = sizeof (struct un_softc); /* patch un_last to large number to disable claiming of stray interrupts */ int un_last = -1; #ifndef UBIWATCH #define UBIWATCH 0 #endif #ifndef UBOWATCH #define UBOWATCH 30 #endif int uniwatch = UBIWATCH; /* no input watchdog */ int unowatch = UBOWATCH; /* 30 second output watchdog */ typedef int spl_t; struct iroot { unsigned short sysbus; unsigned short not_used1; unsigned short not_used2; unsigned short iscp_low; unsigned short iscp_high; }; struct iscp { unsigned short busy; unsigned short scb_offset; unsigned short scb_base_low; unsigned short scb_base_high; }; struct scb { unsigned short status; unsigned short command; unsigned short cbl_offset; unsigned short rfa_offset; unsigned short crcerrs; unsigned short alnerrs; unsigned short rscerrs; unsigned short ovrnerrs; }; struct setup { unsigned short status; unsigned short command; unsigned short link_offset; unsigned short addr0; unsigned short addr1; unsigned short addr2; }; struct tcb { unsigned short status; unsigned short command; unsigned short link_offset; unsigned short tbd_offset; unsigned char dest[6]; unsigned short type; }; struct romaddr { unsigned short bytea; unsigned short byteb; unsigned short bytec; unsigned short byted; unsigned short bytee; unsigned short bytef; }; /* * see Page 1-10 figure 8 for overall structure of RFD's, FD's, RBD's * and DB's. * */ /* recieve frame descriptor (fd) */ struct fd { unsigned short status; unsigned short command; unsigned short link_offset; unsigned short rbd_offset; unsigned short dest_addr[3]; unsigned short source_addr[3]; unsigned short type; /* also known as "length" */ }; /* receive buffer descriptor (see page 1-10) */ struct rbd { unsigned short status; /* recieve frame status */ unsigned short nxt_rbd_offset; /* link field */ unsigned short buff_addr_l; /* buffer address */ unsigned short buff_addr_h; /* " " */ unsigned short size; /* size buffer can hold */ }; /* transmist buffer (frame) descriptor */ struct tbd { unsigned short status; /* eof & act count */ unsigned short nxt_tbd_offset; unsigned short buff_addr_l; unsigned short buff_addr_h; }; struct buff { struct ether_header hdr; char data[1518]; }; int unintr(); #ifdef DEBUG #define ncprintf if (undebug) printf #else #define ncprintf if (0) printf #endif int unprobe(), unattach(); extern int pcif_512_fw; #define PTOKV(addr) (set_512_window(addr) + pcif_512_fw); caddr_t unstd[] = {(caddr_t) 0x00001550, (caddr_t) 0x00001554, (caddr_t) 0x00001558, (caddr_t) 0x0000155c, 0}; struct iocc_device *uninfo[NUN]; int uninit(), unioctl(), unoutput(), unreset(), unwatch(); int unsuspend(); struct iocc_driver undriver = {unprobe, 0, unattach, 0, unstd, "un", uninfo, /* int csr chanrel flags suspend */ 0, 0, unintr, 0, 0, DRIVER_SUSPEND, unsuspend }; #define Offset(off) ((us->ram_addr + off) & 0x0ffff) int undebug = 0; /* debugging flags */ int unnoint = 0; /* don't attempt to cause interrupt */ int noun = 0; /* don't use un */ #define UB_GSFTINT 0x20 /* Generate software interrupt */ #define MAX_PACKET 1518 /* MAX PACKET SIZE */ #define DATA_OFFSET 0x800 /* where to start data buffers */ /* * unprobe - This adapter can only be at interrupt 3 */ unprobe(p) register caddr_t p; { printf("unprobe called: port=%x\n", p); if (noun) return(PROBE_BAD); if (unnoint) return(PROBE_NOINT); /* didn't attempt hardware int */ outb(p,UB_GSFTINT); PROBE_DELAY(200000); outb(p, 0); outb(p+1,0xFF); /* CA */ return (PROBE_OK); /* attempted hardware interrupt */ } int un_output(), un_ioctl(); /* * unattach - make the interface available to the network software * (if the auto-configuration software determines that the interface * exists). The system will initialize the interface when it is * ready to accept packets. */ unattach(iod) register struct iocc_device *iod; { int unit = iod->iod_unit; register struct un_softc *us = &un_softc[unit]; register struct ifnet *ifp = &us->es_if; int old_window = get_512_window(); char *p = (char *) iod->iod_addr; int x = inb(p+1); int y = inb(p+3); int ram = ((x << 8) | ((y & 0x3) << 6)) << 8; int rom; if (unit > un_last) un_last = unit; /* remember last unit */ us->csr = (short) iod->iod_addr; p = (char *) set_512_window(ram) + pcif_512_fw; rom = p[0x29] << 12; /* get the rom addr */ printf("unattach called: port=%x ca=%x pos5=%x ram=%x rom=%x\n", us->csr, x, y, ram,rom); if (rom >= 0xc0000 && rom < 0xdffff) us->rom_addr = rom; /* use it if it looks valid */ else printf("unattach: couldn't find rom address\n"); us->ram_addr = ram; us->ram_seg = us->ram_addr >> 16; /* high order bits */ ifp->if_unit = unit; ifp->if_name = "un"; /* use "un0" etc */ ifp->if_mtu = ETHERMTU; set_512_window(old_window); ifp->if_init = uninit; ifp->if_ioctl = un_ioctl; ifp->if_output = un_output; ifp->if_reset = unreset; ifp->if_flags = IFF_BROADCAST | IFF_NOTRAILERS; if_attach(ifp); DEBUGF(undebug, printf("un%d: attached\n", unit)); } unsuspend(iod, idr, how) register struct iocc_device *iod; struct iocc_driver *idr; { int unit = iod->iod_unit; register struct un_softc *us = &un_softc[unit]; register struct ifnet *ifp = &us->es_if; if (how == SUSPEND_START) { if (ifp->if_flags & IFF_RUNNING) { unzap(unit); ifp->if_flags |= IFF_RUNNING; /* remember we were running */ } } else if (how == SUSPEND_DONE) { if (ifp->if_flags & IFF_RUNNING) { ifp->if_flags &= ~IFF_RUNNING; /* pretend not running */ uninit(unit); } } } uninit(iv) int iv; { spl_t ospl; int i; int ii; int old_window = get_512_window(); struct un_softc *us = &un_softc[iv]; struct ifnet *ifp = &us->es_if; struct iroot *initptr; struct iscp *iscpptr; struct romaddr *romaddrptr; struct setup *setupptr; if (ifp->if_addrlist == (struct ifaddr *) 0) { /* no address */ return; } if (ifp->if_flags & IFF_RUNNING) return; /* already running! */ DEBUGF(undebug, printf("un%d: uninit ram=%x rom=%x\n", iv,us->ram_addr,us->rom_addr)); ifp->if_flags |= IFF_RUNNING; /* */ /* System D7FDE STATUS */ /* Control D7FE0 COMMAND */ /* Block D7FE2 CBL OFFSET */ /* D7FE4 RFA OFFSET */ /* D7FE6 CRCERRS */ /* D7FE8 ALNERRS */ /* D7FEA RSCERRS */ /* D7FEC OVRNERRS */ /* */ /* Intermediate D7FEE BUSY = 1 (reset) */ /* System D7FF0 SCB OFFSET = 0x7FDE */ /* Configuration D7FF2 SCB BASE (lo) = 0x0000 */ /* Pointer D7FF4 SCB BASE (hi) = 0x000D */ /* */ /* Initialization D7FF6 SYSBUS = 0 (16 bit bus) */ /* Root D7FF8 not used */ /* D7FFA not used */ /* D7FFC ISCP ADDR (lo) = 0x7FEE */ /* D7FFE ISCP ADDR (hi) = 0x000D */ /* */ set_pcvec_map(UNIRQ, 0); /* disable int forwarding */ initptr = (struct iroot *) PTOKV(us->ram_addr + 0x7FF6); initptr->sysbus = swaph(0); /* 16 bit bus */ initptr->not_used1 = swaph(0); initptr->not_used2 = swaph(0); initptr->iscp_low = swaph(Offset(0x7FEE)); initptr->iscp_high = swaph(us->ram_seg); iscpptr = (struct iscp *) PTOKV(us->ram_addr + 0x7FEE); iscpptr->busy = swaph(0x01); /* init in progress */ iscpptr->scb_offset = swaph(Offset(0x7FDE)); iscpptr->scb_base_low = swaph(Offset(0x0)); iscpptr->scb_base_high = swaph(us->ram_seg); us->scbptr = (struct scb *) PTOKV(us->ram_addr + 0x7FDE); us->scbptr->status = 0; us->scbptr->command = 0; us->scbptr->cbl_offset = 0; us->scbptr->rfa_offset = 0; us->scbptr->crcerrs = 0; us->scbptr->alnerrs = 0; us->scbptr->rscerrs = 0; us->scbptr->ovrnerrs = 0; outb(us->csr,(CHINTENB | RESET)); DELAY(10); outb(us->csr, CHINTENB); outb(us->csr+1,0xFF); /* CA */ while (!(inb(us->csr) & INTPNDG)); DEBUGF(undebug, printf("un%d: uninit reset done\n", iv)); us->state = CRESET; us->rec_nm_buf = 0; /* which rcvr buffer we use */ romaddrptr = (struct romaddr *) PTOKV(us->rom_addr + 0x10); setupptr = (struct setup *) PTOKV(us->ram_addr + 0x7FD2); setupptr->status = 0; setupptr->command = swaph(0x8001); /* EL + SETUP */ setupptr->link_offset = 0; setupptr->addr0 = ((romaddrptr->byteb) >> 8) | romaddrptr->bytea; setupptr->addr1 = ((romaddrptr->byted) >> 8) | romaddrptr->bytec; setupptr->addr2 = ((romaddrptr->bytef) >> 8) | romaddrptr->bytee; #define LAN_ADDR_SIZE ETH_ADDR_SIZE bcopy((char *) &setupptr->addr0, us->es_addr, LAN_ADDR_SIZE); printf("un%d: ethernet address ", iv); unprintethaddr(us->es_addr); printf("\n"); ospl = splimp(); while (us->scbptr->command != 0); us->scbptr->command = swaph(CUCSTART); us->scbptr->cbl_offset = swaph(Offset(0x7FD2)); outb(us->csr+1,0xFF); /* CA */ splx(ospl); DEBUGF(undebug, printf("un%d: uninit cucstart done\n", iv)); for (i=0; !(setupptr->status & swaph(OKBIT));++i) if (i > 1000) { printf("un%d: timed out waiting for OKBIT\n",iv); break; } else delay(1); /* build fd's backwards from offset 7FBC */ us->fdptr[0] = (struct fd *) PTOKV(us->ram_addr + 0x7FBC); for (i = 1; i < RBUFFS; i++) us->fdptr[i] = us->fdptr[i-1] - 1; /* allocate rbd's just before fd's */ us->rbdptr[0] = ((struct rbd *) us->fdptr[RBUFFS-1]) - 1; for (i = 1; i < RBUFFS; i++) us->rbdptr[i] = us->rbdptr[i-1] - 1; /* allocate tcb's before rbd's */ us->tcbptr[0] = ((struct tcb *) us->rbdptr[RBUFFS-1]) - 1; us->tcbptr[1] = us->tcbptr[0] - 1; /* allocate tbd's before tcb's */ us->tbdptr[0] = ((struct tbd *) us->tcbptr[1]) - 1; us->tbdptr[1] = us->tbdptr[0] - 1; /* point scb's rfa_offset to first fd */ us->scbptr->rfa_offset = swaph((unsigned short) us->fdptr[0]); /* chain the fd's together via the link_offset */ for (i=0 ; i < RBUFFS; i++) us->fdptr[i]->link_offset = swaph((ushort) us->fdptr[((i+1)%RBUFFS)]); /* initialize the fd's to point to the appropriate rbd's */ for (i = 0; i < RBUFFS; i++) { us->fdptr[i]->rbd_offset = swaph((ushort) us->rbdptr[i]); us->fdptr[i]->status = 0; us->fdptr[i]->command = 0; us->fdptr[i]->type = 0; for (ii = 0; ii < 3; ii++) { us->fdptr[i]->dest_addr[ii] = 0; us->fdptr[i]->source_addr[ii] = 0; } } /* suspend if we use the last fd */ us->fdptr[RBUFFS-1]->command |= swaph(FDSUSPEND); /* Overrun protection */ /* initialize the first data buffer pointer to start of ram */ us->rbufptr[0] = (struct buff *) PTOKV(us->ram_addr+DATA_OFFSET); /* initialize the rest of the data buffer pointers */ for( i = 1; i < RBUFFS; i++) us->rbufptr[i] = us->rbufptr[i-1] + 1; for (i = 0; i < RBUFFS; i++) { us->rbdptr[i]->buff_addr_l = swaph((ushort) &(us->rbufptr[i]->data[0])); us->rbdptr[i]->buff_addr_h = swaph(us->ram_seg); us->rbdptr[i]->status = 0; us->rbdptr[i]->nxt_rbd_offset = 0; us->rbdptr[i]->size = swaph(0x85EE); /* 1518(0x5ee)+last RBD(0x8000)*/ } /* transmit buffers follows receive buffers */ us->tbufptr[0] = us->rbufptr[RBUFFS-1] + 1; us->tbufptr[1] = us->tbufptr[0] + 1; DEBUGF(undebug, printf("un%d: free buffer space=%d\n", iv, (caddr_t) (us->tbdptr[1] - 1) - (caddr_t) (us->tbufptr[1]+1))); us->tbuffstate[0] = FREE; /* mark xmit buffers as free */ us->tbuffstate[1] = FREE; us->tcbptr[0]->status = 0; us->tcbptr[1]->status = 0; DEBUGF(undebug, printf("un%d: uninit calling ostart\n", iv)); /* Attach this device to the outside world! */ un_ostart(&us->es_if); if (!us->us_watch) { timeout(unwatch, (caddr_t) iv, hz); us->us_watch++; /* timer running */ } done: set_pcvec_map(UNIRQ, 1); /* enable int forwarding */ set_512_window(old_window); DEBUGF(undebug, printf("un%d: uninit done\n", iv)); } /* * note: since this adapter appears to cause stray interrupts that it * later disclaims, we use un_last to determine if we are the last un adapter * and always claims interrupts in this case. This does not work if there is * anything else at this interrupt level, so the un addapter must be configured * so that it ends up last on the chain. */ unintr(iv) int iv; /* board we are going to service */ { unsigned short build_comm; int old_window = get_512_window(); struct un_softc *us = &un_softc[iv]; struct scb *scb = us->scbptr; /* get scb pointer */ register struct ifnet *ifp = &us->es_if; if (! (inb(us->csr) & INTPNDG)) { DEBUGF(undebug&SHOW_INTR, printf("un%d: unintr - no int\n",iv)); return(iv != un_last); /* 1 = not our interrupt */ } (void) set_512_window(us->ram_addr); /* set to proper 512k window */ DEBUGF(undebug&SHOW_INTR, printf("un%d: unintr\n",iv)); build_comm = 0; if (us->state & CRESET) { ncprintf("unintr: CRESET was set (starting receiver)\n"); ncprintf("unintr: CRESET board %d\n",iv); us->state = CREADY; while (scb->command != 0) ; scb->command = swaph(CX | CNA | RUCSTART); /* start rcvr */ outb(us->csr+1, 0xff); /* CA */ while (scb->command != 0) ; goto out; } if (scb->status & swaph(FR)) { /* rcvd a frame */ build_comm |= FR; unrint(iv); } if (scb->status & swaph(CX)) { /* xmitted a frame */ us->us_owatch = 0; /* set watchdog timer */ build_comm |= CX; if ((us->tbuffstate[0] == BUSY) && (us->tcbptr[0]->status & swaph(DONEBIT))) { int status = swaph(us->tcbptr[0]->status); us->tbuffstate[0] = FREE; ifp->if_collisions += XMIT_NCOL(status); if (status&OKBIT) ifp->if_opackets++; else ifp->if_oerrors++; us->tcbptr[0]->status = 0; } if ((us->tbuffstate[1] == BUSY) && (us->tcbptr[1]->status & swaph(DONEBIT))) { int status = swaph(us->tcbptr[1]->status); ifp->if_collisions += XMIT_NCOL(status); if (status&OKBIT) ifp->if_opackets++; else ifp->if_oerrors++; us->tbuffstate[1] = FREE; us->tcbptr[1]->status = 0; } } if (scb->status & swaph(CNA)) { build_comm |= CNA; } if (scb->status & swaph(NO_RESOURCES)) { build_comm |= (RUCSTART|RNR); scb->rfa_offset=swaph((unsigned short)us->fdptr[us->rec_nm_buf]); ncprintf("unintr: restart after non-802.3 packet\n"); } if ((scb->status & swaph(RNR)) && (!(scb->status & swaph(NO_RESOURCES)))) { /* The receive unit ran out of frames... restart it, since * unrint must have read in all the packets. */ ncprintf("unintr: restarting receiver (ran out of bufs)\n"); build_comm |= (RUCRESUME|RNR); /* resume receiving */ } while (scb->command != 0) ; scb->command = swaph(build_comm); outb(us->csr+1,0xFF); /* CA */ if (build_comm & CX) un_ostart(&us->es_if); out: DEBUGF(undebug&SHOW_INTR, printf("un%d: unintr done crc=%d ovrn=%d aln=%d rsc=%d\n",iv,swaph(scb->crcerrs),swaph(scb->ovrnerrs),swaph(scb->alnerrs),swaph(scb->rscerrs))); if (scb->crcerrs || scb->alnerrs) { /* got some input errors - count them */ ifp->if_ierrors += swaph(scb->crcerrs) + swaph(scb->alnerrs); scb->crcerrs = 0; scb->alnerrs = 0; } set_512_window(old_window); return(0); } /* * Pegasus receive interrupt. */ unrint(iv) int iv; /* which board */ { spl_t s; int stat; int len, len_or_type; int prev_nm_buf; register struct ether_header *ehp; struct un_softc *us = &un_softc[iv]; us->us_iwatch = uniwatch; /* got an interrupt */ DEBUGF(undebug&SHOW_INTR, printf("un%d: unrint\n",iv)); for (; len = (us->rbdptr[us->rec_nm_buf]->status); us->rec_nm_buf=(us->rec_nm_buf+1)%RBUFFS) { len = swaph(len); /* byte swap len */ stat = (us->fdptr[us->rec_nm_buf]->status); stat = swaph(stat); /* swap it */ if (!(stat & DONEBIT)) { ncprintf("unrint: stat %x, not complete\n", stat); break; } /* NEEDSWORK: receiver should be shut down */ if (! (us->es_if.if_flags & IFF_UP)) goto discard; if (!(stat & (OKBIT|BBIT))) { /* Discard bad (not "ok") frames. */ ncprintf("unintr: got not OK packet %x\n", stat); goto discard; } /* * copy in the header to prefix the packet data to * make access simple. */ bcopy(&(us->fdptr[us->rec_nm_buf]->dest_addr[0]), us->rbufptr[us->rec_nm_buf], sizeof(*ehp)); /* clear flag bits in "len": its now length of data portion */ len &= ACT_MASK; /* * At this point we have the frame and the header * If we want the whole frame we use rbufptr[x] * If we want just the data rbufptr[x]->data[0] * the packet type is in fdptr[x]->type * as well as being in the header * the size of the frame is in rbdptr[x]->status * where x = rec_nm_buf */ /* * Process the packet */ s = splimp(); ehp = (struct ether_header *) us->rbufptr[us->rec_nm_buf]; DEBUGF(undebug&SHOW_DATA, unprintpacket("unrint: ",ehp,"\n")); #define eth_type ether_type len_or_type = ntohs(* (u_short *) &ehp->eth_type); if (len_or_type > ETHERMTU) { /* KLUDGE!!! */ struct mbuf *m = NULL; struct mbuf *unget(); switch (len_or_type) { #define LANTYPE_IP ETHERTYPE_IP case LANTYPE_IP: m = unget((char *) (ehp + 1), len, &us->es_if); if (m == NULL) break; if (IF_QFULL(&ipintrq)) { IF_DROP(&ipintrq); m_freem(m); break; } IF_ENQUEUE(&ipintrq, m); schednetisr(NETISR_IP); break; #define LANTYPE_ARP ETHERTYPE_ARP case LANTYPE_ARP: m = unget((char *) (ehp + 1), len, &us->es_if); if (m == NULL) break; arpinput(&us->es_ac, m); break; default: DEBUGF(undebug, printf("un%d: unknown packet type %x\n",iv,len_or_type)); break; } } else DEBUGF(undebug, printf("un%d: unknown len or type %x\n",iv,len_or_type)); #ifdef notdef { struct mbuf *m = NULL; struct mbuf *unget(); #define eth_802_header ether_header struct eth_802_header *eh; eh = (struct eth_802_header *) ehp; len += sizeof(struct ether_header); if (len < sizeof(*eh)) { printf("unrint: short packet %d\n", len); splx(s); goto discard; } #ifdef TEST_CONTROL switch (eh->eth_llc.llc_ctrl & ~CTRL_P_OR_F) { case TEST_CONTROL: m = unget((char *) eh, len,&us->es_if); if (m == NULL) { splx(s); goto discard; } lan_test_recv(&us->es_if, m); goto discard; case XID_CONTROL: m = unget((char *) eh, len,&us->es_if); if (m == NULL) { splx(s); goto discard; } lan_xid_recv(&us->es_if, m); goto discard; case UI_CONTROL: break; default: printf("unrint: bad LLC control field %d\n", eh->eth_llc.llc_ctrl); splx(s); goto discard; } #endif switch (ntohs(* (u_short *) eh->eth_snap.snap_type)) { #define eth_snap ether_snap case LANTYPE_IP: m = unget((char *) (eh + 1), len - sizeof(*eh), &us->es_if); if (m == NULL) break; if (IF_QFULL(&ipintrq)) { IF_DROP(&ipintrq); m_freem(m); break; } IF_ENQUEUE(&ipintrq, m); schednetisr(NETISR_IP); break; case LANTYPE_ARP: m = unget((char *) (eh + 1), len - sizeof(*eh), &us->es_if); if (m == NULL) break; arpinput(&us->es_if, m); break; default: break; } } #endif notdef splx(s); discard: /* So this driver knows we've read this frame. */ us->rbdptr[us->rec_nm_buf]->status = 0; /* Now advance the "SUSPEND" indicator from previous buffer to * the receive buffer we just read in. */ prev_nm_buf = us->rec_nm_buf? us->rec_nm_buf - 1: RBUFFS-1; us->fdptr[prev_nm_buf]->command &= ~swaph(FDSUSPEND); us->fdptr[us->rec_nm_buf]->command |= swaph(FDSUSPEND); } } /* * unxmit -- transmit a buffer -- before calling, you must: * find a free buffer by checking tbuffstate[bufnum] ( 0 = free ) * copy your entire eth packet into tbufptr[bufnum]->{hdr,data}. */ unxmit(bufnum, len, iv) u_int bufnum; u_int len; /* Length of entire packet, including header */ u_int iv; /* which board */ { register struct tcb *tcbp; register struct tbd *tbdp; register struct buff *tbp; struct un_softc *us = &un_softc[iv]; DEBUGF(undebug&SHOW_IO, printf("un%d: unxmit bufnum=%d len=%d\n",iv,bufnum,len)); if (bufnum > 1) panic("unxmit"); tcbp = us->tcbptr[bufnum]; tbp = us->tbufptr[bufnum]; tbdp = us->tbdptr[bufnum]; /* Copy dest & type to where card expects to find them. */ #define eth_dhost ether_dhost #define eth_shost ether_shost bcopy(tbp->hdr.eth_dhost, tcbp->dest, ETH_ADDR_SIZE); /* bcopy(&tbp->hdr.eth_type, tcbp->type, 2); */ tcbp->type = (tbp->hdr.eth_type); /* length ?? */ DEBUGF(undebug&SHOW_IO, unprintpacket("unxmit: ",&tbp->hdr,"\n")); tcbp->command = swaph(0xA004); /* EL & I & XMT */ tcbp->link_offset = 0; tcbp->tbd_offset = swaph((unsigned short) tbdp); /* tbdp->status = swaph((len - sizeof(struct ether_header)) | EOFBIT); */ tbdp->status = swaph((len) | EOFBIT); tbdp->nxt_tbd_offset = 0; tbdp->buff_addr_l = swaph((unsigned short) tbp->data); tbdp->buff_addr_h = swaph(us->ram_seg); while (us->scbptr->command != 0); us->scbptr->command = swaph(CUCSTART); us->scbptr->cbl_offset = swaph((unsigned short) tcbp); outb(us->csr+1,0xFF); /* CA */ us->us_owatch = unowatch; } struct mbuf * unget(addr, totlen, ifp) register u_char *addr; register int totlen; struct ifnet *ifp; { register int len; register struct mbuf *m; struct mbuf *top = NULL, **mp = ⊤ u_char *mcp; ++ifp->if_ipackets; DEBUGF(undebug&SHOW_IO, { int i; printf("un%d: unget addr=%x",ifp->if_unit,addr); if(undebug&SHOW_DATA) for (i=0; i<16; ++i) printf(" %x",addr[i]); printf("\n");}); while (totlen > 0) { MGET(m, M_DONTWAIT, MT_DATA); if (m == NULL) goto bad; len = totlen; if (ifp != NULL) len += sizeof(ifp); #ifdef notdef /* Should we try to use a cluster for efficiency? */ if (len >= mincluster) { MCLGET(m); if (m->m_len == CLBYTES) m->m_len = len = MIN(CLBYTES, len); else m->m_len = len = MIN(MLEN, len); } else #endif { 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; } return top; bad: m_freem(top); DEBUGF(undebug&SHOW_IO, printf("un%d: unget BAD\n",ifp->if_unit)); return NULL; } un_output(ifp, m0, dst) struct ifnet *ifp; struct mbuf *m0; struct sockaddr *dst; { int error; struct mbuf *m = m0, *mcopy = NULL; register struct ether_header *ec; #ifdef notdef struct lan_arp *ah; #endif char edst[LAN_ADDR_SIZE]; int usetrailers; /* N.B. this is currently ignored */ struct in_addr idst; short type; spl_t s; extern struct ifnet loif; int old_window = get_512_window(); int iv = ifp->if_unit; struct un_softc *us = &un_softc[iv]; (void) set_512_window(us->ram_addr); /* set to proper 512k window */ DEBUGF(undebug&SHOW_IO, printf("un%d: unoutput\n",iv)); if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { error = ENETDOWN; goto bad; } switch (dst->sa_family) { #ifdef INET case AF_INET: idst = ((struct sockaddr_in *) dst)->sin_addr; if (! arpresolve(&us->es_ac,m,&idst,edst,&usetrailers)) { set_512_window(old_window); return 0; /* not resolved */ } if (! bcmp(edst, etherbroadcastaddr, ETH_ADDR_SIZE)) mcopy = m_copy(m, 0, M_COPYALL); type = LANTYPE_IP; break; #endif INET #ifdef AF_ARP case AF_ARP: ah = mtod(m, struct lan_arp *); bcopy(ah->arp_tha, edst, LAN_ADDR_SIZE); type = LANTYPE_ARP; break; #endif case AF_UNSPEC: ec = (struct ether_header *) dst->sa_data; bcopy((caddr_t) ec->eth_dhost, (caddr_t) edst, sizeof(edst)); #ifdef notdef type = NO_SNAP; #else type = ec->ether_type; #endif break; default: ncprintf("un_output: cannot handle address family 0x%x\n", dst->sa_family); error = EAFNOSUPPORT; goto bad; } #ifdef IFF_IEEE if (!(ifp->if_flags & IFF_IEEE)) /* Xerox ethernet header */ #endif { if ((m->m_off > MMAXOFF) || ((MMINOFF + sizeof(struct ether_header)) > m->m_off)) { m = m_get(M_DONTWAIT, MT_HEADER); if (m == NULL) { error = ENOBUFS; goto bad; } m->m_next = m0; m->m_off = MMINOFF; m->m_len = sizeof(struct ether_header); } else { m->m_off -= sizeof(struct ether_header); m->m_len += sizeof(struct ether_header); } ec = mtod(m, struct ether_header *); bcopy(edst, (caddr_t) ec->eth_dhost, LAN_ADDR_SIZE); bcopy(us->es_addr, (caddr_t) ec->eth_shost, LAN_ADDR_SIZE); ec->eth_type = htons(type); } #ifdef IFF_IEEE else { /* IEEE802.3 header */ register struct eth_mac_header *mh; register int header_size; header_size = sizeof(struct lan_llc_header) + sizeof(struct lan_snap_header); switch (type) { case LANTYPE_IP: case LANTYPE_ARP: if ((m->m_off > MMAXOFF) || ((MMINOFF + header_size) > m->m_off)) { m = m_get(M_DONTWAIT, MT_HEADER); if (m == NULL) { error = ENOBUFS; goto bad; } m->m_next = m0; m->m_off = MMINOFF; m->m_len = header_size; m0 = m; } else { m->m_off -= header_size; m->m_len += header_size; } fill_llc_header(mtod(m, struct lan_llc_header *), type); /* FALLSTHROUGH */ case NO_SNAP: if ((m->m_off > MMAXOFF) || ((MMINOFF + sizeof(*mh)) > m->m_off)) { m = m_get(M_DONTWAIT, MT_HEADER); if (m == NULL) { error = ENOBUFS; goto bad; } m->m_next = m0; m->m_off = MMINOFF; m->m_len = sizeof(*mh); } else { m->m_off -= sizeof(*mh); m->m_len += sizeof(*mh); } /* Fill in ethernet MAC header */ mh = mtod(m, struct eth_mac_header *); bcopy(edst, (caddr_t) mh->eth_dhost, LAN_ADDR_SIZE); bcopy(us->es_addr, (caddr_t) mh->eth_shost, LAN_ADDR_SIZE); /* mh->eth_len = XXX; */ /* Set later */ } } #endif s = splimp(); if (IF_QFULL(&ifp->if_snd)) { IF_DROP(&ifp->if_snd); error = ENOBUFS; m0 = m; splx(s); goto bad; } IF_ENQUEUE(&ifp->if_snd, m); un_ostart(ifp); splx(s); set_512_window(old_window); return (mcopy ? looutput(&loif, mcopy, dst) : NULL); bad: m_freem(m0); if (mcopy) m_freem(mcopy); set_512_window(old_window); return error; } un_ostart(ifp) register struct ifnet *ifp; { int iv = ifp->if_unit; register struct un_softc *us = &un_softc[iv]; int bufnum; struct mbuf *m, *m0; register char *bufp; register unsigned short length; spl_t s; s = splimp(); DEBUGF(undebug&SHOW_IO, printf("un%d: unostart\n",iv)); #define IF_EMPTYQUEUE(queue) ((queue)->ifq_head == 0) while (! IF_EMPTYQUEUE(&ifp->if_snd)) { /* find a free buffer to put a packet in */ if (us->tbuffstate[0] == FREE) { us->tbuffstate[0] = BUSY; bufnum = 0; ++us->if_buf0; } else if (us->tbuffstate[1] == FREE) { us->tbuffstate[1] = BUSY; bufnum = 1; ++us->if_buf1; } else { ++us->if_nobuf; break; } IF_DEQUEUE(&ifp->if_snd, m); bufp = (char *) us->tbufptr[bufnum]; length = 0; for (m0 = m; m0 != NULL; m0 = m0->m_next) { bcopy(mtod(m0, char *), bufp, m0->m_len); bufp += m0->m_len; length += m0->m_len; } #ifdef IFF_IEEE /* For IEEE802.3 we need to fill in the length */ if (ifp->if_flags & IFF_IEEE) { bufp = (char *) us->tbufptr[bufnum]; ((struct eth_mac_header *) bufp)->eth_len = htons(length - sizeof(struct eth_mac_header)); } #endif IFF_IEEE length = MAX(length, ETHERMIN); m_freem(m); unxmit(bufnum, length, iv); } splx(s); DEBUGF(undebug&SHOW_IO, printf("un%d: unostart done\n",iv)); return; } un_ioctl(ifp, cmd, data) struct ifnet *ifp; int cmd; caddr_t data; { register struct ifaddr *ifa = (struct ifaddr *) data; int error = 0; spl_t s; DEBUGF(undebug&SHOW_IO, printf("un%d: unioctl cmd=%x\n",ifp->if_unit,cmd)); s = splimp(); switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr.sa_family) { #ifdef INET case AF_INET: uninit(ifp->if_unit); /* before arpwhohas */ ((struct arpcom *)ifp)->ac_ipaddr=IA_SIN(ifa)->sin_addr; arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); break; #endif INET default: uninit(ifp->if_unit); break; } break; case SIOCSIFFLAGS: ifp->if_flags |= IFF_NOTRAILERS; /* kill trailers */ if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { unzap(ifp->if_unit); } else if (ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0) uninit(ifp->if_unit); break; default: error = EINVAL; break; } splx(s); DEBUGF(undebug&SHOW_IO, printf("un%d: unioctl done\n",ifp->if_unit)); return error; } unreset(unit) register unsigned int unit; { register struct iocc_device *iod; if (unit < NUN && (iod = uninfo[unit]) != 0 && iod->iod_alive != 0) { if (un_softc[unit].es_if.if_flags & IFF_RUNNING) { DEBUGF(undebug, printf("un%d: reset\n", unit)); unzap(unit); } } } /* * turn off the hardware via a reset thru the CSR */ static unzap(unit) register unsigned int unit; { register struct un_softc *us = &un_softc[unit]; DEBUGF(undebug, printf("un%d: unzap\n", unit)); un_softc[unit].es_if.if_flags &= ~IFF_RUNNING; /* not running */ outb(us->csr,(RESET)); DELAY(10); outb(us->csr, 0); outb(us->csr+1,0xFF); /* CA */ delay(1); DEBUGF(undebug, printf("un%d: unzap done\n", unit)); } /* * unprintethaddr - print an ethernet address */ static unprintethaddr(p) register char *p; { register int i; for (i = 0; i < ETH_ADDR_SIZE; i++) { if (i != 0) printf(":"); printf("%x", *p++); } } #ifdef DEBUG static char *ethertype(type) { if (type == ETHERTYPE_IP) return("ip"); if (type == ETHERTYPE_ARP) return("arp"); return("?"); } static unprintpacket(prefix,eh,postfix) register struct ether_header *eh; char *prefix, *postfix; { char cbuf[6]; printf("%sfrom = ",prefix); bcopy(eh->ether_shost, cbuf, sizeof(cbuf)); unprintethaddr(cbuf); printf(" to = "); bcopy(eh->ether_dhost, cbuf, sizeof(cbuf)); unprintethaddr(cbuf); printf(" type=%s(%x) %s",ethertype(eh->ether_type),eh->ether_type,postfix); } #endif /* DEBUG */ /* * watch routine. We check to see if we've not gotten any transmit or * receive interrupts lately and attempt to restart if necessary. * We assume that on an ethernet with at least one other active machine * that we will get at least one broadcast packet per timeout period. */ unwatch(arg) caddr_t arg; { register int unit = (int) arg; register struct un_softc *us = &un_softc[unit]; register struct ifnet *ifp = &us->es_if; int restart = 0; if ((ifp->if_flags & IFF_RUNNING) != 0) { if (us->us_owatch && --us->us_owatch==0) { printf("un%d: lost output interrupt - restarting.\n", unit); ++restart; } if (us->us_iwatch && --us->us_iwatch==0) { DEBUGF(undebug, printf("un%d: lost input interrupt?? - restarting.\n", unit);); ++restart; } if (restart) { unzap(unit); /* clears RUNNING */ uninit(unit); } timeout(unwatch, (caddr_t) unit, hz); } else us->us_watch = 0; /* if not running don't watch it */ } #endif /* NUN > 0 */