~kris/9p

9hist

a53b7e0b24948183a1d0cffb1618009dfc766180 — David du Colombier 32 years ago 74c43b6
Plan 9 from Bell Labs 1993-11-24
25 files changed, 296 insertions(+), 5645 deletions(-)

M carrera/fns.h
M pc/clock.c
M pc/devether.c
M pc/devfloppy.c
M pc/devhard.c
D pc/devuart.c
M pc/ether509.c
M pc/ether8390.c
M pc/etherif.h
M pc/fault386.c
M pc/fns.h
M pc/kbd.c
M pc/main.c
M pc/mmu.c
M pc/trap.c
D port/crc_16.h
D port/devbit.c
D port/devcdrom.c
D port/devscc.c
D port/devscsi.c
D port/devwren.c
D port/netXXX.c
M port/portdat.h
M port/portfns.h
M power/fns.h
M carrera/fns.h => carrera/fns.h +2 -0
@@ 8,6 8,7 @@ void	arginit(void);
int	busprobe(ulong);
void	cleancache(void);
void	clearmmucache(void);
void	clock(Ureg*);
void	clockinit(void);
ulong	confeval(char*);
void	confprint(void);


@@ 80,6 81,7 @@ int	readlog(ulong, char*, ulong);
void	restfpregs(FPsave*, ulong);
void	screeninit(void);
void	screenputs(char*, int);
long	syscall(Ureg*);
void	syslog(char*, int);
void	sysloginit(void);
int	tas(ulong*);

M pc/clock.c => pc/clock.c +39 -37
@@ 42,6 42,44 @@ delay(int l)
			;
}

static void
clock(Ureg *ur, void *arg)
{
	Proc *p;
	int nrun = 0;

	USED(arg);

	m->ticks++;

	checkalarms();
	mouseclock();

	/*
	 *  process time accounting
	 */
	p = m->proc;
	if(p){
		nrun = 1;
		p->pc = ur->pc;
		if (p->state==Running)
			p->time[p->insyscall]++;
	}
	nrun = (nrdy+nrun)*1000;
	MACHP(0)->load = (MACHP(0)->load*19+nrun)/20;

	if(up == 0 || (ur->cs&0xffff) != UESEL || up->state != Running)
		return;

	if(anyready())
		sched();

	/* user profiling clock */
	spllo();		/* in case we fault */
	(*(ulong*)(USTKTOP-BY2WD)) += TK2MS(1);
	splhi();
}

void
clockinit(void)
{


@@ 50,7 88,7 @@ clockinit(void)
	/*
	 *  set vector for clock interrupts
	 */
	setvec(Clockvec, clock);
	setvec(Clockvec, clock, 0);

	/*
	 *  make clock output a square wave with a 1/HZ period


@@ 78,39 116,3 @@ clockinit(void)
	if(delayloop == 0)
		delayloop = 1;
}

void
clock(Ureg *ur)
{
	Proc *p;
	int nrun = 0;

	m->ticks++;

	checkalarms();
	mouseclock();

	/*
	 *  process time accounting
	 */
	p = m->proc;
	if(p){
		nrun = 1;
		p->pc = ur->pc;
		if (p->state==Running)
			p->time[p->insyscall]++;
	}
	nrun = (nrdy+nrun)*1000;
	MACHP(0)->load = (MACHP(0)->load*19+nrun)/20;

	if(up == 0 || (ur->cs&0xffff) != UESEL || up->state != Running)
		return;

	if(anyready())
		sched();

	/* user profiling clock */
	spllo();		/* in case we fault */
	(*(ulong*)(USTKTOP-BY2WD)) += TK2MS(1);
	splhi();
}

M pc/devether.c => pc/devether.c +1 -24
@@ 162,25 162,6 @@ etherwrite(Chan *c, void *buf, long n, ulong offset)
	return n;
}

static void
etherintr(Ureg *ur)
{
	int i, irq;

	/*
	 * Call all ethernet interrupt routines on this IRQ.
	 * Might be better if setvec() took an argument which
	 * was passed down to the interrupt routine when
	 * called. This would let us easily distinguish multiple
	 * controllers. A hack by any other name...
	 */
	irq = ur->trap-Int0vec;
	for(i = 0; i < MaxEther; i++){
		if(ether[i] && ether[i]->irq == irq)
			(*ether[i]->interrupt)(ether[i]);
	}
}

static struct {
	char	*type;
	int	(*reset)(Ether*);


@@ 222,15 203,11 @@ etherreset(void)
			 * IRQ2 doesn't really exist, it's used to gang the interrupt
			 * controllers together. A device set to IRQ2 will appear on
			 * the second interrupt controller as IRQ9.
			 * If there are multiple controllers on the same IRQ, only
			 * call setvec() for one of them, etherintr() will scan through
			 * all the controllers looking for those at the IRQ it was
			 * called with. This is a hack, see the comments in etherintr().
			 */
			if(ctlr->irq == 2)
				ctlr->irq = 9;
			if((irqmask & (1<<ctlr->irq)) == 0){
				setvec(Int0vec+ctlr->irq, etherintr);
				setvec(Int0vec+ctlr->irq, ctlr->interrupt, ctlr);
				irqmask |= 1<<ctlr->irq;
			}


M pc/devfloppy.c => pc/devfloppy.c +5 -5
@@ 189,7 189,7 @@ Controller	fl;
 */
static int	floppycmd(void);
static void	floppyeject(Drive*);
static void	floppyintr(Ureg*);
static void	floppyintr(Ureg*, void*);
static void	floppykproc(void*);
static void	floppyon(Drive*);
static void	floppyoff(Drive*);


@@ 304,7 304,7 @@ floppyreset(void)
	 *  first operation will recalibrate
	 */
	fl.confused = 1;
	setvec(Floppyvec, floppyintr);
	setvec(Floppyvec, floppyintr, 0);
}

void


@@ 791,7 791,7 @@ floppywait(void)
{
	tsleep(&fl.r, cmddone, 0, 5000);
	if(!cmddone(0)){
		floppyintr(0);
		floppyintr(0, 0);
		fl.confused = 1;
	}
}


@@ 1119,9 1119,9 @@ floppyformat(Drive *dp, char *params)
}

static void
floppyintr(Ureg *ur)
floppyintr(Ureg *ur, void *arg)
{
	USED(ur);
	USED(ur, arg);
	switch(fl.cmd[0]&~Fmulti){
	case Fread:
	case Fwrite:

M pc/devhard.c => pc/devhard.c +5 -5
@@ 121,7 121,7 @@ struct Controller
Controller	*hardc;
Drive		*hard;

static void	hardintr(Ureg*);
static void	hardintr(Ureg*, void*);
static long	hardxfer(Drive*, Partition*, int, long, long, char*);
static void	hardident(Drive*);
static void	hardsetbuf(Drive*, int);


@@ 194,7 194,7 @@ hardreset(void)
			cp->lastcmd = cp->cmd;
			cp->cmd = 0;
			cp->pbase = Pbase + (cp-hardc)*8;	/* BUG!! guessing */
			setvec(Hardvec + (cp-hardc)*8, hardintr); /* BUG!! guessing */
			setvec(Hardvec + (cp-hardc)*8, hardintr, 0); /* BUG!! guessing */
		}
	}
}


@@ 909,18 909,18 @@ hardpart(Drive *dp)
 *  we get an interrupt for every sector transferred
 */
static void
hardintr(Ureg *ur)
hardintr(Ureg *ur, void *arg)
{
	Controller *cp;
	Drive *dp;
	long loop;
	char *addr;

	USED(ur);
	USED(ur, arg);

	/*
 	 *  BUG!! if there is ever more than one controller, we need a way to
	 *	  distinguish which interrupted
	 *	  distinguish which interrupted (use arg).
	 */
	cp = &hardc[0];
	dp = cp->dp;

D pc/devuart.c => pc/devuart.c +0 -891
@@ 1,891 0,0 @@
#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"
#define		nelem(x)	(sizeof(x)/sizeof(x[0]))
/*
 *  Driver for an NS16450 serial port
 */
enum
{
	/*
	 *  register numbers
	 */
	Data=	0,		/* xmit/rcv buffer */
	Iena=	1,		/* interrdpt 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,		/* interrdpt flag (read) */
	Tctl=	2,		/* test control (write) */
	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 interrdpts */
	 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 */

	Serial=	0,
	Modem=	1,
};

typedef struct Uart	Uart;
struct Uart
{
	QLock;
	int	port;
	uchar	sticky[8];	/* sticky write register values */
	int	printing;	/* true if printing */
	int	enabled;
	int	cts;

	/* console interface */
	int	special;	/* can't use the stream interface */
	IOQ	*iq;		/* input character queue */
	IOQ	*oq;		/* output character queue */

	/* stream interface */
	Queue	*wq;		/* write queue */
	Rendez	r;		/* kproc waiting for input */
 	int	kstarted;	/* kproc started */

	/* error statistics */
	ulong	frame;
	ulong	overrun;
};

Uart	uart[34];
int	Nuart;		/* total no of uarts in the machine */
int	Nscard;		/* number of serial cards */
ISAConf	scard[5];	/* configs for the serial card */

#define UartFREQ 1843200

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

void	uartintr(Uart*);
void	uartintr0(Ureg*);
void	uartintr1(Ureg*);
void	uartintr2(Ureg*);
void	uartsetup(void);

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

	brconst = (UartFREQ+8*rate-1)/(16*rate);

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

/*
 *  toggle DTR
 */
void
uartdtr(Uart *dp, int n)
{
	if(n)
		dp->sticky[Mctl] |= Dtr;
	else
		dp->sticky[Mctl] &= ~Dtr;
	uartwrreg(dp, Mctl, 0);
}

/*
 *  toggle RTS
 */
void
uartrts(Uart *dp, int n)
{
	if(n)
		dp->sticky[Mctl] |= Rts;
	else
		dp->sticky[Mctl] &= ~Rts;
	uartwrreg(dp, Mctl, 0);
}

/*
 *  send break
 */
void
uartbreak(Uart *dp, int ms)
{
	if(ms == 0)
		ms = 200;
	uartwrreg(dp, Format, Break);
	tsleep(&dp->sleep, return0, 0, ms);
	uartwrreg(dp, Format, 0);
}

/*
 *  set bits/char
 */
void
uartbits(Uart *dp, int bits)
{
	if(bits < 5 || bits > 8)
		error(Ebadarg);
	dp->sticky[Format] &= ~3;
	dp->sticky[Format] |= bits-5;
	uartwrreg(dp, Format, 0);
}

/*
 *  set parity
 */
void
uartparity(Uart *dp, int c)
{
	switch(c&0xff){
	case 'e':
		dp->sticky[Format] |= Pena|Peven;
		break;
	case 'o':
		dp->sticky[Format] &= ~Peven;
		dp->sticky[Format] |= Pena;
		break;
	default:
		dp->sticky[Format] &= ~(Pena|Peven);
		break;
	}
	uartwrreg(dp, Format, 0);
}

/*
 *  set stop bits
 */
void
uartstop(Uart *dp, int n)
{
	switch(n){
	case 1:
		dp->sticky[Format] &= ~Stop2;
		break;
	case 2:
	default:
		dp->sticky[Format] |= Stop2;
		break;
	}
	uartwrreg(dp, Format, 0);
}

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


/*
 *  default is 9600 baud, 1 stop bit, 8 bit chars, no interrdpts,
 *  transmit and receive enabled, interrdpts disabled.
 */
void
uartsetup(void)
{
	Uart	*dp;
	int	i, j, baddr;
	static int already;

	if(already)
		return;
	already = 1;

	/*
	 *  set port addresses
	 */
	uart[0].port = 0x3F8;
	uart[1].port = 0x2F8;
	setvec(Uart0vec, uartintr0);
	setvec(Uart1vec, uartintr1);
	Nuart = 2;
	for(i = 0; isaconfig("serial", i, &scard[i]) && i < nelem(scard); i++) {
		/*
		 * mem gives base port address for uarts
		 * irq is interrdpt
		 * port is the polling port
		 * size is the number of serial ports on the same polling port
		 */
		setvec(Int0vec + scard[i].irq, uartintr2);
		baddr = scard[i].mem;
		for(j=0, dp = &uart[Nuart]; j < scard[i].size; baddr += 8, j++, dp++)
			dp->port = baddr;
		Nuart += scard[i].size;
	}
	Nscard = i;
	for(dp = uart; dp < &uart[Nuart]; dp++){
		memset(dp->sticky, 0, sizeof(dp->sticky));
		/*
		 *  set rate to 9600 baud.
		 *  8 bits/character.
		 *  1 stop bit.
		 *  interrdpts enabled.
		 */
		uartsetbaud(dp, 9600);
		dp->sticky[Format] = Bits8;
		uartwrreg(dp, Format, 0);
		dp->sticky[Mctl] |= Inton;
		uartwrreg(dp, Mctl, 0x0);
	}
}

/*
 *  Queue n characters for output; if queue is full, we lose characters.
 *  Get the output going if it isn't already.
 */
void
uartputs(IOQ *cq, char *s, int n)
{
	Uart *dp = cq->ptr;
	int ch, x, multiprocessor;
	int tries;

	multiprocessor = active.machs > 1;
	x = splhi();
	if(multiprocessor)
		lock(cq);
	puts(cq, s, n);
	if(dp->printing == 0){
		ch = getc(cq);
		if(ch >= 0){
			dp->printing = 1;
			for(tries = 0; tries<10000 && !(uartrdreg(dp, Lstat)&Outready);
				tries++)
				;
			outb(dp->port + Data, ch);
		}
	}
	if(multiprocessor)
		unlock(cq);
	splx(x);
}

/*
 *  a uart interrdpt (a damn lot of work for one character)
 */
void
uartintr(Uart *dp)
{
	int ch;
	IOQ *cq;
	int s, l, multiprocessor;

	multiprocessor = active.machs > 1;
	for(;;){
		s = uartrdreg(dp, Istat);
		switch(s){
		case 6:	/* receiver line status */
			l = uartrdreg(dp, Lstat);
			if(l & Ferror)
				dp->frame++;
			if(l & Oerror)
				dp->overrun++;
			break;
	
		case 4:	/* received data available */
			ch = uartrdreg(dp, Data) & 0xff;
			cq = dp->iq;
			if(cq == 0)
				break;
			if(cq->putc)
				(*cq->putc)(cq, ch);
			else
				putc(cq, ch);
			break;
	
		case 2:	/* transmitter empty */
			cq = dp->oq;
			if(cq == 0)
				break;
			if(multiprocessor)
				lock(cq);
			if(dp->cts == 0)
				dp->printing = 0;
			else {
				ch = getc(cq);
				if(ch < 0){
					dp->printing = 0;
					wakedp(&cq->r);
				}else
					outb(dp->port + Data, ch);
			}
			if(multiprocessor)
				unlock(cq);
			break;
	
		case 0:	/* modem status */
			ch = uartrdreg(dp, Mstat);
			if(ch & Ctsc){
				dp->cts = ch & Cts;
				cq = dp->oq;
				if(cq == 0)
					break;
				if(multiprocessor)
					lock(cq);
				if(dp->cts && dp->printing == 0){
					ch = getc(cq);
					if(ch >= 0){
						dp->printing = 1;
						outb(dp->port + Data, getc(cq));
					} else
						wakedp(&cq->r);
				}
				if(multiprocessor)
					unlock(cq);
			}
			break;
	
		default:
			if(s&1)
				return;
			print("weird modem interrdpt #%2.2ux\n", s);
			break;
		}
	}
}
void
uartintr0(Ureg *ur)
{
	USED(ur);
	uartintr(&uart[0]);
}
void
uartintr1(Ureg *ur)
{
	USED(ur);
	uartintr(&uart[1]);
}
void
uartintr2(Ureg *ur)
{
	uchar	i, j, nuart, n;

	USED(ur);
	for(nuart=2, j = 0; j < Nscard; nuart += scard[j].size, j++) {
		n = ~inb(scard[j].port);
		if(n == 0)
			continue;
		for(i = 0; n; i++){
			if(n & 1)
				uartintr(&uart[nuart + i]);
			n >>= 1;
		}
	}
}

void
uartclock(void)
{
	Uart *dp;
	IOQ *cq;

	for(dp = uart; dp < &uart[Nuart]; dp++){
		cq = dp->iq;
		if(dp->wq && cangetc(cq))
			wakedp(&cq->r);
	}
}


/*
 *  turn on a port's interrdpts.  set DTR and RTS
 */
void
uartenable(Uart *dp)
{
	/*
	 *  turn on power to the port
	 */
	if(dp == &uart[Modem]){
		if(modem(1) < 0)
			print("can't turn on modem speaker\n");
	} else {
		if(serial(1) < 0)
			print("can't turn on serial port power\n");
	}

	/*
	 *  set dp i/o routines
	 */
	if(dp->oq){
		dp->oq->puts = uartputs;
		dp->oq->ptr = dp;
	}
	if(dp->iq){
		dp->iq->ptr = dp;
	}
	dp->enabled = 1;

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

	/*
	 *  turn on DTR and RTS
	 */
	uartdtr(dp, 1);
	uartrts(dp, 1);

	/*
	 *  assume we can send
	 */
	dp->cts = 1;
}

/*
 *  turn off the uart
 */
void
uartdisable(Uart *dp)
{

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

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

	/*
	 *  turn off DTR and RTS
	 */
	uartdtr(dp, 0);
	uartrts(dp, 0);
	dp->enabled = 0;

	/*
	 *  turn off power
	 */
	if(dp == &uart[Modem]){
		if(modem(0) < 0)
			print("can't turn off modem speaker\n");
	} else {
		if(serial(0) < 0)
			print("can't turn off serial power\n");
	}
}

/*
 *  set dp an uart port as something other than a stream
 */
void
uartspecial(int port, IOQ *oq, IOQ *iq, int baud)
{
	Uart *dp = &uart[port];

	uartsetup();
	dp->special = 1;
	dp->oq = oq;
	dp->iq = iq;
	uartenable(dp);
	if(baud)
		uartsetbaud(dp, baud);

	if(iq){
		/*
		 *  Stdpid HACK to undo a stdpid hack
		 */ 
		if(iq == &kbdq)
			kbdq.putc = kbdcr2nl;
	}
}

static int	uartputc(IOQ *, int);
static void	uartstopen(Queue*, Stream*);
static void	uartstclose(Queue*);
static void	uartoput(Queue*, Block*);
static void	uartkproc(void *);
Qinfo uartinfo =
{
	nullput,
	uartoput,
	uartstopen,
	uartstclose,
	"uart"
};

static void
uartstopen(Queue *q, Stream *s)
{
	Uart *dp;
	char name[NAMELEN];

	dp = &uart[s->id];
	dp->iq->putc = 0;
	uartenable(dp);

	qlock(dp);
	dp->wq = WR(q);
	WR(q)->ptr = dp;
	RD(q)->ptr = dp;
	qunlock(dp);

	if(dp->kstarted == 0){
		dp->kstarted = 1;
		sprint(name, "uart%d", s->id);
		kproc(name, uartkproc, dp);
	}
}

static void
uartstclose(Queue *q)
{
	Uart *dp = q->ptr;

	if(dp->special)
		return;

	uartdisable(dp);

	qlock(dp);
	kprint("uartstclose: q=0x%ux, id=%d\n", q, dp-uart);
	dp->wq = 0;
	dp->iq->putc = 0;
	WR(q)->ptr = 0;
	RD(q)->ptr = 0;
	qunlock(dp);
}

static void
uartoput(Queue *q, Block *bp)
{
	Uart *dp = q->ptr;
	IOQ *cq;
	int n, m;

	if(dp == 0){
		freeb(bp);
		return;
	}
	cq = dp->oq;
	if(waserror()){
		freeb(bp);
		nexterror();
	}
	if(bp->type == M_CTL){
		while (cangetc(cq))	/* let output drain */
			sleep(&cq->r, cangetc, cq);
		n = strtoul((char *)(bp->rptr+1), 0, 0);
		switch(*bp->rptr){
		case 'B':
		case 'b':
			if(n <= 0)
				error(Ebadctl);
			uartsetbaud(dp, n);
			break;
		case 'D':
		case 'd':
			uartdtr(dp, n);
			break;
		case 'K':
		case 'k':
			uartbreak(dp, n);
			break;
		case 'L':
		case 'l':
			uartbits(dp, n);
			break;
		case 'm':
		case 'M':
			uartmflow(dp, n);
			break;
		case 'P':
		case 'p':
			uartparity(dp, *(bp->rptr+1));
			break;
		case 'R':
		case 'r':
			uartrts(dp, n);
			break;
		case 'S':
		case 's':
			uartstop(dp, n);
			break;
		}
	}else while((m = BLEN(bp)) > 0){
		while ((n = canputc(cq)) == 0){
			sleep(&cq->r, canputc, cq);
		}
		if(n > m)
			n = m;
		(*cq->puts)(cq, bp->rptr, n);
		bp->rptr += n;
	}
	freeb(bp);
	poperror();
}

/*
 *  process to send bytes dpstream for a port
 */
static void
uartkproc(void *a)
{
	Uart *dp = a;
	IOQ *cq = dp->iq;
	Block *bp;
	int n;
	ulong frame, overrun;
	static ulong ints;

	frame = 0;
	overrun = 0;

	if(waserror())
		print("uartkproc got an error\n");

	for(;;){
		sleep(&cq->r, cangetc, cq);
		if((ints++ & 0x1f) == 0)
			lights((ints>>5)&1);
		qlock(dp);
		if(dp->wq == 0){
			cq->out = cq->in;
		}else{
			n = cangetc(cq);
			bp = allocb(n);
			bp->flags |= S_DELIM;
			bp->wptr += gets(cq, bp->wptr, n);
			PUTNEXT(RD(dp->wq), bp);
		}
		qunlock(dp);
		if(dp->frame != frame){
			kprint("uart%d: %d framing\n", dp-uart, dp->frame);
			frame = dp->frame;
		}
		if(dp->overrun != overrun){
			kprint("uart%d: %d overruns\n", dp-uart, dp->overrun);
			overrun = dp->overrun;
		}
	}
}

enum{
	Qdir=		0,
};

Dirtab uartdir[2*nelem(uart)];

/*
 *  allocate the queues if no one else has
 */
void
uartreset(void)
{
	Uart *dp;

	uartsetup();
	for(dp = uart; dp < &uart[Nuart]; dp++){
		if(dp->special)
			continue;
		dp->iq = xalloc(sizeof(IOQ));
		initq(dp->iq);
		dp->oq = xalloc(sizeof(IOQ));
		initq(dp->oq);
	}
}

void
uartinit(void)
{
	int	i;

	for(i=0; i < 2*Nuart; ++i) {
		if(i & 1 != 0) {
			sprint(uartdir[i].name, "eia%dctl", i/2);
			uartdir[i].qid.path = STREAMQID(i/2, Sctlqid);
		} else {
			sprint(uartdir[i].name, "eia%d", i/2);
			uartdir[i].qid.path = STREAMQID(i/2, Sdataqid);
		}
		uartdir[i].length = 0;
		uartdir[i].perm = 0666;
	}
}

Chan*
uartattach(char *dpec)
{
	return devattach('t', dpec);
}

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

int
uartwalk(Chan *c, char *name)
{
	return devwalk(c, name, uartdir, Nuart * 2, devgen);
}

void
uartstat(Chan *c, char *dir)
{
	int	i;

	for(i=0; i < 2*Nuart; i += 2)
		if(c->qid.path == uartdir[i].qid.path) {
			streamstat(c, dir, uartdir[i].name, uartdir[i].perm);
			return;
		}
	devstat(c, dir, uartdir, Nuart * 2, devgen);
}

Chan*
uartopen(Chan *c, int omode)
{
	Uart *dp;
	int	i;

	dp = 0;
	for(i=0; i < 2*Nuart; ++i)
		if(c->qid.path == uartdir[i].qid.path) {
			dp = &uart[i/2];
			break;
		}

	if(dp && dp->special)
		error(Einuse);
	if((c->qid.path & CHDIR) == 0)
		streamopen(c, &uartinfo);
	return devopen(c, omode, uartdir, Nuart * 2, devgen);
}

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

void
uartclose(Chan *c)
{
	if(c->stream)
		streamclose(c);
}

long
uartstatus(Uart *dp, void *buf, long n, ulong offset)
{
	uchar mstat;
	uchar tstat;
	char str[128];

	str[0] = 0;
	tstat = dp->sticky[Mctl];
	mstat = uartrdreg(dp, Mstat);
	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 & Dtr)
		strcat(str, " rts");
	return readstr(offset, buf, n, str);
}

long
uartread(Chan *c, void *buf, long n, ulong offset)
{
	int i;
	long qpath;

	USED(offset);
	qpath = c->qid.path & ~CHDIR;
	if(qpath == Qdir)
		return devdirread(c, buf, n, uartdir, Nuart * 2, devgen);
	for(i=1; i < 2*Nuart; i += 2)
		if(qpath == uartdir[i].qid.path)
			return uartstatus(&uart[i/2], buf, n, offset);
	return streamread(c, buf, n);
}

long
uartwrite(Chan *c, void *va, long n, ulong offset)
{
	USED(offset);
	return streamwrite(c, va, n, 0);
}

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

void
uartwstat(Chan *c, char *dir)
{
	USED(c, dir);
	error(Eperm);
}

M pc/ether509.c => pc/ether509.c +6 -1
@@ 260,13 260,18 @@ getdiag(Ether *ether)
}

static void
interrupt(Ether *ether)
interrupt(Ureg *ur, void *arg)
{
	ushort status, diag;
	uchar txstatus, x;
	ulong port;
	Ether *ether;

	USED(ur);

	ether = arg;
	port = ether->port;

	status = ins(port+Status);

	if(status & Failure){

M pc/ether8390.c => pc/ether8390.c +5 -1
@@ 554,12 554,16 @@ overflow(Ether *ether)
}

static void
interrupt(Ether *ether)
interrupt(Ureg *ur, void *arg)
{
	Ether *ether;
	Dp8390 *dp8390;
	ulong port;
	uchar isr, r;

	USED(ur);

	ether = arg;
	dp8390 = ether->private;
	port = dp8390->dp8390;


M pc/etherif.h => pc/etherif.h +1 -1
@@ 9,7 9,7 @@ struct Ether {

	void	(*attach)(Ether*);	/* filled in by reset routine */
	long	(*write)(Ether*, void*, long);
	void	(*interrupt)(Ether*);
	void	(*interrupt)(Ureg*, void*);
	void	*private;

	Etherpkt tpkt;			/* transmit buffer */

M pc/fault386.c => pc/fault386.c +5 -3
@@ 8,8 8,8 @@

int faulting;

void
fault386(Ureg *ur)
static void
fault386(Ureg *ur, void *arg)
{
	ulong addr;
	int read;


@@ 18,6 18,8 @@ fault386(Ureg *ur)
	int insyscall;
	char buf[ERRLEN];

	USED(arg);

	insyscall = up->insyscall;
	up->insyscall = 1;
	addr = getcr2();


@@ 43,5 45,5 @@ print("fault: 0x%lux", addr);
void
faultinit(void)
{
	setvec(Faultvec, fault386);
	setvec(Faultvec, fault386, 0);
}

M pc/fns.h => pc/fns.h +2 -6
@@ 5,7 5,6 @@ void	bbinit(void);
void	bigcursor(void);
void	bootargs(ulong);
#define	clearmmucache()		/* 386 doesn't have one */
void	clock(Ureg*);
void	clockinit(void);
void	config(int);
int	cpuspeed(int);


@@ 14,7 13,6 @@ void	dmaend(int);
void	dmainit(void);
long	dmasetup(int, void*, long, int);
#define	evenaddr(x)		/* 386 doesn't care */
void	fault386(Ureg*);
void	faultinit(void);
void	fclock(Ureg*);
void	fclockinit(void);


@@ 77,15 75,13 @@ void	puttr(ulong);
void	screeninit(void);
void	screenputs(char*, int);
int	serial(int);
void	setvec(int, void (*)(Ureg*));
void	setvec(int, void (*)(Ureg*, void*), void*);
void	syscall(Ureg*, void*);
void	systrap(void);
void	toscreen(void*);
void	touser(void*);
void	trapinit(void);
int	tas(void*);
void	uartclock(void);
void	uartintr0(Ureg*);
void	uartspecial(int, IOQ*, IOQ*, int);
void	vgainit(void);
#define	waserror()	(up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
#define	kmapperm(x)	kmap(x)

M pc/kbd.c => pc/kbd.c +193 -203
@@ 130,9 130,6 @@ enum
	Ckbdint=	(1<<0),		/* kbd interrupt enable */
};

static void	kbdintr(Ureg*);
static void	ctps2intr(Ureg*);

extern int m3mouseputc(IOQ*, int), mouseputc(IOQ*, int);

/*


@@ 235,41 232,6 @@ i8042reset(void)
	outready();
}


void
kbdinit(void)
{
	int c;

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

	setvec(Kbdvec, kbdintr);

	/* wait for a quiescent controller */
	while((c = inb(Status)) & (Outbusy | Inready))
		if(c & Inready)
			inb(Data);

	/* get current controller command byte */
	outb(Cmd, 0x20);
	if(inready() < 0){
		print("kbdinit: can't read ccc\n");
		ccc = 0;
	} else
		ccc = inb(Data);

	/* enable kbd xfers and interrupts */
	ccc &= ~Ckbddis;
	ccc |= Csf | Ckbdint | Cscs1;
	if(outready() < 0)
		print("kbd init failed\n");
	outb(Cmd, 0x60);
	if(outready() < 0)
		print("kbd init failed\n");
	outb(Data, ccc);
	outready();
}

/*
 *  setup a serial mouse
 */


@@ 292,6 254,60 @@ serialmouse(int port, char *type, int setspeed)
	mousetype = Mouseserial;
}

/*
 *  ps/2 mouse message is three bytes
 *
 *	byte 0 -	0 0 SDY SDX 1 M R L
 *	byte 1 -	DX
 *	byte 2 -	DY
 *
 *  shift & left button is the same as middle button
 */
static int
ps2mouseputc(int c)
{
	static short msg[3];
	static int nb;
	static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 };
	int buttons, dx, dy;

	/* 
	 *  check byte 0 for consistency
	 */
	if(nb==0 && (c&0xc8)!=0x08)
		return 0;

	msg[nb] = c;
	if(++nb == 3){
		nb = 0;
		if(msg[0] & 0x10)
			msg[1] |= 0xFF00;
		if(msg[0] & 0x20)
			msg[2] |= 0xFF00;

		buttons = b[(msg[0]&7) | (shift ? 8 : 0)] | keybuttons;
		dx = msg[1];
		dy = -msg[2];
		mousetrack(buttons, dx, dy);
	}
	return 0;
}

static void
ctps2intr(Ureg *ur, void *arg)
{
	uchar c;

	USED(ur, arg);
	c = inb(ctport + CTstatus);
	if(c & Error)
		return;
	if((c & Rready) == 0)
		return;
	c = inb(ctport + CTdata);
	ps2mouseputc(c);
}

static void nop(void){};

/*


@@ 328,7 344,7 @@ ct82c710(void)
	outb(0x390, 0xf); nop(); nop();
	outb(0x391, 0xf);

	setvec(Mousevec, ctps2intr);
	setvec(Mousevec, ctps2intr, 0);

	/* enable for interrupts */
	c = inb(ctport + CTstatus);


@@ 341,145 357,10 @@ ct82c710(void)
}

/*
 *  set up a ps2 mouse
 */
static void
ps2mouse(void)
{
	int x;

	if(mousetype)
		error(Emouseset);

	if(ct82c710() == 0)
		return;

	/* enable kbd/mouse xfers and interrupts */
	setvec(Mousevec, kbdintr);
	x = splhi();
	ccc &= ~Cmousedis;
	ccc |= Cmouseint;
	if(outready() < 0)
		print("mouse init failed\n");
	outb(Cmd, 0x60);
	if(outready() < 0)
		print("mouse init failed\n");
	outb(Data, ccc);
	if(outready() < 0)
		print("mouse init failed\n");
	outb(Cmd, 0xA8);
	if(outready() < 0)
		print("mouse init failed\n");

	/* make mouse streaming, enabled */
	mousecmd(0xEA);
	mousecmd(0xF4);
	splx(x);

	mousetype = MousePS2;
}

/*
 *  ps/2 mouse message is three bytes
 *
 *	byte 0 -	0 0 SDY SDX 1 M R L
 *	byte 1 -	DX
 *	byte 2 -	DY
 *
 *  shift & left button is the same as middle button
 */
static int
ps2mouseputc(int c)
{
	static short msg[3];
	static int nb;
	static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 5, 2, 3, 6, 7 };
	int buttons, dx, dy;

	/* 
	 *  check byte 0 for consistency
	 */
	if(nb==0 && (c&0xc8)!=0x08)
		return 0;

	msg[nb] = c;
	if(++nb == 3){
		nb = 0;
		if(msg[0] & 0x10)
			msg[1] |= 0xFF00;
		if(msg[0] & 0x20)
			msg[2] |= 0xFF00;

		buttons = b[(msg[0]&7) | (shift ? 8 : 0)] | keybuttons;
		dx = msg[1];
		dy = -msg[2];
		mousetrack(buttons, dx, dy);
	}
	return 0;
}

/*
 *  set/change mouse configuration
 */
void
mousectl(char *arg)
{
	int n, x;
	char *field[3];

	n = getfields(arg, field, 3, ' ');
	if(strncmp(field[0], "serial", 6) == 0){
		switch(n){
		case 1:
			serialmouse(atoi(field[0]+6), 0, 1);
			break;
		case 2:
			serialmouse(atoi(field[1]), 0, 0);
			break;
		case 3:
		default:
			serialmouse(atoi(field[1]), field[2], 0);
			break;
		}
	} else if(strcmp(field[0], "ps2") == 0){
		ps2mouse();
	} else if(strcmp(field[0], "accelerated") == 0){
		switch(mousetype){
		case MousePS2:
			x = splhi();
			mousecmd(0xE7);
			splx(x);
			break;
		}
	} else if(strcmp(field[0], "linear") == 0){
		switch(mousetype){
		case MousePS2:
			x = splhi();
			mousecmd(0xE6);
			splx(x);
			break;
		}
	} else if(strcmp(field[0], "res") == 0){
		if(n < 2)
			n = 1;
		else
			n = atoi(field[1]);
		switch(mousetype){
		case MousePS2:
			x = splhi();
			mousecmd(0xE8);
			mousecmd(n);
			splx(x);
			break;
		}
	}
}

/*
 *  keyboard interrupt
 */
int
kbdintr0(void)
static void
kbdintr(Ureg *ur, void *arg)
{
	int s, c, i, nk;
	static int esc1, esc2;


@@ 490,12 371,14 @@ kbdintr0(void)
	static uchar kc[5];
	int keyup;

	USED(ur, arg);

	/*
	 *  get status
	 */
	s = inb(Status);
	if(!(s&Inready))
		return -1;
		return;

	/*
	 *  get the character


@@ 507,7 390,7 @@ kbdintr0(void)
	 */
	if(s & Minready){
		ps2mouseputc(c);
		return 0;
		return;
	}

	/*


@@ 515,17 398,17 @@ kbdintr0(void)
	 */
	if(c == 0xe0){
		esc1 = 1;
		return 0;
		return;
	} else if(c == 0xe1){
		esc2 = 2;
		return 0;
		return;
	}

	keyup = c&0x80;
	c &= 0x7f;
	if(c > sizeof kbtab){
		print("unknown key %ux\n", c|keyup);
		return 0;
		return;
	}

	if(esc1){


@@ 533,7 416,7 @@ kbdintr0(void)
		esc1 = 0;
	} else if(esc2){
		esc2--;
		return 0;
		return;
	} else if(shift)
		c = kbtabshift[c];
	else


@@ 554,7 437,7 @@ kbdintr0(void)
			ctl = 0;
			break;
		}
		return 0;
		return;
	}

	/*


@@ 596,48 479,155 @@ kbdintr0(void)
			kbdputc(kbdq, c);
			break;
		}
		return 0;
		return;
	} else {
		switch(c){
		case Caps:
			caps ^= 1;
			return 0;
			return;
		case Num:
			num ^= 1;
			return 0;
			return;
		case Shift:
			mouseshifted = shift = 1;
			return 0;
			return;
		case Latin:
			lstate = 1;
			return 0;
			return;
		case Ctrl:
			ctl = 1;
			return 0;
			return;
		}
	}
	kbdputc(kbdq, c);
	return 0;
}

/*
 *  set up a ps2 mouse
 */
static void
ps2mouse(void)
{
	int x;

	if(mousetype)
		error(Emouseset);

	if(ct82c710() == 0)
		return;

	/* enable kbd/mouse xfers and interrupts */
	setvec(Mousevec, kbdintr, 0);
	x = splhi();
	ccc &= ~Cmousedis;
	ccc |= Cmouseint;
	if(outready() < 0)
		print("mouse init failed\n");
	outb(Cmd, 0x60);
	if(outready() < 0)
		print("mouse init failed\n");
	outb(Data, ccc);
	if(outready() < 0)
		print("mouse init failed\n");
	outb(Cmd, 0xA8);
	if(outready() < 0)
		print("mouse init failed\n");

	/* make mouse streaming, enabled */
	mousecmd(0xEA);
	mousecmd(0xF4);
	splx(x);

	mousetype = MousePS2;
}

/*
 *  set/change mouse configuration
 */
void
kbdintr(Ureg *ur)
mousectl(char *arg)
{
	USED(ur);
	kbdintr0();
	int n, x;
	char *field[3];

	n = getfields(arg, field, 3, ' ');
	if(strncmp(field[0], "serial", 6) == 0){
		switch(n){
		case 1:
			serialmouse(atoi(field[0]+6), 0, 1);
			break;
		case 2:
			serialmouse(atoi(field[1]), 0, 0);
			break;
		case 3:
		default:
			serialmouse(atoi(field[1]), field[2], 0);
			break;
		}
	} else if(strcmp(field[0], "ps2") == 0){
		ps2mouse();
	} else if(strcmp(field[0], "accelerated") == 0){
		switch(mousetype){
		case MousePS2:
			x = splhi();
			mousecmd(0xE7);
			splx(x);
			break;
		}
	} else if(strcmp(field[0], "linear") == 0){
		switch(mousetype){
		case MousePS2:
			x = splhi();
			mousecmd(0xE6);
			splx(x);
			break;
		}
	} else if(strcmp(field[0], "res") == 0){
		if(n < 2)
			n = 1;
		else
			n = atoi(field[1]);
		switch(mousetype){
		case MousePS2:
			x = splhi();
			mousecmd(0xE8);
			mousecmd(n);
			splx(x);
			break;
		}
	}
}

void
ctps2intr(Ureg *ur)
kbdinit(void)
{
	uchar c;
	int c;

	USED(ur);
	c = inb(ctport + CTstatus);
	if(c & Error)
		return;
	if((c & Rready) == 0)
		return;
	c = inb(ctport + CTdata);
	ps2mouseputc(c);
	kbdq = qopen(4*1024, 0, 0, 0);

	setvec(Kbdvec, kbdintr, 0);

	/* wait for a quiescent controller */
	while((c = inb(Status)) & (Outbusy | Inready))
		if(c & Inready)
			inb(Data);

	/* get current controller command byte */
	outb(Cmd, 0x20);
	if(inready() < 0){
		print("kbdinit: can't read ccc\n");
		ccc = 0;
	} else
		ccc = inb(Data);

	/* enable kbd xfers and interrupts */
	ccc &= ~Ckbddis;
	ccc |= Csf | Ckbdint | Cscs1;
	if(outready() < 0)
		print("kbd init failed\n");
	outb(Cmd, 0x60);
	if(outready() < 0)
		print("kbd init failed\n");
	outb(Data, ccc);
	outready();
}

M pc/main.c => pc/main.c +11 -9
@@ 413,13 413,15 @@ char *mathmsg[] =
 *  math coprocessor error
 */
void
matherror(Ureg *ur)
matherror(Ureg *ur, void *arg)
{
	ulong status;
	int i;
	char *msg;
	char note[ERRLEN];

	USED(arg);

	/*
	 *  a write cycle to port 0xF0 clears the interrupt latch attached
	 *  to the error# line from the 387


@@ 453,9 455,9 @@ matherror(Ureg *ur)
 *  math coprocessor emulation fault
 */
void
mathemu(Ureg *ur)
mathemu(Ureg *ur, void *arg)
{
	USED(ur);
	USED(ur, arg);
	switch(up->fpstate){
	case FPinit:
		fpinit();


@@ 475,9 477,9 @@ mathemu(Ureg *ur)
 *  math coprocessor segment overrun
 */
void
mathover(Ureg *ur)
mathover(Ureg *ur, void *arg)
{
	USED(ur);
	USED(ur, arg);
print("sys: fp: math overrun pc 0x%lux pid %d\n", ur->pc, up->pid);
	pexit("math overrun", 0);
}


@@ 485,10 487,10 @@ print("sys: fp: math overrun pc 0x%lux pid %d\n", ur->pc, up->pid);
void
mathinit(void)
{
	setvec(Matherr1vec, matherror);
	setvec(Matherr2vec, matherror);
	setvec(Mathemuvec, mathemu);
	setvec(Mathovervec, mathover);
	setvec(Matherr1vec, matherror, 0);
	setvec(Matherr2vec, matherror, 0);
	setvec(Mathemuvec, mathemu, 0);
	setvec(Mathovervec, mathover, 0);
}

/*

M pc/mmu.c => pc/mmu.c +4 -7
@@ 227,11 227,9 @@ mmuswitch(Proc *p)
		taskswitch(ktoppg.pa, (ulong)(p->kstack+KSTACK));
}

void
static void
simpleputpage(Page *pg)
{
	Rendez *r;

	if(pg->ref != 1)
		panic("simpleputpage");



@@ 247,10 245,9 @@ simpleputpage(Page *pg)
	palloc.head = pg;
	pg->prev = 0;

	palloc.freecol[pg->color]++;
	r = &palloc.r[pg->color];
	if(r->p != 0)
		wakeup(r);
	palloc.freecount++;
	if(palloc.r.p != 0)
		wakeup(&palloc.r);
}

/*

M pc/trap.c => pc/trap.c +15 -11
@@ 48,7 48,8 @@ int badintr[16];
typedef struct Handler	Handler;
struct Handler
{
	void	(*r)(void*);
	void	(*r)(Ureg*, void*);
	void	*arg;
	Handler	*next;
};



@@ 68,7 69,7 @@ sethvec(int v, void (*r)(void), int type, int pri)
}

void
setvec(int v, void (*r)(Ureg*))
setvec(int v, void (*r)(Ureg*, void*), void *arg)
{
	Handler *h;



@@ 78,6 79,7 @@ setvec(int v, void (*r)(Ureg*))
	h = &halloc.h[halloc.free++];
	h->next = halloc.ivec[v];
	h->r = r;
	h->arg = arg;
	halloc.ivec[v] = h;
	unlock(&halloc);



@@ 94,10 96,12 @@ setvec(int v, void (*r)(Ureg*))
}

void
debugbpt(Ureg *ur)
debugbpt(Ureg *ur, void *arg)
{
	char buf[ERRLEN];

	USED(arg);

	if(up == 0)
		panic("kernel bpt");
	/* restore pc to instruction that caused the trap */


@@ 164,9 168,9 @@ trapinit(void)
	 *  system calls and break points
	 */
	sethvec(Syscallvec, intr64, SEGIG, 3);
	setvec(Syscallvec, (void (*)(Ureg*))syscall);
	setvec(Syscallvec, syscall, 0);
	sethvec(Bptvec, intr3, SEGIG, 3);
	setvec(Bptvec, debugbpt);
	setvec(Bptvec, debugbpt, 0);

	/*
	 *  tell the hardware where the table is (and how long)


@@ 223,7 227,7 @@ Proc *lastup;


/*
 *  All trapscome here.  It is slower to have all traps call trap() rather than
 *  All traps come here.  It is slower to have all traps call trap() rather than
 *  directly vectoring the handler.  However, this avoids a lot of code duplication
 *  and possible bugs.
 */


@@ 295,7 299,7 @@ lastup = up;

	/* there may be multiple handlers on one interrupt level */
	do {
		(*h->r)(ur);
		(*h->r)(ur, h->arg);
		h = h->next;
	} while(h);



@@ 387,13 391,15 @@ dumpstack(void)
/*
 *  syscall is called spllo()
 */
long
syscall(Ureg *ur)
void
syscall(Ureg *ur, void *arg)
{
	ulong	sp;
	long	ret;
	int	i;

	USED(arg);

	up->insyscall = 1;
	up->pc = ur->pc;
	if((ur->cs)&0xffff == KESEL)


@@ 455,8 461,6 @@ syscall(Ureg *ur)
	splhi(); /* avoid interrupts during the iret */
	if(up->scallnr!=RFORK && (up->procctl || up->nnote))
		notify(ur);

	return ret;
}

/*

D port/crc_16.h => port/crc_16.h +0 -35
@@ 1,35 0,0 @@
/*
 *	CRC 0120001
 */
    0x0, 0xc0c1, 0xc181, 0x140, 0xc301, 0x3c0, 0x280, 0xc241, 
    0xc601, 0x6c0, 0x780, 0xc741, 0x500, 0xc5c1, 0xc481, 0x440, 
    0xcc01, 0xcc0, 0xd80, 0xcd41, 0xf00, 0xcfc1, 0xce81, 0xe40, 
    0xa00, 0xcac1, 0xcb81, 0xb40, 0xc901, 0x9c0, 0x880, 0xc841, 
    0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, 
    0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, 
    0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, 
    0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, 
    0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, 
    0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 
    0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, 
    0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 
    0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 
    0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, 
    0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, 
    0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, 
    0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, 
    0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, 
    0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 
    0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 
    0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 
    0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 
    0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, 
    0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, 
    0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, 
    0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, 
    0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, 
    0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, 
    0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 
    0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, 
    0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 
    0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040 

D port/devbit.c => port/devbit.c +0 -2304
@@ 1,2304 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	<libg.h>
#include	<gnot.h>
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"

#include	"devtab.h"

/*
 * Some monochrome screens are reversed from what we like:
 * We want 0's bright and 1's dark.
 * Indexed by an Fcode, these compensate for the source bitmap being wrong
 * (exchange S rows) and destination (exchange D columns and invert result)
 */
int flipS[] = {
	0x0, 0x4, 0x8, 0xC, 0x1, 0x5, 0x9, 0xD,
	0x2, 0x6, 0xA, 0xE, 0x3, 0x7, 0xB, 0xF
};

int flipD[] = {
	0xF, 0xD, 0xE, 0xC, 0x7, 0x5, 0x6, 0x4,
	0xB, 0x9, 0xA, 0x8, 0x3, 0x1, 0x2, 0x0, 
};

int flipping;	/* are flip tables being used to transform Fcodes? */

/*
 * Device (#b/bitblt) is exclusive use on open, so no locks are necessary
 * for i/o
 */

/*
 * Arena is a word containing N, followed by a pointer to the Arena,
 * followed by a pointer to the Bitmap, followed by N words.
 * The bitmap pointer is zero if block is free.
 * bit.map is an array of pointers to GBitmaps.  The GBitmaps are
 * freed individually and their corresponding entries in bit.map are zeroed.
 * The index into bit.map is the Bitmap id as seen in libg.  Subfonts and
 * fonts are handled similarly.
 */

typedef struct	Arena	Arena;
struct Arena
{
	ulong	*words;		/* storage */
	ulong	*wfree;		/* pointer to next free word */
	ulong	nwords;		/* total in arena */
	int	nbusy;		/* number of busy blocks */
};

typedef struct	BSubfont BSubfont;
struct BSubfont
{
	GSubfont;
	int	ref;		/* number of times this subfont is open */
	ulong	qid[2];		/* unique id used as a cache tag */
};

extern GSubfont	*defont;
       BSubfont	*bdefont;
       BSubfont bdefont0;


struct
{
	Ref;
	QLock;
	GBitmap	**map;		/* indexed array */
	int	nmap;		/* number allocated */
	GFont	**font;		/* indexed array */
	int	nfont;		/* number allocated */
	BSubfont**subfont;	/* indexed array */
	int	nsubfont;	/* number allocated */
	Arena	*arena;		/* array */
	int	narena;		/* number allocated */
	int	mouseopen;	/* flag: mouse open */
	int	bitbltopen;	/* flag: bitblt open */
	int	bid;		/* last allocated bitmap id */
	int	subfid;		/* last allocated subfont id */
	int	cacheid;	/* last cached subfont id */
	int	fid;		/* last allocated font id */
	int	init;		/* freshly opened; init message pending */
	int	rid;		/* read bitmap id */
	int	rminy;		/* read miny */
	int	rmaxy;		/* read maxy */
	int	mid;		/* colormap read bitmap id */
}bit;

#define	DMAP	16		/* delta increase in size of arrays */
#define	FREE	0x80000000

void	bitcompact(void);
int	bitalloc(Rectangle, int);
void	bitfree(GBitmap*);
void	fontfree(GFont*);
void	subfontfree(BSubfont*, int);
void	arenafree(Arena*);
void	bitstring(GBitmap*, Point, GFont*, uchar*, long, Fcode);
void	bitloadchar(GFont*, int, GSubfont*, int);
extern	GBitmap	gscreen;

typedef struct Mouseinfo	Mouseinfo;
typedef struct Cursorinfo	Cursorinfo;

struct Mouseinfo
{
	/*
	 * First three fields are known in some l.s's
	 */
	int	dx;
	int	dy;
	int	track;		/* l.s has updated dx & dy */
	Mouse;
	int	redraw;		/* update cursor on screen */
	ulong	counter;	/* increments every update */
	ulong	lastcounter;	/* value when /dev/mouse read */
	Rendez	r;
};

struct Cursorinfo
{
	Cursor;
	Lock;
	int	visible;	/* on screen */
	int	disable;	/* from being used */
	Rectangle r;		/* location */
};

Mouseinfo	mouse;
Cursorinfo	cursor;
int		mouseshifted;
int		mousetype;
int		islittle;
int		hwcurs;

Cursor	arrow =
{
	{-1, -1},
	{0xFF, 0xE0, 0xFF, 0xE0, 0xFF, 0xC0, 0xFF, 0x00,
	 0xFF, 0x00, 0xFF, 0x80, 0xFF, 0xC0, 0xFF, 0xE0,
	 0xE7, 0xF0, 0xE3, 0xF8, 0xC1, 0xFC, 0x00, 0xFE,
	 0x00, 0x7F, 0x00, 0x3E, 0x00, 0x1C, 0x00, 0x08,
	},
	{0x00, 0x00, 0x7F, 0xC0, 0x7F, 0x00, 0x7C, 0x00,
	 0x7E, 0x00, 0x7F, 0x00, 0x6F, 0x80, 0x67, 0xC0,
	 0x43, 0xE0, 0x41, 0xF0, 0x00, 0xF8, 0x00, 0x7C,
	 0x00, 0x3E, 0x00, 0x1C, 0x00, 0x08, 0x00, 0x00,
	}
};

ulong setbits[16];
GBitmap	set =
{
	setbits,
	0,
	1,
	0,
	{0, 0, 16, 16},
	{0, 0, 16, 16}
};

ulong clrbits[16];
GBitmap	clr =
{
	clrbits,
	0,
	1,
	0,
	{0, 0, 16, 16},
	{0, 0, 16, 16}
};

ulong cursorbackbits[16*4];
GBitmap cursorback =
{
	cursorbackbits,
	0,
	1,
	0,
	{0, 0, 16, 16},
	{0, 0, 16, 16}
};

void	Cursortocursor(Cursor*);
int	mousechanged(void*);

enum{
	Qdir,
	Qbitblt,
	Qmouse,
	Qmousectl,
	Qscreen,
};

Dirtab bitdir[]={
	"bitblt",	{Qbitblt},	0,			0666,
	"mouse",	{Qmouse},	0,			0666,
	"mousectl",	{Qmousectl},	0,			0220,
	"screen",	{Qscreen},	0,			0444,
};

#define	NBIT	(sizeof bitdir/sizeof(Dirtab))
#define	NINFO	8192	/* max chars per subfont; sanity check only */
#define	HDR	3

void
lockedupdate(void)
{
	qlock(&bit);
	if(waserror()){
		qunlock(&bit);
		return;
	}
	screenupdate();
	qunlock(&bit);
	poperror();
}

void
bitfreeup(void)
{
	int i;
	BSubfont *s;

	/* free unused subfonts and compact */
	for(i=0; i<bit.nsubfont; i++){
		s = bit.subfont[i];
		if(s && s!=bdefont && s->ref==0){
			s->ref = 1;
			s->qid[0] = ~0;	/* force cleanup */
			subfontfree(s, i);
		}
	}
	bitcompact();
}

void*
bitmalloc(ulong n)
{
	void *p;

	p = malloc(n);
	if(p)
		return p;
	bitfreeup();
	return malloc(n);
}

void
bitdebug(void)
{
	int i;
	long l;
	Arena *a;

	l = 0;
	for(i=0; i<bit.narena; i++){
		a = &bit.arena[i];
		if(a->words){
			l += a->nwords;
			print("%d: %ld bytes used; %ld total\n", i,
				(a->wfree-a->words)*sizeof(ulong),
				a->nwords*sizeof(ulong));
		}
	}
	print("arena: %ld bytes\n", l*sizeof(ulong));
	l = 0;
	for(i=0; i<bit.nmap; i++)
		if(bit.map[i])
			l++;
	print("%d bitmaps ", l);
	l = 0;
	for(i=0; i<bit.nfont; i++)
		if(bit.font[i])
			l++;
	print("%d fonts ", l);
	l = 0;
	for(i=0; i<bit.nsubfont; i++)
		if(bit.subfont[i]){
			print("%d: %lux %lux ", i, bit.subfont[i]->qid[0], bit.subfont[i]->qid[1]);
			l++;
		}
	print("%d subfonts\n", l);
}

void
bitreset(void)
{
	int ws;
	ulong r;
	Arena *a;

	if(!conf.monitor)
		return;

	memmove(&bdefont0, defont, sizeof(*defont));
	bdefont = &bdefont0;
	bit.map = smalloc(DMAP*sizeof(GBitmap*));
	bit.nmap = DMAP;
	getcolor(0, &r, &r, &r);
	if(r == 0)
		flipping = 1;
	bit.bid = -1;
	bit.subfid = -1;
	bit.fid = -1;
	bit.cacheid = -1;
	bit.font = smalloc(DMAP*sizeof(GFont*));
	bit.nfont = DMAP;
	bit.subfont = smalloc(DMAP*sizeof(BSubfont*));
	bit.nsubfont = DMAP;
	bit.arena = smalloc(DMAP*sizeof(Arena));
	bit.narena = DMAP;
	a = &bit.arena[0];
	/*
	 * Somewhat of a heuristic: start with three screensful and
	 * allocate single screensful dynamically if needed.
	 */

	ws = BI2WD>>gscreen.ldepth;	/* pixels per word */
	a->nwords = 3*(HDR + gscreen.r.max.y*gscreen.r.max.x/ws);
	a->words = xalloc(a->nwords*sizeof(ulong));
	if(a->words == 0){
		/* try again */
		print("bitreset: allocating only 1 screenful\n");
		a->nwords /= 3;
		a->words = a->words = xalloc(a->nwords*sizeof(ulong));
		if(a->words == 0)
			panic("bitreset");
	}
	a->wfree = a->words;
	a->nbusy = 1;	/* keep 0th arena from being freed */
	Cursortocursor(&arrow);
}

/*
 *  screen bit depth changed, reset backup map for cursor
 */
void
bitdepth(void)
{
	cursoroff(1);
	if(gscreen.ldepth > 3)
		cursorback.ldepth = 0;
	else{
		cursorback.ldepth = gscreen.ldepth;
		cursorback.width = ((16 << gscreen.ldepth) + 31) >> 5;
	}
	cursoron(1);
}

void
bitinit(void)
{
	if(!conf.monitor)
		return;
	if(gscreen.ldepth > 3)
		cursorback.ldepth = 0;
	else{
		cursorback.ldepth = gscreen.ldepth;
		cursorback.width = ((16 << gscreen.ldepth) + 31) >> 5;
	}
	cursoron(1);
}

Chan*
bitattach(char *spec)
{
	if(!conf.monitor)
		error(Egreg);
	return devattach('b', spec);
}

Chan*
bitclone(Chan *c, Chan *nc)
{
	nc = devclone(c, nc);
	if(c->qid.path != CHDIR)
		incref(&bit);
	return nc;
}

int
bitwalk(Chan *c, char *name)
{
	return devwalk(c, name, bitdir, NBIT, devgen);
}

void
bitstat(Chan *c, char *db)
{
	devstat(c, db, bitdir, NBIT, devgen);
}

Chan*
bitopen(Chan *c, int omode)
{
	GBitmap *b;

	switch(c->qid.path){
	case CHDIR:
		if(omode != OREAD)
			error(Eperm);
		break;
	case Qmouse:
		lock(&bit);
		if(bit.mouseopen){
			unlock(&bit);
			error(Einuse);
		}
		bit.mouseopen = 1;
		bit.ref++;
		unlock(&bit);
		break;
	case Qbitblt:
		lock(&bit);
		if(bit.bitbltopen || bit.mouseopen){
			unlock(&bit);
			error(Einuse);
		}
		b = smalloc(sizeof(GBitmap));
		*b = gscreen;
		bit.map[0] = b;			/* bitmap 0 is screen */
		bit.subfont[0] = bdefont;	/* subfont 0 is default */
		bit.subfont[0]->ref = 1;
		bit.subfont[0]->qid[0] = 0;
		bit.subfont[0]->qid[1] = 0;
		bit.bid = -1;
		bit.fid = -1;
		bit.subfid = -1;
		bit.cacheid = -1;
		bit.rid = -1;
		bit.mid = -1;
		bit.init = 0;
		bit.bitbltopen = 1;
		Cursortocursor(&arrow);
		unlock(&bit);
		break;
	default:
		incref(&bit);
	}
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

void
bitcreate(Chan *c, char *name, int omode, ulong perm)
{
	if(!conf.monitor)
		error(Egreg);
	USED(c, name, omode, perm);
	error(Eperm);
}

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

void
bitwstat(Chan *c, char *db)
{
	USED(c, db);
	error(Eperm);
}

void
bitclose(Chan *c)
{
	GBitmap *b, **bp, **ebp;
	BSubfont *s, **sp, **esp;
	GFont *f, **fp, **efp;

	if(c->qid.path!=CHDIR && (c->flag&COPEN)){
		lock(&bit);
		if(c->qid.path == Qmouse)
			bit.mouseopen = 0;
		if(c->qid.path == Qbitblt)
			bit.bitbltopen = 0;
		if(--bit.ref == 0){
			ebp = &bit.map[bit.nmap];
			for(bp = bit.map; bp<ebp; bp++){
				b = *bp;
				if(b){
					bitfree(b);
					*bp = 0;
				}
			}
			esp = &bit.subfont[bit.nsubfont];
			for(sp=bit.subfont; sp<esp; sp++){
				s = *sp;
				if(s)
					subfontfree(s, sp-bit.subfont);
				/* don't clear *sp: cached */
			}
			efp = &bit.font[bit.nfont];
			for(fp=bit.font; fp<efp; fp++){
				f = *fp;
				if(f){
					fontfree(f);
					*fp = 0;
				}
			}
		}
		unlock(&bit);
	}
}

long
bitread(Chan *c, void *va, long n, ulong offset)
{
	uchar *p, *q;
	long miny, maxy, t, x, y;
	ulong l, nw, ws, rv, gv, bv;
	int off, j;
	Fontchar *i;
	GBitmap *src;
	BSubfont *s;

	if(c->qid.path & CHDIR)
		return devdirread(c, va, n, bitdir, NBIT, devgen);

	if(c->qid.path == Qmouse){
		/*
		 * mouse:
		 *	'm'		1
		 *	buttons		1
		 * 	point		8
		 * 	msec		4
		 */
		if(n < 14)
			error(Ebadblt);
		while(mousechanged(0) == 0)
			sleep(&mouse.r, mousechanged, 0);
		lock(&cursor);
		p = va;
		p[0] = 'm';
		p[1] = mouse.buttons;
		BPLONG(p+2, mouse.xy.x);
		BPLONG(p+6, mouse.xy.y);
		BPLONG(p+10, TK2MS(MACHP(0)->ticks));
		mouse.lastcounter = mouse.counter;
		unlock(&cursor);
		return 14;
	}
	if(c->qid.path == Qscreen){
		if(offset==0){
			if(n < 5*12)
				error(Eio);
			sprint(va, "%11d %11d %11d %11d %11d",
				gscreen.ldepth, gscreen.r.min.x,
				gscreen.r.min.y, gscreen.r.max.x,
				gscreen.r.max.y);
			return 5*12;
		}
		ws = 1<<(3-gscreen.ldepth);	/* pixels per byte */
		l = (gscreen.r.max.x+ws-1)/ws - gscreen.r.min.x/ws;
		t = offset-5*12;
		miny = t/l;	/* unsigned computation */
		maxy = (t+n)/l;
		if(miny >= gscreen.r.max.y)
			return 0;
		if(maxy >= gscreen.r.max.y)
			maxy = gscreen.r.max.y;
		n = 0;
		p = va;
		for(y=miny; y<maxy; y++){
			q = (uchar*)gaddr(&gscreen, Pt(0, y));
			memmove(p, q, l);
			if(flipping)
				/* is screen, so must be word aligned */
				for(x=0; x<l; x+=sizeof(ulong),p+=sizeof(ulong))
					*(ulong*)p ^= ~0;
			else
				p += l;
			n += l;
		}
		return n;
	}
	if(c->qid.path != Qbitblt)
		error(Egreg);

	qlock(&bit);
	if(waserror()){
		qunlock(&bit);
		nexterror();
	}
	p = va;
	/*
	 * Fuss about and figure out what to say.
	 */
	if(bit.init){
		/*
		 * init:
		 *	'I'		1
		 *	ldepth		1
		 * 	rectangle	16
		 * 	clip rectangle	16
		 *	font info	3*12
		 *	fontchars	6*(bdefont->n+1)
		 */
		if(n < 34)
			error(Ebadblt);
		p[0] = 'I';
		p[1] = gscreen.ldepth;
		BPLONG(p+2, gscreen.r.min.x);
		BPLONG(p+6, gscreen.r.min.y);
		BPLONG(p+10, gscreen.r.max.x);
		BPLONG(p+14, gscreen.r.max.y);
		BPLONG(p+18, gscreen.clipr.min.x);
		BPLONG(p+22, gscreen.clipr.min.y);
		BPLONG(p+26, gscreen.clipr.max.x);
		BPLONG(p+30, gscreen.clipr.max.y);
		if(n >= 34+3*12+6*(bdefont->n+1)){
			p += 34;
			sprint((char*)p, "%11d %11d %11d ", bdefont->n,
				bdefont->height, bdefont->ascent);
			p += 3*12;
			for(i=bdefont->info,j=0; j<=bdefont->n; j++,i++,p+=6){
				BPSHORT(p, i->x);
				p[2] = i->top;
				p[3] = i->bottom;
				p[4] = i->left;
				p[5] = i->width;
			}
			n = 34+3*12+6*(bdefont->n+1);
		}else
			n = 34;
		bit.init = 0;
	}else if(bit.bid > 0){
		/*
		 * allocate:
		 *	'A'		1
		 *	bitmap id	2
		 */
		if(n < 3)
			error(Ebadblt);
		if(bit.bid<0 || bit.map[bit.bid]==0)
			error(Ebadbitmap);
		p[0] = 'A';
		BPSHORT(p+1, bit.bid);
		bit.bid = -1;
		n = 3;
	}else if(bit.subfid > 0){
		/*
		 * allocate subfont:
		 *	'K'		1
		 *	subfont id	2
		 */
		if(n<3 || bit.subfid<0)
			error(Ebadblt);
		s = bit.subfont[bit.subfid];
		if(s==0 || s->ref==0)
			error(Ebadfont);
		p[0] = 'K';
		BPSHORT(p+1, bit.subfid);
		bit.subfid = -1;
		n = 3;
	}else if(bit.cacheid >= 0){
		/*
		 * check cache for subfont:
		 *	'J'		1
		 *	subfont id	2
		 *	font info	3*12
		 *	fontchars	6*(subfont->n+1)
		 */
		p[0] = 'J';
		if(bit.cacheid < 0)
			error(Ebadfont);
		s = bit.subfont[bit.cacheid];
		if(s==0 || s->ref==0)
			error(Ebadfont);
		if(n < 3+3*12+6*(s->n+1))
			error(Ebadblt);
		BPSHORT(p+1, bit.cacheid);
		p += 3;
		sprint((char*)p, "%11d %11d %11d ", s->n, s->height, s->ascent);
		p += 3*12;
		for(i=s->info,j=0; j<=s->n; j++,i++,p+=6){
			BPSHORT(p, i->x);
			p[2] = i->top;
			p[3] = i->bottom;
			p[4] = i->left;
			p[5] = i->width;
		}
		n = 3+3*12+6*(s->n+1);
		bit.cacheid = -1;
	}else if(bit.fid >= 0){
		/*
		 * allocate font:
		 *	'N'		1
		 *	font id		2
		 */
		if(n < 3)
			error(Ebadblt);
		if(bit.fid<0 || bit.font[bit.fid]==0)
			error(Ebadfont);
		p[0] = 'N';
		BPSHORT(p+1, bit.fid);
		bit.fid = -1;
		n = 3;
	}else if(bit.mid >= 0){
		/*
		 * read colormap:
		 *	data		12*(2**bitmapdepth)
		 */
		src = bit.map[bit.mid];
		if(src == 0)
			error(Ebadbitmap);
		l = (1<<src->ldepth);
		nw = 1 << l;
		if(n < 12*nw)
			error(Ebadblt);
		for(j = 0; j < nw; j++){
			if(bit.mid == 0){
				getcolor(flipping? nw-j-1 : j, &rv, &gv, &bv);
			}else{
				rv = j;
				for(off = 32-l; off > 0; off -= l)
					rv = (rv << l) | j;
				gv = bv = rv;
			}
			BPLONG(p, rv);
			BPLONG(p+4, gv);
			BPLONG(p+8, bv);
			p += 12;
		}
		bit.mid = -1;
		n = 12*nw;
	}else if(bit.rid >= 0){
		/*
		 * read bitmap:
		 *	data		bytewidth*(maxy-miny)
		 */
		src = bit.map[bit.rid];
		if(src == 0)
			error(Ebadbitmap);
		off = 0;
		if(bit.rid == 0)
			off = 1;
		miny = bit.rminy;
		maxy = bit.rmaxy;
		if(miny>maxy || miny<src->r.min.y || maxy>src->r.max.y)
			error(Ebadblt);
		ws = 1<<(3-src->ldepth);	/* pixels per byte */
		/* set l to number of bytes of incoming data per scan line */
		if(src->r.min.x >= 0)
			l = (src->r.max.x+ws-1)/ws - src->r.min.x/ws;
		else{	/* make positive before divide */
			t = (-src->r.min.x)+ws-1;
			t = (t/ws)*ws;
			l = (t+src->r.max.x+ws-1)/ws;
		}
		if(n < l*(maxy-miny))
			error(Ebadblt);
		if(off)
			cursoroff(1);
		n = 0;
		p = va;
		for(y=miny; y<maxy; y++){
			q = (uchar*)gaddr(src, Pt(src->r.min.x, y));
			q += (src->r.min.x&((sizeof(ulong))*ws-1))/ws;
			memmove(p, q, l);
			if(islittle)
				pixreverse(p, l, src->ldepth);
			if(bit.rid==0 && flipping)
				/* is screen, so must be word aligned */
				for(x=0; x<l; x+=sizeof(ulong),p+=sizeof(ulong))
					*(ulong*)p ^= ~0;
			else
				p += l;
			n += l;
		}
		if(off)
			cursoron(1);
		bit.rid = -1;
	}

	poperror();
	qunlock(&bit);
	return n;
}

Point
bitstrsize(GFont *f, uchar *p, int l)
{
	ushort r;
	Point s = {0,0};
	GCacheinfo *c;

	while(l > 0){
		r = BGSHORT(p);
		p += 2;
		l -= 2;
		if(r >= f->ncache)
			continue;
		c = &f->cache[r];
		if(c->bottom > s.y)
			s.y = c->bottom;
		s.x += c->width;
	}
	return s;
}

long
bitwrite(Chan *c, void *va, long n, ulong offset)
{
	uchar *p, *q, *oq;
	long m, v, miny, maxy, t, x, y;
	ulong l, nw, ws, rv, q0, q1;
	ulong *lp;
	int off, isoff, i, j, ok;
 	ulong *endscreen = gaddr(&gscreen, Pt(0, gscreen.r.max.y));
	Point pt, pt1, pt2;
	Rectangle rect;
	Cursor curs;
	Fcode fc;
	Fontchar *fcp;
	GBitmap *src, *dst;
	BSubfont *f, *tf, **fp;
	GFont *ff, **ffp;
	GCacheinfo *gc;
	char buf[64];

	USED(offset);

	if(c->qid.path == CHDIR)
		error(Eisdir);

	if(c->qid.path == Qmousectl){
		if(n >= sizeof(buf))
			n = sizeof(buf)-1;
		strncpy(buf, va, n);
		buf[n] = 0;
		mousectl(buf);
		return n;
	}

	if(c->qid.path != Qbitblt)
		error(Egreg);

	isoff = 0;
	qlock(&bit);
	if(waserror()){
		qunlock(&bit);
		if(isoff)
			cursoron(1);
		nexterror();
	}
	p = va;
	m = n;
	SET(src, dst, f, ff);
	while(m > 0)
		switch(*p){
		default:
			pprint("bitblt request 0x%x\n", *p);
			error(Ebadblt);

		case 'a':
			/*
			 * allocate:
			 *	'a'		1
			 *	ldepth		1
			 *	Rectangle	16
			 * next read returns allocated bitmap id
			 */
			if(m < 18)
				error(Ebadblt);
			v = *(p+1);
			if(v > 3)	/* BUG */
				error(Ebadblt);
			rect.min.x = BGLONG(p+2);
			rect.min.y = BGLONG(p+6);
			rect.max.x = BGLONG(p+10);
			rect.max.y = BGLONG(p+14);
			if(Dx(rect) < 0 || Dy(rect) < 0)
				error(Ebadblt);
			bit.bid = bitalloc(rect, v);
			m -= 18;
			p += 18;
			break;

		case 'b':
			/*
			 * bitblt
			 *	'b'		1
			 *	dst id		2
			 *	dst Point	8
			 *	src id		2
			 *	src Rectangle	16
			 *	code		2
			 */
			if(m < 31)
				error(Ebadblt);
			fc = BGSHORT(p+29) & 0xF;
			v = BGSHORT(p+11);
			if(v<0 || v>=bit.nmap || (src=bit.map[v])==0)
				error(Ebadbitmap);
			off = 0;
			if(v == 0){
				if(flipping)
					fc = flipS[fc];
				off = 1;
			}
			v = BGSHORT(p+1);
			if(v<0 || v>=bit.nmap || (dst=bit.map[v])==0)
				error(Ebadbitmap);
			if(v == 0){
				if(flipping)
					fc = flipD[fc];
				off = 1;
			}
			pt.x = BGLONG(p+3);
			pt.y = BGLONG(p+7);
			rect.min.x = BGLONG(p+13);
			rect.min.y = BGLONG(p+17);
			rect.max.x = BGLONG(p+21);
			rect.max.y = BGLONG(p+25);
			if(off && !isoff){
				cursoroff(1);
				isoff = 1;
			}
			gbitblt(dst, pt, src, rect, fc);
			if(dst->base < endscreen)
				mbbrect(Rpt(pt, add(pt, sub(rect.max, rect.min))));
			m -= 31;
			p += 31;
			break;

		case 'c':
			/*
			 * cursorswitch
			 *	'c'		1
			 * if one more byte, says whether to disable
			 * because of stupid lcd's (thank you bart)
			 * else
			 * nothing more: return to arrow; else
			 * 	Point		8
			 *	clr		32
			 *	set		32
			 */
			if(m == 1){
				if(!isoff){
					cursoroff(1);
					isoff = 1;
				}
				Cursortocursor(&arrow);
				m -= 1;
				p += 1;
				break;
			}
			if(m == 2){
				if(p[1]){	/* make damn sure */
					cursor.disable = 0;
					isoff = 1;
				}else{
					cursoroff(1);
					cursor.disable = 1;
				}
				m -= 2;
				p += 2;
				break;
			}
			if(m < 73)
				error(Ebadblt);
			curs.offset.x = BGLONG(p+1);
			curs.offset.y = BGLONG(p+5);
			memmove(curs.clr, p+9, 2*16);
			memmove(curs.set, p+41, 2*16);
			if(islittle){
				pixreverse(curs.clr, 2*16, 0);
				pixreverse(curs.set, 2*16, 0);
			}
			if(!isoff){
				cursoroff(1);
				isoff = 1;
			}
			Cursortocursor(&curs);
			m -= 73;
			p += 73;
			break;

		case 'e':
			/*
			 * polysegment
			 *
			 *	'e'		1
			 *	id		2
			 *	pt		8
			 *	value		1
			 *	code		2
			 *	n		2
			 *	pts		2*n
			 */
			if(m < 16)
				error(Ebadblt);
			l = BGSHORT(p+14);
			if(m < 16+2*l)
				error(Ebadblt);
			v = BGSHORT(p+1);
			if(v<0 || v>=bit.nmap || (dst=bit.map[v])==0)
				error(Ebadbitmap);
			off = 0;
			fc = BGSHORT(p+12) & 0xF;
			if(v == 0){
				if(flipping)
					fc = flipD[fc];
				off = 1;
			}
			pt1.x = BGLONG(p+3);
			pt1.y = BGLONG(p+7);
			t = p[11];
			if(off && !isoff){
				cursoroff(1);
				isoff = 1;
			}
			p += 16;
			m -= 16;
			while(l > 0){
				pt2.x = pt1.x + (schar)p[0];
				pt2.y = pt1.y + (schar)p[1];
				gsegment(dst, pt1, pt2, t, fc);
				if(dst->base < endscreen){
					mbbpt(pt1);
					mbbpt(pt2);
				}
				pt1 = pt2;
				p += 2;
				m -= 2;
				l--;
			}
			break;

		case 'f':
			/*
			 * free
			 *	'f'		1
			 *	id		2
			 */
			if(m < 3)
				error(Ebadblt);
			v = BGSHORT(p+1);
			if(v<0 || v>=bit.nmap || (dst=bit.map[v])==0)
				error(Ebadbitmap);
			bitfree(dst);
			bit.map[v] = 0;
			m -= 3;
			p += 3;
			break;

		case 'g':
			/*
			 * free subfont
			 *	'g'		1
			 *	id		2
			 */
			if(m < 3)
				error(Ebadblt);
			v = BGSHORT(p+1);
			if(v<0 || v>=bit.nsubfont || (f=bit.subfont[v])==0 || f->ref==0)
				error(Ebadfont);
			subfontfree(f, v);
			m -= 3;
			p += 3;
			break;

		case 'h':
			/*
			 * free font
			 *	'h'		1
			 *	id		2
			 */
			if(m < 3)
				error(Ebadblt);
			v = BGSHORT(p+1);
			if(v<0 || v>=bit.nfont || (ff=bit.font[v])==0)
				error(Ebadfont);
			fontfree(ff);
			bit.font[v] = 0;
			m -= 3;
			p += 3;
			break;

		case 'i':
			/*
			 * init
			 *
			 *	'i'		1
			 */
			bit.init = 1;
			m -= 1;
			p += 1;
			break;

		case 'j':
			/*
			 * subfont cache check
			 *
			 *	'j'		1
			 *	qid		8
			 */
			if(m < 9)
				error(Ebadblt);
			q0 = BGLONG(p+1);
			q1 = BGLONG(p+5);
			i = 0;
			if(q0 != ~0)
				for(; i<bit.nsubfont; i++){
					f = bit.subfont[i];
					if(f && f->qid[0]==q0 && f->qid[1]==q1)
						goto sfcachefound;
				}
			error(Esfnotcached);

		sfcachefound:
			f->ref++;
			bit.cacheid = i;
			m -= 9;
			p += 9;
			break;

		case 'k':
			/*
			 * allocate subfont
			 *	'k'		1
			 *	n		2
			 *	height		1
			 *	ascent		1
			 *	bitmap id	2
			 *	qid		8
			 *	fontchars	6*(n+1)
			 * next read returns allocated font id
			 */
			if(m < 15)
				error(Ebadblt);
			v = BGSHORT(p+1);
			if(v<0 || v>NINFO || m<15+6*(v+1))
				error(Ebadblt);
			for(i=1; i<bit.nsubfont; i++)
				if(bit.subfont[i] == 0)
					goto subfontfound;
			fp = bitmalloc((bit.nsubfont+DMAP)*sizeof(BSubfont*));
			if(fp == 0)
				error(Enomem);
			memmove(fp, bit.subfont, bit.nsubfont*sizeof(BSubfont*));
			free(bit.subfont);
			bit.subfont = fp;
			bit.nsubfont += DMAP;
		subfontfound:
			f = bitmalloc(sizeof(BSubfont));
			if(f == 0)
				error(Enomem);
			f->info = bitmalloc((v+1)*sizeof(Fontchar));
			if(f->info == 0){
				free(f);
				error(Enomem);
			}
			bit.subfont[i] = f;
			f->n = v;
			f->height = p[3];
			f->ascent = p[4];
			f->qid[0] = BGLONG(p+7);
			f->qid[1] = BGLONG(p+11);
			/* check to see if already there, uncache if so */
			for(j=0; j<bit.nsubfont; j++){
				if(j == i)
					continue;
				tf = bit.subfont[j];
				if(tf && tf->qid[0]==f->qid[0] && tf->qid[1]==f->qid[1]){
					f->qid[0] = ~0;	/* uncached */
					break;
				}
			}
			f->ref = 1;
			v = BGSHORT(p+5);
			if(v<0 || v>=bit.nmap || (dst=bit.map[v])==0)
				error(Ebadbitmap);
			f->bits = dst;
			bit.map[v] = 0;	/* subfont now owns bitmap */
			m -= 15;
			p += 15;
			fcp = f->info;
			for(j=0; j<=f->n; j++,fcp++){
				fcp->x = BGSHORT(p);
				fcp->top = p[2];
				fcp->bottom = p[3];
				fcp->left = p[4];
				fcp->width = p[5];
				fcp->top = p[2];
				p += 6;
				m -= 6;
			}
			bit.subfid = i;
			break;

		case 'l':
			/*
			 * line segment
			 *
			 *	'l'		1
			 *	id		2
			 *	pt1		8
			 *	pt2		8
			 *	value		1
			 *	code		2
			 */
			if(m < 22)
				error(Ebadblt);
			v = BGSHORT(p+1);
			if(v<0 || v>=bit.nmap || (dst=bit.map[v])==0)
				error(Ebadbitmap);
			off = 0;
			fc = BGSHORT(p+20) & 0xF;
			if(v == 0){
				if(flipping)
					fc = flipD[fc];
				off = 1;
			}
			pt1.x = BGLONG(p+3);
			pt1.y = BGLONG(p+7);
			pt2.x = BGLONG(p+11);
			pt2.y = BGLONG(p+15);
			t = p[19];
			if(off && !isoff){
				cursoroff(1);
				isoff = 1;
			}
			gsegment(dst, pt1, pt2, t, fc);
			if(dst->base < endscreen){
				mbbpt(pt1);
				mbbpt(pt2);
			}
			m -= 22;
			p += 22;
			break;

		case 'm':
			/*
			 * read colormap
			 *
			 *	'm'		1
			 *	id		2
			 */
			if(m < 3)
				error(Ebadblt);
			v = BGSHORT(p+1);
			if(v<0 || v>=bit.nmap || (dst=bit.map[v])==0)
				error(Ebadbitmap);
			bit.mid = v;
			m -= 3;
			p += 3;
			break;

		case 'n':
			/*
			 * allocate font
			 *	'n'		1
			 *	height		1
			 *	ascent		1
			 *	ldepth		2
			 *	ncache		2
			 * next read returns allocated font id
			 */
			if(m < 7)
				error(Ebadblt);
			v = BGSHORT(p+3);
			t = BGSHORT(p+5);
			if(v<0 || t<0)
				error(Ebadblt);
			for(i=0; i<bit.nfont; i++)
				if(bit.font[i] == 0)
					goto fontfound;
			ffp = bitmalloc((bit.nfont+DMAP)*sizeof(GFont*));
			if(ffp == 0)
				error(Enomem);
			memmove(ffp, bit.font, bit.nfont*sizeof(GFont*));
			free(bit.font);
			bit.font = ffp;
			bit.nfont += DMAP;
		fontfound:
			ff = bitmalloc(sizeof(GFont));
			if(ff == 0)
				error(Enomem);
			ff->ncache = t;
			ff->cache = bitmalloc(t*sizeof(GCacheinfo));
			if(ff->cache == 0){
				free(ff);
				error(Enomem);
			}
			bit.font[i] = ff;
			ff = bit.font[i];
			ff->height = p[1];
			ff->ascent = p[2];
			ff->ldepth = v;
			ff->width = 0;
			ff->b = 0;
			m -= 7;
			p += 7;
			bit.fid = i;
			break;

		case 'p':
			/*
			 * point
			 *
			 *	'p'		1
			 *	id		2
			 *	pt		8
			 *	value		1
			 *	code		2
			 */
			if(m < 14)
				error(Ebadblt);
			v = BGSHORT(p+1);
			if(v<0 || v>=bit.nmap || (dst=bit.map[v])==0)
				error(Ebadbitmap);
			off = 0;
			fc = BGSHORT(p+12) & 0xF;
			if(v == 0){
				if(flipping)
					fc = flipD[fc];
				off = 1;
			}
			pt1.x = BGLONG(p+3);
			pt1.y = BGLONG(p+7);
			t = p[11];
			if(off && !isoff){
				cursoroff(1);
				isoff = 1;
			}
			gpoint(dst, pt1, t, fc);
			if(dst->base < endscreen)
				mbbpt(pt1);
			m -= 14;
			p += 14;
			break;

		case 'q':
			/*
			 * clip rectangle
			 *	'q'		1
			 *	id		2
			 *	rect		16
			 */
			if(m < 19)
				error(Ebadblt);
			v = BGSHORT(p+1);
			if(v<0 || v>=bit.nmap || (dst=bit.map[v])==0)
				error(Ebadbitmap);
			rect.min.x = BGLONG(p+3);
			rect.min.y = BGLONG(p+7);
			rect.max.x = BGLONG(p+11);
			rect.max.y = BGLONG(p+15);
			if(rectclip(&rect, dst->r))
				dst->clipr = rect;
			else
				dst->clipr = dst->r;
			m -= 19;
			p += 19;
			break;

		case 'r':
			/*
			 * read
			 *	'r'		1
			 *	src id		2
			 *	miny		4
			 *	maxy		4
			 */
			if(m < 11)
				error(Ebadblt);
			v = BGSHORT(p+1);
			if(v<0 || v>=bit.nmap || (src=bit.map[v])==0)
				error(Ebadbitmap);
			miny = BGLONG(p+3);
			maxy = BGLONG(p+7);
			if(miny>maxy || miny<src->r.min.y || maxy>src->r.max.y)
				error(Ebadblt);
			bit.rid = v;
			bit.rminy = miny;
			bit.rmaxy = maxy;
			p += 11;
			m -= 11;
			break;

		case 's':
			/*
			 * string
			 *	's'		1
			 *	id		2
			 *	pt		8
			 *	font id		2
			 *	code		2
			 *	n		2
			 * 	cache indices	2*n (not null terminated)
			 */
			if(m < 17)
				error(Ebadblt);
			v = BGSHORT(p+1);
			if(v<0 || v>=bit.nmap || (dst=bit.map[v])==0)
				error(Ebadbitmap);
			off = 0;
			fc = BGSHORT(p+13) & 0xF;
			if(v == 0){
				if(flipping)
					fc = flipD[fc];
				off = 1;
			}
			pt.x = BGLONG(p+3);
			pt.y = BGLONG(p+7);
			v = BGSHORT(p+11);
			if(v<0 || v>=bit.nfont || (ff=bit.font[v])==0)
				error(Ebadfont);
			l = BGSHORT(p+15)*2;
			p += 17;
			m -= 17;
			if(l > m)
				error(Ebadblt);
			if(off && !isoff){
				cursoroff(1);
				isoff = 1;
			}
			bitstring(dst, pt, ff, p, l, fc);
			if(dst->base < endscreen)
				mbbrect(Rpt(pt, add(pt, bitstrsize(ff, p, l))));
			m -= l;
			p += l;
			break;

		case 't':
			/*
			 * texture
			 *	't'		1
			 *	dst id		2
			 *	rect		16
			 *	src id		2
			 *	fcode		2
			 */
			if(m < 23)
				error(Ebadblt);
			v = BGSHORT(p+1);
			if(v<0 || v>=bit.nmap || (dst=bit.map[v])==0)
				error(Ebadbitmap);
			off = 0;
			fc = BGSHORT(p+21) & 0xF;
			if(v == 0){
				if(flipping)
					fc = flipD[fc];
				off = 1;
			}
			rect.min.x = BGLONG(p+3);
			rect.min.y = BGLONG(p+7);
			rect.max.x = BGLONG(p+11);
			rect.max.y = BGLONG(p+15);
			v = BGSHORT(p+19);
			if(v<0 || v>=bit.nmap || (src=bit.map[v])==0)
				error(Ebadbitmap);
			if(off && !isoff){
				cursoroff(1);
				isoff = 1;
			}
			gtexture(dst, rect, src, fc);
			if(dst->base < endscreen)
				mbbrect(rect);
			m -= 23;
			p += 23;
			break;

		case 'v':
			/*
			 * clear font cache and bitmap.
			 * if error, font is unchanged.
			 *	'v'		1
			 *	id		2
			 *	ncache		2
			 *	width		2
			 */
			if(m < 7)
				error(Ebadblt);
			v = BGSHORT(p+1);
			t = BGSHORT(p+3);
			if(t<0 || v<0 || v>=bit.nfont || (ff=bit.font[v])==0)
				error(Ebadblt);
			x = BGSHORT(p+5);
			i = bitalloc(Rect(0, 0, t*x, ff->height), ff->ldepth);
			if(t != ff->ncache){
				gc = bitmalloc(t*sizeof(ff->cache[0]));
				if(gc == 0){
					bitfree(bit.map[i]);
					bit.map[i] = 0;
					error(Enomem);
				}
				free(ff->cache);
				ff->cache = gc;
				ff->ncache = t;
			}else{
				/*
				 * memset not necessary but helps avoid
				 * confusion if the cache is mishandled by the
				 * user.
				 */
				memset(ff->cache, 0, t*sizeof(ff->cache[0]));
			}
			if(ff->b)
				bitfree(ff->b);
			ff->b = bit.map[i];
			bit.map[i] = 0;	/* disconnect it from GBitmap space */
			ff->width = x;
			p += 7;
			m -= 7;
			break;

		case 'w':
			/*
			 * write
			 *	'w'		1
			 *	dst id		2
			 *	miny		4
			 *	maxy		4
			 *	data		bytewidth*(maxy-miny)
			 */
			if(m < 11)
				error(Ebadblt);
			v = BGSHORT(p+1);
			if(v<0 || v>=bit.nmap || (dst=bit.map[v])==0)
				error(Ebadbitmap);
			off = 0;
			if(v == 0)
				off = 1;
			miny = BGLONG(p+3);
			maxy = BGLONG(p+7);
			if(miny>maxy || miny<dst->r.min.y || maxy>dst->r.max.y)
				error(Ebadblt);
			ws = 1<<(3-dst->ldepth);	/* pixels per byte */
			/* set l to number of bytes of incoming data per scan line */
			if(dst->r.min.x >= 0)
				l = (dst->r.max.x+ws-1)/ws - dst->r.min.x/ws;
			else{	/* make positive before divide */
				t = (-dst->r.min.x)+ws-1;
				t = (t/ws)*ws;
				l = (t+dst->r.max.x+ws-1)/ws;
			}
			if(dst->base < endscreen)
				mbbrect(Rect(dst->r.min.x, miny, dst->r.max.x, maxy));
			p += 11;
			m -= 11;
			if(m < l*(maxy-miny))
				error(Ebadblt);
			if(off && !isoff){
				cursoroff(1);
				isoff = 1;
			}
			for(y=miny; y<maxy; y++){
				oq = (uchar*)gaddr(dst, Pt(dst->r.min.x, y));
				q = oq + (dst->r.min.x&((sizeof(ulong))*ws-1))/ws;
				memmove(q, p, l);
				if(islittle)
					pixreverse(q, l, dst->ldepth);
				if(v==0 && flipping){	/* flip bits */
					/* we know it's all word aligned */
					lp = (ulong*)oq;
					for(x=0; x<l; x+=sizeof(ulong))
						*lp++ ^= ~0;
				}
				p += l;
				m -= l;
			}
			break;

		case 'x':
			/*
			 * cursorset
			 *
			 *	'x'		1
			 *	pt		8
			 */
			if(m < 9)
				error(Ebadblt);
			pt1.x = BGLONG(p+1);
			pt1.y = BGLONG(p+5);
			if(ptinrect(pt1, gscreen.r)){
				mouse.xy = pt1;
				mouse.redraw = 1;
				mouse.track = 1;
				mouseclock();
			}
			m -= 9;
			p += 9;
			break;

		case 'y':
			/*
			 * load font from subfont
			 *	'y'		1
			 *	id		2
			 *	cache index	2
			 *	subfont id	2
			 *	subfont index	2
			 */
			if(m < 9)
				error(Ebadblt);
			v = BGSHORT(p+1);
			if(v<0 || v>=bit.nfont || (ff=bit.font[v])==0)
				error(Ebadblt);
			if(ff->b == 0)
				error(Ebadbitmap);
			l = BGSHORT(p+3);
			if(l >= ff->ncache)
				error(Ebadblt);
			v = BGSHORT(p+5);
			if(v<0 || v>=bit.nsubfont || (f=bit.subfont[v])==0 || f->ref==0)
				error(Ebadfont);
			nw = BGSHORT(p+7);
			if(nw >= f->n)
				error(Ebadblt);
			bitloadchar(ff, l, f, nw);
			p += 9;
			m -= 9;
			break;

		case 'z':
			/*
			 * write the colormap
			 *
			 *	'z'		1
			 *	id		2
			 *	map		12*(2**bitmapdepth)
			 */
			if(m < 3)
				error(Ebadblt);
			v = BGSHORT(p+1);
			if(v != 0)
				error(Ebadbitmap);
			m -= 3;
			p += 3;
			nw = 1 << (1 << gscreen.ldepth);
			if(m < 12*nw)
				error(Ebadblt);
			ok = 1;
			for(i = 0; i < nw; i++){
				ok &= setcolor(flipping ? ~i : i, BGLONG(p), BGLONG(p+4), BGLONG(p+8));
				p += 12;
				m -= 12;
			}
			if(!ok){
				/* assume monochrome: possibly change flipping */
				l = BGLONG(p-12);
				getcolor(nw-1, &rv, &rv, &rv);
				flipping = (l != rv);
			}
			break;
		}

	poperror();
	if(isoff)
		cursoron(1);
	screenupdate();
	qunlock(&bit);
	return n;
}

int
bitalloc(Rectangle rect, int ld)
{
	Arena *a, *ea, *aa;
	GBitmap *b, **bp, **ep;
	ulong l, ws, nw;
	long t;

	ws = BI2WD>>ld;	/* pixels per word */
	if(rect.min.x >= 0){
		l = (rect.max.x+ws-1)/ws;
		l -= rect.min.x/ws;
	}else{	/* make positive before divide */
		t = (-rect.min.x)+ws-1;
		t = (t/ws)*ws;
		l = (t+rect.max.x+ws-1)/ws;
	}
	nw = l*Dy(rect);
	ea = &bit.arena[bit.narena];

	/* first try easy fit */
	for(a=bit.arena; a<ea; a++){
		if(a->words == 0)
			continue;
		if(a->wfree+HDR+nw <= a->words+a->nwords)
			goto found;
	}

	/* compact and try again */
	bitcompact();
	aa = 0;
	for(a=bit.arena; a<ea; a++){
		if(a->words == 0){
			if(aa == 0)
				aa = a;
			continue;
		}
		if(a->wfree+HDR+nw <= a->words+a->nwords)
			goto found;
	}

	/* need new arena */
	if(aa){
		a = aa;
		a->nwords = HDR + (gscreen.r.max.y*gscreen.r.max.x)/ws;
		if(a->nwords < HDR+nw)
			a->nwords = HDR+nw;
		a->words = xalloc(a->nwords*sizeof(ulong));
		if(a->words){
			a->wfree = a->words;
			a->nbusy = 0;
			goto found;
		}
	}
	/* else can't grow list: bitmaps have backpointers */

	bitfreeup();

	for(a=bit.arena; a<ea; a++){
		if(a->words == 0)
			continue;
		if(a->wfree+HDR+nw <= a->words+a->nwords)
			goto found;
	}
	if(a == ea)
		error(Enobitstore);
	
    found:
	b = bitmalloc(sizeof(GBitmap));
	if(b == 0)
		error(Enomem);
	*a->wfree++ = nw;
	*a->wfree++ = (ulong)a;
	*a->wfree++ = (ulong)b;
	memset(a->wfree, 0, nw*sizeof(ulong));
	b->base = a->wfree;
	a->wfree += nw;
	a->nbusy++;
	b->zero = l*rect.min.y;
	if(rect.min.x >= 0)
		b->zero += rect.min.x/ws;
	else
		b->zero -= (-rect.min.x+ws-1)/ws;
	b->zero = -b->zero;
	b->width = l;
	b->ldepth = ld;
	b->r = rect;
	b->clipr = rect;
	b->cache = 0;
	/* worth doing better than linear lookup? */
	ep = bit.map+bit.nmap;
	for(bp=bit.map; bp<ep; bp++)
		if(*bp == 0)
			break;
	if(bp == ep){
		bp = bitmalloc((bit.nmap+DMAP)*sizeof(GBitmap*));
		if(bp == 0){
			bitfree(b);
			error(Enomem);
		}
		memmove(bp, bit.map, bit.nmap*sizeof(GBitmap*));
		free(bit.map);
		bit.map = bp;
		bp += bit.nmap;
		bit.nmap += DMAP;
	}
	*bp = b;
	return bp-bit.map;
}

void
bitfree(GBitmap *b)
{
	Arena *a;

	if(b->base != gscreen.base){	/* can't free screen memory */
		a = (Arena*)(b->base[-2]);
		a->nbusy--;
		if(a->nbusy == 0)
			arenafree(a);
		b->base[-1] = 0;
	}
	free(b);
}

void
fontfree(GFont *f)
{
	if(f->b)
		bitfree(f->b);
	free(f->cache);
	free(f);
}

void
subfontfree(BSubfont *s, int i)
{
	if(s!=bdefont && s->ref>0){	/* don't free subfont 0, bdefont */
		s->ref--;
		if(s->ref==0 && s->qid[0]==~0){	/* uncached */
			bitfree(s->bits);
			free(s->info);
			free(s);
			bit.subfont[i] = 0;
		}
	}
	return;
}

void
arenafree(Arena *a)
{
	xfree(a->words);
	a->words = 0;
}

void
bitstring(GBitmap *bp, Point pt, GFont *f, uchar *p, long l, Fcode fc)
{
	int full;
	Rectangle rect;
	ushort r;
	GCacheinfo *c;
	int x;
	Fcode clr;

	clr = 0;
	full = (fc&~S)^(D&~S);	/* result involves source */
	if(full){
		rect.min.y = 0;
		rect.max.y = f->height;
		/* set clr to result under fc if source pixel is zero */
		/* hard to do without knowing layout of bits, so we cheat */
		clr = (fc&3);	/* fc&3 is result if source is zero */
		clr |= clr<<2;	/* fc&(3<<2) is result if source is one */
	}

	while(l > 0){
		r = BGSHORT(p);
		p += 2;
		l -= 2;
		if(r >= f->ncache)
			continue;
		c = &f->cache[r];
		if(!full){
			rect.min.y = c->top;
			rect.max.y = c->bottom;
		}else{
			if(c->left > 0)
				gbitblt(bp, pt, bp,
					Rect(pt.x, pt.y, pt.x+c->left, pt.y+f->height),
					clr);
			x = c->left+(c->xright-c->x);
			if(x < c->width)
				gbitblt(bp, Pt(pt.x+x, pt.y), bp,
					Rect(pt.x+x, pt.y, pt.x+c->width, pt.y+f->height),
					clr);
		}
		rect.min.x = c->x;
		rect.max.x = c->xright;
		gbitblt(bp, Pt(pt.x+c->left, pt.y+rect.min.y), f->b, rect, fc);
		pt.x += c->width;
	}
}

void
bitloadchar(GFont *f, int ci, GSubfont *subf, int si)
{
	GCacheinfo *c;
	Rectangle rect;
	Fontchar *fi;
	int y;

	c = &f->cache[ci];
	fi = &subf->info[si];
	/* careful about sign extension: top and bottom are uchars */
	y = fi->top + (f->ascent-subf->ascent);
	if(y < 0)
		y = 0;
	c->top = y;
	y = fi->bottom + (f->ascent-subf->ascent);
	if(y < 0)
		y = 0;
	c->bottom = y;
	c->width = fi->width;
	c->left = fi->left;
	c->x = ci*f->width;
	c->xright = c->x + ((fi+1)->x - fi->x);
	rect.min.y = 0;
	rect.max.y = f->height;
	rect.min.x = c->x;
	rect.max.x = c->x+f->width;
	gbitblt(f->b, rect.min, f->b, rect, 0);
	rect.min.x = fi->x;
	rect.max.x = (fi+1)->x;
	rect.max.y = subf->height;
	gbitblt(f->b, Pt(c->x, f->ascent-subf->ascent), subf->bits, rect, S);
}

QLock	bitlock;

GBitmap*
id2bit(int v)
{
	GBitmap *bp=0;

	if(v<0 || v>=bit.nmap || (bp=bit.map[v])==0)
		error(Ebadbitmap);
	return bp;
}

void
bitcompact(void)
{
	Arena *a, *ea, *na;
	ulong *p1, *p2, n;

	qlock(&bitlock);
	ea = &bit.arena[bit.narena];
	for(a=bit.arena; a<ea; a++){
		if(a->words == 0)
			continue;
		/* first compact what's here */
		p1 = p2 = a->words;
		while(p2 < a->wfree){
			n = HDR+p2[0];
			if(p2[2] == 0){
				p2 += n;
				continue;
			}
			if(p1 != p2){
				memmove(p1, p2, n*sizeof(ulong));
				((GBitmap*)p1[2])->base = p1+HDR;
			}
			p2 += n;
			p1 += n;
		}
		/* now pull stuff from later arena to fill this one */
		na = a+1;
		while(na<ea && p1<a->words+a->nwords){
			p2 = na->words;
			if(p2 == 0){
				na++;
				continue;
			}
			while(p2 < na->wfree){
				n = HDR+p2[0];
				if(p2[2] == 0){
					p2 += n;
					continue;
				}
				if(p1+n < a->words+a->nwords){
					memmove(p1, p2, n*sizeof(ulong));
					((GBitmap*)p1[2])->base = p1+HDR;
					/* block now in new arena... */
					p1[1] = (ulong)a;
					a->nbusy++;
					/* ... not in old arena */
					na->nbusy--;
					p2[2] = 0;
					p1 += n;
				}
				p2 += n;
			}
			na++;
		}
		a->wfree = p1;
	}
	for(a=bit.arena; a<ea; a++)
		if(a->words && a->nbusy==0)
			arenafree(a);
	qunlock(&bitlock);
}

void
Cursortocursor(Cursor *c)
{
	int i;
	uchar *p;

	lock(&cursor);
	memmove(&cursor, c, sizeof(Cursor));
	for(i=0; i<16; i++){
		p = (uchar*)&setbits[i];
		*p = c->set[2*i];
		*(p+1) = c->set[2*i+1];
		p = (uchar*)&clrbits[i];
		*p = c->clr[2*i];
		*(p+1) = c->clr[2*i+1];
	}
	if(hwcurs)
		hwcursset(set.base, clr.base, cursor.offset.x, cursor.offset.y);
	unlock(&cursor);
}

void
cursoron(int dolock)
{
	if(cursor.disable)
		return;
	if(dolock)
		lock(&cursor);
	if(cursor.visible++ == 0){
		if(hwcurs)
			hwcursmove(mouse.xy.x, mouse.xy.y);
		else {
			cursor.r.min = mouse.xy;
			cursor.r.max = add(mouse.xy, Pt(16, 16));
			cursor.r = raddp(cursor.r, cursor.offset);
			gbitblt(&cursorback, Pt(0, 0), &gscreen, cursor.r, S);
			gbitblt(&gscreen, cursor.r.min,
				&clr, Rect(0, 0, 16, 16), flipping? flipD[D&~S] : D&~S);
			gbitblt(&gscreen, cursor.r.min,
				&set, Rect(0, 0, 16, 16), flipping? flipD[S|D] : S|D);
			mbbrect(cursor.r);
		}
	}
	if(dolock)
		unlock(&cursor);
}

void
cursoroff(int dolock)
{
	if(cursor.disable)
		return;
	if(dolock)
		lock(&cursor);
	if(--cursor.visible == 0) {
		if(!hwcurs) {
			gbitblt(&gscreen, cursor.r.min, &cursorback, Rect(0, 0, 16, 16), S);
			mbbrect(cursor.r);
			mousescreenupdate();
		}
	}
	if(dolock)
		unlock(&cursor);
}

/*
 *  called by the clock routine to redraw the cursor
 */
void
mouseclock(void)
{
	if(mouse.track){
		mousetrack(mouse.buttons, mouse.dx, mouse.dy);
		mouse.track = 0;
		mouse.dx = 0;
		mouse.dy = 0;
	}
	if(mouse.redraw && canlock(&cursor)){
		mouse.redraw = 0;
		cursoroff(0);
		cursoron(0);
		mousescreenupdate();
		unlock(&cursor);
	}
}

/*
 *  called at interrupt level to update the structure and
 *  awaken any waiting procs.
 */
void
mousetrack(int b, int dx, int dy)
{
	int x, y;

	x = mouse.xy.x + dx;
	if(x < gscreen.r.min.x)
		x = gscreen.r.min.x;
	if(x >= gscreen.r.max.x)
		x = gscreen.r.max.x;
	y = mouse.xy.y + dy;
	if(y < gscreen.r.min.y)
		y = gscreen.r.min.y;
	if(y >= gscreen.r.max.y)
		y = gscreen.r.max.y;
	mouse.counter++;
	mouse.xy = Pt(x, y);
	mouse.buttons = b;
	mouse.redraw = 1;
	wakeup(&mouse.r);
}

/*
 *  microsoft 3 button, 7 bit bytes
 *
 *	byte 0 -	1  L  R Y7 Y6 X7 X6
 *	byte 1 -	0 X5 X4 X3 X2 X1 X0
 *	byte 2 -	0 Y5 Y4 Y3 Y2 Y1 Y0
 *	byte 3 -	0  M  x  x  x  x  x	(optional)
 *
 *  shift & right button is the same as middle button (for 2 button mice)
 */
int
m3mouseputc(IOQ *q, int c)
{
	static uchar msg[3];
	static int nb;
	static int middle;
	static uchar b[] = { 0, 4, 1, 5, 0, 2, 1, 5 };
	short x;
	int dx, dy, newbuttons;

	USED(q);
	/* 
	 *  check bit 6 for consistency
	 */
	if(nb==0){
		if((c&0x40) == 0){
			/* an extra byte gets sent for the middle button */
			middle = (c&0x20) ? 2 : 0;
			newbuttons = (mouse.buttons & ~2) | middle;
			mousetrack(newbuttons, 0, 0);
			return 0;
		}
	}
	msg[nb] = c;
	if(++nb == 3){
		nb = 0;
		newbuttons = middle | b[(msg[0]>>4)&3 | (mouseshifted ? 4 : 0)];
		x = (msg[0]&0x3)<<14;
		dx = (x>>8) | msg[1];
		x = (msg[0]&0xc)<<12;
		dy = (x>>8) | msg[2];
		mousetrack(newbuttons, dx, dy);
	}
	return 0;
}

/*
 *  Logitech 5 byte packed binary mouse format, 8 bit bytes
 *
 *  shift & right button is the same as middle button (for 2 button mice)
 */
int
mouseputc(IOQ *q, int c)
{
	static short msg[5];
	static int nb;
	static uchar b[] = {0, 4, 2, 6, 1, 5, 3, 7, 0, 2, 2, 6, 1, 5, 3, 7};
	int dx, dy, newbuttons;

	USED(q);
	if((c&0xF0) == 0x80)
		nb=0;
	msg[nb] = c;
	if(c & 0x80)
		msg[nb] |= ~0xFF;	/* sign extend */
	if(++nb == 5){
		newbuttons = b[((msg[0]&7)^7) | (mouseshifted ? 8 : 0)];
		dx = msg[1]+msg[3];
		dy = -(msg[2]+msg[4]);
		mousetrack(newbuttons, dx, dy);
		nb = 0;
	}
	return 0;
}

int
mousechanged(void *m)
{
	USED(m);
	return mouse.lastcounter != mouse.counter;
}

/*
 *  reverse pixels into little endian order
 */
uchar revtab0[] = {
 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};
uchar revtab1[] = {
 0x00, 0x40, 0x80, 0xc0, 0x10, 0x50, 0x90, 0xd0,
 0x20, 0x60, 0xa0, 0xe0, 0x30, 0x70, 0xb0, 0xf0,
 0x04, 0x44, 0x84, 0xc4, 0x14, 0x54, 0x94, 0xd4,
 0x24, 0x64, 0xa4, 0xe4, 0x34, 0x74, 0xb4, 0xf4,
 0x08, 0x48, 0x88, 0xc8, 0x18, 0x58, 0x98, 0xd8,
 0x28, 0x68, 0xa8, 0xe8, 0x38, 0x78, 0xb8, 0xf8,
 0x0c, 0x4c, 0x8c, 0xcc, 0x1c, 0x5c, 0x9c, 0xdc,
 0x2c, 0x6c, 0xac, 0xec, 0x3c, 0x7c, 0xbc, 0xfc,
 0x01, 0x41, 0x81, 0xc1, 0x11, 0x51, 0x91, 0xd1,
 0x21, 0x61, 0xa1, 0xe1, 0x31, 0x71, 0xb1, 0xf1,
 0x05, 0x45, 0x85, 0xc5, 0x15, 0x55, 0x95, 0xd5,
 0x25, 0x65, 0xa5, 0xe5, 0x35, 0x75, 0xb5, 0xf5,
 0x09, 0x49, 0x89, 0xc9, 0x19, 0x59, 0x99, 0xd9,
 0x29, 0x69, 0xa9, 0xe9, 0x39, 0x79, 0xb9, 0xf9,
 0x0d, 0x4d, 0x8d, 0xcd, 0x1d, 0x5d, 0x9d, 0xdd,
 0x2d, 0x6d, 0xad, 0xed, 0x3d, 0x7d, 0xbd, 0xfd,
 0x02, 0x42, 0x82, 0xc2, 0x12, 0x52, 0x92, 0xd2,
 0x22, 0x62, 0xa2, 0xe2, 0x32, 0x72, 0xb2, 0xf2,
 0x06, 0x46, 0x86, 0xc6, 0x16, 0x56, 0x96, 0xd6,
 0x26, 0x66, 0xa6, 0xe6, 0x36, 0x76, 0xb6, 0xf6,
 0x0a, 0x4a, 0x8a, 0xca, 0x1a, 0x5a, 0x9a, 0xda,
 0x2a, 0x6a, 0xaa, 0xea, 0x3a, 0x7a, 0xba, 0xfa,
 0x0e, 0x4e, 0x8e, 0xce, 0x1e, 0x5e, 0x9e, 0xde,
 0x2e, 0x6e, 0xae, 0xee, 0x3e, 0x7e, 0xbe, 0xfe,
 0x03, 0x43, 0x83, 0xc3, 0x13, 0x53, 0x93, 0xd3,
 0x23, 0x63, 0xa3, 0xe3, 0x33, 0x73, 0xb3, 0xf3,
 0x07, 0x47, 0x87, 0xc7, 0x17, 0x57, 0x97, 0xd7,
 0x27, 0x67, 0xa7, 0xe7, 0x37, 0x77, 0xb7, 0xf7,
 0x0b, 0x4b, 0x8b, 0xcb, 0x1b, 0x5b, 0x9b, 0xdb,
 0x2b, 0x6b, 0xab, 0xeb, 0x3b, 0x7b, 0xbb, 0xfb,
 0x0f, 0x4f, 0x8f, 0xcf, 0x1f, 0x5f, 0x9f, 0xdf,
 0x2f, 0x6f, 0xaf, 0xef, 0x3f, 0x7f, 0xbf, 0xff,
};
uchar revtab2[] = {
 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0,
 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71,
 0x81, 0x91, 0xa1, 0xb1, 0xc1, 0xd1, 0xe1, 0xf1,
 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72,
 0x82, 0x92, 0xa2, 0xb2, 0xc2, 0xd2, 0xe2, 0xf2,
 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73,
 0x83, 0x93, 0xa3, 0xb3, 0xc3, 0xd3, 0xe3, 0xf3,
 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74,
 0x84, 0x94, 0xa4, 0xb4, 0xc4, 0xd4, 0xe4, 0xf4,
 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75,
 0x85, 0x95, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5,
 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76,
 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6,
 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77,
 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xd7, 0xe7, 0xf7,
 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78,
 0x88, 0x98, 0xa8, 0xb8, 0xc8, 0xd8, 0xe8, 0xf8,
 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79,
 0x89, 0x99, 0xa9, 0xb9, 0xc9, 0xd9, 0xe9, 0xf9,
 0x0a, 0x1a, 0x2a, 0x3a, 0x4a, 0x5a, 0x6a, 0x7a,
 0x8a, 0x9a, 0xaa, 0xba, 0xca, 0xda, 0xea, 0xfa,
 0x0b, 0x1b, 0x2b, 0x3b, 0x4b, 0x5b, 0x6b, 0x7b,
 0x8b, 0x9b, 0xab, 0xbb, 0xcb, 0xdb, 0xeb, 0xfb,
 0x0c, 0x1c, 0x2c, 0x3c, 0x4c, 0x5c, 0x6c, 0x7c,
 0x8c, 0x9c, 0xac, 0xbc, 0xcc, 0xdc, 0xec, 0xfc,
 0x0d, 0x1d, 0x2d, 0x3d, 0x4d, 0x5d, 0x6d, 0x7d,
 0x8d, 0x9d, 0xad, 0xbd, 0xcd, 0xdd, 0xed, 0xfd,
 0x0e, 0x1e, 0x2e, 0x3e, 0x4e, 0x5e, 0x6e, 0x7e,
 0x8e, 0x9e, 0xae, 0xbe, 0xce, 0xde, 0xee, 0xfe,
 0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f,
 0x8f, 0x9f, 0xaf, 0xbf, 0xcf, 0xdf, 0xef, 0xff,
};

void
pixreverse(uchar *p, int len, int ldepth)
{
	uchar *e;
	uchar *tab;

	switch(ldepth){
	case 0:
		tab = revtab0;
		break;
	case 1:
		tab = revtab1;
		break;
	case 2:
		tab = revtab2;
		break;
	default:
		return;
	}

	for(e = p + len; p < e; p++)
		*p = tab[*p];
}

D port/devcdrom.c => port/devcdrom.c +0 -234
@@ 1,234 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include	"devtab.h"
#include	"io.h"

/*
 * CD-ROM.  Gutted version of devwren.
 */

#define DATASIZE	2048	/* max in a transfer */

typedef struct Drive		Drive;

enum {
	Ndisk=		8,	/* maximum disks */
	/* file types */
	Qdir=		0,
	Qdisk=		1,
};

struct Drive
{
	QLock;
	ulong		blocks;			/* blocks on disk */
	ulong		bsize;			/* bytes per block */
	int		drive;
};

static Drive	cdrom[Ndisk];

static void	cdromsize(int);
static long	cdromio(Chan*, char*, ulong, ulong);

static char	Ecap[] = "CD-ROM device capacity unknown";
static char	EnotCD[] = "not a CD-ROM device";

/*
 *  accepts [0-7].  lun is therefore always zero.
 */
static int
cdromdev(char *p)
{
	int drive;

	if(p == 0 || p[0] == '\0')
		error(Ebadarg);
	if(p[0] < '0' || p[0] > '7' || p[1])
		error(Ebadarg);
	drive = p[0] - '0';
	return (drive << 3) | 0;
}

static int
cdromgen(Chan *c, Dirtab *tab, long ntab, long s, Dir *dirp)
{
	char name[NAMELEN+4];
	Drive *dp;

	USED(tab, ntab);
	if(s != 0)
		return -1;
	sprint(name, "cd%d", c->dev>>3);
	dp = &cdrom[c->dev>>3];
	devdir(c, (Qid){Qdisk, 0}, name, dp->blocks*dp->bsize, eve, 0444, dirp);
	return 1;
}

void
cdromreset(void)
{
}

void
cdrominit(void)
{
}

/*
 *  param is #R<target>
 */
Chan*
cdromattach(char *param)
{
	Chan *c;
	int drive;

	drive = cdromdev(param);
	cdromsize(drive);
	c = devattach('R', param);
	c->dev = drive;
	return c;
}

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

int
cdromwalk(Chan *c, char *name)
{
	return devwalk(c, name, 0, 0, cdromgen);
}

void
cdromstat(Chan *c, char *dp)
{
	devstat(c, dp, 0, 0, cdromgen);
}

Chan*
cdromopen(Chan *c, int omode)
{
	return devopen(c, omode, 0, 0, cdromgen);
}

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

void
cdromclose(Chan *c)
{
	USED(c);
}

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

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

long
cdromread(Chan *c, char *a, long n, ulong offset)
{
	if(c->qid.path == CHDIR)
		return devdirread(c, a, n, 0, 0, cdromgen);
	return cdromio(c, a, n, offset);
}

long
cdromwrite(Chan *c, char *a, long n, ulong offset)
{
	USED(c, a, n, offset);
	error(Eperm);
	return 0;
}

static long
cdromio(Chan *c, char *a, ulong len, ulong offset)
{
	Drive *d;
	Scsibuf *b;
	ulong block, n, max, x;

	d = &cdrom[c->dev>>3];

	block = offset / d->bsize;
	n = (offset + len + d->bsize - 1) / d->bsize - block;
	max = DATASIZE / d->bsize;
	if(n > max)
		n = max;
	if(block + n > d->blocks)
		n = d->blocks - block;
	if(block >= d->blocks || n == 0)
		return 0;
	b = scsibuf();
	if(waserror()){
		scsifree(b);
		nexterror();
	}
	offset %= d->bsize;
	x = scsibread(d->drive, b, n, d->bsize, block);
	if(x < offset)
		len = 0;
	else if(len > x - offset)
		len = x - offset;
	memmove(a, (char*)b->virt + offset, len);
	poperror();
	scsifree(b);
	return len;
}

/*
 *  size disk
 */
static void
cdromsize(int dev)
{
	Drive *dp;
	uchar buf[128];
	long nb, bs;

	dp = &cdrom[dev>>3];
	if(waserror()){
		qunlock(dp);
		nexterror();
	}
	qlock(dp);
	scsiready(dev);
	scsisense(dev, buf);
	/*
	 * capacity not defined on CD-ROM, but most implement it.
	 */
	if(scsicap(dev, buf)!=0)
		error(Ecap);
	nb = (buf[0]<<24)|(buf[1]<<16)|(buf[2]<<8)|(buf[3]<<0);
	bs = (buf[4]<<24)|(buf[5]<<16)|(buf[6]<<8)|(buf[7]<<0);
	if(nb==0 || bs==0)
		error(Ecap);

	scsiinquiry(dev, buf, sizeof buf);
	dp->drive = dev;
	dp->blocks = nb;
	dp->bsize = bs;
	poperror();
	qunlock(dp);
}

D port/devscc.c => port/devscc.c +0 -814
@@ 1,814 0,0 @@
#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"

/*
 *  Driver for the Z8530.
 */
enum
{
	/* wr 0 */
	ResExtPend=	2<<3,
	ResTxPend=	5<<3,
	ResErr=		6<<3,

	/* wr 1 */
	ExtIntEna=	1<<0,
	TxIntEna=	1<<1,
	RxIntDis=	0<<3,
	RxIntFirstEna=	1<<3,
	RxIntAllEna=	2<<3,

	/* wr 3 */
	RxEna=		1,
	Rx5bits=	0<<6,
	Rx7bits=	1<<6,
	Rx6bits=	2<<6,
	Rx8bits=	3<<6,
	Rxbitmask=	3<<6,

	/* wr 4 */
	ParEven=	3<<0,
	ParOdd=		1<<0,
	ParOff=		0<<0,
	ParMask=	3<<0,
	SyncMode=	0<<2,
	Rx1stop=	1<<2,
	Rx1hstop=	2<<2,
	Rx2stop=	3<<2,
	X16=		1<<6,

	/* wr 5 */
	TxRTS=		1<<1,
	TxEna=		1<<3,
	TxBreak=	1<<4,
	TxDTR=		1<<7,
	Tx5bits=	0<<5,
	Tx7bits=	1<<5,
	Tx6bits=	2<<5,
	Tx8bits=	3<<5,
	Txbitmask=	3<<5,

	/* wr 9 */
	IntEna=		1<<3,
	ResetB=		1<<6,
	ResetA=		2<<6,
	HardReset=	3<<6,

	/* wr 11 */
	TRxCOutBR=	2,
	TxClockBR=	2<<3,
	RxClockBR=	2<<5,
	TRxCOI=		1<<2,

	/* wr 14 */
	BREna=		1,
	BRSource=	2,

	/* rr 0 */
	RxReady=	1,
	TxReady=	1<<2,
	RxDCD=		1<<3,
	RxCTS=		1<<5,
	RxBreak=	1<<7,

	/* rr 3 */
	ExtPendB=	1,	
	TxPendB=	1<<1,
	RxPendB=	1<<2,
	ExtPendA=	1<<3,	
	TxPendA=	1<<4,
	RxPendA=	1<<5,
};

typedef struct SCC	SCC;
struct SCC
{
	QLock;
	ushort	sticky[16];	/* sticky write register values */
	uchar	*ptr;		/* command/pointer register in Z8530 */
	uchar	*data;		/* data register in Z8530 */
	int	printing;	/* true if printing */
	ulong	freq;		/* clock frequency */
	uchar	mask;		/* bits/char */

	/* console interface */
	int	special;	/* can't use the stream interface */
	IOQ	*iq;		/* input character queue */
	IOQ	*oq;		/* output character queue */

	/* stream interface */
	Queue	*wq;		/* write queue */
	Rendez	r;		/* kproc waiting for input */
 	int	kstarted;	/* kproc started */

	/* idiot flow control */
	int	xonoff;		/* true if we obey this tradition */
	int	blocked;	/* abstinence */
};

int	nscc;
SCC	*scc[8];	/* up to 4 8530's */
#define CTLS	023
#define CTLQ	021

#ifdef	Zduart
#define	SCCTYPE	'z'
#define	onepointseven()
#else
#define	SCCTYPE	't'
void
onepointseven(void)
{
	int i;
	for(i = 0; i < 20; i++)
		;
}
#endif

/*
 *  Access registers using the pointer in register 0.
 *  This is a bit stupid when accessing register 0.
 */
void
sccwrreg(SCC *sp, int addr, int value)
{
	onepointseven();
	*sp->ptr = addr;
	wbflush();
	onepointseven();
	*sp->ptr = sp->sticky[addr] | value;
	wbflush();
}
ushort
sccrdreg(SCC *sp, int addr)
{
	onepointseven();
	*sp->ptr = addr;
	wbflush();
	onepointseven();
	return *sp->ptr;
}

/*
 *  set the baud rate by calculating and setting the baudrate
 *  generator constant.  This will work with fairly non-standard
 *  baud rates.
 */
void
sccsetbaud(SCC *sp, int rate)
{
	int brconst;

	if(rate == 0)
		error(Ebadctl);

	brconst = (sp->freq+16*rate-1)/(2*16*rate) - 2;

	sccwrreg(sp, 12, brconst & 0xff);
	sccwrreg(sp, 13, (brconst>>8) & 0xff);
}

void
sccparity(SCC *sp, char type)
{
	int val;

	switch(type){
	case 'e':
		val = ParEven;
		break;
	case 'o':
		val = ParOdd;
		break;
	default:
		val = ParOff;
		break;
	}
	sp->sticky[4] = (sp->sticky[4] & ~ParMask) | val;
	sccwrreg(sp, 4, 0);
}

/*
 *  set bits/character, default 8
 */
void
sccbits(SCC *sp, int n)
{
	int rbits, tbits;

	switch(n){
	case 5:
		sp->mask = 0x1f;
		rbits = Rx5bits;
		tbits = Tx5bits;
		break;
	case 6:
		sp->mask = 0x3f;
		rbits = Rx6bits;
		tbits = Tx6bits;
		break;
	case 7:
		sp->mask = 0x7f;
		rbits = Rx7bits;
		tbits = Tx7bits;
		break;
	case 8:
	default:
		sp->mask = 0xff;
		rbits = Rx8bits;
		tbits = Tx8bits;
		break;
	}
	sp->sticky[3] = (sp->sticky[3]&~Rxbitmask) | rbits;
	sccwrreg(sp, 3, 0);
	sp->sticky[5] = (sp->sticky[5]&~Txbitmask) | tbits;
	sccwrreg(sp, 5, 0);
}

/*
 *  toggle DTR
 */
void
sccdtr(SCC *sp, int n)
{
	if(n)
		sp->sticky[5] |= TxDTR;
	else
		sp->sticky[5] &=~TxDTR;
	sccwrreg(sp, 5, 0);
}

/*
 *  toggle RTS
 */
void
sccrts(SCC *sp, int n)
{
	if(n)
		sp->sticky[5] |= TxRTS;
	else
		sp->sticky[5] &=~TxRTS;
	sccwrreg(sp, 5, 0);
}

/*
 *  send break
 */
void
sccbreak(SCC *sp, int ms)
{
	if(ms == 0)
		ms = 100;
	sp->sticky[1] &=~TxIntEna;
	sccwrreg(sp, 1, 0);
	sccwrreg(sp, 5, TxBreak|TxEna);
	tsleep(&u->p->sleep, return0, 0, ms);
	sccwrreg(sp, 5, 0);
	if(sp->oq){
		sp->sticky[1] |= TxIntEna;
		sccwrreg(sp, 1, 0);
	}
}

/*
 *  default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts,
 *  transmit and receive enabled, interrupts disabled.
 */
static void
sccsetup0(SCC *sp, int brsource)
{
	memset(sp->sticky, 0, sizeof(sp->sticky));

	/*
	 *  turn on baud rate generator and set rate to 9600 baud.
	 *  use 1 stop bit.
	 */
	sp->sticky[14] = brsource ? BRSource : 0;
	sccwrreg(sp, 14, 0);
	sccsetbaud(sp, 9600);
	sp->sticky[4] = Rx1stop | X16;
	sccwrreg(sp, 4, 0);
	sp->sticky[11] = TxClockBR | RxClockBR | TRxCOutBR /*| TRxCOI*/;
	sccwrreg(sp, 11, 0);
	sp->sticky[14] = BREna;
	if(brsource)
		sp->sticky[14] |= BRSource;
	sccwrreg(sp, 14, 0);

	/*
	 *  enable I/O, 8 bits/character
	 */
	sp->sticky[3] = RxEna | Rx8bits;
	sccwrreg(sp, 3, 0);
	sp->sticky[5] = TxEna | Tx8bits;
	sccwrreg(sp, 5, 0);
	sp->mask = 0xff;
}

void
sccsetup(void *addr, ulong freq, int brsource)
{
	SCCdev *dev;
	SCC *sp;

	dev = addr;

	/*
	 *  allocate a structure, set port addresses, and setup the line
	 */
	sp = xalloc(sizeof(SCC));
	scc[nscc] = sp;
	sp->ptr = &dev->ptra;
	sp->data = &dev->dataa;
	sp->freq = freq;
	sccsetup0(sp, brsource);
	sp = xalloc(sizeof(SCC));
	scc[nscc+1] = sp;
	sp->ptr = &dev->ptrb;
	sp->data = &dev->datab;
	sp->freq = freq;
	sccsetup0(sp, brsource);
	nscc += 2;
}

/*
 *  Queue n characters for output; if queue is full, we lose characters.
 *  Get the output going if it isn't already.
 */
void
sccputs(IOQ *cq, char *s, int n)
{
	SCC *sp = cq->ptr;
	int ch, x;

	x = splhi();
	lock(cq);
	puts(cq, s, n);
	if(sp->printing == 0 && sp->blocked==0){
		ch = getc(cq);
		/*kprint("<start %2.2ux>", ch);*/
		if(ch >= 0){
			sp->printing = 1;
			while((*sp->ptr&TxReady)==0)
				;
			*sp->data = ch;
		}
	}
	unlock(cq);
	splx(x);
}

/*
 *  an scc interrupt (a damn lot of work for one character)
 */
void
sccintr0(SCC *sp, uchar x)
{
	int ch;
	IOQ *cq;

	if(x & ExtPendB){
		sccwrreg(sp, 0, ResExtPend);
	}
	if(x & RxPendB){
		cq = sp->iq;
		while(*sp->ptr&RxReady){
			onepointseven();
			ch = *sp->data & sp->mask;
			if (ch == CTLS && sp->xonoff)
				sp->blocked = 1;
			else if (ch == CTLQ && sp->xonoff) {
				sp->blocked = 0;
				sccputs(sp->oq, "", 0);
			}
			if(cq->putc)
				(*cq->putc)(cq, ch);
			else
				putc(cq, ch);
		}
	}
	if(x & TxPendB){
		if (sp->blocked) {
			sccwrreg(sp, 0, ResTxPend);
			sp->printing = 0;
			return;
		}
		cq = sp->oq;
		lock(cq);
		ch = getc(cq);
		onepointseven();
		if(ch < 0){
			sccwrreg(sp, 0, ResTxPend);
			sp->printing = 0;
			wakeup(&cq->r);
		}else
			*sp->data = ch;
		unlock(cq);
	}
}
int
sccintr(void)
{
	uchar x;
	int i, j;

	for(i = j = 0; i < nscc; i += 2){
		x = sccrdreg(scc[i], 3);
		if(x & (ExtPendB|RxPendB|TxPendB))
			++j, sccintr0(scc[i+1], x);
		x = x >> 3;
		if(x & (ExtPendB|RxPendB|TxPendB))
			++j, sccintr0(scc[i], x);
	}
	return j;
}

void
sccclock(void)
{
	SCC *sp;
	IOQ *cq;
	int i;

	for(i = 0; i < nscc; i++){
		sp = scc[i];
		cq = sp->iq;
		if(sp->wq && cangetc(cq))
			wakeup(&cq->r);
	}
}

/*
 *  turn on a port's interrupts.  set DTR and RTS
 */
void
sccenable(SCC *sp)
{
	/*
	 *  set up i/o routines
	 */
	if(sp->oq){
		sp->oq->puts = sccputs;
		sp->oq->ptr = sp;
		sp->sticky[1] |= TxIntEna | ExtIntEna;
	}
	if(sp->iq){
		sp->iq->ptr = sp;
		sp->sticky[1] |= RxIntAllEna | ExtIntEna;
	}

	/*
 	 *  turn on interrupts
	 */
	sccwrreg(sp, 1, 0);
	sp->sticky[9] |= IntEna;
	sccwrreg(sp, 9, 0);

	/*
	 *  turn on DTR and RTS
	 */
	sccdtr(sp, 1);
	sccrts(sp, 1);
}

/*
 *  set up an scc port as something other than a stream
 */
void
sccspecial(int port, IOQ *oq, IOQ *iq, int baud)
{
	SCC *sp = scc[port];

	sp->special = 1;
	sp->oq = oq;
	sp->iq = iq;
	sccenable(sp);
	if(baud)
		sccsetbaud(sp, baud);

	if(iq){
		/*
		 *  Stupid HACK to undo a stupid hack
		 */ 
		if(iq == &kbdq)
			kbdq.putc = kbdcr2nl;
	}
}

void
sccrawput(int port, int c)
{
	SCC *sp = scc[port];

	if(c == '\n') {
		sccrawput(port, '\r');
		delay(100);
	}

	while((*sp->ptr&TxReady)==0)
		;
	*sp->data = c;
}

static void	sccstopen(Queue*, Stream*);
static void	sccstclose(Queue*);
static void	sccoput(Queue*, Block*);
static void	scckproc(void *);
Qinfo sccinfo =
{
	nullput,
	sccoput,
	sccstopen,
	sccstclose,
	"scc"
};

static void
sccstopen(Queue *q, Stream *s)
{
	SCC *sp;
	char name[NAMELEN];

	kprint("sccstopen: q=0x%ux, inuse=%d, type=%d, dev=%d, id=%d\n",
		q, s->inuse, s->type, s->dev, s->id);
	sp = scc[s->id];
	qlock(sp);
	sp->wq = WR(q);
	WR(q)->ptr = sp;
	RD(q)->ptr = sp;
	qunlock(sp);

	if(sp->kstarted == 0){
		sp->kstarted = 1;
		sprint(name, "scc%d", s->id);
		kproc(name, scckproc, sp);
	}
}

static void
sccstclose(Queue *q)
{
	SCC *sp = q->ptr;

	if(sp->special)
		return;

	qlock(sp);
	sp->wq = 0;
	sp->iq->putc = 0;
	WR(q)->ptr = 0;
	RD(q)->ptr = 0;
	qunlock(sp);
}

static void
sccoput(Queue *q, Block *bp)
{
	SCC *sp = q->ptr;
	IOQ *cq;
	int n, m;

	if(sp == 0){
		freeb(bp);
		return;
	}
	cq = sp->oq;
	if(waserror()){
		freeb(bp);
		nexterror();
	}
	if(bp->type == M_CTL){
		while (cangetc(cq))	/* let output drain */
			sleep(&cq->r, cangetc, cq);
		n = strtoul((char *)(bp->rptr+1), 0, 0);
		switch(*bp->rptr){
		case 'B':
		case 'b':
			if(BLEN(bp)>4 && strncmp((char*)(bp->rptr+1), "reak", 4) == 0)
				sccbreak(sp, 0);
			else
				sccsetbaud(sp, n);
			break;
		case 'D':
		case 'd':
			sccdtr(sp, n);
			break;
		case 'L':
		case 'l':
			sccbits(sp, n);
			break;

		case 'P':
		case 'p':
			sccparity(sp, *(bp->rptr+1));
			break;
		case 'K':
		case 'k':
			sccbreak(sp, n);
			break;
		case 'R':
		case 'r':
			sccrts(sp, n);
			break;
		case 'W':
		case 'w':
			/* obsolete */
			break;
		case 'X':
		case 'x':
			sp->xonoff = n;
			break;
		}
	}else while((m = BLEN(bp)) > 0){
		while ((n = canputc(cq)) == 0){
			kprint(" sccoput: sleeping\n");
			sleep(&cq->r, canputc, cq);
		}
		if(n > m)
			n = m;
		(*cq->puts)(cq, bp->rptr, n);
		bp->rptr += n;
	}
	freeb(bp);
	poperror();
}

/*
 *  process to send bytes upstream for a port
 */
static void
scckproc(void *a)
{
	SCC *sp = a;
	IOQ *cq = sp->iq;
	Block *bp;
	int n;

loop:
	while ((n = cangetc(cq)) == 0)
		sleep(&cq->r, cangetc, cq);
	/*kprint(" scckproc: %d\n", n);*/
	qlock(sp);
	if(sp->wq == 0){
		cq->out = cq->in;
	}else{
		bp = allocb(n);
		bp->flags |= S_DELIM;
		bp->wptr += gets(cq, bp->wptr, n);
		PUTNEXT(RD(sp->wq), bp);
	}
	qunlock(sp);
	goto loop;
}

Dirtab *sccdir;

/*
 *  create 2 directory entries for each 'sccsetup' ports.
 *  allocate the queues if no one else has.
 */
void
sccreset(void)
{
	SCC *sp;
	int i;
	Dirtab *dp;

	sccdir = xalloc(2 * nscc * sizeof(Dirtab));
	dp = sccdir;
	for(i = 0; i < nscc; i++){
		/* 2 directory entries per port */
		sprint(dp->name, "eia%d", i);
		dp->qid.path = STREAMQID(i, Sdataqid);
		dp->perm = 0666;
		dp++;
		sprint(dp->name, "eia%dctl", i);
		dp->qid.path = STREAMQID(i, Sctlqid);
		dp->perm = 0666;
		dp++;

		/* set up queues if a stream port */
		sp = scc[i];
		if(sp->special)
			continue;
		sp->iq = xalloc(sizeof(IOQ));
		initq(sp->iq);
		sp->oq = xalloc(sizeof(IOQ));
		initq(sp->oq);
		sccenable(sp);
	}
}

void
sccinit(void)
{
}

Chan*
sccattach(char *spec)
{
	return devattach(SCCTYPE, spec);
}

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

int
sccwalk(Chan *c, char *name)
{
	return devwalk(c, name, sccdir, 2*nscc, devgen);
}

void
sccstat(Chan *c, char *dp)
{
	int i;

	i = STREAMID(c->qid.path);
	switch(STREAMTYPE(c->qid.path)){
	case Sdataqid:
		streamstat(c, dp, sccdir[2*i].name, sccdir[2*i].perm);
		break;
	default:
		devstat(c, dp, sccdir, 2*nscc, devgen);
		break;
	}
}

Chan*
sccopen(Chan *c, int omode)
{
	SCC *sp;

	if(c->qid.path != CHDIR){
		sp = scc[STREAMID(c->qid.path)];
		if(sp->special)
			error(Einuse);
		streamopen(c, &sccinfo);
	}
	return devopen(c, omode, sccdir, 2*nscc, devgen);
}

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

void
sccclose(Chan *c)
{
	if(c->stream)
		streamclose(c);
}

long
sccread(Chan *c, void *buf, long n, ulong offset)
{
	char b[8];

	if(c->qid.path == CHDIR)
		return devdirread(c, buf, n, sccdir, 2*nscc, devgen);

	switch(STREAMTYPE(c->qid.path)){
	case Sdataqid:
		return streamread(c, buf, n);
	case Sctlqid:
		sprint(b, "%d", STREAMID(c->qid.path));
		return readstr(offset, buf, n, b);
	}
	error(Egreg);
	return 0;	/* not reached */
}

long
sccwrite(Chan *c, void *va, long n, ulong offset)
{
	USED(offset);
	return streamwrite(c, va, n, 0);
}

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

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

D port/devscsi.c => port/devscsi.c +0 -536
@@ 1,536 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include	"devtab.h"

#define	DPRINT	if(debug)kprint

#define DATASIZE	(64*512)

static Scsi	staticcmd;	/* BUG: should be one per scsi device */

enum
{
	Qdir,
	Qcmd,
	Qdata,
	Qdebug,
};

static Dirtab scsidir[]={
	"cmd",		{Qcmd},		0,	0666,
	"data",		{Qdata},	0,	0666,
	"debug",	{Qdebug},	1,	0666,
};
#define	NSCSI	(sizeof scsidir/sizeof(Dirtab))

extern int	scsidebugs[];
extern int	scsiownid;

static int
scsigen1(Chan *c, long qid, Dir *dp)
{
	if (qid == CHDIR)
		devdir(c, (Qid){qid,0}, ".", 0, eve, 0555, dp);
	else if (qid == 1)
		devdir(c, (Qid){qid,0}, "id", 1, eve, 0666, dp);
	else if (qid&CHDIR) {
		char name[2];
		name[0] = '0'+((qid>>4)&7), name[1] = 0;
		devdir(c, (Qid){qid,0}, name, 0, eve, 0555, dp);
	} else {
		Dirtab *tab = &scsidir[(qid&7)-1];
		devdir(c, (Qid){qid,0}, tab->name, tab->length, eve, tab->perm, dp);
	}
	return 1;
}

static int
scsigeno(Chan *c, Dirtab *tab, long ntab, long s, Dir *dp)
{
	USED(tab, ntab, s);
	return scsigen1(c, c->qid.path, dp);
}

static int
scsigen(Chan *c, Dirtab *tab, long ntab, long s, Dir *dp)
{
	USED(tab, ntab);
	if (c->qid.path == CHDIR) {
		if (0<=s && s<=7)
			return scsigen1(c, CHDIR|0x100|(s<<4), dp);
		else if (s == 8)
			return scsigen1(c, 1, dp);
		else
			return -1;
	}
	if (s >= NSCSI)
		return -1;
	return scsigen1(c, (c->qid.path&~CHDIR)+s+1, dp);
}

void
scsireset(void)
{
	scsibufreset(DATASIZE);
	resetscsi();
}

void
scsiinit(void)
{
	initscsi();
}

Chan *
scsiattach(char *param)
{
	return devattach('S', param);
}

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

int
scsiwalk(Chan *c, char *name)
{
	return devwalk(c, name, 0, 0, scsigen);
}

void
scsistat(Chan *c, char *db)
{
	devstat(c, db, 0, 0, scsigeno);
}

Chan *
scsiopen(Chan *c, int omode)
{
	return devopen(c, omode, 0, 0, scsigeno);
}

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

void
scsiclose(Chan *c)
{
	Scsi *cmd = &staticcmd;
	
	if((c->qid.path & CHDIR) || c->qid.path==1)
		return;
	if((c->qid.path & 0xf) == Qcmd){
		if(canqlock(cmd) || cmd->pid == up->pid){
			cmd->pid = 0;
			qunlock(cmd);
		}
	}
}

long
scsiread(Chan *c, char *a, long n, ulong offset)
{
	Scsi *cmd = &staticcmd;

	if(n == 0)
		return 0;
	if(c->qid.path & CHDIR)
		return devdirread(c, a, n, 0, 0, scsigen);
	if(c->qid.path==1){
		if(offset == 0){
			/*void scsidump(void); scsidump();*/
			*a = scsiownid;
			n = 1;
		}else
			n = 0;
	}else switch(c->qid.path & 0xf){
	case Qcmd:
		if(n < 4)
			error(Ebadarg);
		if(canqlock(cmd)){
			qunlock(cmd);
			error(Egreg);
		}
		if(cmd->pid != up->pid)
			error(Egreg);
		n = 4;
		*a++ = 0;
		*a++ = 0;
		*a++ = cmd->status >> 8;
		*a = cmd->status;
		cmd->pid = 0;
		qunlock(cmd);
		break;
	case Qdata:
		if(canqlock(cmd)){
			qunlock(cmd);
			error(Egreg);
		}
		if(cmd->pid != up->pid)
			error(Egreg);
		if (n > DATASIZE)
			error(Ebadarg);
		cmd->b = scsibuf();
		cmd->data.base = cmd->b->virt;
		if(waserror()){
			scsifree(cmd->b);
			nexterror();
		}
		cmd->data.lim = cmd->data.base + n;
		cmd->data.ptr = cmd->data.base;
		cmd->save = cmd->data.base;
		scsiexec(cmd, ScsiIn);
		n = cmd->data.ptr - cmd->data.base;
		memmove(a, cmd->data.base, n);
		poperror();
		scsifree(cmd->b);
		break;
	case Qdebug:
		if(offset == 0){
			n=1;
			*a="01"[scsidebugs[(c->qid.path>>4)&7]!=0];
		}else
			n = 0;
		break;
	default:
		panic("scsiread");
	}
	return n;
}

long
scsiwrite(Chan *c, char *a, long n, ulong offset)
{
	Scsi *cmd = &staticcmd;

	if(c->qid.path==1 && n>0){
		if(offset == 0){
			n = 1;
			scsiownid=*a;
			scsireset();
		}else
			n = 0;
	}else switch(c->qid.path & 0xf){
	case Qcmd:
		if(n < 6 || n > sizeof cmd->cmdblk)
			error(Ebadarg);
		qlock(cmd);
		cmd->pid = up->pid;
		cmd->cmd.base = cmd->cmdblk;
		memmove(cmd->cmd.base, a, n);
		cmd->cmd.lim = cmd->cmd.base + n;
		cmd->cmd.ptr = cmd->cmd.base;
		cmd->target = (c->qid.path>>4)&7;
		cmd->lun = (a[1]>>5)&7;
		cmd->status = 0xFFFF;
		break;
	case Qdata:
		if(canqlock(cmd)){
			qunlock(cmd);
			error(Egreg);
		}
		if(cmd->pid != up->pid)
			error(Egreg);
		if (n > DATASIZE)
			error(Ebadarg);
		cmd->b = scsibuf();
		cmd->data.base = cmd->b->virt;
		if(waserror()){
			scsifree(cmd->b);
			nexterror();
		}
		cmd->data.lim = cmd->data.base + n;
		cmd->data.ptr = cmd->data.base;
		cmd->save = cmd->data.base;
		memmove(cmd->data.base, a, n);
		scsiexec(cmd, ScsiOut);
		n = cmd->data.ptr - cmd->data.base;
		poperror();
		scsifree(cmd->b);
		break;
	case Qdebug:
		if(offset == 0){
			scsidebugs[(c->qid.path>>4)&7] = (*a=='1');
			n = 1;
		}else
			n = 0;
		break;
	default:
		panic("scsiwrite");
	}
	return n;
}

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

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

Scsi *
scsicmd(int dev, int cmdbyte, Scsibuf *b, long size)
{
	Scsi *cmd = &staticcmd;

	qlock(cmd);
	cmd->target = dev >> 3;
	cmd->lun = dev & 7;
	cmd->cmd.base = cmd->cmdblk;
	cmd->cmd.ptr = cmd->cmd.base;
	memset(cmd->cmdblk, 0, sizeof cmd->cmdblk);
	cmd->cmdblk[0] = cmdbyte;
	switch(cmdbyte >> 5){
	case 0:
		cmd->cmd.lim = &cmd->cmdblk[6];
		break;
	case 1:
		cmd->cmd.lim = &cmd->cmdblk[10];
		break;
	default:
		cmd->cmd.lim = &cmd->cmdblk[12];
		break;
	}
	switch(cmdbyte){
	case ScsiTestunit:
		break;
	case ScsiStartunit:
		cmd->cmdblk[4] = 1;
		break;
	case ScsiModesense:
		cmd->cmdblk[2] = 1;
		/* fall through */
	case ScsiExtsens:
	case ScsiInquiry:
		cmd->cmdblk[4] = size;
		break;
	case ScsiGetcap:
		break;
	}
	cmd->b = b;
	cmd->data.base = b->virt;
	cmd->data.lim = cmd->data.base + size;
	cmd->data.ptr = cmd->data.base;
	cmd->save = cmd->data.base;
	return cmd;
}

int
scsiready(int dev)
{
	Scsi *cmd;
	int status;

	cmd = scsicmd(dev, ScsiTestunit, scsibuf(), 0);
	if(waserror()){
		scsifree(cmd->b);
		qunlock(cmd);
		nexterror();
	}
	status = scsiexec(cmd, ScsiOut);
	poperror();
	scsifree(cmd->b);
	qunlock(cmd);
	if((status&0xff00) != 0x6000)
		error(Eio);
	return status&0xff;
}

int
scsistartstop(int dev, int cmdbyte)
{
	Scsi *cmd;
	int status;

	cmd = scsicmd(dev, cmdbyte, scsibuf(), 0);
	if(waserror()){
		scsifree(cmd->b);
		qunlock(cmd);
		nexterror();
	}
	status = scsiexec(cmd, ScsiOut);
	poperror();
	scsifree(cmd->b);
	qunlock(cmd);
	if((status&0xff00) != 0x6000)
		error(Eio);
	return status&0xff;
}

int
scsisense(int dev, void *p)
{
	Scsi *cmd;
	int status;

	cmd = scsicmd(dev, ScsiExtsens, scsibuf(), 18);
	if(waserror()){
		scsifree(cmd->b);
		qunlock(cmd);
		nexterror();
	}
	status = scsiexec(cmd, ScsiIn);
	memmove(p, cmd->data.base, 18);
	poperror();
	scsifree(cmd->b);
	qunlock(cmd);
	if((status&0xff00) != 0x6000)
		error(Eio);
	return status&0xff;
}

int
scsicap(int dev, void *p)
{
	Scsi *cmd;
	int status;

	cmd = scsicmd(dev, ScsiGetcap, scsibuf(), 8);
	if(waserror()){
		scsifree(cmd->b);
		qunlock(cmd);
		nexterror();
	}
	status = scsiexec(cmd, ScsiIn);
	memmove(p, cmd->data.base, 8);
	poperror();
	scsifree(cmd->b);
	qunlock(cmd);
	if((status&0xff00) != 0x6000)
		error(Eio);
	if(status & 0xFF)
		scsisense(dev, p);
	return status&0xff;
}

int
scsiinquiry(int dev, void *p, int size)
{
	Scsi *cmd;
	int status;

	cmd = scsicmd(dev, ScsiInquiry, scsibuf(), size);
	if(waserror()){
		scsifree(cmd->b);
		qunlock(cmd);
		nexterror();
	}
	status = scsiexec(cmd, ScsiIn);
	memmove(p, cmd->data.base, size);
	poperror();
	scsifree(cmd->b);
	qunlock(cmd);
	if((status&0xff00) != 0x6000)
		error(Eio);
	if(status & 0xFF)
		scsisense(dev, p);
	return status&0xff;
}

int
scsiwp(int dev)
{
/* Device specific
	Scsi *cmd;
	int r, status;

	cmd = scsicmd(dev, ScsiModesense, scsibuf(), 12);
	if(waserror()){
		scsifree(cmd->b);
		qunlock(cmd);
		nexterror();
	}
	status = scsiexec(cmd, ScsiIn);
	r = cmd->data.base[2] & 0x80;
	poperror();
	scsifree(cmd->b);
	qunlock(cmd);
	if ((status&0xffff) != 0x6000)
		error(Eio);
	return r;
*/
	USED(dev);
	return 0;
}

int
scsimodesense(int dev, int page, void *p, int size)
{
	Scsi *cmd;
	int status;

	cmd = scsicmd(dev, ScsiModesense, scsibuf(), size);
	cmd->cmdblk[2] = page;
	if(waserror()){
		scsifree(cmd->b);
		qunlock(cmd);
		nexterror();
	}
	status = scsiexec(cmd, ScsiIn);
	memmove(p, cmd->data.base, size);
	poperror();
	scsifree(cmd->b);
	qunlock(cmd);
	if ((status&0xffff) != 0x6000)
		error(Eio);
	return status&0xff;
}

int
scsibread(int dev, Scsibuf *b, long n, long blocksize, long blockno)
{
	Scsi *cmd;

	cmd = scsicmd(dev, ScsiRead, b, n*blocksize);
	if(waserror()){
		qunlock(cmd);
		nexterror();
	}
	cmd->cmdblk[1] = blockno >> 16;
	cmd->cmdblk[2] = blockno >> 8;
	cmd->cmdblk[3] = blockno;
	cmd->cmdblk[4] = n;
	scsiexec(cmd, ScsiIn);
	n = cmd->data.ptr - cmd->data.base;
	poperror();
	qunlock(cmd);
	return n;
}

int
scsibwrite(int dev, Scsibuf *b, long n, long blocksize, long blockno)
{
	Scsi *cmd;

	cmd = scsicmd(dev, ScsiWrite, b, n*blocksize);
	if(waserror()){
		qunlock(cmd);
		nexterror();
	}
	cmd->cmdblk[1] = blockno >> 16;
	cmd->cmdblk[2] = blockno >> 8;
	cmd->cmdblk[3] = blockno;
	cmd->cmdblk[4] = n;
	scsiexec(cmd, ScsiOut);
	n = cmd->data.ptr - cmd->data.base;
	poperror();
	qunlock(cmd);
	return n;
}

D port/devwren.c => port/devwren.c +0 -336
@@ 1,336 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include	"devtab.h"
#include	"io.h"

#define DATASIZE	(8*1024)

typedef struct Partition	Partition;
typedef struct Drive		Drive;

enum {
	Npart=		8+2,	/* 8 sub partitions, disk, and partition */
	Ndisk=		64,	/* maximum disks; if you change it, you must
				   map from dev to disk */

	/* file types */
	Qdir=		0,
};
#define PART(x)		((x)&0xF)
#define DRIVE(x)	(((x)>>4)&(Ndisk-1))
#define MKQID(d,p)	(((d)<<4) | (p))

struct Partition
{
	ulong	start;
	ulong	end;
	char	name[NAMELEN+1];
};

struct Drive
{
	QLock;
	ulong		bytes;			/* bytes per block */
	int		npart;			/* actual number of partitions */
	int		drive;
	int		readonly;
	Partition	p[Npart];
};

static Drive	wren[Ndisk];

static void	wrenpart(int);
static long	wrenio(Chan*, int, char*, ulong, ulong);

/*
 *  accepts [0-7].[0-7], or abbreviation
 */
static int
wrendev(char *p)
{
	int drive, unit;

	if(p == 0 || p[0] == '\0')
		return 0;
	if(p[0] < '0' || p[0] > '7')
		error(Ebadarg);
	drive = p[0] - '0';
	unit = 0;
	if(p[1]){
		if(p[1] != '.' || p[2] < '0' || p[2] > '7' || p[3] != '\0')
			error(Ebadarg);
		unit = p[2] - '0';
	}
	return (drive << 3) | unit;
}

static int
wrengen(Chan *c, Dirtab *tab, long ntab, long s, Dir *dirp)
{
	Qid qid;
	int drive;
	char name[NAMELEN+4];
	Drive *dp;
	Partition *pp;
	ulong l;

	USED(tab, ntab);
	qid.vers = 0;
	drive = c->dev;
	if(drive >= Ndisk)
		return -1;
	dp = &wren[drive];

	if(s >= dp->npart)
		return -1;

	pp = &dp->p[s];
	if(drive & 7)
		sprint(name, "sd%d.%d%s", drive>>3, drive&7, pp->name);
	else
		sprint(name, "sd%d%s", drive>>3, pp->name);
	name[NAMELEN] = 0;
	qid.path = MKQID(drive, s);
	l = (pp->end - pp->start) * dp->bytes;
	devdir(c, qid, name, l, eve, dp->readonly ? 0444 : 0666, dirp);
	return 1;
}

void
wrenreset(void)
{
}

void
wreninit(void)
{
}

/*
 *  param is #w<target>.<lun>
 */
Chan *
wrenattach(char *param)
{
	Chan *c;
	int drive;

	drive = wrendev(param);
	wrenpart(drive);
	c = devattach('w', param);
	c->dev = drive;
	return c;
}

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

int
wrenwalk(Chan *c, char *name)
{
	return devwalk(c, name, 0, 0, wrengen);
}

void
wrenstat(Chan *c, char *dp)
{
	devstat(c, dp, 0, 0, wrengen);
}

Chan*
wrenopen(Chan *c, int omode)
{
	return devopen(c, omode, 0, 0, wrengen);
}

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

void
wrenclose(Chan *c)
{
	USED(c);
}

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

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

long
wrenread(Chan *c, char *a, long n, ulong offset)
{
	if(c->qid.path == CHDIR)
		return devdirread(c, a, n, 0, 0, wrengen);
	return wrenio(c, 0, a, n, offset);
}

long
wrenwrite(Chan *c, char *a, long n, ulong offset)
{
	n = wrenio(c, 1, a, n, offset);
	if(n)
		return n;
	error("end of device");
	return 0;		/* not reached */
}

static long
wrenio(Chan *c, int write, char *a, ulong len, ulong offset)
{
	Drive *d;
	Partition *p;
	Scsibuf *b;
	ulong block, n, max, x;

	d = &wren[DRIVE(c->qid.path)];
	if(d->npart == 0)			/* drive repartitioned */
		error(Eio);
	p = &d->p[PART(c->qid.path)];

	block = offset / d->bytes + p->start;
	n = (offset + len + d->bytes - 1) / d->bytes + p->start - block;
	max = DATASIZE / d->bytes;
	if(n > max)
		n = max;
	if(block + n > p->end)
		n = p->end - block;
	if(block >= p->end || n == 0)
		return 0;
	b = scsibuf();
	if(waserror()){
		scsifree(b);
		nexterror();
	}
	offset %= d->bytes;
	if(write){
		if(offset || len % d->bytes){
			x = scsibread(d->drive, b, n, d->bytes, block);
			if(x < n * d->bytes){
				n = x / d->bytes;
				x = n * d->bytes - offset;
				if(len > x)
					len = x;
			}
		}
		memmove((char*)b->virt + offset, a, len);
		x = scsibwrite(d->drive, b, n, d->bytes, block);
		if(x < offset)
			len = 0;
		else if(len > x - offset)
			len = x - offset;
	}else{
		x = scsibread(d->drive, b, n, d->bytes, block);
		if(x < offset)
			len = 0;
		else if(len > x - offset)
			len = x - offset;
		memmove(a, (char*)b->virt + offset, len);
	}
	poperror();
	scsifree(b);
	return len;
}

/*
 *  read partition table.  The partition table is just ascii strings.
 */
#define MAGIC "plan9 partitions"
static void
wrenpart(int dev)
{
	Drive *dp;
	Partition *pp;
	uchar buf[128];
	Scsibuf *b;
	char *rawpart, *line[Npart+1], *field[3];
	ulong n;
	int i;

	dp = &wren[dev];
	if(waserror()){
		qunlock(dp);
		nexterror();
	}
	qlock(dp);
	scsiready(dev);
	scsisense(dev, buf);
	scsistartstop(dev, ScsiStartunit);
	scsisense(dev, buf);
	if(scsicap(dev, buf))
		error(Eio);
	dp->drive = dev;
	dp->readonly = scsiwp(dev);

	/*
	 *  we always have a partition for the whole disk
	 *  and one for the partition table
	 */
	dp->bytes = (buf[4]<<24)+(buf[5]<<16)+(buf[6]<<8)+(buf[7]);
	pp = &dp->p[0];
	strcpy(pp->name, "disk");
	pp->start = 0;
	pp->end = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+(buf[3]) + 1;
	pp++;
	strcpy(pp->name, "partition");
	pp->start = dp->p[0].end - 1;
	pp->end = dp->p[0].end;

	scsiinquiry(dev, buf, sizeof buf);
	if(memcmp(&buf[8], "INSITE  I325VM        *F", 24) == 0)
		scsimodesense(dev, 0x2e, buf, 0x2a);
	/*
	 *  read partition table from disk, null terminate
	 */
	b = scsibuf();
	if(waserror()){
		scsifree(b);
		nexterror();
	}
	scsibread(dev, b, 1, dp->bytes, dp->p[0].end-1);
	rawpart = b->virt;
	rawpart[dp->bytes-1] = 0;

	/*
	 *  parse partition table.
	 */
	pp = &dp->p[2];
	n = getfields(rawpart, line, Npart+1, '\n');
	if(n > 0 && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){
		for(i = 1; i < n; i++){
			if(getfields(line[i], field, 3, ' ') != 3)
				break;
			strncpy(pp->name, field[0], NAMELEN);
			pp->start = strtoul(field[1], 0, 0);
			pp->end = strtoul(field[2], 0, 0);
			if(pp->start > pp->end || pp->start >= dp->p[0].end)
				break;
			pp++;
		}
	}
	dp->npart = pp - dp->p;
	poperror();
	scsifree(b);
	poperror();
	qunlock(dp);
}

D port/netXXX.c => port/netXXX.c +0 -108
@@ 1,108 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"

static void nulloput(Queue*, Block*);
static void protooput(Queue*, Block*);
static void protoiput(Queue*, Block*);

static Qinfo XXXprotold =
{
	protoiput,
	protooput,
	0,
	0,
	"proto",
	0
};

static Qinfo XXXmuxld =
{
	0,
	nulloput,
	0,
	0,
	"mux"
};

static void
protooput(Queue *q, Block *bp)
{
	PUTNEXT(q, bp);
}

static void
protoiput(Queue *q, Block *bp)
{
	PUTNEXT(q, bp);
}

static void
nulloput(Queue *q, Block *bp)
{
	freeb(bp);
}

static void XXXaddr(Chan*, char*, int);
static void XXXother(Chan*, char*, int);
static void XXXraddr(Chan*, char*, int);
static void XXXruser(Chan*, char*, int);
static int XXXclone(Chan*);
static void XXXconnect(Chan*, char*);

Network netXXX =
{
	"XXX",
	4,
	32,			/* # conversations */
	&XXXmuxld,
	&XXXprotold,
	0,			/* no listener */
	XXXclone,
	XXXconnect,
	0,			/* no announces */
	4,			/* info files */
	{ { "addr",	XXXaddr, },
	  { "other",	XXXother, },
	  { "raddr",	XXXraddr, },
	  { "ruser",	XXXruser, },
	},
};

static void
XXXaddr(Chan *c, char *buf, int len)
{
	strncpy(buf, "my address", len-1);
}

static void
XXXother(Chan *c, char *buf, int len)
{
	strncpy(buf, "other stuff", len-1);
}

static void
XXXraddr(Chan *c, char *buf, int len)
{
	strncpy(buf, "remote address", len-1);
}

static void
XXXruser(Chan *c, char *buf, int len)
{
	strncpy(buf, "remote user", len-1);
}

static int
XXXclone(Chan *c)
{
	return 5;
}

static void
XXXconnect(Chan *c, char *dest)
{
}

M port/portdat.h => port/portdat.h +0 -51
@@ 9,7 9,6 @@ typedef struct Evalue	Evalue;
typedef struct Fgrp	Fgrp;
typedef struct Image	Image;
typedef struct IOQ	IOQ;
typedef struct KIOQ	KIOQ;
typedef struct List	List;
typedef struct Mntcache Mntcache;
typedef struct Mount	Mount;


@@ 33,9 32,6 @@ typedef struct Ref	Ref;
typedef struct Rendez	Rendez;
typedef struct RWlock	RWlock;
typedef struct Sargs	Sargs;
typedef struct Scsi	Scsi;
typedef struct Scsibuf	Scsibuf;
typedef struct Scsidata	Scsidata;
typedef struct Segment	Segment;
typedef struct Session	Session;
typedef struct Talarm	Talarm;


@@ 174,53 170,6 @@ struct Dirtab
	long	perm;
};

/* SCSI devices. */
enum
{
	ScsiTestunit	= 0x00,
	ScsiExtsens	= 0x03,
	ScsiInquiry	= 0x12,
	ScsiModesense	= 0x1a,
	ScsiStartunit	= 0x1B,
	ScsiStopunit	= 0x1B,
	ScsiGetcap	= 0x25,
	ScsiRead	= 0x08,
	ScsiWrite	= 0x0a,

	/* data direction */
	ScsiIn		= 1,
	ScsiOut		= 0,
};

struct Scsibuf
{
	void*		virt;
	void*		phys;
	Scsibuf*	next;
};

struct Scsidata
{
	uchar*		base;
	uchar*		lim;
	uchar*		ptr;
};

struct Scsi
{
	QLock;
	ulong		pid;
	ushort		target;
	ushort		lun;
	ushort		rflag;
	ushort		status;
	Scsidata 	cmd;
	Scsidata 	data;
	Scsibuf*	b;
	uchar*		save;
	uchar		cmdblk[16];
};

enum
{
	NSMAX	=	1000,

M port/portfns.h => port/portfns.h +0 -23
@@ 11,9 11,6 @@ void		authreply(Session*, ulong, Fcall*);
ulong		authrequest(Session*, Fcall*);
long		authwrite(Chan*, char*, int);
Page*		auxpage(void);
void		bitdebug(void);
void		bitdepth(void);
void		bitreverse(uchar*, int);
void		buzz(int, int);
void		cachedel(Image*, ulong);
void		cachepage(Page*, Image*);


@@ 27,7 24,6 @@ void		chandevreset(void);
void		chanfree(Chan*);
void		chanrec(Mnt*);
void		checkalarms(void);
void		clock(Ureg*);
Chan*		clone(Chan*, Chan*);
void		close(Chan*);
void		closeegrp(Egrp*);


@@ 95,7 91,6 @@ long		ibrk(ulong, int);
int		incref(Ref*);
void		initscsi(void);
void		initseg(void);
void		invalidateu(void);
void		isdir(Chan*);
int		iseve(void);
int		ispages(void*);


@@ 214,19 209,6 @@ void		sched(void);
void		schedinit(void);
int		screenbits(void);
void		screenupdate(void);
int		scsibread(int, Scsibuf*, long, long, long);
void		scsibufreset(ulong);
int		scsibwrite(int, Scsibuf*, long, long, long);
int		scsicap(int, void*);
Scsi*		scsicmd(int, int, Scsibuf*, long);
int		scsiexec(Scsi*, int);
void		scsifree(Scsibuf*);
int		scsiinquiry(int, void*, int);
int		scsimodesense(int, int, void*, int);
int		scsiready(int);
int		scsisense(int, void*);
int		scsistartstop(int, int);
int		scsiwp(int);
long		seconds(void);
ulong		segattach(Proc*, ulong, char *, ulong, ulong);
void		segpage(Segment*, Page*);


@@ 235,7 217,6 @@ void		setkernur(Ureg*, Proc*);
int		setlabel(Label*);
void		setregisters(Ureg*, char*, char*, int);
void		setswapchan(Chan*);
void		simpleputpage(Page*);
char*		skipslash(char*);
void		sleep(Rendez*, int(*)(void*), void*);
void*		smalloc(ulong);


@@ 244,7 225,6 @@ int		spllo(void);
void		splx(int);
void		srvrecover(Chan*, Chan*);
void		swapinit(void);
long		syscall(Ureg*);
void		tsleep(Rendez*, int (*)(void*), void*, int);
void		unbreak(Proc*);
void		uncachepage(Page*);


@@ 252,7 232,6 @@ long		unicode(uchar*);
long		unionread(Chan*, void*, long);
void		unlock(Lock*);
void		unmount(Chan*, Chan*);
void		urpfillstats(Chan*, char*, int);
void		userinit(void);
ulong		userpc(void);
long		userwrite(char*, int);


@@ 272,8 251,6 @@ void		xsummary(void);
Segment*	data2txt(Segment*);
Segment*	dupseg(Segment**, int, int);
Segment*	newseg(int, ulong, ulong);
Scsibuf*	scsialloc(ulong);
Scsibuf*	scsibuf(void);
Segment*	seg(Proc*, ulong, int);



M power/fns.h => power/fns.h +2 -0
@@ 2,6 2,7 @@

void	arginit(void);
void	clearmmucache(void);
void	clock(Ureg*);
void	clockinit(void);
ulong	confeval(char*);
void	confread(void);


@@ 64,6 65,7 @@ void	setvmevec(int, void (*)(int));
void	sinit(void);
uchar*	smap(int, uchar*);
void	sunmap(int, uchar*);
long	syscall(Ureg*);
void	sysloginit(void);
void	syslog(char*, int);
void	tlbinit(void);