--- conf/gpm-silitek.conf +++ conf/gpm-silitek.conf @@ -0,0 +1,45 @@ +# Standard key mapping for Silitek SM-1000 +# on Linux virtual console (TERM=linux) +key 1 1 1 +key 2 2 2 +key 3 3 3 +key 4 4 4 +key 5 5 5 +key 6 6 6 +key 7 7 7 +key 8 8 8 +key 9 9 9 +key 0 0 0 +key * * * +key # # # +# Ctrl+Q +key > \021 \021 +# Ctrl+S +key || \023 \023 +# Crtl+C +key [] \003 \003 +# End +key >>| \033[4~ \033[4~ +# Home +key |<< \033[1~ \033[1~ +# Delete +key Mute \033[3~ \033[3~ +# Cursor up +key Vol+ \033[A \033[A +# Cursor down +key Vol- \033[B \033[B +# Insert +key Display \033[2~ \033[2~ +# Page up +key PgUp \033[5~ \033[5~ +Page down +key PgDn \033[6~ \033[6~ +# Cursor left +key Back \033[D \033[D +# Cursor right +key Forward \033[C \033[C +# F1 upto F4 +key CD \033[[A \033[[A +key ShowWiz \033[[B \033[[B +key WWW \033[[C \033[[C +key Close \033[[D \033[[D --- doc/README.silitek +++ doc/README.silitek @@ -0,0 +1,188 @@ +This README describes the support for the Silitek SM-1000 IR commander +(also called Netshooter). + +This IR commander can be used with two types of mouse driver: + + * Type `silicom' supports the IR receiver which is shipped the + SM-1000 and plugged into a serial connector. + + * Type `silips2' supports the IR receiver which is shipped + with the Silitek IR keyboard SK-7100 (also called Airboard). + The mouse connector of this IR receiver should be plugged + into the PS/2 mouse connector (not to a serial connector). + You may need an adapter (9 pin serial to PS/2) to do this. + +Both types of mouse driver uses the system wide configuration file + + /etc/gpm-silitek.conf + +which defines the return values of any key not being mouse button +or mouse stick. For this keys the drag mouse button works as a +modifier: pressed once, the last column in /etc/gpm-silitek.conf +is used. To switch back to the normal return values, the drag mouse +button has to be pressed again. It is allowed to use escaped +sequences as return values. Characters other than printable can +be coded by using the backslash, e.g. `\033' for the octal number +of the special character ESC (Escape) of `\x0D' for the hexadecimal +number of the special character CR (Carriage Return), for more +codings see the manual page ascii(7). + +The default key mapping for the SM-1000 IR commander is on the +virtual console is: + + SM-1000 Key normal drag active + ------------------------------------- + key 1 1 1 + key 2 2 2 + key 3 3 3 + key 4 4 4 + key 5 5 5 + key 6 6 6 + key 7 7 7 + key 8 8 8 + key 9 9 9 + key 0 0 0 + key * * * + key # # # + key > Ctrl Q Ctrl Q + key || Ctrl S Ctrl S + key [] Crtl C Crtl C + key >>| End End + key |<< Home Home + key Mute Delete Delete + key Vol+ Cursor Up Cursor Up + key Vol- Cursor Down Cursor Down + key Display Insert Insert + key PgUp Page Up Page Up + key PgDn Page Down Page Down + key Back Cursor Left Cursor Left + key Forward Cursor Right Cursor Right + key CD F1 F1 + key ShowWiz F2 F2 + key WWW F3 F3 + key Close F4 F4 + +Usage with the IR receiver of the Silitek Keyboard SK-7100: + +In comparison of the IR receiver of shipped with the SM-1000 +this IR receiver has the advantage that both mouse interface +of the IR keyboard and the IR Netshooter can be used in parallel. +One advantage more is that only one IR receiver is required to +use both the IR keyboard and the IR Netshooter. Next point is +that the IR receiver of the IR keyboard seems to more insensitive +for other IR commanders like those from TV sets. + +The Multimedia keys of the IR keyboard SK-7100 can be configured +by using setkeycodes(8) and loadkeys(8) to be usable in parallel +to those of the IR commander SM-1000. + +Notice: The key 7 of the IR commander SM-1000 generates the keyboard +scancode for KP_ENTER if the SK-7100 keyboard is used in parallel. +This happens even if the mouse type `silicom' is used in conjunction +with the IR receiver of the SM-1000. This because if both IR receiver +are used, the SM-1000 and the SK-7100, both receives signals from the +SM-1000. Therefore this key should be disabled (note that the SK-7100 +does not have a KP_ENTER): + +-------------------------------------------------------------------- +loadkeys <<-EOF +keycode 96 = F39 +string F39 = "" +EOF +-------------------------------------------------------------------- + +With `showkeys -s' on the virtual console and `xev' under X11 the +the other Multimedia keys of the SK-7100 causes the following +scancodes/keycodes: + + SK-7100 Key console X11 + -------------------------- + Close e017 151 + CD e025 165 + Video e018 152 + WWW e032 178 + U/P e01e 158 + |<< e010 144 + || e012 146 + > e022 162 + [] e024 164 + >>| e019 153 + Vol- e02e 174 + Vol+ e030 176 + Mute e020 160 + Display e026 166 + +If we map for e.g. the keys + + Video Switch to vc 8 where the Video Recoder Program runs (Console_8) + |<< Cursor Left (keycode 105) + >>| Cursor Right (keycode 106) + Display Win Menu key (keycode 127) + +(Compares this with `dumpkeys | less' on a virtual console). + +With the following command sequence (called in a script during boot): + +-------------------------------------------------------------------- +setkeycodes \ + e017 85 \ + e025 89 \ + e018 90 \ + e032 91 \ + e01e 92 \ + e010 105 \ + e012 94 \ + e022 95 \ + e024 120 \ + e019 106 \ + e02e 122 \ + e030 123 \ + e020 124 \ + e026 127 +loadkeys <<-EOF + keycode 85 = F50 + keycode 89 = F51 + keycode 90 = Console_8 + keycode 91 = F53 + keycode 92 = F54 + keycode 94 = F56 + keycode 95 = F57 + keycode 120 = F58 + keycode 122 = F60 + keycode 123 = F61 + keycode 124 = F62 + keycode 127 = F63 + keycode 96 = F39 + string F50 = "\033[[Z" + string F51 = "\033[[Y" + string F53 = "\033[[W" + string F54 = "\033[[U" + string F56 = "\033[[H" + string F57 = "\033[[T" + string F58 = "\033[[F" + string F60 = "\033[[L" + string F61 = "\033[[M" + string F62 = "\033[[X" + string F63 = "\033[[R" + string F39 = "" +EOF +-------------------------------------------------------------------- + +the Multimedia keys on IR Keyboard SK-7100 are usable (for ncurses +based programs see below). After editing /etc/gpm-silitek.conf +to get the similar named keys to work similar, both the IR Keyboard +SK-7100 and the IR commander SM-1000 are usable in parallel. + +For ncurses based programs the added keys have to be extended +by dumping the current terminfo entry for the virtual consoles +for TERM=linux: + + infocmp -1 linux > linux.tic + +and editing the file linux.tic which means adding the function +keys kf50, kf51, kf52, kf53, kf54, kf56, kf57, kf58, kf60, kf61, +kf62, kf63, and kf39. After that the command + + tic linux.tic + +installs the edited terminfo entry for TERM=linux. --- src/Makefile.in +++ src/Makefile.in @@ -13,7 +13,7 @@ # Main portion: regular build rules -GSRC = main.c gpm.c gpn.c mice.c special.c twiddler.c synaptics.c \ +GSRC = main.c gpm.c gpn.c mice.c special.c twiddler.c synaptics.c silitek.c \ startup.c server_tools.c GOBJ = $(GSRC:.c=.o) report.o tools.o @@ -24,7 +24,7 @@ PICS = $(LOBJ:.o=.lo) -HDRS = gpm.h gpmInt.h twiddler.h synaptics.h message.h +HDRS = gpm.h gpmInt.h twiddler.h synaptics.h silitek.h message.h PSRC = prog/mev.c prog/hltest.c prog/mouse-test.c prog/disable-paste.c @@ -144,7 +144,7 @@ $(CC) -I. @CPPFLAGS@ $(CPPFLAGS) @CFLAGS@ $(CFLAGS) -c -o $@.o $< $(CC) @LDFLAGS@ $(LDFLAGS) -o $@ $@.o @LIBS@ $(LIBS) lib/libgpm.a -prog/mouse-test: mice.o twiddler.o synaptics.o +prog/mouse-test: mice.o twiddler.o synaptics.o silitek.o $(PROG): lib/libgpm.so lib/@SHLIB@ lib/libgpm.a --- src/headers/message.h +++ src/headers/message.h @@ -194,6 +194,10 @@ #define GPM_MESS_INCORRECT_LINE "%s: %s :%i: Incorrect line:\"%s\"" #define GPM_MESS_FIRST_DEV "Use -m device -t protocol [-o options]!" +#define GPM_MESS_SILIPS2_ENABLE "silips2: enable error" +#define GPM_MESS_SILIPS2_DISABLE "silips2: disable error" +#define GPM_MESS_SILIPS2_RESET "silips2: reset error" + /* warnings */ #define GPM_MESS_REQUEST_ON "Request on vc %i > %i" --- src/headers/silitek.h +++ src/headers/silitek.h @@ -0,0 +1,33 @@ +/* + * silitek.h - support for the Silitek SM-1000 (Netshooter) with its IR + * receiver plugged serial connector and also with the IR + * receiver of the Silitek SK-7100 keyboard (Airboard) + * PS2 mouse connector. + * + * Copyright 2002 Werner Fink + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ +extern int silitek_get_check(unsigned char *data); +extern int silitek_get_check_ps2(unsigned char *data); +extern void silitek_keys(unsigned char *data, int *drag); +extern void silitek_keys_ps2(unsigned char *data, int *drag); +extern int silitek_ghost_ps2(unsigned char *data); +extern int fd_silitek; +#define SILI_SYSTEM_FILE SYSCONFDIR "/gpm-silitek.conf" +#define SILISTRLEN 32 +#define SILISTRSCN "key %32s %32s %32s" +extern void silitek_mapping(void); --- src/mice.c +++ src/mice.c @@ -74,6 +74,7 @@ #include "headers/gpmInt.h" #include "headers/twiddler.h" #include "headers/synaptics.h" +#include "headers/silitek.h" #include "headers/message.h" /*========================================================================*/ @@ -667,6 +668,86 @@ } +#define GPM_B_BOTH (GPM_B_LEFT|GPM_B_RIGHT) +static int M_silitek_ps2(Gpm_Event *state, unsigned char *data) +{ + static int drag = 0; + + if (!silitek_get_check_ps2(data)) + return -1; + + /* + * Map some ghost mouse PS2 events caused by the other + * keys. Maybe there is a better initialization for the chip + * of the SK-7100 IR receiver which would avoid those events. + */ + (void)silitek_ghost_ps2(data); + + /* All none PS2 events */ + if (data[0] & 0xc0) + { + silitek_keys_ps2(data, &drag); + return -1; /* Do not highlight, but success */ + } + + state->buttons = ((data[0]&0x01)<<2)|(data[0]&0x02)|((data[0]&0x04)>>2); + + /* Drag is just a modifier for none mouse keys */ + if (state->buttons&GPM_B_LEFT) + drag = ((!drag) ? 1 : 0); + else + drag = 0; + + if(data[1]) state->dx = (data[0] & 0x10) ? data[1] - 0xFF : data[1]; + if(data[2]) state->dy = -((data[0] & 0x20) ? data[2] - 0xFF : data[2]); + + return 0; +} + +static int M_silitek(Gpm_Event *state, unsigned char *data) +{ + static int drag = 0; + + if (!silitek_get_check(data)) + return -1; + + /* All none mouse events */ + if (!(data[0] & 0x40)) + { + silitek_keys(data, &drag); + return -1; /* Do not highlight, but success */ + } + + /* + * Sanity check: SM-1000 mouse events do not use this bit + * but Keyboard SK-7100 PgUp/PgDn/`x' + * (0xfe5da5/0xfd5da5/0x7c845d) + */ + if ((data[1] & 0x40) || (data[2] & 0x40)) + return -1; + + switch(data[0]) + { + case 0xfe: state->buttons = GPM_B_LEFT; break; + case 0xfd: state->buttons = GPM_B_RIGHT; break; + case 0x7f: state->buttons = GPM_B_BOTH; break; + case 0x7c: state->buttons = 0; break; + default: return -1; /* Do not highlight */ + break; + } + + /* Drag is just a modifier for none mouse keys */ + if (state->buttons&GPM_B_LEFT) + drag = ((!drag) ? 1 : 0); + else + drag = 0; + + state->dx = (data[1] & 0x20) ? ((data[1] & 0x1f) - 0x20) : (data[1] & 0x1f); + state->dy = (data[2] & 0x20) ? ((data[2] & 0x1f) - 0x20) : (data[2] & 0x1f); + + return 0; +} + static int M_netmouse(Gpm_Event *state, unsigned char *data) { /* Avoid these beasts if you can. They connect to normal PS/2 port, @@ -745,7 +826,6 @@ return type; } -#define GPM_B_BOTH (GPM_B_LEFT|GPM_B_RIGHT) static int M_mman(Gpm_Event *state, unsigned char *data) { /* @@ -1828,7 +1908,7 @@ * * Returns 0 if OK, or >0 if 1 or more errors occurred. */ -static int write_to_mouse(int fd, unsigned char *data, size_t len) +static int write_to_mouse(int fd, const unsigned char *data, size_t len) { int i; int error = 0; @@ -1845,6 +1925,89 @@ return(error); } +static Gpm_Type *I_silitek(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) +{ + struct termios tty; + int speed = B1200; + fd_silitek = fd; + + tcgetattr(fd, &tty); + cfmakeraw(&tty); + cfsetspeed(&tty, speed); + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + tty.c_cflag |= CS8; +#if 0 + tty.c_cflag &= ~(CRTSCTS|CSTOPB|PARODD); + tty.c_cflag |= flags; +#endif + tcsetattr(fd, TCSAFLUSH, &tty); + silitek_mapping(); + + return type; +} + +static Gpm_Type *I_silitek_ps2(int fd, unsigned short flags, struct Gpm_Type *type, int argc, char **argv) +{ + const unsigned char off[] = {GPM_AUX_DISABLE_DEV, }; + const unsigned char buf[] = {GPM_AUX_SET_RES, 3, + GPM_AUX_SET_SCALE11, + GPM_AUX_SET_SAMPLE, 200, + GPM_AUX_ENABLE_DEV, }; + int n, c, err, id; + + fd_silitek = fd; + + (void)write_to_mouse(fd, off, sizeof(off)); + + err=1; + for (n = 0; n < 3 && err; n++) + { + err=0; + c = GPM_AUX_RESET; + write(fd, &c, 1); + read(fd, &c, 1); + if (c != GPM_AUX_ACK) err++; + read(fd, &c, 1); + if (c != 0xAA) err++; + read(fd, &c, 1); + if (c != 0x00) err++; + if (err) usleep(50000); + } + + (void)write_to_mouse(fd, buf, sizeof(buf)); + usleep(50000); + + if ((id = read_mouse_id(fd)) == GPM_AUX_ID_ERROR) + { + if (write_to_mouse(fd, off, sizeof(off))) + gpm_report(GPM_PR_ERR,GPM_MESS_SILIPS2_DISABLE); + + err=1; + for (n = 0; n < 3 && err; n++) + { + err=0; + c = GPM_AUX_RESET; + write(fd, &c, 1); + read(fd, &c, 1); + if (c != GPM_AUX_ACK) err++; + read(fd, &c, 1); + if (c != 0xAA) err++; + read(fd, &c, 1); + if (c != 0x00) err++; + if (err) + usleep(50000); + } + if (err > 0) + gpm_report(GPM_PR_ERR,GPM_MESS_SILIPS2_RESET); + if (write_to_mouse(fd, buf, sizeof(buf))) + gpm_report(GPM_PR_ERR,GPM_MESS_SILIPS2_ENABLE); + id = 0x00; + } + silitek_mapping(); + + return type; +} /* intellimouse, ps2 version: Ben Pfaff and Colin Plumb */ /* Autodetect: Steve Bennett */ @@ -2297,6 +2460,12 @@ {"ps2", "Busmice of the ps/2 series. Most busmice, actually.", "PS/2", M_ps2, I_ps2, STD_FLG, {0xc0, 0x00, 0x00, 0x00}, 3, 1, 0, 0, R_ps2, 1}, + {"silips2","Silitek SM-1000 Netshooter plugged into ps/2 mouse connector.", + "SiliPS/2", M_silitek_ps2, I_silitek_ps2, STD_FLG, + {0x00, 0x00, 0x00, 0x00}, 1, 1, 0, 0, 0, 1}, + {"silicom","Silitek SM-1000 Netshooter plugged into serial connector.", + "SiliCom", M_silitek, I_silitek, STD_FLG, + {0x20, 0x20, 0x00, 0x00}, 1, 1, 0, 0, 0, 0}, {"sun", "'msc' protocol, but only 3 bytes per packet.", "", M_sun, I_serial, CS8 | CSTOPB | STD_FLG, {0xf8, 0x80, 0x00, 0x00}, 3, 1, 0, 0, 0, 0}, --- src/silitek.c +++ src/silitek.c @@ -0,0 +1,580 @@ +/* + * silitek.c - support for the Silitek SM-1000 (Netshooter) with its IR + * receiver plugged serial connector and also with the IR + * receiver of the Silitek SK-7100 keyboard (Airboard) + * PS2 mouse connector. + * + * Copyright 2002 Werner Fink + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* + * Serial settings used herein: raw but I/O with baud rate 1200 and 8bit (YES!) + * For testing, e.g.: stty -a < /dev/ttyS1 + * To set this, e.g.: stty raw 1200 cs8 < /dev/ttyS1 + * For reading keycodes use, e.g.: + * od [-j (1|2)] -v -t x1 --width=3 < /dev/ttyS1 + * compare with I_silitek() in mice.c. + * Which provides following keycodes received by the IR receiver + * included in the SM-1000 shipment for the SM-1000 its self: + * + * First byte: 0xbf (Key down, 8 bit striped this is 0x3f) + * 0x31 (Key hold down) + * 0x2a (Key up) + * 0x7c (Mouse event: move, any button up) + * 0x7f (Mouse event: move, l+r-mouse button down) + * 0xfd (Mouse event: move, r-mouse button down) + * 0xfe (Mouse event: move, l-mouse button down) + * Which provides: Any if (byte0 & 0x20) == 0x20 (for GPM protocol check) + * Key if (byte0 & 0x40) == 0x00 + * Mouse if (byte0 & 0x40) == 0x40 + * Second and third byte (unsigned char, aka 8bits): + * 1: 0xc1 0xfe + * 2: 0xc2 0xfd + * 3: 0x43 0x7c + * 4: 0xc4 0xfb + * 5: 0x45 0x7a + * 6: 0x46 0x79 + * 7: 0xc7 0xf8 + * 8: 0xc8 0xf7 + * 9: 0x49 0x76 + * 0: 0x4a 0x75 + * *: 0xcb 0xf4 + * #: 0x4c 0x73 + * >: 0xcd 0xf2 + * ||: 0xce 0xf1 + * []: 0x4f 0x70 + * >>|: 0xd0 0xef + * |<<: 0x51 0x6e + * Mute: 0x52 0x6d + * Vol+: 0xd3 0xec + * Vol-: 0x54 0x6b + * Display: 0xd5 0xea + * PgUp: 0xd6 0xe9 + * PgDn: 0x57 0x68 + * Back: 0x58 0x67 + * Forward: 0xd9 0xe6 + * CD: 0xda 0xe5 + * ShowWiz: 0x5b 0x64 + * WWW: 0xdc 0xe3 + * Close: 0x5d 0x62 + * Which provides: byte1 & 0x20 == 0x00, byte1 & 0x40 == 0x40 + * (byte1 & 0x7f) & (byte2 & 0x7f) == 0x40 + * (byte1 & 0x7f) | (byte2 & 0x7f) == 0x7f + * (byte1 & 0x1f) == keycode + * + * No mouse move but mouse button: + * 0x80 0x80 + * Mouse move but no button: + * right (byte1 & 0x20) == 0x20 + * left (byte2 & 0x20) == 0x20 + * value (byte1 & 0x1f), (byte2 & 0x1f) + * Mouse move and button: + * OR combinations from above. + * In both bytes, byte1 and byte2, the 0x40 bit seeems not to be used. + * + * PS2 settings used herin: Standard PS2 mouse. + * For reading keycodes use, e.g.: + * od [-j (1|2)] -v -t x1 --width=3 < /dev/psaux + * compare with I_silitek_ps2() in mice.c. + * Which provides following keycodes received by the IR receiver + * included in the SK-7100 shipment for the SM-1000 keys: + * + * First byte: 0xe7 (Key down) + * 0xd7 (Key hold down) + * 0xf7 (Key up) + * byte0 & 0xc0 == 0x00 (PS2 mouse event) + * Which provides: Key if (byte0 & 0xc0) == 0xc0 + * Mouse if (byte0 & 0xc0) == 0x00 (PS2 mouse) + * In other words: No GPM protocol check. + * Second and third byte (unsigned char, aka 8bits): + * 1: 0x01 0xfe + * 2: 0x02 0xfd + * 3: 0x03 0xfc + * 4: 0x04 0xfb + * 5: 0x05 0xfa + * 6: 0x06 0xf9 + * 7: 0x07 0xf8 + * 8: 0x08 0xf7 + * 9: 0x09 0xf6 + * 0: 0x0a 0xf5 + * *: 0x0b 0xf4 + * #: 0x0c 0xf3 + * >: 0x0d 0xf2 + * ||: 0x0e 0xf1 + * []: 0x0f 0xf0 + * >>|: 0x10 0xef + * |<<: 0x11 0xee + * Mute: 0x12 0xed + * Vol+: 0x13 0xec + * Vol-: 0x14 0xeb + * Display: 0x15 0xea + * PgUp: 0x16 0xe9 + * PgDn: 0x17 0xe8 + * Back: 0x18 0xe7 + * Forward: 0x19 0xe6 + * CD: 0x1a 0xe5 + * ShowWiz: 0x1b 0xe4 + * WWW: 0x1c 0xe3 + * Close: 0x1d 0xe2 + * Which provides: byte1 & byte2 == 0x00 + * byte1 | byte2 == 0xff + * byte1 == keycode + * + * Mouse is standard PS2 mouse + */ + +/* + * TODO + * Serial part: Find device settings which avoids changing IR sequences of other + * vendors like from Sony TV commanders. This because to filter + * them out. + * PS2 part: Better device settings which avoids the `ghost' mouse events + * caused by the other keys. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "headers/gpm.h" +#include "headers/silitek.h" +#include "headers/gpmInt.h" +#include "headers/message.h" + +int fd_silitek = -1; + +struct silitek_key_struct { + char *key; + char item[32]; + char drag[32]; +} static K_silitek[] = { + /* TERM=linux */ + { NULL, "", "" }, + {"1", "1", "1" }, + {"2", "2", "2" }, + {"3", "3", "3" }, + {"4", "4", "4" }, + {"5", "5", "5" }, + {"6", "6", "6" }, + {"7", "7", "7" }, + {"8", "8", "8" }, + {"9", "9", "9" }, + {"0", "0", "0" }, + {"*", "*", "*" }, + {"#", "#", "#" }, + {">", "\021", "\021" }, /* Ctrl+Q */ + {"||", "\023", "\023" }, /* Ctrl+S */ + {"[]", "\003", "\003" }, /* Ctrl+C */ + {">>|", "\033[4~", "\033[4~"}, /* End */ + {"|<<", "\033[1~", "\033[1~"}, /* Home */ + {"Mute", "\033[3~", "\033[3~"}, /* Delete */ + {"Vol+", "\033[A", "\033[A" }, /* Cursor up */ + {"Vol-", "\033[B", "\033[B" }, /* Cursor down */ + {"Display", "\033[2~", "\033[2~"}, /* Insert */ + {"PgUp", "\033[5~", "\033[5~"}, /* Page up */ + {"PgDn", "\033[6~", "\033[6~"}, /* Page down */ + {"Back", "\033[D", "\033[D" }, /* Cursor left */ + {"Forward", "\033[C", "\033[C" }, /* Cursor right */ + {"CD", "\033[[A", "\033[[A"}, /* F1 */ + {"ShowWiz", "\033[[B", "\033[[B"}, /* F2 */ + {"WWW", "\033[[C", "\033[[C"}, /* F3 */ + {"Close", "\033[[D", "\033[[D"}, /* F4 */ + { NULL, "", "" } +}; +#define SILITEK_KEYS (sizeof(K_silitek)/sizeof(struct silitek_key_struct)) + +/* From twiddler.c: The same silly function as in gpm.c */ +static inline int open_console(const int mode) +{ + int fd; + extern struct options option; + + if ((fd=open(option.consolename, mode)) < 0) + gpm_report(GPM_PR_OOPS,GPM_MESS_OPEN,option.consolename); + return fd; +} + +static inline int silitek_wait(int timeout) +{ + fd_set Set; + struct timeval tv = {0, timeout}; + int ret = 0; + + do { + FD_ZERO(&Set); + FD_SET(fd_silitek, &Set); + ret = select(fd_silitek+1, &Set, NULL, NULL, &tv); + + } while (ret < 0 && (errno == EINTR || errno == EAGAIN)); + + if (ret < 0 || !(FD_ISSET(fd_silitek, &Set))) + return 0; + return 1; +} + +static inline int silitek_read(unsigned char *data, int timeout) +{ + ssize_t r = 0; + do { + if (!silitek_wait(timeout)) + break; + r = read(fd_silitek, data, 1); + + } while (r < 0 && (errno == EINTR || errno == EAGAIN)); + + if(r != 1) + return 0; + return 1; +} + +int silitek_get_check(unsigned char *data) +{ + if ((data[0] != 0xbf) && /* key down */ + (data[0] != 0x31) && /* key hold down */ + (data[0] != 0x2a) && /* key up */ + (data[0] != 0x7c) && /* mouse event and/or button up */ + (data[0] != 0x7f) && /* mouse event and l+r-mouse button down */ + (data[0] != 0xfd) && /* mouse event and r-mouse button down */ + (data[0] != 0xfe)) /* mouse event and l-mouse button down */ + return 0; + if (!silitek_read(&data[1], 50000)) + return 0; + if (!silitek_read(&data[2], 50000)) + return 0; +#if 0 + /* + * Sony IR + * various, it seems that the bit rate is to high to get stable + * bytes in correct order. Or the protocol of such a TV commander + * is more complicated than those of the SM-1000 because the IR + * receivers of the SM-1000 seems not read this fully correct. + */ + if (data[0] == 0xfe) { + if (data[1] == 0x14 && data[2] == 0x8c) + return 0; + if (data[1] == 0x94 && (data[2] == 0x8c || data[2] == 0x8d)) + return 0; + } +#endif + return 1; +} + +void silitek_keys(unsigned char *data, int *drag) +{ + int ret = 0, unblank = 4, cfd; + int but = (data[1] & 0x7f); + int chk = (data[2] & 0x7f); + int timediff = 0; + char *item; + + if ((but & 0x20) || (but & 0x40) != 0x40) + return; + if ((but&chk) != 0x40 || (but|chk) != 0x7f) + return; + but &= 0x1f; + chk &= 0x1f; + + if ((but|chk) != 0x1f) + return; + if (but < 1 || but > SILITEK_KEYS) + return; + +#define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *)NULL)) +#define DIF_TIME(t1,t2) ((t2.tv_sec-t1.tv_sec)*1000 + (t2.tv_usec-t1.tv_usec)/1000) + + switch(data[0]) { + static struct timeval uptv; + struct timeval tv; + case 0x2a: + uptv.tv_sec = 0; + /* fall through */ + default: + return; + break; + case 0x31: + case 0xbf: + GET_TIME(tv); + if (!uptv.tv_sec) { + uptv.tv_sec = tv.tv_sec; + uptv.tv_usec = tv.tv_usec; + } + timediff = DIF_TIME(uptv, tv); + break; + } + +#undef GET_TIME +#undef DIF_TIME + + if (timediff && timediff < opt_time) + return; + + item = K_silitek[but].item; + if (*drag) { + item = K_silitek[but].drag; + but = 0; + } +#if 0 + *drag = 0; +#endif + + cfd = open_console(O_WRONLY|O_NONBLOCK|O_NOCTTY); + if (cfd < 0) + return; + while (*item && !ret) + ret = ioctl(cfd, TIOCSTI, item++); + ioctl(cfd, TIOCLINUX, &unblank); + close(cfd); +} + +int silitek_get_check_ps2(unsigned char *data) +{ +#if 0 + /* + * Enable this to trace foreign IR bytes sequences received by + * by the IR receiver of the SK-7100 keyboard. + */ + FILE *log; + if (!(log = fopen("/tmp/log", "a"))) + return 0; + fprintf(log, "TV: 0x%.2x 0x%.2x 0x%.2x\n", data[0], data[1], data[2]); + fclose(log); +#endif + if ((data[0] != 0xe7) && /* key down */ + (data[0] != 0xd7) && /* key hold down */ + (data[0] != 0xf7) && /* key up */ + (data[0]&0xc0)) /* PS2 mouse event */ + return 0; + if (!silitek_read(&data[1], 50000)) + return 0; + if (!silitek_read(&data[2], 50000)) + return 0; + /* + * Other IR, current known: + * + * Sony IR + * 0x29 0x0c 0xf4 + * 0x29 0x14 0xf4 + */ + if (data[0] == 0x29 && data[2] == 0xf4 && ((data[1] && 0x0c) || (data[1] && 0x14))) { + return 0; + } + return 1; +} + +void silitek_keys_ps2(unsigned char *data, int *drag) +{ + int ret = 0, unblank = 4, cfd; + int but = data[1]; + int chk = data[2]; + int timediff = 0; + char *item; + + if ((but&chk) && (but|chk) != 0xff) + return; + + if (but < 1 || but > SILITEK_KEYS) + return; + +#define GET_TIME(tv) (gettimeofday(&tv, (struct timezone *)NULL)) +#define DIF_TIME(t1,t2) ((t2.tv_sec-t1.tv_sec)*1000 + (t2.tv_usec-t1.tv_usec)/1000) + + switch(data[0]) { + static struct timeval uptv; + struct timeval tv; + case 0xe7: + uptv.tv_sec = 0; + /* fall through */ + default: + return; + break; + case 0xd7: + case 0xf7: + GET_TIME(tv); + if (!uptv.tv_sec) { + uptv.tv_sec = tv.tv_sec; + uptv.tv_usec = tv.tv_usec; + } + timediff = DIF_TIME(uptv, tv); + break; + } + +#undef GET_TIME +#undef DIF_TIME + + if (timediff && timediff < opt_time) + return; + + item = K_silitek[but].item; + if (*drag) { + item = K_silitek[but].drag; + but = 0; + } +#if 0 + *drag = 0; +#endif + + cfd = open_console(O_WRONLY|O_NONBLOCK|O_NOCTTY); + if (cfd < 0) + return; + while (*item && !ret) + ret = ioctl(cfd, TIOCSTI, item++); + ioctl(cfd, TIOCLINUX, &unblank); + close(cfd); +} + +/* + * Map some ghost mouse PS2 events caused by the other + * keys. Maybe there is a better initialization for the chip + * of the SK-7100 IR receiver which would avoid those events. + */ +int silitek_ghost_ps2(unsigned char *data) +{ + if (data[0] == 0x18 || data[0] == 0x19) { + int but = data[1] - 0xc0; + int chk = data[2] - 0x01; + + if ((but >= 1 && but <= SILITEK_KEYS) && (but == chk)) { + data[0] = 0xf7; + data[1] = but; + data[2] = 0xff - but; + } + } + return 0; +} + +static char ansicstr_ret[SILISTRLEN+1]; +static char* ansicstr(char *string) +{ + int c, conv = 0, o; + char *ptr = string; + char *ret = &ansicstr_ret[0]; + + memset(&ansicstr_ret[0], 0, sizeof(ansicstr_ret)); + while (ptr && *ptr) { + switch (c = *ptr++) { + case '\\': + if (!conv) { + conv = 1; + continue; + } else + c = '\\'; + conv = 0; break; + case 'a': if (conv) c = '\a'; conv = 0; break; + case 'b': if (conv) c = '\b'; conv = 0; break; + case 't': if (conv) c = '\t'; conv = 0; break; + case 'n': if (conv) c = '\n'; conv = 0; break; + case 'v': if (conv) c = '\v'; conv = 0; break; + case 'f': if (conv) c = '\f'; conv = 0; break; + case 'r': if (conv) c = '\r'; conv = 0; break; + case 'e': if (conv) c = '\e'; conv = 0; break; + case 'E': if (conv) c = '\e'; conv = 0; break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + if (!conv) break; + c -= '0'; + for (o = 2; ptr && (*ptr >= '0' && *ptr <= '7' ) && o--; ptr++) + c = (c * 8) + (*ptr - '0'); + conv = 0; break; + case 'x': + if (!conv) break; + for (o = 3; ptr && isxdigit(*ptr) && o--; ptr++) + c = (c * 16) + + ((*ptr >= 'a' && *ptr <= 'f') ? (*ptr - 'a' + 10) : + ((*ptr >= 'A' && *ptr <= 'F') ? (*ptr - 'A' + 10) : (*ptr - '0'))); + if (o == 3) { + *ret++ = '\\'; + c = 'x'; + } + conv = 0; break; + default: + conv = 0; break; + } + *ret++ = c; + } + return &ansicstr_ret[0]; +} + +void silitek_mapping() +{ + static int mapdone = 0; + FILE *conf; + char line[128]; + int k = 1; + + /* Just in case if called twice with -M option */ + if (mapdone) + return; + mapdone = 1; + + if (!(conf = fopen(SILI_SYSTEM_FILE, "r"))) { + if (errno != ENOENT) + gpm_report(GPM_PR_OOPS,GPM_MESS_OPEN,SILI_SYSTEM_FILE); + return; + } + + while (fgets(line, 128, conf)) { + int n; + char key[SILISTRLEN+1], item[SILISTRLEN+1], drag[SILISTRLEN+1]; + /* Comments and empty lines */ + if (line[0] == '\n' || line[0] == '#') + continue; + + if (line[strlen(line) - 1] == '\n') + line[strlen(line) - 1] = '\0'; + while (isspace(line[strlen(line) - 1])) + line[strlen(line) - 1] = '\0'; + + if (line[0] == '\0') + continue; + + key[0] = item[0] = drag[0] = '\0'; + if ((n = sscanf(line, SILISTRSCN, key, item, drag)) > 1 && n < 4) { + while (k < SILITEK_KEYS) { + if (!K_silitek[k].key) { + k = 1; + break; + } + if (!strncasecmp(key, K_silitek[k].key, SILISTRLEN)) { + size_t i = strlen(item), d = strlen(drag); + if (d) { + if (d > SILISTRLEN) + d = SILISTRLEN; + strncpy(K_silitek[k].drag, ansicstr(drag), d); + K_silitek[k].drag[d] = '\0'; + } + if (i) { + if (i > SILISTRLEN) + i = SILISTRLEN; + strncpy(K_silitek[k].item, ansicstr(item), i); + K_silitek[k].item[i] = '\0'; + } + break; + } + k++; + } + } + } +}