1659607e0Smrg/* 2659607e0Smrg * Copyright 1998 by Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp> 3659607e0Smrg * 4659607e0Smrg * Permission to use, copy, modify, distribute, and sell this software and its 5659607e0Smrg * documentation for any purpose is hereby granted without fee, provided that 6659607e0Smrg * the above copyright notice appear in all copies and that both that 7659607e0Smrg * copyright notice and this permission notice appear in supporting 8659607e0Smrg * documentation, and that the name of Kazutaka YOKOTA not be used in 9659607e0Smrg * advertising or publicity pertaining to distribution of the software without 10659607e0Smrg * specific, written prior permission. Kazutaka YOKOTA makes no representations 11659607e0Smrg * about the suitability of this software for any purpose. It is provided 12659607e0Smrg * "as is" without express or implied warranty. 13659607e0Smrg * 14659607e0Smrg * KAZUTAKA YOKOTA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15659607e0Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 16659607e0Smrg * EVENT SHALL KAZUTAKA YOKOTA BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17659607e0Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18659607e0Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19659607e0Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 20659607e0Smrg * PERFORMANCE OF THIS SOFTWARE. 21659607e0Smrg */ 22659607e0Smrg 23659607e0Smrg#ifdef HAVE_CONFIG_H 24659607e0Smrg#include "config.h" 25659607e0Smrg#endif 26659607e0Smrg 27b73be646Smrg#include <xorg-server.h> 28659607e0Smrg#include <stdio.h> 29659607e0Smrg#include <string.h> 30659607e0Smrg#include <unistd.h> 31659607e0Smrg#include <X11/X.h> 32659607e0Smrg#include <X11/Xproto.h> 33659607e0Smrg#include "inputstr.h" 34659607e0Smrg#include "scrnintstr.h" 35659607e0Smrg#include "xf86.h" 36659607e0Smrg#include "xf86Priv.h" 37659607e0Smrg#include "xf86Xinput.h" 38659607e0Smrg#include "xf86_OSproc.h" 39659607e0Smrg#include "mouse.h" 40659607e0Smrg#include "mousePriv.h" 41659607e0Smrg 42659607e0Smrg#ifdef MOUSEINITDEBUG 43659607e0Smrg# define DEBUG 44659607e0Smrg# define EXTMOUSEDEBUG 45659607e0Smrg#endif 46659607e0Smrg 47659607e0Smrg/* serial PnP ID string */ 48659607e0Smrgtypedef struct { 491450c749Smrg int revision; /* PnP revision, 100 for 1.00 */ 501450c749Smrg char *eisaid; /* EISA ID including mfr ID and product ID */ 511450c749Smrg char *serial; /* serial No, optional */ 521450c749Smrg char *class; /* device class, optional */ 531450c749Smrg char *compat; /* list of compatible drivers, optional */ 541450c749Smrg char *description; /* product description, optional */ 551450c749Smrg int neisaid; /* length of the above fields... */ 56659607e0Smrg int nserial; 57659607e0Smrg int nclass; 58659607e0Smrg int ncompat; 59659607e0Smrg int ndescription; 60659607e0Smrg} pnpid_t; 61659607e0Smrg 62659607e0Smrg/* symbol table entry */ 63659607e0Smrgtypedef struct { 641450c749Smrg const char *name; 65659607e0Smrg MouseProtocolID val; 66659607e0Smrg} symtab_t; 67659607e0Smrg 68659607e0Smrg/* PnP EISA/product IDs */ 69659607e0Smrgstatic symtab_t pnpprod[] = { 701450c749Smrg { "KML0001", PROT_THINKING }, /* Kensington ThinkingMouse */ 711450c749Smrg { "MSH0001", PROT_IMSERIAL }, /* MS IntelliMouse */ 721450c749Smrg { "MSH0004", PROT_IMSERIAL }, /* MS IntelliMouse TrackBall */ 731450c749Smrg { "KYEEZ00", PROT_MS }, /* Genius EZScroll */ 741450c749Smrg { "KYE0001", PROT_MS }, /* Genius PnP Mouse */ 751450c749Smrg { "KYE0002", PROT_MS }, /* MouseSystem (Genius?) SmartScroll */ 761450c749Smrg { "KYE0003", PROT_IMSERIAL }, /* Genius NetMouse */ 771450c749Smrg { "KYE0004", PROT_IMSERIAL }, /* Genius NetScroll+ (serial) */ 781450c749Smrg { "LGI800C", PROT_IMSERIAL }, /* Logitech MouseMan (4 button model) */ 791450c749Smrg { "LGI8033", PROT_IMSERIAL }, /* Logitech Cordless MouseMan Wheel */ 801450c749Smrg { "LGI8050", PROT_IMSERIAL }, /* Logitech MouseMan+ */ 811450c749Smrg { "LGI8051", PROT_IMSERIAL }, /* Logitech FirstMouse+ */ 821450c749Smrg { "LGI8001", PROT_LOGIMAN }, /* Logitech serial */ 831450c749Smrg { "A4W0005", PROT_IMSERIAL }, /* A4 Tech 4D/4D+ Mouse */ 841450c749Smrg { "PEC9802", PROT_IMSERIAL }, /* 8D Scroll Mouse */ 851450c749Smrg 861450c749Smrg { "PNP0F00", PROT_BM }, /* MS bus */ 871450c749Smrg { "PNP0F01", PROT_MS }, /* MS serial */ 881450c749Smrg { "PNP0F02", PROT_BM }, /* MS InPort */ 891450c749Smrg { "PNP0F03", PROT_PS2 }, /* MS PS/2 */ 90659607e0Smrg /* 91659607e0Smrg * EzScroll returns PNP0F04 in the compatible device field; but it 92659607e0Smrg * doesn't look compatible... XXX 93659607e0Smrg */ 941450c749Smrg { "PNP0F04", PROT_MSC }, /* MouseSystems */ 951450c749Smrg { "PNP0F05", PROT_MSC }, /* MouseSystems */ 96659607e0Smrg#ifdef notyet 971450c749Smrg { "PNP0F06", PROT_??? }, /* Genius Mouse */ 981450c749Smrg { "PNP0F07", PROT_??? }, /* Genius Mouse */ 99659607e0Smrg#endif 1001450c749Smrg { "PNP0F08", PROT_LOGIMAN }, /* Logitech serial */ 1011450c749Smrg { "PNP0F09", PROT_MS }, /* MS BallPoint serial */ 1021450c749Smrg { "PNP0F0A", PROT_MS }, /* MS PnP serial */ 1031450c749Smrg { "PNP0F0B", PROT_MS }, /* MS PnP BallPoint serial */ 1041450c749Smrg { "PNP0F0C", PROT_MS }, /* MS serial compatible */ 1051450c749Smrg { "PNP0F0D", PROT_BM }, /* MS InPort compatible */ 1061450c749Smrg { "PNP0F0E", PROT_PS2 }, /* MS PS/2 compatible */ 1071450c749Smrg { "PNP0F0F", PROT_MS }, /* MS BallPoint compatible */ 108659607e0Smrg#ifdef notyet 1091450c749Smrg { "PNP0F10", PROT_??? }, /* TI QuickPort */ 110659607e0Smrg#endif 1111450c749Smrg { "PNP0F11", PROT_BM }, /* MS bus compatible */ 1121450c749Smrg { "PNP0F12", PROT_PS2 }, /* Logitech PS/2 */ 1131450c749Smrg { "PNP0F13", PROT_PS2 }, /* PS/2 */ 114659607e0Smrg#ifdef notyet 1151450c749Smrg { "PNP0F14", PROT_??? }, /* MS Kids Mouse */ 116659607e0Smrg#endif 1171450c749Smrg { "PNP0F15", PROT_BM }, /* Logitech bus */ 118659607e0Smrg#ifdef notyet 1191450c749Smrg { "PNP0F16", PROT_??? }, /* Logitech SWIFT */ 120659607e0Smrg#endif 1211450c749Smrg { "PNP0F17", PROT_LOGIMAN }, /* Logitech serial compat */ 1221450c749Smrg { "PNP0F18", PROT_BM }, /* Logitech bus compatible */ 1231450c749Smrg { "PNP0F19", PROT_PS2 }, /* Logitech PS/2 compatible */ 124659607e0Smrg#ifdef notyet 1251450c749Smrg { "PNP0F1A", PROT_??? }, /* Logitech SWIFT compatible */ 1261450c749Smrg { "PNP0F1B", PROT_??? }, /* HP Omnibook */ 1271450c749Smrg { "PNP0F1C", PROT_??? }, /* Compaq LTE TrackBall PS/2 */ 1281450c749Smrg { "PNP0F1D", PROT_??? }, /* Compaq LTE TrackBall serial */ 1291450c749Smrg { "PNP0F1E", PROT_??? }, /* MS Kids Trackball */ 130659607e0Smrg#endif 1311450c749Smrg { NULL, PROT_UNKNOWN }, 132659607e0Smrg}; 133659607e0Smrg 134659607e0Smrgstatic const char *pnpSerial[] = { 1351450c749Smrg "BaudRate", "1200", 1361450c749Smrg "DataBits", "7", 1371450c749Smrg "StopBits", "1", 1381450c749Smrg "Parity", "None", 1391450c749Smrg "FlowControl", "None", 1401450c749Smrg "VTime", "0", 1411450c749Smrg "VMin", "1", 1421450c749Smrg NULL 143659607e0Smrg}; 144659607e0Smrg 145659607e0Smrgstatic int pnpgets(InputInfoPtr, char *, Bool *prePNP); 146659607e0Smrgstatic int pnpparse(InputInfoPtr, pnpid_t *, char *, int); 147659607e0Smrgstatic MouseProtocolID prepnpparse(InputInfoPtr pInfo, char *buf); 148659607e0Smrgstatic symtab_t *pnpproto(pnpid_t *); 149659607e0Smrgstatic symtab_t *gettoken(symtab_t *, char *, int); 150659607e0Smrgstatic MouseProtocolID getPs2ProtocolPnP(InputInfoPtr pInfo); 151659607e0Smrgstatic MouseProtocolID probePs2ProtocolPnP(InputInfoPtr pInfo); 152659607e0Smrg 153659607e0Smrgstatic MouseProtocolID 154659607e0SmrgMouseGetSerialPnpProtocol(InputInfoPtr pInfo) 155659607e0Smrg{ 1561450c749Smrg char buf[256]; /* PnP ID string may be up to 256 bytes long */ 157659607e0Smrg pnpid_t pnpid; 158659607e0Smrg symtab_t *t; 159659607e0Smrg int len; 160659607e0Smrg Bool prePNP; 161659607e0Smrg 1621450c749Smrg if ((len = pnpgets(pInfo, buf, &prePNP)) > 0) 163659607e0Smrg { 1641450c749Smrg if (!prePNP) { 1651450c749Smrg if (pnpparse(pInfo, &pnpid, buf, len) && 1661450c749Smrg (t = pnpproto(&pnpid)) != NULL) { 1671450c749Smrg xf86MsgVerb(X_INFO, 2, "%s: PnP-detected protocol ID: %d\n", 1681450c749Smrg pInfo->name, t->val); 1691450c749Smrg return (t->val); 1701450c749Smrg } 1711450c749Smrg } else 1721450c749Smrg return prepnpparse(pInfo,buf); 173659607e0Smrg } 174659607e0Smrg return PROT_UNKNOWN; 175659607e0Smrg} 176659607e0Smrg 177659607e0SmrgMouseProtocolID 178659607e0SmrgMouseGetPnpProtocol(InputInfoPtr pInfo) 179659607e0Smrg{ 180659607e0Smrg MouseDevPtr pMse = pInfo->private; 181659607e0Smrg mousePrivPtr mPriv = (mousePrivPtr)pMse->mousePriv; 182659607e0Smrg MouseProtocolID val; 183659607e0Smrg CARD32 last; 1841450c749Smrg 185659607e0Smrg if ((val = MouseGetSerialPnpProtocol(pInfo)) != PROT_UNKNOWN) { 1861450c749Smrg if (val == MouseGetSerialPnpProtocol(pInfo)) 1871450c749Smrg return val; 188659607e0Smrg } 189659607e0Smrg 190659607e0Smrg last = mPriv->pnpLast; 191659607e0Smrg mPriv->pnpLast = currentTime.milliseconds; 192659607e0Smrg 193659607e0Smrg if (last) { 1941450c749Smrg if (last - currentTime.milliseconds < 100 1951450c749Smrg || (mPriv->disablePnPauto 1961450c749Smrg && (last - currentTime.milliseconds < 10000))) { 197659607e0Smrg#ifdef EXTMOUSEDEBUG 1981450c749Smrg xf86ErrorF("Mouse: Disabling PnP\n"); 199659607e0Smrg#endif 2001450c749Smrg mPriv->disablePnPauto = TRUE; 2011450c749Smrg return PROT_UNKNOWN; 2021450c749Smrg } 203659607e0Smrg } 2041450c749Smrg 205659607e0Smrg#ifdef EXTMOUSEDEBUG 206659607e0Smrg if (mPriv->disablePnPauto) 2071450c749Smrg xf86ErrorF("Mouse: Enabling PnP\n"); 208659607e0Smrg#endif 209659607e0Smrg mPriv->disablePnPauto = FALSE; 210659607e0Smrg 211659607e0Smrg if (mPriv->soft) 2121450c749Smrg return getPs2ProtocolPnP(pInfo); 213659607e0Smrg else 2141450c749Smrg return probePs2ProtocolPnP(pInfo); 215659607e0Smrg} 216659607e0Smrg 217659607e0Smrg/* 2181450c749Smrg * Try to elicit a PnP ID as described in 2191450c749Smrg * Microsoft, Hayes: "Plug and Play External COM Device Specification, 220659607e0Smrg * rev 1.00", 1995. 221659607e0Smrg * 222659607e0Smrg * The routine does not fully implement the COM Enumerator as per Section 223659607e0Smrg * 2.1 of the document. In particular, we don't have idle state in which 2241450c749Smrg * the driver software monitors the com port for dynamic connection or 2251450c749Smrg * removal of a device at the port, because `moused' simply quits if no 226659607e0Smrg * device is found. 227659607e0Smrg * 2281450c749Smrg * In addition, as PnP COM device enumeration procedure slightly has 229659607e0Smrg * changed since its first publication, devices which follow earlier 2301450c749Smrg * revisions of the above spec. may fail to respond if the rev 1.0 231659607e0Smrg * procedure is used. XXX 232659607e0Smrg */ 233659607e0Smrgstatic int 234659607e0Smrgpnpgets(InputInfoPtr pInfo, char *buf, Bool *prePNP) 235659607e0Smrg{ 236659607e0Smrg int i; 237659607e0Smrg char c; 238659607e0Smrg pointer pnpOpts; 2391450c749Smrg 240659607e0Smrg#if 0 2411450c749Smrg /* 242659607e0Smrg * This is the procedure described in rev 1.0 of PnP COM device spec. 2436aab59a7Smrg * Unfortunately, some devices which conform to earlier revisions of 244659607e0Smrg * the spec gets confused and do not return the ID string... 245659607e0Smrg */ 246659607e0Smrg 247659607e0Smrg /* port initialization (2.1.2) */ 248659607e0Smrg if ((i = xf86GetSerialModemState(pInfo->fd)) == -1) 2491450c749Smrg return 0; 2501450c749Smrg i |= XF86_M_DTR; /* DTR = 1 */ 2511450c749Smrg i &= ~XF86_M_RTS; /* RTS = 0 */ 252659607e0Smrg if (xf86SetSerialModemState(pInfo->fd, i) == -1) 2531450c749Smrg goto disconnect_idle; 254659607e0Smrg usleep(200000); 255659607e0Smrg if ((i = xf86GetSerialModemState(pInfo->fd)) == -1 || 2561450c749Smrg (i & XF86_M_DSR) == 0) 2571450c749Smrg goto disconnect_idle; 258659607e0Smrg 259659607e0Smrg /* port setup, 1st phase (2.1.3) */ 260659607e0Smrg pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1); 261659607e0Smrg xf86SetSerial(pInfo->fd, pnpOpts); 2621450c749Smrg i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */ 263659607e0Smrg xf86SerialModemClearBits(pInfo->fd, i); 264659607e0Smrg usleep(200000); 2651450c749Smrg i = TIOCM_DTR; /* DTR = 1, RTS = 0 */ 266659607e0Smrg xf86SerialModemSetBits(pInfo->fd, i); 267659607e0Smrg usleep(200000); 268659607e0Smrg 269659607e0Smrg /* wait for response, 1st phase (2.1.4) */ 270659607e0Smrg xf86FlushInput(pInfo->fd); 2711450c749Smrg i = TIOCM_RTS; /* DTR = 1, RTS = 1 */ 272659607e0Smrg xf86SerialModemSetBits(pInfo->fd, i); 273659607e0Smrg 274659607e0Smrg /* try to read something */ 275659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) { 276659607e0Smrg 2771450c749Smrg /* port setup, 2nd phase (2.1.5) */ 2781450c749Smrg i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */ 2791450c749Smrg xf86SerialModemClearBits(pInfo->fd, i); 280659607e0Smrg usleep(200000); 281659607e0Smrg 2821450c749Smrg /* wait for response, 2nd phase (2.1.6) */ 2831450c749Smrg xf86FlushInput(pInfo->fd); 2841450c749Smrg i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */ 2851450c749Smrg xf86SerialModemSetBits(pInfo->fd, i); 286659607e0Smrg 287659607e0Smrg /* try to read something */ 2881450c749Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 2891450c749Smrg goto connect_idle; 290659607e0Smrg } 291659607e0Smrg#else 292659607e0Smrg /* 293659607e0Smrg * This is a simplified procedure; it simply toggles RTS. 294659607e0Smrg */ 295659607e0Smrg 296659607e0Smrg if ((i = xf86GetSerialModemState(pInfo->fd)) == -1) 2971450c749Smrg return 0; 2981450c749Smrg i |= XF86_M_DTR; /* DTR = 1 */ 2991450c749Smrg i &= ~XF86_M_RTS; /* RTS = 0 */ 300659607e0Smrg if (xf86SetSerialModemState(pInfo->fd, i) == -1) 3011450c749Smrg goto disconnect_idle; 302659607e0Smrg usleep(200000); 303659607e0Smrg 304659607e0Smrg pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1); 305659607e0Smrg xf86SetSerial(pInfo->fd, pnpOpts); 306659607e0Smrg 3076aab59a7Smrg /* wait for response */ 308659607e0Smrg xf86FlushInput(pInfo->fd); 3091450c749Smrg i = XF86_M_DTR | XF86_M_RTS; /* DTR = 1, RTS = 1 */ 310659607e0Smrg xf86SerialModemSetBits(pInfo->fd, i); 311659607e0Smrg 312659607e0Smrg /* try to read something */ 313659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 314659607e0Smrg goto connect_idle; 315659607e0Smrg#endif 316659607e0Smrg 317659607e0Smrg /* collect PnP COM device ID (2.1.7) */ 318659607e0Smrg i = 0; 319659607e0Smrg *prePNP = FALSE; 3201450c749Smrg 3211450c749Smrg usleep(200000); /* the mouse must send `Begin ID' within 200msec */ 322659607e0Smrg while (xf86ReadSerial(pInfo->fd, &c, 1) == 1) { 3231450c749Smrg /* we may see "M", or "M3..." before `Begin ID' */ 3241450c749Smrg if (c == 'M') 3251450c749Smrg *prePNP = TRUE; 3261450c749Smrg 3271450c749Smrg if ((c == 0x08) || (c == 0x28)) { /* Begin ID */ 3281450c749Smrg *prePNP = FALSE; 3291450c749Smrg buf[0] = c; 3301450c749Smrg i = 1; 3311450c749Smrg break; 332659607e0Smrg } 3331450c749Smrg if (*prePNP) 3341450c749Smrg buf[i++] = c; 3351450c749Smrg 3361450c749Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 3371450c749Smrg break; 338659607e0Smrg } 339659607e0Smrg if (i <= 0) { 3401450c749Smrg /* we haven't seen `Begin ID' in time... */ 3411450c749Smrg goto connect_idle; 342659607e0Smrg } 343659607e0Smrg if (*prePNP) 3441450c749Smrg return i; 3451450c749Smrg 3461450c749Smrg ++c; /* make it `End ID' */ 347659607e0Smrg for (;;) { 3481450c749Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 3491450c749Smrg break; 3501450c749Smrg 3511450c749Smrg xf86ReadSerial(pInfo->fd, &buf[i], 1); 3521450c749Smrg if (buf[i++] == c) /* End ID */ 3531450c749Smrg break; 3541450c749Smrg if (i >= 256) 3551450c749Smrg break; 356659607e0Smrg } 357659607e0Smrg if (buf[i - 1] != c) 3581450c749Smrg goto connect_idle; 359659607e0Smrg return i; 360659607e0Smrg 361659607e0Smrg /* 3621450c749Smrg * According to PnP spec, we should set DTR = 1 and RTS = 0 while 3631450c749Smrg * in idle state. But, `moused' shall set DTR = RTS = 1 and proceed, 3641450c749Smrg * assuming there is something at the port even if it didn't 365659607e0Smrg * respond to the PnP enumeration procedure. 366659607e0Smrg */ 367659607e0Smrgdisconnect_idle: 3681450c749Smrg i = XF86_M_DTR | XF86_M_RTS; /* DTR = 1, RTS = 1 */ 369659607e0Smrg xf86SerialModemSetBits(pInfo->fd, i); 370659607e0Smrgconnect_idle: 371659607e0Smrg return 0; 372659607e0Smrg} 373659607e0Smrg 374659607e0Smrgstatic int 375659607e0Smrgpnpparse(InputInfoPtr pInfo, pnpid_t *id, char *buf, int len) 376659607e0Smrg{ 377659607e0Smrg char s[3]; 378659607e0Smrg int offset; 379659607e0Smrg int sum = 0; 380659607e0Smrg int i, j; 381659607e0Smrg 382659607e0Smrg id->revision = 0; 383659607e0Smrg id->eisaid = NULL; 384659607e0Smrg id->serial = NULL; 385659607e0Smrg id->class = NULL; 386659607e0Smrg id->compat = NULL; 387659607e0Smrg id->description = NULL; 388659607e0Smrg id->neisaid = 0; 389659607e0Smrg id->nserial = 0; 390659607e0Smrg id->nclass = 0; 391659607e0Smrg id->ncompat = 0; 392659607e0Smrg id->ndescription = 0; 393659607e0Smrg 394659607e0Smrg offset = 0x28 - buf[0]; 395659607e0Smrg 396659607e0Smrg /* calculate checksum */ 397659607e0Smrg for (i = 0; i < len - 3; ++i) { 3981450c749Smrg sum += buf[i]; 3991450c749Smrg buf[i] += offset; 400659607e0Smrg } 401659607e0Smrg sum += buf[len - 1]; 402659607e0Smrg for (; i < len; ++i) 4031450c749Smrg buf[i] += offset; 404659607e0Smrg xf86MsgVerb(X_INFO, 2, "%s: PnP ID string: `%*.*s'\n", pInfo->name, 4051450c749Smrg len, len, buf); 406659607e0Smrg 407659607e0Smrg /* revision */ 408659607e0Smrg buf[1] -= offset; 409659607e0Smrg buf[2] -= offset; 410659607e0Smrg id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f); 411659607e0Smrg xf86MsgVerb(X_INFO, 2, "%s: PnP rev %d.%02d\n", pInfo->name, 4121450c749Smrg id->revision / 100, id->revision % 100); 413659607e0Smrg 4144d6d3f96Smrg /* EISA vendor and product ID */ 415659607e0Smrg id->eisaid = &buf[3]; 416659607e0Smrg id->neisaid = 7; 417659607e0Smrg 418659607e0Smrg /* option strings */ 419659607e0Smrg i = 10; 420659607e0Smrg if (buf[i] == '\\') { 421659607e0Smrg /* device serial # */ 422659607e0Smrg for (j = ++i; i < len; ++i) { 423659607e0Smrg if (buf[i] == '\\') 4241450c749Smrg break; 425659607e0Smrg } 4261450c749Smrg if (i >= len) 4271450c749Smrg i -= 3; 4281450c749Smrg if (i - j == 8) { 429659607e0Smrg id->serial = &buf[j]; 430659607e0Smrg id->nserial = 8; 4311450c749Smrg } 432659607e0Smrg } 433659607e0Smrg if (buf[i] == '\\') { 434659607e0Smrg /* PnP class */ 435659607e0Smrg for (j = ++i; i < len; ++i) { 436659607e0Smrg if (buf[i] == '\\') 4371450c749Smrg break; 438659607e0Smrg } 4391450c749Smrg if (i >= len) 4401450c749Smrg i -= 3; 4411450c749Smrg if (i > j + 1) { 442659607e0Smrg id->class = &buf[j]; 443659607e0Smrg id->nclass = i - j; 444659607e0Smrg } 445659607e0Smrg } 446659607e0Smrg if (buf[i] == '\\') { 4471450c749Smrg /* compatible driver */ 448659607e0Smrg for (j = ++i; i < len; ++i) { 449659607e0Smrg if (buf[i] == '\\') 4501450c749Smrg break; 451659607e0Smrg } 4521450c749Smrg /* 4531450c749Smrg * PnP COM spec prior to v0.96 allowed '*' in this field, 4541450c749Smrg * it's not allowed now; just ignore it. 4551450c749Smrg */ 4561450c749Smrg if (buf[j] == '*') 4571450c749Smrg ++j; 4581450c749Smrg if (i >= len) 4591450c749Smrg i -= 3; 4601450c749Smrg if (i > j + 1) { 461659607e0Smrg id->compat = &buf[j]; 462659607e0Smrg id->ncompat = i - j; 463659607e0Smrg } 464659607e0Smrg } 465659607e0Smrg if (buf[i] == '\\') { 4661450c749Smrg /* product description */ 467659607e0Smrg for (j = ++i; i < len; ++i) { 468659607e0Smrg if (buf[i] == ';') 4691450c749Smrg break; 470659607e0Smrg } 4711450c749Smrg if (i >= len) 4721450c749Smrg i -= 3; 4731450c749Smrg if (i > j + 1) { 474659607e0Smrg id->description = &buf[j]; 475659607e0Smrg id->ndescription = i - j; 476659607e0Smrg } 477659607e0Smrg } 478659607e0Smrg 479659607e0Smrg /* checksum exists if there are any optional fields */ 480659607e0Smrg if ((id->nserial > 0) || (id->nclass > 0) 4811450c749Smrg || (id->ncompat > 0) || (id->ndescription > 0)) { 4821450c749Smrg xf86MsgVerb(X_INFO, 4, "%s: PnP checksum: 0x%02X\n", pInfo->name, sum); 483659607e0Smrg sprintf(s, "%02X", sum & 0x0ff); 484659607e0Smrg if (strncmp(s, &buf[len - 3], 2) != 0) { 485659607e0Smrg#if 0 4861450c749Smrg /* 4871450c749Smrg * Checksum error!! 4881450c749Smrg * I found some mice do not comply with the PnP COM device 4891450c749Smrg * spec regarding checksum... XXX 4901450c749Smrg */ 4911450c749Smrg return FALSE; 492659607e0Smrg#endif 493659607e0Smrg } 494659607e0Smrg } 495659607e0Smrg 496659607e0Smrg return TRUE; 497659607e0Smrg} 498659607e0Smrg 499659607e0Smrg/* We can only identify MS at the moment */ 500659607e0Smrgstatic MouseProtocolID 501659607e0Smrgprepnpparse(InputInfoPtr pInfo, char *buf) 502659607e0Smrg{ 503659607e0Smrg if (buf[0] == 'M' && buf[1] == '3') 5041450c749Smrg return PROT_MS; 505659607e0Smrg return PROT_UNKNOWN; 506659607e0Smrg} 507659607e0Smrg 508659607e0Smrg 509659607e0Smrgstatic symtab_t * 510659607e0Smrgpnpproto(pnpid_t *id) 511659607e0Smrg{ 512659607e0Smrg symtab_t *t; 513659607e0Smrg int i, j; 514659607e0Smrg 515659607e0Smrg if (id->nclass > 0) 5161450c749Smrg if (strncmp(id->class, "MOUSE", id->nclass) != 0) 5171450c749Smrg /* this is not a mouse! */ 5181450c749Smrg return NULL; 519659607e0Smrg 520659607e0Smrg if (id->neisaid > 0) { 521659607e0Smrg t = gettoken(pnpprod, id->eisaid, id->neisaid); 5221450c749Smrg if (t->val != -1) 523659607e0Smrg return t; 524659607e0Smrg } 525659607e0Smrg 526659607e0Smrg /* 527659607e0Smrg * The 'Compatible drivers' field may contain more than one 528659607e0Smrg * ID separated by ','. 529659607e0Smrg */ 530659607e0Smrg if (id->ncompat <= 0) 5311450c749Smrg return NULL; 532659607e0Smrg for (i = 0; i < id->ncompat; ++i) { 533659607e0Smrg for (j = i; id->compat[i] != ','; ++i) 534659607e0Smrg if (i >= id->ncompat) 5351450c749Smrg break; 536659607e0Smrg if (i > j) { 537659607e0Smrg t = gettoken(pnpprod, id->compat + j, i - j); 5381450c749Smrg if (t->val != -1) 539659607e0Smrg return t; 5401450c749Smrg } 541659607e0Smrg } 542659607e0Smrg 543659607e0Smrg return NULL; 544659607e0Smrg} 545659607e0Smrg 546659607e0Smrg/* name/val mapping */ 547659607e0Smrg 548659607e0Smrgstatic symtab_t * 5496aab59a7Smrggettoken(symtab_t *tab, char *s, int len) 550659607e0Smrg{ 551659607e0Smrg int i; 552659607e0Smrg 553659607e0Smrg for (i = 0; tab[i].name != NULL; ++i) { 5541450c749Smrg if (strncmp(tab[i].name, s, len) == 0) 5551450c749Smrg break; 556659607e0Smrg } 557659607e0Smrg return &tab[i]; 558659607e0Smrg} 559659607e0Smrg 560659607e0Smrg/******************* PS/2 PnP probing ****************/ 561659607e0Smrg 562659607e0Smrgstatic int 563659607e0SmrgreadMouse(InputInfoPtr pInfo, unsigned char *u) 564659607e0Smrg{ 565659607e0Smrg 566659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 5671450c749Smrg return FALSE; 568659607e0Smrg 569659607e0Smrg xf86ReadSerial(pInfo->fd, u, 1); 570659607e0Smrg return TRUE; 571659607e0Smrg} 572659607e0Smrg 573659607e0Smrgstatic void 574659607e0Smrgps2DisableWrapMode(InputInfoPtr pInfo) 575659607e0Smrg{ 576659607e0Smrg unsigned char reset_wrap_mode[] = { 0xEC }; 577659607e0Smrg ps2SendPacket(pInfo, reset_wrap_mode, sizeof(reset_wrap_mode)); 578659607e0Smrg} 579659607e0Smrg 580659607e0SmrgBool 581659607e0Smrgps2SendPacket(InputInfoPtr pInfo, unsigned char *bytes, int len) 582659607e0Smrg{ 583659607e0Smrg unsigned char c; 584659607e0Smrg int i,j; 5851450c749Smrg 586659607e0Smrg#ifdef DEBUG 587659607e0Smrg xf86ErrorF("Ps/2 data package:"); 588659607e0Smrg for (i = 0; i < len; i++) 5891450c749Smrg xf86ErrorF(" %x", *(bytes + i)); 590659607e0Smrg xf86ErrorF("\n"); 591659607e0Smrg#endif 592659607e0Smrg 593659607e0Smrg for (i = 0; i < len; i++) { 5941450c749Smrg for (j = 0; j < 10; j++) { 5951450c749Smrg xf86WriteSerial(pInfo->fd, bytes + i, 1); 5961450c749Smrg usleep(10000); 5971450c749Smrg if (!readMouse(pInfo,&c)) { 598659607e0Smrg#ifdef DEBUG 5991450c749Smrg xf86ErrorF("sending 0x%x to PS/2 unsuccessful\n",*(bytes + i)); 600659607e0Smrg#endif 6011450c749Smrg return FALSE; 6021450c749Smrg } 603659607e0Smrg#ifdef DEBUG 6044d6d3f96Smrg xf86ErrorF("Received: 0x%x\n",c); 605659607e0Smrg#endif 6061450c749Smrg if (c == 0xFA) /* ACK */ 6071450c749Smrg break; 6081450c749Smrg 6091450c749Smrg if (c == 0xFE) /* resend */ 6101450c749Smrg continue; 611659607e0Smrg 612659607e0Smrg 6131450c749Smrg if (c == 0xFC) /* error */ 6141450c749Smrg return FALSE; 615659607e0Smrg 6164d6d3f96Smrg /* Some mice accidentally enter wrap mode during init */ 6171450c749Smrg if (c == *(bytes + i) /* wrap mode */ 6181450c749Smrg && (*(bytes + i) != 0xEC)) /* avoid recursion */ 6191450c749Smrg ps2DisableWrapMode(pInfo); 620659607e0Smrg 6211450c749Smrg return FALSE; 6221450c749Smrg } 6231450c749Smrg if (j == 10) 6241450c749Smrg return FALSE; 625659607e0Smrg } 6261450c749Smrg 627659607e0Smrg return TRUE; 628659607e0Smrg} 6291450c749Smrg 630659607e0Smrgstatic Bool 631659607e0Smrgps2DisableDataReporting(InputInfoPtr pInfo) 632659607e0Smrg{ 633659607e0Smrg unsigned char packet[] = { 0xF5 }; 634659607e0Smrg return ps2SendPacket(pInfo, packet, sizeof(packet)); 635659607e0Smrg} 636659607e0Smrg 637659607e0SmrgBool 638659607e0Smrgps2EnableDataReporting(InputInfoPtr pInfo) 639659607e0Smrg{ 640659607e0Smrg unsigned char packet[] = { 0xF4 }; 641659607e0Smrg return ps2SendPacket(pInfo, packet, sizeof(packet)); 642659607e0Smrg} 643659607e0Smrg 644659607e0Smrgint 645659607e0Smrgps2GetDeviceID(InputInfoPtr pInfo) 646659607e0Smrg{ 647659607e0Smrg unsigned char u; 648659607e0Smrg unsigned char packet[] = { 0xf2 }; 649659607e0Smrg 650659607e0Smrg usleep(30000); 651659607e0Smrg xf86FlushInput(pInfo->fd); 6521450c749Smrg if (!ps2SendPacket(pInfo, packet, sizeof(packet))) 6531450c749Smrg return -1; 654659607e0Smrg while (1) { 6551450c749Smrg if (!readMouse(pInfo,&u)) 6561450c749Smrg return -1; 6571450c749Smrg if (u != 0xFA) 6581450c749Smrg break; 659659607e0Smrg } 660659607e0Smrg#ifdef DEBUG 661659607e0Smrg xf86ErrorF("Obtained Mouse Type: %x\n",u); 662659607e0Smrg#endif 663659607e0Smrg return (int) u; 664659607e0Smrg} 665659607e0Smrg 666659607e0SmrgBool 667659607e0Smrgps2Reset(InputInfoPtr pInfo) 668659607e0Smrg{ 669659607e0Smrg unsigned char u; 670659607e0Smrg unsigned char packet[] = { 0xff }; 671659607e0Smrg unsigned char reply[] = { 0xaa, 0x00 }; 672659607e0Smrg unsigned int i; 673659607e0Smrg#ifdef DEBUG 674659607e0Smrg xf86ErrorF("PS/2 Mouse reset\n"); 675659607e0Smrg#endif 6761450c749Smrg if (!ps2SendPacket(pInfo, packet, sizeof(packet))) 6771450c749Smrg return FALSE; 678659607e0Smrg /* we need a little delay here */ 679659607e0Smrg xf86WaitForInput(pInfo->fd, 500000); 680659607e0Smrg for (i = 0; i < sizeof(reply) ; i++) { 6811450c749Smrg if (!readMouse(pInfo,&u)) { 6821450c749Smrg goto EXIT; 6831450c749Smrg } 6841450c749Smrg if (u != reply[i]) 6851450c749Smrg goto EXIT; 686659607e0Smrg } 687659607e0Smrg return TRUE; 6881450c749Smrg 689659607e0Smrg EXIT: 690659607e0Smrg xf86FlushInput(pInfo->fd); 691659607e0Smrg return FALSE; 692659607e0Smrg} 693659607e0Smrg 694659607e0Smrgstatic MouseProtocolID 695659607e0SmrgprobePs2ProtocolPnP(InputInfoPtr pInfo) 696659607e0Smrg{ 697659607e0Smrg unsigned char u; 698659607e0Smrg MouseProtocolID ret = PROT_UNKNOWN; 699659607e0Smrg 700659607e0Smrg xf86FlushInput(pInfo->fd); 701659607e0Smrg 702659607e0Smrg ps2DisableDataReporting(pInfo); 7031450c749Smrg 704659607e0Smrg if (ps2Reset(pInfo)) { /* Reset PS2 device */ 7051450c749Smrg unsigned char seq[] = { 243, 200, 243, 100, 243, 80 }; 7061450c749Smrg /* Try to identify Intelli Mouse */ 7071450c749Smrg if (ps2SendPacket(pInfo, seq, sizeof(seq))) { 7081450c749Smrg u = ps2GetDeviceID(pInfo); 7091450c749Smrg if (u == 0x03) { 7101450c749Smrg /* found IntelliMouse now try IntelliExplorer */ 7111450c749Smrg unsigned char im_seq[] = { 243, 200, 243, 200, 243, 80 }; 7121450c749Smrg if (ps2SendPacket(pInfo,im_seq,sizeof(im_seq))) { 7131450c749Smrg u = ps2GetDeviceID(pInfo); 7141450c749Smrg if (u == 0x04) 7151450c749Smrg ret = PROT_EXPPS2; 7161450c749Smrg else 7171450c749Smrg ret = PROT_IMPS2; 7181450c749Smrg } 7191450c749Smrg } else if (ps2Reset(pInfo)) /* reset again to find sane state */ 7201450c749Smrg ret = PROT_PS2; 7211450c749Smrg } 7221450c749Smrg 7231450c749Smrg if (ret != PROT_UNKNOWN) 7241450c749Smrg ps2EnableDataReporting(pInfo); 725659607e0Smrg } 726659607e0Smrg return ret; 727659607e0Smrg} 728659607e0Smrg 729659607e0Smrgstatic struct ps2protos { 7301450c749Smrg int Id; 731659607e0Smrg MouseProtocolID protoID; 732659607e0Smrg} ps2 [] = { 733659607e0Smrg { 0x0, PROT_PS2 }, 734659607e0Smrg { 0x3, PROT_IMPS2 }, 735659607e0Smrg { 0x4, PROT_EXPPS2 }, 736659607e0Smrg { -1 , PROT_UNKNOWN } 737659607e0Smrg}; 738659607e0Smrg 739659607e0Smrg 740659607e0Smrgstatic MouseProtocolID 741659607e0SmrggetPs2ProtocolPnP(InputInfoPtr pInfo) 742659607e0Smrg{ 743659607e0Smrg int Id; 744659607e0Smrg int i; 745659607e0Smrg MouseProtocolID proto; 746659607e0Smrg int count = 4; 747659607e0Smrg 748659607e0Smrg xf86FlushInput(pInfo->fd); 749659607e0Smrg 750659607e0Smrg while (--count) 7511450c749Smrg if (ps2DisableDataReporting(pInfo)) 7521450c749Smrg break; 7531450c749Smrg 754659607e0Smrg if (!count) { 7551450c749Smrg proto = PROT_UNKNOWN; 7561450c749Smrg goto EXIT; 757659607e0Smrg } 7581450c749Smrg 759659607e0Smrg if ((Id = ps2GetDeviceID(pInfo)) == -1) { 7601450c749Smrg proto = PROT_UNKNOWN; 7611450c749Smrg goto EXIT; 762659607e0Smrg } 763659607e0Smrg 764659607e0Smrg if (-1 == ps2EnableDataReporting(pInfo)) { 7651450c749Smrg proto = PROT_UNKNOWN; 7661450c749Smrg goto EXIT; 767659607e0Smrg } 768659607e0Smrg 769659607e0Smrg for (i = 0; ps2[i].protoID != PROT_UNKNOWN; i++) { 7701450c749Smrg if (ps2[i].Id == Id) { 7711450c749Smrg xf86MsgVerb(X_PROBED,2,"Found PS/2 proto ID %x\n",Id); 7721450c749Smrg proto = ps2[i].protoID; 7731450c749Smrg goto EXIT; 7741450c749Smrg } 775659607e0Smrg } 7761450c749Smrg 777659607e0Smrg proto = PROT_UNKNOWN; 778659607e0Smrg xf86Msg(X_ERROR,"Found unknown PS/2 proto ID %x\n",Id); 7791450c749Smrg 780659607e0Smrg EXIT: 781659607e0Smrg xf86FlushInput(pInfo->fd); 782659607e0Smrg return proto; 783659607e0Smrg} 784659607e0Smrg 785