Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/ss/devincon.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## diffname ss/devincon.c 1990/1223
## diff -e /dev/null /n/bootesdump/1990/1223/sys/src/9/sparc/devincon.c
0a
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"errno.h"
#include	"devtab.h"

#include	"io.h"
#include	"ureg.h"

typedef struct Incon	Incon;
typedef struct Device	Device;

#define NOW (MACHP(0)->ticks*MS2HZ)

#define MICROSECOND USED(NOW)

#define DPRINT if(0)

enum {
	Minstation=	2,	/* lowest station # to poll */
	Maxstation=	15,	/* highest station # to poll */
	Nincon=		1,	/* number of incons */
	Nin=		32,	/* Blocks in the input ring */
	Bsize=		128,	/* size of an input ring block */
	Mfifo=		0xff	/* a mask, must be 2^n-1, must be > Nin */
};

/*
 *  incon datakit board
 */
struct Device {
	uchar	cdata;
#define	cpolln	cdata
	uchar	u0;
	uchar	cstatus;
	uchar	u1;
	uchar	creset;
	uchar	u2;
	uchar	csend;
	uchar	u3;
	ushort	data_cntl;	/* data is high byte, cntl is low byte */
	uchar	status;
#define cmd	status
	uchar	u5;
	uchar	reset;
	uchar	u6;
	uchar	send;
	uchar	u7;
};
#define	INCON	((Device *)0x40700000)

struct Incon {
	QLock;

	QLock	xmit;		/* transmit lock */
	QLock	reslock;	/* reset lock */
	Device	*dev;
	int	station;	/* station number */
	int	state;		/* chip state */
	Rendez	r;		/* output process */
	Rendez	kr;		/* input kernel process */
	ushort	chan;		/* current input channel */
	Queue	*rq;		/* read queue */
	int	kstarted;	/* true if kernel process started */

	/*  input blocks */

	Block	*inb[Nin];
	ushort	wi;		
	ushort	ri;

	/* statistics */

	ulong	overflow;	/* overflow errors */
	ulong	pack0;		/* channel 0 */
	ulong	crc;		/* crc errors */
	ulong	in;		/* bytes in */
	ulong	out;		/* bytes out */
};

Incon incon[Nincon];

/*
 *  chip state
 */
enum {
	Selecting,
	Selected,
	Notliving,
};

/*
 *  internal chip registers
 */
#define	sel_polln	0
#define	sel_station	1
#define	sel_poll0	2
#define sel_rcv_cnt	3
#define sel_rcv_tim	4
#define sel_tx_cnt	5

/*
 *  CSR bits
 */
#define INCON_RUN	0x80
#define INCON_STOP	0x00
#define ENABLE_IRQ	0x40
#define ENABLE_TX_IRQ	0x20
#define INCON_ALIVE	0x80
#define TX_FULL		0x10
#define TX_EMPTY	0x08
#define RCV_EMPTY	0x04
#define OVERFLOW	0x02
#define CRC_ERROR	0x01

/*
 *  polling constants
 */
#define HT_GNOT	0x30
#define ST_UNIX 0x04
#define NCHAN 16

static void inconkproc(void*);

/*
 *  incon stream module definition
 */
static void inconoput(Queue*, Block*);
static void inconstopen(Queue*, Stream*);
static void inconstclose(Queue*);
Qinfo inconinfo =
{
	nullput,
	inconoput,
	inconstopen,
	inconstclose,
	"incon"
};

int incondebug;

/*
 *  set the incon parameters
 */
void
inconset(Incon *ip, int cnt, int del)
{
	Device *dev;

	if (cnt<1 || cnt>14 || del<1 || del>15)
		error(Ebadarg);

	dev = ip->dev;
	dev->cmd = sel_rcv_cnt | INCON_RUN;
	MICROSECOND;
	*(uchar *)&dev->data_cntl = cnt;
	MICROSECOND;
	dev->cmd = sel_rcv_tim | INCON_RUN;
	MICROSECOND;
	*(uchar *)&dev->data_cntl = del;
	MICROSECOND;
	dev->cmd = INCON_RUN | ENABLE_IRQ;
}

/*
 *  parse a set request
 */
void
inconsetctl(Incon *ip, Block *bp)
{
	char *field[3];
	int n;
	int del;
	int cnt;

	del = 15;
	n = getfields((char *)bp->rptr, field, 3, ' ');
	switch(n){
	default:
		freeb(bp);
		error(Ebadarg);
	case 2:
		del = strtol(field[1], 0, 0);
		if(del<0 || del>15){
			freeb(bp);
			error(Ebadarg);
		}
		/* fall through */
	case 1:
		cnt = strtol(field[0], 0, 0);
		if(cnt<0 || cnt>15){
			freeb(bp);
			error(Ebadarg);
		}
	}
	inconset(ip, cnt, del);
	freeb(bp);
}

static void
nop(void)
{
}

/*
 *  poll for a station number
 */
void 
inconpoll(Incon *ip, int station)
{
	ulong timer;
	Device *dev;

	dev = ip->dev;

	/*
	 *  get us to a known state
	 */
	ip->state = Notliving;
	dev->cmd = INCON_STOP;

	/*
	 * try a station number
	 */
	dev->cmd = sel_station;
	*(uchar *)&dev->data_cntl = station;
	dev->cmd = sel_poll0;
	*(uchar *)&dev->data_cntl = HT_GNOT;
	dev->cmd = sel_rcv_cnt;
	*(uchar *)&dev->data_cntl = 3;
	dev->cmd = sel_rcv_tim;
	*(uchar *)&dev->data_cntl = 15;
	dev->cmd = sel_tx_cnt;
	*(uchar *)&dev->data_cntl = 1;
	dev->cmd = sel_polln;
	*(uchar *)&dev->data_cntl = 0x00;
	*(uchar *)&dev->data_cntl = ST_UNIX;
	*(uchar *)&dev->data_cntl = NCHAN;
	*(uchar *)&dev->data_cntl = 'g';
	*(uchar *)&dev->data_cntl = 'n';
	*(uchar *)&dev->data_cntl = 'o';
	*(uchar *)&dev->data_cntl = 't';
	dev->cpolln = 0;

	/*
	 *  poll and wait for ready (or 1/4 second)
	 */
	ip->state = Selecting;
	dev->cmd = INCON_RUN | ENABLE_IRQ;
	timer = NOW + 250;
	while (NOW < timer) {
		nop();
		if(dev->status & INCON_ALIVE){
			ip->station = station;
			ip->state = Selected;
			break;
		}
	}
}

/*
 *  reset the chip and find a new staion number
 */
void
inconrestart(Incon *ip)
{
	Device *dev;
	int i;

	if(!canqlock(&ip->reslock))
		return;

	print("inconrestart\n");

	/*
	 *  poll for incon station numbers
	 */
	for(i = Minstation; i <= Maxstation; i++){
		inconpoll(ip, i);
		if(ip->state == Selected)
			break;
	}
	switch(ip->state) {
	case Selecting:
		print("incon[%d] not polled\n", ip-incon);
		break;
	case Selected:
		print("incon[%d] station %d\n", ip-incon, ip->station);
		inconset(ip, 3, 15);
		break;
	default:
		print("incon[%d] bollixed\n", ip-incon);
		break;
	}
	qunlock(&ip->reslock);
}

/*
 *  reset all incon chips.
 */
void
inconreset(void)
{
	int i;
	Incon *ip;

	incon[0].dev = INCON;
	incon[0].state = Selected;
	incon[0].ri = incon[0].wi = 0;
/*	inconset(&incon[0], 3, 15); /**/
	for(i=1; i<Nincon; i++){
		incon[i].dev = INCON+i;
		incon[i].state = Notliving;
		incon[i].dev->cmd = INCON_STOP;
		incon[i].ri = incon[i].wi = 0;
	}
}

void
inconinit(void)
{
}

/*
 *  enable the device for interrupts, spec is the device number
 */
Chan*
inconattach(char *spec)
{
	Incon *ip;
	int i;
	Chan *c;

	i = strtoul(spec, 0, 0);
	if(i >= Nincon)
		error(Ebadarg);
	ip = &incon[i];
	if(ip->state != Selected)
		inconrestart(ip);

	c = devattach('i', spec);
	c->dev = i;
	c->qid.path = CHDIR;
	c->qid.vers = 0;
	return c;
}

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

int	 
inconwalk(Chan *c, char *name)
{
	return devwalk(c, name, 0, 0, streamgen);
}

void	 
inconstat(Chan *c, char *dp)
{
	devstat(c, dp, 0, 0, streamgen);
}

Chan*
inconopen(Chan *c, int omode)
{
	if(c->qid.path == CHDIR){
		if(omode != OREAD)
			error(Eperm);
	}else
		streamopen(c, &inconinfo);
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

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

void	 
inconclose(Chan *c)
{
	if(c->qid.path != CHDIR)
		streamclose(c);
}

long	 
inconread(Chan *c, void *buf, long n)
{
	return streamread(c, buf, n);
}

long	 
inconwrite(Chan *c, void *buf, long n)
{
	return streamwrite(c, buf, n, 0);
}

void	 
inconremove(Chan *c)
{
	error(Eperm);
}

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

/*
 *	the stream routines
 */

/*
 *  create the kernel process for input
 */
static void
inconstopen(Queue *q, Stream *s)
{
	Incon *ip;
	char name[32];

	ip = &incon[s->dev];
	sprint(name, "incon%d", s->dev);
	q->ptr = q->other->ptr = ip;
	ip->rq = q;
	kproc(name, inconkproc, ip);
}

/*
 *  kill off the kernel process
 */
static int
kNotliving(void *arg)
{
	Incon *ip;

	ip = (Incon *)arg;
	return ip->kstarted == 0;
}
static void
inconstclose(Queue * q)
{
	Incon *ip;

	ip = (Incon *)q->ptr;
	qlock(ip);
	ip->rq = 0;
	qunlock(ip);
	wakeup(&ip->kr);
	sleep(&ip->r, kNotliving, ip);
}

/*
 *  free all blocks of a message in `q', `bp' is the first block
 *  of the message
 */
static void
freemsg(Queue *q, Block *bp)
{
	for(; bp; bp = getq(q)){
		if(bp->flags & S_DELIM){
			freeb(bp);
			return;
		}
		freeb(bp);
	}
}

/*
 *  output a block
 *
 *  the first 2 bytes of every message are the channel number,
 *  low order byte first.  the third is a possible trailing control
 *  character.
 */
void
inconoput(Queue *q, Block *bp)
{
	Device *dev;
	Incon *ip;
	ulong end;
	int chan;
	int ctl;
	int n, size;

	ip = (Incon *)q->ptr;

	if(bp->type != M_DATA){
		if(streamparse("inconset", bp))
			inconsetctl(ip, bp);
		else
			freeb(bp);
		return;
	}

	/*
	 *  get a whole message before handing bytes to the device
	 */
	if(!putq(q, bp))
		return;

	/*
	 *  one transmitter at a time
	 */
	qlock(&ip->xmit);
	dev = ip->dev;

	/*
	 *  parse message
	 */
	bp = getq(q);
	if(bp->wptr - bp->rptr < 3){
		freemsg(q, bp);
		qunlock(&ip->xmit);
		return;
	}
	chan = bp->rptr[0] | (bp->rptr[1]<<8);
	ctl = bp->rptr[2];
	bp->rptr += 3;
	if(chan<=0)
		print("bad channel %d\n", chan);

	if(incondebug)
		print("->(%d)%uo %d\n", chan, ctl, bp->wptr - bp->rptr);

	/*
	 *  make sure there's an incon out there
	 */
	if(!(dev->status&INCON_ALIVE) || ip->state==Notliving){
		inconrestart(ip);
		freemsg(q, bp);
		qunlock(&ip->xmit);
		return;
	}

	/*
	 *  send the 8 bit data
	 */
	for(;;){
		/*
		 *  spin till there is room
		 */
		for(end = NOW+1000; dev->status & TX_FULL;){
			nop();	/* make sure we don't optimize too much */
			if(NOW > end){
				print("incon output stuck\n");
				freemsg(q, bp);
				qunlock(&ip->xmit);
				return;
			}
		}

		/*
		 *  put in next packet
		 */
		n = bp->wptr - bp->rptr;
		if(n > 16)
			n = 16;
		size = n;
		dev->cdata = chan;
		while(n--){
			*(uchar *)&dev->data_cntl = *bp->rptr++;
		}

		/*
		 *  get next block 
		 */
		if(bp->rptr >= bp->wptr){
			if(bp->flags & S_DELIM){
				freeb(bp);
				break;
			}
			freeb(bp);
			bp = getq(q);
			if(bp==0)
				break;
		}

		/*
		 *  end packet
		 */
		dev->cdata = 0;
	}

	/*
	 *  send the control byte if there is one
	 */
	if(ctl){
		if(size >= 16){
			dev->cdata = 0;
			MICROSECOND;
			for(end = NOW+1000; dev->status & TX_FULL;){
				nop();	/* make sure we don't optimize too much */
				if(NOW > end){
					print("incon output stuck\n");
					freemsg(q, bp);
					qunlock(&ip->xmit);
					return;
				}
			}
			dev->cdata = chan;
			MICROSECOND;
		}
		if(dev->status & TX_FULL)
			print("inconfull\n");
		dev->cdata = ctl;
	}
	MICROSECOND;
	dev->cdata = 0;

	qunlock(&ip->xmit);
	return;
}

/*
 *  return true if the raw fifo is non-empty
 */
static int
notempty(void *arg)
{
	Incon *ip;

	ip = (Incon *)arg;
	return ip->ri!=ip->wi;
}

/*
 *  Read bytes from the raw input circular buffer.
 */
static void
inconkproc(void *arg)
{
	Incon *ip;
	Block *bp, *nbp;
	int i;
	int locked;

	ip = (Incon *)arg;
	ip->kstarted = 1;

	/*
	 *  create a number of blocks for input
	 */
	for(i = 0; i < Nin; i++){
		bp = ip->inb[i] = allocb(Bsize);
		bp->wptr += 3;
	}

	locked = 0;
	if(waserror()){
		if(locked)
			qunlock(ip);
		ip->kstarted = 0;
		wakeup(&ip->r);
		return;
	}

	for(;;){
		/*
		 *  sleep if input fifo empty
		 */
		sleep(&ip->kr, notempty, ip);

		/*
		 *  die if the device is closed
		 */
		USED(locked);
		qlock(ip);
		locked = 1;
		if(ip->rq == 0){
			qunlock(ip);
			ip->kstarted = 0;
			wakeup(&ip->r);
			poperror();
			return;
		}

		/*
		 *  send blocks upstream and stage new blocks.  if the block is small
		 *  (< 64 bytes) copy into a smaller buffer.
		 */
		while(ip->ri != ip->wi){
			bp = ip->inb[ip->ri];
			PUTNEXT(ip->rq, bp);
			bp = ip->inb[ip->ri] = allocb(Bsize);
			bp->wptr += 3;
			ip->ri = (ip->ri+1)%Nin;
		}
		USED(locked);
		qunlock(ip);
		locked = 0;
	}
}

/*
 *  drop a single packet
 */
static void
droppacket(Device *dev)
{
	int i;
	int c;

	for(i = 0; i < 17; i++){
		c = dev->data_cntl;
		if(c==0)
			break;
	}
}

/*
 *  flush the input fifo
 */
static void
flushfifo(Device *dev)
{
	while(!(dev->status & RCV_EMPTY))
		droppacket(dev);
}

/*
 *  advance the queue. if we've run out of staged input blocks,
 *  drop the packet and return 0.  otherwise return the next input
 *  block to fill.
 */
static Block *
nextin(Incon *ip, unsigned int c)
{
	Block *bp;
	int next;

	bp = ip->inb[ip->wi];
	bp->base[0] = ip->chan;
	bp->base[1] = ip->chan>>8;
	bp->base[2] = c;
	if(incondebug)
		print("<-(%d)%uo %d\n", ip->chan, c, bp->wptr-bp->rptr);

	next = (ip->wi+1)%Nin;
	if(next == ip->ri){
		bp->wptr = bp->base+3;
		return bp;
	}
	ip->wi = next;

	return ip->inb[ip->wi];
}

/*
 *  read the packets from the device into the staged input blocks.
 *  we have to do this at interrupt tevel to turn off the interrupts.
 */
static void
rdpackets(Incon *ip)
{
	Block *bp;
	unsigned int c;
	Device *dev;
	uchar *p;
	int first = ip->wi;

	dev = ip->dev;
	bp = ip->inb[ip->wi];
	if(bp==0){
		flushfifo(ip->dev);
		return;
	}
	p = bp->wptr;
	while(!(dev->status & RCV_EMPTY)){
		/*
		 *  get channel number
		 */
		c = (dev->data_cntl)>>8;
		if(c == 0){
			droppacket(dev);
			continue;
		}
		if(ip->chan != c){
			if(p - bp->rptr > 3){
				bp->wptr = p;
				bp = nextin(ip, 0);
				p = bp->wptr;
			}
			ip->chan = c;
		}

		/*
		 *  null byte marks end of packet
		 */
		for(;;){
			if((c=dev->data_cntl)&1) {
				/*
				 *  data byte, put in local buffer
				 */
				*p++ = c>>8;
			} else if (c>>=8) {
				/*
				 *  control byte ends block
				 */
				bp->wptr = p;
				bp = nextin(ip, c);
				p = bp->wptr;
			} else {
				/* end of packet */
				break;
			}
		}

		/*
		 *  pass a block on if it doesn't have room for one more
		 *  packet.  this way we don't have to check per byte.
		 */
		if(p + 16 > bp->lim){
			bp->wptr = p;
			bp = nextin(ip, 0);
			p = bp->wptr;
		}
	}	
	bp->wptr = p;
	if(bp->wptr != bp->base+3)
		nextin(ip, 0);

	if(first != ip->wi)/**/
		wakeup(&ip->kr);
}

/*
 *  Receive an incon interrupt.  One entry point
 *  for all types of interrupt.  Until we figure out
 *  how to use more than one incon, this routine only
 *  is for incon[0].
 */
inconintr(Ureg *ur)
{
	uchar status;
	Incon *ip;

	ip = &incon[0];

	status = ip->dev->status;
	if(!(status & RCV_EMPTY))
		rdpackets(ip);

	/* check for exceptional conditions */
	if(status&(OVERFLOW|CRC_ERROR)){
		if(status&OVERFLOW)
			ip->overflow++;
		if(status&CRC_ERROR)
			ip->crc++;
	}

	/* see if it died underneath us */
	if(!(status&INCON_ALIVE)){
		switch(ip->state){
		case Selected:
			ip->dev->cmd = INCON_STOP;
			print("Incon died\n");
			break;
		case Selecting:
			print("rejected\n");
			break;
		default:
			ip->dev->cmd = INCON_STOP;
			break;
		}
		ip->state = Notliving;
	}
}

incontoggle()
{
	incondebug ^= 1;
}
.
## diffname ss/devincon.c 1991/0201 # deleted
## diff -e /n/bootesdump/1990/1223/sys/src/9/sparc/devincon.c /n/bootesdump/1991/0201/sys/src/9/sparc/devincon.c
1,883d

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].