shithub: riscv

Download patch

ref: 8ceb100fa4081cc441acc693190ce7da55fcb3af
parent: 5bfaf253d3ea0f0d60eb12b45b0866a79c5ae66b
author: cinap_lenrek <[email protected]>
date: Fri Oct 18 14:41:27 EDT 2013

nusb/ether: new driver interface with kernel inspired Block buffers

introduce kernel inspured Block structure with
read/write pointers to pass packets arround.

the intend is to avoid copying when adding/removing
frame headers and simplifying the drivers.

the driver now calls etheriq() directly allowing it to
queue multiple packets in one pass without having
to keep state.

transmit gets a buffer passed that has room for
frame headers and trailers so no copying is needed.

blocks are refcounted avoiding another copy when
passed to only one receive queue (common case).

receive queues are now limited to 100KB avoiding
buffer bloat.

Dq* a memory leak in destroyfid has been fixed.

lots of minor cleanups.

--- a/sys/src/cmd/nusb/ether/asix.c
+++ b/sys/src/cmd/nusb/ether/asix.c
@@ -207,51 +207,53 @@
 }
 
 static int
-asixread(Dev *ep, uchar *p, int plen)
+asixreceive(Dev *ep)
 {
-	int n, m;
+	Block *b;
 	uint hd;
+	int n;
 
-	if(nbin < 4)
-		nbin = read(ep->dfd, bin, sizeof(bin));
-	if(nbin < 0)
+	b = allocb(Maxpkt+4);
+	if((n = read(ep->dfd, b->wp, b->lim - b->base)) < 0){
+		freeb(b);
 		return -1;
-	if(nbin < 4)
-		return 0;
-	hd = GET4(bin);
-	n = hd & 0xFFFF;
-	m = n+4;
-	hd = (hd>>16) ^ 0xFFFF;
-	if((n != hd) || (n < 6) || (m > nbin)){
-		nbin = 0;
-		return 0;
 	}
-	if(n > plen)
-		n = plen;
-	if(n > 0)
-		memmove(p, bin+4, n);
-	if(m < nbin)
-		memmove(bin, bin+m, nbin - m);
-	nbin -= m;
-	return n;
+	b->wp += n;
+	while(BLEN(b) >= 4){
+		hd = GET4(b->rp);
+		b->rp += 4;
+		n = hd & 0xFFFF;
+		hd = (hd>>16) ^ 0xFFFF;
+		if((n != hd) || (n > BLEN(b)))
+			break;
+		if(n == BLEN(b)){
+			etheriq(b, 1);
+			return 0;
+		}
+		etheriq(copyblock(b, n), 1);
+		b->rp += n;
+	}
+	freeb(b);
+	return 0;
 }
 
 static void
-asixwrite(Dev *ep, uchar *p, int n)
+asixtransmit(Dev *ep, Block *b)
 {
 	uint hd;
+	int n;
 
-	if(n > sizeof(bout)-8)
-		n = sizeof(bout)-8;
+	n = BLEN(b);
 	hd = n | (n<<16)^0xFFFF0000;
-	PUT4(bout, hd);
-	memmove(bout+4, p, n);
+	b->rp -= 4;
+	PUT4(b->rp, hd);
 	n += 4;
 	if((n % ep->maxpkt) == 0){
-		PUT4(bout+n, 0xFFFF0000);
-		n += 4;
+		PUT4(b->wp, 0xFFFF0000);
+		b->wp += 4;
 	}
-	write(ep->dfd, bout, n);
+	write(ep->dfd, b->rp, BLEN(b));
+	freeb(b);
 }
 
 static int
@@ -312,8 +314,8 @@
 	asixset(d, Cwmedium, Mall178);
 	asixset(d, Cwrxctl, Rxctlso|Rxctlab);
 
-	epread = asixread;
-	epwrite = asixwrite;
+	epreceive = asixreceive;
+	eptransmit = asixtransmit;
 	return 0;
 }
 
@@ -369,7 +371,7 @@
 	if(asixset(d, Cwrxctl, Rxctlso|Rxctlab) < 0)
 		return -1;
 
-	epread = asixread;
-	epwrite = asixwrite;
+	epreceive = asixreceive;
+	eptransmit = asixtransmit;
 	return 0;
 }
--- a/sys/src/cmd/nusb/ether/aue.c
+++ b/sys/src/cmd/nusb/ether/aue.c
@@ -78,8 +78,6 @@
 static int csr8w(Dev *, int, int);
 static int eeprom16r(Dev *, int);
 static void reset(Dev *);
-static int aueread(Dev *, uchar *, int);
-static void auewrite(Dev *, uchar *, int);
 
 static int
 csr8r(Dev *d, int reg)
@@ -165,49 +163,43 @@
 }
 
 static int
-aueread(Dev *ep, uchar *p, int plen)
+auereceive(Dev *ep)
 {
-	int n;
+	Block *b;
 	uint hd;
-	uchar *q;
+	int n;
 
-	if(nbin < 4)
-		nbin = read(ep->dfd, bin, sizeof bin);
-	if(nbin < 0)
+	b = allocb(Maxpkt+4);
+	if((n = read(ep->dfd, b->wp, b->lim - b->base)) < 0){
+		freeb(b);
 		return -1;
-	if(nbin < 4)
+	}
+	if(n < 4){
+		freeb(b);
 		return 0;
-	q = bin + nbin - 4;
-	hd = GET4(q);
+	}
+	b->wp += n-4;
+	hd = GET4(b->wp);
 	n = hd & 0xfff;
-	if(n < 6 || n > nbin) {
-		nbin = 0;
+	if((hd & Rxerror) != 0 || n > BLEN(b)){
+		freeb(b);
 		return 0;
 	}
-	if(hd & Rxerror) {
-		fprint(2, "%s: rx error %#ux\n",
-			argv0, hd);
-		n = 0;
-	} else {
-		if(n > plen)
-			n = plen;
-		if(n > 0)
-			memmove(p, bin, n);
-	}
-	if(n < nbin)
-		memmove(bin, bin+n, nbin-n);
-	nbin -= n;
-	return n;
+	b->wp = b->rp + n;
+	etheriq(b, 1);
+	return 0;
 }
 
 static void
-auewrite(Dev *ep, uchar *p, int n)
+auetransmit(Dev *ep, Block *b)
 {
-	if(n > sizeof bout-2)
-		n = sizeof bout - 2;
-	PUT2(bout, n);
-	memmove(bout+2, p, n);
-	write(ep->dfd, bout, n+2);
+	int n;
+
+	n = BLEN(b);
+	b->rp -= 2;
+	PUT2(b->rp, n);
+	write(ep->dfd, b->rp, BLEN(b));
+	freeb(b);
 }
 
 int
@@ -227,7 +219,7 @@
 	csr8w(d, Ctl0, C0rxstatappend|C0rxen);
 	csr8w(d, Ctl0, csr8r(d, Ctl0)|C0txen);
 	csr8w(d, Ctl2, csr8r(d, Ctl2)|C2ep3clr);
-	epread = aueread;
-	epwrite = auewrite;
+	epreceive = auereceive;
+	eptransmit = auetransmit;
 	return 0;
 }
--- a/sys/src/cmd/nusb/ether/cdc.c
+++ b/sys/src/cmd/nusb/ether/cdc.c
@@ -5,33 +5,50 @@
 #include <u.h>
 #include <libc.h>
 #include <thread.h>
-#include <ip.h>
 
 #include "usb.h"
 #include "dat.h"
 
+#include <ip.h>
+
 static int
-cdcread(Dev *ep, uchar *p, int n)
+cdcreceive(Dev *ep)
 {
-	return read(ep->dfd, p, n);
+	Block *b;
+	int n;
+
+	b = allocb(Maxpkt);
+	if((n = read(ep->dfd, b->wp, b->lim - b->base)) < 0){
+		freeb(b);
+		return -1;
+	}
+	b->wp += n;
+	etheriq(b, 1);
+	return 0;
 }
 
 static void
-cdcwrite(Dev *ep, uchar *p, int n)
+cdctransmit(Dev *ep, Block *b)
 {
-	if(write(ep->dfd, p, n) < 0){
-		fprint(2, "cdcwrite: %r\n");
-	} else {
-		/*
-		 * this may not work with all CDC devices. the
-		 * linux driver sends one more random byte
-		 * instead of a zero byte transaction. maybe we
-		 * should do the same?
-		 */
-		if(n % ep->maxpkt == 0)
-			write(ep->dfd, "", 0);
+	int n;
+
+	n = BLEN(b);
+	if(write(ep->dfd, b->rp, n) < 0){
+		freeb(b);
+		return;
 	}
+	freeb(b);
+
+	/*
+	 * this may not work with all CDC devices. the
+	 * linux driver sends one more random byte
+	 * instead of a zero byte transaction. maybe we
+	 * should do the same?
+	 */
+	if((n % ep->maxpkt) == 0)
+		write(ep->dfd, "", 0);
 }
+
 int
 cdcinit(Dev *d)
 {
@@ -55,8 +72,8 @@
 					parseether(macaddr, mac);
 					free(mac);
 
-					epread = cdcread;
-					epwrite = cdcwrite;
+					epreceive = cdcreceive;
+					eptransmit = cdctransmit;
 					return 0;
 				}
 			}
--- a/sys/src/cmd/nusb/ether/dat.h
+++ b/sys/src/cmd/nusb/ether/dat.h
@@ -1,3 +1,36 @@
+typedef struct Block Block;
+struct Block
+{
+	Ref;
+
+	Block	*next;
+
+	uchar	*rp;
+	uchar	*wp;
+	uchar	*lim;
+
+	uchar	base[];
+};
+
+#define BLEN(s)	((s)->wp - (s)->rp)
+
+Block*	allocb(int size);
+void	freeb(Block*);
+Block*	copyblock(Block*, int);
+
+typedef struct Ehdr Ehdr;
+struct Ehdr
+{
+	uchar	d[6];
+	uchar	s[6];
+	uchar	type[2];
+};
+
+enum {
+	Ehdrsz	= 6+6+2,
+	Maxpkt	= 2000,
+};
+
 enum
 {
 	Cdcunion = 6,
@@ -10,10 +43,8 @@
 
 /* to be filled in by *init() */
 uchar macaddr[6];
-int (*epread)(Dev *, uchar *, int);
-void (*epwrite)(Dev *, uchar *, int);
 
-/* temporary buffers */
-uchar bout[4*1024];
-uchar bin[4*1024];
-int nbin;
+void	etheriq(Block*, int wire);
+
+int	(*epreceive)(Dev*);
+void	(*eptransmit)(Dev*, Block*);
--- a/sys/src/cmd/nusb/ether/ether.c
+++ b/sys/src/cmd/nusb/ether/ether.c
@@ -1,18 +1,17 @@
 #include <u.h>
 #include <libc.h>
 #include <thread.h>
-#include <fcall.h>
-#include <9p.h>
-#include <ip.h>
 
 #include "usb.h"
 #include "dat.h"
 
+#include <fcall.h>
+#include <9p.h>
+#include <ip.h>
+
 typedef struct Tab Tab;
-typedef struct Qbuf Qbuf;
 typedef struct Dq Dq;
 typedef struct Conn Conn;
-typedef struct Ehdr Ehdr;
 typedef struct Stats Stats;
 
 enum
@@ -48,28 +47,25 @@
 	"type",	0444,
 };
 
-struct Qbuf
-{
-	Qbuf		*next;
-	int		ndata;
-	uchar	data[];
-};
-
 struct Dq
 {
-	QLock	l;
+	QLock;
+
 	Dq		*next;
 	Req		*r;
 	Req		**rt;
-	Qbuf		*q;
-	Qbuf		**qt;
+	Block		*q;
+	Block		**qt;
 
+	int		size;
+
 	int		nb;
 };
 
 struct Conn
 {
-	QLock	l;
+	QLock;
+
 	int		used;
 	int		type;
 	int		prom;
@@ -76,13 +72,6 @@
 	Dq		*dq;
 };
 
-struct Ehdr
-{
-	uchar	d[6];
-	uchar	s[6];
-	uchar	type[2];
-};
-
 struct Stats
 {
 	int		in;
@@ -104,9 +93,6 @@
 #define NUM(path)			(((uint)(path) & 0xFFFFFF00)>>8)
 #define NUMCONN(c)		(((long)(c)-(long)&conn[0])/sizeof(conn[0]))
 
-static int
-receivepacket(void *buf, int len);
-
 static void
 fillstat(Dir *d, uvlong path)
 {
@@ -238,7 +224,7 @@
 matchrq(Dq *d)
 {
 	Req *r;
-	Qbuf *b;
+	Block *b;
 
 	while(r = d->r){
 		int n;
@@ -245,6 +231,8 @@
 
 		if((b = d->q) == nil)
 			break;
+
+		d->size -= BLEN(b);
 		if((d->q = b->next) == nil)
 			d->qt = &d->q;
 		if((d->r = (Req*)r->aux) == nil)
@@ -251,11 +239,12 @@
 			d->rt = &d->r;
 
 		n = r->ifcall.count;
-		if(n > b->ndata)
-			n = b->ndata;
-		memmove(r->ofcall.data, b->data, n);
-		free(b);
+		if(n > BLEN(b))
+			n = BLEN(b);
+		memmove(r->ofcall.data, b->rp, n);
 		r->ofcall.count = n;
+		freeb(b);
+
 		respond(r, nil);
 	}
 }
@@ -266,19 +255,18 @@
 	Dq *d;
 
 	d = r->fid->aux;
-	qlock(&d->l);
+	qlock(d);
 	if(d->q==nil && d->nb){
-		qunlock(&d->l);
+		qunlock(d);
 		r->ofcall.count = 0;
 		respond(r, nil);
 		return;
 	}
-	// enqueue request
 	r->aux = nil;
 	*d->rt = r;
 	d->rt = (Req**)&r->aux;
 	matchrq(d);
-	qunlock(&d->l);
+	qunlock(d);
 }
 
 static void
@@ -287,6 +275,7 @@
 	Dq *d;
 	void *p;
 	int n;
+	Block *b;
 
 	d = r->fid->aux;
 	p = r->ifcall.data;
@@ -295,11 +284,28 @@
 		d->nb = 1;
 		goto out;
 	}
-	epwrite(epout, p, n);
-	if(receivepacket(p, n) == 0)
-		stats.out++;
+
+	/* minimum frame length for rtl8150 */
+	if(n < 60)
+		n = 60;
+
+	/* slack space for header and trailers */
+	n += 2*16;
+
+	b = allocb(n);
+
+	/* header space */
+	b->wp += 16;
+	b->rp = b->wp;
+
+	/* copy in the ethernet packet */
+	memmove(b->wp, p, r->ifcall.count);
+	b->wp += r->ifcall.count;
+
+	etheriq(b, 0);
+
 out:
-	r->ofcall.count = n;
+	r->ofcall.count = r->ifcall.count;
 	respond(r, nil);
 }
 
@@ -433,7 +439,6 @@
 	Dq *d;
 	Conn *c;
 
-
 	/*
 	 * lib9p already handles the blatantly obvious.
 	 * we just have to enforce the permissions we have set.
@@ -472,7 +477,7 @@
 	case Qtype:
 	CaseConn:
 		c = &conn[NUM(path)];
-		qlock(&c->l);
+		qlock(c);
 		if(c->used++ == 0){
 			c->type = 0;
 			c->prom = 0;
@@ -481,7 +486,7 @@
 			d->next = c->dq;
 			c->dq = d;
 		}
-		qunlock(&c->l);
+		qunlock(c);
 		break;
 	}
 
@@ -501,7 +506,7 @@
 	f = o->fid;
 	if(TYPE(f->qid.path) == Qdata){
 		d = f->aux;
-		qlock(&d->l);
+		qlock(d);
 		for(p=&d->r; *p; p=(Req**)&((*p)->aux)){
 			if(*p == o){
 				if((*p = (Req*)o->aux) == nil)
@@ -511,7 +516,7 @@
 				break;
 			}
 		}
-		qunlock(&d->l);
+		qunlock(d);
 	}
 	respond(r, nil);
 }
@@ -521,12 +526,12 @@
 fsdestroyfid(Fid *fid)
 {
 	Conn *c;
-	Qbuf *b;
 	Dq **x, *d;
+	Block *b;
 
 	if(TYPE(fid->qid.path) >= Qndir){
 		c = &conn[NUM(fid->qid.path)];
-		qlock(&c->l);
+		qlock(c);
 		if(d = fid->aux){
 			fid->aux = nil;
 			for(x=&c->dq; *x; x=&((*x)->next)){
@@ -535,17 +540,16 @@
 					break;
 				}
 			}
-			qlock(&d->l);
 			while(b = d->q){
 				d->q = b->next;
-				free(b);
+				freeb(b);
 			}
-			qunlock(&d->l);
+			free(d);
 		}
 		if(TYPE(fid->qid.path) == Qctl)
 			c->prom = 0;
 		c->used--;
-		qunlock(&c->l);
+		qunlock(c);
 	}
 }
 
@@ -648,26 +652,26 @@
 	return 0;
 }
 
-static int
-receivepacket(void *buf, int len)
+void
+etheriq(Block *b, int wire)
 {
-	int i;
-	int t;
+	int i, t;
+	Block *q;
+	Conn *c;
+	Dq *d;
 	Ehdr *h;
 
-	if(len < sizeof(*h))
-		return -1;
+	if(BLEN(b) < Ehdrsz){
+		freeb(b);
+		return;
+	}
 
-	h = (Ehdr*)buf;
+	h = (Ehdr*)b->rp;
 	t = (h->type[0]<<8)|h->type[1];
 
 	for(i=0; i<nconn; i++){
-		Qbuf *b;
-		Conn *c;
-		Dq *d;
-
 		c = &conn[i];
-		qlock(&c->l);
+		qlock(c);
 		if(!c->used)
 			goto next;
 		if(c->type > 0)
@@ -677,28 +681,35 @@
 			if(memcmp(h->d, macaddr, sizeof(macaddr)))
 				goto next;
 		for(d=c->dq; d; d=d->next){
-			int n;
-
-			n = len;
-			if(c->type == -2 && n > 64)
-				n = 64;
-
-			b = emalloc9p(sizeof(*b) + n);
-			b->ndata = n;
-			memcpy(b->data, buf, n);
-
-			qlock(&d->l);
-			// enqueue buffer
-			b->next = nil;
-			*d->qt = b;
-			d->qt = &b->next;
+			if(d->size > 100000)
+				continue;
+			if(c->type == -2) {
+				q = copyblock(b, 64);
+			} else if(wire && b->ref == 1) {
+				incref(b);
+				q = b;
+			} else {
+				q = copyblock(b, BLEN(b));
+			}
+			qlock(d);
+			q->next = nil;
+			*d->qt = q;
+			d->qt = &q->next;
+			d->size += BLEN(q);
 			matchrq(d);
-			qunlock(&d->l);
+			qunlock(d);
 		}
 next:
-		qunlock(&c->l);
+		qunlock(c);
 	}
-	return 0;
+	if(wire) {
+		freeb(b);
+		stats.in++;
+	} else {
+		/* transmit frees buffer */
+		(*eptransmit)(epout, b);
+		stats.out++;
+	}
 }
 
 static void
@@ -705,8 +716,7 @@
 usbreadproc(void *)
 {
 	char err[ERRMAX];
-	uchar buf[4*1024];
-	int n, nerr;
+	int nerr;
 
 	atnotify(inote, 1);
 
@@ -714,8 +724,8 @@
 
 	nerr = 0;
 	for(;;){
-		n = epread(epin, buf, sizeof(buf));
-		if(n < 0){
+		/* receive allocates buffer and calls etheriq(b, 1); */
+		if((*epreceive)(epin) < 0){
 			rerrstr(err, sizeof(err));
 			if(strstr(err, "interrupted") || strstr(err, "timed out"))
 				continue;
@@ -725,10 +735,6 @@
 			threadexitsall(err);
 		}
 		nerr = 0;
-		if(n == 0)
-			continue;
-		if(receivepacket(buf, n) == 0)
-			stats.in++;
 	}
 }
 
@@ -819,7 +825,7 @@
 	werrstr("");
 	if((*ethertype[et].init)(d) < 0)
 		sysfatal("%s init failed: %r", ethertype[et].name);
-	if(epread == nil || epwrite == nil)
+	if(epreceive == nil || eptransmit == nil)
 		sysfatal("bug in init");
 
 	if((epin = openep(d, ei)) == nil)
@@ -845,4 +851,38 @@
 	threadpostsharesrv(&fs, nil, "usbnet", s);
 
 	threadexits(0);
+}
+
+Block*
+allocb(int size)
+{
+	Block *b;
+
+	b = emalloc9p(sizeof(*b) + size);
+	b->lim = b->base + size;
+	b->rp = b->base;
+	b->wp = b->base;
+	b->next = nil;
+	b->ref = 1;
+	return b;
+}
+
+void
+freeb(Block *b)
+{
+	if(decref(b) == 0)
+		free(b);
+}
+
+Block*
+copyblock(Block *b, int count)
+{
+	Block *nb;
+
+	if(count > BLEN(b))
+		count = BLEN(b);
+	nb = allocb(count);
+	memmove(nb->wp, b->rp, count);
+	nb->wp += count;
+	return nb;
 }
--- a/sys/src/cmd/nusb/ether/smsc.c
+++ b/sys/src/cmd/nusb/ether/smsc.c
@@ -205,48 +205,51 @@
 }
 
 static int
-smscread(Dev *ep, uchar *p, int plen)
+smscreceive(Dev *ep)
 {
-	int n, m;
+	Block *b;
 	uint hd;
+	int n;
 
-	if(nbin < 4)
-		nbin = read(ep->dfd, bin, Doburst ? Hsburst*512: sizeof(bin));
-	if(nbin < 0)
+	if(Doburst)
+		b = allocb(Hsburst*512);
+	else
+		b = allocb(Maxpkt+4);
+	if((n = read(ep->dfd, b->wp, b->lim - b->base)) < 0){
+		freeb(b);
 		return -1;
-	if(nbin < 4)
-		return 0;
-	hd = GET4(bin);
-	n = hd >> 16;
-	m = (n + 4 + 3) & ~3;
-	if(n < 6 || n > nbin-4){
-		nbin = 0;
-		return 0;
 	}
-	if(hd & Rxerror){
-		fprint(2, "smsc rx error %8.8ux\n", hd);
-		n = 0;
-	}else{
-		if(n > plen)
-			n = plen;
-		if(n > 0)
-			memmove(p, bin+4, n);
+	b->wp += n;
+	while(BLEN(b) >= 4){
+		hd = GET4(b->rp);
+		b->rp += 4;
+		n = hd >> 16;
+		if(n > BLEN(b))
+			break;
+		if((hd & Rxerror) == 0){
+			if(n == BLEN(b)){
+				etheriq(b, 1);
+				return 0;
+			}
+			etheriq(copyblock(b, n), 1);
+		}
+		b->rp += (n + 3) & ~3;
 	}
-	if(m < nbin)
-		memmove(bin, bin+m, nbin - m);
-	nbin -= m;
-	return n;
+	freeb(b);
+	return 0;
 }
 
 static void
-smscwrite(Dev *ep, uchar *p, int n)
+smsctransmit(Dev *ep, Block *b)
 {
-	if(n > sizeof(bout)-8)
-		n = sizeof(bout)-8;
-	PUT4(bout, n | Txfirst | Txlast);
-	PUT4(bout+4, n);
-	memmove(bout+8, p, n);
-	write(ep->dfd, bout, n+8);
+	int n;
+
+	n = BLEN(b);
+	b->rp -= 8;
+	PUT4(b->rp, n | Txfirst | Txlast);
+	PUT4(b->rp+4, n);
+	write(ep->dfd, b->rp, BLEN(b));
+	freeb(b);
 }
 
 int
@@ -284,7 +287,7 @@
 	wr(d, Maccr, rr(d, Maccr)|Txen|Rxen);
 	wr(d, Txcfg, Txon);
 
-	epwrite = smscwrite;
-	epread = smscread;
+	eptransmit = smsctransmit;
+	epreceive = smscreceive;
 	return 0;
 }
--- a/sys/src/cmd/nusb/ether/url.c
+++ b/sys/src/cmd/nusb/ether/url.c
@@ -164,8 +164,6 @@
 static int csr16w(Dev *, int, int);
 static int csr32w(Dev *, int, int);
 static void reset(Dev *);
-static int urlread(Dev *, uchar *, int);
-static void urlwrite(Dev *, uchar *, int);
 int urlinit(Dev *);
 
 static int
@@ -263,49 +261,43 @@
 }
 
 static int
-urlread(Dev *ep, uchar *p, int plen)
+urlreceive(Dev *ep)
 {
-	int n;
+	Block *b;
 	uint hd;
-	uchar *q;
+	int n;
 
-	if(nbin < 4)
-		nbin = read(ep->dfd, bin, sizeof bin);
-	if(nbin < 0)
+	b = allocb(Maxpkt+4);
+	if((n = read(ep->dfd, b->wp, b->lim - b->base)) < 0){
+		freeb(b);
 		return -1;
-	if(nbin < 4)
+	}
+	if(n < 4){
+		freeb(b);
 		return 0;
-	n = nbin - 4;
-	if(n < 6) {
-		nbin = 0;
-		return 0;
 	}
-	q = bin + n;
-	hd = GET2(q);
-	if((hd & Vpm) == 0) {
-		fprint(2, "url: rx error: %#.4ux\n", hd);
-		n = 0;
-	} else {
-		if(n > plen)
-			n = plen;
-		if(n > 0)
-			memmove(p, bin, n);
-	}
-	nbin = 0;
-	return n;
+	n -= 4;
+	b->wp += n;
+	hd = GET2(b->wp);
+	if((hd & Vpm) == 0)
+		freeb(b);
+	else
+		etheriq(b, 1);
+	return 0;
 }
 
 static void
-urlwrite(Dev *ep, uchar *p, int n)
+urltransmit(Dev *ep, Block *b)
 {
-	if(n > sizeof bout)
-		n = sizeof bout;
-	memmove(bout, p, n);
-	if(n < Mfl) {
-		memset(bout+n, 0, Mfl-n);
-		n = Mfl;
+	int n;
+
+	n = BLEN(b);
+	if(n < Mfl){
+		memset(b->wp, 0, Mfl-n);
+		b->wp += (Mfl-n);
 	}
-	write(ep->dfd, bout, n);
+	write(ep->dfd, b->rp, BLEN(b));
+	freeb(b);
 }
 
 int
@@ -331,7 +323,7 @@
 
 	csr8w(d, Cr, Te|Re);
 
-	epwrite = urlwrite;
-	epread = urlread;
+	epreceive = urlreceive;
+	eptransmit = urltransmit;
 	return 0;
 }