/* $Id$ */ /* * Copyright (c) 2009 Dimitri Sokolyuk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #define debug 0 struct termios tio; char *device = "/dev/ttyS0"; char to_kbd[] = "#TO_KBD"; char to__pc[] = "#TO__PC"; int minlen = 10; /* minimal response length (if any) */ /* Ctrl-Alt-F1 */ char caf[] = {0x14, 0x11, 0x05, 0xf0, 0x05, 0xf0, 0x11, 0xf0, 0x14}; /* Ctrl-Alt-Del */ char cad[] = {0x14, 0x11, 0x71, 0xf0, 0x71, 0xf0, 0x11, 0xf0, 0x14}; enum {NONE, SOFT, HARD, QUERY}; int compose(char *buf, int addr, char cmd, char *data, int dlen); int chkresponse(char *buf, int *addr, char *data, int *dlen); int talk(int fd, char *buf, int wlen, int rlen); int prstr(char *data, int len); int main(int argc, char **argv); int opendev(char *dev); int closedev(int fd); unsigned short int get_crc_16(int start, char *p, int n); int compose(char *buf, int addr, char cmd, char *data, int dlen) { char *p; int crc; p = buf; memcpy(p, to_kbd, sizeof(to_kbd) - 1); p += sizeof(to_kbd) - 1; *p++ = (addr >> 16) & 0xff; *p++ = (addr >> 8) & 0xff; *p++ = addr & 0xff; *p++ = dlen + 4; *p++ = cmd; while (dlen--) *p++ = *data++; crc = get_crc_16(0, buf, p - buf); #if debug printf("crc: 0x%x\n", crc); #endif *p++ = (crc >> 8) & 0xff; *p++ = crc & 0xff; return p - buf; } int chkresponse(char *buf, int *addr, char *data, int *dlen) { char *p; int crc, len; if (memcmp(buf, to__pc, sizeof(to__pc) - 1) != 0) return -1; p = buf + sizeof(to__pc) - 1; *addr = (*p++ & 0xff) << 16; *addr |= (*p++ & 0xff) << 8; *addr |= *p++ & 0xff; *dlen = *p++ - 3; for (len = *dlen; len--; ++data) *data = *p++; crc = (*p++ & 0xff) << 8; crc |= *p & 0xff; #if debug printf("crc: 0x%x\n", get_crc_16(0, buf, *dlen + 11)); #endif if (get_crc_16(0, buf, *dlen + 11) != crc) return -1; return 0; } int talk(int fd, char *buf, int wlen, int rlen) { fd_set readfd; struct timeval tv; int done = 0; int expected = minlen; int ret; int expflag = 1; ret = write(fd, buf, wlen); if (ret == -1) { perror("write"); return ret; } tv.tv_usec = 0; tv.tv_sec = 5; FD_ZERO(&readfd); FD_SET(fd, &readfd); while (done < expected) { ret = select(fd + 1, &readfd, NULL, NULL, &tv); if (ret == -1) perror("select"); if (ret <= 0) return ret; if (FD_ISSET(fd, &readfd)) { ret = read(fd, buf + done, rlen - done); if (ret == -1) { perror("read"); return ret; } done += ret; } if (expflag && done >= minlen) { expflag = 0; expected += (int)buf[minlen]; } #if debug printf("done: %d, expected: %d\n", done, expected); #endif } return done; } int prstr(char *data, int len) { data += 2; len -= 2; while (len--) putchar(*data++); putchar('\n'); return 0; } int opendev(char *dev) { struct termios raw; int fd; fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) err(1, "%s", dev); tcgetattr(fd, &tio); memcpy(&raw, &tio, sizeof(struct termios)); cfsetspeed(&raw, B2400); cfmakeraw(&raw); #if 1 raw.c_cc[VMIN] = minlen; /* at least 10 bytes */ raw.c_cc[VTIME] = 2; /* 200 ms */ #endif if (tcsetattr(fd, TCSAFLUSH, &raw) < 0) err(1, "can't set raw mode"); return fd; } int closedev(int fd) { if (tcsetattr(fd, TCSAFLUSH, &tio) < 0) err(1, "can't restore from raw mode"); close(fd); return 0; } int telejet(char *tj, int type) { char buf[256], data[256]; int fd, len, ret; int eval = -1; int addr = 0xffffff; #if debug char *p; #endif if (tj) addr = strtol(tj, NULL, 0x10); else if (type != QUERY) return -1; #if debug printf("addr: 0x%x\n", addr); #endif fd = opendev("/dev/ttyS0"); switch (type) { case QUERY: len = compose(buf, addr, 'V', NULL, 0); break; case HARD: /* push the button */ len = compose(buf, addr, 'R', NULL, 0); break; case SOFT: default: /* escape from x11 to console 1 */ len = compose(buf, addr, 'D', caf, sizeof(caf)); talk(fd, buf, len, sizeof(buf)); /* send tree finger */ len = compose(buf, addr, 'D', cad, sizeof(cad)); break; } ret = talk(fd, buf, len, sizeof(buf)); closedev(fd); if (ret > 0) { if (chkresponse(buf, &addr, data, &len) == 0) { if (memcmp(data + len - 2, "OK", 2) == 0) { eval = 0; #if debug prstr(data, len); #endif } #if debug } else { printf("chk failed, len=%d\n", ret); for (p = buf; ret-- > 0; ++p) printf("'%c'\t0x%.2x\n", *p & 0xff, *p & 0xff); #endif } } return eval; } #if 1 void usage() { extern char *__progname; fprintf(stderr, "usage: %s [-hsq] [addr]\n", __progname); exit(1); } int main(int argc, char **argv) { int c, type = SOFT; while ((c = getopt(argc, argv, "f:shq")) != -1) switch (c) { case 'f': device = strdup(optarg); break; case 's': type = SOFT; break; case 'h': type = HARD; break; case 'q': type = QUERY; break; } argc -= optind; argv += optind; if (!argc && type != QUERY) usage(); return telejet(*argv, type); } #endif