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

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char    *rcsid = "$Header:pc_bios.c 12.0$";
#endif

/*******************************************/
/*					   */
/*     pc_bios.c   1.0	   87/06/15	   */
/*					   */
/*******************************************/


#include "pcbios.h"

#if NPCBIOS > 0

/*
 * PC BIOS Interface
 *
 * Note: This module is VERY rough and crude, but it
 *   works, having the kernel call 'callbios' directly.
 *
 *   Routines getpcmem and freepcmem are not finished, and
 *   should not be considered working. The ioctl could be
 *   use to call 'callbios', but it is not supported.
 */


/*--------------------------------------------------------------*
 * Include files needed for this device driver			*
 *--------------------------------------------------------------*/

#if defined(KERNEL) || defined(STANDALONE)
#include   "../h/types.h"
#include   "../h/file.h"
#include   "../h/ioctl.h"
#include   "../h/param.h"
#else
#include   <sys/types.h>
#include   <sys/file.h>
#include   <sys/ioctl.h>
#include   <sys/param.h>
#endif

#ifdef KERNEL
#include   "../h/systm.h"
#include   "../h/dir.h"
#include   "../h/user.h"
#include   "../h/proc.h"
#include   "../machine/io.h"
#include   "../h/buf.h"
#include   "../machineio/ioccvar.h"
#else				/* STANDALONE system */
#include   "../standca/sa.h"
#endif KERNEL

#include   "../ca_atr/pc_bios.h"
#include   "../ca_atr/pcif.h"
#include   "../ca_atr/display_debug.h"


/*--------------------------------------------------------------*
 * External Variables and Constants				*
 *--------------------------------------------------------------*/

#ifdef STANDALONE
extern struct cbcb *cbcb;	/* needed to get PC addrs of */
extern long    cbcb_addr;	/* of bios data area	     */
#endif STANDALONE


/*--------------------------------------------------------------*
 * Local Variables and Constants				*
 *--------------------------------------------------------------*/

#ifdef DEBUG
long           *debug_bios = &display_debug;	/* point to local debug flags    */
#endif DEBUG

static int      bios_busy = 0;	/* 0, if bios driver is free     */
static int      set_bios_data_ptr = 0;	/* 0, if biosdata ptr is not set */
static struct bios_data *bios_data_ptr = 0;	/* ptr to bios data in PC mem    */
static int      pcbiosioctl_sleep = 0;	/* used for sleep/wakeup control */

#ifdef KERNEL
/* if cold = 1, then callbios routine should poll, not sleep */
extern int      cold;

/* make pcbios interrupt driven */
int 
pcbiosprobe(), pcbiosattach(), pcbiosint();
static caddr_t  pcbiosstd[] = {(caddr_t) 0x00000000};
static struct iocc_device *pcbiosdinfo[1];
struct iocc_driver pcbdriver = {
 /*
  * PROBE	SLAVE	ATTACH	     DGO   ADDR 	 DNAME	    DINFO 
  *
  * MNAME	MINFO	INT	     CSR   CHANREL	 FLAGS		   
  */
	  pcbiosprobe, 0, pcbiosattach, 0, pcbiosstd, "pcbios", pcbiosdinfo,
				0, 0, pcbiosint, 0, 0, 0};
#endif KERNEL


#ifdef KERNEL
/*--------------------------------------------------------------*
 * pcbiosprobe -						*
 *--------------------------------------------------------------*/
pcbiosprobe(addr)
	register caddr_t addr;
{
	DDEBUG(BIOS_PROBE, {
		printf("enter: pc bios probe routine\n");
	});

	DDEBUG(BIOS_PROBE, {
		printf("normal exit: pc bios probe routine\n");
	});
	return (PROBE_NOINT);
}
#endif KERNEL


#ifdef KERNEL
/*--------------------------------------------------------------*
 * pcbiosattach -						*
 *--------------------------------------------------------------*/
pcbiosattach(iod)
	register struct iocc_device *iod;
{
	DDEBUG(BIOS_PROBE, {
		printf("enter: pc bios probe routine\n");
	});

	/* turn PC interrput on */
	/* NOTE: MUST ADD A SET 512 WINDOW */
	pcvec_map[15] = 1;

	printf("PCBIOS is attached\n");

	DDEBUG(BIOS_PROBE, {
		printf("normal exit: pc bios probe routine\n");
	});
	return (1);
}
#endif KERNEL


#ifdef KERNEL
/*--------------------------------------------------------------*
 * pcbiosint -							*
 *--------------------------------------------------------------*/
pcbiosint()
{
	DDEBUG(BIOS_INT, {
		printf("enter: pc bios int routine\n");
	});

	pcbiosioctl_sleep = -1;	/* ioctl will receive a valid wakeup */
	wakeup(&pcbiosioctl_sleep);

	DDEBUG(BIOS_INT, {
		printf("normal exit: pc bios int routine\n");
	});
	return (0);
}
#endif KERNEL


/*--------------------------------------------------------------*
 * pcbiosopen - 						*
 *--------------------------------------------------------------*/
pcbiosopen(dev, flags)
	dev_t           dev;
	int             flags;
{
	DDEBUG(BIOS_OPEN, {
		printf("enter: pc bios open routine\n");
	});

	if (bios_busy)
		return (-1);

	/* set bios busy flag to indicate BIOS driver in use */
	bios_busy = 1;

	DDEBUG(BIOS_OPEN, {
		printf("normal exit: pc bios open routine\n");
	});
	return (0);
}


/*--------------------------------------------------------------*
 * pcbiosclose -						*
 *--------------------------------------------------------------*/
pcbiosclose(dev)
	dev_t           dev;
{
	DDEBUG(BIOS_CLOSE, {
		printf("enter: pc bios close routine\n");
	});

	/* FREE ANY MEMORY BLOCKS USED FOR THIS USER PROCESS */
	/* ... NEEDS TO BE IMPLEMENTED			     */

	/* set bios busy flag to indicate BIOS driver in free */
	bios_busy = 0;

	DDEBUG(BIOS_CLOSE, {
		printf("normal exit: pc bios close routine\n");
	});
	return (0);
}


/*--------------------------------------------------------------*
 * pcbiosioctl -						*
 *--------------------------------------------------------------*/
pcbiosioctl(fd, command, data)
	dev_t           fd;
	int             command;
	char           *data;
{
	DDEBUG(BIOS_IOCTL, {
		printf("enter: pc bios ioctl routine\n");
	});

	int             ret_code;
	int             cmd_lowbyte;

	cmd_lowbyte = command & 0x000000ff;
	switch (cmd_lowbyte) {
	case CMD_CALLBIOS:
		ret_code = callbios(data);
		break;
	case CMD_GETBIOSMEM:
		ret_code = getbiosmem(data);
		break;
	case CMD_FREEBIOSMEM:
		ret_code = freebiosmem(data);
		break;
	case CMD_BIOSTEST:
		ret_code = echo_pcbios(data);
		break;

	default:
		ret_code = err_pcbios(cmd_lowbyte, data);
	};

	DDEBUG(BIOS_IOCTL, {
		printf("normal exit: pc bios ioctl routine\n");
	});
	return (ret_code);
}


/*--------------------------------------------------------------*
 * callbios -							*
 *--------------------------------------------------------------*/
callbios(bios_int_ptr)
	struct bios_int *bios_int_ptr;
{
	register struct proc *rp;
	register int    runstate;

	DDEBUG(BIOS_CALL, {
		printf("enter: pc bios call routine\n");
	});

	register int    i = 0;	/* poll counter		    */
	register int    poll_wait = 1000;	/* maximum wait time for poll */


	pcbiosioctl_sleep = 0;	/* used if kernel system	     */
	bios_int_ptr->finished = 0;	/* used if standalone system   */

	pc_req(CB_BIOSREQ, bios_int_ptr, BIOSENT);	/* make bios call */

#ifdef KERNEL
/*	if ( cold == 1 ) */
	if (cold == cold) {	/* routine always poll, later it will be
		 * fixed *//* if cold start, poll until PC request is
		 * finished */
		pcvec_map[15] = 0;
		while ((bios_int_ptr->finished & 0xFFFF) == 0) {
			delay(100);
			if (i++ > poll_wait) {
				printf("PC POLL TIMEOUT.\n");
				return (-1);
			}
		}
		pcvec_map[15] = 1;
	} else {		/* try to sleep if process is not already
				 * sleeping */
		rp = u.u_procp;
		runstate = rp->p_stat;

		if (runstate != SRUN) {	/* can't sleep, poll until PC request
					 * is finished */
			pcvec_map[15] = 0;
			while ((bios_int_ptr->finished & 0xFFFF) == 0) {
				delay(100);
				if (i++ > poll_wait) {
					printf("PC POLL TIMEOUT.\n");
					return (-1);
				}
			}
			pcvec_map[15] = 1;
		} else {	/* sleep until PC request is finished */
			while (pcbiosioctl_sleep == 0)
				sleep(&pcbiosioctl_sleep, PSLEP);
		}
	}
#else				/* assume STANDALONE */
	pc_poll(&(bios_int_ptr->finished), 0xFFFF);
#endif KERNEL

	/* CHECK FOR ERRORS?? */
	/* POST PROCESSING    */

	DDEBUG(BIOS_CALL, {
		printf("normal exit: pc bios call routine\n");
	});
	return (0);
}


/*--------------------------------------------------------------*
 * getbiosmem - return ptr and size in pcregs			*
 *--------------------------------------------------------------*/
getbiosmem(bios_memory_ptr)
	struct bios_memory *bios_memory_ptr;
{
	DDEBUG(BIOS_GETMEM, {
		printf("enter: pc bios get memory routine\n");
	});

	long            old_window;

	if (!set_bios_data_ptr) {
		old_window = get_512_window();
		set_512_window(cbcb_addr);	/* cbcb_addr as long */
		bios_data_ptr =
			(struct bios_data *) (cbcb->cbcb_ent[BIOSENT].pc_cb + pcif_512_fw);
		set_bios_data_ptr = 1;

		set_512_window(old_window);
	}
/* NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO NO */
	/* Always called from ioctl, thus always invoked via user */
	/* Note: should allocate requested blocks, but for now	  */
	/* just return usr_base_data			  */
	bios_memory_ptr->size = bios_data_ptr->size_usr_data;
	bios_memory_ptr->unx_ptr = (char *) &(bios_data_ptr->usr_data[0]);
	bios_memory_ptr->pc_ptr = (bios_memory_ptr->unx_ptr) - pcif_512_fw;

	DDEBUG(BIOS_GETMEM, {
		printf("normal exit: pc bios get memory routine\n");
	});
	return (0);
}


/*--------------------------------------------------------------*
 * freebiosmem - free PC memory block				*
 *--------------------------------------------------------------*/
freebiosmem(bios_memory_ptr)
	struct bios_memory *bios_memory_ptr;
{
	DDEBUG(BIOS_FREEMEM, {
		printf("enter: pc bios free memory routine\n");
	});

	/* FREE PC MEMORY BLOCK, NOT IMPLEMENTED AS YET */

	DDEBUG(BIOS_FREEMEM, {
		printf("normal exit: pc bios free memory routine\n");
	});
	return (0);
}


/*--------------------------------------------------------------*
 * echo_pcbios							*
 *--------------------------------------------------------------*/
echo_pcbios(bios_int_ptr)
	struct bios_int *bios_int_ptr;
{
	printf("ECHO PC REGISTERS SETTINGS\n");
	printf("Register values are:\n");
	printf("  AX %x  AH %x  AL %x\n",
	       bios_int_ptr->gen_reg.x.ax, bios_int_ptr->gen_reg.h.ah,
	       bios_int_ptr->gen_reg.h.al);
	printf("  BX %x  BH %x  BL %x\n",
	       bios_int_ptr->gen_reg.x.bx, bios_int_ptr->gen_reg.h.bh,
	       bios_int_ptr->gen_reg.h.bl);
	printf("  CX %x  CH %x  CL %x\n",
	       bios_int_ptr->gen_reg.x.cx, bios_int_ptr->gen_reg.h.ch,
	       bios_int_ptr->gen_reg.h.cl);
	printf("  DX %x  DH %x  DL %x\n",
	       bios_int_ptr->gen_reg.x.dx, bios_int_ptr->gen_reg.h.dh,
	       bios_int_ptr->gen_reg.h.dl);
	printf("  SI %x  DI %x  BP %x\n",
	       bios_int_ptr->gen_reg.x.si, bios_int_ptr->gen_reg.x.di,
	       bios_int_ptr->gen_reg.x.bp);
	printf("  CS %x  DS %x  ES %x  SS %x\n",
	       bios_int_ptr->seg_reg.cs, bios_int_ptr->seg_reg.ds,
	       bios_int_ptr->seg_reg.es, bios_int_ptr->seg_reg.ss);
	printf("  CFLAG %x\n", bios_int_ptr->gen_reg.x.cflag);

	return (0);
}


/*--------------------------------------------------------------*
 * err_pcbios							*
 *--------------------------------------------------------------*/
err_pcbios(cmd, bios_int_ptr)
	int             cmd;
	struct bios_int *bios_int_ptr;
{
	printf("PC BIOS Driver command %x is not supported\n", cmd);
	return (-1);
}

#endif NPCBIOS
