/*
 *  ek3 -- serial external keyboard :-) for SL-Zaurus
 *
 *  revision history
 *	0.0: Feb. 14, 2004 by Dai ISHIJIMA
 *	0.1: Feb. 15, 2004 (improve ctrl- [ \ ] ^ _ )
 *	0.2: Mar. 20, 2004 (as ek4, support PockeTop keyboard) 
 *
 *  derived from ek2 (Feb. 17, 2003), pk (Feb. 14, 2004)
 *
 *  usage: ek3 [options] [serial device]
 *
 *  example: ek3 -i
 *
 */

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "tty.h"
#include "codeconv.h"
#include "irkb101.h"
#include "ps2key.h"
#include "pocketop.h"
#include "keychars.h"

#define KBDSIM "/dev/kbdsim"
#define EXTKEY "/dev/ttyS1"

#define DEFAULT_SPEED 9600
#define RAW_KEYBOARD 'r'
#define SJIS_RAW_KEYBOARD 's'
#define IRKB101_PS2KEYBOARD 'i'
#define HHC 'h'
#define HHC_BPS 4800
#define POCKETOP 'p'

#define YES 1
#define NO  0

#define KEY_PRESS   1
#define KEY_RELEASE 0

char *prog;
#define shift --argc; ++argv


/*
 * keycode definitions
 *	see also "kernel/linux24/drivers/char/corgi_keymap.map"
 *	but something wrong...
 */

/* ALPHABETS (1..26) */
enum {
    KEY_A = 1,	KEY_B,	KEY_C,	KEY_D,	/*  1.. 4 */
    KEY_E,	KEY_F,	KEY_G,	KEY_H,	/*  5.. 8 */
    KEY_I,	KEY_J,	KEY_K,	KEY_L,	/*  9..12 */
    KEY_M,	KEY_N,	KEY_O,	KEY_P,	/* 13..16 */
    KEY_Q,	KEY_R,	KEY_S,	KEY_T,	/* 17..20 */
    KEY_U,	KEY_V,	KEY_W,	KEY_X,	/* 21..24 */
    KEY_Y,	KEY_Z};			/* 25, 26 */

/* MODIFIERS, FUNCTIONS */
enum {
    KEY_SHIFT = 27,	KEY_RET,	KEY_MENU,	KEY_ALT,   /* 27- */
    KEY_BS,		KEY_KIGOU,	KEY_LCDOFF,	KEY_CANCEL,/* 31- */
    KEY_LEFT,		KEY_UP,		KEY_DOWN,	KEY_RIGHT, /* 35- */
/*  ESC [ D		ESC [ A		ESC [ B		ESC [ C		  */
    KEY_OK,		KEY_HOME};				   /* 39- */


/* NUMBERS */
enum {
    KEY_ONE = 41,	KEY_TWO,	KEY_THREE,	KEY_FOUR,  /* 41- */
    KEY_FIVE,		KEY_SIX,	KEY_SEVEN,	KEY_EIGHT, /* 45- */
    KEY_NINE, KEY_ZERO};					   /* 49- */

/* SYMBOLS */
enum {
    KEY_USCORE = 51,	KEY_DOT,	KEY_EQUAL,	KEY_YEN,   /* 51- */
    KEY_SEMI,		KEY_COLON,	KEY_FN_I};		   /* 55- */
					/* 」*/

/* SYMBOLS */
enum {
    KEY_MINUS = 58,	KEY_ASTER,	KEY_LOCK,	KEY_UNDEF, /* 58- */
    KEY_BAR,		KEY_COMMA,	KEY_PERIOD,	KEY_TAB,   /* 62- */
    KEY_X_2,		KEY_C_2,	KEY_V_2,	KEY_PLUS,  /* 66- */
    KEY_LBRACKET,	KEY_LARGE,	KEY_CIRCUM,	KEY_RBRACKET,/* 70- */
    KEY_SMALL,		KEY_FN_U,	KEY_DARK,	KEY_BRITE, /* 74- */
			/* 「 */    
    KEY_UNDEF6,		KEY_UNDEF7,	KEY_UNDEF8,	KEY_UNDEF9,/* 78- */
    KEY_Z_2,		KEY_UNDEFA,	KEY_UNDEFB,	KEY_AT,    /* 82- */
    KEY_LESS,		KEY_GRATER,	KEY_F9,		KEY_SLASH, /* 86- */
                                      /* ESC [ 20 ~ */
    KEY_MAIL,		KEY_SPACE,	KEY_SPACE_2,	KEY_UNDEFC,/* 90- */
    KEY_UNDEFD,		KEY_KANJI};				   /* 94- */

/* FUNCTIONS */
enum {
    KEY_MUSIC = 96,	KEY_MUSIC2,	KEY_MUSIC3,	KEY_MUSIC4,/* 96- */
    KEY_MUSIC5,		KEY_MUSIC6,	KEY_UNDEFE};		   /* 100- */

/* MODIFIERS, CONTROLS */
enum {
    KEY_SHIFT_2 = 103,	KEY_CTRL,	KEY_CTRL_HAT,	KEY_MENUX,  /* 103- */
    KEY_CTRL_YEN,	KEY_LBRACE,	KEY_OFF,	KEY_MAIL2,  /* 107- */
    KEY_RBRACE,		KEY_KIGOU2,	KEY_KRIGHT,	KEY_KLEFT,  /* 111- */
    KEY_PRTSC,		KEY_CTRL_US,	KEY_SYNC,	KEY_YEN2,   /* 115- */
    KEY_UNDEFI,		KEY_LARGE2,	KEY_KANAHIRA,	KEY_KANJI2, /* 119- */
    KEY_OK2,	   	KEY_CANCEL2,	KEY_KUP,	KEY_KDOWN,  /* 123- */
    KEY_UNDEFK};						    /* 127- */


#define CTRL(ch) ((unsigned char)ch & 0x1f)


/* ASCII -> KEYCODE */
int char2key(int ch, unsigned char *keybuf)
{
    if (ch == 0) {
	keybuf[0] = KEY_CTRL;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_SPACE;	keybuf[3] = KEY_PRESS;
	keybuf[4] = KEY_SPACE;	keybuf[5] = KEY_RELEASE;
	keybuf[6] = KEY_CTRL;	keybuf[7] = KEY_RELEASE;
	return(4);
    }
    else if (ch == '\010') {	/* BS */
	keybuf[0] = KEY_BS;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_BS;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '\r') {
	keybuf[0] = KEY_RET;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_RET;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch <= CTRL('z')) {
	keybuf[0] = KEY_CTRL;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_A + ch - CTRL('a');
	keybuf[3] = KEY_PRESS;
	keybuf[4] = keybuf[2];	keybuf[5] = KEY_RELEASE;
	keybuf[6] = KEY_CTRL;	keybuf[7] = KEY_RELEASE;
	return(4);
    }
    else if (ch == CTRL('[')) {
	keybuf[0] = KEY_CANCEL;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_CANCEL;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == CTRL('\\')) {
	keybuf[0] = KEY_CTRL_YEN;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_CTRL_YEN;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == CTRL(']')) {	/* mapping Fn+S with keyhelper */
	keybuf[0] = KEY_ALT;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_S;	keybuf[3] = KEY_PRESS;
	keybuf[4] = KEY_S;	keybuf[5] = KEY_RELEASE;
	keybuf[6] = KEY_ALT;		keybuf[7] = KEY_RELEASE;
	return(4);
    }
    else if (ch == CTRL('^')) {
	keybuf[0] = KEY_CTRL_HAT;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_CTRL_HAT;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == CTRL('_')) {
	keybuf[0] = KEY_CTRL_US;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_CTRL_US;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == ' ') {
	keybuf[0] = KEY_SPACE;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_SPACE;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (('!' <= ch) && (ch <= ')')) {
	keybuf[0] = KEY_SHIFT;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_ONE + ch - '!';
	keybuf[3] = KEY_PRESS;
	keybuf[4] = keybuf[2];	keybuf[5] = KEY_RELEASE;
	keybuf[6] = KEY_SHIFT;	keybuf[7] = KEY_RELEASE;
	return(4);
    }
    else if (ch == '*') {
	keybuf[0] = KEY_ASTER;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_ASTER;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '+') {
	keybuf[0] = KEY_PLUS;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_PLUS;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == ',') {
	keybuf[0] = KEY_COMMA;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_COMMA;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '-') {
	keybuf[0] = KEY_MINUS;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_MINUS;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '.') {
	keybuf[0] = KEY_PERIOD;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_PERIOD;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '/') {
	keybuf[0] = KEY_SLASH;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_SLASH;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (('1' <= ch) && (ch <= '9')) {
	keybuf[0] = KEY_ONE + ch - '1';
	keybuf[1] = KEY_PRESS;
	keybuf[2] = keybuf[0];	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '0') {
	keybuf[0] = KEY_ZERO;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_ZERO;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == ':') {
	keybuf[0] = KEY_COLON;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_COLON;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == ';') {
	keybuf[0] = KEY_SEMI;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_SEMI;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '<') {
	keybuf[0] = KEY_LESS;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_LESS;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '=') {
	keybuf[0] = KEY_EQUAL;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_EQUAL;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '>') {
	keybuf[0] = KEY_GRATER;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_GRATER;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '?') {
	keybuf[0] = KEY_SHIFT;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_PERIOD;	keybuf[3] = KEY_PRESS;
	keybuf[4] = KEY_PERIOD;	keybuf[5] = KEY_RELEASE;
	keybuf[6] = KEY_SHIFT;	keybuf[7] = KEY_RELEASE;
	return(4);
    }
    else if (ch == '@') {
	keybuf[0] = KEY_AT;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_AT;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (('A' <= ch) && (ch <= 'Z')) {
	keybuf[0] = KEY_SHIFT;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_A + ch - 'A';
	keybuf[3] = KEY_PRESS;
	keybuf[4] = keybuf[2];	keybuf[5] = KEY_RELEASE;
	keybuf[6] = KEY_SHIFT;	keybuf[7] = KEY_RELEASE;
	return(4);
    }
    else if (ch == '[') {
	keybuf[0] = KEY_LBRACKET;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_LBRACKET;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '\134') {
	keybuf[0] = KEY_YEN;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_YEN;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == ']') {
	keybuf[0] = KEY_RBRACKET;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_RBRACKET;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '^') {
	keybuf[0] = KEY_CIRCUM;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_CIRCUM;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '_') {
	keybuf[0] = KEY_USCORE;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_USCORE;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '`') {
	keybuf[0] = KEY_ALT;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_SHIFT;	keybuf[3] = KEY_PRESS;
	keybuf[4] = KEY_MINUS;	keybuf[5] = KEY_PRESS;
	keybuf[6] = KEY_MINUS;	keybuf[7] = KEY_RELEASE;
	keybuf[8] = KEY_SHIFT;	keybuf[9] = KEY_RELEASE;
	keybuf[10] = KEY_ALT;	keybuf[11] = KEY_RELEASE;
	return(6);
    }
    else if (('a' <= ch) && (ch <= 'z')) {
	keybuf[0] = KEY_A + ch - 'a';
	keybuf[1] = KEY_PRESS;
	keybuf[2] = keybuf[0];	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '{') {
	keybuf[0] = KEY_LBRACE;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_LBRACE;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '|') {
	keybuf[0] = KEY_BAR;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_BAR;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '}') {
	keybuf[0] = KEY_RBRACE;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_RBRACE;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == '~') {
	keybuf[0] = KEY_SHIFT;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_ZERO;	keybuf[3] = KEY_PRESS;
	keybuf[4] = KEY_ZERO;	keybuf[5] = KEY_RELEASE;
	keybuf[6] = KEY_SHIFT;	keybuf[7] = KEY_RELEASE;
	return(4);
    }
    else if (ch == '\177') {
	keybuf[0] = KEY_ALT;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_BS;	keybuf[3] = KEY_PRESS;
	keybuf[4] = KEY_BS;	keybuf[5] = KEY_RELEASE;
	keybuf[6] = KEY_ALT;	keybuf[7] = KEY_RELEASE;
	return(4);
    }
    /* special functions */
    else if (ch == CHAR_LEFT) {
	keybuf[0] = KEY_KLEFT;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_KLEFT;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == CHAR_RIGHT) {
	keybuf[0] = KEY_KRIGHT;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_KRIGHT;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == CHAR_UP) {
	keybuf[0] = KEY_KUP;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_KUP;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == CHAR_DOWN) {
	keybuf[0] = KEY_KDOWN;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_KDOWN;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == CHAR_HOME) {
	keybuf[0] = KEY_HOME;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_HOME;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == CHAR_KANJI) {
	keybuf[0] = KEY_KANJI;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_KANJI;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == CHAR_COPY) {
	keybuf[0] = KEY_PRTSC;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_PRTSC;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    else if (ch == CHAR_MENU) {
	keybuf[0] = KEY_MENU;	keybuf[1] = KEY_PRESS;
	keybuf[2] = KEY_MENU;	keybuf[3] = KEY_RELEASE;
	return(2);
    }
    return(0);
}


void sendkey(int fd, unsigned char *s, int verbose)
{
    if (verbose) {
	fprintf(stderr, "%s: code=%d(%x), ", prog, s[0], s[0]);
	if (s[1] == KEY_PRESS) {
	    fprintf(stderr, "PRESS\n");
	}
	else if (s[1] == KEY_RELEASE) {
	    fprintf(stderr, "RELEASE\n");
	}
	else {
	    fprintf(stderr, "UNKNOWN(%d)\n", s[1]);
	}
    }
    write(fd, s, 2);
}


void pseudo_input(int fd, int ch, int verbose)
{
    unsigned char keybuf[BUFSIZ];
    int n;
    int i;

    n = char2key(ch, keybuf);
    for (i = 0; i < n; i++) {
	sendkey(fd, &keybuf[2 * i], verbose);
    }
}


/* ASCII文字列をキーコードに変換して kbdsim に渡す */
void ascii_write(int kbdsim, unsigned char *buf, int n, int verbose)
{
    int i;

    for (i = 0; i < n; i++) {
	pseudo_input(kbdsim, buf[i], verbose);
    }
}

void dumpbuf(int n, char *buf)
{
    int i;

    fprintf(stderr, "%s: DUMP", prog);
    for (i = 0; i < n; i++) {
	fprintf(stderr, " %02x", buf[i]);
    }
    fprintf(stderr, "\n");
}

		 
/* 外部キーボードを使う */
void keyboard(int extkey, int kbdsim, int mode, int verbose)
{
    int nfds;
    fd_set readfds;
    struct timeval timeout;
    int n;	/* 読み込んだ文字の数 */
    char buf[BUFSIZ];

    while (ttygetc(extkey) != EOF) {
	; /* 起動時のごみキャラクタを読み飛ばす */
    }
    for (;;) {
	FD_ZERO(&readfds);
	FD_SET(extkey, &readfds); /* 外部キーボードを調べる */
	timeout.tv_sec = 0;
	timeout.tv_usec = 500000;
	nfds = select(extkey + 1, &readfds, NULL, NULL, &timeout);
	if ((nfds < 0) && (errno != EINTR)) {
	    fprintf(stderr, ">>>if ((nfds < 0) && (errno != EINTR)) {\n");
	    break;
	}
	if ((nfds > 0) && (FD_ISSET(extkey, &readfds))) {
	    /* 外部キーボードからのデータをキーボード入力へ */
	    n = read(extkey, buf, BUFSIZ);
	    if (n > 0) {
		if (mode == IRKB101_PS2KEYBOARD) {
		    n = irkb101conv(n, buf);
#ifdef EBUG
		    dumpbuf(n, buf);
#endif		    
		    n = ps2conv(n, buf);
		}
		else if (mode == HHC) {
		    n = ps2conv(n, buf);
		}
		else if (mode == POCKETOP) {
		    n = pocketopconv(n, buf);
		}
		ascii_write(kbdsim, buf, n, verbose);
	    }
	}
    }
}


void usage()
{
    fprintf(stderr, "Usage: %s [option]... [device]\n", prog);
    fputs("    options:\n", stderr);
    fputs("\t-raw: RAW keyboard (no conversion) mode\n", stderr);
    fputs("\t-irkb101: IrKB101+PS/2 mode\n", stderr);
    fputs("\t-IrKB101: IrKB101+PS/2 mode without initialize\n", stderr);
    fputs("\t-cradle: Happy Hacking Cradle+PS/2 mode\n", stderr);
    fputs("\t-pocketop: PockeTop keyboard\n", stderr);
    fputs("\t-#: set serial speed [bps]\n", stderr);
    fputs("    default:\n", stderr);
    fprintf(stderr, "\t%s -%d -raw %s\n", prog, DEFAULT_SPEED, EXTKEY);
}


int main(int argc, char *argv[])
{
    int mode;
    char *namkbdsim;
    char *namextkey;
    int kbdsim, extkey;
    int speed;
    int n;
    int init101;
    int verbose;

    prog = *argv;
    shift;
    speed = DEFAULT_SPEED;
    mode = RAW_KEYBOARD;
    namkbdsim = KBDSIM;
    namextkey = EXTKEY;
    init101 = YES;
    verbose = NO;
    while ((argc > 0) && (argv[0][0] == '-')) {
	if (argv[0][1] == 'r') {
	    mode = RAW_KEYBOARD;
	}
	else if (argv[0][1] == 'i') {
	    mode = IRKB101_PS2KEYBOARD;
	}
	else if (argv[0][1] == 'I') {
	    mode = IRKB101_PS2KEYBOARD;
	    init101 = NO;
	}
	else if (argv[0][1] == 'c') {
	    mode = HHC;
	    speed = HHC_BPS;
	}
	else if (argv[0][1] == 'p') {
	    mode = POCKETOP;
	}
	else if (('0' <= argv[0][1]) && (argv[0][1] <= '9')) {
	    speed = -atoi(*argv);
	}
	else if ((argc >= 2) && (argv[0][1] == 'd')) {
	    shift;
	    namkbdsim = *argv;
	}
	else if (argv[0][1] == 'v') {
	    verbose = !verbose;
	}
	else {
	    usage();
	    exit(1);
	}
	shift;
    }
    if (argc > 0) {
	namextkey = *argv;
    }
    if ((kbdsim = open(namkbdsim, O_WRONLY)) <= 0) {
	fprintf(stderr, "%s: can't open KBDSIM device %s\n", prog, namkbdsim);
	exit(1);
    }
    extkey = opentty(namextkey);
    ttyinit(extkey, speed);
    if ((mode == IRKB101_PS2KEYBOARD) && (init101 == YES)) {
	if ((n = init_irkb101(extkey)) != 0) {
	    fprintf(stderr,
		    "%s: can't initialize IrKB101+PS/2 keyboard (%d)\n",
		    prog, n);
	    closetty(extkey);
	    exit(1);
	}
	fprintf(stderr, "%s: IrKB101+PS/2 initialized\n", prog);
    }
    keyboard(extkey, kbdsim, mode, verbose);
    closetty(extkey);
    close(kbdsim);
    exit(0);
}

/* Local Variables: */
/* compile-command:
   "gcc -g -Wall -o ek4 ek4.c tty.c codeconv.c irkb101.c ps2key.c" */
/* End: */
