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 */