/* * --- SDE-COPYRIGHT-NOTE-BEGIN --- * This copyright note is auto-generated by ./scripts/Create-CopyPatch. * * Filename: package/.../serpnp/serpnp.c * Copyright (C) 2004 - 2006 The T2 SDE Project * * More information can be found in the files COPYING and README. * * 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; version 2 of the License. A copy of the * GNU General Public License can be found in the file COPYING. * --- SDE-COPYRIGHT-NOTE-END --- */ /* * Serial / COM PnP evaluation as defined in: * "Plug and Play External COM Device Specification" * by Microsoft Corporation & Hayes Microcomputer Products, Inc. * http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/pnpcom.rtf * * Copyright 2005 by René Rebe * * inspired partly by the X.org mouse/pnp code: * Copyright 1998 by Kazutaka YOKOTA */ #include #include #include #include #include #include #include #include #include struct symtab_t { char* name; char* drv; }; /* PnP EISA/product IDs */ static symtab_t pnpsymtab[] = { #ifdef notyet { "KML0001", PROT_THINKING }, /* Kensignton ThinkingMouse */ #endif { "MSH0001", "-ms3" }, /* MS IntelliMouse */ { "MSH0004", "-ms3" }, /* MS IntelliMouse TrackBall */ { "KYEEZ00", "-ms" }, /* Genius EZScroll */ { "KYE0001", "-ms" }, /* Genius PnP Mouse */ { "KYE0002", "-ms" }, /* MouseSystem (Genius?) SmartScroll */ { "KYE0003", "-ms3" }, /* Genius NetMouse */ { "LGI800", "-ms" }, /* Logitech FirstMouse+ on ReneR's desk */ { "LGI800C", "-ms3" }, /* Logitech MouseMan (4 button model) */ { "LGI8033", "-ms3" }, /* Logitech Cordless MouseMan Wheel */ { "LGI8050", "-ms3" }, /* Logitech MouseMan+ */ { "LGI8051", "-ms3" }, /* Logitech FirstMouse+ */ { "LGI8001", "-ms" }, /* Logitech serial */ // was -mman -ReneR { "A4W0005", "-ms3" }, /* A4 Tech 4D/4D+ Mouse */ { "PEC9802", "-ms3" }, /* 8D Scroll Mouse */ #ifdef notyet { "PNP0F00", PROT_BM }, /* MS bus */ #endif { "PNP0F01", "-ms" }, /* MS serial */ #ifdef notyet { "PNP0F02", PROT_BM }, /* MS InPort */ #endif /* * EzScroll returns PNP0F04 in the compatible device field; but it * doesn't look compatible... XXX */ { "PNP0F04", "-msc" }, /* MouseSystems */ { "PNP0F05", "-msc" }, /* MouseSystems */ #ifdef notyet { "PNP0F06", PROT_??? }, /* Genius Mouse */ { "PNP0F07", PROT_??? }, /* Genius Mouse */ #endif { "PNP0F08", "-mman" }, /* Logitech serial */ { "PNP0F09", "-ms" }, /* MS BallPoint serial */ { "PNP0F0A", "-ms" }, /* MS PnP serial */ { "PNP0F0B", "-ms" }, /* MS PnP BallPoint serial */ { "PNP0F0C", "-ms" }, /* MS serial comatible */ #ifdef notyet { "PNP0F0D", PROT_BM }, /* MS InPort comatible */ #endif { "PNP0F0F", "-ms" }, /* MS BallPoint comatible */ #ifdef notyet { "PNP0F10", PROT_??? }, /* TI QuickPort */ { "PNP0F11", PROT_BM }, /* MS bus comatible */ { "PNP0F14", PROT_??? }, /* MS Kids Mouse */ { "PNP0F15", PROT_BM }, /* Logitech bus */ { "PNP0F16", PROT_??? }, /* Logitech SWIFT */ #endif { "PNP0F17", "-mman" }, /* Logitech serial compat */ #ifdef notyet { "PNP0F18", PROT_BM }, /* Logitech bus compatible */ { "PNP0F1A", PROT_??? }, /* Logitech SWIFT compatible */ { "PNP0F1B", PROT_??? }, /* HP Omnibook */ { "PNP0F1C", PROT_??? }, /* Compaq LTE TrackBall PS/2 */ { "PNP0F1D", PROT_??? }, /* Compaq LTE TrackBall serial */ { "PNP0F1E", PROT_??? }, /* MS Kids Trackball */ #endif { NULL, NULL }, }; /* serial PnP ID string */ struct pnpid_t { int revision; /* PnP revision, 100 for 1.00 */ char* eisaid; /* EISA ID including mfr ID and product ID */ char* serial; /* serial No, optional */ char* devclass; /* device class, optional */ char* compat; /* list of compatible drivers, optional */ char* description; /* product description, optional */ int neisaid; /* length of the above fields... */ int nserial; int ndevclass; int ncompat; int ndescription; }; bool pnpparse (pnpid_t* id, char* buf, int len) { char s[3]; int offset; int sum = 0; int i, j; id->revision = 0; id->eisaid = NULL; id->serial = NULL; id->devclass = NULL; id->compat = NULL; id->description = NULL; id->neisaid = 0; id->nserial = 0; id->ndevclass = 0; id->ncompat = 0; id->ndescription = 0; offset = 0x28 - buf[0]; /* calculate checksum */ for (i = 0; i < len - 3; ++i) { sum += buf[i]; buf[i] += offset; } sum += buf[len - 1]; for (; i < len; ++i) buf[i] += offset; //printf ("PnP ID string: `%*.*s'\n", len, len, buf); /* revision */ buf[1] -= offset; buf[2] -= offset; id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f); //printf ("PnP rev %d.%02d\n", id->revision / 100, id->revision % 100); /* EISA vender and product ID */ id->eisaid = &buf[3]; id->neisaid = 7; // workaround for my Logitech mice? only has 6 ... -ReneR if (id->eisaid [id->neisaid-1] == ')') id->neisaid--; /* option strings */ i = 10; if (buf[i] == '\\') { /* device serial # */ for (j = ++i; i < len; ++i) { if (buf[i] == '\\') break; } if (i >= len) i -= 3; if (i - j == 8) { id->serial = &buf[j]; id->nserial = 8; } } if (buf[i] == '\\') { /* PnP class */ for (j = ++i; i < len; ++i) { if (buf[i] == '\\') break; } if (i >= len) i -= 3; if (i > j + 1) { id->devclass = &buf[j]; id->ndevclass = i - j; } } if (buf[i] == '\\') { /* compatible driver */ for (j = ++i; i < len; ++i) { if (buf[i] == '\\') break; } /* * PnP COM spec prior to v0.96 allowed '*' in this field, * it's not allowed now; just ignore it. */ if (buf[j] == '*') ++j; if (i >= len) i -= 3; if (i > j + 1) { id->compat = &buf[j]; id->ncompat = i - j; } } if (buf[i] == '\\') { /* product description */ for (j = ++i; i < len; ++i) { if (buf[i] == ';') break; } if (i >= len) i -= 3; if (i > j + 1) { id->description = &buf[j]; id->ndescription = i - j; } } /* checksum exists if there are any optional fields */ if ((id->nserial > 0) || (id->ndevclass > 0) || (id->ncompat > 0) || (id->ndescription > 0)) { printf ("PnP checksum: 0x%02X\n", sum); sprintf(s, "%02X", sum & 0x0ff); if (strncmp(s, &buf[len - 3], 2) != 0) { printf ("checksum error!"); #if 0 /* * Checksum error!! * I found some mice do not comply with the PnP COM device * spec regarding checksum... XXX */ return false; #endif } } return true; } int tty = 0; struct termios oldserial_io; void tty_cleanup() { printf ("Resetting port ...\n"); tcsetattr(tty, TCSANOW, &oldserial_io); } int main (int argc, char* argv[]) { struct termios newserial_io; if (argc <= 1) { printf("Usage: %s devname\n", argv[0]); return -1; } //open Serialport for reading and writing tty = open (argv[1], O_RDWR | O_NOCTTY); if (tty < 0) { perror("serial port"); return 0; } // save current serial port settings tcgetattr(tty, &oldserial_io); tcgetattr(tty, &newserial_io); // control // raw mode newserial_io.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); newserial_io.c_cc[VTIME] = 2; newserial_io.c_cc[VMIN] = 0; cfsetispeed(&newserial_io, B1200); newserial_io.c_cflag &= ~CSIZE; newserial_io.c_cflag |= CS7; atexit (tty_cleanup); tcflush(tty, TCIFLUSH); tcsetattr(tty,TCSANOW,&newserial_io); // This is a simplified procedure; it simply toggles RTS. unsigned int i; ioctl(tty, TIOCMGET, &i); i |= TIOCM_DTR; // DTR = 1 i &= ~TIOCM_RTS; // RTS = 0 ioctl(tty, TIOCMSET, &i); usleep(200000); /* wait for respose */ tcflush(tty, TCIFLUSH); i |= TIOCM_DTR | TIOCM_RTS; // DTR = 1, RTS = 1 ioctl(tty, TIOCMSET, &i); bool non_pnp_mice = false; bool pnp = true; unsigned char c; char buf [256]; i = 0; while (read (tty, &c, 1) == 1) { if (c == 'M') non_pnp_mice = true; if ((c == 0x08) || (c == 0x28)) { /* Begin ID */ buf[0] = c; i = 1; break; } } if (i <= 0) { /* we haven't seen `Begin ID' in time... */ return 0; } ++c; /* make it `End ID' */ while (read (tty, &buf[i], 1) == 1) { if (buf[i++] == c) /* End ID */ break; if (i >= sizeof(buf)) return 1; } printf ("God PnP fields - %d bytes:\n", i); for (unsigned int j = 0; j < i; ++j) printf ("%d %x\n", buf[j], buf[j]); pnpid_t id; pnpparse (&id, buf, i); printf ("%.*s\n", id.neisaid, id.eisaid); if (id.ndevclass > 0) { printf ("CLASS: %.*s\n", id.ndevclass, id.devclass); } else { symtab_t* it = pnpsymtab; while (it->name != 0 && strncmp(it->name, id.eisaid, id.neisaid) != 0) ++it; if (it->name != 0) printf ("CLASS: MOUSE %s\n", it->drv); } if (id.ndescription > 9) { printf ("DESCRIPTION: %.*s\n", id.ndescription, id.description); } return 0; }