/************************************************************************ * * * Copyright (c) 1985 by * * Digital Equipment Corporation, Maynard, MA * * All rights reserved. * * If you touch this presotto, you DIE! * ************************************************************************/ /* * DEQNA Ethernet Communications Controller interface. * Provides fairly raw access to a number of controllers. * Minor device N talks to unit N / 8. Each written record should * be an interlan output packet - 6 bytes addr, 2 bytes type, data. * An ethernet packet type may be associated with a minor device with * the ENIOTYPE ioctl; input packets of that type will be sent to * the minor device in question. Input packets include 6 bytes src addr, * 6 bytes dest, 2 bytes type, and data. * * The physical address of a controller may be fetched with the ENIOADDR * ioctl on a minor device associated with that unit. */ /* * NB: Both transmit and receive buffer descriptor lists are rings. The * last descriptor in each list points to the first. To prevent the * device from overrunning the software, one descriptor in each ring * is left invalid. */ #include "qe.h" #include "../h/param.h" #include "../h/systm.h" #include "../h/stream.h" #include "../h/map.h" #include "../h/buf.h" #include "../h/ubavar.h" #include "../h/conf.h" #include "../h/enio.h" #include "../h/ttyio.h" #include "../h/ttyld.h" #include "../h/deqna.h" #include "../h/ethernet.h" /* for address calcualations of stream blocks */ extern u_char blkdata[]; /* stream.c */ extern long blkubad; /* * This constant should really be 60 because the qna adds 4 bytes of crc. * However when set to 60 our packets are ignored by deuna's , 3coms are * okay ?????????????????????????????????????????? */ #define ETHERMINTU 64 #define ETHERMAXTU 1564 /* * The number of recieve packets is set to minimize the possibility * of incuring a race condition between the hardware and software. */ #define NRCV 12 /* Receive descriptors */ #define NXMT 12 /* Transmit descriptors */ #define NTOT (NXMT + NRCV) #define MAX_RCV_BYTES 3*ETHERMAXTU /* maximum bytes in receive ring */ int nNQE = NQE; int nNRCV = NRCV; int nNXMT = NXMT; int nNTOT = NTOT; /* mask for setting deqna deqna csr */ int qebits = QE_RCV_ENABLE|QE_INT_ENABLE|QE_XMIT_INT|QE_RCV_INT|QE_ILOOP; /* One of these structures exists per minor device number (stream) */ #define CHANS_PER_UNIT 8 struct qechan{ int unit; /* device unit number */ struct queue *rq; /* stream queues */ int type; /* ethernet protocol # */ struct block *xmt[NXMT]; /* next packet to xmt (null terminated) */ int nxmt; }; struct qechan *qefindchan(); /* One of these structures exists per device */ struct qe { /* statistics */ int ierrors,oerrors; /* input/output errors */ int ipackets,opackets; /* input/output packets */ /* for xmit/rcv */ int rindex; /* Receive index */ int orindex; int xindex; /* Transmit index */ int oxindex; int rbytes; /* bytes in receive buffer */ int status; struct qe_ring rring[NRCV+1]; /* Receive ring descriptors */ struct qe_ring xring[NXMT+1]; /* Transmit ring descriptors */ struct block *rbp[NRCV]; /* receive blocks */ struct block *xbp[NXMT]; /* transmit blocks */ struct qe_ring *rringaddr; /* mapping info for rings */ struct qe_ring *xringaddr; /* "" */ /* channel info */ int lastch; /* next channel to scan */ struct qechan chan[CHANS_PER_UNIT]; /* device parammeters */ int qe_intvec; /* Interrupt vector */ struct qedevice *addr; /* device addr */ u_char setup_pkt[16][8]; /* Setup packet */ } qe[NQE]; #define NEXTCH(c) (((c)+1)%CHANS_PER_UNIT) /* next channel */ /* flag settings */ #define ATTACHED 0x4 #define SETUPQD 0x8 /* handy macros for ring management */ #define NFREE(s) ((s)->xindex>=(s)->oxindex?(NXMT-((s)->xindex-(s)->oxindex)-1):\ ((s)->oxindex-(s)->xindex-1)) #define NEXTRCV(c) (((c)+1)%NRCV) #define NEXTXMT(c) (((c)+1)%NXMT) /* uba structure */ int qeprobe(), qeattach(); u_short qestd[] = { 0 }; struct uba_device *qeinfo[NQE]; struct uba_driver qedriver = { qeprobe, 0, qeattach, 0, qestd, "qe", qeinfo }; #define ADDRMASK 0x3ffff /* Build a setup packet - the physical address will already be present * in first column. */ qesetup(qp) register struct qe *qp; { int i, j, offset = 0, next = 3; /* * Copy the target address to the rest of the entries in this row. */ for ( j = 0; j < 6 ; j++ ) for ( i = 2 ; i < 8 ; i++ ) qp->setup_pkt[j][i] = qp->setup_pkt[j][1]; /* * Duplicate the first half. */ bcopy(qp->setup_pkt, qp->setup_pkt[8], 64); /* * Fill in the broadcast address. */ for ( i = 0; i < 6 ; i++ ) qp->setup_pkt[i][2] = 0xff; } /* Invalidate a ring descriptor */ qeinvaldesc(rp) register struct qe_ring *rp; { /* invalidate a descriptor */ bzero(rp, sizeof(struct qe_ring)); } /* Fill in a descriptor */ qefilldesc(rp, addr, len, setup, eom) register struct qe_ring *rp; u_long addr; /* Qbus address */ int len; int setup; /* true if a setup packet */ int eom; /* true if end of message */ { /* fill in the descriptor */ bzero( rp, sizeof(struct qe_ring)); if ((addr) & 1) { /* buffer starts on an odd byte */ len++; rp->qe_odd_begin = 1; addr &= 0xfffe; } if (len & 1) { /* buffer ends on an odd byte */ len++; rp->qe_odd_end = 1; } rp->qe_buf_len = -(len/2); addr &= ADDRMASK; rp->qe_addr_lo = (short)addr; rp->qe_addr_hi = (short)(addr >> 16); if (setup) rp->qe_setup = 1; if (eom) rp->qe_eomsg = 1; rp->qe_status2 = 1; rp->qe_flag = rp->qe_status1 = QE_NOTYET; rp->qe_valid = 1; } /* set up a receive descriptor */ qercvblock(qp) register struct qe *qp; { register long a; register int len; register struct block *bp; /* make sure we don't hog too many buffers or wrap around */ if (qp->rbytes>MAX_RCV_BYTES || NEXTRCV(qp->rindex)==qp->orindex) return -1; /* get a block */ if ((bp = allocb(ETHERMAXTU))==NULL) panic("qebinitdesc: no blocks"); qp->rbp[qp->rindex] = bp; /* translate the block data address to a q-bus address */ if((unsigned)(bp->rptr) < (unsigned)blkdata){ printf("memaddr %x blkdata %x\n", bp->rptr, blkdata); panic("qebinitdesc: translation"); } a = (long)(bp->rptr)-(long)blkdata+((long)blkubad&ADDRMASK); /* fill in the descriptor */ len = bp->lim - bp->rptr; qefilldesc(&qp->rring[qp->rindex], a, len, 0, 0); qp->rbytes += len; /* make sure there are blocks queued for input */ /*printf("qercvblock: csr %x\n", qp->addr->qe_csr);*/ if ((qp->addr->qe_csr&QE_RL_INVALID) && qp->rring[qp->rindex].qe_status1 == QE_NOTYET) { a = (int)&qp->rringaddr[qp->rindex]; qp->addr->qe_rcvlist_lo = (short)a; qp->addr->qe_rcvlist_hi = (short)(a >> 16); } qp->rindex = NEXTRCV(qp->rindex); return 0; } /* add a buffer to the transmit ring */ qexmtblock(qp, bp, setup, eom) struct qe *qp; struct block *bp; int setup; /* true if a setup block */ int eom; /* true if end of message */ { register long a; register int len; if (qp->xring[qp->xindex].qe_status1 == QE_NOTYET) panic("qexmitbp"); qp->xbp[qp->xindex] = bp; /* translate the block data address to a q-bus address */ if((unsigned)(bp->rptr) < (unsigned)blkdata){ printf("memaddr %x blkdata %x\n", bp->rptr, blkdata); panic("qebinitdesc: translation"); } a = (long)(bp->rptr)-(long)blkdata+((long)blkubad&ADDRMASK); /* fill in the descriptor */ len = bp->wptr - bp->rptr; qefilldesc(&qp->xring[qp->xindex], a, len, setup, eom); /* kick device if xmit list is invalid */ /*printf("qexmtblock: csr %x\n", qp->addr->qe_csr);*/ if ((qp->addr->qe_csr&QE_XL_INVALID) && qp->xring[qp->xindex].qe_status1==QE_NOTYET) { a = (int)(qp->xringaddr+qp->xindex); qp->addr->qe_xmtlist_lo = (short)a; qp->addr->qe_xmtlist_hi = (short)(a >> 16); } qp->xindex = NEXTXMT(qp->xindex); } /* put a setup block on the xmt ring */ qexmtsetup(qp) register struct qe *qp; { struct block *bp; /* get a free block */ bp = allocb(sizeof(qp->setup_pkt)); if (bp==NULL) panic("qexmtsetup: no setup blocks"); if (bp->lim - bp->rptr < sizeof(qp->setup_pkt)) { freeb(bp); panic("qexmtsetup: too small"); } /* and fill it in */ bcopy(qp->setup_pkt, bp->rptr, sizeof(qp->setup_pkt)); bp->wptr = bp->rptr + sizeof(qp->setup_pkt); qexmtblock(qp, bp, 1, 1); return 0; } /* Reset the interface */ qereset(qp) register struct qe *qp; { register int i; /* software reset */ qp->addr->qe_csr = QE_RESET; /*printf("qereset: csr = %x\n", qp->addr->qe_csr);*/ } /* Setup the buffer descriptor lists (into rings) and enable for reception */ qeenable(qp) register struct qe *qp; { register int i, ch; /* clear out all queued blocks */ for (i=0; i<NRCV; i++) if (qp->rbp[i]) { freeb(qp->rbp[i]); qp->rbp[i] = NULL; } for (i=0; i<NXMT; i++) if (qp->xbp[i]) { freeb(qp->xbp[i]); qp->xbp[i] = NULL; } for (ch=0; ch<CHANS_PER_UNIT; ch++) { qp->chan[ch].nxmt = 0; for (i=0; i<NXMT; i++) if (qp->chan[ch].xmt[i]) { freeb(qp->chan[ch].xmt[i]); qp->chan[ch].xmt[i] = NULL; } } /* Invalidate all decriptors and form the xmt and rcv rings */ for (i = 0 ; i < NRCV ; i++) qeinvaldesc(&qp->rring[i]); qefilldesc(&qp->rring[i], qp->rringaddr, 0, 0, 0); qp->rring[i].qe_chain = 1; for (i = 0 ; i < NXMT ; i++) qeinvaldesc(&qp->xring[i]); qefilldesc(&qp->xring[i], qp->xringaddr, 0, 0, 0); qp->xring[i].qe_chain = 1; /* initialize ring pointers */ qp->lastch = qp->orindex = qp->oxindex = qp->xindex = qp->rindex = 0; qp->rbytes = 0; /* enable interrupts */ qp->addr->qe_vector = qp->qe_intvec; qp->addr->qe_csr = qebits; /* set up some buffers */ while (qercvblock(qp)==0) ; qp->status |= SETUPQD; while (qexmtpacket(qp)==0) ; /*printf("qe csr after enable: %x\n", qp->addr->qe_csr);*/ } /* * Probe the device to see if it's there. We do this by sending a setup * packet and waiting for it to loop back. */ qeprobe(reg) caddr_t reg; { register int br, cvec; /* r11, r10 value-result */ register struct qedevice *addr = (struct qedevice *)reg; register int i; static int next=0; /* softc index */ register struct qe *qp = &qe[next++]; u_char *setupaddr, *sa; u_short csr; #ifdef lint br = 0; cvec = br; br = cvec; #endif /* zero EVERYTHING */ bzero(qp, sizeof(struct qe)); addr->qe_vector = (uba_hd[numuba].uh_lastiv -= 4); qp->status = 0; qp->addr = addr; /* for qereset */ qereset(qp); /* Map the communications area and the setup packet. */ setupaddr = (u_char *) uballoc(0, qp->setup_pkt, sizeof(qp->setup_pkt), 0); sa = (u_char *)((long)setupaddr & ADDRMASK); qp->rringaddr = (struct qe_ring *) uballoc(0, qp->rring, sizeof(struct qe_ring)*(NTOT+2),0); qp->rringaddr = (struct qe_ring *)((int)(qp->rringaddr) & ADDRMASK); qp->xringaddr = qp->rringaddr+NRCV+1; /* * The QNA will loop the setup packet back to the receive ring * for verification, therefore we initialize the first * receive & transmit ring descriptors and link the setup packet * to them. */ qefilldesc(qp->xring, sa, sizeof(qp->setup_pkt), 1, 1); qefilldesc(qp->rring, sa, sizeof(qp->setup_pkt), 0, 0); /* * Get the addr off of the interface and place it into the setup * packet. This code looks strange due to the fact that the address * is placed in the setup packet in col. major order. */ for( i = 0 ; i < 6 ; i++ ) qp->setup_pkt[i][1] = addr->qe_sta_addr[i]; qesetup(qp); /* Start the interface and wait for the packet. */ addr->qe_csr = QE_INT_ENABLE | QE_XMIT_INT | QE_RCV_INT; addr->qe_rcvlist_lo = (short)qp->rringaddr; addr->qe_rcvlist_hi = (short)((int)(qp->rringaddr) >> 16); addr->qe_xmtlist_lo = (short)qp->xringaddr; addr->qe_xmtlist_hi = (short)((int)(qp->xringaddr) >> 16); DELAY(10000); csr = addr->qe_csr; /* clamp down on the device again */ qereset(qp); /* release the mapping */ ubarelse(0, &setupaddr); addr->qe_vector = uba_hd[numuba].uh_lastiv; if (!(csr&(QE_XMIT_INT|QE_RCV_INT))) { printf("deqna failed setup: csr = %x\n", csr); return 0; } cvec = addr->qe_vector; br = 0x15; return 1; } /* set up sc[i] to reflect the physical parameters of unit number i*/ qeattach(ui) struct uba_device *ui; { register struct qe *qp = &qe[ui->ui_unit]; register struct qedevice *addr = (struct qedevice *)ui->ui_addr; register int i; int s=spl6(); /* Save the vector for initialization at reset time */ qp->qe_intvec = addr->qe_vector; qp->addr = addr; /* Read the address from the prom and save it */ for( i=0 ; i<6 ; i++ ) qp->setup_pkt[i][1] = addr->qe_sta_addr[i] & 0xff; /* close all channels */ for (i=0; i<CHANS_PER_UNIT; i++) { qp->chan[i].nxmt = 0; qp->chan[i].rq = NULL; } /* clear block pointers */ for (i=0; i<NRCV; i++) qp->rbp[i] = NULL; for (i=0; i<NXMT; i++) qp->xbp[i] = NULL; qp->status |= ATTACHED; splx(s); } /* start transmitting */ qexmtpacket(qp) register struct qe *qp; { register int unit = qp - qe; register int i; register struct qechan *cp; int a; /* first send a setup packet if requested */ if (qp->status&SETUPQD) { /*printf("qexmtpacket: setup\n");*/ qp->status &= ~SETUPQD; if (NFREE(qp)<1 || qexmtsetup(qp)<0) { qp->status |= SETUPQD; return -1; /* no more blocks */ } /*printf("qexmtpacket: setup qd\n");*/ } /* Look for a channel that has a packet that will fit */ for(i=NEXTCH(qp->lastch); ; i=NEXTCH(i)) { cp = &qp->chan[i]; if (cp->nxmt && cp->nxmt<=NFREE(qp)) break; if (i==qp->lastch) return -1; } qp->lastch = i; /* send the packet */ qedebug(cp->xmt[0], 1); for (i=0; i<cp->nxmt-1; i++) { qexmtblock(qp, cp->xmt[i], 0, 0); cp->xmt[i] = (struct block *)NULL; } qexmtblock(qp, cp->xmt[i], 0, 1); cp->xmt[i] = (struct block *)NULL; cp->nxmt = 0; qestagepacket(cp); return 0; } /* All device interrupts come here */ qeintr(unit) int unit; { register struct qe *qp = &qe[unit]; int a, csr, s=spl6(); csr = qp->addr->qe_csr; /*printf("qeintr: in csr %x\n", csr);*/ qp->addr->qe_csr = qebits; if( csr & QE_RCV_INT ) qerint(qp); if( csr & QE_XMIT_INT ) qexint(qp); if( csr & QE_NEX_MEM_INT ) printf("qe: Non existant memory interrupt\n"); /*printf("qeintr: out csr %x\n", qp->addr->qe_csr);*/ splx(s); } /* Take transmitted packets out of the ring. Since the device cannot receive * its own broadcasts, loop back locally destined packets here. */ qexint(qp) register struct qe *qp; { register int last; /* index of last block of packet */ register int first; /* index of first block of packet */ register struct qechan *cp; /* loop once per packet */ for(;;) { /* loop once per block */ first = qp->oxindex; for (last=first; ; last=NEXTXMT(last)) { if (last==qp->xindex || qp->xring[last].qe_status1==QE_NOTYET){ while (qexmtpacket(qp)==0) ; return; } if (qp->xring[last].qe_eomsg==1) break; } /* statistics */ if (qp->xring[last].qe_status1&QE_ERROR) { printf("qexint: failure status1 %x status2 %x\n", qp->xring[last].qe_status1, qp->xring[last].qe_status2); qp->oerrors++; } else { qp->opackets++; } /* find correct channel (for possible loop back) */ if (qebits&QE_ELOOP) cp = NULL; else cp = qefindchan(qp, qp->xbp[first], 1); /* loop once per block to free (or loop back) packet */ for(last=NEXTXMT(last); first!=last; first=NEXTXMT(first)) { /*printf("qexint: removing %d to %x\n", first, cp);*/ if (cp) (*cp->rq->next->qinfo->putp)(cp->rq->next,qp->xbp[first]); else freeb(qp->xbp[first]); qeinvaldesc(&qp->xring[first]); qp->xbp[first] = NULL; } qp->oxindex = last; /* add a delim */ if (cp) { struct block *bp = allocb(0); if (bp==NULL) panic("qexint: out of delim blocks"); bp->type = M_DELIM; (*cp->rq->next->qinfo->putp)(cp->rq->next, bp); } } } /* Take received packets off the ring and pass them the appropriate * input channel. */ qerint(qp) register struct qe *qp; { register int last; /* index of last block of packet */ register int first; /* index of first block of packet */ register struct qechan *cp; struct qe_ring *rp; struct block *bp; int len; /* loop once per packet */ for(;;) { /* loop once per block */ first = qp->orindex; for (last=first; ; last=NEXTRCV(last)) { if (last==qp->rindex || qp->rring[last].qe_status1==QE_NOTYET) { /* put more buffer space on the ring */ while(qercvblock(qp)==0) ; return; } if (!(qp->rring[last].qe_status1&QE_LASTNOT)) break; } /* statistics */ if (qp->rring[last].qe_status1&QE_ERROR) { /* printf("qerint: failure\n");*/ cp = NULL; qp->oerrors++; } else if (qp->rring[last].qe_status1&QE_ESETUP) { /* printf("qerint: setup\n");*/ cp = NULL; } else { cp = qefindchan(qp, qp->rbp[first], 0); qp->opackets++; } /* loop once per block to free (or loop back) packet */ if (cp) { qedebug(qp->rbp[first], 1); rp = &qp->rring[last]; len = ((rp->qe_status1&QE_RBL_HI)| (rp->qe_status2&QE_RBL_LO))+60; } for(last=NEXTRCV(last); first!=last; first=NEXTRCV(first)) { /*printf("qerint: received %d to %x\n", first, cp);*/ bp = qp->rbp[first]; qp->rbytes -= bp->lim - bp->rptr; if (cp) { rp = &qp->rring[first]; if (rp->qe_status1&QE_LASTNOT) { bp->wptr = bp->lim; len -= bp->lim-bp->rptr; } else { bp->wptr = bp->rptr + len; if (bp->wptr > bp->lim) panic("qerint: length"); } (*cp->rq->next->qinfo->putp)(cp->rq->next, bp); } else freeb(bp); qeinvaldesc(&qp->rring[first]); qp->rbp[first] = NULL; } /* add a delim */ if (cp) { struct block *bp = allocb(0); if (bp==NULL) panic("qerint: out of delim blocks"); bp->type = M_DELIM; (*cp->rq->next->qinfo->putp)(cp->rq->next, bp); } qp->orindex = last; } } int nodev(), qeopen(), qeclose(), qeput(); struct qinit qerinit = { nodev, NULL, qeopen, qeclose, 0, 0 }; struct qinit qewinit = { qeput, NULL, qeopen, qeclose, 4*ETHERMAXTU, 64 }; struct streamtab qesinfo = { &qerinit, &qewinit }; /* find the input queue for a packet */ struct qechan * qefindchan(qp, bp, checklocal) register struct qe *qp; struct block *bp; int checklocal; { register int i; register struct etherpup *ep=(struct etherpup *)bp->rptr; int ok; /*printf("qefindchan: %x,%x,%x,%x,%x,%x:%d\n", ep->dhost[0], ep->dhost[1], ep->dhost[2], ep->dhost[3], ep->dhost[4], ep->dhost[5], ep->type);*/ if (checklocal) { ok = 3; for (i=0; i<6; i++) { if (ep->dhost[i]!=0xff) ok &= ~1; if (ep->dhost[i]!=qp->setup_pkt[i][1]) ok &= ~2; if (!ok) return NULL; } } for (i=0; i<CHANS_PER_UNIT; i++) if (qp->chan[i].rq!=NULL && qp->chan[i].type==ep->type) return(&qp->chan[i]); return NULL; } /* open a channel */ qeopen(q, dev) register struct queue *q; register dev_t dev; { register struct qechan *cp; register struct qe *qp; int unit, s, ch, chan; dev = minor(dev); chan = dev % CHANS_PER_UNIT; unit = dev / CHANS_PER_UNIT; if(unit >= NQE) return(0); qp = &qe[unit]; if (!(qp->status&ATTACHED)) return(0); cp = &qp->chan[chan]; if(cp->rq) return(0); cp->unit = unit; cp->type = 0; q->ptr = (caddr_t)cp; WR(q)->ptr = (caddr_t)cp; WR(q)->flag |= QDELIM|QBIGB; q->flag |= QDELIM; cp->rq = q; /* reset and enable on first open of the device */ s = spl6(); for (ch=0; ch < CHANS_PER_UNIT; ch++) if (ch!=chan && qp->chan[ch].rq!=NULL) break; if (ch==CHANS_PER_UNIT) { qereset(qp); qeenable(qp); } splx(s); return(1); } qeclose(q) register struct queue *q; { register struct qechan *cp; cp = (struct qechan *)q->ptr; cp->rq = 0; cp->type = 0; } /* qeput expects the first block of each packet to contain a ethernet header */ qeput(q, bp) register struct queue *q; struct block *bp; { register struct qechan *cp=(struct qechan *)q->ptr; int s; if(bp->type == M_DATA){ putq(q, bp); return; } else if(bp->type == M_IOCTL){ qeioctl(q, bp); return; } else if(bp->type == M_DELIM){ putq(q, bp); } else { freeb(bp); return; } /* get the next packet from the queue and start transmitting */ s = spl6(); qestagepacket(cp); while (qexmtpacket(&qe[cp->unit])==0) ; splx(s); } /* Stage the next packet for this channel */ qestagepacket(cp) register struct qechan *cp; { register struct block *bp; register int nblocks=0; register int nbytes=0; if (cp->nxmt!=0) return; /* gather the next packet */ while(bp = getq(WR(cp->rq))) { if (bp->type == M_DATA) { if (nblocks < NXMT) { cp->xmt[nblocks++] = bp; nbytes += bp->wptr-bp->rptr; } else { printf("qestagepacket: too many blocks\n"); freeb(bp); } } else if (bp->type == M_DELIM) { struct etherpup *ep; struct qe *qp=&qe[cp->unit]; int i; if (nblocks==0) { printf("qestagepacket: no data\n"); break; } if (nbytes<ETHERMINTU) { cp->xmt[nblocks-1]->wptr += ETHERMINTU-nbytes; } else if (nbytes>ETHERMAXTU) { printf("qestagepacket: too long\n"); break; } if (cp->xmt[0]->wptr-cp->xmt[0]->rptr < sizeof(struct etherpup)) { printf("qestagepacket: too wierd\n"); break; } ep = (struct etherpup *)cp->xmt[0]->rptr; ep->type = cp->type; for(i=0; i<6; i++) ep->shost[i] = qp->setup_pkt[i][1]; cp->nxmt = nblocks; freeb(bp); /*printf("qestagepacket: staged %d\n", cp->nxmt);*/ return; } else { printf("qestagepacket: bad bp->type\n"); break; } } /* something is wrong */ if (bp) freeb(bp); else if (nblocks!=0) printf("qestagepacket: missing delim\n"); for(--nblocks; nblocks>=0; nblocks--) freeb(cp->xmt[nblocks]); cp->nxmt = 0; } #define QEDEBSIZE 64 struct { time_t time; unsigned short code; struct etherpup pup; } qed[QEDEBSIZE]; int qei = 0; qedebug(bp, code) register struct block *bp; { qed[qei].time = time; qed[qei].code = code; bcopy(bp->rptr, &qed[qei].pup, sizeof(qed[qei].pup)); qei = (qei + 1) % QEDEBSIZE; } qeioctl(q, bp) register struct queue *q; register struct block *bp; { union stmsg *sp; register struct qechan *cp; int i; char *ap; cp = (struct qechan *)q->ptr; sp = (union stmsg *)bp->rptr; bp->type = M_IOCACK; switch(sp->ioc0.com){ case ENIOTYPE: cp->type = *((int *)(sp->iocx.xxx)); break; case ENIOADDR: for (ap=sp->iocx.xxx, i=0; i<6; i++) *ap++ = qe[cp->unit].addr->qe_sta_addr[i]; break; default: bp->type = M_IOCNAK; break; } qreply(q, bp); }