
#include "struct.h"
#include <conio.h>
#include <i86.h>
#include <sys/irqinfo.h>
#include <sys/trace.h>
#include <sys/tracecod.h>

extern struct dev_entry dev_table[];
extern struct dev_entry *dev_list[];
extern struct driver_ctrl drv_ctrl;
extern int num_asyncs;
extern long divisor;

pid_t (far *tti)(int, int, unsigned);
unsigned tti_ds;

static char timer_enabled = 0;

static char int_enabled[16] ={
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

static unsigned irq_hdl[16] ={
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

pid_t (far *async_irq[16])();

void init_tables() {
	async_irq[0] = &async_irq0;
	async_irq[1] = &async_irq1;
	async_irq[2] = &async_irq2;
	async_irq[3] = &async_irq3;
	async_irq[4] = &async_irq4;
	async_irq[5] = &async_irq5;
	async_irq[6] = &async_irq6;
	async_irq[7] = &async_irq7;
	async_irq[8] = &async_irq8;
	async_irq[9] = &async_irq9;
	async_irq[10] = &async_irq10;
	async_irq[11] = &async_irq11;
	async_irq[12] = &async_irq12;
	async_irq[13] = &async_irq13;
	async_irq[14] = &async_irq14;
	async_irq[15] = &async_irq15;
	}

void init(index, base, irq, baud, mode)
int index;
unsigned base, irq;
long baud;
int mode;
	{
	register struct dev_entry *ttp;
	extern pid_t far poll_devices();

	/*
	 * Get a free dev_entry
	 */
	ttp = &dev_table[index];

	ttp->iobase = base;
	ttp->irq = irq;
	ttp->busy = 0;
	ttp->baud = baud;
	ttp->parity = 0;
	ttp->stop_bits = 1;
	ttp->data_bits = 8;
	ttp->mode = mode;
	ttp->modem_control = (inp(ttp->iobase+4) & 0xE4) | 0x08;
	if(mode & MODE_DTR)
		ttp->modem_control |= 1;
	if(mode & MODE_RTS)
		ttp->modem_control |= 2;

	/*
	 * Tell irq handler(s) how to call Dev
	 */
	tti = drv_ctrl.tti;
	tti_ds = drv_ctrl.tti_ds;

	/*
	 * Only setup IRQ handler for non-pcmcia devices
	 * pcmcia devices will have this done later when card is inserted
	 */

	if(ttp->slots == 0  &&  irq > 0  &&  irq <= 15) {
		reset_async(index, 1);
		}

	if(timer_enabled == 0) {
		timer_enabled = 1;
		/*
		 * Link into 100 msec timer chain
		 */
		qnx_hint_attach( -1, &poll_devices, FP_SEG(&dev_table) );
		}

	}

/*
 * Default device scan
 */


int find_asyncs() {
	if(check_async(0x3f8))
			new_async(num_asyncs++, 0x3f8, 4, 0);
	if(check_async(0x2f8))
			new_async(num_asyncs++, 0x2f8, 3, 0);
	return(num_asyncs);
	}

static int check_async(unsigned port)
	{
	/*
	 * Use Line control latch to determine if controller card is present
	 */
	_disable();
	outp(port + 3, 0x05);
	if(inp(port + 3) == 0x05) {
		outp(port + 3, 0x0a);
		if(inp(port + 3) == 0x0a) {
			/*
			 * Found one....
			 * Clear any pending interrupts
			 */
			outp(port + 1, 0x00);	/* Disable all interrupts */
			inp(port + 5);		/* Clear Line Status Interrupt */
			inp(port + 0);		/* Clear RX Interrupt */
			inp(port + 2);		/* Clear TX Interrupt */
			inp(port + 6);		/* Clear Modem Interrupt */
			_enable();
			return(1);
			}
		}
	_enable();
	return(0);
	}


/*
 * These routines are called by Stty(), so make SURE that
 * stack checking is OFF
 */
#pragma off (check_stack);

static void set_port(port, mask, data)
unsigned port, mask, data;
	{
	unsigned char c;
	c = inp(port);
	outp(port, (c & ~mask) | (data & mask));
	}

int reset_async(index, init)
int index;
int init;
	{
	register struct dev_entry *ttp;
	unsigned port;
	unsigned value;

	ttp = &dev_table[index];
	port = ttp->iobase;

	if(port == 0)
		return(0);

	/*
	 * Set Baud rate
	 */
	if(ttp->baud == 0)
		value = 0;
	else
		value = divisor / ttp->baud; /* calculate divisor for baud rate */

	_disable();
	set_port(port + 3, 0x80, 0x80);
	set_port(port + 0, 0xff, value & 0xff);
	set_port(port + 1, 0xff, value >> 8);
	set_port(port + 3, 0x80, 0);
	/*
	 * Set parity, stop bits and data bits
	 */
	set_port(port + 3, 0xFF, ((ttp->parity & 7) << 3)
						   | (((ttp->stop_bits - 1) & 1) << 2)
						   | ((ttp->data_bits - 5) & 3) );

	if(init) {
		/*
		 * Save initial modem status, and also clear all interrupts
		 */
		outp(port + 1, 0x00);	/* Disable all interrupts */
		inp(port + 5); /* Clear Line Status Interrupt */
		inp(port + 0); /* Clear RX Interrupt */
		inp(port + 2); /* Clear TX Interrupt */
		ttp->modem_status = inp(port + 6) & 0xF0; /* Clear Modem Interrupt */
		/*
		 * turn on DTR, RTS
		 */
		outp(port+4, ttp->modem_control);

		switch(ttp->threshold) {
			case 1:  value = 0x01; break;
			case 4:  value = 0x41; break;
			case 8:  value = 0x81; break;
			case 14: value = 0xC1; break;
			default: value = 0; break;
			}

		if(value) {
			/*
			 * Try to enable 1/4/8/14-deep FIFO mode if a 16550
			 * NOTE: That this is NOT always desirable since it significantly
			 *       affects recieved character timing, and does NOT
			 *       significantly increase receive performance.
			 *       (Keep in mind that the 16550 with FIFO disabled
			 *        is still very useful, since it SHOULD buffer
			 *        up to 16 received characters even when FIFO is off)
			 */
			outp(port+2, value);
			if((inp(port+2) & 0xC0) != 0xC0)
				outp(port+2, 0x00);               	
			}
		/*
		 * Enable ALL interrupt sources
		 */
		outp(port+1, 0x0f);

		if((ttp->mode & MODE_IRQ_LINKED) == 0) {
			ttp->mode |= MODE_IRQ_LINKED;
			/*
			 * Add this device to the list of devices at the specified interrupt
			 */
			ttp->link = dev_list[ttp->irq];
			dev_list[ttp->irq] = ttp;
		
			if(int_enabled[ttp->irq] == 0) {
				/*
				 * Vector IRQ to our async handler
				 */
				_enable();
				irq_hdl[ttp->irq] = qnx_hint_attach(ttp->irq, async_irq[ttp->irq], FP_SEG(&drv_ctrl));
				int_enabled[ttp->irq] = 1;
				}
			}
		}

	_enable();

	return(0);
	}

void disable_irq( int index )
	{
	register struct dev_entry *ttp, *tt;
	int irq;

	ttp = &dev_table[index];
	irq = ttp->irq;

	if((ttp->mode & MODE_IRQ_LINKED) != 0) {
		ttp->mode &= ~MODE_IRQ_LINKED;
		/*
		 * Remove this device from list of devices at the specified interrupt
		 */
		tt = (struct dev_entry *) &dev_list[irq];
		while(tt) {
			if(tt->link == ttp) {
				tt->link = ttp->link;
				ttp->link = 0;
				break;
				}
			}
		
		if(dev_list[irq] == 0) {
			/*
			 * IRQ list is now empty
			 */
			int_enabled[irq] = 0;
			qnx_hint_detach(irq_hdl[irq]);
			}
		}
	}

pid_t far poll_devices() {
	int i;
	struct dev_entry *dev = &dev_table[0];

	for( i = 0; i < num_asyncs; ++i) {
		if(dev->iobase != 0  &&  dev->tx_pending != 0  &&  --dev->tx_pending == 0) {
			/*
	 		 * Compensate for "bugs" (especially CMOS parts) which cause 
	 		 * missed Tx interrupts.
			 * Rely on periodic "polling" to reset the chip.
	 		 */
			outp(dev->iobase+1, 0x0f);
			Trace1( (long)_TRACE_DEV_SER_TIMEOUT, _TRACE_TRANSIENT, dev->iobase );
			}
		++dev;
		}
	return(0);
	}

/*
 * If -w is specified, then Dev.ser will disable interrupts
 * on the serial chip when SIGPWR is received (soft reboot)
 */

shutdown_async(int index) {
	unsigned iobase;

	iobase = dev_table[index].iobase;
	if(iobase != 0)
		outp(iobase + 1, 0x00);	/* Disable IER */
	}

#pragma on (check_stack);
