plan 9 kernel history: overview | file list | diff list

1992/0114/pc/devuart.c (diff list | history)

pc/devuart.c on 1991/0823
1991/0823    
#include	"u.h" 
1991/0801    
#include	"lib.h" 
#include	"mem.h" 
#include	"dat.h" 
#include	"fns.h" 
#include	"io.h" 
1992/0111    
#include	"../port/error.h" 
1991/0801    
 
#include	"devtab.h" 
 
/* 
 *  Driver for an NS16450 serial port 
 */ 
enum 
{ 
	/* 
	 *  register numbers 
	 */ 
	Data=	0,		/* xmit/rcv buffer */ 
	Iena=	1,		/* interrupt enable */ 
1991/0823    
	 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 */ 
1991/0808    
	Istat=	2,		/* interrupt flag (read) */ 
	Tctl=	2,		/* test control (write) */ 
1991/0801    
	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 */ 
1991/0810    
	 Inton=	(1<<3),		/*  turn on interrupts */ 
1991/0801    
	 Loop=	(1<<4),		/*  loop bask */ 
	Lstat=	5,		/* line status */ 
	 Inready=(1<<0),	/*  receive buffer full */ 
1991/0823    
	 Oerror=(1<<1),		/*  receiver overrun */ 
	 Perror=(1<<2),		/*  receiver parity error */ 
	 Ferror=(1<<3),		/*  rcv framing error */ 
1991/0803    
	 Outready=(1<<5),	/*  output buffer full */ 
1991/0801    
	Mstat=	6,		/* modem status */ 
	Scratch=7,		/* scratchpad */ 
	Dlsb=	0,		/* divisor lsb */ 
	Dmsb=	1,		/* divisor msb */ 
1991/0808    
 
	Serial=	0, 
	Modem=	1, 
1991/0801    
}; 
 
typedef struct Uart	Uart; 
struct Uart 
{ 
	QLock; 
	int	port; 
1991/0804    
	uchar	sticky[8];	/* sticky write register values */ 
1991/0801    
	int	printing;	/* true if printing */ 
1991/0807    
	int	enabled; 
1991/0801    
 
	/* console interface */ 
	int	nostream;	/* 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 */ 
	Alarm	*a;		/* alarm for waking the kernel process */ 
 	int	kstarted;	/* kproc started */ 
1991/0823    
 
	/* error statistics */ 
	ulong	frame; 
	ulong	overrun; 
1991/0801    
}; 
 
Uart uart[2]; 
 
#define UartFREQ 1846200 
 
1991/0804    
#define uartwrreg(u,r,v)	outb((u)->port + r, (u)->sticky[r] | (v)) 
#define uartrdreg(u,r)		inb((u)->port + r) 
1991/0801    
 
void	uartintr(Uart*); 
void	uartintr0(Ureg*); 
void	uartintr1(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 *up, int rate) 
{ 
	ulong brconst; 
 
1991/0803    
	brconst = (UartFREQ+8*rate-1)/(16*rate); 
1991/0801    
 
	uartwrreg(up, Format, Dra); 
1991/0804    
	outb(up->port+Dmsb, (brconst>>8) & 0xff); 
	outb(up->port+Dlsb, brconst & 0xff); 
1991/0801    
	uartwrreg(up, Format, 0); 
} 
 
/* 
 *  toggle DTR 
 */ 
void 
uartdtr(Uart *up, int n) 
{ 
	if(n) 
		up->sticky[Mctl] |= Dtr; 
	else 
		up->sticky[Mctl] &= ~Dtr; 
	uartwrreg(up, Mctl, 0); 
} 
 
/* 
 *  toggle RTS 
 */ 
void 
uartrts(Uart *up, int n) 
{ 
	if(n) 
		up->sticky[Mctl] |= Rts; 
	else 
		up->sticky[Mctl] &= ~Rts; 
	uartwrreg(up, Mctl, 0); 
} 
 
/* 
 *  send break 
 */ 
void 
uartbreak(Uart *up, int ms) 
{ 
1992/0101    
	if(ms == 0) 
		ms = 200; 
1991/0801    
	uartwrreg(up, Format, Break); 
	tsleep(&u->p->sleep, return0, 0, ms); 
	uartwrreg(up, Format, 0); 
} 
 
/* 
 *  default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts, 
 *  transmit and receive enabled, interrupts disabled. 
 */ 
void 
uartsetup(void) 
{ 
	Uart *up; 
	static int already; 
 
	if(already) 
		return; 
	already = 1; 
 
	/* 
1991/0804    
	 *  set port addresses 
1991/0801    
	 */ 
	uart[0].port = 0x3F8; 
	uart[1].port = 0x2F8; 
	setvec(Uart0vec, uartintr0); 
	setvec(Uart1vec, uartintr1); 
 
	for(up = uart; up < &uart[2]; up++){ 
		memset(up->sticky, 0, sizeof(up->sticky)); 
 
		/* 
		 *  set rate to 9600 baud. 
		 *  8 bits/character. 
		 *  1 stop bit. 
1991/0810    
		 *  interrupts enabled. 
1991/0801    
		 */ 
		uartsetbaud(up, 9600); 
		up->sticky[Format] = Bits8; 
		uartwrreg(up, Format, 0); 
1991/0810    
		up->sticky[Mctl] |= Inton; 
		uartwrreg(up, Mctl, 0x0); 
1991/0801    
	} 
} 
 
/* 
 *  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 *up = cq->ptr; 
	int st, ch, x; 
1991/0803    
	int tries; 
1991/0801    
 
	x = splhi(); 
	lock(cq); 
	puts(cq, s, n); 
	if(up->printing == 0){ 
		ch = getc(cq); 
		if(ch >= 0){ 
			up->printing = 1; 
1991/0803    
			for(tries = 0; tries<10000 && !(uartrdreg(up, Lstat)&Outready); 
				tries++) 
1991/0801    
				; 
			outb(up->port + Data, ch); 
		} 
	} 
	unlock(cq); 
	splx(x); 
} 
 
/* 
 *  a uart interrupt (a damn lot of work for one character) 
 */ 
void 
uartintr(Uart *up) 
{ 
	int ch; 
	IOQ *cq; 
1991/0806    
	int s, l; 
1991/0801    
 
1991/0810    
	for(;;){ 
		s = uartrdreg(up, Istat); 
		switch(s){ 
		case 6:	/* receiver line status */ 
			l = uartrdreg(up, Lstat); 
1991/0823    
			if(l & Ferror) 
				up->frame++; 
			if(l & Oerror) 
				up->overrun++; 
1991/0810    
			break; 
	 
		case 4:	/* received data available */ 
			cq = up->iq; 
			ch = uartrdreg(up, Data) & 0xff; 
			if(cq->putc) 
				(*cq->putc)(cq, ch); 
1991/1113    
			else 
1991/0810    
				putc(cq, ch); 
			break; 
	 
		case 2:	/* transmitter empty */ 
			cq = up->oq; 
			lock(cq); 
			ch = getc(cq); 
			if(ch < 0){ 
				up->printing = 0; 
1991/0801    
				wakeup(&cq->r); 
1991/0810    
			}else 
				outb(up->port + Data, ch); 
			unlock(cq); 
			break; 
	 
		case 0:	/* modem status */ 
1991/0904    
			uartrdreg(up, Mstat); 
1991/0810    
			break; 
	 
		default: 
			if(s&1) 
				return; 
1991/0823    
/*			print("weird modem interrupt\n");/**/ 
1991/0810    
			break; 
1991/0801    
		} 
	} 
} 
void 
uartintr0(Ureg *ur) 
{ 
1991/0810    
	uartintr(&uart[0]); 
1991/0801    
} 
void 
uartintr1(Ureg *ur) 
{ 
1991/0810    
	uartintr(&uart[1]); 
1991/0801    
} 
 
1991/1113    
void 
uartclock(void) 
{ 
	Uart *up; 
	IOQ *cq; 
 
	for(up = uart; up < &uart[2]; up++){ 
		cq = up->iq; 
		if(up->wq && cangetc(cq)) 
			wakeup(&cq->r); 
	} 
} 
 
 
1991/0801    
/* 
 *  turn on a port's interrupts.  set DTR and RTS 
 */ 
void 
uartenable(Uart *up) 
{ 
1991/0806    
	int x; 
 
1991/0801    
	/* 
1991/0807    
	 *  turn on power to the port 
	 */ 
1991/0808    
	if(up == &uart[Serial]){ 
1991/1210    
		if(serial(1) < 0) 
1991/0807    
			print("can't turn on serial port power\n"); 
1991/1001    
	} else { 
1991/1210    
		if(modem(1) < 0) 
1991/1001    
			print("can't turn on modem speaker\n"); 
1991/0807    
	} 
1991/0808    
 
	/* 
1991/0801    
	 *  set up i/o routines 
	 */ 
	if(up->oq){ 
		up->oq->puts = uartputs; 
		up->oq->ptr = up; 
	} 
	if(up->iq){ 
		up->iq->ptr = up; 
	} 
1991/0807    
	up->enabled = 1; 
1991/0801    
 
	/* 
 	 *  turn on interrupts 
	 */ 
1991/0823    
	up->sticky[Iena] = Ircv | Ixmt | Irstat; 
1991/0801    
	uartwrreg(up, Iena, 0); 
 
	/* 
	 *  turn on DTR and RTS 
	 */ 
	uartdtr(up, 1); 
	uartrts(up, 1); 
1991/0807    
} 
1991/0806    
 
1991/0807    
/* 
 *  turn off the uart 
 */ 
uartdisable(Uart *up) 
{ 
 
1991/0806    
	/* 
1991/0807    
 	 *  turn off interrupts 
1991/0806    
	 */ 
1991/0807    
	up->sticky[Iena] = 0; 
	uartwrreg(up, Iena, 0); 
 
	/* 
	 *  turn off DTR and RTS 
	 */ 
	uartdtr(up, 0); 
	uartrts(up, 0); 
	up->enabled = 0; 
 
	/* 
	 *  turn off power 
	 */ 
1991/0808    
	if(up == &uart[Serial]){ 
1991/1210    
		if(serial(0) < 0) 
1991/0807    
			print("can't turn off serial power\n"); 
1991/1210    
	} else { 
		if(modem(0) < 0) 
			print("can't turn off modem speaker\n"); 
1991/0806    
	} 
1991/0808    
 
	/* 
	 *  slow the clock down again 
	 */ 
	clockinit(); 
1991/0801    
} 
 
/* 
 *  set up an uart port as something other than a stream 
 */ 
void 
uartspecial(int port, IOQ *oq, IOQ *iq, int baud) 
{ 
	Uart *up = &uart[port]; 
 
	uartsetup(); 
	up->nostream = 1; 
	up->oq = oq; 
	up->iq = iq; 
	uartenable(up); 
	uartsetbaud(up, baud); 
 
	if(iq){ 
		/* 
		 *  Stupid HACK to undo a stupid hack 
		 */  
		if(iq == &kbdq) 
			kbdq.putc = kbdcr2nl; 
	} 
} 
 
static void	uarttimer(Alarm*); 
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" 
}; 
 
/* 
 *  create a helper process per port 
 */ 
static void 
uarttimer(Alarm *a) 
{ 
	Uart *up = a->arg; 
 
	cancel(a); 
	up->a = 0; 
	wakeup(&up->iq->r); 
} 
 
static void 
uartstopen(Queue *q, Stream *s) 
{ 
	Uart *up; 
	char name[NAMELEN]; 
1991/0806    
 
1991/0801    
	up = &uart[s->id]; 
1991/0808    
	up->iq->putc = 0; 
1991/0806    
	uartenable(up); 
 
1991/0801    
	qlock(up); 
	up->wq = WR(q); 
	WR(q)->ptr = up; 
	RD(q)->ptr = up; 
	qunlock(up); 
 
	if(up->kstarted == 0){ 
		up->kstarted = 1; 
		sprint(name, "uart%d", s->id); 
		kproc(name, uartkproc, up); 
	} 
} 
 
static void 
uartstclose(Queue *q) 
{ 
	Uart *up = q->ptr; 
 
1991/0807    
	uartdisable(up); 
 
1991/0801    
	qlock(up); 
	kprint("uartstclose: q=0x%ux, id=%d\n", q, up-uart); 
	up->wq = 0; 
	up->iq->putc = 0; 
	WR(q)->ptr = 0; 
	RD(q)->ptr = 0; 
	qunlock(up); 
} 
 
static void 
uartoput(Queue *q, Block *bp) 
{ 
	Uart *up = q->ptr; 
	IOQ *cq; 
	int n, m; 
 
	if(up == 0){ 
		freeb(bp); 
		return; 
	} 
	cq = up->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': 
			uartsetbaud(up, n); 
			break; 
		case 'D': 
		case 'd': 
			uartdtr(up, n); 
			break; 
		case 'K': 
		case 'k': 
			uartbreak(up, n); 
			break; 
		case 'R': 
		case 'r': 
			uartrts(up, 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 upstream for a port 
 */ 
static void 
uartkproc(void *a) 
{ 
	Uart *up = a; 
	IOQ *cq = up->iq; 
	Block *bp; 
	int n; 
1991/0823    
	ulong frame, overrun; 
1991/1113    
	static ulong ints; 
1991/0904    
 
	frame = 0; 
	overrun = 0; 
1991/0801    
 
1991/0808    
	if(waserror()) 
		print("uartkproc got an error\n"); 
 
	for(;;){ 
1991/0801    
		sleep(&cq->r, cangetc, cq); 
1991/1113    
		if((ints++ & 0x1f) == 0) 
1991/1210    
			lights((ints>>5)&1); 
1991/0808    
		qlock(up); 
		if(up->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(up->wq), bp); 
		} 
		qunlock(up); 
1991/0823    
		if(up->frame != frame){ 
			kprint("uart%d: %d framing\n", up-uart, up->frame); 
			frame = up->frame; 
		} 
		if(up->overrun != overrun){ 
			kprint("uart%d: %d overruns\n", up-uart, up->overrun); 
			overrun = up->overrun; 
		} 
1991/0801    
	} 
} 
 
enum{ 
	Qdir=		0, 
1991/1115    
	Qeia0=		STREAMQID(0, Sdataqid), 
	Qeia0ctl=	STREAMQID(0, Sctlqid), 
	Qeia1=		STREAMQID(1, Sdataqid), 
	Qeia1ctl=	STREAMQID(1, Sctlqid), 
1991/0801    
}; 
 
Dirtab uartdir[]={ 
1991/1115    
	"eia0",		{Qeia0},	0,		0666, 
	"eia0ctl",	{Qeia0ctl},	0,		0666, 
	"eia1",		{Qeia1},	0,		0666, 
	"eia1ctl",	{Qeia1ctl},	0,		0666, 
1991/0801    
}; 
 
#define	NUart	(sizeof uartdir/sizeof(Dirtab)) 
 
/* 
 *  allocate the queues if no one else has 
 */ 
void 
uartreset(void) 
{ 
	Uart *up; 
1991/0803    
 
1991/0801    
	uartsetup(); 
	for(up = uart; up < &uart[2]; up++){ 
		if(up->nostream) 
			continue; 
		up->iq = ialloc(sizeof(IOQ), 0); 
		initq(up->iq); 
		up->oq = ialloc(sizeof(IOQ), 0); 
		initq(up->oq); 
	} 
} 
 
void 
uartinit(void) 
{ 
} 
 
Chan* 
uartattach(char *upec) 
{ 
	return devattach('t', upec); 
} 
 
Chan* 
uartclone(Chan *c, Chan *nc) 
{ 
	return devclone(c, nc); 
} 
 
int 
uartwalk(Chan *c, char *name) 
{ 
	return devwalk(c, name, uartdir, NUart, devgen); 
} 
 
void 
uartstat(Chan *c, char *dp) 
{ 
	switch(c->qid.path){ 
1991/1115    
	case Qeia0: 
		streamstat(c, dp, "eia0"); 
1991/0801    
		break; 
1991/1115    
	case Qeia1: 
		streamstat(c, dp, "eia1"); 
1991/0801    
		break; 
	default: 
		devstat(c, dp, uartdir, NUart, devgen); 
		break; 
	} 
} 
 
Chan* 
uartopen(Chan *c, int omode) 
{ 
	Uart *up; 
 
	switch(c->qid.path){ 
1991/1115    
	case Qeia0: 
	case Qeia0ctl: 
1991/0801    
		up = &uart[0]; 
		break; 
1991/1115    
	case Qeia1: 
	case Qeia1ctl: 
1991/0801    
		up = &uart[1]; 
		break; 
	default: 
		up = 0; 
		break; 
	} 
 
	if(up && up->nostream) 
1992/0114    
		error(Einuse); 
1991/0801    
 
	if((c->qid.path & CHDIR) == 0) 
		streamopen(c, &uartinfo); 
	return devopen(c, omode, uartdir, NUart, devgen); 
} 
 
void 
uartcreate(Chan *c, char *name, int omode, ulong perm) 
{ 
	error(Eperm); 
} 
 
void 
uartclose(Chan *c) 
{ 
	if(c->stream) 
		streamclose(c); 
} 
 
long 
uartread(Chan *c, void *buf, long n, ulong offset) 
{ 
	switch(c->qid.path&~CHDIR){ 
	case Qdir: 
		return devdirread(c, buf, n, uartdir, NUart, devgen); 
1991/1115    
	case Qeia1ctl: 
	case Qeia0ctl: 
1991/0801    
		return 0; 
	} 
	return streamread(c, buf, n); 
} 
 
long 
uartwrite(Chan *c, void *va, long n, ulong offset) 
{ 
	return streamwrite(c, va, n, 0); 
} 
 
void 
uartremove(Chan *c) 
{ 
	error(Eperm); 
} 
 
void 
uartwstat(Chan *c, char *dp) 
{ 
	error(Eperm); 
} 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)