Browse Source

Plan 9 from Bell Labs 2003-12-12

David du Colombier 20 years ago
parent
commit
4f5976b270

+ 11 - 8
dist/replica/plan9.db

@@ -5,7 +5,7 @@
 29000/lib - 20000000775 sys sys 948037563 0
 29000/mkfile - 664 sys sys 948141302 46
 386 - 20000000775 sys sys 1010957353 0
-386/9load - 775 sys sys 1068156766 181788
+386/9load - 775 sys sys 1071175100 188380
 386/9loaddebug - 775 sys sys 1068385814 262014
 386/9loadlite - 775 sys sys 1066618055 124684
 386/9loadlitedebug - 775 sys sys 1068385814 184444
@@ -232,7 +232,7 @@
 386/bin/fossil/conf - 775 sys sys 1056364255 1497
 386/bin/fossil/flchk - 775 sys sys 1070288097 232862
 386/bin/fossil/flfmt - 775 sys sys 1070288098 230827
-386/bin/fossil/fossil - 775 sys sys 1070288099 345606
+386/bin/fossil/fossil - 775 sys sys 1071179084 345532
 386/bin/freq - 775 sys sys 1064598145 60197
 386/bin/fs - 20000000775 sys sys 954380769 0
 386/bin/fs/32vfs - 775 sys sys 1064598146 95907
@@ -4601,7 +4601,7 @@ sys/man/1/cleanname - 664 sys sys 944959674 580
 sys/man/1/cmp - 664 sys sys 944959673 1092
 sys/man/1/colors - 664 sys sys 954523212 1443
 sys/man/1/comm - 664 sys sys 944959675 665
-sys/man/1/con - 664 sys sys 1022049386 4259
+sys/man/1/con - 664 sys sys 1071156278 4318
 sys/man/1/cp - 664 sys sys 1015024738 1390
 sys/man/1/cpp - 664 sys sys 944959674 2105
 sys/man/1/cpu - 664 sys sys 1040673168 3226
@@ -6002,7 +6002,7 @@ sys/src/ape/lib/ap/plan9/9read.c - 664 sys sys 1014921986 169
 sys/src/ape/lib/ap/plan9/9readn.c - 664 sys sys 1070330880 221
 sys/src/ape/lib/ap/plan9/9wait.c - 664 sys sys 1014921986 1517
 sys/src/ape/lib/ap/plan9/9write.c - 664 sys sys 1014921986 171
-sys/src/ape/lib/ap/plan9/_buf.c - 664 sys sys 1048644332 9661
+sys/src/ape/lib/ap/plan9/_buf.c - 664 sys sys 1071178644 9850
 sys/src/ape/lib/ap/plan9/_dirconv.c - 664 sys sys 1014921985 1419
 sys/src/ape/lib/ap/plan9/_envsetup.c - 664 sys sys 1048644333 2198
 sys/src/ape/lib/ap/plan9/_errno.c - 664 sys sys 1014921985 3909
@@ -6423,7 +6423,7 @@ sys/src/boot/pc/dosboot.c - 664 sys sys 1032215912 11014
 sys/src/boot/pc/dosfs.h - 664 sys sys 1032215924 1467
 sys/src/boot/pc/eoffs - 664 sys sys 1015007950 0
 sys/src/boot/pc/error.h - 664 sys sys 1015007950 3081
-sys/src/boot/pc/ether.c - 664 sys sys 1056073258 4670
+sys/src/boot/pc/ether.c - 664 sys sys 1071175087 4727
 sys/src/boot/pc/ether2000.c - 664 sys sys 1015007950 2609
 sys/src/boot/pc/ether2114x.c - 664 sys sys 1066618033 37048
 sys/src/boot/pc/ether589.c - 664 sys sys 1015007950 4628
@@ -6438,6 +6438,9 @@ sys/src/boot/pc/etherec2t.c - 664 sys sys 1015007951 3598
 sys/src/boot/pc/etherelnk3.c - 664 sys sys 1034454878 44068
 sys/src/boot/pc/etherelnk3x.c - 664 sys sys 1015007951 24989
 sys/src/boot/pc/etherif.h - 664 sys sys 1015007951 1285
+sys/src/boot/pc/ethermii.c - 664 sys sys 1071175087 4493
+sys/src/boot/pc/ethermii.h - 664 sys sys 1071175087 3259
+sys/src/boot/pc/etherrhine.c - 664 sys sys 1071175087 12403
 sys/src/boot/pc/fns.h - 664 sys sys 1032215922 4081
 sys/src/boot/pc/fs.c - 664 sys sys 1032215914 1487
 sys/src/boot/pc/fs.h - 664 sys sys 1032215924 627
@@ -6454,7 +6457,7 @@ sys/src/boot/pc/load.c - 664 sys sys 1056087197 7987
 sys/src/boot/pc/mbr.s - 664 sys sys 1015007953 6234
 sys/src/boot/pc/mem.h - 664 sys sys 1015007953 3407
 sys/src/boot/pc/memory.c - 664 sys sys 1019533021 10272
-sys/src/boot/pc/mkfile - 664 sys sys 1063855535 2971
+sys/src/boot/pc/mkfile - 664 sys sys 1071175087 3001
 sys/src/boot/pc/noether.c - 664 sys sys 1018553453 300
 sys/src/boot/pc/part.c - 664 sys sys 1032215918 7505
 sys/src/boot/pc/pbs.s - 664 sys sys 1017854325 8279
@@ -7517,7 +7520,7 @@ sys/src/cmd/fossil/9p.c - 664 sys sys 1069818231 21498
 sys/src/cmd/fossil/9ping.c - 664 sys sys 1042005503 1563
 sys/src/cmd/fossil/9proc.c - 664 sys sys 1069683861 14521
 sys/src/cmd/fossil/9srv.c - 664 sys sys 1066098097 3682
-sys/src/cmd/fossil/9user.c - 664 sys sys 1066098098 17239
+sys/src/cmd/fossil/9user.c - 664 sys sys 1071179088 17260
 sys/src/cmd/fossil/Ccli.c - 664 sys sys 1042005504 1624
 sys/src/cmd/fossil/Ccmd.c - 664 sys sys 1066098098 7244
 sys/src/cmd/fossil/Ccons.c - 664 sys sys 1055703737 6620
@@ -10194,7 +10197,7 @@ sys/src/cmd/rtstats/resproc.c - 664 sys sys 1037669163 2287
 sys/src/cmd/rtstats/rtstats.c - 664 sys sys 1048644563 13224
 sys/src/cmd/rtstats/time.c - 664 sys sys 1038425433 2094
 sys/src/cmd/rtstats/time.h - 664 sys sys 1038425434 164
-sys/src/cmd/rx.c - 664 sys sys 1066572585 3657
+sys/src/cmd/rx.c - 664 sys sys 1071155928 4188
 sys/src/cmd/sam - 20000000775 sys sys 944961629 0
 sys/src/cmd/sam/address.c - 664 sys sys 944961628 3985
 sys/src/cmd/sam/buff.c - 664 sys sys 1014926937 5161

+ 12 - 0
dist/replica/plan9.log

@@ -12961,3 +12961,15 @@
 1071079350 0 c dist/replica/network - 775 sys sys 1071079179 996
 1071095553 0 c sys/src/cmd/ssh/cmsg.c - 664 sys sys 1071095546 8351
 1071111756 0 c sys/src/ape/lib/ap/mips/lock.c - 664 sys sys 1071110410 2380
+1071156761 0 c sys/man/1/con - 664 sys sys 1071156278 4318
+1071156761 1 c sys/src/cmd/rx.c - 664 sys sys 1071155928 4188
+1071176564 0 c 386/9load - 775 sys sys 1071175100 188380
+1071176564 1 c sys/src/ape/lib/ap/plan9/_buf.c - 664 sys sys 1071175883 9740
+1071176564 2 c sys/src/boot/pc/ether.c - 664 sys sys 1071175087 4727
+1071176564 3 a sys/src/boot/pc/ethermii.c - 664 sys sys 1071175087 4493
+1071176564 4 a sys/src/boot/pc/ethermii.h - 664 sys sys 1071175087 3259
+1071176564 5 a sys/src/boot/pc/etherrhine.c - 664 sys sys 1071175087 12403
+1071176564 6 c sys/src/boot/pc/mkfile - 664 sys sys 1071175087 3001
+1071180165 0 c 386/bin/fossil/fossil - 775 sys sys 1071179084 345532
+1071180165 1 c sys/src/ape/lib/ap/plan9/_buf.c - 664 sys sys 1071178644 9850
+1071180165 2 c sys/src/cmd/fossil/9user.c - 664 sys sys 1071179088 17260

+ 15 - 5
sys/man/1/con

@@ -33,7 +33,7 @@ con, telnet, rx, xms, xmr \- remote login, execution, and XMODEM file transfer
 .PP
 .B rx
 [
-.B -e
+.B -eTr
 ]
 [
 .B -l
@@ -170,16 +170,26 @@ If the target is a Plan 9 machine,
 .B $service
 there will be
 .BR rx .
-The
+Options are:
+.TP
 .B \-e
-option causes a zero length message to be written to the
+a zero length message will be written to the
 connection when standard input is closed.
-The
+.TP
 .B \-l
-option causes
+allows
 .I remuser
 to be used on the remote machine if the remote
 is a BSD machine.
+.TP
+.B \-r
+same as for
+.I con
+.TP
+.B -T
+same as for
+.I con
+.PD
 .PP
 Network addresses for both
 .I con

+ 22 - 11
sys/src/ape/lib/ap/plan9/_buf.c

@@ -262,7 +262,7 @@ goteof:
 int
 select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeout)
 {
-	int n, i, tmp, t, slots, fd;
+	int n, i, tmp, t, slots, fd, err;
 	Fdinfo *f;
 	Muxbuf *b;
 
@@ -291,7 +291,8 @@ select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeo
 					return -1;
 				}
 			b = f->buf;
-			if(rfds && FD_ISSET(i,rfds) && b->eof && b->n == 0) {
+			if(rfds && FD_ISSET(i,rfds) && b->eof && b->n == 0)
+			if(efds == 0 || !FD_ISSET(i,efds)) {
 				errno = EBADF;		/* how X tells a client is gone */
 				return -1;
 			}
@@ -317,20 +318,22 @@ select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeo
 		fd = b->fd;
 		if(fd == -1)
 			continue;
-		if(rfds && FD_ISSET(fd, rfds)) {
-			if(b->n > 0 || b->eof)
+		err = 0;
+		if(efds && FD_ISSET(fd, efds)) {
+			if(b->eof && b->n == 0){
+				err = 1;
 				n++;
-			else{
-				FD_CLR(fd, rfds);
-				FD_SET(fd, &mux->rwant);
+			}else{
+				FD_CLR(fd, efds);
+				FD_SET(fd, &mux->ewant);
 			}
 		}
-		if(efds && FD_ISSET(fd, efds)) {
-			if(b->eof && b->n == 0)
+		if(rfds && FD_ISSET(fd, rfds)) {
+			if(!err && (b->n > 0 || b->eof))
 				n++;
 			else{
-				FD_CLR(fd, efds);
-				FD_SET(fd, &mux->ewant);
+				FD_CLR(fd, rfds);
+				FD_SET(fd, &mux->rwant);
 			}
 		}
 	}
@@ -367,6 +370,7 @@ select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *timeo
 }
 
 static int timerreset;
+static int timerpid;
 
 static void
 alarmed(int v)
@@ -377,6 +381,12 @@ alarmed(int v)
 /* a little over an hour */
 #define LONGWAIT 4000001
 
+static void
+_killtimerproc(void)
+{
+	kill(timerpid, SIGKILL);
+}
+
 static void
 _timerproc(void)
 {
@@ -407,6 +417,7 @@ _timerproc(void)
 			}
 		}
 	}
+	atexit(_killtimerproc);
 	/* parent process continues */
 	_RENDEZVOUS(1, 0);
 }

+ 2 - 0
sys/src/boot/pc/ether.c

@@ -20,6 +20,7 @@ extern int ec2treset(Ether*);
 extern int amd79c970reset(Ether*);
 extern int rtl8139pnp(Ether*);
 extern int ether83815reset(Ether*);
+extern int rhinepnp(Ether*);
 
 struct {
 	char	*type;
@@ -41,6 +42,7 @@ struct {
 	{ "AMD79C970", amd79c970reset, 0, },
 	{ "RTL8139", rtl8139pnp, 0, },
 	{ "83815", ether83815reset, 0, },
+	{ "rhine", rhinepnp, 0, },
 
 	{ 0, }
 };

+ 231 - 0
sys/src/boot/pc/ethermii.c

@@ -0,0 +1,231 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "etherif.h"
+#include "ethermii.h"
+
+int
+mii(Mii* mii, int mask)
+{
+	MiiPhy *miiphy;
+	int bit, phyno, r, rmask;
+
+	/*
+	 * Probe through mii for PHYs in mask;
+	 * return the mask of those found in the current probe.
+	 * If the PHY has not already been probed, update
+	 * the Mii information.
+	 */
+	rmask = 0;
+	for(phyno = 0; phyno < NMiiPhy; phyno++){
+		bit = 1<<phyno;
+		if(!(mask & bit))
+			continue;
+		if(mii->mask & bit){
+			rmask |= bit;
+			continue;
+		}
+		if(mii->mir(mii, phyno, Bmsr) == -1)
+			continue;
+		if((miiphy = malloc(sizeof(MiiPhy))) == nil)
+			continue;
+
+		miiphy->mii = mii;
+		r = mii->mir(mii, phyno, Phyidr1);
+		miiphy->oui = (r & 0x3FFF)<<6;
+		r = mii->mir(mii, phyno, Phyidr2);
+		miiphy->oui |= r>>10;
+		miiphy->phyno = phyno;
+
+		miiphy->anar = ~0;
+		miiphy->fc = ~0;
+		miiphy->mscr = ~0;
+
+		mii->phy[phyno] = miiphy;
+		if(mii->curphy == nil)
+			mii->curphy = miiphy;
+		mii->mask |= bit;
+		mii->nphy++;
+
+		rmask |= bit;
+	}
+	return rmask;
+}
+
+int
+miimir(Mii* mii, int r)
+{
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	return mii->mir(mii, mii->curphy->phyno, r);
+}
+
+int
+miimiw(Mii* mii, int r, int data)
+{
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	return mii->miw(mii, mii->curphy->phyno, r, data);
+}
+
+int
+miireset(Mii* mii)
+{
+	int bmcr;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	bmcr = mii->mir(mii, mii->curphy->phyno, Bmcr);
+	bmcr |= BmcrR;
+	mii->miw(mii, mii->curphy->phyno, Bmcr, bmcr);
+	microdelay(1);
+
+	return 0;
+}
+
+int
+miiane(Mii* mii, int a, int p, int e)
+{
+	int anar, bmsr, mscr, r, phyno;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	phyno = mii->curphy->phyno;
+
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & BmsrAna))
+		return -1;
+
+	if(a != ~0)
+		anar = (AnaTXFD|AnaTXHD|Ana10FD|Ana10HD) & a;
+	else if(mii->curphy->anar != ~0)
+		anar = mii->curphy->anar;
+	else{
+		anar = mii->mir(mii, phyno, Anar);
+		anar &= ~(AnaAP|AnaP|AnaT4|AnaTXFD|AnaTXHD|Ana10FD|Ana10HD);
+		if(bmsr & Bmsr10THD)
+			anar |= Ana10HD;
+		if(bmsr & Bmsr10TFD)
+			anar |= Ana10FD;
+		if(bmsr & Bmsr100TXHD)
+			anar |= AnaTXHD;
+		if(bmsr & Bmsr100TXFD)
+			anar |= AnaTXFD;
+	}
+	mii->curphy->anar = anar;
+
+	if(p != ~0)
+		anar |= (AnaAP|AnaP) & p;
+	else if(mii->curphy->fc != ~0)
+		anar |= mii->curphy->fc;
+	mii->curphy->fc = (AnaAP|AnaP) & anar;
+
+	if(bmsr & BmsrEs){
+		mscr = mii->mir(mii, phyno, Mscr);
+		mscr &= ~(Mscr1000TFD|Mscr1000THD);
+		if(e != ~0)
+			mscr |= (Mscr1000TFD|Mscr1000THD) & e;
+		else if(mii->curphy->mscr != ~0)
+			mscr = mii->curphy->mscr;
+		else{
+			r = mii->mir(mii, phyno, Esr);
+			if(r & Esr1000THD)
+				mscr |= Mscr1000THD;
+			if(r & Esr1000TFD)
+				mscr |= Mscr1000TFD;
+		}
+		mii->curphy->mscr = mscr;
+		mii->miw(mii, phyno, Mscr, mscr);
+	}
+	mii->miw(mii, phyno, Anar, anar);
+
+	r = mii->mir(mii, phyno, Bmcr);
+	if(!(r & BmcrR)){
+		r |= BmcrAne|BmcrRan;
+		mii->miw(mii, phyno, Bmcr, r);
+	}
+
+	return 0;
+}
+
+int
+miistatus(Mii* mii)
+{
+	MiiPhy *phy;
+	int anlpar, bmsr, p, r, phyno;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	phy = mii->curphy;
+	phyno = phy->phyno;
+
+	/*
+	 * Check Auto-Negotiation is complete and link is up.
+	 * (Read status twice as the Ls bit is sticky).
+	 */
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & (BmsrAnc|BmsrAna)))
+{
+print("miistatus 1\n");
+		return -1;
+}
+
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & BmsrLs)){
+print("miistatus 2\n");
+		phy->link = 0;
+		return -1;
+	}
+
+	phy->speed = phy->fd = phy->rfc = phy->tfc = 0;
+	if(phy->mscr){
+		r = mii->mir(mii, phyno, Mssr);
+		if((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)){
+			phy->speed = 1000;
+			phy->fd = 1;
+		}
+		else if((phy->mscr & Mscr1000THD) && (r & Mssr1000THD))
+			phy->speed = 1000;
+	}
+
+	anlpar = mii->mir(mii, phyno, Anlpar);
+	if(phy->speed == 0){
+		r = phy->anar & anlpar;
+		if(r & AnaTXFD){
+			phy->speed = 100;
+			phy->fd = 1;
+		}
+		else if(r & AnaTXHD)
+			phy->speed = 100;
+		else if(r & Ana10FD){
+			phy->speed = 10;
+			phy->fd = 1;
+		}
+		else if(r & Ana10HD)
+			phy->speed = 10;
+	}
+	if(phy->speed == 0)
+{
+print("miistatus 3\n");
+		return -1;
+}
+
+	if(phy->fd){
+		p = phy->fc;
+		r = anlpar & (AnaAP|AnaP);
+		if(p == AnaAP && r == (AnaAP|AnaP))
+			phy->tfc = 1;
+		else if(p == (AnaAP|AnaP) && r == AnaAP)
+			phy->rfc = 1;
+		else if((p & AnaP) && (r & AnaP))
+			phy->rfc = phy->tfc = 1;
+	}
+
+	phy->link = 1;
+
+	return 0;
+}

+ 116 - 0
sys/src/boot/pc/ethermii.h

@@ -0,0 +1,116 @@
+typedef struct Mii Mii;
+typedef struct MiiPhy MiiPhy;
+
+enum {					/* registers */
+	Bmcr		= 0x00,		/* Basic Mode Control */
+	Bmsr		= 0x01,		/* Basic Mode Status */
+	Phyidr1		= 0x02,		/* PHY Identifier #1 */
+	Phyidr2		= 0x03,		/* PHY Identifier #2 */
+	Anar		= 0x04,		/* Auto-Negotiation Advertisement */
+	Anlpar		= 0x05,		/* AN Link Partner Ability */
+	Aner		= 0x06,		/* AN Expansion */
+	Annptr		= 0x07,		/* AN Next Page TX */
+	Annprr		= 0x08,		/* AN Next Page RX */
+	Mscr		= 0x09,		/* MASTER-SLAVE Control */
+	Mssr		= 0x0A,		/* MASTER-SLAVE Status */
+	Esr		= 0x0F,		/* Extended Status */
+
+	NMiiPhyr	= 32,
+	NMiiPhy		= 32,
+};
+
+enum {					/* Bmcr */
+	BmcrSs1		= 0x0040,	/* Speed Select[1] */
+	BmcrCte		= 0x0080,	/* Collision Test Enable */
+	BmcrDm		= 0x0100,	/* Duplex Mode */
+	BmcrRan		= 0x0200,	/* Restart Auto-Negotiation */
+	BmcrI		= 0x0400,	/* Isolate */
+	BmcrPd		= 0x0800,	/* Power Down */
+	BmcrAne		= 0x1000,	/* Auto-Negotiation Enable */
+	BmcrSs0		= 0x2000,	/* Speed Select[0] */
+	BmcrLe		= 0x4000,	/* Loopback Enable */
+	BmcrR		= 0x8000,	/* Reset */
+};
+
+enum {					/* Bmsr */
+	BmsrEc		= 0x0001,	/* Extended Capability */
+	BmsrJd		= 0x0002,	/* Jabber Detect */
+	BmsrLs		= 0x0004,	/* Link Status */
+	BmsrAna		= 0x0008,	/* Auto-Negotiation Ability */
+	BmsrRf		= 0x0010,	/* Remote Fault */
+	BmsrAnc		= 0x0020,	/* Auto-Negotiation Complete */
+	BmsrPs		= 0x0040,	/* Preamble Suppression Capable */
+	BmsrEs		= 0x0100,	/* Extended Status */
+	Bmsr100T2HD	= 0x0200,	/* 100BASE-T2 HD Capable */
+	Bmsr100T2FD	= 0x0400,	/* 100BASE-T2 FD Capable */
+	Bmsr10THD	= 0x0800,	/* 100BASE-T HD Capable */
+	Bmsr10TFD	= 0x1000,	/* 10BASE-T FD Capable */
+	Bmsr100TXHD	= 0x2000,	/* 100BASE-TX HD Capable */
+	Bmsr100TXFD	= 0x4000,	/* 100BASE-TX FD Capable */
+	Bmsr100T4	= 0x8000,	/* 100BASE-T4 Capable */
+};
+
+enum {					/* Anar/Anlpar */
+	Ana10HD		= 0x0020,	/* Advertise 10BASE-T */
+	Ana10FD		= 0x0040,	/* Advertise 10BASE-T FD */
+	AnaTXHD		= 0x0080,	/* Advertise 100BASE-TX */
+	AnaTXFD		= 0x0100,	/* Advertise 100BASE-TX FD */
+	AnaT4		= 0x0200,	/* Advertise 100BASE-T4 */
+	AnaP		= 0x0400,	/* Pause */
+	AnaAP		= 0x0800,	/* Asymmetrical Pause */
+	AnaRf		= 0x2000,	/* Remote Fault */
+	AnaAck		= 0x4000,	/* Acknowledge */
+	AnaNp		= 0x8000,	/* Next Page Indication */
+};
+
+enum {					/* Mscr */
+	Mscr1000THD	= 0x0100,	/* Advertise 1000BASE-T HD */
+	Mscr1000TFD	= 0x0200,	/* Advertise 1000BASE-T FD */
+};
+
+enum {					/* Mssr */
+	Mssr1000THD	= 0x0400,	/* Link Partner 1000BASE-T HD able */
+	Mssr1000TFD	= 0x0800,	/* Link Partner 1000BASE-T FD able */
+};
+
+enum {					/* Esr */
+	Esr1000THD	= 0x1000,	/* 1000BASE-T HD Capable */
+	Esr1000TFD	= 0x2000,	/* 1000BASE-T FD Capable */
+	Esr1000XHD	= 0x4000,	/* 1000BASE-X HD Capable */
+	Esr1000XFD	= 0x8000,	/* 1000BASE-X FD Capable */
+};
+
+typedef struct Mii {
+	Lock;
+	int	nphy;
+	int	mask;
+	MiiPhy*	phy[NMiiPhy];
+	MiiPhy*	curphy;
+
+	void*	ctlr;
+	int	(*mir)(Mii*, int, int);
+	int	(*miw)(Mii*, int, int, int);
+} Mii;
+
+typedef struct MiiPhy {
+	Mii*	mii;
+	int	oui;
+	int	phyno;
+
+	int	anar;
+	int	fc;
+	int	mscr;
+
+	int	link;
+	int	speed;
+	int	fd;
+	int	rfc;
+	int	tfc;
+};
+
+extern int mii(Mii*, int);
+extern int miiane(Mii*, int, int, int);
+extern int miimir(Mii*, int);
+extern int miimiw(Mii*, int, int);
+extern int miireset(Mii*);
+extern int miistatus(Mii*);

+ 676 - 0
sys/src/boot/pc/etherrhine.c

@@ -0,0 +1,676 @@
+ /*
+	Via Rhine driver, written for VT6102.
+	Uses the ethermii to control PHY.
+
+	Currently always copies on both, tx and rx.
+	rx side could be copy-free, and tx-side might be made
+	(almost) copy-free by using (possibly) two descriptors (if it allows
+	arbitrary tx lengths, which it should..): first for alignment and
+	second for rest of the frame. Rx-part should be worth doing.
+*/
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+typedef struct QLock { int r; } QLock;
+#define qlock(i)	while(0)
+#define qunlock(i)	while(0)
+#define coherence()
+#define iprint		print
+
+#include "etherif.h"
+#include "ethermii.h"
+
+typedef struct Desc Desc;
+typedef struct Ctlr Ctlr;
+
+enum {
+	Ntxd = 4,
+	Nrxd = 4,
+	Nwait = 50,
+	Ntxstats = 9,
+	Nrxstats = 8,
+	BIGSTR = 8192,
+};
+
+struct Desc {
+	ulong stat;
+	ulong size;
+	ulong addr;
+	ulong next;
+	char *buf;
+	ulong pad[3];
+};
+
+struct Ctlr {
+	Pcidev *pci;
+	int attached;
+	int txused;
+	int txhead;
+	int txtail;
+	int rxtail;
+	ulong port;
+
+	Mii mii;
+
+	ulong txstats[Ntxstats];
+	ulong rxstats[Nrxstats];
+
+	Desc *txd;	/* wants to be aligned on 16-byte boundary */
+	Desc *rxd;
+
+	QLock attachlck;
+	Lock tlock;
+};
+
+#define ior8(c, r)	(inb((c)->port+(r)))
+#define ior16(c, r)	(ins((c)->port+(r)))
+#define ior32(c, r)	(inl((c)->port+(r)))
+#define iow8(c, r, b)	(outb((c)->port+(r), (int)(b)))
+#define iow16(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
+#define iow32(c, r, l)	(outl((c)->port+(r), (ulong)(l)))
+
+enum Regs {
+	Eaddr = 0x0,
+	Rcr = 0x6,
+	Tcr = 0x7,
+	Cr = 0x8,
+	Isr = 0xc,
+	Imr = 0xe,
+	McastAddr = 0x10,
+	RxdAddr = 0x18,
+	TxdAddr = 0x1C,
+	Bcr = 0x6e,
+	RhineMiiPhy = 0x6C,
+	RhineMiiSr = 0x6D,
+	RhineMiiCr = 0x70,
+	RhineMiiAddr = 0x71,
+	RhineMiiData = 0x72,
+	Eecsr = 0x74,
+	ConfigB = 0x79,
+	ConfigD = 0x7B,
+	MiscCr = 0x80,
+	HwSticky = 0x83,
+	MiscIsr = 0x84,
+	MiscImr = 0x86,
+	WolCrSet = 0xA0,
+	WolCfgSet = 0xA1,
+	WolCgSet = 0xA3,
+	WolCrClr = 0xA4,
+	PwrCfgClr = 0xA5,
+	WolCgClr = 0xA7,
+};
+
+enum Rcrbits {
+	RxErrX = 1<<0,
+	RxSmall = 1<<1,
+	RxMcast = 1<<2,
+	RxBcast = 1<<3,
+	RxProm = 1<<4,
+	RxFifo64 = 0<<5, RxFifo32 = 1<<5, RxFifo128 = 2<<5, RxFifo256 = 3<<5,
+	RxFifo512 = 4<<5, RxFifo768 = 5<<5, RxFifo1024 = 6<<5,
+	RxFifoStoreForward = 7<<5,
+};
+
+enum Tcrbits {
+	TxLoopback0 = 1<<1,
+	TxLoopback1 = 1<<2,
+	TxBackoff = 1<<3,
+	TxFifo128 = 0<<5, TxFifo256 = 1<<5, TxFifo512 = 2<<5, TxFifo1024 = 3<<5,
+	TxFifoStoreForward = 7<<5,
+};
+
+enum Crbits {
+	Init = 1<<0,
+	Start = 1<<1,
+	Stop = 1<<2,
+	RxOn = 1<<3,
+	TxOn = 1<<4,
+	Tdmd = 1<<5,
+	Rdmd = 1<<6,
+	EarlyRx = 1<<8,
+	Reserved0 = 1<<9,
+	FullDuplex = 1<<10,
+	NoAutoPoll = 1<<11,
+	Reserved1 = 1<<12,
+	Tdmd1 = 1<<13,
+	Rdmd1 = 1<<14,
+	Reset = 1<<15,
+};
+
+enum Isrbits {
+	RxOk = 1<<0,
+	TxOk = 1<<1,
+	RxErr = 1<<2,
+	TxErr = 1<<3,
+	TxBufUdf = 1<<4,
+	RxBufLinkErr = 1<<5,
+	BusErr = 1<<6,
+	CrcOvf = 1<<7,
+	EarlyRxInt = 1<<8,
+	TxFifoUdf = 1<<9,
+	RxFifoOvf = 1<<10,
+	TxPktRace = 1<<11,
+	NoRxbuf = 1<<12,
+	TxCollision = 1<<13,
+	PortCh = 1<<14,
+	GPInt = 1<<15
+};
+
+enum Bcrbits {
+	Dma32 = 0<<0, Dma64 = 1<<0, Dma128 = 2<<0,
+	Dma256 = 3<<0, Dma512 = 4<<0, Dma1024 = 5<<0,
+	DmaStoreForward = 7<<0,
+	DupRxFifo0 = 1<<3, DupRxFifo1 = 1<<4, DupRxFifo2 = 1<<5,
+	ExtraLed = 1<<6,
+	MediumSelect = 1<<7,
+	PollTimer0 = 1<<8, PollTimer1 = 1<<9, PollTimer2 = 1<<10,
+	DupTxFifo0 = 1<<11, DupTxFifo1 = 1<<12, DupTxFifo2 = 1<<13,
+};
+
+enum Eecsrbits {
+	EeAutoLoad = 1<<5,
+};
+
+enum MiscCrbits {
+	Timer0Enable= 1<<0,
+	Timer0Suspend = 1<<1,
+	HalfDuplexFlowControl = 1<<2,
+	FullDuplexFlowControl = 1<<3,
+	Timer1Enable = 1<<8,
+	ForceSoftReset = 1<<14,
+};
+
+enum HwStickybits {
+	StickyDS0 = 1<<0,
+	StickyDS1 = 1<<1,
+	WOLEna = 1<<2,
+	WOLStat = 1<<3,
+};
+
+enum WolCgbits {
+	PmeOvr = 1<<7,
+};
+
+enum Descbits {
+	OwnNic = 1<<31,		/* stat */
+	TxAbort = 1<<8,		/* stat */
+	TxError = 1<<15,		/* stat */
+	RxChainbuf = 1<<10,	/* stat */
+	RxChainStart = 1<<9,	/* stat */
+	RxChainEnd = 1<<8,		/* stat */
+	Chainbuf = 1<<15,		/* size rx & tx*/
+	TxDisableCrc = 1<<16,	/* size */
+	TxChainStart = 1<<21,	/* size */
+	TxChainEnd = 1<<22,	/* size */
+	TxInt = 1<<23,			/* size */
+};
+
+enum ConfigDbits {
+	BackoffOptional = 1<<0,
+	BackoffAMD = 1<<1,
+	BackoffDEC = 1<<2,
+	BackoffRandom = 1<<3,
+	PmccTestMode = 1<<4,
+	PciReadlineCap = 1<<5,
+	DiagMode = 1<<6,
+	MmioEnable = 1<<7,
+};
+
+enum ConfigBbits {
+	LatencyTimer = 1<<0,
+	WriteWaitState = 1<<1,
+	ReadWaitState = 1<<2,
+	RxArbit = 1<<3,
+	TxArbit = 1<<4,
+	NoMemReadline = 1<<5,
+	NoParity = 1<<6,
+	NoTxQueuing = 1<<7,
+};
+
+enum RhineMiiCrbits {
+	Mdc = 1<<0,
+	Mdi = 1<<1,
+	Mdo = 1<<2,
+	Mdout = 1<<3,
+	Mdpm = 1<<4,
+	Wcmd = 1<<5,
+	Rcmd = 1<<6,
+	Mauto = 1<<7,
+};
+
+enum RhineMiiSrbits {
+	Speed10M = 1<<0,
+	LinkFail = 1<<1,
+	PhyError = 1<<3,
+	DefaultPhy = 1<<4,
+	ResetPhy = 1<<7,
+};
+
+enum RhineMiiAddrbits {
+	Mdone = 1<<5,
+	Msrcen = 1<<6,
+	Midle = 1<<7,
+};
+
+static char *
+txstatnames[Ntxstats] = {
+	"aborts (excess collisions)",
+	"out of window collisions",
+	"carrier sense losses",
+	"fifo underflows",
+	"invalid descriptor format or underflows",
+	"system errors",
+	"reserved",
+	"transmit errors",
+	"collisions",
+};
+
+static char *
+rxstatnames[Nrxstats] = {
+	"receiver errors",
+	"crc errors",
+	"frame alignment errors",
+	"fifo overflows",
+	"long packets",
+	"run packets",
+	"system errors",
+	"buffer underflows",
+};
+
+static void
+attach(Ether *edev)
+{
+	Ctlr *ctlr;
+	Desc *txd, *rxd, *td, *rd;
+	Mii *mi;
+	MiiPhy *phy;
+	int i, s;
+	
+	ctlr = edev->ctlr;
+	qlock(&ctlr->attachlck);
+	if (ctlr->attached == 0) {
+		txd = ctlr->txd;
+		rxd = ctlr->rxd;
+		for (i = 0; i < Ntxd; ++i) {
+			td = &txd[i];
+			td->next = PCIWADDR(&txd[(i+1) % Ntxd]);
+			td->buf = xspanalloc(sizeof(Etherpkt)+4, 4, 0);
+			td->addr = PCIWADDR(td->buf);
+			td->size = 0;
+			coherence();
+			td->stat = 0;
+		}
+		for (i = 0; i < Nrxd; ++i) {
+			rd = &rxd[i];
+			rd->next = PCIWADDR(&rxd[(i+1) % Nrxd]);
+			rd->buf = xspanalloc(sizeof(Etherpkt)+4, 4, 0);
+			rd->addr = PCIWADDR(rd->buf);
+			rd->size = sizeof(Etherpkt)+4;
+			coherence();
+			rd->stat = OwnNic;
+		}
+
+		ctlr->txhead = ctlr->txtail = ctlr->rxtail = 0;
+		mi = &ctlr->mii;
+		miistatus(mi);
+		phy = mi->curphy;
+		s = splhi();
+		iow32(ctlr, TxdAddr, PCIWADDR(&txd[0]));
+		iow32(ctlr, RxdAddr, PCIWADDR(&rxd[0]));
+		iow16(ctlr, Cr, (phy->fd ? FullDuplex : 0) | NoAutoPoll | TxOn | RxOn | Start | Rdmd);
+		iow16(ctlr, Isr, 0xFFFF);
+		iow16(ctlr, Imr, 0xFFFF);
+		iow8(ctlr, MiscIsr, 0xFF);
+		iow8(ctlr, MiscImr, ~(3<<5));
+		splx(s);
+	}
+	ctlr->attached++;
+	qunlock(&ctlr->attachlck);
+}
+
+static void
+txstart(Ether *edev)
+{
+	Ctlr *ctlr;
+	Desc *txd, *td;
+	int i, txused, n;
+	RingBuf *tb;
+
+	ctlr = edev->ctlr;
+
+	txd = ctlr->txd;
+	i = ctlr->txhead;
+	txused = ctlr->txused;
+	n = 0;
+	while (txused < Ntxd) {
+		tb = &edev->tb[edev->ti];
+		if(tb->owner != Interface)
+			break;
+
+		td = &txd[i];
+		memmove(td->buf, tb->pkt, tb->len);
+		td->size = tb->len | TxChainStart | TxChainEnd | TxInt; /* could reduce number of ints here */
+		coherence();
+		td->stat = OwnNic;
+		i = (i + 1) % Ntxd;
+		txused++;
+		n++;
+
+		tb->owner = Host;
+		edev->ti = NEXT(edev->ti, edev->ntb);
+	}
+	if (n)
+		iow16(ctlr, Cr, ior16(ctlr, Cr) | Tdmd);
+
+	ctlr->txhead = i;
+	ctlr->txused = txused;
+}
+
+static void
+transmit(Ether *edev)
+{
+	Ctlr *ctlr;
+	ctlr = edev->ctlr;
+	ilock(&ctlr->tlock);
+	txstart(edev);
+	iunlock(&ctlr->tlock);
+}
+
+static void
+txcomplete(Ether *edev)
+{
+	Ctlr *ctlr;
+	Desc *txd, *td;
+	int i, txused, j;
+	ulong stat;
+
+	ctlr = edev->ctlr;
+ 	txd = ctlr->txd;
+	txused = ctlr->txused;
+	i = ctlr->txtail;
+	while (txused > 0) {
+		td = &txd[i];
+		stat = td->stat;
+
+		if (stat & OwnNic)
+			break;
+
+		ctlr->txstats[Ntxstats-1] += stat & 0xF;
+		for (j = 0; j < Ntxstats-1; ++j)
+			if (stat & (1<<(j+8)))
+				ctlr->txstats[j]++;
+
+		i = (i + 1) % Ntxd;
+		txused--;
+	}
+	ctlr->txused = txused;
+	ctlr->txtail = i;
+
+	if (txused <= Ntxd/2)
+		txstart(edev);
+}
+
+static void
+interrupt(Ureg *, void *arg)
+{
+	Ether *edev;
+	Ctlr *ctlr;
+	RingBuf *rb;
+	ushort  isr, misr;
+	ulong stat;
+	Desc *rxd, *rd;
+	int i, n, j, size;
+
+	edev = (Ether*)arg;
+	ctlr = edev->ctlr;
+	iow16(ctlr, Imr, 0);
+	isr = ior16(ctlr, Isr);
+	iow16(ctlr, Isr, 0xFFFF);
+	misr = ior16(ctlr, MiscIsr) & ~(3<<5); /* don't care about used defined ints */
+
+	if (isr & RxOk) {
+		rxd = ctlr->rxd;
+		i = ctlr->rxtail;
+
+		n = 0;
+		while ((rxd[i].stat & OwnNic) == 0) {
+			rd = &rxd[i];
+			stat = rd->stat;
+			for (j = 0; j < Nrxstats; ++j)
+				if (stat & (1<<j))
+					ctlr->rxstats[j]++;
+
+			if (stat & 0xFF)
+				iprint("rx: %lux\n", stat & 0xFF);
+
+			size = ((rd->stat>>16) & 2047) - 4;
+
+			rb = &edev->rb[edev->ri];
+			if(rb->owner == Interface){
+				rb->owner = Host;
+				rb->len = size;
+				memmove(rb->pkt, rd->buf, size);
+				edev->ri = NEXT(edev->ri, edev->nrb);
+			}
+
+			rd->size = sizeof(Etherpkt)+4;
+			coherence();
+			rd->stat = OwnNic;
+			i = (i + 1) % Nrxd;
+			n++;
+		}
+		if (n)
+			iow16(ctlr, Cr, ior16(ctlr, Cr) | Rdmd);
+		ctlr->rxtail = i;
+		isr &= ~RxOk;
+	}
+	if (isr & TxOk) {
+		txcomplete(edev);
+		isr &= ~TxOk;
+	}
+	if (isr | misr)
+		iprint("etherrhine: unhandled irq(s). isr:%x misr:%x\n", isr, misr);
+
+	iow16(ctlr, Imr, 0xFFFF);
+}
+
+static void
+promiscuous(void *arg, int enable)
+{
+	Ether *edev;
+	Ctlr *ctlr;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+	ilock(&ctlr->tlock);
+	iow8(ctlr, Rcr, ior8(ctlr, Rcr) | (enable ? RxProm : RxBcast));
+	iunlock(&ctlr->tlock);
+}
+
+static int
+miiread(Mii *mii, int phy, int reg)
+{
+	Ctlr *ctlr;
+	int n;
+
+	ctlr = mii->ctlr;
+	
+	n = Nwait;
+	while (n-- && ior8(ctlr, RhineMiiCr) & (Rcmd | Wcmd))
+		microdelay(1);
+	if (n == Nwait)
+		iprint("etherrhine: miiread: timeout\n");
+
+	iow8(ctlr, RhineMiiCr, 0);
+	iow8(ctlr, RhineMiiPhy, phy);
+	iow8(ctlr, RhineMiiAddr, reg);
+	iow8(ctlr, RhineMiiCr, Rcmd);
+
+	n = Nwait;
+	while (n-- && ior8(ctlr, RhineMiiCr) & Rcmd)
+		microdelay(1);
+	if (n == Nwait)
+		iprint("etherrhine: miiread: timeout\n");
+
+	n = ior16(ctlr, RhineMiiData);
+
+	return n;
+}
+
+static int
+miiwrite(Mii *mii, int phy, int reg, int data)
+{
+	int n;
+	Ctlr *ctlr;
+
+	ctlr = mii->ctlr;
+
+	n = Nwait;
+	while (n-- && ior8(ctlr, RhineMiiCr) & (Rcmd | Wcmd))
+		microdelay(1);
+	if (n == Nwait)
+		iprint("etherrhine: miiwrite: timeout\n");
+
+	iow8(ctlr, RhineMiiCr, 0);
+	iow8(ctlr, RhineMiiPhy, phy);
+	iow8(ctlr, RhineMiiAddr, reg);
+	iow16(ctlr, RhineMiiData, data);
+	iow8(ctlr, RhineMiiCr, Wcmd);
+
+	n = Nwait;
+	while (n-- && ior8(ctlr, RhineMiiCr) & Wcmd)
+		microdelay(1);
+	if (n == Nwait)
+		iprint("etherrhine: miiwrite: timeout\n");
+
+	return 0;
+}
+
+static void
+reset(Ctlr* ctlr)
+{
+	int i;
+
+	iow16(ctlr, Cr, ior16(ctlr, Cr) | Stop);
+	iow16(ctlr, Cr, ior16(ctlr, Cr) | Reset);
+
+	for (i = 0; i < Nwait; ++i) {
+		if ((ior16(ctlr, Cr) & Reset) == 0)
+			return;
+		delay(5);
+	}
+	iprint("etherrhine: reset timeout\n");
+}
+
+static void
+detach(Ether* edev)
+{
+	reset(edev->ctlr);
+}
+
+static void
+init(Ether *edev)
+{
+	Ctlr *ctlr;
+	int i;
+
+	ctlr = edev->ctlr;
+
+	ilock(&ctlr->tlock);
+
+	pcisetbme(ctlr->pci);
+
+	reset(ctlr);
+
+	iow8(ctlr, Eecsr, ior8(ctlr, Eecsr) | EeAutoLoad);
+	for (i = 0; i < Nwait; ++i) {
+		if ((ior8(ctlr, Eecsr) & EeAutoLoad) == 0)
+			break;
+		delay(5);
+	}
+	if (i == Nwait)
+		iprint("etherrhine: eeprom autoload timeout\n");
+
+	for (i = 0; i < Eaddrlen; ++i)
+		edev->ea[i] = ior8(ctlr, Eaddr + i);
+
+	ctlr->mii.mir = miiread;
+	ctlr->mii.miw = miiwrite;
+	ctlr->mii.ctlr = ctlr;
+
+	if(mii(&ctlr->mii, ~0) == 0 || ctlr->mii.curphy == nil){
+		iprint("etherrhine: init mii failure\n");
+		return;
+	}
+	for (i = 0; i < NMiiPhy; ++i)
+		if (ctlr->mii.phy[i])
+			if (ctlr->mii.phy[i]->oui != 0xFFFFF)
+				ctlr->mii.curphy = ctlr->mii.phy[i];
+
+	miistatus(&ctlr->mii);
+
+	iow16(ctlr, Imr, 0);
+	iow16(ctlr, Cr, ior16(ctlr, Cr) | Stop);
+
+	iunlock(&ctlr->tlock);
+}
+
+static Pcidev *
+rhinematch(ulong)
+{
+	static int nrhines = 0;
+	int nfound = 0;
+	Pcidev *p = nil;
+
+	while (p = pcimatch(p, 0x1106, 0))
+		if (p->did == 0x3065)
+			if (++nfound > nrhines) {
+				nrhines++;
+				break;
+			}
+	return p;
+}
+
+int
+rhinepnp(Ether *edev)
+{
+	Pcidev *p;
+	Ctlr *ctlr;
+	ulong port;
+
+	p = rhinematch(edev->port);
+	if (p == nil)
+		return -1;
+
+	port = p->mem[0].bar & ~1;
+
+	if ((ctlr = malloc(sizeof(Ctlr))) == nil) {
+		print("etherrhine: couldn't allocate memory for ctlr\n");
+		return -1;
+	}
+	memset(ctlr, 0, sizeof(Ctlr));
+	ctlr->txd = xspanalloc(sizeof(Desc) * Ntxd, 16, 0);
+	ctlr->rxd = xspanalloc(sizeof(Desc) * Nrxd, 16, 0);
+		
+	ctlr->pci = p;
+	ctlr->port = port;
+
+	edev->ctlr = ctlr;
+	edev->port = ctlr->port;
+	edev->irq = p->intl;
+	edev->tbdf = p->tbdf;
+
+	init(edev);
+
+
+	edev->attach = attach;
+	edev->transmit = transmit;
+	edev->interrupt = interrupt;
+	edev->detach = detach;
+
+	return 0;
+}

+ 2 - 0
sys/src/boot/pc/mkfile

@@ -61,6 +61,8 @@ ETHER=\
 	ether8390.$O\
 	etherec2t.$O\
 	etherelnk3.$O\
+	ethermii.$O\
+	etherrhine.$O\
 
 BCOM=\
 	bcom.$O\

+ 12 - 11
sys/src/cmd/fossil/9user.c

@@ -393,8 +393,8 @@ usersFileWrite(Ubox* box)
 	r = 0;
 	if((dir = fileOpen(fs, "/active")) == nil)
 		goto tidy0;
-	if((file = fileWalk(dir, "adm")) == nil)
-		file = fileCreate(dir, "adm", ModeDir|0775, uidadm);
+	if((file = fileWalk(dir, uidadm)) == nil)
+		file = fileCreate(dir, uidadm, ModeDir|0775, uidadm);
 	fileDecRef(dir);
 	if(file == nil)
 		goto tidy;
@@ -517,7 +517,7 @@ uboxInit(char* users, int len)
 {
 	User *g, *u;
 	Ubox *box, *obox;
-	int blank, comment, i, nuser;
+	int blank, comment, i, nline, nuser;
 	char *buf, *f[5], **line, *p, *q, *s;
 
 	/*
@@ -526,7 +526,7 @@ uboxInit(char* users, int len)
 	 * when the server writes the database back out.
 	 */
 	blank = 1;
-	comment = nuser = 0;
+	comment = nline = 0;
 
 	s = p = buf = vtMemAlloc(len+1);
 	for(q = users; *q != '\0'; q++){
@@ -536,7 +536,7 @@ uboxInit(char* users, int len)
 			if(!blank){
 				if(p != s){
 					*p++ = '\n';
-					nuser++;
+					nline++;
 					s = p;
 				}
 				blank = 1;
@@ -552,16 +552,14 @@ uboxInit(char* users, int len)
 	}
 	*p = '\0';
 
-	line = vtMemAllocZ((nuser+2)*sizeof(char*));
-	if((i = gettokens(buf, line, nuser+2, "\n")) != nuser){
-		fprint(2, "nuser %d (%d) botch\n", nuser, i);
+	line = vtMemAllocZ((nline+2)*sizeof(char*));
+	if((i = gettokens(buf, line, nline+2, "\n")) != nline){
+		fprint(2, "nline %d (%d) botch\n", nline, i);
 		vtMemFree(line);
 		vtMemFree(buf);
 		return 0;
 	}
 
-//	fprint(2, "nuser %d\n", nuser);
-
 	/*
 	 * Everything is updated in a local Ubox until verified.
 	 */
@@ -571,7 +569,8 @@ uboxInit(char* users, int len)
 	 * First pass - check format, check for duplicates
 	 * and enter in hash buckets.
 	 */
-	for(i = 0; i < nuser; i++){
+	nuser = 0;
+	for(i = 0; i < nline; i++){
 		s = vtStrDup(line[i]);
 		if(getfields(s, f, nelem(f), 0, ":") != 4){
 			fprint(2, "bad line '%s'\n", line[i]);
@@ -606,6 +605,8 @@ uboxInit(char* users, int len)
 
 		u = userAlloc(f[0], f[1]);
 		uboxAddUser(box, u);
+		line[nuser] = line[i];
+		nuser++;
 
 		vtMemFree(s);
 	}

+ 30 - 3
sys/src/cmd/rx.c

@@ -3,6 +3,8 @@
 #include <auth.h>
 
 int	eof;		/* send an eof if true */
+int	crtonl;		/* convert all received \r to \n */
+int	returns;	/* strip \r on reception */
 char	*note = "die: yankee dog";
 char	*ruser;
 char *key;
@@ -18,7 +20,7 @@ void	sshexec(char *host, char *cmd);
 void
 usage(void)
 {
-	fprint(2, "usage: %s [-e] [-k keypattern] [-l user] net!host command...\n", argv0);
+	fprint(2, "usage: %s [-e] [-T] [-r] [-k keypattern] [-l user] net!host command...\n", argv0);
 	exits("usage");
 }
 
@@ -30,7 +32,15 @@ main(int argc, char *argv[])
 
 	key = "";
 	eof = 1;
+	crtonl = 0;
+	returns = 1;
 	ARGBEGIN{
+	case 'T':
+		crtonl = 1;
+		break;
+	case 'r':
+		returns = 0;
+		break;
 	case 'e':
 		eof = 0;
 		break;
@@ -110,7 +120,7 @@ rex(int fd, char *cmd, char *proto)
 void
 tcpexec(int fd, char *addr, char *cmd)
 {
-	char *u, buf[4096];
+	char *cp, *ep, *u, buf[4096];
 	int kid, n;
 	char *r;
 
@@ -143,9 +153,26 @@ tcpexec(int fd, char *addr, char *cmd)
 	}
 
 	kid = send(fd);
-	while((n=read(fd, buf, sizeof buf))>0)
+	while((n=read(fd, buf, sizeof buf))>0){
+		if(crtonl) {
+			/* convert cr's to nl's */
+			for (cp = buf; cp < buf + n; cp++)
+				if (*cp == '\r')
+					*cp = '\n';
+		}
+		else if(!returns){
+			/* convert cr's to null's */
+			cp = buf;
+			ep = buf + n;
+			while(cp < ep && (cp = memchr(cp, '\r', ep-cp))){
+				memmove(cp, cp+1, ep-cp-1);
+				ep--;
+				n--;
+			}
+		}
 		if(write(1, buf, n)!=n)
 			error("write error", 0);
+	}
 	sleep(250);
 	postnote(PNPROC, kid, note);/**/
 	exits(0);