~kris/9p

9hist

6a9c500e0bfd986687fcbd9ce7cd60d262d3b951 — David du Colombier 32 years ago a726f80
Plan 9 from Bell Labs 1994-09-02
M carrera/clock.c => carrera/clock.c +1 -0
@@ 91,6 91,7 @@ clock(Ureg *ur)
	}

	checkalarms();
	uartclock();
	mouseclock();

	if(up == 0 || up->state != Running)

M carrera/fns.h => carrera/fns.h +4 -3
@@ 96,6 96,7 @@ void		vector0(void);
void		vector100(void);
void		vector180(void);
void		vmereset(void);
void		uartclock(void);
void		uvst(void*, void*);
void		uvld(void*, void*);
void		wbflush(void);


@@ 103,9 104,9 @@ void		wrcompare(ulong);
void		Xdelay(int);

void		serialinit(void);
void		NS16552special(int, int, Queue**, Queue**, int (*)(Queue*, int));
void		NS16552setup(ulong, ulong);
void		NS16552intr(int);
void		ns16552special(int, int, Queue**, Queue**, int (*)(Queue*, int));
void		ns16552setup(ulong, ulong);
void		ns16552intr(int);
void		etherintr(void);
void		iomapinit(void);
void		enetaddr(uchar*);

M carrera/kbd.c => carrera/kbd.c +1 -0
@@ 354,6 354,7 @@ kbdinit(void)
	int i;

	kbdq = qopen(4*1024, 0, 0, 0);
	qnoblock(kbdq, 1);

	/*
	 *  empty the buffer

M carrera/main.c => carrera/main.c +1 -1
@@ 163,7 163,7 @@ machinit(void)
void
serialinit(void)
{
	NS16552setup(Uart1, UartFREQ);
	ns16552install();
	kbdinit();
}


A carrera/ns16552.h => carrera/ns16552.h +41 -0
@@ 0,0 1,41 @@
#define outb(p, ch)			*(uchar*)((p)^7) = ch	
#define uartwrreg(u,r,v)	outb((u)->port + (r), (u)->sticky[r] | (v))
#define uartrdreg(u,r)		*(uchar*)(((u)->port + (r))^7)

#define uartpower(x, y)

void
ns16552install(void)
{
	static int already;

	if(already)
		return;
	already = 1;

	ns16552setup(Uart1, UartFREQ);
}

#define RD(r)	(*(uchar*)((Uart1+r)^7))
static void
ns16552iputc(char c)
{
	while((RD(5) & (1<<5)) == 0)
		;
	*(uchar*)(Uart1^7) = c;
	while((RD(5) & (1<<5)) == 0)
		;
}

int
iprint(char *fmt, ...)
{
	int n, i;
	char buf[512];

	n = doprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
	for(i = 0; i < n; i++)
		ns16552iputc(buf[i]);
	
	return n;
}

M carrera/trap.c => carrera/trap.c +2 -2
@@ 298,10 298,10 @@ intr(Ureg *ur)
		default:
			panic("unknown devint=#%lux", devint);
		case 0x28:		/* Serial 1 */
			NS16552intr(0);
			ns16552intr(0);
			break;
		case 0x24:		/* Serial 2 */
			NS16552intr(1);
			ns16552intr(1);
			break;
		case 0x10:
			fiberint(0);

M pc/fault386.c => pc/fault386.c +1 -3
@@ 24,7 24,6 @@ fault386(Ureg *ur, void *arg)
	read = !(ur->ecode & 2);
	user = (ur->cs&0xffff) == UESEL;
	spllo();
/*print("F%d:A#%lux:U%d:R%d|", up->pid, addr, user, read);/**/
	n = fault(addr, read);
	if(n < 0){
		if(user){


@@ 33,9 32,8 @@ fault386(Ureg *ur, void *arg)
			postnote(up, 1, buf, NDebug);
			return;
		}
print("fault: 0x%lux", addr);
		dumpregs(ur);
		panic("fault: 0x%lux", addr);
		panic("fault: 0x%lux\n", addr);
	}
	up->insyscall = insyscall;
}

M pc/fns.h => pc/fns.h +1 -3
@@ 97,9 97,7 @@ int	x86(void);
#define KADDR(a)	((void*)((ulong)(a)|KZERO))
#define PADDR(a)	((ulong)(a)&~KZERO)

void	NS16552special(int, int, Queue**, Queue**, int (*)(Queue*, int));
int	NS16552m3mouse(Queue*, int);
int	NS16552mouse(Queue*, int);
void	ns16552special(int, int, Queue**, Queue**, int (*)(Queue*, int));

void	hnputl(void*, ulong v);
void	hnputs(void*, ushort v);

M pc/kbd.c => pc/kbd.c +3 -2
@@ 254,9 254,9 @@ serialmouse(int port, char *type, int setspeed)
	if(setspeed)
		setspeed = 1200;
	if(type && *type == 'M')
		NS16552special(port, setspeed, &mouseq, 0, m3mouseputc);
		ns16552special(port, setspeed, &mouseq, 0, m3mouseputc);
	else
		NS16552special(port, setspeed, &mouseq, 0, mouseputc);
		ns16552special(port, setspeed, &mouseq, 0, mouseputc);
	mousetype = Mouseserial;
}



@@ 597,6 597,7 @@ kbdinit(void)
	int c;

	kbdq = qopen(4*1024, 0, 0, 0);
	qnoblock(kbdq, 1);

	setvec(Kbdvec, kbdintr, 0);


A pc/ns16552.h => pc/ns16552.h +147 -0
@@ 0,0 1,147 @@
/*
 *  PC specific code for the ns16552.  It includes support for the 2 built
 *  in uarts plus up to 5 MP-008 8 uart ISA cards.
 */
enum
{
	Maxcard= 5,		/* max serial cards */
	UartFREQ= 1843200,

	Serial=	0,
	Modem=	1,
};

#define uartwrreg(u,r,v)	outb((u)->port + r, (u)->sticky[r] | (v))
#define uartrdreg(u,r)		inb((u)->port + r)

void	ns16552setup(ulong, ulong);

/*
 *  definition of an optional serial card
 */
typedef struct Scard
{
	ISAConf;	/* card configuration */
	int	first;	/* number of first port */
} Scard;
static Scard *scard[Maxcard]; 	/* configs for the serial card */

/* power management currently only makes sense on the AT&T safari */
static void
uartpower(int dev, int onoff)
{
	switch(dev){
	case Modem:
		if(modem(onoff) < 0)
			print("can't turn %s modem speaker\n", onoff?"on":"off");
		break;
	case Serial:
		if(serial(onoff) < 0)
			print("can't turn %s serial port power\n", onoff?"on":"off");
		break;
	}
}

/*
 *  handle an interrupt to a single uart
 */
static void
ns16552intrx(Ureg *ur, void *arg)
{
	USED(ur);

	ns16552intr((ulong)arg);
}

/*
 *  interrupts from the multiport card, MP-008.  A polling port
 *  tells which of 8 devices interrupted.
 */
static void
mp008intr(Ureg *ur, void *arg)
{
	int i, loops;
	uchar n;
	Scard *mp;

	USED(ur);
	mp = arg;
	for(loops = 0;; loops++){
		if(loops > 1024)
			panic("mp008intr");
		n = ~inb(mp->mem);
		if(n == 0)
			return;
		for(i = 0; n; i++){
			if(n & 1)
				ns16552intrx(ur, uart[mp->first+i]);
			n >>= 1;
		}
	}
}

/*
 *  install the uarts (called by reset)
 */
static void
ns16552install(void)
{
	int i, j, port;
	Scard *sc;

	/* first two ports are always there and always the normal frequency */
	ns16552setup(0x3F8, UartFREQ);
	setvec(Uart0vec, ns16552intrx, (void*)0);
	ns16552setup(0x2F8, UartFREQ);
	setvec(Uart1vec, ns16552intrx, (void*)1);

	/* the rest come out of plan9.ini */
	for(i = 0; i < Maxcard; i++){
		sc = scard[i] = xalloc(sizeof(Scard));
		if(isaconfig("serial", i, sc) == 0){
			xfree(sc);
			break;
		}

		if(strcmp(sc->type, "MP008") == 0 || strcmp(sc->type, "mp008") == 0){
			/*
			 *  port gives base port address for uarts
			 *  irq is interrupt
			 *  mem is the polling port
			 *  size is the number of serial ports on the same polling port
			 *  freq is the baud rate generator frequency
			 */
			if(sc->size == 0)
				sc->size = 8;
			if(sc->freq == 0)
				sc->freq = UartFREQ;
			sc->first = nuart;
			setvec(Int0vec+sc->irq, mp008intr, sc);
			port = sc->port;
			for(j=0; j < sc->size; j++){
				ns16552setup(port, sc->freq);
				port += 8;
			}
		} else {
			/*
			 *  port gives base port address for the uart
			 *  irq is interrupt
			 *  freq is the baud rate generator frequency
			 */
			ns16552setup(sc->port, sc->freq);
			setvec(Int0vec+sc->irq, ns16552intrx, (void*)(nuart-1));
		}
	}
}

int
iprint(char *fmt, ...)
{
	char buf[PRINTSIZE];
	int n;

	n = doprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
	screenputs(buf, n);

	return n;
}

M pc/trap.c => pc/trap.c +1 -0
@@ 346,6 346,7 @@ dumpregs(Ureg *ur)

	x = (ulong*)(ur+1);
	dumpregs2(ur);
	print("  CR0 %8.8lux CR2 %8.8lux\n", getcr0(), getcr2());
	print("  magic %lux %lux %lux\n", x[0], x[1], x[2]);
	print("  ur %lux up %lux\n", ur, up);
}

M port/devcons.c => port/devcons.c +7 -5
@@ 34,7 34,9 @@ void
printinit(void)
{
	klogq = qopen(32*1024, 0, 0, 0);
	qnoblock(klogq, 1);
	lineq = qopen(2*1024, 0, 0, 0);
	qnoblock(lineq, 1);
}

/*


@@ 73,7 75,7 @@ putstrn0(char *str, int n, int usewrite)
			buf[m] = '\r';
			buf[m+1] = '\n';
			if(usewrite)
				qwrite(printq, buf, m+2, 0);
				qwrite(printq, buf, m+2);
			else {
				x = splhi();
				qproduce(printq, buf, m+2);


@@ 83,7 85,7 @@ putstrn0(char *str, int n, int usewrite)
			n -= m + 1;
		} else {
			if(usewrite)
				qwrite(printq, str, n, 0);
				qwrite(printq, str, n);
			else {
				x = splhi();
				qproduce(printq, str, n);


@@ 138,7 140,7 @@ kprint(char *fmt, ...)
	int n;

	n = doprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
	qwrite(klogq, buf, n, 1);
	qwrite(klogq, buf, n);
	return n;
}



@@ 540,7 542,7 @@ consread(Chan *c, void *buf, long n, ulong offset)
			if(kbd.x == sizeof(kbd.line) || eol){
				if(ch == 0x04)
					kbd.x--;
				qwrite(lineq, kbd.line, kbd.x, 1);
				qwrite(lineq, kbd.line, kbd.x);
				kbd.x = 0;
			}
		}


@@ 743,7 745,7 @@ conswrite(Chan *c, void *va, long n, ulong offset)
			if(strncmp(a, "rawon", 5) == 0){
				qlock(&kbd);
				if(kbd.x){
					qwrite(kbdq, kbd.line, kbd.x, 1);
					qwrite(kbdq, kbd.line, kbd.x);
					kbd.x = 0;
				}
				kbd.raw = 1;

A port/devns16552.c => port/devns16552.c +895 -0
@@ 0,0 1,895 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"

#include	"devtab.h"
#include	"../port/netif.h"

/*
 *  Driver for the ns16552.
 */
enum
{
	/*
	 *  register numbers
	 */
	Data=	0,		/* xmit/rcv buffer */
	Iena=	1,		/* interrupt enable */
	 Ircv=	(1<<0),		/*  for char rcv'd */
	 Ixmt=	(1<<1),		/*  for xmit buffer empty */
	 Irstat=(1<<2),		/*  for change in rcv'er status */
	 Imstat=(1<<3),		/*  for change in modem status */
	Istat=	2,		/* interrupt flag (read) */
	 Fenabd=(3<<6),		/*  on if fifo's enabled */
	Fifoctl=2,		/* fifo control (write) */
	 Fena=	(1<<0),		/*  enable xmit/rcv fifos */
	 Ftrig=	(1<<6),		/*  trigger after 4 input characters */
	 Fclear=(3<<1),		/*  clear xmit & rcv fifos */
	Format=	3,		/* byte format */
	 Bits8=	(3<<0),		/*  8 bits/byte */
	 Stop2=	(1<<2),		/*  2 stop bits */
	 Pena=	(1<<3),		/*  generate parity */
	 Peven=	(1<<4),		/*  even parity */
	 Pforce=(1<<5),		/*  force parity */
	 Break=	(1<<6),		/*  generate a break */
	 Dra=	(1<<7),		/*  address the divisor */
	Mctl=	4,		/* modem control */
	 Dtr=	(1<<0),		/*  data terminal ready */
	 Rts=	(1<<1),		/*  request to send */
	 Ri=	(1<<2),		/*  ring */
	 Inton=	(1<<3),		/*  turn on interrupts */
	 Loop=	(1<<4),		/*  loop back */
	Lstat=	5,		/* line status */
	 Inready=(1<<0),	/*  receive buffer full */
	 Oerror=(1<<1),		/*  receiver overrun */
	 Perror=(1<<2),		/*  receiver parity error */
	 Ferror=(1<<3),		/*  rcv framing error */
	 Outready=(1<<5),	/*  output buffer full */
	Mstat=	6,		/* modem status */
	 Ctsc=	(1<<0),		/*  clear to send changed */
	 Dsrc=	(1<<1),		/*  data set ready changed */
	 Rire=	(1<<2),		/*  rising edge of ring indicator */
	 Dcdc=	(1<<3),		/*  data carrier detect changed */
	 Cts=	(1<<4),		/*  complement of clear to send line */
	 Dsr=	(1<<5),		/*  complement of data set ready line */
	 Ring=	(1<<6),		/*  complement of ring indicator line */
	 Dcd=	(1<<7),		/*  complement of data carrier detect line */
	Scratch=7,		/* scratchpad */
	Dlsb=	0,		/* divisor lsb */
	Dmsb=	1,		/* divisor msb */

	CTLS= 023,
	CTLQ= 021,

	Stagesize= 1024,
	Nuart=	32,		/* max per machine */
};

typedef struct Uart Uart;
struct Uart
{
	QLock;

	Uart	*elist;		/* next enabled interface */

	uchar	sticky[8];	/* sticky write register values */
	ulong	port;
	Lock	plock;		/* for printing variable */
	int	printing;	/* true if printing */
	ulong	freq;		/* clock frequency */
	int	opens;
	uchar	mask;		/* bits/char */
	uchar	istat;		/* last istat read */
	uchar	fifoon;		/* fifo's enabled */
	uchar	nofifo;		/* earlier chip version with nofifo */
	int	enabled;
	int	dev;

	int	frame;
	int	overrun;

	/* flow control */
	int	xonoff;		/* software flow control on */
	int	blocked;
	int	modem;		/* hardware flow control on */
	int	cts;
	int	ctsbackoff;
	Rendez	r;

	/* buffers */
	int	(*putc)(Queue*, int);
	Queue	*iq;
	Queue	*oq;

	/* staging areas to avoid some of the per character costs */
	uchar	istage[Stagesize];
	uchar	*ip;
	uchar	*ie;

	uchar	ostage[Stagesize];
	uchar	*op;
	uchar	*oe;
};

static Uart *uart[Nuart];
static int nuart;

static int haveinput;
struct Uartalloc {
	Lock;
	Uart *elist;	/* list of enabled interfaces */
} uartalloc;

void ns16552intr(int);

/*
 *  pick up architecture specific routines and definitions
 */
#include "ns16552.h"

/*
 *  set the baud rate by calculating and setting the baudrate
 *  generator constant.  This will work with fairly non-standard
 *  baud rates.
 */
static void
ns16552setbaud(Uart *p, int rate)
{
	ulong brconst;

	brconst = (p->freq+8*rate-1)/(16*rate);

	uartwrreg(p, Format, Dra);
	outb(p->port + Dmsb, (brconst>>8) & 0xff);
	outb(p->port + Dlsb, brconst & 0xff);
	uartwrreg(p, Format, 0);
}

static void
ns16552parity(Uart *p, char type)
{
	switch(type){
	case 'e':
		p->sticky[Format] |= Pena|Peven;
		break;
	case 'o':
		p->sticky[Format] &= ~Peven;
		p->sticky[Format] |= Pena;
		break;
	default:
		p->sticky[Format] &= ~(Pena|Peven);
		break;
	}
	uartwrreg(p, Format, 0);
}

/*
 *  set bits/character, default 8
 */
void
ns16552bits(Uart *p, int bits)
{
	if(bits < 5 || bits > 8)
		error(Ebadarg);

	p->sticky[Format] &= ~3;
	p->sticky[Format] |= bits-5;

	uartwrreg(p, Format, 0);
}


/*
 *  toggle DTR
 */
void
ns16552dtr(Uart *p, int n)
{
	if(n)
		p->sticky[Mctl] |= Dtr;
	else
		p->sticky[Mctl] &= ~Dtr;

	uartwrreg(p, Mctl, 0);
}

/*
 *  toggle RTS
 */
void
ns16552rts(Uart *p, int n)
{
	if(n)
		p->sticky[Mctl] |= Rts;
	else
		p->sticky[Mctl] &= ~Rts;

	uartwrreg(p, Mctl, 0);
}

/*
 *  send break
 */
void
ns16552break(Uart *p, int ms)
{
	if(ms == 0)
		ms = 200;

	uartwrreg(p, Format, Break);
	tsleep(&up->sleep, return0, 0, ms);
	uartwrreg(p, Format, 0);
}

void
ns16552fifoon(Uart *p)
{
	ulong i, x, c;

	if(p->nofifo)
		return;

	x = splhi();

	/* empty buffer */
	c = 0;				/* c is to to avoid an unused warning */
	for(i = 0; i < 16; i++)
		c = uartrdreg(p, Data);
	USED(c);
	ns16552intr(p->dev);

	/* turn on fifo */
	p->fifoon = 1;
	uartwrreg(p, Fifoctl, Fena|Ftrig);

	ns16552intr(p->dev);
	USED(c);
	if((p->istat & Fenabd) == 0){
		/* didn't work, must be an earlier chip type */
		p->nofifo = 1;
	}
		
	splx(x);
}

/*
 *  modem flow control on/off (rts/cts)
 */
void
ns16552mflow(Uart *p, int n)
{
	if(n){
		p->sticky[Iena] |= Imstat;
		uartwrreg(p, Iena, 0);
		p->modem = 1;
		p->cts = uartrdreg(p, Mstat) & Cts;

		/* turn on fifo's */
		ns16552fifoon(p);
	} else {
		p->sticky[Iena] &= ~Imstat;
		uartwrreg(p, Iena, 0);
		p->modem = 0;
		p->cts = 1;

		/* turn off fifo's */
		p->fifoon = 0;
		uartwrreg(p, Fifoctl, 0);
	}
}

/*
 *  turn on a port's interrupts.  set DTR and RTS
 */
void
ns16552enable(Uart *p)
{
	Uart **l;

	if(p->enabled)
		return;

	uartpower(p->dev, 1);

	/*
 	 *  turn on interrupts
	 */
	p->sticky[Iena] = Ircv | Ixmt | Irstat;
	uartwrreg(p, Iena, 0);

	/*
	 *  turn on DTR and RTS
	 */
	ns16552dtr(p, 1);
	ns16552rts(p, 1);

	/*
	 *  assume we can send
	 */
	p->cts = 1;
	p->blocked = 0;

	lock(&uartalloc);
	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
		if(*l == p)
			break;
	}
	if(*l == 0){
		p->elist = uartalloc.elist;
		uartalloc.elist = p;
	}
	p->enabled = 1;
	unlock(&uartalloc);
}

/*
 *  turn off a port's interrupts.  reset DTR and RTS
 */
void
ns16552disable(Uart *p)
{
	Uart **l;

	/*
 	 *  turn off interrpts
	 */
	p->sticky[Iena] = 0;
	uartwrreg(p, Iena, 0);

	/*
	 *  revert to default settings
	 */
	p->sticky[Format] = Bits8;
	uartwrreg(p, Format, 0);

	/*
	 *  turn off DTR, RTS, hardware flow control & fifo's
	 */
	ns16552dtr(p, 0);
	ns16552rts(p, 0);
	ns16552mflow(p, 0);
	p->xonoff = p->blocked = 0;

	uartpower(p->dev, 0);

	lock(&uartalloc);
	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
		if(*l == p){
			*l = p->elist;
			break;
		}
	}
	p->enabled = 0;
	unlock(&uartalloc);
}

/*
 *  put some bytes into the local queue to avoid calling
 *  qconsume for every character
 */
static int
stageoutput(Uart *p)
{
	int n;

	n = qconsume(p->oq, p->ostage, Stagesize);
	if(n <= 0)
		return 0;
	p->op = p->ostage;
	p->oe = p->ostage + n;
	return n;
}

/*
 *  (re)start output
 */
static void
ns16552kick(Uart *p)
{
	int x, n;

	x = splhi();
	lock(&p->plock);
	if(p->printing == 0 || (uartrdreg(p, Lstat) & Outready))
	if(p->cts && p->blocked == 0){
		n = 0;
		while((uartrdreg(p, Lstat) & Outready) == 0){
			if(++n > 10000){
				print("stuck serial line\n");
				break;
			}
		}
		do{
			if(p->op >= p->oe && stageoutput(p) == 0)
				break;
			outb(p->port + Data, *(p->op++));
		}while(uartrdreg(p, Lstat) & Outready);
	}
	unlock(&p->plock);
	splx(x);
}

/*
 *  default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts,
 *  transmit and receive enabled, interrupts disabled.
 */
static void
ns16552setup0(Uart *p)
{
	memset(p->sticky, 0, sizeof(p->sticky));
	/*
	 *  set rate to 9600 baud.
	 *  8 bits/character.
	 *  1 stop bit.
	 *  interrpts enabled.
	 */
	p->sticky[Format] = Bits8;
	uartwrreg(p, Format, 0);
	p->sticky[Mctl] |= Inton;
	uartwrreg(p, Mctl, 0x0);

	ns16552setbaud(p, 9600);

	p->iq = qopen(4*1024, 0, 0, 0);
	p->oq = qopen(4*1024, 0, ns16552kick, p);

	p->ip = p->istage;
	p->ie = &p->istage[Stagesize];
	p->op = p->ostage;
	p->oe = p->ostage;
}

/*
 *  called by main() to create a new duart
 */
void
ns16552setup(ulong port, ulong freq)
{
	Uart *p;

	if(nuart >= Nuart)
		return;

	p = xalloc(sizeof(Uart));
	uart[nuart] = p;
	p->dev = nuart;
	nuart++;
	p->port = port;
	p->freq = freq;
	ns16552setup0(p);
}

/*
 *  called by main() to configure a duart port as a console or a mouse
 */
void
ns16552special(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int))
{
	Uart *p = uart[port];

	ns16552enable(p);
	if(baud)
		ns16552setbaud(p, baud);
	p->putc = putc;
	if(in)
		*in = p->iq;
	if(out)
		*out = p->oq;
	p->opens++;
}

/*
 *  handle an interrupt to a single uart
 */
void
ns16552intr(int dev)
{
	uchar ch;
	int s, l, loops;
	Uart *p = uart[dev];

	for(loops = 0;; loops++){
		if(loops > 100000)
			panic("ns16552intr");
		p->istat = s = uartrdreg(p, Istat);
		switch(s&0x3f){
		case 6:	/* receiver line status */
			l = uartrdreg(p, Lstat);
			if(l & Ferror)
				p->frame++;
			if(l & Oerror)
				p->overrun++;
			break;
	
		case 4:	/* received data available */
		case 12:
			ch = uartrdreg(p, Data) & 0xff;
			if(p->xonoff){
				if(ch == CTLS){
					p->blocked = 1;
				}else if (ch == CTLQ){
					p->blocked = 0;
					 /* clock gets output going again */
				}
			}
			if(p->putc)
				(*p->putc)(p->iq, ch);
			else {
				if(p->ip < p->ie)
					*p->ip++ = ch;
				haveinput = 1;
			}
			break;
	
		case 2:	/* transmitter not full */
			lock(&p->plock);
			l = uartrdreg(p, Lstat) & Outready;
			if(l && p->cts && p->blocked == 0)
			if(p->op < p->oe || stageoutput(p)){
				do{
					outb(p->port + Data, *(p->op++));
					if(p->op >= p->oe && stageoutput(p) == 0)
						break;
					l = uartrdreg(p, Lstat) & Outready;
				}while(l);
			}
			if(l)
				p->printing = 0;
			unlock(&p->plock);
			break;
	
		case 0:	/* modem status */
			ch = uartrdreg(p, Mstat);
			if(ch & Ctsc){
				p->cts = ch & Cts; /* clock gets output going again */
				p->ctsbackoff += 1;
			}
			break;
	
		default:
			if(s&1)
				return;
			print("weird modem interrupt #%2.2ux\n", s);
			break;
		}
	}
}

/*
 *  we save up input characters till clock time
 *
 *  There's also a bit of code to get a stalled print going.
 *  It shouldn't happen, but it does.  Obviously I don't
 *  understand interrupts. -- presotto
 */
void
uartclock(void)
{
	int n;
	Uart *p;

	for(p = uartalloc.elist; p; p = p->elist){
		ns16552intr(p->dev);
		if(haveinput){
			n = p->ip - p->istage;
			if(n > 0 && p->iq){
				if(n > Stagesize)
					panic("uartclock");
				if(qproduce(p->iq, p->istage, n) != n)
					;
				p->ip = p->istage;
			}
		}
		if(p->ctsbackoff-- < 0){
			p->ctsbackoff = 0;
			ns16552kick(p);
		}
	}
	haveinput = 0;
}

Dirtab *ns16552dir;

/*
 *  all uarts must be ns16552setup() by this point or inside of ns16552install()
 */
void
ns16552reset(void)
{
	int i;
	Dirtab *dp;

	ns16552install();	/* architecture specific */

	ns16552dir = xalloc(3 * nuart * sizeof(Dirtab));
	dp = ns16552dir;
	for(i = 0; i < nuart; i++){
		/* 3 directory entries per port */
		sprint(dp->name, "eia%d", i);
		dp->qid.path = NETQID(i, Ndataqid);
		dp->perm = 0666;
		dp++;
		sprint(dp->name, "eia%dctl", i);
		dp->qid.path = NETQID(i, Nctlqid);
		dp->perm = 0666;
		dp++;
		sprint(dp->name, "eia%dstat", i);
		dp->qid.path = NETQID(i, Nstatqid);
		dp->perm = 0444;
		dp++;
	}
}

void
ns16552init(void)
{
}

Chan*
ns16552attach(char *spec)
{
	return devattach('t', spec);
}

Chan*
ns16552clone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

int
ns16552walk(Chan *c, char *name)
{
	return devwalk(c, name, ns16552dir, 3*nuart, devgen);
}

void
ns16552stat(Chan *c, char *dp)
{
	int i;
	Uart *p;
	Dir dir;

	i = NETID(c->qid.path);
	switch(NETTYPE(c->qid.path)){
	case Ndataqid:
		p = uart[i];
		devdir(c, c->qid, ns16552dir[3*i].name, qlen(p->iq), eve, 0660, &dir);
		convD2M(&dir, dp);
		break;
	default:
		devstat(c, dp, ns16552dir, 3*nuart, devgen);
		break;
	}
}

Chan*
ns16552open(Chan *c, int omode)
{
	Uart *p;

	if(c->qid.path & CHDIR){
		if(omode != OREAD)
			error(Ebadarg);
	} else if(NETTYPE(c->qid.path) == Nstatqid){
		;
	} else {
		p = uart[NETID(c->qid.path)];
		qlock(p);
		p->opens++;
		ns16552enable(p);
		qreopen(p->iq);
		qreopen(p->oq);
		qunlock(p);
	}

	c->mode = omode&~OTRUNC;
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

void
ns16552create(Chan *c, char *name, int omode, ulong perm)
{
	USED(c, name, omode, perm);
	error(Eperm);
}

void
ns16552close(Chan *c)
{
	Uart *p;

	if(c->qid.path & CHDIR)
		return;
	if(NETTYPE(c->qid.path) == Nstatqid)
		return;

	p = uart[NETID(c->qid.path)];
	if(c->flag & COPEN){
		qlock(p);
		p->opens--;
		if(p->opens == 0){
			ns16552disable(p);
			qclose(p->iq);
			qclose(p->oq);
			p->ip = p->istage;
		}
		qunlock(p);
	}
}

static long
uartstatus(Chan *c, Uart *p, void *buf, long n, long offset)
{
	uchar mstat;
	uchar tstat;
	char str[256];
	int i;

	str[0] = 0;
	tstat = p->sticky[Mctl];
	mstat = uartrdreg(p, Mstat);
	i = sprint(str, "%d printing %d cts %d qlen %d/%d", NETID(c->qid.path),
		p->printing, p->cts, qlen(p->iq), qlen(p->oq));
	sprint(str+i, " lstat %ux mstat %ux istat %ux ier %ux ferr %d oerr %d",
		uartrdreg(p, Lstat), mstat, uartrdreg(p, Istat), uartrdreg(p, Iena),
		p->frame, p->overrun);
	if(mstat & Cts)
		strcat(str, " cts");
	if(mstat & Dsr)
		strcat(str, " dsr");
	if(mstat & Ring)
		strcat(str, " ring");
	if(mstat & Dcd)
		strcat(str, " dcd");
	if(tstat & Dtr)
		strcat(str, " dtr");
	if(tstat & Rts)
		strcat(str, " rts");
	strcat(str, "\n");
	return readstr(offset, buf, n, str);
}

long
ns16552read(Chan *c, void *buf, long n, ulong offset)
{
	Uart *p;

	if(c->qid.path & CHDIR)
		return devdirread(c, buf, n, ns16552dir, 3*nuart, devgen);

	p = uart[NETID(c->qid.path)];
	switch(NETTYPE(c->qid.path)){
	case Ndataqid:
		return qread(p->iq, buf, n);
	case Nctlqid:
		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
	case Nstatqid:
		return uartstatus(c, p, buf, n, offset);
	}

	return 0;
}

static void
ns16552ctl(Uart *p, char *cmd)
{
	int i, n;

	/* let output drain for a while */
	for(i = 0; i < 16 && qlen(p->oq); i++)
		tsleep(&p->r, qlen, p->oq, 125);

	if(strncmp(cmd, "break", 5) == 0){
		ns16552break(p, 0);
		return;
	}
		

	n = atoi(cmd+1);
	switch(*cmd){
	case 'B':
	case 'b':
		ns16552setbaud(p, n);
		break;
	case 'D':
	case 'd':
		ns16552dtr(p, n);
		break;
	case 'H':
	case 'h':
		qhangup(p->iq);
		qhangup(p->oq);
		break;
	case 'L':
	case 'l':
		ns16552bits(p, n);
		break;
	case 'm':
	case 'M':
		ns16552mflow(p, n);
		break;
	case 'n':
	case 'N':
		qnoblock(p->oq, n);
		break;
	case 'P':
	case 'p':
		ns16552parity(p, *(cmd+1));
		break;
	case 'K':
	case 'k':
		ns16552break(p, n);
		break;
	case 'R':
	case 'r':
		ns16552rts(p, n);
		break;
	case 'Q':
	case 'q':
		qsetlimit(p->iq, n);
		qsetlimit(p->oq, n);
		break;
	case 'W':
	case 'w':
		/* obsolete */
		break;
	case 'X':
	case 'x':
		p->xonoff = n;
		break;
	}
}

long
ns16552write(Chan *c, void *buf, long n, ulong offset)
{
	Uart *p;
	char cmd[32];

	USED(offset);

	if(c->qid.path & CHDIR)
		error(Eperm);

	p = uart[NETID(c->qid.path)];

	/*
	 *  The fifo's turn themselves off sometimes.
	 *  It must be something I don't understand. -- presotto
	 */
	if((p->istat & Fenabd) == 0 && p->fifoon && p->nofifo == 0)
		ns16552fifoon(p);

	switch(NETTYPE(c->qid.path)){
	case Ndataqid:
		return qwrite(p->oq, buf, n);
	case Nctlqid:
		if(n >= sizeof(cmd))
			n = sizeof(cmd)-1;
		memmove(cmd, buf, n);
		cmd[n] = 0;
		ns16552ctl(p, cmd);
		return n;
	}
}

void
ns16552remove(Chan *c)
{
	USED(c);
	error(Eperm);
}

void
ns16552wstat(Chan *c, char *dp)
{
	USED(c, dp);
	error(Eperm);
}

M port/devpipe.c => port/devpipe.c +2 -2
@@ 313,11 313,11 @@ pipewrite(Chan *c, void *va, long n, ulong offset)

	switch(NETTYPE(c->qid.path)){
	case Qdata0:
		n = qwrite(p->q[1], va, n, 0);
		n = qwrite(p->q[1], va, n);
		break;

	case Qdata1:
		n = qwrite(p->q[0], va, n, 0);
		n = qwrite(p->q[0], va, n);
		break;

	default:

M port/portfns.h => port/portfns.h +3 -1
@@ 202,7 202,9 @@ long		qread(Queue*, void*, int);
void		qreopen(Queue*);
void		qunlock(QLock*);
int		qwindow(Queue*);
long		qwrite(Queue*, void*, int, int);
long		qwrite(Queue*, void*, int);
void		qsetlimit(Queue*, int);
void		qnoblock(Queue*, int);
int		readnum(ulong, char*, ulong, ulong, int);
int		readstr(ulong, char*, ulong, char*);
void		ready(Proc*);

M port/qio.c => port/qio.c +34 -10
@@ 13,6 13,8 @@ struct

static int debuging;

#define QDEBUG if(0)

/*
 *  IO queues
 */


@@ 27,7 29,9 @@ struct Queue

	int	len;		/* bytes in queue */
	int	limit;		/* max bytes in queue */
	int	inilim;		/* initial limit */
	int	state;
	int	noblock;	/* true if writes return immediately when q full */
	int	eof;		/* number of eofs read by user */

	void	(*kick)(void*);	/* restart output */


@@ 172,7 176,7 @@ qconsume(Queue *q, void *vp, int len)
		unlock(q);
		return -1;
	}
checkb(b, "qconsume 1");
	QDEBUG checkb(b, "qconsume 1");

	n = BLEN(b);
	if(n < len)


@@ 195,7 199,7 @@ checkb(b, "qconsume 1");
	if(dowakeup)
		wakeup(&q->wr);

checkb(b, "qconsume 2");
	QDEBUG checkb(b, "qconsume 2");
	/* discard the block if we're done with it */
	if((q->state & Qmsg) || len == n)
		freeb(b);


@@ 238,7 242,7 @@ qpass(Queue *q, Block *b)
		q->bfirst = b;
	q->blast = b;
	q->len += len;
checkb(b, "qpass");
	QDEBUG checkb(b, "qpass");

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


@@ 308,7 312,7 @@ qproduce(Queue *q, void *vp, int len)
		q->bfirst = b;
	q->blast = b;
	q->len += len;
checkb(b, "qproduce");
	QDEBUG checkb(b, "qproduce");

	if(q->state & Qstarve){
		q->state &= ~Qstarve;


@@ 337,7 341,7 @@ qopen(int limit, int msg, void (*kick)(void*), void *arg)
	if(q == 0)
		return 0;

	q->limit = limit;
	q->limit = q->inilim = limit;
	q->kick = kick;
	q->arg = arg;
	q->state = msg ? Qmsg : 0;


@@ 422,7 426,7 @@ qread(Queue *q, void *vp, int len)
			sleep(&q->rr, notempty, q);
		}
	}
checkb(b, "qread 1");
	QDEBUG checkb(b, "qread 1");

	/* remove a buffered block */
	q->bfirst = b->next;


@@ 443,7 447,7 @@ checkb(b, "qread 1");
	memmove(p, b->rp, n);
	b->rp += n;

checkb(b, "qread 2");
	QDEBUG checkb(b, "qread 2");
	/* free it or put what's left on the queue */
	if(b->rp >= b->wp || (q->state&Qmsg)) {
		freeb(b);


@@ 482,7 486,7 @@ qnotfull(void *a)
 *  all copies should be outside of ilock since they can fault.
 */
long
qwrite(Queue *q, void *vp, int len, int nowait)
qwrite(Queue *q, void *vp, int len)
{
	int n, sofar, dowakeup;
	Block *b;


@@ 530,7 534,7 @@ qwrite(Queue *q, void *vp, int len, int nowait)
	
		/* flow control */
		while(!qnotfull(q)){
			if(nowait){
			if(q->noblock){
				freeb(b);
				qunlock(&q->wlock);
				poperror();


@@ 549,7 553,7 @@ qwrite(Queue *q, void *vp, int len, int nowait)
		}
		poperror();

checkb(b, "qwrite");
		QDEBUG checkb(b, "qwrite");
		if(q->syncbuf){
			/* we guessed wrong and did an extra copy */
			if(n > q->synclen)


@@ 645,6 649,8 @@ qreopen(Queue *q)
	q->state &= ~Qclosed;
	q->state |= Qstarve;
	q->eof = 0;
	q->noblock = 0;
	q->limit = q->inilim;
}

/*


@@ 678,3 684,21 @@ qcanread(Queue *q)
{
	return q->bfirst!=0;
}

/*
 *  change queue limit
 */
void
qsetlimit(Queue *q, int limit)
{
	q->limit = limit;
}

/*
 *  set blocking/nonblocking
 */
void
qnoblock(Queue *q, int onoff)
{
	q->noblock = onoff;
}

M power/devduart.c => power/devduart.c +1 -1
@@ 672,7 672,7 @@ duartwrite(Chan *c, void *va, long n, ulong offset)
	p = &uart[NETID(c->qid.path)];
	switch(NETTYPE(c->qid.path)){
	case Ndataqid:
		return qwrite(p->oq, va, n, 0);
		return qwrite(p->oq, va, n);
	case Nctlqid:
		if(n >= sizeof(cmd))
			n = sizeof(cmd)-1;