/* * ek -- serial external keyboard :-) for FreeBSD/Linux * * revision history * 0.0: Feb. 5, 2003 by Dai ISHIJIMA * 0.1: Feb. 6, 2003 (code conversion) * 0.2: Feb. 11, 2003 (IrKB101 support) * 0.3: Feb. 17, 2003 (HHC support) * * usage: ek [serial device] * * example: ek /dev/ttyS1 * * see also: source of ``script'' command */ #ifdef SLZAURUS #ifndef LINUX #define LINUX #endif #endif #ifdef LINUX #define NEED_PTY_H #define NEED_UTMP_H #define NEED_WAIT_H #else #define NEED_LIBUTIL_H #endif #include #include #include #include #include /* bzero() <- FDZERO() */ #include #include #include #ifdef NEED_LIBUTIL_H #include /* openpty() */ /* -lutil */ #endif #ifdef NEED_PTY_H #include /* openpty() */ /* -lutil */ #endif #ifdef NEED_UTMP_H #include /* login_tty() */ /* -lutil */ #endif #ifdef NEED_WAIT_H #include /* wait3(), WNOHANG */ #endif #include #include #include #include "tty.h" #include "codeconv.h" #include "irkb101.h" #include "ps2key.h" #ifdef LINUX #define DEFAULT_SHELL "/bin/sh" #ifdef SLZAURUS #define DEFAULT_DEVICE "/dev/ttyS1" /* raw IrDA */ #else #define DEFAULT_DEVICE "/dev/ttyS0" /* COM1: */ #endif #else #define DEFAULT_SHELL "/bin/csh" #define DEFAULT_DEVICE "/dev/cuaa1" /* IrDA, COM2: */ #endif #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 EK_ENVIRON "EXTERNAL_KEYBOARD" #define EK_VALUE "SERIAL" #define SHELL_ENV "SHELL" #define ENV_OVERWRITE 1 /* 子プロセスの状態その他 */ #define RUNNING 0x01 #define ORG_KBD_EOF 0xc0 #define EXT_KBD_EOF 0xa0 #define CHILD_EOF 0x90 #define CHILD_EXIT 0x08 #define CHILD_CHANGED 0x04 #define YES 1 #define NO 0 char *prog; #define shift --argc; ++argv static int childstatus = RUNNING; static pid_t child; /* * 仮想端末関連 */ /* 子プロセスとしてシェルを起動する */ void subshell(int master, int slave) { char *shell; /* 起動するシェルを決める */ shell = getenv(SHELL_ENV); if (shell == NULL) { shell = DEFAULT_SHELL; } close(master); login_tty(slave); execl(shell, "sh", "-i", NULL); /* シェルを「対話モード」で起動 */ /* ここから下は実行されないはず… */ fprintf(stderr, "%s: can't start subshell (%s)\r\n", prog, shell); kill(0, SIGTERM); exit(1); } #ifdef LINUX /* SIGCHILD で飛んでくる。exitした場合、電源OFF→ON */ void chk_child(int dummy) { int status; pid_t pid; while ((pid = wait3(&status, WNOHANG, NULL)) > 0) { if (pid == child) { /* 多分、exit した */ childstatus = CHILD_EXIT; } } /* 多分、電源OFF→ON */ childstatus |= CHILD_CHANGED; } #endif /* ウィンドウサイズ変更, 縦横切り替え...(?) */ void winch(int dummy) { kill(child, SIGWINCH); } /* バイトの分解と合成 */ #define lobyte(x) (((unsigned short)(x)) & 0xff) #define hibyte(x) ((((unsigned short)(x)) >> 8) & 0xff) #define hilo(h,l) ((((unsigned char)(h)) << 8) | ((unsigned char)(l))) int sjisconv(int n, char *buf) { static unsigned char mybuf[BUFSIZ]; static int p = 0; int i, j; int code; j = p; for (i = 0; (j < BUFSIZ) && (i < n); i++, j++) { mybuf[j] = buf[i]; } n = 0; for (i = 0; (i < j) && (n < BUFSIZ); i++, n++) { if (mybuf[i] < 0x80) { buf[n] = mybuf[i]; } else if (iskana(mybuf[i])) { code = jtoe(kanakana(mybuf[i])); buf[n] = hibyte(code); buf[n + 1] = lobyte(code); ++n; } else if ((i + 1 < j) && (n - 1 < BUFSIZ)) { code = jtoe(stoj(hilo(mybuf[i], mybuf[i + 1]))); ++i; buf[n] = hibyte(code); buf[n + 1] = lobyte(code); ++n; } else { break; } } for (p = 0; i < j; p++, i++) { mybuf[p] = mybuf[i]; } return(n); } /* 外部キーボードを使う */ int keyboard(int extkey, int master, int mode) { int nfds; fd_set readfds; struct timeval timeout; int n; /* 読み込んだ文字の数 */ char buf[BUFSIZ]; childstatus = RUNNING; while (ttygetc(extkey) != EOF) { ; /* 起動時のごみキャラクタを読み飛ばす */ } while (childstatus & RUNNING) { FD_ZERO(&readfds); FD_SET(master, &readfds); /* 子プロセスの出力を調べる */ FD_SET(fileno(stdin), &readfds); /* 標準入力 (本体キー) を調べる */ FD_SET(extkey, &readfds); /* 外部キーボードを調べる */ timeout.tv_sec = 0; timeout.tv_usec = 500000; nfds = select(master + 1, &readfds, NULL, NULL, &timeout); if ((nfds < 0) && (errno != EINTR)) { fprintf(stderr, ">>>if ((nfds < 0) && (errno != EINTR)) {\n"); break; } if ((nfds > 0) && (FD_ISSET(fileno(stdin), &readfds))) { /* 標準入力 (本体キーボード) からのデータを */ /* 子プロセスに書き込む */ n = read(fileno(stdin), buf, BUFSIZ); if (n <= 0) { childstatus = ORG_KBD_EOF; } if (n > 0) { (void)write(master, buf, n); } } if ((nfds > 0) && (FD_ISSET(extkey, &readfds))) { /* 外部キーボードからのデータを子プロセスに書き込む */ if (childstatus & CHILD_CHANGED) { while (ttygetc(extkey) != EOF) { ; /* 再起動時のごみキャラクタを読み飛ばす */ } childstatus &= ~CHILD_CHANGED; } else { n = read(extkey, buf, BUFSIZ); if (n <= 0) { childstatus = EXT_KBD_EOF; } if (n > 0) { if (mode == SJIS_RAW_KEYBOARD) { n = sjisconv(n, buf); } else if (mode == IRKB101_PS2KEYBOARD) { n = irkb101conv(n, buf); n = ps2conv(n, buf); } else if (mode == HHC) { n = ps2conv(n, buf); } (void)write(master, buf, n); } } } if (nfds > 0 && FD_ISSET(master, &readfds)) { /* 子プロセスからのデータを標準出力へ書き出す */ n = read(master, buf, BUFSIZ); if (n <= 0) { childstatus = CHILD_EOF; break; } write(fileno(stdout), buf, n); } } /* while */ if (childstatus == CHILD_EOF) { fprintf(stderr, "%s: EOF encountered, child process finished\r\n", prog); } else if (childstatus & CHILD_EXIT) { fprintf(stderr, "%s: caught SIGCHLD, child process finished\r\n", prog); } else if (childstatus == ORG_KBD_EOF) { fprintf(stderr, "%s: EOF encountered on keyboard\r\n", prog); } else if (childstatus == EXT_KBD_EOF) { fprintf(stderr, "%s: EOF encountered on external keyboard\r\n", prog); } else { fprintf(stderr, "%s: child process finished (may be)\r\n", prog); } return(childstatus); } /* 仮想端末を開く */ void pseudoterm(int extkey, int mode) { struct termios tsave; /* 端末の設定 保存版 */ struct termios t; /* 端末の設定 */ struct winsize w; /* 端末の画面サイズ */ int master, slave; int status; tcgetattr(fileno(stdin), &t); tcgetattr(fileno(stdin), &tsave); ioctl(fileno(stdin), TIOCGWINSZ, &w); if (openpty(&master, &slave, NULL, &t, &w) != 0) { fprintf(stderr, "%s: can't open pty\n", prog); exit(1); } cfmakeraw(&t); t.c_lflag &= ~ECHO; (void)tcsetattr(fileno(stdin), TCSAFLUSH, &t); child = fork(); if (child < 0) { /* fork失敗 */ fprintf(stderr, "%s: can't fork\n", prog); exit(1); } else if (child == 0) { /* 子プロセス */ subshell(master, slave); } else { /* 元々のプロセス */ #ifdef LINUX signal(SIGCHLD, chk_child); /* 子プロセスが死んだかチェック */ #endif signal(SIGWINCH, winch); /* ウィンドウサイズ変更 */ /* 外部キーボードを使う */ fprintf(stderr, "%s: external keyboard now enabled\r\n", prog); status = keyboard(extkey, master, mode); fprintf(stderr, "%s: external keyboard now disabled\r\n", prog); } (void)tcsetattr(fileno(stdin), TCSAFLUSH, &tsave); } 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-sjis: SJIS 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-#: set serial speed [bps]\n", stderr); fputs(" default:\n", stderr); fprintf(stderr, "\t%s -%d -sjis %s\n", prog, DEFAULT_SPEED, DEFAULT_DEVICE); } int main(int argc, char *argv[]) { int mode; char *keydev; char *keyenv; int fd; int speed; int n; int init101; prog = *argv; shift; speed = DEFAULT_SPEED; mode = SJIS_RAW_KEYBOARD; init101 = YES; while ((argc > 0) && (argv[0][0] == '-')) { if (argv[0][1] == 'r') { mode = RAW_KEYBOARD; } else if (argv[0][1] == 's') { mode = SJIS_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 (('0' <= argv[0][1]) && (argv[0][1] <= '9')) { speed = -atoi(*argv); } else { usage(); exit(1); } shift; } if (argc > 0) { keydev = *argv; } else { keydev = DEFAULT_DEVICE; } /* すでに外部キーボードが有効になっているか? */ keyenv = getenv(EK_ENVIRON); if (keyenv != NULL) { fprintf(stderr, "%s: may already running %s, ", prog, prog); fprintf(stderr, "environ $%s set, value = %s\r\n", EK_ENVIRON, keyenv); /* IrKB101の場合は初期化しない */ init101 = NO; } setenv(EK_ENVIRON, EK_VALUE, ENV_OVERWRITE); /* */ fd = opentty(keydev); ttyinit(fd, speed); if ((mode == IRKB101_PS2KEYBOARD) && (init101 == YES)) { if ((n = init_irkb101(fd)) != 0) { fprintf(stderr, "%s: can't initialize IrKB101+PS/2 keyboard (%d)\n", prog, n); closetty(fd); exit(1); } fprintf(stderr, "%s: IrKB101+PS/2 initialized\n", prog); } pseudoterm(fd, mode); closetty(fd); exit(0); } /* Local Variables: */ /* compile-command:"gcc -g -Wall -o ek2 ek2.c tty.c codeconv.c irkb101.c ps2key.c -lutil" */ /* End: */