Ejemplo n.º 1
0
void
timerdel(Timer *dt)
{
	Timers *tt;
	uvlong when;

	ilock(dt);
	if(tt = dt->tt){
		ilock(tt);
		when = tdel(dt);
		if(when && tt == &timers[m->machno])
			timerset(tt->head->twhen);
		iunlock(tt);
	}
	iunlock(dt);
}
Ejemplo n.º 2
0
Archivo: tod.c Proyecto: 8l/inferno
/*
 *  Set the time of day struct
 */
void
todset(vlong t, vlong delta, int n)
{
	if(!tod.init)
		todinit();

	ilock(&tod);
	if(t >= 0){
		tod.off = t;
		tod.last = fastticks(nil);
		tod.lasttime = 0;
		tod.delta = 0;
		tod.sstart = tod.send;
	} else {
		if(n <= 0)
			n = 1;
		n *= HZ;
		if(delta < 0 && n > -delta)
			n = -delta;
		if(delta > 0 && n > delta)
			n = delta;
		delta = delta/n;
		tod.sstart = MACHP(0)->ticks;
		tod.send = tod.sstart + n;
		tod.delta = delta;
	}
	iunlock(&tod);
}
Ejemplo n.º 3
0
Archivo: fs.c Proyecto: funbar/Uptime
// Look up and return the inode for a path name.
// If parent != 0, return the inode for the parent and copy the final
// path element into name, which must have room for DIRSIZ bytes.
// Must be called inside a transaction since it calls iput().
static struct inode*
namex(char *path, int nameiparent, char *name)
{
  struct inode *ip, *next;

  if(*path == '/')
    ip = iget(ROOTDEV, ROOTINO);
  else
    ip = idup(proc->cwd);

  while((path = skipelem(path, name)) != 0){
    ilock(ip);
    if(ip->type != T_DIR){
      iunlockput(ip);
      return 0;
    }
    if(nameiparent && *path == '\0'){
      // Stop one level early.
      iunlock(ip);
      return ip;
    }
    if((next = dirlookup(ip, name, 0)) == 0){
      iunlockput(ip);
      return 0;
    }
    iunlockput(ip);
    ip = next;
  }
  if(nameiparent){
    iput(ip);
    return 0;
  }
  return ip;
}
Ejemplo n.º 4
0
/*
 *  Mark a queue as closed.  No further IO is permitted.
 *  All blocks are released.
 */
void
qclose(Queue *q)
{
	Block *bfirst;

	if(q == nil)
		return;

	/* mark it */
	ilock(q);
	q->state |= Qclosed;
	q->state &= ~(Qflow|Qstarve);
	kstrcpy(q->err, Ehungup, ERRMAX);
	bfirst = q->bfirst;
	q->bfirst = nil;
	q->len = 0;
	q->dlen = 0;
	q->noblock = 0;
	iunlock(q);

	/* free queued blocks */
	freeblist(bfirst);

	/* wake up readers/writers */
	wakeup(&q->rr);
	wakeup(&q->wr);
}
Ejemplo n.º 5
0
static void
shutdown(Hci *hp)
{
	int i;
	Ctlr *ctlr;
	Eopio *opio;

	ctlr = hp->aux;
	ilock(ctlr);
	opio = ctlr->opio;
	opio->cmd |= Chcreset;		/* controller reset */
	coherence();
	for(i = 0; i < 100; i++){
		if((opio->cmd & Chcreset) == 0)
			break;
		delay(1);
	}
	if(i >= 100)
		print("ehci %#p controller reset timed out\n", ctlr->capio);
	delay(100);
	ehcirun(ctlr, 0);
	opio->frbase = 0;
	coherence();
	iunlock(ctlr);
}
Ejemplo n.º 6
0
void
timerdel(Timer *dt)
{
	Timers *tt;
	int64_t when;

	ilock(&dt->l);
	if((tt = dt->tt) != nil){
		ilock(&tt->l);
		when = tdel(dt);
		if(when && tt == &timers[machp()->machno])
			timerset(tt->head->twhen);
		iunlock(&tt->l);
	}
	iunlock(&dt->l);
}
Ejemplo n.º 7
0
static void
cecputs(char *str, int n)
{
	int i, c, wake;
	Conn *cp;

	wake = 0;
	for(cp = conn; cp < conn+Nconns; cp++){
		ilock(cp);
		if(cp->state == Copen){
			for (i = 0; i < n; i++){
				c = str[i];
				if(c == 0)
					continue;	/* BOTCH */
				if(c == '\n')
					cbput(cp, '\r');
				cbput(cp, c);
			}
			cp->stalled = 1;
			wake = 1;
		}
		iunlock(cp);
	}
	if(wake){
		tcond = 1;
		wakeup(&trendez);
	}
}
Ejemplo n.º 8
0
static void
ks8695_modemintr(Ureg*, void *arg)
{
	Ctlr *ctlr;
	Uart *uart;
	int old, r;

	uart = arg;
	ctlr = uart->regs;
	r = csr8r(ctlr, Msr);
	if(r & Dcts){
		ilock(&uart->tlock);
		old = uart->cts;
		uart->cts = r & Cts;
		if(old == 0 && uart->cts)
			uart->ctsbackoff = 2;
		iunlock(&uart->tlock);
	}
 	if(r & Ddsr){
		old = r & Dsr;
		if(uart->hup_dsr && uart->dsr && !old)
			uart->dohup = 1;
		uart->dsr = old;
	}
 	if(r & Ddcd){
		old = r & Dcd;
		if(uart->hup_dcd && uart->dcd && !old)
			uart->dohup = 1;
		uart->dcd = old;
	}
}
Ejemplo n.º 9
0
void *
osi_UFSOpen(afs_dcache_id_t *ainode)
{
    struct inode *ip;
    struct osi_file *afile = NULL;
    extern int cacheDiskType;
    afs_int32 code = 0;
    int dummy;
    AFS_STATCNT(osi_UFSOpen);
    if (cacheDiskType != AFS_FCACHE_TYPE_UFS) {
	osi_Panic("UFSOpen called for non-UFS cache\n");
    }
    if (!afs_osicred_initialized) {
	/* valid for alpha_osf, SunOS, Ultrix */
	memset(&afs_osi_cred, 0, sizeof(afs_ucred_t));
	crhold(&afs_osi_cred);	/* don't let it evaporate, since it is static */
	afs_osicred_initialized = 1;
    }
    afile = osi_AllocSmallSpace(sizeof(struct osi_file));
    setuerror(0);
    AFS_GUNLOCK();
    ip = (struct inode *)igetinode(afs_cacheVfsp, (dev_t) cacheDev.dev,
				   (ino_t) ainode->ufs, &dummy);
    AFS_GLOCK();
    if (getuerror()) {
	osi_FreeSmallSpace(afile);
	osi_Panic("UFSOpen: igetinode failed");
    }
    iunlock(ip);
    afile->vnode = ITOV(ip);
    afile->size = VTOI(afile->vnode)->i_size;
    afile->offset = 0;
    afile->proc = (int (*)())0;
    return (void *)afile;
}
Ejemplo n.º 10
0
void
freeb(Block *b)
{
	void *dead = (void*)Bdead;
	uint8_t *p;

	if(b == nil)
		return;

	/*
	 * drivers which perform non cache coherent DMA manage their own buffer
	 * pool of uncached buffers and provide their own free routine.
	 */
	if(b->free) {
		b->free(b);
		return;
	}
	if(b->flag & BINTR) {
		ilock(&ialloc);
		ialloc.bytes -= b->lim - b->base;
		iunlock(&ialloc);
	}

	p = b->base;

	/* poison the block in case someone is still holding onto it */
	b->next = dead;
	b->rp = dead;
	b->wp = dead;
	b->lim = dead;
	b->base = dead;

	free(p);
}
Ejemplo n.º 11
0
Archivo: allocb.c Proyecto: npe9/harvey
Block*
iallocb(int size)
{
	Block *b;
	static int m1, m2;

	if(ialloc.bytes > conf.ialloc){
		if((m1++%10000)==0)
			print("iallocb: limited %lud/%lud\n",
				ialloc.bytes, conf.ialloc);
		return 0;
	}

	if((b = _allocb(size)) == nil){
		if((m2++%10000)==0)
			print("iallocb: no memory %lud/%lud\n",
				ialloc.bytes, conf.ialloc);
		return nil;
	}
	setmalloctag(b, getcallerpc(&size));
	b->flag = BINTR;

	ilock(&ialloc.lk);
	ialloc.bytes += b->lim - b->base;
	iunlock(&ialloc.lk);

	return b;
}
Ejemplo n.º 12
0
int
qpassnolim(Queue *q, Block *b)
{
	int dlen, len, dowakeup;

	/* sync with qread */
	dowakeup = 0;
	ilock(q);

	if(q->state & Qclosed){
		freeblist(b);
		iunlock(q);
		return BALLOC(b);
	}

	/* add buffer to queue */
	if(q->bfirst)
		q->blast->next = b;
	else
		q->bfirst = b;
	len = BALLOC(b);
	dlen = BLEN(b);
	QDEBUG checkb(b, "qpass");
	while(b->next){
		b = b->next;
		QDEBUG checkb(b, "qpass");
		len += BALLOC(b);
		dlen += BLEN(b);
	}
	q->blast = b;
	q->len += len;
	q->dlen += dlen;

	if(q->len >= q->limit/2)
		q->state |= Qflow;

	if(q->state & Qstarve){
		q->state &= ~Qstarve;
		dowakeup = 1;
	}
	iunlock(q);

	if(dowakeup)
		wakeup(&q->rr);

	return len;
}
Ejemplo n.º 13
0
void
intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
{
	int vno;
	Vctl *v;

	if(f == nil){
		print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n",
			irq, tbdf, name);
		return;
	}

	if(tbdf != BUSUNKNOWN && (irq == 0xff || irq == 0)){
		print("intrenable: got unassigned irq %d, tbdf 0x%uX for %s\n",
			irq, tbdf, name);
		irq = -1;
	}

	if((v = xalloc(sizeof(Vctl))) == nil)
		panic("intrenable: out of memory");
	v->isintr = 1;
	v->irq = irq;
	v->tbdf = tbdf;
	v->f = f;
	v->a = a;
	strncpy(v->name, name, KNAMELEN-1);
	v->name[KNAMELEN-1] = 0;

	ilock(&vctllock);
	vno = arch->intrenable(v);
	if(vno == -1){
		iunlock(&vctllock);
		print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n",
			irq, tbdf, v->name);
		xfree(v);
		return;
	}
	if(vctl[vno]){
		if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi)
			panic("intrenable: handler: %s %s %#p %#p %#p %#p",
				vctl[vno]->name, v->name,
				vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi);
		v->next = vctl[vno];
	}
	vctl[vno] = v;
	iunlock(&vctllock);
}
Ejemplo n.º 14
0
static struct inode *_name(char *p, int nameiparent, char *dirname)
{
    struct inode *in, *next;
    char *name;
    char arr[64];
    char *path = arr;
    strcpy(arr, p);
    if(*path == '/') {
        in = iget(0, 1);
        while(*++path == '/')
            ;
    }
    else
        in = idup(current_proc->cwd);
    name = strtok(path, "/");
    while(name)
    {
//        printk("File system search:%s\n", name);
        ilock(in);
        if(!ISDIR(in->mode)) 
        {
            iunlock(in);
            iput(in);
            return NULL;
        }
        if(nameiparent && !tok_hasnext())
        {
            iunlock(in);
            if(dirname)
                strcpy(dirname, name);
            return in;
        }
        if(!(next = dirlookup(in, name, 0))) {
            iunlock(in);
            iput(in);
            return NULL;
        }
        iunlock(in);
        iput(in);
        in = next;
        if(!tok_hasnext() && dirname)
            strcpy(dirname, name);
        name = strtok(NULL, "/");
    }
    
    return in;
}
Ejemplo n.º 15
0
/*
 *  copy from offset in the queue
 */
Block*
qcopy(Queue *q, int len, ulong offset)
{
	int sofar;
	int n;
	Block *b, *nb;
	uchar *p;

	nb = allocb(len);

	ilock(q);

	/* go to offset */
	b = q->bfirst;
	for(sofar = 0; ; sofar += n){
		if(b == nil){
			iunlock(q);
			return nb;
		}
		n = BLEN(b);
		if(sofar + n > offset){
			p = b->rp + offset - sofar;
			n -= offset - sofar;
			break;
		}
		QDEBUG checkb(b, "qcopy");
		b = b->next;
	}

	/* copy bytes from there */
	for(sofar = 0; sofar < len;){
		if(n > len - sofar)
			n = len - sofar;
		memmove(nb->wp, p, n);
		qcopycnt += n;
		sofar += n;
		nb->wp += n;
		b = b->next;
		if(b == nil)
			break;
		n = BLEN(b);
		p = b->rp;
	}
	iunlock(q);

	return nb;
}
Ejemplo n.º 16
0
static void
w_timer(void* arg)
{
    Ether* ether = (Ether*) arg;
    Ctlr* ctlr = (Ctlr*)ether->ctlr;

    ctlr->timerproc = up;
    for(;;) {
        tsleep(&up->sleep, return0, 0, MSperTick);
        ctlr = (Ctlr*)ether->ctlr;
        if(ctlr == 0)
            break;
        if((ctlr->state & (Attached|Power)) != (Attached|Power))
            continue;
        ctlr->ticks++;

        ilock(ctlr);

        // Seems that the card gets frames BUT does
        // not send the interrupt; this is a problem because
        // I suspect it runs out of receive buffers and
        // stops receiving until a transmit watchdog
        // reenables the card.
        // The problem is serious because it leads to
        // poor rtts.
        // This can be seen clearly by commenting out
        // the next if and doing a ping: it will stop
        // receiving (although the icmp replies are being
        // issued from the remote) after a few seconds.
        // Of course this `bug' could be because I'm reading
        // the card frames in the wrong way; due to the
        // lack of documentation I cannot know.

        if(csr_ins(ctlr, WR_EvSts)&WEvs) {
            ctlr->tickintr++;
            w_intr(ether);
        }

        if((ctlr->ticks % 10) == 0) {
            if(ctlr->txtmout && --ctlr->txtmout == 0) {
                ctlr->nwatchdogs++;
                w_txdone(ctlr, WTxErrEv);
                if(w_enable(ether)) {
                    DEBUG("wavelan: wdog enable failed\n");
                }
                w_txstart(ether);
            }
            if((ctlr->ticks % 120) == 0)
                if(ctlr->txbusy == 0)
                    w_cmd(ctlr, WCmdEnquire, WTyp_Stats);
            if(ctlr->scanticks > 0)
                if((ctlr->ticks % ctlr->scanticks) == 0)
                    if(ctlr->txbusy == 0)
                        w_cmd(ctlr, WCmdEnquire, WTyp_Scan);
        }
        iunlock(ctlr);
    }
    pexit("terminated", 0);
}
Ejemplo n.º 17
0
static void
igbetransmit(Ether* edev)
{
	Block *bp;
	Ctlr *ctlr;
	Tdesc *tdesc;
	RingBuf *tb;
	int tdh;

	/*
	 * For now there are no smarts here. Tuning comes later.
	 */
	ctlr = edev->ctlr;
	ilock(&ctlr->tdlock);

	/*
	 * Free any completed packets
	 * - try to get the soft tdh to catch the tdt;
	 * - if the packet had an underrun bump the threshold
	 *   - the Tu bit doesn't seem to ever be set, perhaps
	 *     because Rs mode is used?
	 */
	tdh = ctlr->tdh;
	for(;;){
		tdesc = &ctlr->tdba[tdh];
		if(!(tdesc->status & Tdd))
			break;
		if(tdesc->status & Tu){
			ctlr->ett++;
			csr32w(ctlr, Ett, ctlr->ett);
		}
		tdesc->status = 0;
		if(ctlr->tb[tdh] != nil){
			freeb(ctlr->tb[tdh]);
			ctlr->tb[tdh] = nil;
		}
		tdh = NEXT(tdh, Ntdesc);
	}
	ctlr->tdh = tdh;

	/* copy packets from the software RingBuf to the transmission q */
	/* from boot ether83815.c */
	while((tb = &edev->tb[edev->ti])->owner == Interface){
		bp = fromringbuf(edev);

		/* put the buffer on the transmit queue */
		if(ctlr->bqhead)
			ctlr->bqtail->next = bp;
		else
			ctlr->bqhead = bp;
		ctlr->bqtail = bp;

		txstart(edev);		/* kick transmitter */
		tb->owner = Host;	/* give descriptor back */

		edev->ti = NEXT(edev->ti, edev->ntb);
	}
	iunlock(&ctlr->tdlock);
}
Ejemplo n.º 18
0
static void
igbeim(Ctlr* ctlr, int im)
{
	ilock(&ctlr->imlock);
	ctlr->im |= im;
	csr32w(ctlr, Ims, ctlr->im);
	iunlock(&ctlr->imlock);
}
Ejemplo n.º 19
0
int
vgaxi(long port, uchar index)
{
	uchar data;

	ilock(&vgaxlock);
	switch(port){

	case Seqx:
	case Crtx:
	case Grx:
		outb(port, index);
		data = inb(port+1);
		break;

	case Attrx:
		/*
		 * Allow processor access to the colour
		 * palette registers. Writes to Attrx must
		 * be preceded by a read from Status1 to
		 * initialise the register to point to the
		 * index register and not the data register.
		 * Processor access is allowed by turning
		 * off bit 0x20.
		 */
		inb(Status1);
		if(index < 0x10){
			outb(Attrx, index);
			data = inb(Attrx+1);
			inb(Status1);
			outb(Attrx, 0x20|index);
		}
		else{
			outb(Attrx, 0x20|index);
			data = inb(Attrx+1);
		}
		break;

	default:
		iunlock(&vgaxlock);
		return -1;
	}
	iunlock(&vgaxlock);

	return data & 0xFF;
}
Ejemplo n.º 20
0
static void
attach(Ether *ether)
{
	Smc91xx* ctlr;

	ctlr = ether->ctlr;
	ilock(ctlr);
	
	if (ctlr->attached) {
		iunlock(ctlr);
		return;
	}

	chipenable(ether);
	ctlr->attached = 1;
	iunlock(ctlr);
}
Ejemplo n.º 21
0
void
cgaconsputs(char* s, int n)
{
	ilock(&cgalock);
	while(n-- > 0)
		cgaputc(*s++);
	iunlock(&cgalock);
}
Ejemplo n.º 22
0
static void
vt6102imr(Ctlr* ctlr, int imr)
{
	ilock(&ctlr->clock);
	ctlr->imr |= imr;
	csr16w(ctlr, Imr, ctlr->imr);
	iunlock(&ctlr->clock);
}
Ejemplo n.º 23
0
static void
freeref(int ref)
{
	ilock(&refalloc);
	refalloc.refs[ref] = refalloc.free;
	refalloc.free = ref;
	iunlock(&refalloc);
}
Ejemplo n.º 24
0
Archivo: devrtc.c Proyecto: 8l/inferno
void
nvramwrite(int addr, uchar data)
{
	ilock(&nvrtlock);
	outb(Paddr, addr);
	outb(Pdata, data);
	iunlock(&nvrtlock);
}
Ejemplo n.º 25
0
int conwrite(struct inode *in, char *addr, int len)
{
    iunlock(in);
    for(int i = 0; i < len && addr[i]; i++)
        putchar_normal(addr[i]);
    ilock(in);
    return len;
}
Ejemplo n.º 26
0
/*
 * transmit strategy: fill the output ring as far as possible,
 * perhaps leaving a few spare; kick off the output and take
 * an interrupt only when the transmit queue is empty.
 */
static void
transmit(Ether *ether)
{
	int i, kick, len;
	Block *b;
	Ctlr *ctlr = ether->ctlr;
	Gbereg *reg = ctlr->reg;
	Tx *t;

	ethercheck(ether);
	ilock(ctlr);
	txreplenish(ether);			/* reap old packets */

	/* queue new packets; use at most half the tx descs to avoid livelock */
	kick = 0;
	for (i = Ntx/2 - 2; i > 0; i--) {
		t = &ctlr->tx[ctlr->txhead];	/* *t is uncached */
		assert(((uintptr)t & (Descralign - 1)) == 0);
		if(t->cs & TCSdmaown) {		/* descriptor busy? */
			ctlr->txringfull++;
			break;
		}

		b = qget(ether->oq);		/* outgoing packet? */
		if (b == nil)
			break;
		len = BLEN(b);
		if(len < ether->minmtu || len > ether->maxmtu) {
			freeb(b);
			continue;
		}
		ctlr->txb[ctlr->txhead] = b;

		/* make sure the whole packet is in memory */
		cachedwbse(b->rp, len);
		l2cacheuwbse(b->rp, len);

		/* set up the transmit descriptor */
		t->buf = PADDR(b->rp);
		t->countchk = len << 16;
		coherence();

		/* and fire */
		t->cs = TCSpadding | TCSfirst | TCSlast | TCSdmaown |
			TCSenableintr;
		coherence();

		kick++;
		ctlr->txhead = NEXT(ctlr->txhead, Ntx);
	}
	if (kick) {
		txkick(ctlr);

		reg->irqmask  |= Itxendq(Qno);
		reg->irqemask |= IEtxerrq(Qno) | IEtxunderrun;
	}
	iunlock(ctlr);
}
Ejemplo n.º 27
0
static void
ehcireset(Ctlr *ctlr)
{
	Eopio *opio;
	int i;

	ilock(ctlr);
	dprint("ehci %#p reset\n", ctlr->capio);
	opio = ctlr->opio;

	/*
	 * Turn off legacy mode. Some controllers won't
	 * interrupt us as expected otherwise.
	 */
	ehcirun(ctlr, 0);

	/* clear high 32 bits of address signals if it's 64 bits capable.
	 * This is probably not needed but it does not hurt and others do it.
	 */
	if((ctlr->capio->capparms & C64) != 0){
		dprint("ehci: 64 bits\n");
		opio->seg = 0;
	}

	if(ehcidebugcapio != ctlr->capio){
		opio->cmd |= Chcreset;	/* controller reset */
		coherence();
		for(i = 0; i < 100; i++){
			if((opio->cmd & Chcreset) == 0)
				break;
			delay(1);
		}
		if(i == 100)
			print("ehci %#p controller reset timed out\n", ctlr->capio);
	}

	/* requesting more interrupts per µframe may miss interrupts */
	opio->cmd &= ~Citcmask;
	opio->cmd |= 1 << Citcshift;		/* max of 1 intr. per 125 µs */
	coherence();
	switch(opio->cmd & Cflsmask){
	case Cfls1024:
		ctlr->nframes = 1024;
		break;
	case Cfls512:
		ctlr->nframes = 512;
		break;
	case Cfls256:
		ctlr->nframes = 256;
		break;
	default:
		panic("ehci: unknown fls %ld", opio->cmd & Cflsmask);
	}
	coherence();
	dprint("ehci: %d frames\n", ctlr->nframes);
	iunlock(ctlr);
}
Ejemplo n.º 28
0
Archivo: gpio.c Proyecto: 8l/inferno
void
gpiorelease(ulong mask)
{
	ilock(&gpiolock);
	if((gpioreserved & mask) != mask)
		panic("gpiorelease: unexpected release of 0x%.8lux", ~gpioreserved & mask);
	gpioreserved &= ~mask;
	iunlock(&gpiolock);
}
Ejemplo n.º 29
0
Archivo: gpio.c Proyecto: 8l/inferno
void
gpioreserve(ulong mask)
{
	ilock(&gpiolock);
	if(gpioreserved & mask)
		panic("gpioreserve: duplicate use of 0x%.8lux", gpioreserved & mask);
	gpioreserved |= mask;
	iunlock(&gpiolock);
}
Ejemplo n.º 30
0
static void
i82563interrupt(Ureg*, void* arg)
{
	int icr, im, rdh, txdw = 0;
	Block *bp;
	Ctlr *ctlr;
	Ether *edev;
	Rdesc *rdesc;

	edev = arg;
	ctlr = edev->ctlr;

	ilock(&ctlr->imlock);
	csr32w(ctlr, Imc, ~0);
	im = ctlr->im;

	for(icr = csr32r(ctlr, Icr); icr & ctlr->im; icr = csr32r(ctlr, Icr)){
		if(icr & (Rxseq|Lsc)){
			/* should be more here */
		}

		rdh = ctlr->rdh;
		for (;;) {
			rdesc = &ctlr->rdba[rdh];
			if(!(rdesc->status & Rdd))
				break;
			if ((rdesc->status & Reop) && rdesc->errors == 0) {
				bp = ctlr->rb[rdh];
				if(0 && memcmp(bp->rp, broadcast, 6) != 0)
					print("#l%d: rx %d %E %E %d\n",
						edev->ctlrno, rdh, bp->rp,
						bp->rp+6, rdesc->length);
				ctlr->rb[rdh] = nil;
				bp->wp += rdesc->length;
				if (interesting(bp))
					toringbuf(edev, bp);
				freeb(bp);
			} else if (rdesc->status & Reop && rdesc->errors)
				print("%s: input packet error %#ux\n",
					tname[ctlr->type], rdesc->errors);
			rdesc->status = 0;
			rdh = NEXT(rdh, Nrdesc);
		}
		ctlr->rdh = rdh;
		if(icr & Rxdmt0)
			i82563replenish(ctlr);
		if(icr & Txdw){
			im &= ~Txdw;
			txdw++;
		}
	}
	ctlr->im = im;
	csr32w(ctlr, Ims, im);
	iunlock(&ctlr->imlock);
	if(txdw)
		i82563transmit(edev);
}