2 * --- T2-COPYRIGHT-NOTE-BEGIN ---
3 * T2 SDE: package/.../serpnp/serpnp.c
4 * Copyright (C) 2004 - 2022 The T2 SDE Project
6 * This Copyright note is generated by scripts/Create-CopyPatch,
7 * more information can be found in the files COPYING and README.
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2.
11 * --- T2-COPYRIGHT-NOTE-END ---
15 * Serial / COM PnP evaluation as defined in:
16 * "Plug and Play External COM Device Specification"
17 * by Microsoft Corporation & Hayes Microcomputer Products, Inc.
18 * http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/pnpcom.rtf
20 * Copyright 2005 by René Rebe
22 * inspired partly by the X.org mouse/pnp code:
23 * Copyright 1998 by Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
33 #include <sys/types.h>
37 #include <sys/ioctl.h>
45 /* PnP EISA/product IDs */
46 static symtab_t pnpsymtab
[] = {
48 { "KML0001", PROT_THINKING
}, /* Kensignton ThinkingMouse */
50 { "MSH0001", "-ms3" }, /* MS IntelliMouse */
51 { "MSH0004", "-ms3" }, /* MS IntelliMouse TrackBall */
52 { "KYEEZ00", "-ms" }, /* Genius EZScroll */
53 { "KYE0001", "-ms" }, /* Genius PnP Mouse */
54 { "KYE0002", "-ms" }, /* MouseSystem (Genius?) SmartScroll */
55 { "KYE0003", "-ms3" }, /* Genius NetMouse */
56 { "LGI800", "-ms" }, /* Logitech FirstMouse+ on ReneR's desk */
57 { "LGI800C", "-ms3" }, /* Logitech MouseMan (4 button model) */
58 { "LGI8033", "-ms3" }, /* Logitech Cordless MouseMan Wheel */
59 { "LGI8050", "-ms3" }, /* Logitech MouseMan+ */
60 { "LGI8051", "-ms3" }, /* Logitech FirstMouse+ */
61 { "LGI8001", "-ms" }, /* Logitech serial */ // was -mman -ReneR
62 { "A4W0005", "-ms3" }, /* A4 Tech 4D/4D+ Mouse */
63 { "PEC9802", "-ms3" }, /* 8D Scroll Mouse */
66 { "PNP0F00", PROT_BM
}, /* MS bus */
68 { "PNP0F01", "-ms" }, /* MS serial */
70 { "PNP0F02", PROT_BM
}, /* MS InPort */
73 * EzScroll returns PNP0F04 in the compatible device field; but it
74 * doesn't look compatible... XXX
76 { "PNP0F04", "-msc" }, /* MouseSystems */
77 { "PNP0F05", "-msc" }, /* MouseSystems */
79 { "PNP0F06", PROT_
??? }, /* Genius Mouse */
80 { "PNP0F07", PROT_
??? }, /* Genius Mouse */
82 { "PNP0F08", "-mman" }, /* Logitech serial */
83 { "PNP0F09", "-ms" }, /* MS BallPoint serial */
84 { "PNP0F0A", "-ms" }, /* MS PnP serial */
85 { "PNP0F0B", "-ms" }, /* MS PnP BallPoint serial */
86 { "PNP0F0C", "-ms" }, /* MS serial comatible */
88 { "PNP0F0D", PROT_BM
}, /* MS InPort comatible */
90 { "PNP0F0F", "-ms" }, /* MS BallPoint comatible */
92 { "PNP0F10", PROT_
??? }, /* TI QuickPort */
93 { "PNP0F11", PROT_BM
}, /* MS bus comatible */
94 { "PNP0F14", PROT_
??? }, /* MS Kids Mouse */
95 { "PNP0F15", PROT_BM
}, /* Logitech bus */
96 { "PNP0F16", PROT_
??? }, /* Logitech SWIFT */
98 { "PNP0F17", "-mman" }, /* Logitech serial compat */
100 { "PNP0F18", PROT_BM
}, /* Logitech bus compatible */
101 { "PNP0F1A", PROT_
??? }, /* Logitech SWIFT compatible */
102 { "PNP0F1B", PROT_
??? }, /* HP Omnibook */
103 { "PNP0F1C", PROT_
??? }, /* Compaq LTE TrackBall PS/2 */
104 { "PNP0F1D", PROT_
??? }, /* Compaq LTE TrackBall serial */
105 { "PNP0F1E", PROT_
??? }, /* MS Kids Trackball */
110 /* serial PnP ID string */
112 int revision
; /* PnP revision, 100 for 1.00 */
113 char* eisaid
; /* EISA ID including mfr ID and product ID */
114 char* serial
; /* serial No, optional */
115 char* devclass
; /* device class, optional */
116 char* compat
; /* list of compatible drivers, optional */
117 char* description
; /* product description, optional */
118 int neisaid
; /* length of the above fields... */
125 bool pnpparse (pnpid_t
* id
, char* buf
, int len
)
137 id
->description
= NULL
;
142 id
->ndescription
= 0;
144 offset
= 0x28 - buf
[0];
146 /* calculate checksum */
147 for (i
= 0; i
< len
- 3; ++i
) {
154 //printf ("PnP ID string: `%*.*s'\n", len, len, buf);
159 id
->revision
= ((buf
[1] & 0x3f) << 6) | (buf
[2] & 0x3f);
160 //printf ("PnP rev %d.%02d\n", id->revision / 100, id->revision % 100);
162 /* EISA vender and product ID */
163 id
->eisaid
= &buf
[3];
166 // workaround for my Logitech mice? only has 6 ... -ReneR
167 if (id
->eisaid
[id
->neisaid
-1] == ')')
172 if (buf
[i
] == '\\') {
173 /* device serial # */
174 for (j
= ++i
; i
< len
; ++i
) {
181 id
->serial
= &buf
[j
];
185 if (buf
[i
] == '\\') {
187 for (j
= ++i
; i
< len
; ++i
) {
194 id
->devclass
= &buf
[j
];
195 id
->ndevclass
= i
- j
;
198 if (buf
[i
] == '\\') {
199 /* compatible driver */
200 for (j
= ++i
; i
< len
; ++i
) {
205 * PnP COM spec prior to v0.96 allowed '*' in this field,
206 * it's not allowed now; just ignore it.
213 id
->compat
= &buf
[j
];
217 if (buf
[i
] == '\\') {
218 /* product description */
219 for (j
= ++i
; i
< len
; ++i
) {
226 id
->description
= &buf
[j
];
227 id
->ndescription
= i
- j
;
231 /* checksum exists if there are any optional fields */
232 if ((id
->nserial
> 0) || (id
->ndevclass
> 0)
233 || (id
->ncompat
> 0) || (id
->ndescription
> 0)) {
234 printf ("PnP checksum: 0x%02X\n", sum
);
235 sprintf(s
, "%02X", sum
& 0x0ff);
236 if (strncmp(s
, &buf
[len
- 3], 2) != 0) {
237 printf ("checksum error!");
241 * I found some mice do not comply with the PnP COM device
242 * spec regarding checksum... XXX
253 struct termios oldserial_io
;
257 printf ("Resetting port ...\n");
258 tcsetattr(tty
, TCSANOW
, &oldserial_io
);
261 int main (int argc
, char* argv
[])
263 struct termios newserial_io
;
266 printf("Usage: %s devname\n", argv
[0]);
270 //open Serialport for reading and writing
271 tty
= open (argv
[1], O_RDWR
| O_NOCTTY
);
274 perror("serial port");
278 // save current serial port settings
279 tcgetattr(tty
, &oldserial_io
);
280 tcgetattr(tty
, &newserial_io
);
284 newserial_io
.c_lflag
&= ~(ICANON
| ECHO
| ECHOE
| ISIG
);
285 newserial_io
.c_cc
[VTIME
] = 2;
286 newserial_io
.c_cc
[VMIN
] = 0;
288 cfsetispeed(&newserial_io
, B1200
);
289 newserial_io
.c_cflag
&= ~CSIZE
;
290 newserial_io
.c_cflag
|= CS7
;
292 atexit (tty_cleanup
);
294 tcflush(tty
, TCIFLUSH
);
296 tcsetattr(tty
,TCSANOW
,&newserial_io
);
298 // This is a simplified procedure; it simply toggles RTS.
301 ioctl(tty
, TIOCMGET
, &i
);
302 i
|= TIOCM_DTR
; // DTR = 1
303 i
&= ~TIOCM_RTS
; // RTS = 0
304 ioctl(tty
, TIOCMSET
, &i
);
308 /* wait for respose */
309 tcflush(tty
, TCIFLUSH
);
310 i
|= TIOCM_DTR
| TIOCM_RTS
; // DTR = 1, RTS = 1
311 ioctl(tty
, TIOCMSET
, &i
);
313 bool non_pnp_mice
= false;
320 while (read (tty
, &c
, 1) == 1) {
325 if ((c
== 0x08) || (c
== 0x28)) { /* Begin ID */
333 /* we haven't seen `Begin ID' in time... */
337 ++c
; /* make it `End ID' */
338 while (read (tty
, &buf
[i
], 1) == 1) {
339 if (buf
[i
++] == c
) /* End ID */
341 if (i
>= sizeof(buf
))
345 printf ("God PnP fields - %d bytes:\n", i
);
347 for (unsigned int j
= 0; j
< i
; ++j
)
348 printf ("%d %x\n", buf
[j
], buf
[j
]);
351 pnpparse (&id
, buf
, i
);
353 printf ("%.*s\n", id
.neisaid
, id
.eisaid
);
355 if (id
.ndevclass
> 0) {
356 printf ("CLASS: %.*s\n", id
.ndevclass
, id
.devclass
);
359 symtab_t
* it
= pnpsymtab
;
360 while (it
->name
!= 0 && strncmp(it
->name
, id
.eisaid
, id
.neisaid
) != 0)
363 printf ("CLASS: MOUSE %s\n", it
->drv
);
365 if (id
.ndescription
> 9) {
366 printf ("DESCRIPTION: %.*s\n", id
.ndescription
, id
.description
);