/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:rompint.c 12.1$ */
/* $ACIS:rompint.c 12.1$ */
/* $Source: /ibm/acis/usr/sys/os2code/RCS/rompint.c,v $ */

/*
 * Copyright University of Southern California, 1988
 */

#include <doscalls.h>
#include <subcalls.h>
#include <dos.h>
#include "pctype.h"
#include "pcparam.h"
#include "rb.h"
#include "os2data.h"
#include "os2ioctl.h"
#include "sem.h"

extern	char	vec_map[];
extern	int	maskint_flag;
static int	intr_level;

#define Q_INTS	1	/* defined if using int queing */

long int3_sem;		/* semaphore for rompint3 */
long int4_sem;		/* semaphore for rompint4 */

#define RAM_SEM(sem) ((unsigned long) (long far *) & sem)

/* Interrupts have to be queued if the ROMP is still processing the
 * previous interrupt.
 * The interrupt queue structure is defined in rb.h
 */

#define	LVL3_INTR	0
#define LVL4_INTR	1

#ifdef Q_INTS
static struct	intque	intq[2]={0};	/* one for lvl3 and one for lvl4 */

/* functions to operate on the interrupt queues */

static void
q_int(qno,irqlvl,fromintlvl)
int	qno,irqlvl,fromintlvl;

{	register struct intque *iq = &intq[qno];
	register int i;

	i = iq->tail;
	iq->irqlvl[i] = irqlvl;
	iq->fromintlvl[i] = fromintlvl;
	i = (i + 1) % MAXINTQ;
	if ( i != iq->head )
		iq->tail = i;
	else 
		printf("q_int: interrupt queue %d overflow\n",qno);
}

static int
pop_intq(qno,irqlvl,fromintlvl)
int	qno;
int	*irqlvl,*fromintlvl;

{	register struct intque *iq = &intq[qno];
	register int i;

	if ( (i = iq->head) != iq->tail ) {
		*irqlvl = iq->irqlvl[i];
		*fromintlvl = iq->fromintlvl[i];
		i = (i + 1) % MAXINTQ;
		iq->head = i;
		return 1;		/* interrupt queued */
	}
	return 0;	/* queue empty */
}

static int
intr_pending(qno)
int	qno;

{	register struct intque *iq = &intq[qno];

	return (iq->head != iq->tail);
}

static void
reset_intq(qno)
int	qno;

{	register struct intque *iq = &intq[qno];

	iq->head = 0;
	iq->tail = 0;
}
#endif /* Q_INTS */

/* function to determine if we can send a particular level interrupt */

static int
can_send_intr(level)
register int level;

{	register int i;
	register int mask;

	if ( level == LVL3_INTR )
		mask = R_INT3SET;
	else
		mask = R_INT4SET;
	i = IOIN(r_base+R_INTR);
	
	return !(i & mask);
}

/* this function sends the interrupt.
 */

rompint0()
{
	cbcbptr->romp_int0 = 1;
	SETBIT(r_base+R_INTR,R_INT0);
	DELAY(10);
	RSETBIT(r_base+R_INTR,R_INT0);
}

static int
send_interrupt(level,irq)
register int	level;
int	irq;

{	
	switch ( level ) {
	case LVL3_INTR:
		if( vec_map[(irq & 0x0007)] == 0 )
			return 0;	/* disabled */
		cbcbptr->romp_int3 = LOBYTE(irq);
		SETBIT(r_base+R_INTR, R_INT3);
		break;
	case LVL4_INTR:
		if( vec_map[0x8 | (irq & 0x0007)] == 0 )
			return 0;	/* disabled */
		cbcbptr->romp_int4 = LOBYTE(irq);
		SETBIT(r_base+R_INTR, R_INT4);
		break;
	}
	return 1;
}

#ifdef Q_INTS
/* this function sends the next interrupt without checking to see if
 * the previous one is clear. It just pops an interrupt off the queue
 * and sends it directly.
 */

static void
send_next_intr(level)
register int	level;

{
	int	irq,from;

	while ( pop_intq(level,&irq,&from) ){
		if ( send_interrupt(level,irq) == 0 ) {
			/* this one disabled so
			 * throw it away and try
			 * the next.
			 */
			continue;
		}
		return;
	}
	return;
}

static void
q_send_interrupt(level,irqlvl)
int	level, irqlvl;

{
	/*
	 * check to see if the PCIF rompint <level> bit is busy. 
	 */
	if ( !can_send_intr(level) ) {
		/* then busy so enqueue the request */
		q_int(level,irqlvl,0);
		return;
	}
	else if ( intr_pending(level) ) {
		/* can send, but something in
		 * the queue. Send that and queue the 
		 * current req.
		 */

		q_int(level,irqlvl,0);

		send_next_intr(level);
		return;
	}
	else {
		/* none pending so send this one directly if not disabled */
		send_interrupt(level,irqlvl);
		return;
	}
}
#endif /* Q_INTS */

/* send a level 3 interrupt */

void
rompint3(irqlvl)
int             irqlvl;

{
#ifdef Q_INTS
	ENTER_CS();
	q_send_interrupt(LVL3_INTR,irqlvl);
	EXIT_CS();
#else
	int rc;
	int timeout = 10000;
	if (rc = DOSSEMREQUEST(RAM_SEM(int3_sem), (long) 10000))
		printf("SEMREQUEST(int3_sem)=%d\n",rc);
	while ( !can_send_intr(LVL3_INTR) )
		if (--timeout)
			DOSSLEEP((long) 1);
		else {
			printf("int3 timeout\n");
			break;
		}
	send_interrupt(LVL3_INTR,irqlvl);
	if (rc=DOSSEMCLEAR(RAM_SEM(int3_sem)))
		printf("SEMCLEAR(int3_sem)=%d\n",rc);

#endif
}

/* send a level 4 interrupt */

void
rompint4(irqlvl)
int             irqlvl;

{
#ifdef Q_INTS
	ENTER_CS();
	q_send_interrupt(LVL4_INTR,irqlvl);
	EXIT_CS();
#else
	int rc;
	int timeout = 10000;
	if (rc = DOSSEMREQUEST(RAM_SEM(int4_sem), (long) 10000))
		printf("SEMREQUEST(int4_sem)=%d\n",rc);
	while ( !can_send_intr(LVL4_INTR) )
		if (--timeout)
			DOSSLEEP((long) 1);
		else {
			printf("int4 timeout\n");
			break;
		}
	send_interrupt(LVL4_INTR,irqlvl);
	if (rc = DOSSEMCLEAR(RAM_SEM(int4_sem)))
		printf("SEMCLEAR(int4_sem)=%d\n",rc);
#endif
}


void
send_pending_interrupts()

{
#ifdef Q_INTS
	ENTER_CS();
	if ( intr_pending(LVL3_INTR) && can_send_intr(LVL3_INTR) )
		send_next_intr(LVL3_INTR);
	if ( intr_pending(LVL4_INTR) && can_send_intr(LVL4_INTR) )
		send_next_intr(LVL4_INTR);
	EXIT_CS();
#endif /* Q_INTS */
}

void
init_intr_queues()

{
#ifdef Q_INTS
	reset_intq(LVL3_INTR);
	reset_intq(LVL4_INTR);
#endif /* Q_INTS */
}

int
send_priority_kbd_intr()
{
	unsigned int	timeout = 0xffff;

	while ( !can_send_intr(LVL3_INTR) &&  --timeout)
		;
	send_interrupt(LVL3_INTR,FAKEPOLL(KBIRQ));
	return(!timeout);
}

static	int	first_ub_intr = 0;
/* generic polling of interrupts received by the driver */

int
poll_interrupts()
{
	int rc;

	if( !(rc=DOSDEVIOCTL(0,(char far *)&intr_level,
			INT_POLL,PCIF_CAT,pcif_handle))){
		if ( intr_level < 0 ) {
			/* no interrupt pending */
			return 0;
		}
#ifdef DEBUG
		/*printf("poll_interrupts: %d\n", intr_level);
*/
#endif
		/* remap the cascade interrupt level */
		if ( intr_level == 9 )
			intr_level = 2;

		vec_map[intr_level] = 1;

		/* pass on the interrupt
		 * We use Intr3 if the level is less than 8,
		 * and Intr4 if the level is greater than 8.
		 */
		if ( intr_level < 8 )
			rompint3(FAKEPOLL(intr_level));
		else
			rompint4(FAKEPOLL(intr_level));
		return 1;
	}
	else {
		printf("poll_tr_intr: DOSDEVIOCTL returned %d\n",rc);
		return -1;
	}
}
