2 * $Id: inputattach.c,v 1.24 2006/02/08 12:19:31 vojtech Exp $
4 * Copyright (c) 1999-2000 Vojtech Pavlik
8 * Twiddler support Copyright (c) 2001 Arndt Schoenewald
9 * Sponsored by Quelltext AG (http://www.quelltext-ag.de), Dortmund, Germany
13 * Input line discipline attach program
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 * Should you need to contact me, the author, you can do so either by
32 * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
33 * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
36 #include <linux/serio.h>
38 #include <sys/types.h>
40 #include <sys/ioctl.h>
53 #include "serio-ids.h"
55 static int readchar(int fd
, unsigned char *c
, int timeout
)
61 tv
.tv_usec
= timeout
* 1000;
66 if (!select(fd
+ 1, &set
, NULL
, NULL
, &tv
))
69 if (read(fd
, c
, 1) != 1)
75 static void setline(int fd
, int flags
, int speed
)
81 t
.c_cflag
= flags
| CREAD
| HUPCL
| CLOCAL
;
82 t
.c_iflag
= IGNBRK
| IGNPAR
;
88 cfsetispeed(&t
, speed
);
89 cfsetospeed(&t
, speed
);
91 tcsetattr(fd
, TCSANOW
, &t
);
94 static int logitech_command(int fd
, char *c
)
99 for (i
= 0; c
[i
]; i
++) {
101 if (readchar(fd
, &d
, 1000))
109 static int magellan_init(int fd
, unsigned long *id
, unsigned long *extra
)
111 write(fd
, "m3\rpBB\rz\r", 9);
115 static int warrior_init(int fd
, unsigned long *id
, unsigned long *extra
)
117 if (logitech_command(fd
, "*S"))
120 setline(fd
, CS8
, B4800
);
124 static int spaceball_waitchar(int fd
, unsigned char c
, unsigned char *d
,
129 while (!readchar(fd
, &b
, timeout
)) {
142 static int spaceball_waitcmd(int fd
, char c
, char *d
)
146 for (i
= 0; i
< 8; i
++) {
147 if (spaceball_waitchar(fd
, 0x0d, d
, 1000))
156 static int spaceball_cmd(int fd
, char *c
, char *d
)
160 for (i
= 0; c
[i
]; i
++)
164 i
= spaceball_waitcmd(fd
, toupper(c
[0]), d
);
169 #define SPACEBALL_1003 1
170 #define SPACEBALL_2003B 3
171 #define SPACEBALL_2003C 4
172 #define SPACEBALL_3003C 7
173 #define SPACEBALL_4000FLX 8
174 #define SPACEBALL_4000FLX_L 9
176 static int spaceball_init(int fd
, unsigned long *id
, unsigned long *extra
)
180 if (spaceball_waitchar(fd
, 0x11, r
, 4000) ||
181 spaceball_waitchar(fd
, 0x0d, r
, 1000))
184 if (spaceball_waitcmd(fd
, '@', r
))
187 if (strncmp("@1 Spaceball alive", r
, 18))
190 if (spaceball_waitcmd(fd
, '@', r
))
193 if (spaceball_cmd(fd
, "hm", r
))
196 if (!strncmp("Hm2003B", r
, 7))
197 *id
= SPACEBALL_2003B
;
198 if (!strncmp("Hm2003C", r
, 7))
199 *id
= SPACEBALL_2003C
;
200 if (!strncmp("Hm3003C", r
, 7))
201 *id
= SPACEBALL_3003C
;
203 if (!strncmp("HvFirmware", r
, 10)) {
205 if (spaceball_cmd(fd
, "\"", r
))
208 if (strncmp("\"1 Spaceball 4000 FLX", r
, 21))
211 if (spaceball_waitcmd(fd
, '"', r
))
214 if (strstr(r
, " L "))
215 *id
= SPACEBALL_4000FLX_L
;
217 *id
= SPACEBALL_4000FLX
;
219 if (spaceball_waitcmd(fd
, '"', r
))
222 if (spaceball_cmd(fd
, "YS", r
))
225 if (spaceball_cmd(fd
, "M", r
))
231 if (spaceball_cmd(fd
, "P@A@A", r
) ||
232 spaceball_cmd(fd
, "FT@", r
) ||
233 spaceball_cmd(fd
, "MSS", r
))
239 static int stinger_init(int fd
, unsigned long *id
, unsigned long *extra
)
243 unsigned char *response
= "\r\n0600520058C272";
245 if (write(fd
, " E5E5", 5) != 5) /* Enable command */
248 for (i
= 0; i
< 16; i
++) /* Check for Stinger */
249 if (readchar(fd
, &c
, 200) || c
!= response
[i
])
255 static int mzp_init(int fd
, unsigned long *id
, unsigned long *extra
)
257 if (logitech_command(fd
, "*X*q"))
260 setline(fd
, CS8
, B9600
);
264 static int newton_init(int fd
, unsigned long *id
, unsigned long *extra
)
268 unsigned char response
[35] = {
269 0x16, 0x10, 0x02, 0x64, 0x5f, 0x69, 0x64, 0x00,
270 0x00, 0x00, 0x0c, 0x6b, 0x79, 0x62, 0x64, 0x61,
271 0x70, 0x70, 0x6c, 0x00, 0x00, 0x00, 0x01, 0x6e,
272 0x6f, 0x66, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x10,
276 for (i
= 0; i
< sizeof(response
); i
++)
277 if (readchar(fd
, &c
, 400) || c
!= response
[i
])
283 static int twiddler_init(int fd
, unsigned long *id
, unsigned long *extra
)
288 /* Turn DTR off, otherwise the Twiddler won't send any data. */
289 if (ioctl(fd
, TIOCMGET
, &line
))
292 if (ioctl(fd
, TIOCMSET
, &line
))
296 * Check whether the device on the serial line is the Twiddler.
298 * The Twiddler sends data packets of 5 bytes which have the following
299 * properties: the MSB is 0 on the first and 1 on all other bytes, and
300 * the high order nibble of the last byte is always 0x8.
302 * We read and check two of those 5 byte packets to be sure that we
303 * are indeed talking to a Twiddler.
306 /* Read at most 5 bytes until we find one with the MSB set to 0 */
307 for (count
= 0; count
< 5; count
++) {
308 if (readchar(fd
, c
, 500))
310 if ((c
[0] & 0x80) == 0)
315 /* Could not find header byte in data stream */
319 /* Read remaining 4 bytes plus the full next data packet */
320 for (count
= 1; count
< 10; count
++)
321 if (readchar(fd
, c
+ count
, 500))
324 /* Check whether the bytes of both data packets obey the rules */
325 for (count
= 1; count
< 10; count
++) {
326 if ((count
% 5 == 0 && (c
[count
] & 0x80) != 0x00) ||
327 (count
% 5 == 4 && (c
[count
] & 0xF0) != 0x80) ||
328 (count
% 5 != 0 && (c
[count
] & 0x80) != 0x80)) {
329 /* Invalid byte in data packet */
337 static int fujitsu_init(int fd
, unsigned long *id
, unsigned long *extra
)
339 unsigned char cmd
, data
;
341 /* Wake up the touchscreen */
342 cmd
= 0xff; /* Dummy data */;
343 if (write(fd
, &cmd
, 1) != 1)
346 /* Wait to settle down */
347 usleep(100 * 1000); /* 100 ms */
349 /* Reset the touchscreen */
350 cmd
= 0x81; /* Cold reset */
351 if (write(fd
, &cmd
, 1) != 1)
355 if (readchar(fd
, &data
, 100) || (data
& 0xbf) != 0x90)
359 if (readchar(fd
, &data
, 100) || data
!= 0x00)
365 static int dump_init(int fd
, unsigned long *id
, unsigned long *extra
)
367 unsigned char c
, o
= 0;
371 if (write(fd
, &c
, 1) != 1) /* Enable command */
375 if (!readchar(fd
, &c
, 1)) {
376 printf("%02x (%c) ", c
, ((c
> 32) && (c
< 127)) ? c
: 'x');
396 int (*init
)(int fd
, unsigned long *id
, unsigned long *extra
);
399 static struct input_types input_types
[] = {
400 { "--sunkbd", "-skb", "Sun Type 4 and Type 5 keyboards",
402 SERIO_SUNKBD
, 0x00, 0x00, 1, NULL
},
403 { "--lkkbd", "-lk", "DEC LK201 / LK401 keyboards",
405 SERIO_LKKBD
, 0x00, 0x00, 1, NULL
},
406 { "--vsxxx-aa", "-vs",
407 "DEC VSXXX-AA / VSXXX-GA mouse and VSXXX-A tablet",
408 B4800
, CS8
|CSTOPB
|PARENB
|PARODD
,
409 SERIO_VSXXXAA
, 0x00, 0x00, 1, NULL
},
410 { "--spaceorb", "-orb", "SpaceOrb 360 / SpaceBall Avenger",
412 SERIO_SPACEORB
, 0x00, 0x00, 1, NULL
},
413 { "--spaceball", "-sbl", "SpaceBall 2003 / 3003 / 4000 FLX",
415 SERIO_SPACEBALL
, 0x00, 0x00, 0, spaceball_init
},
416 { "--magellan", "-mag", "Magellan / SpaceMouse",
417 B9600
, CS8
| CSTOPB
| CRTSCTS
,
418 SERIO_MAGELLAN
, 0x00, 0x00, 1, magellan_init
},
419 { "--warrior", "-war", "WingMan Warrior",
421 SERIO_WARRIOR
, 0x00, 0x00, 1, warrior_init
},
422 { "--stinger", "-sting", "Gravis Stinger",
424 SERIO_STINGER
, 0x00, 0x00, 1, stinger_init
},
425 { "--mousesystems", "-msc", "3-button Mouse Systems mouse",
427 SERIO_MSC
, 0x00, 0x01, 1, NULL
},
428 { "--sunmouse", "-sun", "3-button Sun mouse",
430 SERIO_SUN
, 0x00, 0x01, 1, NULL
},
431 { "--microsoft", "-bare", "2-button Microsoft mouse",
433 SERIO_MS
, 0x00, 0x00, 1, NULL
},
434 { "--mshack", "-ms", "3-button mouse in Microsoft mode",
436 SERIO_MS
, 0x00, 0x01, 1, NULL
},
437 { "--mouseman", "-mman", "3-button Logitech / Genius mouse",
439 SERIO_MP
, 0x00, 0x01, 1, NULL
},
440 { "--intellimouse", "-ms3", "Microsoft IntelliMouse",
442 SERIO_MZ
, 0x00, 0x11, 1, NULL
},
443 { "--mmwheel", "-mmw",
444 "Logitech mouse with 4-5 buttons or a wheel",
446 SERIO_MZP
, 0x00, 0x13, 1, mzp_init
},
447 { "--iforce", "-ifor", "I-Force joystick or wheel",
449 SERIO_IFORCE
, 0x00, 0x00, 0, NULL
},
450 { "--newtonkbd", "-newt", "Newton keyboard",
452 SERIO_NEWTON
, 0x00, 0x00, 1, newton_init
},
453 { "--h3600ts", "-ipaq", "Ipaq h3600 touchscreen",
455 SERIO_H3600
, 0x00, 0x00, 0, NULL
},
456 { "--stowawaykbd", "-ipaqkbd", "Stowaway keyboard",
458 SERIO_STOWAWAY
, 0x00, 0x00, 1, NULL
},
459 { "--ps2serkbd", "-ps2ser", "PS/2 via serial keyboard",
461 SERIO_PS2SER
, 0x00, 0x00, 1, NULL
},
462 { "--twiddler", "-twid", "Handykey Twiddler chording keyboard",
464 SERIO_TWIDKBD
, 0x00, 0x00, 0, twiddler_init
},
465 { "--twiddler-joy", "-twidjoy", "Handykey Twiddler used as a joystick",
467 SERIO_TWIDJOY
, 0x00, 0x00, 0, twiddler_init
},
468 { "--elotouch", "-elo", "ELO touchscreen, 10-byte mode",
469 B9600
, CS8
| CRTSCTS
,
470 SERIO_ELO
, 0x00, 0x00, 0, NULL
},
471 { "--elo4002", "-elo6b", "ELO touchscreen, 6-byte mode",
472 B9600
, CS8
| CRTSCTS
,
473 SERIO_ELO
, 0x01, 0x00, 0, NULL
},
474 { "--elo271-140", "-elo4b", "ELO touchscreen, 4-byte mode",
475 B9600
, CS8
| CRTSCTS
,
476 SERIO_ELO
, 0x02, 0x00, 0, NULL
},
477 { "--elo261-280", "-elo3b", "ELO Touchscreen, 3-byte mode",
478 B9600
, CS8
| CRTSCTS
,
479 SERIO_ELO
, 0x03, 0x00, 0, NULL
},
480 { "--mtouch", "-mtouch", "MicroTouch (3M) touchscreen",
481 B9600
, CS8
| CRTSCTS
,
482 SERIO_MICROTOUCH
, 0x00, 0x00, 0, NULL
},
483 { "--touchright", "-tr", "Touchright serial touchscreen",
484 B9600
, CS8
| CRTSCTS
,
485 SERIO_TOUCHRIGHT
, 0x00, 0x00, 0, NULL
},
486 { "--touchwin", "-tw", "Touchwindow serial touchscreen",
487 B4800
, CS8
| CRTSCTS
,
488 SERIO_TOUCHWIN
, 0x00, 0x00, 0, NULL
},
489 { "--penmount", "-pm", "Penmount touchscreen",
490 B19200
, CS8
| CRTSCTS
,
491 SERIO_PENMOUNT
, 0x00, 0x00, 0, NULL
},
492 { "--fujitsu", "-fjt", "Fujitsu serial touchscreen",
494 SERIO_FUJITSU
, 0x00, 0x00, 1, fujitsu_init
},
495 { "--inexio", "-inx", "iNexio serial touchscreen",
496 B19200
, CS8
|CREAD
|CLOCAL
|HUPCL
,
497 SERIO_INEXIO
, 0x00, 0x00, 1, NULL
},
498 { "--dump", "-dump", "Just enable device",
500 0, 0x00, 0x00, 0, dump_init
},
501 { NULL
, NULL
, NULL
, 0, 0, 0, 0, 0, 0, NULL
}
504 static void show_help(void)
506 struct input_types
*type
;
509 puts("Usage: inputattach [--daemon] <mode> <device>");
513 for (type
= input_types
; type
->name
; type
++)
514 printf(" %-16s %-8s %s\n",
515 type
->name
, type
->name2
, type
->desc
);
520 int main(int argc
, char **argv
)
524 struct input_types
*type
= NULL
;
525 const char *device
= NULL
;
528 unsigned long id
, extra
;
534 for (i
= 1; i
< argc
; i
++) {
535 if (!strcasecmp(argv
[i
], "--help")) {
538 } else if (!strcasecmp(argv
[i
], "--daemon")) {
540 } else if (need_device
) {
544 if (type
&& type
->name
) {
546 "inputattach: '%s' - "
547 "only one mode allowed\n", argv
[i
]);
550 for (type
= input_types
; type
->name
; type
++) {
551 if (!strcasecmp(argv
[i
], type
->name
) ||
552 !strcasecmp(argv
[i
], type
->name2
)) {
558 "inputattach: invalid mode '%s'\n",
566 if (!type
|| !type
->name
) {
567 fprintf(stderr
, "inputattach: must specify mode\n");
572 fprintf(stderr
, "inputattach: must specify device\n");
576 fd
= open(device
, O_RDWR
| O_NOCTTY
| O_NONBLOCK
);
578 fprintf(stderr
, "inputattach: '%s' - %s\n",
579 device
, strerror(errno
));
583 setline(fd
, type
->flags
, type
->speed
);
586 while (!readchar(fd
, &c
, 100))
592 if (type
->init
&& type
->init(fd
, &id
, &extra
)) {
593 fprintf(stderr
, "inputattach: device initialization failed\n");
598 if (ioctl(fd
, TIOCSETD
, &ldisc
)) {
599 fprintf(stderr
, "inputattach: can't set line discipline\n");
603 devt
= type
->type
| (id
<< 8) | (extra
<< 16);
605 if (ioctl(fd
, SPIOCSTYPE
, &devt
)) {
606 fprintf(stderr
, "inputattach: can't set device type\n");
610 retval
= EXIT_SUCCESS
;
611 if (daemon_mode
&& daemon(0, 0) < 0) {
612 perror("inputattach");
613 retval
= EXIT_FAILURE
;
619 ioctl(fd
, TIOCSETD
, &ldisc
);