Return-Path: Received: from merlin.ARPA (purdue-merlin.ARPA) by mit-borax.ARPA (4.12/4.7) id AA05180; Wed, 30 Jan 85 07:23:42 est From: Christopher A Kent Message-Id: <8501301223.AA05191@merlin.ARPA> Received: by merlin.ARPA; Wed, 30 Jan 85 07:23:35 est Date: 30 Jan 1985 0723-EST (Wednesday) To: dab@mit-borax.ARPA (David A. Bridgham) Subject: Re: deqna driver In-Reply-To: Your message of 29 Jan 1985 2115-EST (Tuesday). <8501300215.AA03334@mit-borax.ARPA> It's true. Here is what I have running as a small standalone pdp-11 program. I hope it helps -- there are lots of bugs in the documentation. If you have any questions, feel free to ask. Cheers, chris ------------- /* $Header: /c/cak/src/deqna/RCS/if_ether.h,v 1.2 85/01/23 15:51:27 cak Exp $ */ /* * if_ether.h - 10 Mb/s ethernet constants and such * * Author: Christopher A. Kent * Dept. of Computer Sciences * Purdue University * Date: Sat Jan 12 1985 */ /* * $Log: if_ether.h,v $ * Revision 1.2 85/01/23 15:51:27 cak * Added loopback ethertype (0x0060). * * Revision 1.1 85/01/22 21:52:46 cak * Initial revision * */ /* * Some basic types (these for the pdp-11). */ typedef char u_char; /* 11 doesn't really do unsigned char */ typedef unsigned int u_short; /* * Structure of a 10Mb/s Ethernet address. */ struct ether_addr { u_char octet[6]; }; typedef struct ether_addr EtherAddr; /* * Structure of a 10Mb/s Ethernet header. */ struct ether_header { EtherAddr ether_dhost; EtherAddr ether_shost; u_short ether_type; }; typedef struct ether_header EtherHeader; /* * Fundamental 10 Mb/s Ethernet constants. */ #define ETHERMIN 64 /* minimum packet size */ #define ETHERMAX 1500 /* maximum packet size */ #define ETHER_MAX_DATA (ETHERMAX - sizeof(EtherHeader)) /* * Structure of a 10 Mb/s Ethernet packet. */ struct ether_packet { struct ether_header ep_header; u_char ep_data[ETHER_MAX_DATA]; }; typedef struct ether_packet EtherPacket; #define ep_dhost ep_header.ether_dhost #define ep_shost ep_header.ether_shost #define ep_type ep_header.ether_type /* * Ethernet Address Resolution Protocol. * * See RFC 826 for protocol description. Structure below is adapted * to resolving internet addresses. Field names used correspond to * RFC 826. */ struct ether_arp { u_short arp_hrd; /* format of hardware address */ #define ARPHRD_ETHER 1 /* ethernet hardware address */ u_short arp_pro; /* format of proto. address (ETHER_IP) */ u_char arp_hln; /* length of hardware address (6) */ u_char arp_pln; /* length of protocol address (4) */ u_short arp_op; #define ARP_REQUEST 1 /* request to resolve address */ #define ARP_REPLY 2 /* response to previous request */ EtherAddr arp_sha; /* sender hardware address */ u_char arp_spa[4]; /* sender protocol address */ EtherAddr arp_tha; /* target hardware address */ u_char arp_tpa[4]; /* target protocol address */ }; /* * 10 Mb/s packet types. */ #define ETHER_LOOP 0x0060 /* loopbacks */ #define ETHER_ECHO 0x0200 /* echoes */ #define ETHER_PUP 0x0400 /* PUP protocol */ #define ETHER_IP 0x0800 /* IP protocol */ #define ETHER_ARP 0x0806 /* Addr. resolution protocol */ --------- /* $Header: /c/cak/src/deqna/RCS/if_qereg.h,v 1.3 85/01/24 16:24:28 cak Exp $ */ /* * if_qereg.h - DEQNA register descriptions * * Author: Christopher A. Kent * Dept. of Computer Sciences * Purdue University * Date: Sat Jan 12 1985 * Copyright (c) 1985 Christopher A. Kent * May be freely redistributed provide this notice remains intact * in all copies or derivatives. */ /* * $Log: if_qereg.h,v $ * Revision 1.3 85/01/24 16:24:28 cak * Minor cleanup. * * Revision 1.2 85/01/24 15:22:39 cak * Missed the unused 0040000 bit in the _bits defn in qe_csr. * * Revision 1.1 85/01/22 03:44:03 cak * Initial revision * */ struct qedevice { union { u_short _physAddr[sizeof(EtherAddr)]; struct { u_short _unused[2]; u_short _rcvLoBDL; /* low 16 bits of addr */ u_short _rcvHiBDL; /* high bits + status*/ u_short _xmitLoBDL; /* as for receive */ u_short _xmitHiBDL; u_short _vector; /* interrupt vector */ u_short _csreg; /* Control and Status */ }_regs; }_dev; }; #define qe_physAddr _dev._physAddr #define qe_rcvLoBDL _dev._regs._rcvLoBDL #define qe_rcvHiBDL _dev._regs._rcvHiBDL #define qe_xmitLoBDL _dev._regs._xmitLoBDL #define qe_xmitHiBDL _dev._regs._xmitHiBDL #define qe_vector _dev._regs._vector #define qe_csreg _dev._regs._csreg /* * Control and Status Register */ #define QE_RCV_ENBL 0000001 /* Receiver Enable */ #define QE_RESET 0000002 /* Reset */ #define QE_NXM 0000004 /* Nonexistent Memory Interrupt */ #define QE_BDROM 0000010 /* Boot/Diagnostic ROM */ #define QE_XL_INV 0000020 /* Transmit List Invalid */ #define QE_RL_INV 0000040 /* Receive List Invalid */ #define QE_IE 0000100 /* Interrupt Enable */ #define QE_XMIT_IR 0000200 /* Transmit Interrupt Request */ #define QE_INT_LOOP 0000400 /* Internal Loopback (active low!) */ #define QE_EXT_LOOP 0001000 /* External Loopback */ #define QE_SANITY_ENBL 0002000 /* Sanity Timer Enable */ /* 0004000 reserved */ #define QE_FUSE 0010000 /* Bulkhead Fuse OK */ #define QE_CARRIER 0020000 /* Carrier Present */ /* 0040000 reserved */ #define QE_RCV_IR 0100000 /* Receive Interrupt Request */ struct qe_csr { union { u_short _register; struct { u_short _rcvEnable:1, _reset:1, _nxmIntr:1, _bootDiagROM:1, _xlInval:1, _rlInval:1, _ie:1, _xmitIR:1, _intLoopBack:1, _extLoopBack:1, _sanityEnable:1, :1, _fuseOK:1, _carrier:1, :1, _rcvIR:1; }_bits; }_csr; }; /* this must fit in two bytes! */ #define csr_rcvEnable _csr._bits._rcvEnable #define csr_reset _csr._bits._reset #define csr_nxmIntr _csr._bits._nxmIntr #define csr_bootDiagROM _csr._bits._bootDiagROM #define csr_xlInval _csr._bits._xlInval #define csr_rlInval _csr._bits._rlInval #define csr_ie _csr._bits._ie #define csr_xmitIR _csr._bits._xmitIR #define csr_intLoopBack _csr._bits._intLoopBack #define csr_extLoopBack _csr._bits._extLoopBack #define csr_sanityEnable _csr._bits._sanityEnable #define csr_fuseOK _csr._bits._fuseOK #define csr_carrier _csr._bits._carrier #define csr_rcvIR _csr._bits._rcvIR /* * DEQNA Buffer Descriptor */ /* flag word */ /* all other bits unused */ #define QE_BUF_UNUSED 0100000 /* DEQNA not yet using buffer */ #define QE_BUF_INUSE 0140000 /* DEQNA is using this buffer */ #define qeBufLen(x) (-(((x)+1)>>1)) /* 2's complement word count */ /* hi address / descriptor word */ #define QE_HI_ADDR 0000077 /* hi order address bits of buffer */ #define QE_HBO_START 0000100 /* buffer starts on byte boundary */ #define QE_LBO_END 0000200 /* buffer ends on byte boundary */ /* 07400 undefined */ #define QE_SET_UP 0010000 /* buffer is setup packet */ #define QE_EOM 0020000 /* last buffer in message */ #define QE_CHAIN 0040000 /* address is pointer, not buffer */ #define QE_VALID 0100000 /* descriptor has valid address */ /* * BDL status words */ /* transmit status word 1 */ /* 017 reserved */ #define QE_COUNT 0000360 /* Collision count */ #define QE_FAIL 0000400 /* Heart failure */ #define QE_ABORT 0001000 /* Excessive collisions */ #define QE_STE16 0002000 /* Sanity timer up, 4 minutes */ #define QE_NOCAR 0004000 /* No carrier during xmit */ #define QE_LOSS 0010000 /* Carrier lost during xmit */ /* 0020000 reserved */ #define QE_BUFUSE 0140000 /* Buffer use status */ #define QE_ERRUSED 0040000 /* Error/used */ #define QE_LASTNOT 0100000 /* not last segment */ /* transmit status word 2 */ #define QE_TDR 0037777 /* TDR count (100 ns resolution) */ /* 0140000 reserved */ /* receive status word 1 */ #define QE_OVF 0000001 /* EPP receive overflow */ #define QE_CRCERR 0000002 /* Incoming CRC error */ #define QE_FRAME 0000004 /* Framing alignment error */ /* 0370 reserved */ #define QE_RBLHI 0003400 /* High bits of Rcvd Byte Length */ #define QE_RUNT 0004000 /* Incoming runt */ #define QE_DISCARD 0010000 /* Should probably discard this one */ #define QE_ESETUP 0020000 /* Looped back packet */ /* 0140000 as for xmit status 1 */ /* receive status word 2 */ #define QE_RBL 0000377 /* Received Byte Length <07:00> */ #define QE_RBL2 0177400 /* copy of QE_RBL */ /* magic setup bits for length */ #define QE_MULTICAST 0000001 /* receive all multicast packets */ #define QE_PROMISCUOUS 0000002 /* receive all packets */ #define QE_LED1_OFF 0000004 /* turn off LED 1 */ #define QE_LED2_OFF 0000010 /* turn off LED 2 */ #define QE_LED3_OFF 0000014 /* turn off LED 3 */ /* turn all on by toggling QE_BDROM */ /* * Set sanity timer to fire after n seconds of not transmitting: * logsecs = logbase2(n) + 1. */ #define SanityValue(logsecs) (logsecs << 4) struct qe_bufd { union { struct { u_short _flags; /* flag word */ u_short _desAddr; /* descriptor bits + high addr bits */ }_words; struct { u_short :14, _flag:2; u_short _hiAddr:6, _HBOStart:1, _LBOEnd:1, :4, _setUp:1, _endOfMsg:1, _chain:1, _valid:1; }_bdBits; }_bd; u_short _loAddr; /* buffer address low bits */ u_short _length; /* length of message (in words) */ union { u_short _status[2]; struct { u_short :4, _count:4, _fail:1, _abort:1, _ste16:1, _nocar:1, _loss:1, :1, _xuse:2; /* BDL use status: */ #define LAST_NOERR 0 /* last segment, no errors */ #define LAST_ERR 1 /* last segment, errors occurred */ #define UNUSED 2 /* buffer not used yet */ #define BUSY 3 /* buffer used, not end of message */ u_short _tdr:14, :2; }_xmit; struct { u_short _ovf:1, _crcerr:1, _frame:1, :5, _rblhi:3, _runt:1, _discard:1, _esetup:1, _ruse:2; u_short _rbl:8, _rbl2:8; #define QE_RBLINIT 052652 /* init value so halves differ */ }_rcv; }_st; }; #define bd_flags _bd._words._flags #define bd_desAddr _bd._words._desAddr #define bd_flag _bd._bdBits._flag #define bd_hiAddr _bd._bdBits._hiAddr #define bd_HBOStart _bd._bdBits._HBOStart #define bd_LBOEnd _bd._bdBits._LBOEnd #define bd_setUp _bd._bdBits._setUp #define bd_endOfMsg _bd._bdBits._endOfMsg #define bd_chain _bd._bdBits._chain #define bd_valid _bd._bdBits._valid #define bd_loAddr _loAddr #define bd_length _length #define bd_status _st._status #define xmit_count _st._xmit._count #define xmit_fail _st._xmit._fail #define xmit_abort _st._xmit._abort #define xmit_ste16 _st._xmit._ste16 #define xmit_nocar _st._xmit._nocar #define xmit_loss _st._xmit._loss #define xmit_use _st._xmit._xuse #define xmit_tdr _st._xmit._tdr #define rcv_ovf _st._rcv._ovf #define rcv_crcerr _st._rcv._crcerr #define rcv_frame _st._rcv._frame #define rcv_rblhi _st._rcv._rblhi #define rcv_runt _st._rcv._runt #define rcv_discard _st._rcv._discard #define rcv_esetup _st._rcv._esetup #define rcv_use _st._rcv._ruse #define rcv_rbl _st._rcv._rbl #define rcv_rbl2 _st._rcv._rbl2 ----------- /* $Header: /c/cak/src/deqna/RCS/if_qe.c,v 1.7 85/01/28 15:56:13 cak Exp $ */ /* * if_qe.c - DEQNA ethernet interface generic driver * * Author: Christopher A. Kent * Dept. of Computer Sciences * Purdue University * Date: Wed Jan 16 1985 * Copyright (c) 1985 Christopher A. Kent * May be freely redistributed provided this notice remains intact * in all copies or derivatives. */ /* * $Log: if_qe.c,v $ * Revision 1.7 85/01/28 15:56:13 cak * Make wait loops pause for clock interrupts, rather than just * hard loops. * * Revision 1.6 85/01/24 16:24:08 cak * Minor cleanup. * * Revision 1.5 85/01/24 16:06:39 cak * Removed debugging code, added receive interrupt counter. * * Revision 1.4 85/01/24 15:50:43 cak * Changes to use interrupts wherever possible. * * Revision 1.3 85/01/23 23:15:28 cak * Added support for multiple transmit buffers in the ring. * * Revision 1.2 85/01/23 16:26:42 cak * Added support for multiple deqna devices. * (despite what DEC says about RFI/EMI emissions.) * * Revision 1.1 85/01/22 21:52:19 cak * Initial revision * */ static char rcs_ident[] = "$Header: /c/cak/src/deqna/RCS/if_qe.c,v 1.7 85/01/28 15:56:13 cak Exp $"; #include "if_ether.h" #include "if_qereg.h" #define NQE 2 /* handle up to NQE devices */ #define TRUE 1 #define FALSE (!TRUE) #define XMIT_BDLS 2 #define RCV_BDLS 2 #define QE_TIMEOUT 10*60 /* 10 seconds wait for operation */ #define ETHER_BUF_DATA (2048 - sizeof(EtherHeader)) struct qe_buf { EtherHeader b_header; u_char b_data[ETHER_BUF_DATA]; /* room for BDROM segment */ }; #define b_dhost b_header.ether_dhost #define b_shost b_header.ether_shost #define b_type b_header.ether_type struct qe_softc { struct qedevice *qeaddr; /* the device itself */ int unit; /* unit number for errors */ int open; /* is this really open? */ EtherAddr physAddr; /* ethernet address */ u_char setUpBuf[16][8]; /* set up buffer */ int rcv_ints; /* # rcv interrupts */ int rcv_sem; /* receive semaphore */ int rcv_index; /* into rcv_list */ struct qe_bufd rcv_list[RCV_BDLS+1]; /* receive chain */ struct qe_buf rcv_buf[RCV_BDLS]; /* receive buffers */ int xmit_sem; /* transmit semaphore */ int xmit_index; /* into xmit_list */ struct qe_bufd xmit_list[XMIT_BDLS+1]; /* transmit chain */ struct qe_buf xmit_buf[XMIT_BDLS]; /* transmit buffer */ }qe_softc[NQE]; /* kludge for 11 compiler that won't do the assignment */ #define qeCSRcopy(src,dst) {*(int *)&dst= *(int*)&src;} /* * pick apart an address. Use (u_short) on 11 to avoid arithmetic * shift -- other machines may need something else. */ #define hiord(x) ((u_short)(x)>>16) #define loord(x) ((u_short)(x)&0xffff) /* * hacks to enable interrupts, etc., on LSI-11. */ #define disable(ps) asm("mfps ~ps");asm("mtps $0340") #define restore(ps) asm("mtps ~ps") #define enable(ps) asm("mtps $000") #define pause() asm("wait") /* * qe_open - prepare device for operation * first time ever: set up hardware address * go through some self-test * every time: set up the receive and transmit buffers */ qe_open(addr, vector, ether) EtherAddr *ether; { register struct qe_softc *qep = &qe_softc[0]; register int i; int unit = -1; int *vecp; int _qe_intr(); for (i = 0; i < NQE; i++, qep++) if (qep->qeaddr == addr) { if (qep->open == TRUE) break; /* already open, fail */ unit = i; qe_init(qep); /* just init the BDLs */ qeOnline(qep); /* bring it up */ break; } else if (qep->qeaddr == 0) { /* never been used */ unit = i; qep->qeaddr = (struct qedevice *) addr; qep->unit = unit; qep->qeaddr->qe_vector = vector; vecp = (int *)vector; *vecp++ = _qe_intr; *vecp = 0240 + unit; /* br5 */ if (qe_hardinit(qep) != TRUE){ /* full init */ unit = -1; qep->qeaddr = 0; qep->unit = -1; } break; } if (unit != -1) { /* seems OK */ qep->open = TRUE; qep->rcv_ints = 0; for (i = 0; i < sizeof (EtherAddr); i++) ether->octet[i] = qep->physAddr.octet[i]; } return unit; } /* * qe_close - shut down operations */ qe_close(unit) { register struct qe_softc *qep; if (unit >= NQE || (qep = &qe_softc[unit])->open == FALSE) { printf("qe%d_close: illegal unit\n", unit); return -1; } qep->open = FALSE; qep->qeaddr->qe_csreg |= QE_RESET; return 0; } /* * qe_read - read next packet * copies up to len bytes of the next packet into the user's buffer; * returns the actual length. * if there's a packet waiting, just return it and advance the * ring, else block until a packet comes in, or timeout expires. */ qe_read(unit, buf, length, timeout, missed) register char *buf; int *missed; { register struct qe_softc *qep; struct qe_bufd *qeb; int done, status; u_short packetLength; int ps; if (unit >= NQE || (qep = &qe_softc[unit])->open == FALSE) { printf("qe%d_read: illegal unit\n", unit); return -1; } done = FALSE; qeb = &qep->rcv_list[qep->rcv_index]; while (!done) { status = TRUE; disable(ps); *missed = qep->rcv_ints; qep->rcv_ints = 0; if (qeb->rcv_use == UNUSED) { /* is a packet ready? */ qep->rcv_sem = FALSE; status = wait(&qep->rcv_sem, timeout); } restore(ps); packetLength = -1; if (status && !qeb->rcv_discard) { packetLength = qeb->rcv_rblhi << 8; packetLength |= qeb->rcv_rbl; packetLength += 60; /* deqna magic! sec 4.3.2.5 */ bcopy(&qep->rcv_buf[qep->rcv_index], buf, min(packetLength, length)); done = TRUE; } else { done = ( !status || (qeb->rcv_crcerr && qeb->rcv_discard && !qeb->rcv_runt)); if (status) { if (qeb->rcv_ovf) printf("ovf "); if (qeb->rcv_crcerr) printf("crcerr "); if (qeb->rcv_frame) printf("frame "); if (qeb->rcv_runt) printf("runt "); if (qeb->rcv_discard) printf("discard "); if (qeb->rcv_esetup) printf("esetup "); printf("\n"); } } } if (status) { /* return buffer to usefulness */ qeRestoreRcvBD(qep, qeb); /* * If the device has received RCV_BDLS packets since * last read, the receive list will now be invalid. * Inform the device that there is another buffer * waiting for it. */ if (qep->qeaddr->qe_csreg & QE_RL_INV){ printf("qe%d: receive went invalid\n", unit); qep->qeaddr->qe_rcvLoBDL = loord(qeb); qep->qeaddr->qe_rcvHiBDL = hiord(qeb); } } return packetLength; } /* * qe_write - write a packet * takes a user buffer and writes it. Blocks until written. * Note that the deqna will not hear its own packets; if this * is necessary, code must go here to loop broadcasts and other * packets that would reach us to the input side. */ qe_write(unit, buf, len) register char *buf; { register struct qe_softc *qep; if (unit >= NQE || (qep = &qe_softc[unit])->open == FALSE) { printf("qe%d_write: illegal unit\n", unit); return -1; } qeLoadXmitBD(qep, buf, max(len, ETHERMIN), FALSE); if (qeStart(qep)) return len; else return -1; } /* * qe_control - device control operations * change the mode of the device (promiscuous, etc) * this should handle adding and deleting multicasts, too. * To turn off a mode, simply do qe_control(0). */ qe_control(unit, magic) { register struct qe_softc *qep; if (unit >= NQE || (qep = &qe_softc[unit])->open == FALSE) { printf("qe%d_control: illegal unit\n", unit); return -1; } return qeMode(qep, magic); } /* * qe_intr - interrupt routine. Determine type of interrupt, signal * upper later. */ qe_intr(unit) { register struct qe_softc *qep = &qe_softc[unit]; struct qe_csr csr; qeCSRcopy(qep->qeaddr->qe_csreg, csr); qep->qeaddr->qe_csreg = qep->qeaddr->qe_csreg; /* clear RI, XI */ if (csr.csr_rcvIR){ qep->rcv_sem = TRUE; qep->rcv_ints++; if (csr.csr_rlInval) printf("\nqe%dintr: rcv list went invalid\n", unit); } if (csr.csr_xmitIR) if (csr.csr_nxmIntr) printf("\nqe%dintr: NXM!\n", unit); else{ qep->xmit_sem = TRUE; } } /* * qe_hardinit - once-only device set up * resets the device and the LEDs, then * sets up the address recognition matrix and performs some * simple self-test. If all goes well, the device is brought online. * * If we knew how to get at the bootstrap * ROM stuff, this would be the place to use it. */ qe_hardinit(qep) register struct qe_softc *qep; { register struct qedevice *qeaddr = qep->qeaddr; register int i; struct qe_buf *qeb; int retval; qeaddr->qe_csreg |= QE_RESET; /* reset the device */ qeaddr->qe_csreg &= ~QE_RESET; delay(); /* let the bits settle */ qeaddr->qe_csreg |= QE_BDROM; /* reset the LEDs (undocumented!) */ delay(); /* must be at least 100 usec */ qeaddr->qe_csreg &= ~QE_BDROM; qeSetEtherAddr(qep); /* fetch physical address */ printf("deqna %d at 0%o, physical address ", qep->unit, (int)qeaddr); printEtherAddr(&qep->physAddr); printf("\n"); qeFillSetup(&qep->physAddr, qep->setUpBuf); qe_init(qep); /* set up BDLs */ qeaddr->qe_csreg |= QE_IE; /* enable interrupts */ retval = FALSE; qeMode(qep, QE_LED1_OFF); /* indicate progress */ qeb = &qep->xmit_buf[qep->xmit_index]; qeb->b_dhost = qep->physAddr; qeb->b_shost = qep->physAddr; qeb->b_type = htons(ETHER_LOOP); for (i = 0; i < ETHER_BUF_DATA; i++) qeb->b_data[i] = i % 512; if (qeLoopBack(qep, TRUE)) { qeMode(qep, QE_LED2_OFF); if (qeLoopBack(qep, FALSE)) { qeMode(qep, QE_LED3_OFF); qeOnline(qep); retval = TRUE; } } return retval; } /* * qe_init - reinitialize data structures * set up the receive ring, invalidate the transmit buffer, * hand receive ring to device. */ qe_init(qep) register struct qe_softc *qep; { register int i; register struct qe_bufd *qeb; qep->qeaddr->qe_csreg |= QE_RESET; /* invalidate BDLs */ qep->qeaddr->qe_csreg &= ~QE_RESET; qep->xmit_index = 0; /* initialize transmit ring */ for (i = 0; i <= XMIT_BDLS; i++){ qeb = &qep->xmit_list[i]; qeb->bd_flags = QE_BUF_UNUSED; qeb->bd_hiAddr = 0; qeb->bd_loAddr = 0; qeb->bd_length = 0; qeb->bd_chain = FALSE; qeb->bd_valid = FALSE; qeb->xmit_use = UNUSED; } qeb = &qep->xmit_list[XMIT_BDLS]; qeb->bd_chain = TRUE; /* * qeb->bd_flag, qeb->status words are apparently * unused in chain pointer. */ qeb->bd_valid = TRUE; qeb->bd_hiAddr = hiord(qep->xmit_list); qeb->bd_loAddr = loord(qep->xmit_list); qep->rcv_index = 0; /* initialize receive ring */ for (i = 0; i < RCV_BDLS; i++) { qeb = &qep->rcv_list[i]; qeb->bd_flags = QE_BUF_UNUSED; qeb->bd_hiAddr = hiord(&qep->rcv_buf[i]); qeb->bd_loAddr = loord(&qep->rcv_buf[i]); qeb->bd_length = qeBufLen(sizeof(qep->rcv_buf[i])); qeb->bd_chain = FALSE; qeb->bd_valid = TRUE; qeb->rcv_use = UNUSED; qeb->bd_status[1] = QE_RBLINIT; } qeb = &qep->rcv_list[RCV_BDLS]; qeb->bd_chain = TRUE; /* * qeb->bd_flag, qeb->status words are * apparently unused in chain pointer */ qeb->bd_valid = TRUE; qeb->bd_hiAddr = hiord(qep->rcv_list); qeb->bd_loAddr = loord(qep->rcv_list); /* hand the receive ring to the device */ qep->qeaddr->qe_rcvLoBDL = loord(qep->rcv_list); qep->qeaddr->qe_rcvHiBDL = hiord(qep->rcv_list); } /* * qeSetEtherAddr - copy physical address from device into softc */ qeSetEtherAddr(qep) register struct qe_softc *qep; { register int i; for (i = 0; i < sizeof (EtherAddr); i++) qep->physAddr.octet[i] = qep->qeaddr->qe_physAddr[i] & 0xff; } /* * qeFillSetup - fill a setup buffer with the physical address */ qeFillSetup(addr, buf) EtherAddr *addr; u_char buf[16][8]; { register int i, j; for (i = 0; i < sizeof(EtherAddr); i++) { buf[i+8][0] = buf[i][0] = 0; /* boundaries must be 0 */ buf[i+8][1] = buf[i][1] = 0xffff; /* broadcast */ for (j = 2; j < 8; j++) buf[i+8][j] = buf[i][j] = addr->octet[i]; } for (i = sizeof(EtherAddr); i < 8; i++) /* more boundaries */ for (j = 0; j < 8; j++) buf[i+8][j] = buf[i][j] = 0; } /* * qeMode - change the mode of the device by sending a setup packet. * Packet loops back into receive ring for synchronization; * wait till it comes back. Packets that are received while waiting * are discarded (oh well). */ qeMode(qep, magic) register struct qe_softc *qep; { register struct qe_bufd *qeb; int status, ps; qeLoadXmitBD(qep, qep->setUpBuf, sizeof(qep->setUpBuf) + magic, TRUE); if (qeStart(qep)) { /* it got sent -- wait for it to come back */ qeb = &qep->rcv_list[qep->rcv_index]; status = TRUE; disable(ps); if (qeb->rcv_use == UNUSED) { /* is a packet ready? */ do { qep->rcv_sem = FALSE; status = wait(&qep->rcv_sem, QE_TIMEOUT); if (!status) break; if (qeb->rcv_esetup == FALSE){ printf("qe%dMode: lost packet\n", qep->unit); qeRestoreRcvBD(qep, qeb); } } while (qeb->rcv_esetup == FALSE); } restore(ps); if (!status){ printf("qe%dMode: timed out on receive\n", qep->unit); return FALSE; } /* successful -- restore rcv BDL that got tromped */ qeRestoreRcvBD(qep, qeb); return (qep->xmit_list[0].xmit_use != LAST_ERR); } else return FALSE; } /* * qeLoadXmitBD - load a transmit descriptor with a buffer * assumes entire message fits in one buffer -- caveat emptor! */ qeLoadXmitBD(qep, buf, len, setup) register struct qe_softc *qep; char *buf; int len, setup; { register struct qe_bufd *qeb = &qep->xmit_list[0]; qeb->bd_flags = QE_BUF_UNUSED; qeb->xmit_use = UNUSED; qeb->bd_HBOStart = FALSE; /* is this always true? */ qeb->bd_LBOEnd = (len&1); qeb->bd_length = qeBufLen(len); qeb->bd_hiAddr = hiord(buf); qeb->bd_loAddr = loord(buf); qeb->bd_setUp = setup; qeb->bd_endOfMsg = TRUE; qeb->bd_valid = TRUE; } /* * qeRestoreRcvBD - restore a receive BDL entry to usable form and * advance the ring pointer. */ qeRestoreRcvBD(qep, qeb) register struct qe_softc *qep; register struct qe_bufd *qeb; { qeb->bd_flags = QE_BUF_UNUSED; qeb->rcv_use = UNUSED; qeb->bd_status[1] = QE_RBLINIT; /* don't need to reset bd_valid because DEQNA doesn't touch it! */ qep->rcv_index = (qep->rcv_index + 1) % RCV_BDLS; } /* * qeLoopBack - send a loopback packet. */ qeLoopBack(qep, internal) register struct qe_softc *qep; { register int i, *j; struct qe_csr csr; int status, msize; qeCSRcopy(qep->qeaddr->qe_csreg, csr); /* gak! */ csr.csr_intLoopBack = !internal; csr.csr_extLoopBack = !internal; csr.csr_rcvEnable = FALSE; qeCSRcopy(csr, qep->qeaddr->qe_csreg); j = (int *)&qep->rcv_buf[qep->rcv_index]; for (i = 0; i < sizeof(qep->rcv_buf[qep->rcv_index]); i++) *j++ = 0; msize = internal ? 6 : sizeof(EtherPacket); qeLoadXmitBD(qep, &qep->xmit_buf[qep->xmit_index], msize, FALSE); status = internal ? qeIntLoop(qep) : qeStart(qep); if (!status) printf("qe%dLoopBack: pkt xmit fails\n", qep->unit); if (status) { /* * xmit was successful; must now wait for receive to complete. * Loopbacks don't cause a receive interrupt! */ for (i = 0; i < QE_TIMEOUT; i++) if (qep->rcv_list[qep->rcv_index].rcv_use != UNUSED) break; else pause(); if ( i >= QE_TIMEOUT){ printf("qeLoopBack: timed out on receive\n"); return FALSE; } for (i = 0; i < msize & status; i++) { status &= ((u_char *)&qep->rcv_buf[qep->rcv_index])[i] == ((u_char *)&qep->xmit_buf[qep->xmit_index])[i]; } if (i != msize) printf("qe%dLoopBack mismatch at %d of %d\n", qep->unit, i, msize); /* successful -- restore rcv BDL that got tromped */ qeRestoreRcvBD(qep, &qep->rcv_list[qep->rcv_index]); } return status; } /* * qeIntLoop - do an internal loopback. Leaves the device in internal * loopback mode. Only difference is that internal loopback must * have the receiver enabled. */ qeIntLoop(qep) register struct qe_softc *qep; { int retval; qep->qeaddr->qe_csreg |= QE_RCV_ENBL; return qeStart(qep); /* do the transmit */ } /* * qeStart - start transmitting the xmit ring. */ qeStart(qep) register struct qe_softc *qep; { int ps, status; disable(ps); qep->qeaddr->qe_xmitLoBDL = loord(&qep->xmit_list[0]); qep->qeaddr->qe_xmitHiBDL = hiord(&qep->xmit_list[0]); qep->xmit_sem = FALSE; status = wait(&qep->xmit_sem, QE_TIMEOUT); restore(ps); if (!status) printf("qe%dStart: timed out on transmit\n", qep->unit); return status; } /* * qeOnline - reset data structures and bring the device on line. */ qeOnline(qep) register struct qe_softc *qep; { register struct qedevice *qeaddr = qep->qeaddr; qeaddr->qe_csreg |= QE_RESET; /* reset the device */ qeaddr->qe_csreg &= ~QE_RESET; qep->rcv_index = 0; qeaddr->qe_rcvLoBDL = loord(qep->rcv_list); qeaddr->qe_rcvHiBDL = hiord(qep->rcv_list); qeaddr->qe_csreg &= ~QE_EXT_LOOP; qeaddr->qe_csreg |= (QE_RCV_ENBL | QE_IE | QE_INT_LOOP); } /* * HELP FUNCTIONS * your environment may already have routines to do these things; * if so, use them instead. */ /* * printEtherAddr - print an ethernet address */ printEtherAddr(a) char a[]; { register i; for (i = 0; i < 6; i++){ printf("%x%x", (a[i] & 0xf0) >> 4, a[i] & 0x0f); if (i < 5) printf("."); } } /* * delay - delay a short while */ delay() { int i; for (i = 500; i ; i--) ; } /* * wait -- wait for a semaphore to go TRUE, or a timeout to expire. * (In a real OS, this would suspend the process, too.) */ wait(sem, timeout) int *sem; { int timeCount = 0; int ps; /* save processor priority */ enable(ps); while (*sem == FALSE && (timeout == 0 || timeCount < timeout)){ pause(); timeCount++; } restore(ps); return timeout == 0 || timeCount < timeout; } /* * clock interrupt routine. Well, something has to be there! */ ckintr() { } min(a, b) { return ab?a:b; } bcopy(src, dst, count) register char *src, *dst; int count; { for (; count; count--) *dst++ = *src++; } ----------- /* $Header$ */ /* * qetest.c - test deqna driver * * Author: Christopher A. Kent * Dept. of Computer Sciences * Purdue University * Date: Tue Jan 22 1985 * Copyright (c) 1985 Christopher A. Kent */ /* * $Log$ */ static char rcs_ident[] = "$Header$"; #include "if_ether.h" #include "if_qereg.h" char myIPaddr[] = { 192, 5, 48, 30 }; char targetIPaddr[] = { 192, 5, 48, 3 }; main() { struct ether_packet packet; struct ether_arp *arp; EtherAddr physAddr; int fd; int missed; register int i; int *vecp; int _ckintr(); vecp = (int *) 0100; /* set up clock interrupt vector */ *vecp++ = _ckintr; *vecp = 0300; /* br6 */ asm("mtps $000"); /* enable interrupts */ if ((fd = qe_open(0174440, 0300, &physAddr)) == - 1) { printf("open failed\n"); } if ((fd = qe_open(0174460, 0310, &physAddr)) == - 1) { printf("open failed\n"); while (1) ; } /* * send an ARP packet and await the reply */ for (i = 0; i < sizeof (EtherAddr); i++){ packet.ep_dhost.octet[i] = 0377; packet.ep_shost.octet[i] = physAddr.octet[i]; } packet.ep_type = htons(ETHER_ARP); arp = (struct ether_arp *) &packet.ep_data; arp->arp_hrd = htons(ARPHRD_ETHER); arp->arp_pro = htons(ETHER_IP); arp->arp_hln = sizeof(arp->arp_sha); arp->arp_pln = sizeof(arp->arp_spa); arp->arp_op = htons(ARP_REQUEST); for (i = 0; i < arp->arp_hln; i++) { arp->arp_sha.octet[i] = packet.ep_shost.octet[i]; arp->arp_tha.octet[i] = 0; } for (i = 0; i < arp->arp_pln; i++) { arp->arp_spa[i] = myIPaddr[i]; arp->arp_tpa[i] = targetIPaddr[i]; } printf("qe_write returns %d\n", qe_write(fd, &packet, sizeof(*arp) + sizeof(EtherHeader))); for (i = 0; i < 10; i++) if(qe_read(fd, &packet, sizeof(packet), 5*60, &missed) > 0){ if (arp->arp_op == htons(ARP_REPLY)){ printf("target is at "); printEtherAddr(&arp->arp_sha); printf("\n"); break; } else printf("funny packet\n"); }else printf("."); if (i == 10) printf("read timed out\n"); qe_control(fd, QE_PROMISCUOUS); while (1) if((i = qe_read(fd, &packet, sizeof(packet), 0, &missed)) > 0){ printf("%d bytes, type 0x%x: ", i, ntohs(packet.ep_type)); printEtherAddr(&packet.ep_shost); printf(" -> "); printEtherAddr(&packet.ep_dhost); printf(" (missed %d)\n", missed); } else printf("."); qe_close(fd); } ----------- / $Header$ / / if_qesup.s - Various cheap support routines for deqna / .globl _htonl, _htons, _ntohl, _ntohs _htonl: _ntohl: mov 4(sp), r1 swab r1 _htons: _ntohs: mov 2(sp), r0 swab r0 rts pc .globl __qe_intr, _qe_intr / deqna interrupt __qe_intr: mfps -(sp) / save device descriptor from PS mov r0,-(sp) / save r0 (csv doesn't) mov $_qe_intr,r0 / what routine to call br intr / do it .globl __ckintr, _ckintr __ckintr: / clock interrupt mfps -(sp) / see above mov r0,-(sp) mov $_ckintr,r0 / fall through ... intr: mov r1,-(sp) / have to save r1, too mov 4(sp), r1 / get saved PS in r1 bic $177760,r1 / mask off device descriptor mov r1,-(sp) / push as argument jsr pc,(r0) / go to C routine mov 2(sp),r1 / restore r1 mov 4(sp),r0 / restore r0 add $8,sp / pop arg, saved r0, r1, PS rtt / return from interrupt ----------- # # make the deqna standalone test program # all: a.out clean: rm a.out *.o install: echo 'No installation yet.' a.out: qetest.o if_qe.o if_qesup.o cc11 -L qetest.o if_qe.o if_qesup.o qetest.o: qetest.c cc11 -c qetest.c if_qe.o: if_qe.c if_qereg.h if_ether.h cc11 -c if_qe.c if_qesup.o: if_qesup.s cc11 -c if_qesup.s d: a.out download -a9; odt down: a.out download -s ----------