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

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

/*
 * 6152 keyboard code
 */
#include "../h/param.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/ioctl.h"
#include "../h/tty.h"
#include "../h/systm.h"
#include "../h/uio.h"
#include "../h/file.h"
#include "../h/kernel.h"
#include "../machine/debug.h"


#include "../machinecons/screen_conf.h"
#include "../machinecons/consdefs.h"
#include "../machinecons/consvars.h"
#include "../machine/io.h"
#include "../ca_atr/kls.h"
#include "../machinecons/keyboard.h"
#include "../ca_atr/pcif.h"

extern int      cold;		/* if 1, we're still in startup */

extern struct clist kbd_data;
unsigned long   cons_sec;	/* time of last console keystroke */
char           *kbd_set_ptr;
int             kbd_set_cnt, kbd_set_out;
int             kbd_led_flags;
int 
kbddone(), kbdstart(), kbd_led_done(), kbd_init_done();
struct kbdq     kbd_led_buf, kbd_init_buf;
struct kbdq     kbdq[KBDPOOLSZ];
struct kbdq    *kbdhead, *kbdtail;
struct klsq    *kbd_last;
struct kbdq    *kbdfree;
int             click = 1;
int             stdclick = 1;
char            kbd_leds_state = 0;

extern int      kls_rd;
extern int      kls_wrt;
extern int      kls_cntir;
extern int      kls_cntiw;
extern int      at_int_mask;

/*
 * the Keyboard wants to give us a character.
 */
/*ARGSUSED*/
kbdint()
{
	register int    ch;
	register struct tty *tp = &cons[cons_if];
	register SCREEN_INFO *si = &cons_info[cons_if];

	DEBUGF(cndebug & 0x40, printf("In KBDINT\n"));

	for (;;) {
		if ((ch = kls_read(kbd_data)) <= 0)
			break;

		DEBUGF(cndebug & 0x80, printf("scan code %x\n", ch));

#ifdef CONS_FOCUS
		if ((ch == cons_switch_focus_code) && (last_scan != 0xF0)) {
			cons_focus(SCREEN_SWITCH);
			last_scan = ch;
			continue;
		}
		last_scan = ch;
#endif CONS_FOCUS

		si->ine.intr_reason = E_INT_KBD;
		(*emulsw[si->ine.emulator].e_rint) (ch, tp, si);
	}

	DEBUGF(cndebug & 0x40, printf("kbdint end\n"));
	cons_sec = time.tv_sec;	/* Last input time */
}

kbdreset()
{
	register SCREEN_INFO *si = &cons_info[cons_display];

#ifdef NOTIMEOUT
	getchar_timeout = 0;
#endif
	make_break = 0;
	set_click(click);
	if (kbdhead != NULL) {
		kbdstart();
	}
	kbd_status(si, kbd_state);
	kbd_init_buf.count = 1;
	return (0);
}



set_kbd_led(leds)
	char            leds;
{
	register int    s;

	if (leds <= 7)
		kbd_leds_state |= leds;	/* turn leds on */
	else
		kbd_leds_state &= leds;	/* turn leds off */

	s = KLSSPL();
	kbd_led_buf.cmd[0].qp_cmd = kbd_leds_state;
	if (kbd_led_flags & KBD_LED_QUEUED) {
		kbd_led_flags |= KBD_LED_WAIT;
	} else {
		kbd_led_buf.count = 2;
		kbdstrategy(&kbd_led_buf);
		kbd_led_flags |= KBD_LED_QUEUED;
	}
	splx(s);
}

kbd_led_done(qp)
	register struct kbdq *qp;
{
	register int    s = KLSSPL();

	if (kbd_led_flags & KBD_LED_WAIT) {
		kbd_led_buf.count = 2;
		kbdstrategy(&kbd_led_buf);
		kbd_led_flags &= ~KBD_LED_WAIT;
	} else
		kbd_led_flags = 0;
	splx(s);
}

kbdstrategy(qp)
	register struct kbdq *qp;
{
	register int    s = KLSSPL();

	qp->qp_next = NULL;
	qp->ercnt = 0;
	if (kbdhead == NULL) {
		kbdhead = qp;
		kbdtail = qp;
		kbdstart();
	} else {
		kbdtail->qp_next = qp;
		kbdtail = qp;
	}
	splx(s);
}

kbddone(qp)
	register struct klsq *qp;
{
	kbd_last = qp;
	if (qp->qp_ret)
		kbdsint();
}

kbdsint()
{
	register struct klsq *qp = kbd_last;
	register struct kbdq *current;

	if (kbdhead == NULL)
		panic("kbdsint\n");

	/* Machine check encountered.. let kbdreset restart */
	if ((qp->qp_ret) == KLS_SOFT_ERROR) {
		kbdhead->count = kbdhead->maxcnt;
		return;
	}
	/* error, resend last command */
	if ((qp->qp_ret & 0xff) != 0xfa) {
		if (kbdhead->ercnt++ < 5)
			kbdstart();
		else
			klsreset();
		return;
	}
	/* find next command. */
	if ((--kbdhead->count) <= 0) {
		current = kbdhead;
		if ((kbdhead = kbdhead->qp_next) != NULL)
			kbdstart();
		(*current->free) (current);
	} else
		kbdstart();
}

kbdstart()
{
	register int    count;

	if (kbdhead == NULL)
		return;
	count = kbdhead->count;
	if ((count > 0) && (count <= kbdhead->maxcnt))
		klsstrategy(&kbdhead->cmd[count - 1]);
	else
		panic("kbdstart");
}

int             current_menu = ALT_MENU;	/* At power up we are in
						 * Alternate menu */

/* Set all the keys to 'typecmd' type.  Then set the special set of
 * keys just to make/break so they don't repeat.
 */
set_default_key_types(typecmd, flag)
	int             typecmd;
	int             flag;
{
#ifdef ENH_AT_KEYBOARD
	int             keys[20];
	register int    i = 0;

	/* Check if Repeat keys should be make/break or not */
	if (flag != current_menu)
		tog_menu();

	/* Set all keys to typecmd */
	set_all_type(typecmd);

	/*
	 * Set the Control, Shift, etc... so they don't repeat repeat
	 * repeat.. 
	 */
	 /* 0 */ keys[i++] = MONO_SHIFT1;
	 /* 1 */ keys[i++] = MONO_SHIFT2;
	 /* 2 */ keys[i++] = MONO_ALT1;
	 /* 3 */ keys[i++] = MONO_ALT2;
	 /* 4 */ keys[i++] = MONO_CAPS_LOCK;
	 /* 5 */ keys[i++] = MONO_CNTRL;
	 /* 6 */ keys[i++] = MONO_PAUSE;
	 /* 7 */ keys[i++] = MONO_PRINT;
	 /* 8 */ keys[i++] = MONO_NUM_LOCK;
	 /* 9 */ keys[i++] = MONO_SCROLL_LOCK;
	 /* 10 */ keys[i++] = MONO_ESC;
	 /* 11 */ keys[i++] = MONO_ACTION;
	 /* 12 */ keys[i++] = 0;
	set_key_type(KSETMKBRK, keys);

	i = 0;
	 /* 0 */ keys[i++] = CONS_SWITCH_FOCUS_CODE;
	 /* 1 */ keys[i++] = 0;
	set_key_type(KSETMAKE, keys);
#endif ENH_AT_KEYBOARD
}

kbdqfree(qp)
	register struct kbdq *qp;
{
	wakeup((caddr_t) qp);
}

tog_menu()
{
#ifdef ENH_AT_KEYBOARD
	set_all_type(KTOGMENU);

	/* wait for command complete */
	current_menu = !current_menu;
#endif ENH_AT_KEYBOARD
}

set_all_type(typecmd)
	register int    typecmd;
{
#ifdef ENH_AT_KEYBOARD
	register struct kbdq *qp;
	register int    s = KLSSPL();

	while ((qp = (struct kbdq *) klsalloc(&kbdfree)) == NULL)
		sleep((caddr_t) & kbdfree, KBDPRI);

	qp->count = 1;
	qp->cmd[0].qp_dest = KLS_WRITE;
	qp->cmd[0].qp_cmd = typecmd;
	qp->cmd[0].qp_callback = kbddone;
	qp->maxcnt = 1;
	qp->free = kbdqfree;
	kbdstrategy(qp);
	while (qp->count != 0)
		sleep((caddr_t) qp, KBDPRI);
	klsfree(kbdfree, qp);
	wakeup((caddr_t) & kbdfree);
	splx(s);
#endif ENH_AT_KEYBOARD
}


/* 'typecmd' is what type of key you want to set the array of key codes 'keys'
 * to.  The last element in the 'keys' array should be a 0
 */
set_key_type(typecmd, keys)
	register int    typecmd;
	register int   *keys;
{
#ifdef ENH_AT_KEYBOARD
	register struct kbdq *qp;
	register int    s = KLSSPL();

	while ((qp = (struct kbdq *) klsalloc(&kbdfree)) == NULL)
		sleep((caddr_t) & kbdfree, KBDPRI);

	while (*keys) {
		for (qp->maxcnt = 0; qp->maxcnt < KBDMAXCOUNT - 1; qp->maxcnt++) {
			qp->cmd[qp->maxcnt].qp_dest = KLS_WRITE;
			qp->cmd[qp->maxcnt].qp_cmd = *keys;
			qp->cmd[qp->maxcnt].qp_callback = kbddone;
			if ((*++keys) == 0) {
				qp->maxcnt++;
				break;
			}
		}
		qp->cmd[qp->maxcnt].qp_dest = KLS_WRITE;
		qp->cmd[qp->maxcnt].qp_cmd = typecmd;
		qp->cmd[qp->maxcnt].qp_callback = kbddone;
		qp->count = (++qp->maxcnt);
		qp->free = kbdqfree;
		kbdstrategy(qp);
		while (qp->count > 0)
			sleep((caddr_t) qp, KBDPRI);
	}
	klsfree(kbdfree, qp);
	wakeup((caddr_t) & kbdfree);
	kbd_init_buf.count = 1;
	kbdstrategy(&kbd_init_buf);
	splx(s);
#endif ENH_AT_KEYBOARD
}


/* Set key click duration */
click_duration(n)
	unsigned char   n;
{
/* Shouldn't be called. AT cannot do this anyway. */
	printf("click_duration: unknown caller ...\n");
}

/* Handy procedure for giving extended commands */
keyextcmd(n)
	unsigned char   n;
{
/* Again, irrelevant in AT land. */
	printf("keyextcmd: unknown caller ...\n");
}


int             beep_freqh = BEEP_FREQ_HIGH;
int             beep_freql = BEEP_FREQ_LOW;
int             beep_vol = 3;	/* 0 = none, non-zero = full */
int             beep_time = CLICKVOL;

beep()
{
	if (!cold)		/* don't beep during autoconf...may miss
				 * interrupts */
		speaker(beep_vol, beep_freqh, beep_freql, beep_time);
}


_reboot(howto, devtype)
{

	if (pc_req(CB_REBOOT, 0, 0) < 0) {	/* Tell the PC that we have a
						 * request */
		printf("reboot: PC request timeout.\n");
	}
	for (;;)
		asmwait();
}

/*
 * if OPEN (normal case) then send a SIGHUP, otherwise a SIGKILL.
 * in any case send a SIGCONT, flush the input/output queues and
 * turn off carrier (which should result in TS_ISOPEN going away).
 */
cnhangup()
{
#ifdef KERNEL
	register struct tty *tp = &cons[cons_if];
	if (tp->t_state & TS_ISOPEN) {
		gsignal(tp->t_pgrp, (tp->t_state & TS_CARR_ON) ? SIGHUP : SIGKILL);
		gsignal(tp->t_pgrp, SIGCONT);
		ttyflush(tp, FREAD | FWRITE);
		tp->t_state &= ~TS_CARR_ON;
	}
#endif KERNEL
}

/*
 * if we are stopped then simulate a start character,
 * otherwise simulate a stop character.
 */
cnscroll()
{

	register struct tty *tp = &cons[cons_if];
	register int    c = (tp->t_state & TS_TTSTOP) ? tp->t_startc : tp->t_stopc;
#ifdef KERNEL
	if (c)
		(*linesw[tp->t_line].l_rint) (c, tp);
#endif
}


kbdinit()
{
	register struct kbdq *qp = &kbd_led_buf;
	/*
	 * If we have a generic kernel, we may need the keyboard before the
	 * console has been opened. In that case we need to call the
	 * kbdcreset here. 
	 */
#ifdef GENERIC
	kbdcreset();
#endif
	_init_kbd++;
	kbdfree = (struct kbdq *) klsminit(kbdq, sizeof(struct kbdq), KBDPOOLSZ);
	qp->cmd[1].qp_dest = KLS_WRITE;
	qp->cmd[1].qp_cmd = SETLED;
	qp->cmd[1].qp_callback = kbddone;
	qp->cmd[0].qp_dest = KLS_WRITE;
	qp->cmd[0].qp_cmd = 0;
	qp->cmd[0].qp_callback = kbddone;
	qp->maxcnt = 2;
	qp->free = kbd_led_done;
	kbd_led_flags = 0;
	qp = &kbd_init_buf;
	qp->cmd[0].qp_dest = KLS_WRITE;
	qp->cmd[0].qp_cmd = KSCAN;
	qp->cmd[0].qp_callback = kbddone;
	qp->maxcnt = 1;
	qp->free = kbd_init_done;
	kbd_state = 0;
}

/* ARGSUSED */
kbd_init_done(kbdqp)
	register struct kbdq *kbdqp;
{
}

kbd_ints(flag)
	int             flag;
{
}

while_kbd_disabled()
{
/*	Continue to read keyboard controller status register		*/
/*	until the Keyboard lock switch has been turned off.		*/
/*	Keylock is 'turned off' when controller 			*/
/*	status bit 4 = 1.						*/
/*	Bit is ONLY updated when data is put in keyboard output		*/
/*	buffer. We will 'force' this by reading the keyboard 		*/
/*	controller's command byte.					*/


}

kbdwait()
{
	while (kbdhead != NULL);
}
