/*
 *  irkb101 -- IrKB101
 *
 *  revision history
 *	0.0: Feb. 10, 2003 by Dai ISHIJIMA
 */

#include <stdio.h>
#include <time.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include <errno.h>
#include "tty.h"
#include "ps2key.h"
#include "irkb101.h"

#define MAXRETRY 3
#define INIT_TIMEOUT 5
#define INIT_TIMEOUT_RESP 2

extern char *prog;

/* IrKB101の初期化 */
int init_irkb101(int fd)
{
    static int init0[] = {
	0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
	0xc0, 0xaa, 0x3f, 0x03, 0x20, 0x5a, 0x01, 0xa2,
	0x00, 0x16, 0x3e, 0x16, 0x14, 0x16, 0x00, 0xc2,
	0x06, 0x1d,
	-1
    };
    static int init1[] = {
	0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
	0xaa, 0x03, 0xf0, 0x02, 0xf1, 0x5a,
	-1
    };
    static int init2[] = {
	0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
	0xaa, 0x22, 0x00, 0x22,
	-1
    };
    static int resp0[] = {0xff, 0x55, 0x08, 0x3f, 0x37, -1};
    static int resp1[] = {0xff, 0x55, 0x08, 0x03, 0x0b, -1};
    static int resp2[] = {0xff, 0x55, 0x08, 0x22, 0x2a, -1};
    static int *init[] = { init0, init1, init2 };
    static int *resp[] = { resp0, resp1, resp2 };
    time_t t1, t2;
    int i, j;
    int retrycount;
    int ch;

    while (ttygetc(fd) != EOF) {
	; /* ごみキャラクタ読み飛ばし */
    }
    retrycount = 0;
    t1 = time(NULL);
    for (i = 0; i < sizeof(init) / sizeof(int *); i++) {
	if (retrycount > MAXRETRY) {
	    return(-1);
	}
	if ((t2 = time(NULL)) - t1 > INIT_TIMEOUT) {
	    return(-2);
	}
	/* 初期化データ列出力 */
#ifdef EBUG
	fprintf(stderr, "sending init string #%d\n", i);
#endif
	for (j = 0; init[i][j] >= 0; j++) {
	    ttyputc(fd, init[i][j]);
	}
	/* 反応を調べる */
	j = 0;
	t2 = time(NULL);
	while (resp[i][j] >= 0) {
	    if (time(NULL) - t2 > INIT_TIMEOUT_RESP) {
		--i;
		break;
	    }
	    ch = ttygetc(fd);
	    if (ch != EOF) {
		if (ch == resp[i][j]) {
		    ++j;
		}
		else if (ch == resp[i][0]) {
		    j = 1;
		}
		else {
#ifdef EBUG
		    fprintf(stderr, "response %02x should be %02x\n",
			    ch, resp[i][j]);
#endif
		    j = 0;
		}
	    }
	}
    }
    return(0);
}

#define MARKER 0xff
#define neg(ch) (unsigned char)(0xff - (unsigned char)(ch))


/* IrKB101の送信データをキーコードに変換 */
int irkb101conv(int n, char *buf)
{
    static unsigned char keybuf[BUFSIZ];
    static int p = 0;
    int i, j;
    int pos, nn;

    /* 入力データをキューに追加 */
    j = p;
    for (i = 0; (j < BUFSIZ) && (i < n); i++, j++) {
	keybuf[j] = buf[i];
    }
    if (j >= BUFSIZ) {
	fprintf(stderr, "%s: irkb101conv: buffer overflow\n", prog);
    }
    n = 0;
    i = 0;
    while (i < j) {
	/* マーカー (0xff) を飛ばす */
	while ((i < j) && (keybuf[i] == MARKER)) {
	    ++i;
	}
	pos = i;
	nn = n;
	while ((i + 1 < j) && (keybuf[i + 1] == neg(keybuf[i]))) {
	    buf[n] = keybuf[i];
	    ++n;
	    i += 2;
	}
	if (i == j - 1) {	/* 現状のデータではキーコード再現不可 */
	    n = nn;
	    i = pos;
	    break;
	}
    }
    /* 処理できなかったデータをキューの先頭に移動 */
    for (p = 0; i < j; p++, i++) {
	keybuf[p] = keybuf[i];
    }
    return(n);
}


#ifdef MAIN
char *prog = "irkb101";

/* シリアル入出力のメイン部分 */
void keyrecv(int fd)
{
    int nfds;
    fd_set readfds;
    struct timeval timeout;
    unsigned char buf[BUFSIZ];
    int n;
    int i;

    for (;;) {
	FD_ZERO(&readfds);
	FD_SET(fd, &readfds);
	timeout.tv_sec = 0;
	timeout.tv_usec = 500000;
	nfds = select(fd + 1, &readfds, NULL, NULL, &timeout);
	if ((nfds < 0) && (errno != EINTR)) {
	    fprintf(stderr, "%s: select failed\n", prog);
	    break;
	}
	if ((nfds > 0) && (FD_ISSET(fd, &readfds))) {
	    /* シリアルポート入力あり */
	    n = read(fd, buf, BUFSIZ);
	    if (n <= 0) {
		break;
	    }
	    else {
		for (i = 0; i < n; i++) {
		    printf("=%02x ", buf[i]);
		}
		n = irkb101conv(n, buf);
		for (i = 0; i < n; i++) {
		    printf("#%02x ", buf[i]);
		}
		n = ps2conv(n, buf);
		for (i = 0; i < n; i++) {
		    if ((' ' <= buf[i]) && (buf[i] <= '~')) {
			printf(">%02x '%c'", buf[i], buf[i]);
		    }
		    else {
			printf(">%02x '\\%03o'", buf[i], buf[i]);
		    }
		}
		printf("\n");
	    }
	}
	fflush(stdout);
    }
}


int main()
{
    int fd;
    int status;
#ifdef LINUX
    fd = opentty("/dev/ttyS1");
#else
    fd = opentty("/dev/cuaa1");
#endif
    ttyinit(fd, 9600);
    status = init_irkb101(fd);
    if (status == 0) {
	printf("Ok\n");
	keyrecv(fd);
    }
    else {
	printf("Failed (%d)\n", status);
    }
    closetty(fd);
    exit(0);
}
#endif


/* Local Variables: */
/* compile-command:"cc -Wall -DMAIN -o irkb101 irkb101.c tty.o" */
/* End: */
