You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							373 lines
						
					
					
						
							9.4 KiB
						
					
					
				
			
		
		
	
	
							373 lines
						
					
					
						
							9.4 KiB
						
					
					
				| /* | |
|  * --- 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 <[email protected]> | |
|  */ | |
|  | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
|  | |
| #include <unistd.h> | |
|  | |
| #include <termios.h> | |
| #include <sys/types.h> | |
| #include <sys/stat.h> | |
| #include <fcntl.h> | |
|  | |
| #include <sys/ioctl.h> | |
|  | |
| 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; | |
| }
 | |
| 
 |