/* ird.c (c) Copyright Daniel D. Lanciani 1995-1998 All rights reserved. ird.c is licensed free of charge for non-commercial distribution and for personal and internal business use only. ird.c may not be distributed for profit, nor may it be included in products or otherwise distributed by commercial entities to their clients or customers without the prior written permission of the author. TO THE EXTENT ALLOWED BY APPLICABLE LAW, ird.c IS PROVIDED "AS IS", WITH NO EXPRESS OR IMPLIED WARRANTY, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL THE AUTHOR BE LIABLE FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE ird.c EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. These copyright, license, and disclaimer notices must be included with all copies of ird.c. Modified versions of ird.c must be marked as such. ddl@danlan.com */ #include #include #include #include #include #include #include #include #include #include #include #define CHUNK 1024 #ifdef DEBUG #define debug(x) puts(x) #else #define debug(x) #endif struct sgttyb sgttyb; char rbuf[128], buf[128]; u_char abuf[2048]; int lsocket, n, nclient, maxclient = 20, on = 1, off = 0, acnt; static struct sockaddr_in sin = { AF_INET }; struct linger linger = { 1, 0 }; u_long lasttime; jmp_buf alrbuf; char *memchr(), *index(), *malloc(), *realloc(); struct clients { int s, cnt, flags; char buf[128]; } *clients; #define C_QUIET 0x0001 char rcvcmd[] = { 42+128, /* acquisition, not internal, mid-gain */ 120, /* max acq time 1/300 sec units */ 60, /* min pulses */ 14, /* min pulse width (in us) * 0.0768 */ 15, /* max time to look for long zero 1/300 sec units */ 2, /* length of long zero 1/300 sec units */ 6, 229, 56, /* 14 * (flash mode acq time ) + 53 (54-58) */ 12, 45, 0, /* 1 => no flash mode */ 2 /* 1 => low gain */ }; char sndcmd[] = { 1, /* transmit all channels */ 135, /* 255 - 0.3 * (inter-command delay) */ 4 /* 0.46 * (flash-mode pulse width in us) - 2 */ }; main(argc, argv) char **argv; { register FILE *m; register int i, c; register char *p; int l; struct servent *sp; fd_set rset, probe; u_long t; #ifndef DEBUG if(fork()) exit(0); close(0); close(1); close(2); if(open("/", 0)) { syslog(LOG_CRIT, "ird: open / not 0: %m"); exit(1); } dup2(0, 1); dup2(0, 2); #endif signal(SIGPIPE, SIG_IGN); #ifdef LOG_LOCAL0 openlog("ird", LOG_PID, LOG_LOCAL0); #else openlog("ird", LOG_PID, 0); #endif syslog(LOG_INFO, "starting"); if(!(clients = (struct clients *) malloc(maxclient * sizeof(struct clients)))) { syslog(LOG_CRIT, "no memory for clients"); exit(1); } FD_ZERO(&rset); if(!(sp = getservbyname("hamir", "tcp"))) { syslog(LOG_ERR, "hamir: unknown service"); exit(1); } if((lsocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { syslog(LOG_ERR, "socket: %m"); exit(1); } sin.sin_port = sp->s_port; if(bind(lsocket, &sin, sizeof(sin))) { syslog(LOG_ERR, "bind: %m"); exit(1); } listen(lsocket, 5); ioctl(lsocket, FIONBIO, &on); FD_SET(lsocket, &rset); if((n = open("/dev/irmaster", 2)) < 0) { syslog(LOG_ERR, "/dev/irmaster: %m"); exit(1); } #ifndef DEBUG if((c = open("/dev/tty", 2)) >= 0) { ioctl(c, TIOCNOTTY, 0); close(c); } #endif ioctl(n, TIOCGETP, &sgttyb); sgttyb.sg_ispeed = sgttyb.sg_ospeed = B9600; sgttyb.sg_flags = RAW; ioctl(n, TIOCSETP, &sgttyb); time(&lasttime); write(n, "\377", 1); sleep(1); write(n, rcvcmd, sizeof(rcvcmd)); ioctl(n, FIONBIO, &on); FD_SET(n, &rset); while(1) { probe = rset; if(select(FD_SETSIZE, &probe, (struct fd_set *)0, (struct fd_set *)0, (struct timeval *)0) <= 0) { syslog(LOG_ERR, "select: %m"); sleep(2); continue; } if(FD_ISSET(lsocket, &probe)) { l = sizeof(sin); if((c = accept(lsocket, &sin, &l)) < 0) goto badaccept; ioctl(c, FIONBIO, &on); if(m = fopen("/usr/local/lib/hcs/ird.access", "r")) { while(fscanf(m, "%s %s\n", buf, rbuf) == 2) if((sin.sin_addr.s_addr & inet_addr(rbuf)) == inet_addr(buf)) break; fclose(m); if((sin.sin_addr.s_addr & inet_addr(rbuf)) != inet_addr(buf)) { close(c); goto badaccept; } } if(nclient >= maxclient) { maxclient += 20; if(!(clients = (struct clients *)realloc( (char *)clients, maxclient * sizeof(struct clients)))) { syslog(LOG_CRIT,"out of client memory"); exit(1); } } setsockopt(c, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)); clients[nclient].flags = clients[nclient].cnt = 0; clients[nclient++].s = c; FD_SET(c, &rset); badaccept:; } for(i = 0; i < nclient; i++) if(FD_ISSET(clients[i].s, &probe)) { c = sizeof(clients[i].buf) - clients[i].cnt - 1; if(c <= 0) goto badclient; c = read(clients[i].s, clients[i].buf + clients[i].cnt, c); if(c < 0) { if(errno != EWOULDBLOCK) goto badclient; continue; } if(c == 0) { badclient: close(clients[i].s); FD_CLR(clients[i].s, &rset); nclient--; for(c = i; c < nclient; c++) clients[c] = clients[c + 1]; i--; continue; } clients[i].cnt += c; while(p = memchr(clients[i].buf, '\n', clients[i].cnt)) { c = p - clients[i].buf + 1; bcopy(clients[i].buf, rbuf, c); if(clients[i].cnt -= c) bcopy(clients[i].buf + c, clients[i].buf, clients[i].cnt); for(p = rbuf; *p >= ' '; p++); *p = 0; if(*rbuf == 's' || *rbuf == 'S') { if(dosend(rbuf + 1, buf)) { write(clients[i].s, "E ", 2); write(clients[i].s, buf, strlen(buf)); write(clients[i].s, "\r\n", 2); } else { ack: write(clients[i].s, "A\r\n", 3); } continue; } if(*rbuf == 'q' || *rbuf == 'Q') { clients[i].flags ^= C_QUIET; goto ack; } write(clients[i].s, "E Syntax\r\n", 10); } } if(FD_ISSET(n, &probe)) { time(&t); if(t - lasttime > 10 || acnt >= sizeof(abuf)) acnt = 0; lasttime = t; c = read(n, abuf + acnt, sizeof(abuf) - acnt); if(c < 0) { if(errno != EWOULDBLOCK) { syslog(LOG_ERR, "read irmaster: %m"); sleep(1); } goto badread; } if(c == 0) { syslog(LOG_ERR, "read irmaster: EOF"); sleep(1); goto badread; } acnt += c; again: for(i = 0; i < acnt - 1; i++) if(abuf[i] == 255 && abuf[i + 1] == 255) { processir(abuf, i); if(acnt -= i + 2) { debug("some left"); bcopy(abuf + i + 2, abuf, acnt); goto again; } break; } badread:; } } } sigalr() { longjmp(alrbuf, 1); } struct vendor { char *name; int (*recv)(); int (*send)(); }; extern struct vendor vendor[]; processir(buf, cnt) u_char *buf; { register int i, l; register char *p; char xbuf[128]; u_short tbuf[CHUNK/2], c, last; debug("process some"); if(cnt > CHUNK) { /*syslog(LOG_ERR, "big data block %d bytes", cnt);*/ return; } if(!(cnt&1)) { /*syslog(LOG_ERR, "even data block %d bytes", cnt);*/ return; } for(i = 1, l = 0, last = 0; i < cnt; i += 2) { c = 256 * buf[i] + buf[i + 1]; tbuf[l++] = c - last; last = c; } for(i = 0; p = vendor[i].name; i++) if(!(*vendor[i].recv)(*buf, tbuf, l, xbuf + 3 + strlen(p))) break; if(!p) return; xbuf[0] = 'R'; xbuf[1] = ' '; l = strlen(p); bcopy(p, &xbuf[2], l); xbuf[l + 2] = ' '; strcat(xbuf, "\r\n"); l = strlen(xbuf); for(i = 0; i < nclient; i++) { if(clients[i].flags & C_QUIET) continue; if(write(clients[i].s, xbuf, l) != l) shutdown(clients[i].s, 2); } } dosend(buf, ebuf) char *buf, *ebuf; { register int i, tot; int cnt, ret; register char *p; char xbuf[CHUNK+1], *loc; u_short tbuf[CHUNK/2]; while(*buf == ' ') buf++; if(!*buf) { strcpy(ebuf, "Missing location"); return(-1); } loc = buf; if(!(p = index(buf, ' '))) { strcpy(ebuf, "Missing location/vendor delimiter"); return(-1); } *p++ = 0; buf = p; if(!*buf) { strcpy(ebuf, "Missing vendor"); return(-1); } again: if(!(p = index(buf, ' '))) { strcpy(ebuf, "Missing vendor/code delimiter"); return(-1); } *p = 0; for(i = 0; vendor[i].name; i++) if(!strcasecmp(vendor[i].name, buf)) break; *p++ = ' '; if(!vendor[i].name) { strcpy(ebuf, "Unknown vendor"); return(-1); } if((ret = (*vendor[i].send)(xbuf, tbuf, &cnt, p)) == -1) { strcpy(ebuf, p); return(-1); } for(i = tot = 0, p = xbuf + 1; i < cnt; i++) { tot += tbuf[i]; *p++ = tot >> 8; *p++ = tot; } if(setjmp(alrbuf)) { syslog(LOG_ERR, "send timed out"); strcpy(ebuf, "Send timed out"); alarm(0); signal(SIGALRM, SIG_DFL); write(n, "\377", 1); write(n, rcvcmd, sizeof(rcvcmd)); ioctl(n, FIONBIO, &on); return(-1); } signal(SIGALRM, sigalr); alarm(30); ioctl(n, FIONBIO, &off); write(n, "\377", 1); write(n, sndcmd, sizeof(sndcmd)); write(n, xbuf, 2 * cnt + 1); write(n, "\377", 1); read(n, xbuf, sizeof(xbuf)); if(ret == 1) { alarm(0); goto again; } write(n, "\377", 1); write(n, rcvcmd, sizeof(rcvcmd)); ioctl(n, FIONBIO, &on); alarm(0); signal(SIGALRM, SIG_DFL); return(0); } struct sony_button { int button; char *name; } sony_vtr_button[] = { 0, "1", 1, "2", 2, "3", 3, "4", 4, "5", 5, "6", 6, "7", 7, "8", 8, "9", 9, "0", 9, "10", 10, "11", 11, "enter", 11, "12", 12, "13", 13, "14", 16, "channel-up", 16, "ch+", 17, "channel-down", 17, "ch-", 20, "x2", 21, "power", 22, "eject", 23, "audio-monitor", 24, "stop", 25, "pause", 26, "play", 27, "rewind", 27, "rew", 28, "fast-forward", 28, "ff", 29, "record", 29, "rec", 32, "pause-engage", 34, "x1/10", 35, "x1/5", 38, "scan", 39, "track-auto/man", 40, "search-rew", 41, "search-ff", 42, "tv/vtr", 45, "vtr", 46, "power-on", 47, "power-off", 48, "", 51, "x1", 55, "track-normal", 58, "rewind-play", 60, "aux", 65, "counter-remain", 66, "up", 67, "down", 68, "tracking-up", 69, "tracking-down", 70, "counter-reset", 72, "index-mark", 73, "index-erase", 76, "time-search", 77, "menu", 78, "tv", 79, "input-select", 80, "ee", 81, "execute", 82, "quick-timer", 83, "index", 88, "tape-speed", 89, "tape-return", 90, "display", 96, "timer-set", 97, "right", 97, "next", 98, "left", 99, "timer-clear", 100, "timer-check", 101, "timer-record", 101, "timer-rec", 104, "insert-audio", 104, "insert-video", 106, "edit-assemble", 107, "edit-mark", 108, "edit-start", 110, "digital-off", 111, "speed+", 112, "speed-", 113, "stop-motion", 115, "flash-motion", 124, "digital-scan", 126, "pause-on", -1, 0 }, sony_direct_play_button[] = { 84, "up-right", 85, "up-left", 86, "down-right", 87, "down-left", 114, "vtr1", 115, "vtr2", 116, "vtr3", 117, "mdp", -1, 0 }, sony_tv_button[] = { 0, "1", 1, "2", 2, "3", 3, "4", 4, "5", 5, "6", 6, "7", 7, "8", 8, "9", 9, "0", 11, "enter", 16, "channel-up", 16, "ch+", 17, "channel-down", 17, "ch-", 18, "volume-up", 18, "volume+", 19, "volume-down", 19, "volume-", 20, "mute", 20, "muting", 21, "power", 22, "standard", 24, "picture+", 25, "picture-", 37, "tv/video", 42, "ant/aux", 46, "power-on", 47, "power-off", 51, "right", 52, "left", 54, "sleep", 58, "display", 59, "jump", 88, "pip-channel-up", 88, "pip-ch+", 89, "pip-channel-down", 89, "pip-ch-", 90, "pip-tv/video", 91, "pip", 92, "digital-memo", 94, "pip-position", 96, "menu", 99, "exit", 101, "select", 116, "up", 117, "down", -1, 0 }, sony_pip_button[] = { 16, "c.caption", 110, "off", 114, "memo-display", 115, "replay", 116, "ch-index-8", 118, "ch-index-16", 119, "split", 124, "audio", -1, 0 }, sony_dss_button[] = { 0, "1", 1, "2", 2, "3", 3, "4", 4, "5", 5, "6", 6, "7", 7, "8", 8, "9", 9, "0", 11, "enter", 16, "channel-up", 16, "ch+", 17, "channel-down", 17, "ch-", 21, "power", 23, "alternate-audio", 46, "power-on", 47, "power-off", 56, "menu", 58, "display", 99, "exit", 101, "up-right", 102, "up-left", 103, "down-right", 104, "down-left", 105, "tv/dss", 108, "jump", 110, "guide", 111, "favorite", 114, "up", 115, "down", 116, "right", 117, "left", 118, "select", -1, 0 }, sony_cd_button[] = { 0, "1", 1, "2", 2, "3", 3, "4", 4, "5", 5, "6", 6, "7", 7, "8", 8, "9", 11, "enter", 18, "volume-up", 18, "volume+", 19, "volume-down", 19, "volume-", 29, "continue", 31, "program", 32, "10/0", 32, "0", 40, "memo", 44, "repeat", 48, "ams-", 49, "ams+", 50, "play", 51, "rewind", 51, "rew", 52, "fast-forward", 52, "ff", 56, "stop", 57, "pause", 53, "shuffle", 61, "disc-skip-", 61, "disc-", 62, "disc-skip+", 62, "disc+", 74, "disc", 75, "track", 76, "group", 96, "jog+", 97, "jog-", -1, 0 }; struct sony_device { int device; char *name; struct sony_button *button; } sony_device[] = { 1, "tv", sony_tv_button, 2, "vtr", sony_vtr_button, 2, "vtr1", sony_vtr_button, 6, "mdp", 0, 7, "vtr2", sony_vtr_button, 11, "vtr3", sony_vtr_button, 12, "surround", 0, 18, "equalizer", 0, 16, "cassette", 0, 16, "tuner", 0, 17, "cd", sony_cd_button, 17, "cd1", sony_cd_button, 28, "dat", 0, 57, "cd2", sony_cd_button, 81, "cd3", sony_cd_button, 119, "direct_play", sony_direct_play_button, 164, "pip", sony_pip_button, 183, "dss", sony_dss_button, 0, 0, 0 }; #define SONYUNIT 47 #define SONYREPEAT 3 recv_sony(f, buf, cnt, name) u_short *buf; char *name; { register int i; register struct sony_button *sb; int d, b, d2, b2; char *dname, *bname; if(f < 0x7c || f > 0x9c) return(-1); if(recv_sony_seq(&buf, &cnt, &d, &b)) return(-1); if(recv_sony_seq(&buf, &cnt, &d2, &b2)) return(-1); if(d != d2 || b != b2) { debug("mismatch"); return(-1); } for(i = 0; sony_device[i].name; i++) if(sony_device[i].device == d) break; if(sony_device[i].name) { dname = sony_device[i].name; sb = sony_device[i].button; } else { dname = "unknown_device"; sb = 0; } if(!sb) sb = sony_vtr_button; while(sb->name) { if(sb->button == b) break; sb++; } if(sb->name) bname = sb->name; else bname = "unknown_button"; sprintf(name, "%s %s %d %d", dname, bname, d, b); return(0); } recv_sony_seq(buf, cntp, device, button) register u_short **buf; int *cntp, *device, *button; { register int i; register unsigned c; unsigned cnt = 0, bcnt = 0; char bits[15]; debug("recv_sony_seq"); while(*cntp > 0) { c = *(*buf)++; (*cntp)--; cnt++; if((cnt&1) && c > 3 * SONYUNIT && c < 5 * SONYUNIT) goto foundstart; } debug("no leader"); return(-1); foundstart: while(*cntp > 0) { c = *(*buf)++; (*cntp)--; cnt++; if(cnt&1) { if(bcnt >= 15) { debug("too many bits"); return(-1); } if(c < SONYUNIT / 2 || c > 2 * SONYUNIT + SONYUNIT /2) { debug("high bit wrong length"); return(-1); } if(c > SONYUNIT + SONYUNIT / 2) bits[bcnt++] = 1; else bits[bcnt++] = 0; } else { if(c > 3 * SONYUNIT) break; if(c < SONYUNIT / 2 || c > SONYUNIT + SONYUNIT / 2) { debug("low bit wrong length"); return(-1); } } } if(bcnt < 12) { debug("short bit count"); return(-1); } *button = 0; for(i = 0, c = 1; i < 7; i++, c <<= 1) if(bits[i]) *button |= c; *device = 0; for(i = 7, c = 1; i < bcnt; i++, c <<= 1) if(bits[i]) *device |= c; return(0); } send_sony(f, buf, cnt, name) u_char *f; int *cnt; register u_short *buf; char *name; { register int i, j, l; struct sony_button *sb; char *p, *q; int b, d, tot; if(!*name) { strcpy(name, "Missing device code"); return(-1); } if(!(p = index(name, ' '))) { strcpy(name, "Missing device/button delimiter"); return(-1); } *p++ = 0; if(!*p) { strcpy(name, "Missing button code"); return(-1); } if(q = index(p, ' ')) *q = 0; if(*name == '#') { d = atoi(name + 1); if(*p == '#') sb = 0; else { for(i = 0; sony_device[i].name; i++) if(sony_device[i].device == d) break; if(sony_device[i].name) sb = sony_device[i].button; else sb = 0; } } else { for(i = 0; sony_device[i].name; i++) if(!strcasecmp(name, sony_device[i].name)) break; if(!sony_device[i].name) { strcpy(name, "Unknown device"); return(-1); } d = sony_device[i].device; sb = sony_device[i].button; } if(!sb) sb = sony_vtr_button; if(*p == '#') b = atoi(p + 1); else { while(sb->name) { if(!strcasecmp(sb->name, p)) break; sb++; } if(!sb->name) { strcpy(name, "Unknown button"); return(-1); } b = sb->button; } *f = 0x8c; if(d > 31) { l = 8; *cnt = 32 * SONYREPEAT; } else { *cnt = 26 * SONYREPEAT; l = 5; } for(i = 0; i < SONYREPEAT; i++) { *buf++ = 4 * SONYUNIT; *buf++ = SONYUNIT; *cnt += 2; tot = 0; for(j = 0; j < 7; j++) if(b & (1 << j)) { *buf++ = 2 * SONYUNIT; *buf++ = SONYUNIT; tot += 3 * SONYUNIT; } else { *buf++ = SONYUNIT; *buf++ = SONYUNIT; tot += 2 * SONYUNIT; } for(j = 0; j < l; j++) if(d & (1 << j)) { *buf++ = 2 * SONYUNIT; if(j < l - 1) { *buf++ = SONYUNIT; tot += 3 * SONYUNIT; } else tot += 2 * SONYUNIT; } else { *buf++ = SONYUNIT; if(j < l - 1) { *buf++ = SONYUNIT; tot += 2 * SONYUNIT; } else tot += SONYUNIT; } *buf++ = 3460 - tot; } if(q) { *q = ' '; strcpy(p - 1, q); return(1); } return(0); } #undef SONYREPEAT #undef SONYUNIT struct jvc_button { int button; char *name; } jvc_vcr_button[] = { 3, "stop", 4, "eject", 6, "fast-forward", 6, "ff", 7, "rewind", 7, "rew", 8, "slow+", 11, "power", 12, "play", 13, "pause", 19, "tv/video", 20, "search>>", 21, "< 0xa6) return(-1); if(recv_jvc_seq(&buf, &cnt, &d, &b, &fh)) return(-1); if(!fh && b != 175 && b != 237) return(-1); if(recv_jvc_seq(&buf, &cnt, &d2, &b2, 0)) return(-1); if(d != d2 || b != b2) { debug("mismatch"); return(-1); } for(i = 0; jvc_device[i].name; i++) if(jvc_device[i].device == d) break; if(jvc_device[i].name) { dname = jvc_device[i].name; sb = jvc_device[i].button; } else { dname = "unknown_device"; sb = 0; } if(!sb) sb = jvc_vcr_button; while(sb->name) { if(sb->button == b) break; sb++; } if(sb->name) bname = sb->name; else bname = "unknown_button"; sprintf(name, "%s %s %d %d", dname, bname, d, b); return(0); } recv_jvc_seq(buf, cntp, device, button, fh) register u_short **buf; int *cntp, *device, *button, *fh; { register int i; register unsigned c; unsigned cnt = 0, bcnt = 0, code = 0; debug("recv_jvc_seq"); if(fh) *fh = 0; if(**buf < 2 * JVCUNIT) goto foundstart; while(*cntp > 1) { c = *(*buf)++; (*cntp)--; cnt++; if((cnt&1) && c > 14 * JVCUNIT && c < 18 * JVCUNIT && **buf > 6 * JVCUNIT && **buf < 10 * JVCUNIT) { (*buf)++; (*cntp)--; cnt++; if(fh) *fh = 1; goto foundstart; } } debug("no leader"); return(-1); foundstart: while(*cntp > 0) { c = *(*buf)++; (*cntp)--; cnt++; if(cnt&1) { if(c < JVCUNIT / 2 || c > JVCUNIT + JVCUNIT / 2) { debug("high bit wrong length"); return(-1); } } else { if(c > 4 * JVCUNIT) break; if(c < JVCUNIT / 2 || c > 3 * JVCUNIT + JVCUNIT / 2) { debug("low bit wrong length"); return(-1); } if(c > 2 * JVCUNIT) code |= (1 << bcnt); if(++bcnt > 16) { debug("too many bits"); return(-1); } } } if(bcnt != 16) { debug("wrong high bit count"); return(-1); } *button = (code >> 8) & 0xff; *device = code & 0xff; return(0); } send_jvc(f, buf, cnt, name) u_char *f; int *cnt; register u_short *buf; char *name; { register int i, j; struct jvc_button *sb; char *p, *q; int b, d, gap; if(!*name) { strcpy(name, "Missing device code"); return(-1); } if(!(p = index(name, ' '))) { strcpy(name, "Missing device/button delimiter"); return(-1); } *p++ = 0; if(!*p) { strcpy(name, "Missing button code"); return(-1); } if(q = index(p, ' ')) *q = 0; if(*name == '#') { d = atoi(name + 1); if(*p == '#') sb = 0; else { for(i = 0; jvc_device[i].name; i++) if(jvc_device[i].device == d) break; if(jvc_device[i].name) sb = jvc_device[i].button; else sb = 0; } } else { for(i = 0; jvc_device[i].name; i++) if(!strcasecmp(name, jvc_device[i].name)) break; if(!jvc_device[i].name) { strcpy(name, "Unknown device"); return(-1); } d = jvc_device[i].device; sb = jvc_device[i].button; } if(!sb) sb = jvc_vcr_button; if(*p == '#') b = atoi(p + 1); else { while(sb->name) { if(!strcasecmp(sb->name, p)) break; sb++; } if(!sb->name) { strcpy(name, "Unknown button"); return(-1); } b = sb->button; } *f = 0x96; d |= (b << 8); *buf++ = 16 * JVCUNIT; *buf++ = 8 * JVCUNIT; *cnt = 2 + 2 * JVCREPEAT * 17; gap = 4408 - (24 + 17) * JVCUNIT; for(i = 0; i < JVCREPEAT; i++) { for(j = 0; j < 16; j++) { *buf++ = JVCUNIT; if(d & (1 << j)) { *buf++ = 3 * JVCUNIT; gap -= 3 * JVCUNIT; } else { *buf++ = JVCUNIT; gap -= JVCUNIT; } } *buf++ = JVCUNIT; *buf++ = gap; gap = 3540 - 17 * JVCUNIT; } if(q) { *q = ' '; strcpy(p - 1, q); return(1); } return(0); } #undef JVCREPEAT #undef JVCUNIT struct mitsubishi_button { int button; char *name; } mitsubishi_vcr_button[] = { 0, "1", 1, "9", 2, "power", 4, "play", 5, "vcr/tv", 8, "2", 9, "0", 10, "channel-up", 10, "ch+", 11, "cancel", 12, "record", 12, "rec", 14, "index+", 16, "3", 18, "channel-down", 18, "ch-", 20, "stop", 22, "index-", 24, "4", 28, "pause", 32, "5", 36, "fast-forward", 36, "ff", 37, "qv", 39, "display", 40, "6", 44, "rewind", 44, "rew", 47, "otr", 48, "7", 51, "vcr+", 53, "enter", 56, "8", 58, "input", 63, "reset-on", 64, "video", 66, "power-on", 80, "audio", 82, "menu", 86, "adjust-on", 94, "adjust-off", 115, "jog-", 116, "jog+", -1, 0 }; struct mitsubishi_device { int device; char *name; struct mitsubishi_button *button; } mitsubishi_device[] = { 0x57, "vcr", mitsubishi_vcr_button, 0x57, "vcra", mitsubishi_vcr_button, 0x157, "vcrb", mitsubishi_vcr_button, 0x47, "tv", 0, 0, 0, 0 }; #define MITSUBISHIREPEAT 3 recv_mitsubishi(f, buf, cnt, name) u_short *buf; char *name; { register int i; register struct mitsubishi_button *sb; int d, b, d2, b2; char *dname, *bname; if(f < 0x9c || f > 0xa8) return(-1); if(recv_mitsubishi_seq(&buf, &cnt, &d, &b)) return(-1); if(recv_mitsubishi_seq(&buf, &cnt, &d2, &b2)) return(-1); if(d != d2 || b != b2) { debug("mismatch"); return(-1); } if(b & 0x80) { b &= 0x7f; d |= 0x100; } for(i = 0; mitsubishi_device[i].name; i++) if(mitsubishi_device[i].device == d) break; if(mitsubishi_device[i].name) { dname = mitsubishi_device[i].name; sb = mitsubishi_device[i].button; } else { dname = "unknown_device"; sb = 0; } if(!sb) sb = mitsubishi_vcr_button; while(sb->name) { if(sb->button == b) break; sb++; } if(sb->name) bname = sb->name; else bname = "unknown_button"; sprintf(name, "%s %s %d %d", dname, bname, d, b); return(0); } recv_mitsubishi_seq(buf, cntp, device, button) register u_short **buf; int *cntp, *device, *button; { register int i; register unsigned c; unsigned cnt = 0, bcnt = 0, code = 0; debug("recv_mitsubishi_seq"); while(*cntp > 0) { c = *(*buf)++; (*cntp)--; cnt++; if(cnt&1) { if(c < 22 || c > 26) { debug("high bit wrong length"); return(-1); } } else { if(c > 1000) break; if(c < 68 || c > 163) { debug("low bit wrong length"); return(-1); } if(c > 115) code |= (1 << bcnt); if(++bcnt > 16) { debug("too many bits"); return(-1); } } } if(bcnt != 16) { debug("wrong high bit count"); return(-1); } *button = (code >> 8) & 0xff; *device = code & 0xff; return(0); } send_mitsubishi(f, buf, cnt, name) u_char *f; int *cnt; register u_short *buf; char *name; { register int i, j; struct mitsubishi_button *sb; char *p, *q; int b, d, gap; if(!*name) { strcpy(name, "Missing device code"); return(-1); } if(!(p = index(name, ' '))) { strcpy(name, "Missing device/button delimiter"); return(-1); } *p++ = 0; if(!*p) { strcpy(name, "Missing button code"); return(-1); } if(q = index(p, ' ')) *q = 0; if(*name == '#') { d = atoi(name + 1); if(*p == '#') sb = 0; else { for(i = 0; mitsubishi_device[i].name; i++) if(mitsubishi_device[i].device == d) break; if(mitsubishi_device[i].name) sb = mitsubishi_device[i].button; else sb = 0; } } else { for(i = 0; mitsubishi_device[i].name; i++) if(!strcasecmp(name, mitsubishi_device[i].name)) break; if(!mitsubishi_device[i].name) { strcpy(name, "Unknown device"); return(-1); } d = mitsubishi_device[i].device; sb = mitsubishi_device[i].button; } if(!sb) sb = mitsubishi_vcr_button; if(*p == '#') b = atoi(p + 1); else { while(sb->name) { if(!strcasecmp(sb->name, p)) break; sb++; } if(!sb->name) { strcpy(name, "Unknown button"); return(-1); } b = sb->button; } *f = 0xa2; if(d & 0x100) { d &= 0xff; b |= 0x80; } d |= (b << 8); *cnt = 2 * MITSUBISHIREPEAT * 17; for(i = 0; i < MITSUBISHIREPEAT; i++) { gap = 4158 - 17*24; for(j = 0; j < 16; j++) { *buf++ = 24; if(d & (1 << j)) { *buf++ = 161; gap -= 161; } else { *buf++ = 69; gap -= 69; } } *buf++ = 24; *buf++ = gap; } if(q) { *q = ' '; strcpy(p - 1, q); return(1); } return(0); } #undef MITSUBISHIREPEAT struct panasonic_button { int button; char *name; } panasonic_vcr_button[] = { 0, "stop", 1, "eject", 2, "rewind", 2, "rew", 3, "fast-forward", 3, "ff", 6, "pause", 6, "still", 8, "record", 8, "rec", 8, "rec/time", 10, "play", 12, "jog+", 13, "jog-", 15, "slow", 15, "f.adv", 16, "1", 17, "2", 18, "3", 19, "4", 20, "5", 21, "6", 22, "7", 23, "8", 24, "9", 25, "0", 51, "audio-out", 52, "channel-up", 52, "ch+", 53, "channel-down", 53, "ch-", 54, "vcr/tv", 55, "tv", 56, "vcr", 57, "display", 59, "-/--", /* XXX */ 61, "power", 62, "power-on", 63, "power-off", 64, "index", 65, "address", /* XXX */ 66, "renumber", /* XXX */ 68, "time-search", /* XXX */ 73, "", 75, "index-mark", /* XXX */ 76, "index-erase", /* XXX */ 83, "memory/repeat/search", /* XXX */ 84, "counter-reset", 85, "remaining/counter/clock", 86, "clock/counter", /* XXX */ 128, "jog-on2", 129, "jog-on", 130, "shuttle-3", 131, "shuttle+3", 132, "shuttle-4", 133, "shuttle+4", 134, "shuttle-5", 135, "shuttle+5", 136, "shuttle-6", 137, "shuttle+6", 138, "shuttle-?", 139, "shuttle+?", 142, "slow-up", 143, "slow-down", 144, "book-mark", 144, "blank-search", 145, "monitor", /* XXX */ 147, "jog-off", 150, "shuttle-1", 151, "shuttle+1", 152, "shuttle-2", 153, "shuttle+2", 162, "memory", 163, "start", 165, "edit", 177, "tracking-up", 178, "tracking-down", 180, "timer", 181, "timer2", 192, "input", 202, "tuner", 203, "a1", 203, "l1", 204, "a2", 204, "l2", 229, "100", 230, "audio-monitor", 245, "zero-search", 245, "zero-stop", 245, "1-min-skip", 252, "test", -1, 0 }, panasonic_vcrx_button[] = { 0, "exit", 1, "program", 1, "prog", 2, "select-up", 3, "select-down", 4, "set", 5, "clear", 7, "a1", 7, "l1", 8, "off-thing", /* XXXXXX */ 9, "timer-clear", 10, "speed", 11, "sp", 13, "slp", 16, "language-select", /* XXX */ 44, "preset/fine/normal", 45, "preset", 46, "fine", 47, "normal", 49, "add/dlt", 50, "add", 51, "dlt", 52, "ant-select", 56, "r-tune", 57, "auto-tune", /* like ant-select */ 62, "tune-up", /* XXXX or 63 */ 63, "tune-down", /* XXXX or 62 */ 64, "text-on", /* XXX */ 67, "text-off", /* XXX */ 68, "vtindex", /* XXX video text index */ 69, "f/t/b", /* XXX */ 70, "reveal", /* XXX */ 71, "hold", /* XXX */ 74, "list", /* XXX */ 77, "page-preset", /* XXX */ 78, "top/memory", /* XXX */ 79, "vpt", /* XXX */ 80, "red", /* XXX */ 81, "green", /* XXX */ 82, "yellow", /* XXX */ 83, "blue", /* XXX */ 86, "menu", 93, "vcr-plus+", 93, "vcr+", 95, "counter/clock", 192, "otr-on+", 193, "otr-on-", 194, "otr-off+", 195, "otr-off-", 224, "clock-blank", /* XXXX */ 242, "no-skip-channel-up", 243, "no-skip-channel-down", -1, 0 }; struct panasonic_device { int device; char *name; struct panasonic_button *button; } panasonic_device[] = { 0x0090, "vcr", panasonic_vcr_button, 0x0090, "vcr1", panasonic_vcr_button, 0x2090, "vcr2", panasonic_vcr_button, 0x0190, "vcrx", panasonic_vcrx_button, 0x0190, "vcrx1", panasonic_vcrx_button, 0x2190, "vcrx2", panasonic_vcrx_button, 0, 0, 0 }; #define PANASONICUNIT 33 recv_panasonic(f, buf, cnt, name) u_short *buf; char *name; { register int i; register struct panasonic_button *sb; int d, b, fh; char *dname, *bname; if(f < 0x88 || f > 0xa8) return(-1); if(recv_panasonic_seq(&buf, &cnt, &d, &b, &fh)) return(-1); if(!fh) return(-1); for(i = 0; panasonic_device[i].name; i++) if(panasonic_device[i].device == d) break; if(panasonic_device[i].name) { dname = panasonic_device[i].name; sb = panasonic_device[i].button; } else { dname = "unknown_device"; sb = 0; } if(!sb) sb = panasonic_vcr_button; while(sb->name) { if(sb->button == b) break; sb++; } if(sb->name) bname = sb->name; else bname = "unknown_button"; sprintf(name, "%s %s %d %d", dname, bname, d, b); return(0); } recv_panasonic_seq(buf, cntp, device, button, fh) register u_short **buf; int *cntp, *device, *button, *fh; { register int i; register unsigned c; unsigned cnt = 0, bcnt = 0, prefix = 0; u_char cksum = 0; debug("recv_panasonic_seq"); if(fh) *fh = 0; *device = *button = 0; if(**buf < 2 * PANASONICUNIT) goto foundstart; while(*cntp > 1) { c = *(*buf)++; (*cntp)--; cnt++; if((cnt&1) && c > 6 * PANASONICUNIT && c < 12 * PANASONICUNIT && **buf > 3 * PANASONICUNIT && **buf < 5 * PANASONICUNIT){ (*buf)++; (*cntp)--; cnt++; if(fh) *fh = 1; goto foundstart; } } debug("no leader"); return(-1); foundstart: while(*cntp > 0) { c = *(*buf)++; (*cntp)--; cnt++; if(cnt&1) { if(c < PANASONICUNIT / 2 || c > PANASONICUNIT + PANASONICUNIT / 2) { debug("high bit wrong length"); return(-1); } } else { if(c > 4 * PANASONICUNIT) break; if(c < PANASONICUNIT / 2 || c > 3 * PANASONICUNIT + PANASONICUNIT / 2) { debug("low bit wrong length"); return(-1); } if(c > 2 * PANASONICUNIT) { if(bcnt < 16) prefix |= (1 << bcnt); else if(bcnt < 32) *device |= (1 << (bcnt - 16)); else if(bcnt < 40) *button |= (1 << (bcnt - 32)); else cksum |= (1 << (bcnt - 40)); } if(++bcnt > 48) { debug("too many bits"); return(-1); } } } if(bcnt != 48) { debug("wrong high bit count"); return(-1); } if(prefix != 0x2002) { debug("prefix is not 0x2002"); return(-1); } cksum ^= *button ^ *device ^ (*device >> 8); if(cksum) { debug("bad cksum"); return(-1); } return(0); } send_panasonic(f, buf, cnt, name) u_char *f; int *cnt; register u_short *buf; char *name; { register int i, word = 0x2002; struct panasonic_button *sb; char *p, *q; int b, d; u_char cksum; if(!*name) { strcpy(name, "Missing device code"); return(-1); } if(!(p = index(name, ' '))) { strcpy(name, "Missing device/button delimiter"); return(-1); } *p++ = 0; if(!*p) { strcpy(name, "Missing button code"); return(-1); } if(q = index(p, ' ')) *q = 0; if(*name == '#') { d = atoi(name + 1); if(*p == '#') sb = 0; else { for(i = 0; panasonic_device[i].name; i++) if(panasonic_device[i].device == d) break; if(panasonic_device[i].name) sb = panasonic_device[i].button; else sb = 0; } } else { for(i = 0; panasonic_device[i].name; i++) if(!strcasecmp(name, panasonic_device[i].name)) break; if(!panasonic_device[i].name) { strcpy(name, "Unknown device"); return(-1); } d = panasonic_device[i].device; sb = panasonic_device[i].button; } if(!sb) sb = panasonic_vcr_button; if(*p == '#') b = atoi(p + 1); else { while(sb->name) { if(!strcasecmp(sb->name, p)) break; sb++; } if(!sb->name) { strcpy(name, "Unknown button"); return(-1); } b = sb->button; } cksum = b ^ d ^ (d >> 8); *f = 0x98; *buf++ = 8 * PANASONICUNIT; *buf++ = 4 * PANASONICUNIT; *cnt = 2 + 2 * 48 + 1; for(i = 0; i < 48; i++) { *buf++ = PANASONICUNIT; if(word & 1) *buf++ = 3 * PANASONICUNIT; else *buf++ = PANASONICUNIT; word >>= 1; switch(i) { case 15: word = d; break; case 31: word = b | (cksum << 8); break; } } *buf = PANASONICUNIT; if(q) { *q = ' '; strcpy(p - 1, q); return(1); } return(0); } #undef PANASONICUNIT struct vendor vendor[] = { "sony", recv_sony, send_sony, "jvc", recv_jvc, send_jvc, "mitsubishi", recv_mitsubishi, send_mitsubishi, "panasonic", recv_panasonic, send_panasonic, 0, 0, 0 };