File:  [MW Coherent from dump] / coherent / d / PS2_KERNEL / io.286 / gc.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:39 2019 UTC (7 years ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/*
 * Interrupt Driver Multi-Port Device Driver.
 * - supports version 7 compatible ioctl
 */

#include "coherent.h"
#include "ins8250.h"
#include <sys/stat.h>
#include <sys/proc.h>
#include <sys/tty.h>		/* indirectly includes sgtty.h */
#include <sys/con.h>
#include <errno.h>
#include <sys/timeout.h>	/* TIM */
#include <sched.h>		/* CVTTOUT, IVTTOUT, SVTTOUT */
#include <poll_clk.h>

/*
 * Definitions.
 *
 * MAX_GCNUM is the maximum number of devices that can be polled
 *   using this driver and can be revised up or down
 * PORT is a convenience macro for the base address of a port
 * port_config is the structure of the initial configuration for each
 *   polled port;  note that "speed" is NOT the actual baud rate, but
 *   the value of the symbol for that baud rate as defined in
 *   /usr/include/sgtty.h
 * GCINT is the IRQ number for the interrupt belonging to the board.
 *   On the AT, IRQ2 is mapped to the same vector as IRQ9.
 */
#define MAX_GCNUM	8
#define	PORT	((int)(tp->t_ddp))
struct port_config {
	int	addr;	/* base address of the 8250-family UART */
	int	speed;	/* B0..B19200 */
};
#define GCINT		2

/*
 * Export Variables - these can be patched without recompiling and linking
 *
 * GCNUM is the actual number of polled serial ports, and should be
 *   less than or equal to MAX_GCNUM
 * GCIRPT is the I/O address of the multiport interrupt register;
 *   a value of 0 in bit 0..3 means a pending interrupt for port 0..3
 * GC_PORTS is an array of address/speed pairs, one for each port
 */
int	GCNUM = 4;
int	GCIRPT = 0x2BF;
/*
 * Defaults are for PC COM4, Enhanced Mode, High Address.
 */
struct port_config GC_PORTS[MAX_GCNUM] = {
	{ 0x2A0, B9600 },
	{ 0x2A8, B9600 },
	{ 0x2B0, B9600 },
	{ 0x2B8, B9600 }
};

/*
 * Export Functions.
 */
int	gcload();
int	gcopen();
int	gcclose();
int	gcread();
int	gcwrite();
int	gcioctl();
int	gcunload();
int	gcpoll();

int	gccycle();
int	gcintr();
int	gcparam();
int	gcstart();

/*
 * Import Functions
 */
int	nulldev();
int	nonedev();

/*
 * Configuration table.
 */
CON gccon ={
	DFCHR|DFPOL,			/* Flags */
	GCINT,				/* Major index */
	gcopen,				/* Open */
	gcclose,			/* Close */
	nulldev,			/* Block */
	gcread,				/* Read */
	gcwrite,			/* Write */
	gcioctl,			/* Ioctl */
	nulldev,			/* Powerfail */
	nulldev,			/* Timeout */
	gcload,				/* Load */
	gcunload,			/* Unload */
	gcpoll				/* Poll */
};

/*
 * Local variables.
 */
static TTY *hstty;
static TTY *hslimtty;
static TIM hstim;

/*
 * Time constant table.
 * Indexed by ioctl baud rate.
 */
static
int timeconst[] = {
	0,				/* 0 */
	2304,				/* 50 */
	1536,				/* 75 */
	1047,				/* 110 */
	857,				/* 134.5 */
	768,				/* 150 */
	576,				/* 200 */
	384,				/* 300 */
	192,				/* 600 */
	96,				/* 1200 */
	64,				/* 1800 */
	58,				/* 2000 */
	48,				/* 2400 */
	32,				/* 3600 */
	24,				/* 4800 */
	16,				/* 7200 */
	12,				/* 9600 */
	6,				/* 19200 */
	6,				/* EXTA */
	6				/* EXTB */
};

/*
 * Load Routine.
 */
static gcload()
{
	register TTY * tp;
	register int port;
	int i, b, s;

	if ((hstty = (TTY *)kalloc(GCNUM*sizeof(TTY))) == 0) {
		printf("gcload: can't allocate tty's\n");
		return;
	}
	kclear(hstty, GCNUM*sizeof(TTY));

	s = sphi();
	for (i = 0; i < GCNUM; i++) {
		port = GC_PORTS[i].addr;
		tp = hstty + i;

		outb( port+MCR, 0 );
		outb( port+IER, 0 );

		if ( inb( port+IER ) )
			break;

		tp->t_cs_sel  = cs_sel();
		tp->t_start   = gcstart;
		tp->t_param   = gcparam;
		tp->t_sgttyb.sg_ospeed = tp->t_sgttyb.sg_ispeed = 
		tp->t_dispeed = tp->t_dospeed = GC_PORTS[i].speed;
		tp->t_ddp     = port;

		b = timeconst[ tp->t_sgttyb.sg_ospeed ];
		outb( port+LCR, LC_DLAB );
		outb( port+DLL, b );
		outb( port+DLH, b >> 8);
		outb( port+LCR, LC_CS8);

		hslimtty = tp;
	}
	setivec(GCINT, gcintr);
	spl(s);
}

static gcunload()
{
	clrivec(GCINT);
	kfree(hstty);
}

/*
 * Open Routine.
 */
gcopen( dev, mode )
dev_t dev;
{
	register TTY * tp = &hstty[ dev & 15 ];
	register int b;
	int s;

	/*
	 * Verify hardware exists.
	 */
	if ( (PORT == 0) || (inb(PORT+IER) & ~IE_TxI) ) {
		u.u_error = ENXIO;
		return;
	}

	/*
	 * Initialize if not already open.
	 */
	if ( ++tp->t_open == 1 ) {
		ttopen( tp );

		if ( dev & 0x80 ) {
			s = sphi();
			b = inb(PORT+MSR);
			tp->t_flags |= T_MODC + T_STOP;
			if ( b & MS_CTS )
				tp->t_flags &= ~T_STOP;
			if ( b & MS_DSR )
				tp->t_flags |=  T_CARR;
			spl( s );
		} else  {
			tp->t_flags &= ~T_MODC;
			tp->t_flags |=  T_CARR;
		}
		gccycle( tp );
	}
	ttsetgrp( tp, dev );
}

/*
 * Close Routine.
 */
gcclose( dev )
dev_t dev;
{
	register TTY * tp = &hstty[ dev & 15 ];

	/*
	 * Reset if last close.
	 */
	if ( tp->t_open == 1 ) {
		int state;

		ttclose( tp );
		/*
		 * ttclose() only emptied the output queue tp->t_oq;
		 * now wait 0.1 sec for the silo tp->rawout to empty
		 * and allow a delay for the UART on-chip xmit buffer to empty
		 *
		 * state 2: waiting for silo to empty
		 * state 1: stalling so UART can empty xmit buffer
		 * state 0: done!
		 */
		state = 2;
		while (state) {
			timeout(&hstim, 10, wakeup, (int)&hstim);
			sleep((char *)&hstim, CVTTOUT, IVTTOUT, SVTTOUT);
			if (tp->t_rawout.si_ix == tp->t_rawout.si_ox  && state)
				state--;
		}
	}

	--tp->t_open;
}

/*
 * Read Routine.
 */
gcread( dev, iop )
dev_t dev;
register IO * iop;
{
	ttread( &hstty[ dev & 15 ], iop, 0 );
}

/*
 * Write Routine.
 */
gcwrite( dev, iop )
dev_t dev;
register IO * iop;
{
	ttwrite( &hstty[ dev & 15 ], iop, 0 );
}

/*
 * Ioctl Routine.
 */
gcioctl( dev, com, vec )
dev_t dev;
int com;
struct sgttyb * vec;
{
	ttioctl( &hstty[ dev & 15 ], com, vec );
}

/*
 * Polling Routine.
 */
gcpoll( dev, ev, msec )
dev_t dev;
int ev;
int msec;
{
	return ttpoll( &hstty[ dev & 15 ], ev, msec );
}

/*
 * Cyclic routine - invoked every clock tick to perform raw input/output.
 *
 *	Notes:	Invoked 10 times per second.
 */
gccycle( tp )
register TTY * tp;
{
	register int resid;
	register int c;

	/*
	 * Process rawin buf.
	 */
	while ( tp->t_rawin.si_ix != tp->t_rawin.si_ox ) {

		ttin( tp, tp->t_rawin.si_buf[ tp->t_rawin.si_ox ] );

		if ( tp->t_rawin.si_ox >= sizeof(tp->t_rawin.si_buf) - 1 )
			tp->t_rawin.si_ox = 0;
		else
			tp->t_rawin.si_ox++;
	}

	/*
	 * Calculate free output slot count.
	 */
	resid  = sizeof(tp->t_rawout.si_buf) - 1;
	resid += tp->t_rawout.si_ox - tp->t_rawout.si_ix;
	resid %= sizeof(tp->t_rawout.si_buf);

	/*
	 * Fill raw output buffer.
	 */
	while ( (--resid >= 0) && ((c = ttout(tp)) >= 0) ) {

		tp->t_rawout.si_buf[ tp->t_rawout.si_ix ] = c;

		if ( tp->t_rawout.si_ix >= sizeof(tp->t_rawout.si_buf) - 1 )
			tp->t_rawout.si_ix = 0;
		else
			tp->t_rawout.si_ix++;
	}

	/*
	 * (Re)start output, waking processes waiting to output, etc.
	 */
	ttstart( tp );

	/*
	 * Schedule next cycle.
	 */
	if ( tp->t_open != 0 )
		timeout( &tp->t_rawtim, HZ/10, gccycle, tp );
}

/*
 * Interrupt driven Polling routine.
 */
gcintr()
{
	register TTY * tp = &hstty[0];
	register int b;

	do {
		if ( tp->t_open == 0 )
			continue;

		/*
		 * Check modem status if modem control is enabled.
		 */
		if ( tp->t_flags & T_MODC ) {

			b = inb( PORT+MSR );

			if ( b & (MS_DCTS|MS_DDSR) ) {

				if ( b & MS_DCTS ) {
					if ( b & MS_CTS )
						tp->t_flags &= ~T_STOP;
					else
						tp->t_flags |=  T_STOP;
				}
				if ( b & MS_DDSR ) {
					if ( b & MS_DSR )
						tp->t_flags |=  T_CARR;
					else {
						tp->t_flags &= ~T_CARR;
						tthup( tp );
					}
				}
			}
		}

		b = inb( PORT+LSR );

		if ( (b & LS_BREAK) && (tp->t_flags & T_CARR) )
			ttsignal( tp, SIGINT );

		/*
		 * Receive ready.
		 */
		if ( b & LS_RxRDY ) {

			tp->t_rawin.si_buf[tp->t_rawin.si_ix] = inb(PORT+DREG);

			if ( tp->t_flags & T_CARR ) {

				if ( ++(tp->t_rawin.si_ix) >=
						sizeof(tp->t_rawin.si_buf) )
					tp->t_rawin.si_ix = 0;
			}
		}

		/*
		 * Transmit ready and raw output data exists.
		 */
		if ( (b & LS_TxRDY) && ((tp->t_flags & T_STOP) == 0)
		  && (tp->t_rawout.si_ix != tp->t_rawout.si_ox) ) {

			outb(	PORT+DREG,
				tp->t_rawout.si_buf[ tp->t_rawout.si_ox ] );

			if ( ++(tp->t_rawout.si_ox) >=
					sizeof(tp->t_rawout.si_buf) )
				tp->t_rawout.si_ox = 0;
		}

	} while ( ++tp <= hslimtty );
}

/*
 * Set hardware parameters.
 */
gcparam( tp )
register TTY * tp;
{
	register int b;
	int s;

	s = sphi();
	/*
	 * Assert required modem control lines (DTR, RTS).
	 */
	b = 0;
	if ( tp->t_sgttyb.sg_ospeed != B0 )
		b |=  MC_DTR | MC_RTS;
	outb( PORT+MCR, b );

	/*
	 * Program baud rate.
	 */
	if (b = timeconst[ tp->t_sgttyb.sg_ospeed ]) {
		outb( PORT+LCR, LC_DLAB );
		outb( PORT+DLL, b );
		outb( PORT+DLH, b >> 8 );
	}

	/*
	 * Program character size, parity.
	 */
	switch ( tp->t_sgttyb.sg_flags & (EVENP|ODDP|RAW) ) {
	case ODDP:		b = LC_CS7|LC_PARENB;		 break;
	case EVENP:		b = LC_CS7|LC_PARENB|LC_PAREVEN; break;
	default:		b = LC_CS8;			 break;
	}
	outb( PORT+LCR, b );

	/*
	 * Enable Transmit Buffer Empty Interrupts.
	 */
	outb( PORT+IER, IE_TxI );

	spl(s);
}

/*
 * Start Routine.
 */
gcstart( tp )
register TTY * tp;
{
	register int s;

	/*
	 * Transmit buffer is empty, and raw output buffer is not.
	 */
	s = sphi();
	if ( (inb( PORT+LSR ) & LS_TxRDY)
	  && (tp->t_rawout.si_ix != tp->t_rawout.si_ox) ) {

		/*
		 * Send next char from raw output buffer.
		 */
		outb( PORT+DREG, tp->t_rawout.si_buf[ tp->t_rawout.si_ox ] );

		if ( ++tp->t_rawout.si_ox >= sizeof(tp->t_rawout.si_buf) )
			tp->t_rawout.si_ox = 0;
	}
	spl( s );
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.