/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */

/* $Header:strategy.c 12.0$ */
/* $ACIS:strategy.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/pc_code/RCS/strategy.c,v $ */

#ifndef lint
static char *rcsid = "$Header:strategy.c 12.0$";
#endif


#include <dos.h>
#include <stdio.h>
#include <sys/types.h>
#include "pcparam.h"
#include "rb.h"
#include "abios_st.h"
#include "dio.h"
#include "bios.h"
#include "trace.h"
#include "vars.h"


extern void 
irq0(), irq1(), irq2(), irq3(), irq4(), irq5(), irq6(), irq7();
extern void 
irq8(), irq9(), irq10(), irq11(), irq12(), irq13(), irq14(), irq15();


extern pcplqaddr_t pcplq[];
extern int      hdint;
extern int      irqsetup;
extern int      pckbd;

extern
                pcpl_head,	/* Next available dpl we can use */
                pcpl_tail;	/* Next one to work on           */

extern u_short	qeoi;		/* we are getting EOI's from ROMP */


/*  pcstrategy - Place a disk request into the queue.
 *
 *  When Unix wishes to make a PC OP request the PC gets an interrupt via
 *  IRQ 7. Once the flih figures out that it is a PC OP request it then
 *  calls us. Since disk requests for the floppy or the hard disk are not
 *  sequenced in Unix we must be able to receive the request at any time.
 *  However, as we all know BIOS is not reentrant so we must make sure
 *  that all BIOS calls are serialized. At the time of a request we only
 *  need two things from the cbcb: 1) the address of the paramater list in Unix
 *  memory, 2) the op code. Once we know these  we can queue the operation
 *  and return.
 *
 */

void pcstrategy(opcode, unix_pl, cbcb)
	u_short         opcode;
	u_long          unix_pl;
	struct cbcb    *cbcb;
{
	register u_short s;
	pcplqaddr_t     pcplqe;

	s = spl();		/* No interrupts when moving queue pointers */

	/*
	 * Get the next request in the queue. Check to see if it is already
	 * in use. If it is then all the entires of the queue are being used
	 * When this happens we abandon the request. Unix will time out
	 * waiting for the interrupt and send it again. 
	 */

	pcplqe = pcplq[pcpl_head];


	if (pcplqe->flags & (PCPLBUSY | PCPLREQUEST))
	{
		DEBUGF(*dbugptr & DISKDEBUG, printf("pc code: Queue is full, request denied\n"));
	} else
	{

		/*
		 * Queue the request by copying the addresses  of the dpl and
		 * fd_stat structures. 
		 */

		pcplqe->op = opcode;
		pcplqe->unix_cb = unix_pl;
		pcplqe->flags = PCPLREQUEST;
		pcplqe->cbcb = cbcb;		/* requesting cbcb */
		if (++pcpl_head >= QSIZE)	/* Update our pointer */
			pcpl_head = 0;
	}
	splx(s);
}

extern ubq_t    ubq[];
extern u_short  ub_head, ub_tail;

ubstrategy(razcb)
	u_long          razcb;	/* Unix address of the ZCB */
{
	register int    s = spl();

	if (ubq[ub_head].zcbflags & (UBBUSY || UBREQUEST))
	{
		splx(s);
		return (-1);
	} else
	{
		ubq[ub_head].unix_zcb = razcb;
		ubq[ub_head].zcbflags = UBREQUEST;
		if (++ub_head >= QSIZE)	/* Update our pointer */
			ub_head = 0;
	}
	splx(s);
}

void cbcbreq(old_stack)			/* Called from _irq7 in as.asm */
	struct i_stack far *old_stack;
{
	register u_short op = 0;
	register int    index = -1;
	register struct cbcb *cbcb;
	int             mask, s;
	u_long          pcpl = 0;
	u_long          zp = 0;
#ifdef DEBUG
	struct	cbcb_int {
		char far *stack;
		u_short	ip; u_short cs; u_short flags;
	} cbcb_int;
	cbcb_int.stack=(char far *) old_stack;
	cbcb_int.ip = old_stack->IP;
	cbcb_int.cs = old_stack->CS;
	cbcb_int.flags = old_stack->flags;
#endif /* DEBUG */

	for (cbcb=cbcbptr; cbcb<end_cbcb; ++cbcb)
		if (cbcb->op_code)
	{
		op = exchw(cbcb->op_code);
		TRACE(TRACE_CBCBQ, cbcb, sizeof *cbcb);
		TRACE(TRACE_CBCBI, &cbcb_int, sizeof(cbcb_int));
		switch (op)
		{

		case CB_HDREQ:
			index = HDENT;
			break;

		case CB_UBREQ:
			zp = exchl((cbcb->cbcb_ent[UBENT].unix_cb));
			ubstrategy(zp);
			break;


		case CB_FDREQ:
			index = FDENT;
			break;

		case CB_TAPEREQ:
			index = TAPEENT;
			break;

		case CB_OPREQ:
			index = OPENT;
			break;

		case CB_MCREQ:
			index = MCENT;
			break;

		case CB_BIOSREQ:
			index = BIOSENT;
			break;

		case CB_MSREQ:
			index = MSENT;
			break;


		case CB_SPKREQ:
			index = SPKENT;
			break;

		case CB_REBOOT:
			index = 0;
			break;

		case CB_AFIREQ:
			index = AFIENT;
			break;
			/*
			 * Interrupt level commands (not queued) 
			 */

		case CB_TODRESET:
			index = CLENT;
			break;

		case CB_KBREQ:
			index = KBENT;
			break;

		case CB_MASKREQ:
			index = MASKENT;
			break;

		case CB_PUTC:
			index = PUTCENT;
			break;

		case CB_SUSPEND:
			index = 0;
			break;

		case CB_POR:		/* POR Power On Reset the romp */
			index = 0;
			break;

		case CB_IPL:		/* IPL the romp */
			index = 0;
			break;

		case CB_HALT:		/* halt: e.g. return to DOS */
			index = 0;
			break;

		case CB_RESTART:	/* restart: reload standalone & unix */
			{
				extern u_short  do_reboot;
				do_reboot = 1;
			}
			break;


		case CB_QEOI:
			if (option_flag & OPTION_QEOI)
				qeoi = QEOIPENDING;	/* we are now queing EOI's */
			break;

		case CB_EOI3:
			pcpl = exchl((cbcb->cbcb_ent[SVENT].unix_cb));
			eoi_master(0x60 + (((u_short) pcpl) & 0x07));
			break;

		case CB_EOI4:
			pcpl = exchl((cbcb->cbcb_ent[SVENT].unix_cb));
			eoi_slave(0x60 + (((u_short) pcpl) & 0x07));
			break;

		default:
			break;
		}

		if (index >= 0)
		{
			pcpl = exchl((cbcb->cbcb_ent[index].unix_cb));
			pcstrategy(op, pcpl, cbcb);
		}
		cbcb->op_code = 0;
		RSETBIT(cbcb->port+R_CTRL, R_IREQ);
	}
	/*
	 * Turn OFF the ROMP->PS Interrupt BIT in PCF, and send the EOI out
	 * to the 8259, then make sure IRQ7 is unmasked. 
	 */
	if (op == 0)
		lpint();		/* assume its a line printer int */
	eoi_master(0x67);
	s = spl();
	mask = IOIN(INTMAST + 1) & ~(1 << 7);
	IOOUT(INTMAST + 1, mask);
	splx(s);
}

#define LP_INT  0x04		/* printer interrupt */

lpint()
{
	int i = inp(0x3bd);
	int j = inp(0x3be);
	DEBUGF(*dbugptr & LPDEBUG,
		rbprintf("lpint: i=%x j=%x\n",i,j));	/* clear interrupt */
	if ((i&LP_INT) == 0)
		rompint3(3,FAKEPOLL(LPIRQ), 0, 0, (struct cbcb *) 0);
}
