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
11
* Sahara Touchit-213 mode added by Claudio Nieder 2008-05-01.
15
* Input line discipline attach program
19
* This program is free software; you can redistribute it and/or modify
20
* it under the terms of the GNU General Public License as published by
21
* the Free Software Foundation; either version 2 of the License, or
22
* (at your option) any later version.
24
* This program is distributed in the hope that it will be useful,
25
* but WITHOUT ANY WARRANTY; without even the implied warranty of
26
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
* GNU General Public License for more details.
29
* You should have received a copy of the GNU General Public License
30
* along with this program; if not, write to the Free Software
31
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33
* Should you need to contact me, the author, you can do so either by
34
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
35
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
41
#include <linux/serio.h>
42
#include "serio-ids.h"
46
#include <sys/ioctl.h>
50
static int readchar(int fd, unsigned char *c, int timeout)
56
tv.tv_usec = timeout * 1000;
61
if (!select(fd + 1, &set, NULL, NULL, &tv))
64
if (read(fd, c, 1) != 1)
70
static void setline(int fd, int flags, int speed)
76
t.c_cflag = flags | CREAD | HUPCL | CLOCAL;
77
t.c_iflag = IGNBRK | IGNPAR;
83
cfsetispeed(&t, speed);
84
cfsetospeed(&t, speed);
86
tcsetattr(fd, TCSANOW, &t);
89
static int logitech_command(int fd, char *c)
94
for (i = 0; c[i]; i++) {
96
if (readchar(fd, &d, 1000))
104
static int magellan_init(int fd, unsigned long *id, unsigned long *extra)
106
write(fd, "m3\rpBB\rz\r", 9);
110
static int warrior_init(int fd, unsigned long *id, unsigned long *extra)
112
if (logitech_command(fd, "*S"))
115
setline(fd, CS8, B4800);
119
static int spaceball_waitchar(int fd, unsigned char c, char *d,
124
while (!readchar(fd, &b, timeout)) {
137
static int spaceball_waitcmd(int fd, char c, char *d)
141
for (i = 0; i < 8; i++) {
142
if (spaceball_waitchar(fd, 0x0d, d, 1000))
151
static int spaceball_cmd(int fd, char *c, char *d)
155
for (i = 0; c[i]; i++)
159
i = spaceball_waitcmd(fd, toupper(c[0]), d);
164
#define SPACEBALL_1003 1
165
#define SPACEBALL_2003B 3
166
#define SPACEBALL_2003C 4
167
#define SPACEBALL_3003C 7
168
#define SPACEBALL_4000FLX 8
169
#define SPACEBALL_4000FLX_L 9
171
static int spaceball_init(int fd, unsigned long *id, unsigned long *extra)
175
if (spaceball_waitchar(fd, 0x11, r, 4000) ||
176
spaceball_waitchar(fd, 0x0d, r, 1000))
179
if (spaceball_waitcmd(fd, '@', r))
182
if (strncmp("@1 Spaceball alive", r, 18))
185
if (spaceball_waitcmd(fd, '@', r))
188
if (spaceball_cmd(fd, "hm", r))
191
if (!strncmp("Hm2003B", r, 7))
192
*id = SPACEBALL_2003B;
193
if (!strncmp("Hm2003C", r, 7))
194
*id = SPACEBALL_2003C;
195
if (!strncmp("Hm3003C", r, 7))
196
*id = SPACEBALL_3003C;
198
if (!strncmp("HvFirmware", r, 10)) {
200
if (spaceball_cmd(fd, "\"", r))
203
if (strncmp("\"1 Spaceball 4000 FLX", r, 21))
206
if (spaceball_waitcmd(fd, '"', r))
209
if (strstr(r, " L "))
210
*id = SPACEBALL_4000FLX_L;
212
*id = SPACEBALL_4000FLX;
214
if (spaceball_waitcmd(fd, '"', r))
217
if (spaceball_cmd(fd, "YS", r))
220
if (spaceball_cmd(fd, "M", r))
226
if (spaceball_cmd(fd, "P@A@A", r) ||
227
spaceball_cmd(fd, "FT@", r) ||
228
spaceball_cmd(fd, "MSS", r))
234
static int stinger_init(int fd, unsigned long *id, unsigned long *extra)
238
unsigned char *response = (unsigned char *)"\r\n0600520058C272";
240
if (write(fd, " E5E5", 5) != 5) /* Enable command */
243
for (i = 0; i < 16; i++) /* Check for Stinger */
244
if (readchar(fd, &c, 200) || c != response[i])
250
static int mzp_init(int fd, unsigned long *id, unsigned long *extra)
252
if (logitech_command(fd, "*X*q"))
255
setline(fd, CS8, B9600);
259
static int newton_init(int fd, unsigned long *id, unsigned long *extra)
263
unsigned char response[35] = {
264
0x16, 0x10, 0x02, 0x64, 0x5f, 0x69, 0x64, 0x00,
265
0x00, 0x00, 0x0c, 0x6b, 0x79, 0x62, 0x64, 0x61,
266
0x70, 0x70, 0x6c, 0x00, 0x00, 0x00, 0x01, 0x6e,
267
0x6f, 0x66, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x10,
271
for (i = 0; i < sizeof(response); i++)
272
if (readchar(fd, &c, 400) || c != response[i])
278
static int twiddler_init(int fd, unsigned long *id, unsigned long *extra)
283
/* Turn DTR off, otherwise the Twiddler won't send any data. */
284
if (ioctl(fd, TIOCMGET, &line))
287
if (ioctl(fd, TIOCMSET, &line))
291
* Check whether the device on the serial line is the Twiddler.
293
* The Twiddler sends data packets of 5 bytes which have the following
294
* properties: the MSB is 0 on the first and 1 on all other bytes, and
295
* the high order nibble of the last byte is always 0x8.
297
* We read and check two of those 5 byte packets to be sure that we
298
* are indeed talking to a Twiddler.
301
/* Read at most 5 bytes until we find one with the MSB set to 0 */
302
for (count = 0; count < 5; count++) {
303
if (readchar(fd, c, 500))
305
if ((c[0] & 0x80) == 0)
310
/* Could not find header byte in data stream */
314
/* Read remaining 4 bytes plus the full next data packet */
315
for (count = 1; count < 10; count++)
316
if (readchar(fd, c + count, 500))
319
/* Check whether the bytes of both data packets obey the rules */
320
for (count = 1; count < 10; count++) {
321
if ((count % 5 == 0 && (c[count] & 0x80) != 0x00) ||
322
(count % 5 == 4 && (c[count] & 0xF0) != 0x80) ||
323
(count % 5 != 0 && (c[count] & 0x80) != 0x80)) {
324
/* Invalid byte in data packet */
332
static int fujitsu_init(int fd, unsigned long *id, unsigned long *extra)
334
unsigned char cmd, data;
336
/* Wake up the touchscreen */
337
cmd = 0xff; /* Dummy data */;
338
if (write(fd, &cmd, 1) != 1)
341
/* Wait to settle down */
342
usleep(100 * 1000); /* 100 ms */
344
/* Reset the touchscreen */
345
cmd = 0x81; /* Cold reset */
346
if (write(fd, &cmd, 1) != 1)
350
if (readchar(fd, &data, 100) || (data & 0xbf) != 0x90)
354
if (readchar(fd, &data, 100) || data != 0x00)
360
static int t213_init(int fd, unsigned long *id, unsigned long *extra)
362
char cmd[]={0x0a,1,'A'};
368
* In case the controller is in "ELO-mode" send a few times
369
* the check active packet to force it into the documented
373
if (write(fd, &cmd, 3) != 3)
375
while (!readchar(fd, &data, 100)) {
385
} else if (data!=0x0a) {
392
} else if (data==0x0a) {
406
static int dump_init(int fd, unsigned long *id, unsigned long *extra)
408
unsigned char c, o = 0;
412
if (write(fd, &c, 1) != 1) /* Enable command */
416
if (!readchar(fd, &c, 1)) {
417
printf("%02x (%c) ", c, ((c > 32) && (c < 127)) ? c : 'x');
437
int (*init)(int fd, unsigned long *id, unsigned long *extra);
440
static struct input_types input_types[] = {
441
{ "--sunkbd", "-skb", "Sun Type 4 and Type 5 keyboards",
443
SERIO_SUNKBD, 0x00, 0x00, 1, NULL },
444
{ "--lkkbd", "-lk", "DEC LK201 / LK401 keyboards",
446
SERIO_LKKBD, 0x00, 0x00, 1, NULL },
447
{ "--vsxxx-aa", "-vs",
448
"DEC VSXXX-AA / VSXXX-GA mouse and VSXXX-A tablet",
449
B4800, CS8|CSTOPB|PARENB|PARODD,
450
SERIO_VSXXXAA, 0x00, 0x00, 1, NULL },
451
{ "--spaceorb", "-orb", "SpaceOrb 360 / SpaceBall Avenger",
453
SERIO_SPACEORB, 0x00, 0x00, 1, NULL },
454
{ "--spaceball", "-sbl", "SpaceBall 2003 / 3003 / 4000 FLX",
456
SERIO_SPACEBALL, 0x00, 0x00, 0, spaceball_init },
457
{ "--magellan", "-mag", "Magellan / SpaceMouse",
458
B9600, CS8 | CSTOPB | CRTSCTS,
459
SERIO_MAGELLAN, 0x00, 0x00, 1, magellan_init },
460
{ "--warrior", "-war", "WingMan Warrior",
462
SERIO_WARRIOR, 0x00, 0x00, 1, warrior_init },
463
{ "--stinger", "-sting", "Gravis Stinger",
465
SERIO_STINGER, 0x00, 0x00, 1, stinger_init },
466
{ "--mousesystems", "-msc", "3-button Mouse Systems mouse",
468
SERIO_MSC, 0x00, 0x01, 1, NULL },
469
{ "--sunmouse", "-sun", "3-button Sun mouse",
471
SERIO_SUN, 0x00, 0x01, 1, NULL },
472
{ "--microsoft", "-bare", "2-button Microsoft mouse",
474
SERIO_MS, 0x00, 0x00, 1, NULL },
475
{ "--mshack", "-ms", "3-button mouse in Microsoft mode",
477
SERIO_MS, 0x00, 0x01, 1, NULL },
478
{ "--mouseman", "-mman", "3-button Logitech / Genius mouse",
480
SERIO_MP, 0x00, 0x01, 1, NULL },
481
{ "--intellimouse", "-ms3", "Microsoft IntelliMouse",
483
SERIO_MZ, 0x00, 0x11, 1, NULL },
484
{ "--mmwheel", "-mmw",
485
"Logitech mouse with 4-5 buttons or a wheel",
487
SERIO_MZP, 0x00, 0x13, 1, mzp_init },
488
{ "--iforce", "-ifor", "I-Force joystick or wheel",
490
SERIO_IFORCE, 0x00, 0x00, 0, NULL },
491
{ "--newtonkbd", "-newt", "Newton keyboard",
493
SERIO_NEWTON, 0x00, 0x00, 1, newton_init },
494
{ "--h3600ts", "-ipaq", "Ipaq h3600 touchscreen",
496
SERIO_H3600, 0x00, 0x00, 0, NULL },
497
{ "--stowawaykbd", "-ipaqkbd", "Stowaway keyboard",
499
SERIO_STOWAWAY, 0x00, 0x00, 1, NULL },
500
{ "--ps2serkbd", "-ps2ser", "PS/2 via serial keyboard",
502
SERIO_PS2SER, 0x00, 0x00, 1, NULL },
503
{ "--twiddler", "-twid", "Handykey Twiddler chording keyboard",
505
SERIO_TWIDKBD, 0x00, 0x00, 0, twiddler_init },
506
{ "--twiddler-joy", "-twidjoy", "Handykey Twiddler used as a joystick",
508
SERIO_TWIDJOY, 0x00, 0x00, 0, twiddler_init },
509
{ "--elotouch", "-elo", "ELO touchscreen, 10-byte mode",
510
B9600, CS8 | CRTSCTS,
511
SERIO_ELO, 0x00, 0x00, 0, NULL },
512
{ "--elo4002", "-elo6b", "ELO touchscreen, 6-byte mode",
513
B9600, CS8 | CRTSCTS,
514
SERIO_ELO, 0x01, 0x00, 0, NULL },
515
{ "--elo271-140", "-elo4b", "ELO touchscreen, 4-byte mode",
516
B9600, CS8 | CRTSCTS,
517
SERIO_ELO, 0x02, 0x00, 0, NULL },
518
{ "--elo261-280", "-elo3b", "ELO Touchscreen, 3-byte mode",
519
B9600, CS8 | CRTSCTS,
520
SERIO_ELO, 0x03, 0x00, 0, NULL },
521
{ "--mtouch", "-mtouch", "MicroTouch (3M) touchscreen",
522
B9600, CS8 | CRTSCTS,
523
SERIO_MICROTOUCH, 0x00, 0x00, 0, NULL },
524
{ "--touchit213", "-t213", "Sahara Touch-iT213 Tablet PC",
526
SERIO_TOUCHIT213, 0x00, 0x00, 0, t213_init },
527
{ "--touchright", "-tr", "Touchright serial touchscreen",
528
B9600, CS8 | CRTSCTS,
529
SERIO_TOUCHRIGHT, 0x00, 0x00, 0, NULL },
530
{ "--touchwin", "-tw", "Touchwindow serial touchscreen",
531
B4800, CS8 | CRTSCTS,
532
SERIO_TOUCHWIN, 0x00, 0x00, 0, NULL },
533
{ "--penmount", "-pm", "Penmount touchscreen",
534
B19200, CS8 | CRTSCTS,
535
SERIO_PENMOUNT, 0x00, 0x00, 0, NULL },
536
{ "--fujitsu", "-fjt", "Fujitsu serial touchscreen",
538
SERIO_FUJITSU, 0x00, 0x00, 1, fujitsu_init },
539
{ "--dump", "-dump", "Just enable device",
541
0, 0x00, 0x00, 0, dump_init },
542
{ NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, NULL }
545
static void show_help(void)
547
struct input_types *type;
550
puts("Usage: inputattach [--daemon] [--always] [--noinit] <mode> <device>");
554
for (type = input_types; type->name; type++)
555
printf(" %-16s %-8s %s\n",
556
type->name, type->name2, type->desc);
561
int main(int argc, char **argv)
565
struct input_types *type = NULL;
566
const char *device = NULL;
569
unsigned long id, extra;
574
int ignore_init_res = 0;
577
for (i = 1; i < argc; i++) {
578
if (!strcasecmp(argv[i], "--help")) {
581
} else if (!strcasecmp(argv[i], "--daemon")) {
583
} else if (!strcasecmp(argv[i], "--always")) {
585
} else if (!strcasecmp(argv[i], "--noinit")) {
587
} else if (need_device) {
591
if (type && type->name) {
593
"inputattach: '%s' - "
594
"only one mode allowed\n", argv[i]);
597
for (type = input_types; type->name; type++) {
598
if (!strcasecmp(argv[i], type->name) ||
599
!strcasecmp(argv[i], type->name2)) {
605
"inputattach: invalid mode '%s'\n",
613
if (!type || !type->name) {
614
fprintf(stderr, "inputattach: must specify mode\n");
619
fprintf(stderr, "inputattach: must specify device\n");
623
fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
625
fprintf(stderr, "inputattach: '%s' - %s\n",
626
device, strerror(errno));
630
setline(fd, type->flags, type->speed);
633
while (!readchar(fd, &c, 100))
639
if (type->init && !no_init) {
640
if (type->init(fd, &id, &extra)) {
641
if (ignore_init_res) {
642
fprintf(stderr, "inputattach: ignored device initialization failure\n");
644
fprintf(stderr, "inputattach: device initialization failed\n");
651
if (ioctl(fd, TIOCSETD, &ldisc)) {
652
fprintf(stderr, "inputattach: can't set line discipline\n");
656
devt = type->type | (id << 8) | (extra << 16);
658
if (ioctl(fd, SPIOCSTYPE, &devt)) {
659
fprintf(stderr, "inputattach: can't set device type\n");
663
retval = EXIT_SUCCESS;
664
if (daemon_mode && daemon(0, 0) < 0) {
665
perror("inputattach");
666
retval = EXIT_FAILURE;
672
ioctl(fd, TIOCSETD, &ldisc);