/* Hello emacs, this should be edited in -*- Fundamental -*- mode */
%{
/*
 * Copyright (c) 1994 Joerg Wunsch
 *
 * All rights reserved.
 *
 * This program is free software.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Joerg Wunsch
 * 4. The name of the developer may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ident "$FreeBSD: stable/2.1/usr.sbin/pcvt/kbdio/kbdio.y 50952 1999-09-05 11:54:50Z peter $"

/*
 * $Log: kbdio.y,v $
 * Revision 1.2  1995/04/01  17:16:26  joerg
 * Update pcvt to 3.20b23
 *
 * Submitted by:	Hellmuth Michaelis <hm@altona.hamburg.com>
 *
 * Revision 1.1.1.1  1995/02/05  13:49:24  jkh
 * PCVT userland utilities.
 * Submitted by:	hm
 *
 * Revision 1.2  1994/09/18  19:49:22  j
 * Refined expr handling; can now set/clear bits.
 *
 * Revision 1.1  1994/09/18  12:57:13  j
 * Initial revision
 *
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/fcntl.h>
#include <machine/cpufunc.h>
#include <machine/pcvt_ioctl.h>

#ifdef __NetBSD__
#include <machine/pio.h>
#endif

#define	KBD_DELAY \
	{ u_char x = inb(0x84); } \
	{ u_char x = inb(0x84); } \
	{ u_char x = inb(0x84); } \
	{ u_char x = inb(0x84); } \
	{ u_char x = inb(0x84); } \
	{ u_char x = inb(0x84); }

#define YYDEBUG 1

void yyerror(const char *msg);

static	void help(int), status(void), data(int), kbd(int), cmdbyte(int),
	kbc(int), whatMCA(void);
static	int kbget(void);
%}

%union {
	int num;
}

%token		NEWLINE
%token		ALL CMD DATA DEFAULTS ECHOC ENABLE EXPR HELP ID LED
%token		MAKE ONLY RELEASE RESEND RESET SCAN STATUS TYPEMATIC
%token		WHAT
%token	<num>	NUM

%type	<num>	expr opr

%%

interpret:	lines ;

lines:		line
		| lines line
		;

line:		statements NEWLINE
		| NEWLINE
		| error	NEWLINE		{ fprintf(stderr, "bing!\n"); }
		;

statements:	statement
		| statements ';' statement
		;

statement:	'?'			{ help(0); }
		| HELP			{ help(0); }
		| HELP EXPR		{ help(1); }
		| STATUS '?'		{ status(); }
		| WHAT '?'		{ whatMCA(); }
		| DATA '?'		{ data(kbget()); }
		| LED '=' NUM		{ kbd(0xed); kbd($3); }
		| ECHOC			{ kbd(0xee); kbget(); }
		| SCAN '=' NUM		{ kbd(0xf0); kbd($3);
					  if($3 == 0) data(kbget()); }
		| SCAN '?'		{ kbd(0xf0); kbd(0); data(kbget()); }
		| ID '?'		{ kbd(0xf2); data(kbget());
					  data(kbget()); }
		| TYPEMATIC '=' NUM ',' NUM
					{ kbd(0xf3);
					  if($3 > 1000) $3 = 1000;
					  if($5 > 30) $5 = 30;
					  if($5 < 2) $5 = 2;
					  kbd(
					      (int)
						(8.0 * log(30.0 / (double)$5)
						 / log(2))
					      | ((($3 / 250) - 1) * 32)
					     );
					}
		| ENABLE		{ kbd(0xf4); }
		| DEFAULTS		{ kbd(0xf6); }
		| ALL TYPEMATIC		{ kbd(0xf7); }
		| ALL MAKE RELEASE	{ kbd(0xf8); }
		| ALL MAKE ONLY		{ kbd(0xf9); }
		| ALL TYPEMATIC MAKE RELEASE
					{ kbd(0xfa); }
		| NUM TYPEMATIC		{ kbd(0xfb); kbd($1); }
		| NUM MAKE RELEASE	{ kbd(0xfc); kbd($1); }
		| NUM MAKE ONLY		{ kbd(0xfd); kbd($1); }
		| RESEND		{ kbd(0xfe); }
		| RESET			{ kbd(0xff); }
		| CMD '?'		{ kbc(0x20); cmdbyte(kbget()); }
		| CMD '=' expr		{ kbc(0x60); kbd($3); }
		| /* lambda */
		;

expr:		opr			{ $$ = $1; }
		| expr '+' opr		{ $$ = $1 | $3; }
		| expr '-' opr		{ $$ = $1 & ~($3); }
		;

opr:		NUM			{ $$ = $1; }
		| CMD			{ kbc(0x20); $$ = kbget(); }
		;

%%

static void
help(int topic) {
	switch(topic) {
	case 0:
	printf(
	"Input consists of lines, containing one or more semicolon-separated\n"
	"statements. Numbers are implicitly hexadecimal, append a dot for\n"
	"decimal numbers. Valid statements include:\n"
	"help [expr];		give help [to expression syntax]\n"
	"status ?		interpret kbd ctrl status byte\n"
	"what ?			check for MCA type 1 or 2 motherboard controller\n"
	"data ?			get one byte of data\n"
	"led = NUM		set kbd LEDs\n"
	"echo = NUM		echo byte to kbd\n"
	"scan = NUM; scan ?	set scan code set; return current set\n"
	"id ?			get two id bytes\n"
	"typematic=delay,rate	set typematic delay(ms)&rate(1/s)\n"
	"enable; defaults	enable kbd; back to defaults\n"
	"all typematic		make all keys typematic\n"
	"all make release	make all keys sending make/release\n"
	"all make only		make all keys sending make only\n"
	"all typematic make release	make all keys typematic & make/release\n"
	"NUM typematic		make specific key typematic\n"
	"NUM make release	make specific key sending make/release\n"
	"NUM make only		make specific key sending make only\n"
	"resend; reset		resend last byte from kbd; reset kbd\n"
	"cmd ?			fetch kbd ctrl command byte\n"
	"cmd = expr		set kbd ctrl command byte\n"
	"\n");
	break;

	case 1:
	printf(
	"Expressions can either consist of a number, possibly followed\n"
	"by a + or - sign and bit values in numeric or symbolic form.\n"
	"Symbolic bit values are:\n"
	"SCCONV IGNPAR CLKLOW OVRINH TEST IRQ\n"
	"\n");
	break;
	}
}

static void
status(void) {
	int c = inb(0x64);
	if(c&0x80) printf("parity error | ");
	if(c&0x40) printf("rx timeout | ");
	if(c&0x20) printf("tx timeout | ");
	if(c&0x10) printf("kbd released ");
	else       printf("kbd locked   ");
	if(c&0x08) printf("| cmd last sent  ");
	else       printf("| data last sent ");
	if(c&0x04) printf("| power-on ");
	else       printf("| test ok  ");
	if(c&0x02) printf("| ctrl write busy ");
	else       printf("| ctrl write ok   ");
	if(c&0x01) printf("| ctrl read ok\n");
	else       printf("| ctrl read empty\n");
}

/* see: Frank van Gilluwe, "The Undocumented PC", Addison Wesley 1994, pp 273 */

static void
whatMCA(void) {
	int new, sav;
	kbc(0x20);		/* get command byte */
	sav = kbget();		/* sav = command byte */
	kbc(0x60);		/* set command byte */
	kbd(sav | 0x40);	/* set keyboard xlate bit */
	kbc(0x20);		/* get keyboard command */
	new = kbget();		/* new = command byte */
	kbc(0x60);		/* set command byte */
	kbd(sav);		/* restore command byte */
	if(new & 0xbf)
		printf("Hmm - looks like MCA type 1 motherboard controller\n");
	else
		printf("Hmm - looks like MCA type 2 motherboard controller\n");
}

static void
kbd(int d) {
	int i = 100000;
	while(i && (inb(0x64) & 2)) i--;
	if(i == 0) { printf("kbd write: timed out\n"); return; }
	outb(0x60, d);
}

static void
kbc(int d) {
	int i = 100000;
	while(i && (inb(0x64) & 2)) i--;
	if(i == 0) { printf("ctrl write: timed out\n"); return; }
	outb(0x64, d);
}

static int
kbget(void) {
	int i, c;
	for(;;) {
		i = 10000;
		while(i && (inb(0x64) & 1) == 0) i--;
		if(i == 0) { printf("data read: timed out\n"); return -1; }
		KBD_DELAY
		c = (unsigned char)inb(0x60);
		switch(c) {
			case 0: case 0xff:
				printf("got kbd overrun\n"); break;
			case 0xaa:
				printf("got self-test OK\n"); break;
			case 0xee:
				printf("got ECHO byte\n"); break;
			case 0xfa:
				printf("got ACK\n"); break;
			case 0xfc:
				printf("got self-test FAIL\n"); break;
			case 0xfd:
				printf("got internal failure\n"); break;
			case 0xfe:
				printf("got RESEND request\n"); break;
			default:
				goto done;
		}
	}
done:
	return c;
}

static void
cmdbyte(int d) {
	if(d&0x40) printf("scan conv ");
	else       printf("pass thru ");
	if(d&0x20) printf("| ign parity   ");
	else       printf("| check parity ");
	if(d&0x10) printf("| kbd clk low ");
	else       printf("| enable kbd  ");
	if(d&0x08) printf("| override kbd inh ");
	if(d&0x04) printf("| test ok  ");
	else       printf("| power-on ");
	if(d&0x01) printf("| irq 1 enable\n");
	else       printf("| no irq\n");
}

static void
data(int d) {
	if(d < 0) return;
	printf("data: 0x%02x\n", d);
}

void yyerror(const char *msg) {
	fprintf(stderr, "yyerror: %s\n", msg);
}

int main(int argc, char **argv) {
	int fd;

	if(argc > 1) yydebug = 1;

	if((fd = open("/dev/console", O_RDONLY)) < 0)
		fd = 0;

	if(ioctl(fd, KDENABIO, 0) < 0) {
		perror("ioctl(KDENABIO)");
		return 1;
	}
	yyparse();

	(void)ioctl(fd, KDDISABIO, 0);
	return 0;
}

