Charles.Forsyth20060303644188019 years agoHistory
781 lines · 12.4 KB · C
RawDownload
#include	"u.h"#include	"../port/lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"io.h"#include	"../port/error.h"#include	"../port/netif.h" /* * currently no DMA or flow control (hardware or software) */ enum{	Stagesize= 1024,	Dmabufsize=Stagesize/2,	Nuart=7,		/* max per machine */ 	CTLS= 023,	CTLQ= 021,}; typedef struct Uart Uart;struct Uart{	QLock; 	int	opens; 	int	enabled; 	int	frame;		/* framing errors */	int	overrun;	/* rcvr overruns */	int	soverrun;	/* software overruns */	int	perror;		/* parity error */	int	bps;		/* baud rate */	uchar	bits;	char	parity; 	int	inters;		/* total interrupt count */	int	rinters;	/* interrupts due to read */	int	winters;	/* interrupts due to write */ 	int	rcount;		/* total read count */	int	wcount;		/* total output count */ 	int	xonoff;		/* software flow control on */	int	blocked;		/* output blocked */ 	/* buffers */	int	(*putc)(Queue*, int);	Queue	*iq;	Queue	*oq; 	int	port;	UartReg	*reg; 	/* staging areas to avoid some of the per character costs */	uchar	*ip;	uchar	*ie;	uchar	*op;	uchar	*oe; 	/* put large buffers last to aid register-offset optimizations: */	char	name[KNAMELEN];	uchar	istage[Stagesize];	uchar	ostage[Stagesize];}; enum {	UTCR0_PE=	0x01,	UTCR0_OES=	0x02,	UTCR0_SBS=	0x04,	UTCR0_DSS=	0x08,	UTCR0_SCE=	0x10,	UTCR0_RCE=	0x20,	UTCR0_TCE=	0x40, 	UTCR3_RXE=	0x01,	UTCR3_TXE=	0x02,	UTCR3_BRK=	0x04,	UTCR3_RIM=	0x08,	UTCR3_TIM=	0x10,	UTCR3_LBM=	0x20, 	UTSR0_TFS=	0x01,	UTSR0_RFS=	0x02,	UTSR0_RID=	0x04,	UTSR0_RBB=	0x08,	UTSR0_REB=	0x10,	UTSR0_EIF=	0x20, 	UTSR1_TBY=	0x01,	UTSR1_RNE=	0x02,	UTSR1_TNF=	0x04,	UTSR1_PRE=	0x08,	UTSR1_FRE=	0x10,	UTSR1_ROR=	0x20,}; static Uart *uart[Nuart];static int nuart;static int uartspcl;int redirectconsole; static voiduartset(Uart *p){	UartReg *reg = p->reg;	ulong ocr3;	ulong brdiv;	int n; 	brdiv = CLOCKFREQ/16/p->bps - 1;	ocr3 = reg->utcr3;	reg->utcr3 = ocr3&~(UTCR3_RXE|UTCR3_TXE);	reg->utcr1 = brdiv >> 8;	reg->utcr2 = brdiv & 0xff;	/* set PE and OES appropriately for o/e/n: */	reg->utcr0 = ((p->parity&3)^UTCR0_OES)|(p->bits&UTCR0_DSS);	reg->utcr3 = ocr3; 	/* set buffer length according to speed, to allow	 * at most a 200ms delay before dumping the staging buffer	 * into the input queue	 */	n = p->bps/(10*1000/200);	p->ie = &p->istage[n < Stagesize ? n : Stagesize];} /* *  send break */static voiduartbreak(Uart *p, int ms){	UartReg *reg = p->reg;	if(ms == 0)		ms = 200;	reg->utcr3 |= UTCR3_BRK;	tsleep(&up->sleep, return0, 0, ms);	reg->utcr3 &= ~UTCR3_BRK;} /* *  turn on a port */static voiduartenable(Uart *p){	UartReg *reg = p->reg; 	if(p->enabled)		return; 	archuartpower(p->port, 1);	uartset(p);	reg->utsr0 = 0xff;		// clear all sticky status bits	// enable receive, transmit, and receive interrupt:	reg->utcr3 = UTCR3_RXE|UTCR3_TXE|UTCR3_RIM;	p->blocked = 0;	p->xonoff = 0;	p->enabled = 1;} /* *  turn off a port */static voiduartdisable(Uart *p){	p->reg->utcr3 = 0;		// disable TX, RX, and ints	p->blocked = 0;	p->xonoff = 0;	p->enabled = 0;	archuartpower(p->port, 0);} /* *  put some bytes into the local queue to avoid calling *  qconsume for every character */static intstageoutput(Uart *p){	int n;	Queue *q = p->oq; 	if(q == nil)		return 0;	n = qconsume(q, p->ostage, Stagesize);	if(n <= 0)		return 0;	p->op = p->ostage;	p->oe = p->ostage + n;	return n;} static voiduartxmit(Uart *p){	UartReg *reg = p->reg;	ulong e = 0; 	if(!p->blocked) {		while(p->op < p->oe || stageoutput(p)) {				if(reg->utsr1 & UTSR1_TNF) {				reg->utdr = *(p->op++);				p->wcount++;			} else {				e = UTCR3_TIM;				break;			}		}	}	reg->utcr3 = (reg->utcr3&~UTCR3_TIM)|e;} static voiduartrecvq(Uart *p){	uchar *cp = p->istage;	int n = p->ip - cp; 	if(n == 0)		return;	if(p->putc)		while(n-- > 0) 			p->putc(p->iq, *cp++);	else if(p->iq) 		if(qproduce(p->iq, p->istage, n) < n){			/* if xonoff, should send XOFF when qwindow(p->iq) < threshold */			p->soverrun++;			//print("qproduce flow control");		}	p->ip = p->istage;} static voiduartrecv(Uart *p){	UartReg *reg = p->reg;	ulong n;	while(reg->utsr1 & UTSR1_RNE) {		int c;		n = reg->utsr1;		c = reg->utdr;		if(n & (UTSR1_PRE|UTSR1_FRE|UTSR1_ROR)) {			if(n & UTSR1_PRE) 				p->perror++;			if(n & UTSR1_FRE) 				p->frame++;			if(n & UTSR1_ROR) 				p->overrun++;			continue;		}		if(p->xonoff){			if(c == CTLS){				p->blocked = 1;			}else if (c == CTLQ){				p->blocked = 0;			}		}		*p->ip++ = c;		if(p->ip >= p->ie)			uartrecvq(p);		p->rcount++;	}	if(reg->utsr0 & UTSR0_RID) {		reg->utsr0 = UTSR0_RID;		uartrecvq(p);	}} static voiduartclock(void){	Uart *p;	int i; 	for(i=0; i<nuart; i++){		p = uart[i];		if(p != nil)			uartrecvq(p);	}} static voiduartkick(void *a){	Uart *p = a;	int x; 	x = splhi();	uartxmit(p);	splx(x);} /* *  UART Interrupt Handler */static voiduartintr(Ureg*, void* arg){	Uart *p = arg;				UartReg *reg = p->reg;	ulong m = reg->utsr0;	int dokick; 	dokick = p->blocked;	p->inters++;	if(m & (UTSR0_RFS|UTSR0_RID|UTSR0_EIF)) {		p->rinters++;		uartrecv(p);	}	if(p->blocked)		dokick = 0;	if((m & UTSR0_TFS) && (reg->utcr3&UTCR3_TIM || dokick)) {		p->winters++;		uartxmit(p);	} 	if(m & (UTSR0_RBB|UTSR0_REB)) {		//print("<BREAK>");		/* reg->utsr0 = UTSR0_RBB|UTSR0_REB; */		reg->utsr0 = m & (UTSR0_RBB|UTSR0_REB);		/* what to do? if anything */	}} static voiduartsetup(ulong port, char *name){	Uart *p; 	if(nuart >= Nuart)		return; 	p = xalloc(sizeof(Uart));	uart[nuart++] = p;	strcpy(p->name, name); 	p->port = port;	p->reg = UARTREG(port);	p->bps = 9600;	p->bits = 8;	p->parity = 'n'; 	p->iq = qopen(4*1024, 0, 0 , p);	p->oq = qopen(4*1024, 0, uartkick, p); 	p->ip = p->istage;	p->ie = &p->istage[Stagesize];	p->op = p->ostage;	p->oe = p->ostage;	if(port == 1)		GPCLKREG->gpclkr0 |= 1;	/* SUS=1 for uart on serial 1 */ 	intrenable(UARTbit(port), uartintr, p, BusCPU, name);} static voiduartinstall(void){	static int already; 	if(already)		return;	already = 1; 	uartsetup(3, "eia0");	uartsetup(1, "eia1");	addclock0link(uartclock, 22);} /* *  called by main() to configure a duart port as a console or a mouse */voiduartspecial(int port, int bps, char parity, Queue **in, Queue **out, int (*putc)(Queue*, int)){	Uart *p; 	uartinstall();	if(port >= nuart) 		return;	p = uart[port];	if(bps) 		p->bps = bps;	if(parity)		p->parity = parity;	uartenable(p);	p->putc = putc;	if(in)		*in = p->iq;	if(out)		*out = p->oq;	p->opens++;	uartspcl = 1;} Dirtab *uartdir;int ndir; static voidsetlength(int i){	Uart *p; 	if(i > 0){		p = uart[i];		if(p && p->opens && p->iq)			uartdir[1+3*i].length = qlen(p->iq);	} else for(i = 0; i < nuart; i++){		p = uart[i];		if(p && p->opens && p->iq)			uartdir[1+3*i].length = qlen(p->iq);	}} /* *  all uarts must be uartsetup() by this point or inside of uartinstall() */static voiduartreset(void){	int i;	Dirtab *dp; 	uartinstall(); 	ndir = 1+3*nuart;	uartdir = xalloc(ndir * sizeof(Dirtab));	dp = uartdir;	strcpy(dp->name, ".");	mkqid(&dp->qid, 0, 0, QTDIR);	dp->length = 0;	dp->perm = DMDIR|0555;	dp++;	for(i = 0; i < nuart; i++){		/* 3 directory entries per port */		strcpy(dp->name, uart[i]->name);		dp->qid.path = NETQID(i, Ndataqid);		dp->perm = 0660;		dp++;		sprint(dp->name, "%sctl", uart[i]->name);		dp->qid.path = NETQID(i, Nctlqid);		dp->perm = 0660;		dp++;		sprint(dp->name, "%sstatus", uart[i]->name);		dp->qid.path = NETQID(i, Nstatqid);		dp->perm = 0444;		dp++;	}} static Chan*uartattach(char *spec){	return devattach('t', spec);} static Walkqid*uartwalk(Chan *c, Chan *nc, char **name, int nname){	return devwalk(c, nc, name, nname, uartdir, ndir, devgen);} static intuartstat(Chan *c, uchar *dp, int n){	if(NETTYPE(c->qid.path) == Ndataqid)		setlength(NETID(c->qid.path));	return devstat(c, dp, n, uartdir, ndir, devgen);} static Chan*uartopen(Chan *c, int omode){	Uart *p; 	c = devopen(c, omode, uartdir, ndir, devgen); 	switch(NETTYPE(c->qid.path)){	case Nctlqid:	case Ndataqid:		p = uart[NETID(c->qid.path)];		qlock(p);		if(p->opens++ == 0){			uartenable(p);			qreopen(p->iq);			qreopen(p->oq);		}		qunlock(p);		break;	} 	return c;}

Lines 1–500 of 781.