xref: /plan9/sys/src/9/port/devloopback.c (revision 2cca75a1b2b8c6083390679d69d5c50cf66d9a01)
159cc4ca5SDavid du Colombier #include	"u.h"
259cc4ca5SDavid du Colombier #include	"../port/lib.h"
359cc4ca5SDavid du Colombier #include	"mem.h"
459cc4ca5SDavid du Colombier #include	"dat.h"
559cc4ca5SDavid du Colombier #include	"fns.h"
659cc4ca5SDavid du Colombier #include	"../port/error.h"
759cc4ca5SDavid du Colombier 
859cc4ca5SDavid du Colombier typedef struct Link	Link;
959cc4ca5SDavid du Colombier typedef struct Loop	Loop;
1059cc4ca5SDavid du Colombier 
1159cc4ca5SDavid du Colombier struct Link
1259cc4ca5SDavid du Colombier {
1359cc4ca5SDavid du Colombier 	Lock;
1459cc4ca5SDavid du Colombier 
1559cc4ca5SDavid du Colombier 	int	ref;
1659cc4ca5SDavid du Colombier 
1759cc4ca5SDavid du Colombier 	long	packets;	/* total number of packets sent */
1859cc4ca5SDavid du Colombier 	long	bytes;		/* total number of bytes sent */
1959cc4ca5SDavid du Colombier 	int	indrop;		/* enable dropping on iq overflow */
2059cc4ca5SDavid du Colombier 	long	soverflows;	/* packets dropped because iq overflowed */
2159cc4ca5SDavid du Colombier 	long	droprate;	/* drop 1/droprate packets in tq */
2259cc4ca5SDavid du Colombier 	long	drops;		/* packets deliberately dropped */
2359cc4ca5SDavid du Colombier 
2480ee5cbfSDavid du Colombier 	vlong	delay0ns;	/* nanosec of delay in the link */
2559cc4ca5SDavid du Colombier 	long	delaynns;	/* nanosec of delay per byte */
2659cc4ca5SDavid du Colombier 
2759cc4ca5SDavid du Colombier 	Block	*tq;		/* transmission queue */
2859cc4ca5SDavid du Colombier 	Block	*tqtail;
2959cc4ca5SDavid du Colombier 	vlong	tout;		/* time the last packet in tq is really out */
3059cc4ca5SDavid du Colombier 	vlong	tin;		/* time the head packet in tq enters the remote side  */
3159cc4ca5SDavid du Colombier 
3259cc4ca5SDavid du Colombier 	long	limit;		/* queue buffering limit */
3359cc4ca5SDavid du Colombier 	Queue	*oq;		/* output queue from other side & packets in the link */
3459cc4ca5SDavid du Colombier 	Queue	*iq;
3559cc4ca5SDavid du Colombier 
369a747e4fSDavid du Colombier 	Timer	ci;		/* time to move packets from  next packet from oq */
3759cc4ca5SDavid du Colombier };
3859cc4ca5SDavid du Colombier 
3959cc4ca5SDavid du Colombier struct Loop
4059cc4ca5SDavid du Colombier {
4159cc4ca5SDavid du Colombier 	QLock;
4259cc4ca5SDavid du Colombier 	int	ref;
4359cc4ca5SDavid du Colombier 	int	minmtu;		/* smallest block transmittable */
4459cc4ca5SDavid du Colombier 	Loop	*next;
4559cc4ca5SDavid du Colombier 	ulong	path;
4659cc4ca5SDavid du Colombier 	Link	link[2];
4759cc4ca5SDavid du Colombier };
4859cc4ca5SDavid du Colombier 
4959cc4ca5SDavid du Colombier static struct
5059cc4ca5SDavid du Colombier {
5159cc4ca5SDavid du Colombier 	Lock;
5259cc4ca5SDavid du Colombier 	ulong	path;
5359cc4ca5SDavid du Colombier } loopbackalloc;
5459cc4ca5SDavid du Colombier 
5559cc4ca5SDavid du Colombier enum
5659cc4ca5SDavid du Colombier {
5759cc4ca5SDavid du Colombier 	Qtopdir=	1,		/* top level directory */
5859cc4ca5SDavid du Colombier 
5959cc4ca5SDavid du Colombier 	Qloopdir,			/* loopback* directory */
6059cc4ca5SDavid du Colombier 
6159cc4ca5SDavid du Colombier 	Qportdir,			/* directory each end of the loop */
6259cc4ca5SDavid du Colombier 	Qctl,
6359cc4ca5SDavid du Colombier 	Qstatus,
6459cc4ca5SDavid du Colombier 	Qstats,
6559cc4ca5SDavid du Colombier 	Qdata,
6659cc4ca5SDavid du Colombier 
6759cc4ca5SDavid du Colombier 	MaxQ,
6859cc4ca5SDavid du Colombier 
6980ee5cbfSDavid du Colombier 	Nloopbacks	= 5,
7059cc4ca5SDavid du Colombier 
7159cc4ca5SDavid du Colombier 	Statelen	= 23*1024,	/* status buffer size */
7259cc4ca5SDavid du Colombier 
7359cc4ca5SDavid du Colombier 	Tmsize		= 8,
74e288d156SDavid du Colombier 	Delayn 		= 10000,	/* default delays in ns */
7559cc4ca5SDavid du Colombier 	Delay0 		= 2500000,
7659cc4ca5SDavid du Colombier 
7759cc4ca5SDavid du Colombier 	Loopqlim	= 32*1024,	/* default size of queues */
7859cc4ca5SDavid du Colombier };
7959cc4ca5SDavid du Colombier 
8059cc4ca5SDavid du Colombier static Dirtab loopportdir[] =
8159cc4ca5SDavid du Colombier {
8259cc4ca5SDavid du Colombier 	"ctl",		{Qctl},		0,			0222,
8359cc4ca5SDavid du Colombier 	"status",	{Qstatus},	0,			0444,
8459cc4ca5SDavid du Colombier 	"stats",	{Qstats},	0,			0444,
8559cc4ca5SDavid du Colombier 	"data",		{Qdata},	0,			0666,
8659cc4ca5SDavid du Colombier };
8759cc4ca5SDavid du Colombier static Dirtab loopdirs[MaxQ];
8859cc4ca5SDavid du Colombier 
8959cc4ca5SDavid du Colombier static Loop	loopbacks[Nloopbacks];
9059cc4ca5SDavid du Colombier 
919a747e4fSDavid du Colombier #define TYPE(x) 	(((ulong)(x))&0xff)
929a747e4fSDavid du Colombier #define ID(x) 		(((ulong)(x))>>8)
939a747e4fSDavid du Colombier #define QID(x,y) 	((((ulong)(x))<<8)|((ulong)(y)))
9459cc4ca5SDavid du Colombier 
9559cc4ca5SDavid du Colombier static void	looper(Loop *lb);
9659cc4ca5SDavid du Colombier static long	loopoput(Loop *lb, Link *link, Block *bp);
9759cc4ca5SDavid du Colombier static void	ptime(uchar *p, vlong t);
9859cc4ca5SDavid du Colombier static vlong	gtime(uchar *p);
9959cc4ca5SDavid du Colombier static void	closelink(Link *link, int dofree);
10059cc4ca5SDavid du Colombier static void	pushlink(Link *link, vlong now);
10159cc4ca5SDavid du Colombier static void	freelb(Loop *lb);
1029a747e4fSDavid du Colombier static void	linkintr(Ureg*, Timer *ci);
10359cc4ca5SDavid du Colombier 
10459cc4ca5SDavid du Colombier static void
loopbackinit(void)10559cc4ca5SDavid du Colombier loopbackinit(void)
10659cc4ca5SDavid du Colombier {
10759cc4ca5SDavid du Colombier 	int i;
10859cc4ca5SDavid du Colombier 
10959cc4ca5SDavid du Colombier 	for(i = 0; i < Nloopbacks; i++)
11059cc4ca5SDavid du Colombier 		loopbacks[i].path = i;
11159cc4ca5SDavid du Colombier 
11259cc4ca5SDavid du Colombier 	/* invert directory tables for non-directory entries */
11359cc4ca5SDavid du Colombier 	for(i=0; i<nelem(loopportdir); i++)
11459cc4ca5SDavid du Colombier 		loopdirs[loopportdir[i].qid.path] = loopportdir[i];
11559cc4ca5SDavid du Colombier }
11659cc4ca5SDavid du Colombier 
11759cc4ca5SDavid du Colombier static Chan*
loopbackattach(char * spec)11859cc4ca5SDavid du Colombier loopbackattach(char *spec)
11959cc4ca5SDavid du Colombier {
1209a747e4fSDavid du Colombier 	Loop *volatile lb;
12159cc4ca5SDavid du Colombier 	Queue *q;
12259cc4ca5SDavid du Colombier 	Chan *c;
12359cc4ca5SDavid du Colombier 	int chan;
12459cc4ca5SDavid du Colombier 	int dev;
12559cc4ca5SDavid du Colombier 
12659cc4ca5SDavid du Colombier 	dev = 0;
12759cc4ca5SDavid du Colombier 	if(spec != nil){
12859cc4ca5SDavid du Colombier 		dev = atoi(spec);
12959cc4ca5SDavid du Colombier 		if(dev >= Nloopbacks)
13059cc4ca5SDavid du Colombier 			error(Ebadspec);
13159cc4ca5SDavid du Colombier 	}
13259cc4ca5SDavid du Colombier 
13359cc4ca5SDavid du Colombier 	c = devattach('X', spec);
13459cc4ca5SDavid du Colombier 	lb = &loopbacks[dev];
13559cc4ca5SDavid du Colombier 
13659cc4ca5SDavid du Colombier 	qlock(lb);
13759cc4ca5SDavid du Colombier 	if(waserror()){
13859cc4ca5SDavid du Colombier 		lb->ref--;
13959cc4ca5SDavid du Colombier 		qunlock(lb);
14059cc4ca5SDavid du Colombier 		nexterror();
14159cc4ca5SDavid du Colombier 	}
14259cc4ca5SDavid du Colombier 
14359cc4ca5SDavid du Colombier 	lb->ref++;
14459cc4ca5SDavid du Colombier 	if(lb->ref == 1){
14559cc4ca5SDavid du Colombier 		for(chan = 0; chan < 2; chan++){
146*2cca75a1SDavid du Colombier 			lb->link[chan].ci.mode = Trelative;
14759cc4ca5SDavid du Colombier 			lb->link[chan].ci.a = &lb->link[chan];
14859cc4ca5SDavid du Colombier 			lb->link[chan].ci.f = linkintr;
14959cc4ca5SDavid du Colombier 			lb->link[chan].limit = Loopqlim;
15059cc4ca5SDavid du Colombier 			q = qopen(lb->link[chan].limit, 0, 0, 0);
15159cc4ca5SDavid du Colombier 			lb->link[chan].iq = q;
15259cc4ca5SDavid du Colombier 			if(q == nil){
15359cc4ca5SDavid du Colombier 				freelb(lb);
15459cc4ca5SDavid du Colombier 				exhausted("memory");
15559cc4ca5SDavid du Colombier 			}
15659cc4ca5SDavid du Colombier 			q = qopen(lb->link[chan].limit, 0, 0, 0);
15759cc4ca5SDavid du Colombier 			lb->link[chan].oq = q;
15859cc4ca5SDavid du Colombier 			if(q == nil){
15959cc4ca5SDavid du Colombier 				freelb(lb);
16059cc4ca5SDavid du Colombier 				exhausted("memory");
16159cc4ca5SDavid du Colombier 			}
16259cc4ca5SDavid du Colombier 			lb->link[chan].indrop = 1;
16359cc4ca5SDavid du Colombier 
16459cc4ca5SDavid du Colombier 			lb->link[chan].delaynns = Delayn;
16559cc4ca5SDavid du Colombier 			lb->link[chan].delay0ns = Delay0;
16659cc4ca5SDavid du Colombier 		}
16759cc4ca5SDavid du Colombier 	}
16859cc4ca5SDavid du Colombier 	poperror();
16959cc4ca5SDavid du Colombier 	qunlock(lb);
17059cc4ca5SDavid du Colombier 
1719a747e4fSDavid du Colombier 	mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR);
17259cc4ca5SDavid du Colombier 	c->aux = lb;
17380ee5cbfSDavid du Colombier 	c->dev = dev;
17459cc4ca5SDavid du Colombier 	return c;
17559cc4ca5SDavid du Colombier }
17659cc4ca5SDavid du Colombier 
17759cc4ca5SDavid du Colombier static int
loopbackgen(Chan * c,char *,Dirtab *,int,int i,Dir * dp)1789a747e4fSDavid du Colombier loopbackgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
17959cc4ca5SDavid du Colombier {
18059cc4ca5SDavid du Colombier 	Dirtab *tab;
18159cc4ca5SDavid du Colombier 	int len, type;
1829a747e4fSDavid du Colombier 	Qid qid;
18359cc4ca5SDavid du Colombier 
18459cc4ca5SDavid du Colombier 	type = TYPE(c->qid.path);
18559cc4ca5SDavid du Colombier 	if(i == DEVDOTDOT){
18659cc4ca5SDavid du Colombier 		switch(type){
18759cc4ca5SDavid du Colombier 		case Qtopdir:
18859cc4ca5SDavid du Colombier 		case Qloopdir:
1899a747e4fSDavid du Colombier 			snprint(up->genbuf, sizeof(up->genbuf), "#X%ld", c->dev);
1909a747e4fSDavid du Colombier 			mkqid(&qid, QID(0, Qtopdir), 0, QTDIR);
1919a747e4fSDavid du Colombier 			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
19259cc4ca5SDavid du Colombier 			break;
19359cc4ca5SDavid du Colombier 		case Qportdir:
1949a747e4fSDavid du Colombier 			snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev);
1959a747e4fSDavid du Colombier 			mkqid(&qid, QID(0, Qloopdir), 0, QTDIR);
1969a747e4fSDavid du Colombier 			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
19759cc4ca5SDavid du Colombier 			break;
19859cc4ca5SDavid du Colombier 		default:
1999a747e4fSDavid du Colombier 			panic("loopbackgen %llux", c->qid.path);
20059cc4ca5SDavid du Colombier 		}
20159cc4ca5SDavid du Colombier 		return 1;
20259cc4ca5SDavid du Colombier 	}
20359cc4ca5SDavid du Colombier 
20459cc4ca5SDavid du Colombier 	switch(type){
20559cc4ca5SDavid du Colombier 	case Qtopdir:
20659cc4ca5SDavid du Colombier 		if(i != 0)
20759cc4ca5SDavid du Colombier 			return -1;
2089a747e4fSDavid du Colombier 		snprint(up->genbuf, sizeof(up->genbuf), "loopback%ld", c->dev);
2099a747e4fSDavid du Colombier 		mkqid(&qid, QID(0, Qloopdir), 0, QTDIR);
2109a747e4fSDavid du Colombier 		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
21159cc4ca5SDavid du Colombier 		return 1;
21259cc4ca5SDavid du Colombier 	case Qloopdir:
21359cc4ca5SDavid du Colombier 		if(i >= 2)
21459cc4ca5SDavid du Colombier 			return -1;
2159a747e4fSDavid du Colombier 		snprint(up->genbuf, sizeof(up->genbuf), "%d", i);
2169a747e4fSDavid du Colombier 		mkqid(&qid, QID(i, QID(0, Qportdir)), 0, QTDIR);
2179a747e4fSDavid du Colombier 		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
21859cc4ca5SDavid du Colombier 		return 1;
21959cc4ca5SDavid du Colombier 	case Qportdir:
22059cc4ca5SDavid du Colombier 		if(i >= nelem(loopportdir))
22159cc4ca5SDavid du Colombier 			return -1;
22259cc4ca5SDavid du Colombier 		tab = &loopportdir[i];
2239a747e4fSDavid du Colombier 		mkqid(&qid, QID(ID(c->qid.path), tab->qid.path), 0, QTFILE);
2249a747e4fSDavid du Colombier 		devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp);
22559cc4ca5SDavid du Colombier 		return 1;
22659cc4ca5SDavid du Colombier 	default:
22759cc4ca5SDavid du Colombier 		/* non directory entries end up here; must be in lowest level */
2289a747e4fSDavid du Colombier 		if(c->qid.type & QTDIR)
22959cc4ca5SDavid du Colombier 			panic("loopbackgen: unexpected directory");
23059cc4ca5SDavid du Colombier 		if(i != 0)
23159cc4ca5SDavid du Colombier 			return -1;
23259cc4ca5SDavid du Colombier 		tab = &loopdirs[type];
23359cc4ca5SDavid du Colombier 		if(tab == nil)
23459cc4ca5SDavid du Colombier 			panic("loopbackgen: unknown type: %d", type);
23559cc4ca5SDavid du Colombier 		len = tab->length;
23659cc4ca5SDavid du Colombier 		devdir(c, c->qid, tab->name, len, eve, tab->perm, dp);
23759cc4ca5SDavid du Colombier 		return 1;
23859cc4ca5SDavid du Colombier 	}
23959cc4ca5SDavid du Colombier }
24059cc4ca5SDavid du Colombier 
24159cc4ca5SDavid du Colombier 
2429a747e4fSDavid du Colombier static Walkqid*
loopbackwalk(Chan * c,Chan * nc,char ** name,int nname)2439a747e4fSDavid du Colombier loopbackwalk(Chan *c, Chan *nc, char **name, int nname)
24459cc4ca5SDavid du Colombier {
2459a747e4fSDavid du Colombier 	Walkqid *wq;
2469a747e4fSDavid du Colombier 	Loop *lb;
2479a747e4fSDavid du Colombier 
2489a747e4fSDavid du Colombier 	wq = devwalk(c, nc, name, nname, nil, 0, loopbackgen);
2499a747e4fSDavid du Colombier 	if(wq != nil && wq->clone != nil && wq->clone != c){
2509a747e4fSDavid du Colombier 		lb = c->aux;
2519a747e4fSDavid du Colombier 		qlock(lb);
2529a747e4fSDavid du Colombier 		lb->ref++;
2539a747e4fSDavid du Colombier 		if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata)
2549a747e4fSDavid du Colombier 			lb->link[ID(c->qid.path)].ref++;
2559a747e4fSDavid du Colombier 		qunlock(lb);
2569a747e4fSDavid du Colombier 	}
2579a747e4fSDavid du Colombier 	return wq;
25859cc4ca5SDavid du Colombier }
25959cc4ca5SDavid du Colombier 
2609a747e4fSDavid du Colombier static int
loopbackstat(Chan * c,uchar * db,int n)2619a747e4fSDavid du Colombier loopbackstat(Chan *c, uchar *db, int n)
26259cc4ca5SDavid du Colombier {
2639a747e4fSDavid du Colombier 	return devstat(c, db, n, nil, 0, loopbackgen);
26459cc4ca5SDavid du Colombier }
26559cc4ca5SDavid du Colombier 
26659cc4ca5SDavid du Colombier /*
26759cc4ca5SDavid du Colombier  *  if the stream doesn't exist, create it
26859cc4ca5SDavid du Colombier  */
26959cc4ca5SDavid du Colombier static Chan*
loopbackopen(Chan * c,int omode)27059cc4ca5SDavid du Colombier loopbackopen(Chan *c, int omode)
27159cc4ca5SDavid du Colombier {
27259cc4ca5SDavid du Colombier 	Loop *lb;
27359cc4ca5SDavid du Colombier 
2749a747e4fSDavid du Colombier 	if(c->qid.type & QTDIR){
27559cc4ca5SDavid du Colombier 		if(omode != OREAD)
27659cc4ca5SDavid du Colombier 			error(Ebadarg);
27759cc4ca5SDavid du Colombier 		c->mode = omode;
27859cc4ca5SDavid du Colombier 		c->flag |= COPEN;
27959cc4ca5SDavid du Colombier 		c->offset = 0;
28059cc4ca5SDavid du Colombier 		return c;
28159cc4ca5SDavid du Colombier 	}
28259cc4ca5SDavid du Colombier 
28359cc4ca5SDavid du Colombier 	lb = c->aux;
28459cc4ca5SDavid du Colombier 	qlock(lb);
28580ee5cbfSDavid du Colombier 	if(TYPE(c->qid.path) == Qdata){
28680ee5cbfSDavid du Colombier 		if(lb->link[ID(c->qid.path)].ref){
28780ee5cbfSDavid du Colombier 			qunlock(lb);
28880ee5cbfSDavid du Colombier 			error(Einuse);
28980ee5cbfSDavid du Colombier 		}
29059cc4ca5SDavid du Colombier 		lb->link[ID(c->qid.path)].ref++;
29180ee5cbfSDavid du Colombier 	}
29259cc4ca5SDavid du Colombier 	qunlock(lb);
29359cc4ca5SDavid du Colombier 
29459cc4ca5SDavid du Colombier 	c->mode = openmode(omode);
29559cc4ca5SDavid du Colombier 	c->flag |= COPEN;
29659cc4ca5SDavid du Colombier 	c->offset = 0;
2979a747e4fSDavid du Colombier 	c->iounit = qiomaxatomic;
29859cc4ca5SDavid du Colombier 	return c;
29959cc4ca5SDavid du Colombier }
30059cc4ca5SDavid du Colombier 
30159cc4ca5SDavid du Colombier static void
loopbackclose(Chan * c)30259cc4ca5SDavid du Colombier loopbackclose(Chan *c)
30359cc4ca5SDavid du Colombier {
30459cc4ca5SDavid du Colombier 	Loop *lb;
30559cc4ca5SDavid du Colombier 	int ref, chan;
30659cc4ca5SDavid du Colombier 
30759cc4ca5SDavid du Colombier 	lb = c->aux;
30859cc4ca5SDavid du Colombier 
30959cc4ca5SDavid du Colombier 	qlock(lb);
31059cc4ca5SDavid du Colombier 
31159cc4ca5SDavid du Colombier 	/*
31259cc4ca5SDavid du Colombier 	 * closing either side hangs up the stream
31359cc4ca5SDavid du Colombier 	 */
31459cc4ca5SDavid du Colombier 	if((c->flag & COPEN) && TYPE(c->qid.path) == Qdata){
31559cc4ca5SDavid du Colombier 		chan = ID(c->qid.path);
31659cc4ca5SDavid du Colombier 		if(--lb->link[chan].ref == 0){
31759cc4ca5SDavid du Colombier 			qhangup(lb->link[chan ^ 1].oq, nil);
31859cc4ca5SDavid du Colombier 			looper(lb);
31959cc4ca5SDavid du Colombier 		}
32059cc4ca5SDavid du Colombier 	}
32159cc4ca5SDavid du Colombier 
32259cc4ca5SDavid du Colombier 
32359cc4ca5SDavid du Colombier 	/*
32459cc4ca5SDavid du Colombier 	 *  if both sides are closed, they are reusable
32559cc4ca5SDavid du Colombier 	 */
32659cc4ca5SDavid du Colombier 	if(lb->link[0].ref == 0 && lb->link[1].ref == 0){
32759cc4ca5SDavid du Colombier 		for(chan = 0; chan < 2; chan++){
32859cc4ca5SDavid du Colombier 			closelink(&lb->link[chan], 0);
32959cc4ca5SDavid du Colombier 			qreopen(lb->link[chan].iq);
33059cc4ca5SDavid du Colombier 			qreopen(lb->link[chan].oq);
33159cc4ca5SDavid du Colombier 			qsetlimit(lb->link[chan].oq, lb->link[chan].limit);
33259cc4ca5SDavid du Colombier 			qsetlimit(lb->link[chan].iq, lb->link[chan].limit);
33359cc4ca5SDavid du Colombier 		}
33459cc4ca5SDavid du Colombier 	}
33559cc4ca5SDavid du Colombier 	ref = --lb->ref;
33659cc4ca5SDavid du Colombier 	if(ref == 0)
33759cc4ca5SDavid du Colombier 		freelb(lb);
33859cc4ca5SDavid du Colombier 	qunlock(lb);
33959cc4ca5SDavid du Colombier }
34059cc4ca5SDavid du Colombier 
34159cc4ca5SDavid du Colombier static void
freelb(Loop * lb)34259cc4ca5SDavid du Colombier freelb(Loop *lb)
34359cc4ca5SDavid du Colombier {
34459cc4ca5SDavid du Colombier 	int chan;
34559cc4ca5SDavid du Colombier 
34659cc4ca5SDavid du Colombier 	for(chan = 0; chan < 2; chan++)
34759cc4ca5SDavid du Colombier 		closelink(&lb->link[chan], 1);
34859cc4ca5SDavid du Colombier }
34959cc4ca5SDavid du Colombier 
35059cc4ca5SDavid du Colombier /*
35159cc4ca5SDavid du Colombier  * called with the Loop qlocked,
35259cc4ca5SDavid du Colombier  * so only pushlink can mess with the queues
35359cc4ca5SDavid du Colombier  */
35459cc4ca5SDavid du Colombier static void
closelink(Link * link,int dofree)35559cc4ca5SDavid du Colombier closelink(Link *link, int dofree)
35659cc4ca5SDavid du Colombier {
35759cc4ca5SDavid du Colombier 	Queue *iq, *oq;
35859cc4ca5SDavid du Colombier 	Block *bp;
35959cc4ca5SDavid du Colombier 
36059cc4ca5SDavid du Colombier 	ilock(link);
36159cc4ca5SDavid du Colombier 	iq = link->iq;
36259cc4ca5SDavid du Colombier 	oq = link->oq;
36359cc4ca5SDavid du Colombier 	bp = link->tq;
36459cc4ca5SDavid du Colombier 	link->tq = nil;
36559cc4ca5SDavid du Colombier 	link->tqtail = nil;
36659cc4ca5SDavid du Colombier 	link->tout = 0;
36759cc4ca5SDavid du Colombier 	link->tin = 0;
3689a747e4fSDavid du Colombier 	timerdel(&link->ci);
36959cc4ca5SDavid du Colombier 	iunlock(link);
37059cc4ca5SDavid du Colombier 	if(iq != nil){
37159cc4ca5SDavid du Colombier 		qclose(iq);
37259cc4ca5SDavid du Colombier 		if(dofree){
37359cc4ca5SDavid du Colombier 			ilock(link);
37459cc4ca5SDavid du Colombier 			free(iq);
37559cc4ca5SDavid du Colombier 			link->iq = nil;
37659cc4ca5SDavid du Colombier 			iunlock(link);
37759cc4ca5SDavid du Colombier 		}
37859cc4ca5SDavid du Colombier 	}
37959cc4ca5SDavid du Colombier 	if(oq != nil){
38059cc4ca5SDavid du Colombier 		qclose(oq);
38159cc4ca5SDavid du Colombier 		if(dofree){
38259cc4ca5SDavid du Colombier 			ilock(link);
38359cc4ca5SDavid du Colombier 			free(oq);
38459cc4ca5SDavid du Colombier 			link->oq = nil;
38559cc4ca5SDavid du Colombier 			iunlock(link);
38659cc4ca5SDavid du Colombier 		}
38759cc4ca5SDavid du Colombier 	}
38859cc4ca5SDavid du Colombier 	freeblist(bp);
38959cc4ca5SDavid du Colombier }
39059cc4ca5SDavid du Colombier 
39159cc4ca5SDavid du Colombier static long
loopbackread(Chan * c,void * va,long n,vlong offset)39259cc4ca5SDavid du Colombier loopbackread(Chan *c, void *va, long n, vlong offset)
39359cc4ca5SDavid du Colombier {
39459cc4ca5SDavid du Colombier 	Loop *lb;
39559cc4ca5SDavid du Colombier 	Link *link;
39659cc4ca5SDavid du Colombier 	char *buf;
39759cc4ca5SDavid du Colombier 	long rv;
39859cc4ca5SDavid du Colombier 
39959cc4ca5SDavid du Colombier 	lb = c->aux;
40059cc4ca5SDavid du Colombier 	switch(TYPE(c->qid.path)){
40159cc4ca5SDavid du Colombier 	default:
40259cc4ca5SDavid du Colombier 		error(Eperm);
40359cc4ca5SDavid du Colombier 		return -1;	/* not reached */
40459cc4ca5SDavid du Colombier 	case Qtopdir:
40559cc4ca5SDavid du Colombier 	case Qloopdir:
40659cc4ca5SDavid du Colombier 	case Qportdir:
40759cc4ca5SDavid du Colombier 		return devdirread(c, va, n, nil, 0, loopbackgen);
40859cc4ca5SDavid du Colombier 	case Qdata:
40959cc4ca5SDavid du Colombier 		return qread(lb->link[ID(c->qid.path)].iq, va, n);
41059cc4ca5SDavid du Colombier 	case Qstatus:
41159cc4ca5SDavid du Colombier 		link = &lb->link[ID(c->qid.path)];
41259cc4ca5SDavid du Colombier 		buf = smalloc(Statelen);
41380ee5cbfSDavid du Colombier 		rv = snprint(buf, Statelen, "delay %lld %ld\n", link->delay0ns, link->delaynns);
41459cc4ca5SDavid du Colombier 		rv += snprint(buf+rv, Statelen-rv, "limit %ld\n", link->limit);
41559cc4ca5SDavid du Colombier 		rv += snprint(buf+rv, Statelen-rv, "indrop %d\n", link->indrop);
41659cc4ca5SDavid du Colombier 		snprint(buf+rv, Statelen-rv, "droprate %ld\n", link->droprate);
41759cc4ca5SDavid du Colombier 		rv = readstr(offset, va, n, buf);
41859cc4ca5SDavid du Colombier 		free(buf);
41959cc4ca5SDavid du Colombier 		break;
42059cc4ca5SDavid du Colombier 	case Qstats:
42159cc4ca5SDavid du Colombier 		link = &lb->link[ID(c->qid.path)];
42259cc4ca5SDavid du Colombier 		buf = smalloc(Statelen);
42359cc4ca5SDavid du Colombier 		rv = snprint(buf, Statelen, "packets: %ld\n", link->packets);
42459cc4ca5SDavid du Colombier 		rv += snprint(buf+rv, Statelen-rv, "bytes: %ld\n", link->bytes);
42559cc4ca5SDavid du Colombier 		rv += snprint(buf+rv, Statelen-rv, "dropped: %ld\n", link->drops);
42659cc4ca5SDavid du Colombier 		snprint(buf+rv, Statelen-rv, "soft overflows: %ld\n", link->soverflows);
42759cc4ca5SDavid du Colombier 		rv = readstr(offset, va, n, buf);
42859cc4ca5SDavid du Colombier 		free(buf);
42959cc4ca5SDavid du Colombier 		break;
43059cc4ca5SDavid du Colombier 	}
43159cc4ca5SDavid du Colombier 	return rv;
43259cc4ca5SDavid du Colombier }
43359cc4ca5SDavid du Colombier 
43459cc4ca5SDavid du Colombier static Block*
loopbackbread(Chan * c,long n,ulong offset)43559cc4ca5SDavid du Colombier loopbackbread(Chan *c, long n, ulong offset)
43659cc4ca5SDavid du Colombier {
43759cc4ca5SDavid du Colombier 	Loop *lb;
43859cc4ca5SDavid du Colombier 
43959cc4ca5SDavid du Colombier 	lb = c->aux;
44059cc4ca5SDavid du Colombier 	if(TYPE(c->qid.path) == Qdata)
44159cc4ca5SDavid du Colombier 		return qbread(lb->link[ID(c->qid.path)].iq, n);
44259cc4ca5SDavid du Colombier 
44359cc4ca5SDavid du Colombier 	return devbread(c, n, offset);
44459cc4ca5SDavid du Colombier }
44559cc4ca5SDavid du Colombier 
44659cc4ca5SDavid du Colombier static long
loopbackbwrite(Chan * c,Block * bp,ulong off)44759cc4ca5SDavid du Colombier loopbackbwrite(Chan *c, Block *bp, ulong off)
44859cc4ca5SDavid du Colombier {
44959cc4ca5SDavid du Colombier 	Loop *lb;
45059cc4ca5SDavid du Colombier 
45159cc4ca5SDavid du Colombier 	lb = c->aux;
45259cc4ca5SDavid du Colombier 	if(TYPE(c->qid.path) == Qdata)
45359cc4ca5SDavid du Colombier 		return loopoput(lb, &lb->link[ID(c->qid.path) ^ 1], bp);
45459cc4ca5SDavid du Colombier 	return devbwrite(c, bp, off);
45559cc4ca5SDavid du Colombier }
45659cc4ca5SDavid du Colombier 
45759cc4ca5SDavid du Colombier static long
loopbackwrite(Chan * c,void * va,long n,vlong off)45859cc4ca5SDavid du Colombier loopbackwrite(Chan *c, void *va, long n, vlong off)
45959cc4ca5SDavid du Colombier {
46059cc4ca5SDavid du Colombier 	Loop *lb;
46159cc4ca5SDavid du Colombier 	Link *link;
4629a747e4fSDavid du Colombier 	Cmdbuf *volatile cb;
4639a747e4fSDavid du Colombier 	Block *volatile bp;
46480ee5cbfSDavid du Colombier 	vlong d0, d0ns;
46580ee5cbfSDavid du Colombier 	long dn, dnns;
46659cc4ca5SDavid du Colombier 
46759cc4ca5SDavid du Colombier 	switch(TYPE(c->qid.path)){
46859cc4ca5SDavid du Colombier 	case Qdata:
46959cc4ca5SDavid du Colombier 		bp = allocb(n);
47059cc4ca5SDavid du Colombier 		if(waserror()){
47159cc4ca5SDavid du Colombier 			freeb(bp);
47259cc4ca5SDavid du Colombier 			nexterror();
47359cc4ca5SDavid du Colombier 		}
47459cc4ca5SDavid du Colombier 		memmove(bp->wp, va, n);
47559cc4ca5SDavid du Colombier 		poperror();
47659cc4ca5SDavid du Colombier 		bp->wp += n;
47759cc4ca5SDavid du Colombier 		return loopbackbwrite(c, bp, off);
47859cc4ca5SDavid du Colombier 	case Qctl:
47959cc4ca5SDavid du Colombier 		lb = c->aux;
48059cc4ca5SDavid du Colombier 		link = &lb->link[ID(c->qid.path)];
48159cc4ca5SDavid du Colombier 		cb = parsecmd(va, n);
4829a747e4fSDavid du Colombier 		if(waserror()){
4839a747e4fSDavid du Colombier 			free(cb);
4849a747e4fSDavid du Colombier 			nexterror();
4859a747e4fSDavid du Colombier 		}
48659cc4ca5SDavid du Colombier 		if(cb->nf < 1)
48759cc4ca5SDavid du Colombier 			error("short control request");
48859cc4ca5SDavid du Colombier 		if(strcmp(cb->f[0], "delay") == 0){
48959cc4ca5SDavid du Colombier 			if(cb->nf != 3)
49059cc4ca5SDavid du Colombier 				error("usage: delay latency bytedelay");
49180ee5cbfSDavid du Colombier 			d0ns = strtoll(cb->f[1], nil, 10);
49259cc4ca5SDavid du Colombier 			dnns = strtol(cb->f[2], nil, 10);
49359cc4ca5SDavid du Colombier 
49459cc4ca5SDavid du Colombier 			/*
49559cc4ca5SDavid du Colombier 			 * it takes about 20000 cycles on a pentium ii
49659cc4ca5SDavid du Colombier 			 * to run pushlink; perhaps this should be accounted.
49759cc4ca5SDavid du Colombier 			 */
49859cc4ca5SDavid du Colombier 
49959cc4ca5SDavid du Colombier 			ilock(link);
50059cc4ca5SDavid du Colombier 			link->delay0ns = d0ns;
50159cc4ca5SDavid du Colombier 			link->delaynns = dnns;
50259cc4ca5SDavid du Colombier 			iunlock(link);
50359cc4ca5SDavid du Colombier 		}else if(strcmp(cb->f[0], "indrop") == 0){
50459cc4ca5SDavid du Colombier 			if(cb->nf != 2)
50559cc4ca5SDavid du Colombier 				error("usage: indrop [01]");
50659cc4ca5SDavid du Colombier 			ilock(link);
50759cc4ca5SDavid du Colombier 			link->indrop = strtol(cb->f[1], nil, 0) != 0;
50859cc4ca5SDavid du Colombier 			iunlock(link);
50959cc4ca5SDavid du Colombier 		}else if(strcmp(cb->f[0], "droprate") == 0){
51059cc4ca5SDavid du Colombier 			if(cb->nf != 2)
51159cc4ca5SDavid du Colombier 				error("usage: droprate ofn");
51259cc4ca5SDavid du Colombier 			ilock(link);
51359cc4ca5SDavid du Colombier 			link->droprate = strtol(cb->f[1], nil, 0);
51459cc4ca5SDavid du Colombier 			iunlock(link);
51559cc4ca5SDavid du Colombier 		}else if(strcmp(cb->f[0], "limit") == 0){
51659cc4ca5SDavid du Colombier 			if(cb->nf != 2)
51780ee5cbfSDavid du Colombier 				error("usage: limit maxqsize");
51859cc4ca5SDavid du Colombier 			ilock(link);
51959cc4ca5SDavid du Colombier 			link->limit = strtol(cb->f[1], nil, 0);
52059cc4ca5SDavid du Colombier 			qsetlimit(link->oq, link->limit);
52159cc4ca5SDavid du Colombier 			qsetlimit(link->iq, link->limit);
52259cc4ca5SDavid du Colombier 			iunlock(link);
52359cc4ca5SDavid du Colombier 		}else if(strcmp(cb->f[0], "reset") == 0){
52459cc4ca5SDavid du Colombier 			if(cb->nf != 1)
52559cc4ca5SDavid du Colombier 				error("usage: reset");
52659cc4ca5SDavid du Colombier 			ilock(link);
52759cc4ca5SDavid du Colombier 			link->packets = 0;
52859cc4ca5SDavid du Colombier 			link->bytes = 0;
52959cc4ca5SDavid du Colombier 			link->indrop = 0;
53059cc4ca5SDavid du Colombier 			link->soverflows = 0;
53159cc4ca5SDavid du Colombier 			link->drops = 0;
53259cc4ca5SDavid du Colombier 			iunlock(link);
53359cc4ca5SDavid du Colombier 		}else
53459cc4ca5SDavid du Colombier 			error("unknown control request");
5359a747e4fSDavid du Colombier 		poperror();
5369a747e4fSDavid du Colombier 		free(cb);
53759cc4ca5SDavid du Colombier 		break;
53859cc4ca5SDavid du Colombier 	default:
53959cc4ca5SDavid du Colombier 		error(Eperm);
54059cc4ca5SDavid du Colombier 	}
54159cc4ca5SDavid du Colombier 
54259cc4ca5SDavid du Colombier 	return n;
54359cc4ca5SDavid du Colombier }
54459cc4ca5SDavid du Colombier 
54559cc4ca5SDavid du Colombier static long
loopoput(Loop * lb,Link * link,Block * volatile bp)5469a747e4fSDavid du Colombier loopoput(Loop *lb, Link *link, Block *volatile bp)
54759cc4ca5SDavid du Colombier {
54859cc4ca5SDavid du Colombier 	long n;
54959cc4ca5SDavid du Colombier 
55059cc4ca5SDavid du Colombier 	n = BLEN(bp);
55159cc4ca5SDavid du Colombier 
55259cc4ca5SDavid du Colombier 	/* make it a single block with space for the loopback timing header */
5539a747e4fSDavid du Colombier 	if(waserror()){
5549a747e4fSDavid du Colombier 		freeb(bp);
5559a747e4fSDavid du Colombier 		nexterror();
5569a747e4fSDavid du Colombier 	}
55759cc4ca5SDavid du Colombier 	bp = padblock(bp, Tmsize);
55859cc4ca5SDavid du Colombier 	if(bp->next)
55959cc4ca5SDavid du Colombier 		bp = concatblock(bp);
56059cc4ca5SDavid du Colombier 	if(BLEN(bp) < lb->minmtu)
56159cc4ca5SDavid du Colombier 		bp = adjustblock(bp, lb->minmtu);
5629a747e4fSDavid du Colombier 	poperror();
563e288d156SDavid du Colombier 	ptime(bp->rp, todget(nil));
56459cc4ca5SDavid du Colombier 
56559cc4ca5SDavid du Colombier 	link->packets++;
56659cc4ca5SDavid du Colombier 	link->bytes += n;
56759cc4ca5SDavid du Colombier 
56859cc4ca5SDavid du Colombier 	qbwrite(link->oq, bp);
56959cc4ca5SDavid du Colombier 
57059cc4ca5SDavid du Colombier 	looper(lb);
57159cc4ca5SDavid du Colombier 	return n;
57259cc4ca5SDavid du Colombier }
57359cc4ca5SDavid du Colombier 
57459cc4ca5SDavid du Colombier static void
looper(Loop * lb)57559cc4ca5SDavid du Colombier looper(Loop *lb)
57659cc4ca5SDavid du Colombier {
57759cc4ca5SDavid du Colombier 	vlong t;
57859cc4ca5SDavid du Colombier 	int chan;
57959cc4ca5SDavid du Colombier 
580e288d156SDavid du Colombier 	t = todget(nil);
58159cc4ca5SDavid du Colombier 	for(chan = 0; chan < 2; chan++)
58259cc4ca5SDavid du Colombier 		pushlink(&lb->link[chan], t);
58359cc4ca5SDavid du Colombier }
58459cc4ca5SDavid du Colombier 
58559cc4ca5SDavid du Colombier static void
linkintr(Ureg *,Timer * ci)5869a747e4fSDavid du Colombier linkintr(Ureg*, Timer *ci)
58759cc4ca5SDavid du Colombier {
58859cc4ca5SDavid du Colombier 	Link *link;
58959cc4ca5SDavid du Colombier 
59059cc4ca5SDavid du Colombier 	link = ci->a;
591e288d156SDavid du Colombier 	pushlink(link, ci->ns);
59259cc4ca5SDavid du Colombier }
59359cc4ca5SDavid du Colombier 
59459cc4ca5SDavid du Colombier /*
59559cc4ca5SDavid du Colombier  * move blocks between queues if they are ready.
59659cc4ca5SDavid du Colombier  * schedule an interrupt for the next interesting time.
59759cc4ca5SDavid du Colombier  *
59859cc4ca5SDavid du Colombier  * must be called with the link ilocked.
59959cc4ca5SDavid du Colombier  */
60059cc4ca5SDavid du Colombier static void
pushlink(Link * link,vlong now)60159cc4ca5SDavid du Colombier pushlink(Link *link, vlong now)
60259cc4ca5SDavid du Colombier {
60359cc4ca5SDavid du Colombier 	Block *bp;
60459cc4ca5SDavid du Colombier 	vlong tout, tin;
60559cc4ca5SDavid du Colombier 
60659cc4ca5SDavid du Colombier 	/*
60759cc4ca5SDavid du Colombier 	 * put another block in the link queue
60859cc4ca5SDavid du Colombier 	 */
60959cc4ca5SDavid du Colombier 	ilock(link);
61059cc4ca5SDavid du Colombier 	if(link->iq == nil || link->oq == nil){
61159cc4ca5SDavid du Colombier 		iunlock(link);
61259cc4ca5SDavid du Colombier 		return;
61359cc4ca5SDavid du Colombier 
61459cc4ca5SDavid du Colombier 	}
6159a747e4fSDavid du Colombier 	timerdel(&link->ci);
61659cc4ca5SDavid du Colombier 
61759cc4ca5SDavid du Colombier 	/*
61859cc4ca5SDavid du Colombier 	 * put more blocks into the xmit queue
61959cc4ca5SDavid du Colombier 	 * use the time the last packet was supposed to go out
62059cc4ca5SDavid du Colombier 	 * as the start time for the next packet, rather than
62159cc4ca5SDavid du Colombier 	 * the current time.  this more closely models a network
62259cc4ca5SDavid du Colombier 	 * device which can queue multiple output packets.
62359cc4ca5SDavid du Colombier 	 */
62459cc4ca5SDavid du Colombier 	tout = link->tout;
62559cc4ca5SDavid du Colombier 	if(!tout)
62659cc4ca5SDavid du Colombier 		tout = now;
62759cc4ca5SDavid du Colombier 	while(tout <= now){
62859cc4ca5SDavid du Colombier 		bp = qget(link->oq);
62959cc4ca5SDavid du Colombier 		if(bp == nil){
63059cc4ca5SDavid du Colombier 			tout = 0;
63159cc4ca5SDavid du Colombier 			break;
63259cc4ca5SDavid du Colombier 		}
63359cc4ca5SDavid du Colombier 
63459cc4ca5SDavid du Colombier 		/*
63559cc4ca5SDavid du Colombier 		 * can't send the packet before it gets queued
63659cc4ca5SDavid du Colombier 		 */
63759cc4ca5SDavid du Colombier 		tin = gtime(bp->rp);
63859cc4ca5SDavid du Colombier 		if(tin > tout)
63959cc4ca5SDavid du Colombier 			tout = tin;
64059cc4ca5SDavid du Colombier 		tout = tout + (BLEN(bp) - Tmsize) * link->delayn;
64159cc4ca5SDavid du Colombier 
64259cc4ca5SDavid du Colombier 		/*
64359cc4ca5SDavid du Colombier 		 * drop packets
64459cc4ca5SDavid du Colombier 		 */
64559cc4ca5SDavid du Colombier 		if(link->droprate && nrand(link->droprate) == 0)
64659cc4ca5SDavid du Colombier 			link->drops++;
64759cc4ca5SDavid du Colombier 		else{
648e288d156SDavid du Colombier 			ptime(bp->rp, tout + link->delay0ns);
64959cc4ca5SDavid du Colombier 			if(link->tq == nil)
65059cc4ca5SDavid du Colombier 				link->tq = bp;
65159cc4ca5SDavid du Colombier 			else
65259cc4ca5SDavid du Colombier 				link->tqtail->next = bp;
65359cc4ca5SDavid du Colombier 			link->tqtail = bp;
65459cc4ca5SDavid du Colombier 		}
65559cc4ca5SDavid du Colombier 	}
65659cc4ca5SDavid du Colombier 
65759cc4ca5SDavid du Colombier 	/*
65859cc4ca5SDavid du Colombier 	 * record the next time a packet can be sent,
65959cc4ca5SDavid du Colombier 	 * but don't schedule an interrupt if none is waiting
66059cc4ca5SDavid du Colombier 	 */
66159cc4ca5SDavid du Colombier 	link->tout = tout;
66259cc4ca5SDavid du Colombier 	if(!qcanread(link->oq))
66359cc4ca5SDavid du Colombier 		tout = 0;
66459cc4ca5SDavid du Colombier 
66559cc4ca5SDavid du Colombier 	/*
66659cc4ca5SDavid du Colombier 	 * put more blocks into the receive queue
66759cc4ca5SDavid du Colombier 	 */
66859cc4ca5SDavid du Colombier 	tin = 0;
66959cc4ca5SDavid du Colombier 	while(bp = link->tq){
67059cc4ca5SDavid du Colombier 		tin = gtime(bp->rp);
67159cc4ca5SDavid du Colombier 		if(tin > now)
67259cc4ca5SDavid du Colombier 			break;
67359cc4ca5SDavid du Colombier 		bp->rp += Tmsize;
67459cc4ca5SDavid du Colombier 		link->tq = bp->next;
67559cc4ca5SDavid du Colombier 		bp->next = nil;
67659cc4ca5SDavid du Colombier 		if(!link->indrop)
67759cc4ca5SDavid du Colombier 			qpassnolim(link->iq, bp);
67859cc4ca5SDavid du Colombier 		else if(qpass(link->iq, bp) < 0)
67959cc4ca5SDavid du Colombier 			link->soverflows++;
68059cc4ca5SDavid du Colombier 		tin = 0;
68159cc4ca5SDavid du Colombier 	}
68259cc4ca5SDavid du Colombier 	if(bp == nil && qisclosed(link->oq) && !qcanread(link->oq) && !qisclosed(link->iq))
68359cc4ca5SDavid du Colombier 		qhangup(link->iq, nil);
68459cc4ca5SDavid du Colombier 	link->tin = tin;
68559cc4ca5SDavid du Colombier 	if(!tin || tin > tout && tout)
68659cc4ca5SDavid du Colombier 		tin = tout;
68759cc4ca5SDavid du Colombier 
688*2cca75a1SDavid du Colombier 	link->ci.ns = tin - now;
68959cc4ca5SDavid du Colombier 	if(tin){
69059cc4ca5SDavid du Colombier 		if(tin < now)
69159cc4ca5SDavid du Colombier 			panic("loopback unfinished business");
6929a747e4fSDavid du Colombier 		timeradd(&link->ci);
69359cc4ca5SDavid du Colombier 	}
69459cc4ca5SDavid du Colombier 	iunlock(link);
69559cc4ca5SDavid du Colombier }
69659cc4ca5SDavid du Colombier 
69759cc4ca5SDavid du Colombier static void
ptime(uchar * p,vlong t)69859cc4ca5SDavid du Colombier ptime(uchar *p, vlong t)
69959cc4ca5SDavid du Colombier {
70059cc4ca5SDavid du Colombier 	ulong tt;
70159cc4ca5SDavid du Colombier 
70259cc4ca5SDavid du Colombier 	tt = t >> 32;
70359cc4ca5SDavid du Colombier 	p[0] = tt >> 24;
70459cc4ca5SDavid du Colombier 	p[1] = tt >> 16;
70559cc4ca5SDavid du Colombier 	p[2] = tt >> 8;
70659cc4ca5SDavid du Colombier 	p[3] = tt;
70759cc4ca5SDavid du Colombier 	tt = t;
70859cc4ca5SDavid du Colombier 	p[4] = tt >> 24;
70959cc4ca5SDavid du Colombier 	p[5] = tt >> 16;
71059cc4ca5SDavid du Colombier 	p[6] = tt >> 8;
71159cc4ca5SDavid du Colombier 	p[7] = tt;
71259cc4ca5SDavid du Colombier }
71359cc4ca5SDavid du Colombier 
71459cc4ca5SDavid du Colombier static vlong
gtime(uchar * p)71559cc4ca5SDavid du Colombier gtime(uchar *p)
71659cc4ca5SDavid du Colombier {
71759cc4ca5SDavid du Colombier 	ulong t1, t2;
71859cc4ca5SDavid du Colombier 
71959cc4ca5SDavid du Colombier 	t1 = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
72059cc4ca5SDavid du Colombier 	t2 = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7];
72159cc4ca5SDavid du Colombier 	return ((vlong)t1 << 32) | t2;
72259cc4ca5SDavid du Colombier }
72359cc4ca5SDavid du Colombier 
72459cc4ca5SDavid du Colombier Dev loopbackdevtab = {
72559cc4ca5SDavid du Colombier 	'X',
72659cc4ca5SDavid du Colombier 	"loopback",
72759cc4ca5SDavid du Colombier 
72859cc4ca5SDavid du Colombier 	devreset,
72959cc4ca5SDavid du Colombier 	loopbackinit,
7309a747e4fSDavid du Colombier 	devshutdown,
73159cc4ca5SDavid du Colombier 	loopbackattach,
73259cc4ca5SDavid du Colombier 	loopbackwalk,
73359cc4ca5SDavid du Colombier 	loopbackstat,
73459cc4ca5SDavid du Colombier 	loopbackopen,
73559cc4ca5SDavid du Colombier 	devcreate,
73659cc4ca5SDavid du Colombier 	loopbackclose,
73759cc4ca5SDavid du Colombier 	loopbackread,
73859cc4ca5SDavid du Colombier 	loopbackbread,
73959cc4ca5SDavid du Colombier 	loopbackwrite,
74059cc4ca5SDavid du Colombier 	loopbackbwrite,
74159cc4ca5SDavid du Colombier 	devremove,
74259cc4ca5SDavid du Colombier 	devwstat,
74359cc4ca5SDavid du Colombier };
744