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

1992/0711/port/devscc.c (diff list | history)

1992/0711/sys/src/9/port/devscc.c:1,7801992/0722/sys/src/9/port/devscc.c:1,795 (short | long | prev | next)
1991/0601    
#include	"u.h" 
1992/0321    
#include	"../port/lib.h" 
1991/0601    
#include	"mem.h" 
#include	"dat.h" 
#include	"fns.h" 
#include	"io.h" 
1992/0111    
#include	"../port/error.h" 
1991/0601    
 
#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, 
1992/0617    
	Rxbitmask=	3<<6, 
1991/0601    
 
	/* wr 4 */ 
1992/0326    
	ParEven=	3<<0, 
	ParOdd=		1<<0, 
	ParOff=		0<<0, 
	ParMask=	3<<0, 
1991/0601    
	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, 
1992/0617    
	Txbitmask=	3<<5, 
1991/0601    
 
	/* 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 */ 
1991/1225    
	ulong	freq;		/* clock frequency */ 
1991/0601    
 
	/* 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 */ 
 	int	kstarted;	/* kproc started */ 
1992/0129    
 
	/* idiot flow control */ 
	int	xonoff;		/* true if we obey this tradition */ 
	int	blocked;	/* abstinence */ 
1991/0601    
}; 
 
1991/1225    
int	nscc; 
SCC	*scc[8];	/* up to 4 8530's */ 
1992/0129    
#define CTLS	023 
#define CTLQ	021 
1991/1225    
 
1991/0601    
void 
onepointseven(void) 
{ 
	int i; 
	for(i = 0; i < 20; i++) 
		; 
} 
 
/* 
 *  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; 
 
1991/1225    
	if(rate == 0) 
1992/0114    
		error(Ebadctl); 
1991/0601    
 
1991/1225    
	brconst = (sp->freq+16*rate-1)/(2*16*rate) - 2; 
 
1991/0601    
	sccwrreg(sp, 12, brconst & 0xff); 
	sccwrreg(sp, 13, (brconst>>8) & 0xff); 
} 
 
1992/0326    
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); 
} 
 
1991/0601    
/* 
1992/0617    
 *  set bits/character, default 8 
 */ 
void 
sccbits(SCC *sp, int n) 
{ 
	int rbits, tbits; 
 
	switch(n){ 
	case 5: 
		rbits = Rx5bits; 
		tbits = Tx5bits; 
		break; 
	case 6: 
		rbits = Rx6bits; 
		tbits = Tx6bits; 
		break; 
	case 7: 
		rbits = Rx7bits; 
		tbits = Tx7bits; 
		break; 
	case 8: 
	default: 
		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); 
} 
 
/* 
1991/0601    
 *  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) 
{ 
1991/1225    
	if(ms == 0) 
		ms = 100; 
1991/0601    
	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); 
	} 
} 
 
/* 
1991/0604    
 *  default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts, 
1991/0601    
 *  transmit and receive enabled, interrupts disabled. 
 */ 
1991/1225    
static void 
sccsetup0(SCC *sp) 
{ 
	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; 
	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 | 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); 
} 
1992/0620    
 
1991/0601    
void 
1991/1225    
sccsetup(void *addr, ulong freq) 
1991/0601    
{ 
1991/0604    
	SCCdev *dev; 
1991/0601    
	SCC *sp; 
 
1991/0604    
	dev = addr; 
1991/0601    
 
	/* 
1991/1225    
	 *  allocate a structure, set port addresses, and setup the line 
1991/0601    
	 */ 
1992/0620    
	sp = xalloc(sizeof(SCC)); 
1991/1225    
	scc[nscc] = sp; 
	sp->ptr = &dev->ptra; 
	sp->data = &dev->dataa; 
	sp->freq = freq; 
	sccsetup0(sp); 
1992/0620    
	sp = xalloc(sizeof(SCC)); 
1991/1225    
	scc[nscc+1] = sp; 
	sp->ptr = &dev->ptrb; 
	sp->data = &dev->datab; 
	sp->freq = freq; 
	sccsetup0(sp); 
	nscc += 2; 
1991/0601    
} 
 
/* 
 *  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); 
1992/0129    
	if(sp->printing == 0 && sp->blocked==0){ 
1991/0601    
		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; 
1992/0129    
			if (ch == CTLS && sp->xonoff) 
				sp->blocked = 1; 
			else if (ch == CTLQ && sp->xonoff) { 
				sp->blocked = 0; 
				sccputs(sp->oq, "", 0); 
			} 
1991/0601    
			if(cq->putc) 
				(*cq->putc)(cq, ch); 
1991/1003    
			else 
1991/0601    
				putc(cq, ch); 
		} 
	} 
	if(x & TxPendB){ 
1992/0129    
		if (sp->blocked) { 
			sccwrreg(sp, 0, ResTxPend); 
			sp->printing = 0; 
			return; 
		} 
1991/0601    
		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); 
	} 
} 
void 
sccintr(void) 
{ 
	uchar x; 
1991/1225    
	int i; 
1991/0601    
 
1991/1225    
	for(i = 0; i < nscc; i += 2){ 
		x = sccrdreg(scc[i], 3); 
		if(x & (ExtPendB|RxPendB|TxPendB)) 
			sccintr0(scc[i+1], x); 
		x = x >> 3; 
		if(x & (ExtPendB|RxPendB|TxPendB)) 
			sccintr0(scc[i], x); 
	} 
1991/0601    
} 
 
1991/1003    
void 
sccclock(void) 
{ 
	SCC *sp; 
	IOQ *cq; 
1991/1225    
	int i; 
1991/1003    
 
1991/1225    
	for(i = 0; i < nscc; i++){ 
		sp = scc[i]; 
1991/1003    
		cq = sp->iq; 
		if(sp->wq && cangetc(cq)) 
			wakeup(&cq->r); 
	} 
} 
 
1991/0601    
/* 
 *  turn on a port's interrupts.  set DTR and RTS 
 */ 
void 
1991/0606    
sccenable(SCC *sp) 
1991/0601    
{ 
	/* 
	 *  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) 
{ 
1991/1225    
	SCC *sp = scc[port]; 
1991/0601    
 
	sp->nostream = 1; 
	sp->oq = oq; 
	sp->iq = iq; 
1991/0606    
	sccenable(sp); 
1991/0601    
	sccsetbaud(sp, baud); 
1991/0727    
 
	if(iq){ 
		/* 
		 *  Stupid HACK to undo a stupid hack 
		 */  
		if(iq == &kbdq) 
			kbdq.putc = kbdcr2nl; 
	} 
1991/0601    
} 
 
1992/0722    
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; 
} 
 
1991/0601    
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); 
1991/1225    
	sp = scc[s->id]; 
1991/0601    
	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; 
 
	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': 
1991/1225    
			if(BLEN(bp)>4 && strncmp((char*)(bp->rptr+1), "reak", 4) == 0) 
				sccbreak(sp, 0); 
			else 
				sccsetbaud(sp, n); 
1991/0601    
			break; 
		case 'D': 
		case 'd': 
			sccdtr(sp, n); 
1992/0326    
			break; 
1992/0617    
		case 'L': 
		case 'l': 
			sccbits(sp, n); 
			break; 
 
1992/0326    
		case 'P': 
		case 'p': 
			sccparity(sp, *(bp->rptr+1)); 
1991/0601    
			break; 
		case 'K': 
		case 'k': 
			sccbreak(sp, n); 
			break; 
		case 'R': 
		case 'r': 
			sccrts(sp, n); 
			break; 
		case 'W': 
		case 'w': 
1991/1003    
			/* obsolete */ 
1992/0129    
			break; 
		case 'X': 
		case 'x': 
			sp->xonoff = n; 
1991/0601    
			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; 
} 
 
1991/1225    
Dirtab *sccdir; 
1991/0601    
 
/* 
1991/1225    
 *  create 2 directory entries for each 'sccsetup' ports. 
 *  allocate the queues if no one else has. 
1991/0601    
 */ 
void 
sccreset(void) 
{ 
	SCC *sp; 
1991/1225    
	int i; 
	Dirtab *dp; 
1991/0601    
 
1992/0620    
	sccdir = xalloc(2 * nscc * sizeof(Dirtab)); 
1991/1225    
	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]; 
1991/0601    
		if(sp->nostream) 
			continue; 
1992/0622    
		sp->iq = xalloc(sizeof(IOQ)); 
1991/0601    
		initq(sp->iq); 
1992/0622    
		sp->oq = xalloc(sizeof(IOQ)); 
1991/0601    
		initq(sp->oq); 
1991/0606    
		sccenable(sp); 
1991/0601    
	} 
} 
 
void 
sccinit(void) 
{ 
} 
 
Chan* 
sccattach(char *spec) 
{ 
	return devattach('t', spec); 
} 
 
Chan* 
sccclone(Chan *c, Chan *nc) 
{ 
	return devclone(c, nc); 
} 
 
int 
sccwalk(Chan *c, char *name) 
{ 
1991/1225    
	return devwalk(c, name, sccdir, 2*nscc, devgen); 
1991/0601    
} 
 
void 
sccstat(Chan *c, char *dp) 
{ 
1991/1225    
	int i; 
 
	i = STREAMID(c->qid.path); 
	switch(STREAMTYPE(c->qid.path)){ 
	case Sdataqid: 
		streamstat(c, dp, sccdir[2*i].name); 
1991/0601    
		break; 
	default: 
1991/1225    
		devstat(c, dp, sccdir, 2*nscc, devgen); 
1991/0601    
		break; 
	} 
} 
 
Chan* 
sccopen(Chan *c, int omode) 
{ 
	SCC *sp; 
 
1991/1225    
	if(c->qid.path != CHDIR){ 
		sp = scc[STREAMID(c->qid.path)]; 
		if(sp->nostream) 
1992/0114    
			error(Einuse); 
1991/0601    
		streamopen(c, &sccinfo); 
1991/1225    
	} 
	return devopen(c, omode, sccdir, 2*nscc, devgen); 
1991/0601    
} 
 
void 
scccreate(Chan *c, char *name, int omode, ulong perm) 
{ 
1991/1115    
	USED(c, name, omode, perm); 
1991/0601    
	error(Eperm); 
} 
 
void 
sccclose(Chan *c) 
{ 
	if(c->stream) 
		streamclose(c); 
} 
 
long 
sccread(Chan *c, void *buf, long n, ulong offset) 
{ 
1991/1225    
	char b[8]; 
1991/0601    
 
1991/1225    
	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)); 
1992/0623    
		return readstr(offset, buf, n, b); 
1991/0601    
	} 
1992/0114    
	error(Egreg); 
1992/0522    
	return 0;	/* not reached */ 
1991/0601    
} 
 
long 
sccwrite(Chan *c, void *va, long n, ulong offset) 
{ 
1992/0711    
	USED(offset); 
1991/0601    
	return streamwrite(c, va, n, 0); 
} 
 
void 
sccremove(Chan *c) 
{ 
1991/1115    
	USED(c); 
1991/0601    
	error(Eperm); 
} 
 
void 
sccwstat(Chan *c, char *dp) 
{ 
1991/1115    
	USED(c, dp); 
1991/0601    
	error(Eperm); 
} 


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