static char rcsid[] = "$Header: console.c,v 820.1 86/12/04 19:55:10 root Exp $";
static char sccsid[]="%W% %Y% %Q% %G%";

/************************************************************************
*									*
*				Copyright 1984				*
*			VALID LOGIC SYSTEMS INCORPORATED		*
*									*
*	This listing contains confidential proprietary information	*
*	which is not to be disclosed to unauthorized persons without	*
*	written consent of an officer of Valid Logic Systems 		*
*	Incorporated.							*
*									*
*	The copyright notice appearing above is included to provide	*
*	statutory protection in the event of unauthorized or 		*
*	unintentional public disclosure.				*
*									*
************************************************************************/

/*****************************************************************************
     
Module Name: console.c

Function:    Manage the virtual console. The virtual console allows any tty 
	     to act as the console. This is necessary on systems without
	     a console.

Origin:      Valid Logic Systems, UNIX Version 7

History:     10/11/82 -- Version 1.1 -- Valid Logic Systems
					Strafford Wentworth

*****************************************************************************/

/*
 *  sas 840224 -- Fixed error reporting scheme.
 *	840225 -- Clear busy bit on exit from constart().
 *	840405 -- Fixed ioctl return value problem.
 *  jfr	841017 -- CON_ASSIGNed conread and conwrite use cdevsw, not linesw;
 *		  conassign() kicks constart().
 */



#include "cons.h"
#if NCONS > 0
#include "../h/param.h"
#include "../h/ioctl.h"
#include "../h/tty.h"		/* tty structure and parameters */
#include "../h/dir.h"		/* so user.h can be included */
#include "../h/user.h"		/* ENXIO */
#include "../h/proc.h"
#include "../h/conf.h"		/* linesw stuff */
#include "../h/uio.h"
/*
#include "../s32/vectors.h"
#include "../machine/cpu.h"
#include "../s32/debug.h"
#include "../s32/reentrant.h"
#include "../s32/necuart.h"
*/
#include "../s32dev/console.h"


/*
 * CON_UNASSIGNED => a lot of candidates. 
 * CON_FOUND      => found virtconsole, subsequent opens will still 
 *                   open candidates.
 * CON_ASSIGNED   => (set in conclose). No more physical console candidate.
 */ 
short constate = CON_UNASSIGNED;

/* hold values when the console is CON_ASSIGNED */
dev_t  condev = NODEV;		/* Device used as physical console */
struct condevsw  *conassmajor;  /* Pointer to assigned physical device */
struct tty       *conptty;      /* Pointer to physical console   */



/* Are used when console is !CON_ASSIGNED */
struct condevsw *conmajor;   /* current major device pointer */
int              conminor;   /* current minor                */

struct tty      contty;      /* Output and intput virtconsole */
u_char		concntlbusy; /* Used to be "struct uartcntl concntl"
				but we only use .busy field and struct
				uartcntl is no longer in tty.h, so I made
				it a single variable.	-- WPG 22 July 85
			      */

/* This will be implemented with a real buffer some day */
#define CONBUFFSIZE 64
struct {
    short count;
    char  buff[CONBUFFSIZE];

} conbuff;

#define CLOSE_ALL     0
#define CLOSE_EXCEPT  1

int constart ();

conopen (dev, flag)
    dev_t dev;
    int   flag;
{
    register struct condevsw *cp;
    register struct tty      *tp  = &contty;
    register struct tty      *phystty;
    register short            i;
#ifdef CONDEBUG
    register int	retval;
#endif CONDEBUG


#ifdef CONDEBUG
    printf("conopen: Entered\n");
#endif

    if (constate == CON_UNASSIGNED) {
	tp->t_addr  = 0;
	tp->t_oproc = constart;

	if ((tp->t_state&TS_ISOPEN) == 0) {
	    tp->t_state  = TS_ISOPEN | TS_CARR_ON;
	    tp->t_flags  = EVENP | ODDP | ECHO | XTABS | CRMOD
	    | CRTBS | CRTERA | CRTKIL | CTLECH;
	    tp->t_ispeed = B9600;
	    tp->t_ospeed = B9600;
	    ttychars (tp);
	}

	if (constate != CON_ASSIGNED) {
	    /* Open all physical console candidates */

	    /* For each major console device */
	    for (cp=condevsw; cp->cmajor != NODEV ;cp++) {

		/* for each cminor tty device */
		for (i=0; cp->cminor[i].number != NODEV ;i++) {
#ifdef CONDEBUG
		    printf("conopen: open phyical dev (%d,%d)\n",cp->cmajor,
			cp->cminor[i].number);
#endif
		    (*cdevsw[cp->cmajor].d_open)
			(makedev(cp->cmajor,cp->cminor[i].number), flag);
		    
		    phystty = cp->cminor[i].ctty;
		    if (phystty->t_state&TS_ISOPEN)
			phystty->t_oproc = constart;
                 }

	    }
	}
#ifdef CONDEBUG
    	printf("conopen: Exit\n");
#endif
	return(0);
    } else {
#ifdef CONDEBUG
	retval = (*cdevsw[major(condev)].d_open) (condev, flag);
    	printf("conopen: Exit\n");
        return(retval);
#else CONDEBUG
        return((*cdevsw[major(condev)].d_open) (condev, flag));
#endif CONDEBUG
    }

}

conclose (dev)
    register dev_t dev;
{
    if (constate != CON_ASSIGNED) 
	/* Close all physical console candidates */
	concloseall(CLOSE_ALL);
    else {
#ifdef AVIBUG
	printf ("conclose: entry\n");
#endif
	/*
	 * (cdevsw[major(condev)].d_close) (condev);
         */
#ifdef AVIBUG
	printf ("conclose: exit\n");
#endif
    }

    /*
     * For now, just return 0 to closei()
     */

    return(0);
}


conread (dev, uio)
    register dev_t dev;
    struct uio *uio;
{
    while (constate != CON_ASSIGNED) {
	/* 
	 * It is not possible to read all the phsical console candidates
	 * simultaneously. Sleep until console is assigned 
	 */

	sleep ((caddr_t) &constate, TTIPRI);
    }

    return((*cdevsw[major(condev)].d_read)(condev, uio));
}

conwrite (dev, uio)
    register dev_t dev;
    struct uio *uio;
{
    /* All writes go to the virtual console start proc, constart */

    if (constate != CON_ASSIGNED)
        return((*linesw[contty.t_line].l_write) (&contty, uio));
    else
        return((*cdevsw[major(condev)].d_write)(condev, uio));

}

constart (tp)
    register struct tty *tp;
{
    register dev_t cdev;
    short          s;
    short          ccount;

    if (constate == CON_UNASSIGNED) {
	/* start physical console candidates */

	s = spl6();

	if (tp != &contty  && tp->t_outq.c_cc != 0) {
            register struct tty *phystty = conmajor->cminor[conminor].ctty;

	    /* Not finished with output to current phys console, continue */

	    (*conmajor->cstart) (phystty);

	} else {
	    loop : ;
	    if (tp == &contty) {
#ifdef CONDEBUG
                printf ("constart: new contty output \n");
#endif
	        /* New tty output. Start with first physical console */
	        ccount = tp->t_outq.c_cc;
	        if (ccount > CONBUFFSIZE)
		    ccount = CONBUFFSIZE;

	        if (concntlbusy  || ccount == 0) {
		    splx (s);
		    return;
	        }

	        concntlbusy = 1;

	        conbuff.count = q_to_b (&tp->t_outq, conbuff.buff, ccount);
	        conmajor = condevsw;
	        conminor = 0;

#ifdef CONDEBUG
	        printf ("constart: %d virt chars, %d in buffer\n",ccount
							       ,conbuff.count);
#endif
	    } else {
                nextminor: ;

#ifdef CONDEBUG
                printf ("constart: concopy output exausted\n");
#endif

	        /* Current output queue exausted, Start next cminor */
	        conminor++;
            }

	    if (conmajor->cminor[conminor].number == NODEV) {
		/* Current cmajor exausted, Start next cmajor */
		conmajor++;
		conminor = 0;
	    }

	    if (conmajor->cmajor == NODEV) {
		/* Finished with this round of output, check for more */
#ifdef CONDEBUG
		printf ("constart: finished with this round of output\n");
#endif

		tp = &contty;
		concntlbusy = 0;

		goto loop;
	    } else {
		register struct tty *phystty = conmajor->cminor[conminor].ctty;
                register int         conleft;

		if ((phystty->t_state&TS_ISOPEN) == 0) {
#ifdef CONDEBUG
		    printf ("constart:Don't start closed tty\n");
#endif
		    goto nextminor;
		}

		/* Output characters to current physical console candidate */
		if (conleft=b_to_q(conbuff.buff,conbuff.count,&phystty->t_outq))
		    printf("constart: %d chars not in queue, call real start\n",
			   conleft);

		(*conmajor->cstart) (phystty);
	    }
	}

	/*
	 * sas 840225 -- Added to clear busy bit
	 */
	concntlbusy = 0;

	splx (s);
    } else {
	if (constate == CON_FOUND) {
	    concloseall (CLOSE_EXCEPT);
	    catq (&contty.t_outq, &conptty->t_outq);
	    constate  = CON_ASSIGNED;
	    tp        = conptty;
	    wakeup ((caddr_t) &constate);
	}

	(*conassmajor->cstart) (tp);
    }
}


conioctl (dev, cmd, addr, flag)
    register dev_t   dev;
    register int     cmd;
    register caddr_t addr;
    register int     flag;
{

#ifdef AVIBUG
    printf ("conioctl entry\n");
#endif
    if (condev != NODEV)
        return ((*cdevsw[major(condev)].d_ioctl) (condev, cmd, addr, flag));
#ifdef AVIBUG
    printf ("conioctl exit\n");
#endif
    return(0);
}

conselect(dev, rw)
    dev_t dev;
    int rw;
{
    if (condev != NODEV)
        return ((*cdevsw[major(condev)].d_select) (condev, rw));
    u.u_error = ENXIO;
    return(0);
}


concandidate (dev)
    register dev_t dev;
{
    /*
     * This procedure returns true if the argument "dev" is a possible
     * candidate for the physical console.
     * This procedure is called by the interrupt procedures if 
     * constate == CON_UNASSIGNED.
     */

    register struct condevsw *cp;
    register short             i;


    for (cp=condevsw; cp->cmajor != NODEV ;cp++) 
	if (major(dev) == cp->cmajor) 
	    for (i=0; cp->cminor[i].number != NODEV ;i++) 
		if (minor(dev) == cp->cminor[i].number) 
		    return (1);
    return (0);
}


conassign (tp)
    register struct tty *tp;
{
    register short i;

    constate = CON_FOUND;
    condev   = tp->t_dev;
    conptty  = tp;


    for (conassmajor=condevsw; conassmajor->cmajor != NODEV  ; conassmajor++) 
        if (conassmajor->cmajor == major(tp->t_dev)) {
	    constart(tp);
	    return(1);
	}

    panic ("bad physical console device\n");
}

concloseall (flag)
    int flag;
{
    /*
     * This procedure closes all the physical consoles except
     * the one mapped to the virtual console. This is under the
     * control of the flag argument.
     */

    register struct condevsw *cp;
    register short            i;

    /* For each major console device */
    for (cp= condevsw; cp->cmajor != NODEV ;cp++) {

	/* for each cminor tty device */
	for (i=0; cp->cminor[i].number != NODEV ; i++) {

            if (flag == CLOSE_EXCEPT && !(cp->cmajor == major (condev) &&
		cp->cminor[i].number == minor (condev))) {

                   if ((cp->cminor[i].ctty->t_state&TS_ISOPEN) != 0) {
#ifdef CONDEBUG
	            printf("concloseall: close phyical dev (%d,%d)\n",
		            cp->cmajor, cp->cminor[i].number);
#endif
	            (*cdevsw[cp->cmajor].d_close)
		        (makedev(cp->cmajor,cp->cminor[i].number));
		}
	    }
	}
    }
}
#endif
