pnp.c revision 6aab59a7
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 27659607e0Smrg#include <stdio.h> 28659607e0Smrg#include <string.h> 29659607e0Smrg#include <unistd.h> 30659607e0Smrg#define NEED_EVENTS 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 "xf86OSmouse.h" 40659607e0Smrg#include "mouse.h" 41659607e0Smrg#include "mousePriv.h" 42659607e0Smrg 43659607e0Smrg#ifdef MOUSEINITDEBUG 44659607e0Smrg# define DEBUG 45659607e0Smrg# define EXTMOUSEDEBUG 46659607e0Smrg#endif 47659607e0Smrg 48659607e0Smrg/* serial PnP ID string */ 49659607e0Smrgtypedef struct { 50659607e0Smrg int revision; /* PnP revision, 100 for 1.00 */ 51659607e0Smrg char *eisaid; /* EISA ID including mfr ID and product ID */ 52659607e0Smrg char *serial; /* serial No, optional */ 53659607e0Smrg char *class; /* device class, optional */ 54659607e0Smrg char *compat; /* list of compatible drivers, optional */ 55659607e0Smrg char *description; /* product description, optional */ 56659607e0Smrg int neisaid; /* length of the above fields... */ 57659607e0Smrg int nserial; 58659607e0Smrg int nclass; 59659607e0Smrg int ncompat; 60659607e0Smrg int ndescription; 61659607e0Smrg} pnpid_t; 62659607e0Smrg 63659607e0Smrg/* symbol table entry */ 64659607e0Smrgtypedef struct { 65659607e0Smrg char *name; 66659607e0Smrg MouseProtocolID val; 67659607e0Smrg} symtab_t; 68659607e0Smrg 69659607e0Smrg/* PnP EISA/product IDs */ 70659607e0Smrgstatic symtab_t pnpprod[] = { 716aab59a7Smrg { "KML0001", PROT_THINKING }, /* Kensington ThinkingMouse */ 72659607e0Smrg { "MSH0001", PROT_IMSERIAL }, /* MS IntelliMouse */ 73659607e0Smrg { "MSH0004", PROT_IMSERIAL }, /* MS IntelliMouse TrackBall */ 74659607e0Smrg { "KYEEZ00", PROT_MS }, /* Genius EZScroll */ 75659607e0Smrg { "KYE0001", PROT_MS }, /* Genius PnP Mouse */ 76659607e0Smrg { "KYE0002", PROT_MS }, /* MouseSystem (Genius?) SmartScroll */ 77659607e0Smrg { "KYE0003", PROT_IMSERIAL }, /* Genius NetMouse */ 78659607e0Smrg { "LGI800C", PROT_IMSERIAL }, /* Logitech MouseMan (4 button model) */ 79659607e0Smrg { "LGI8033", PROT_IMSERIAL }, /* Logitech Cordless MouseMan Wheel */ 80659607e0Smrg { "LGI8050", PROT_IMSERIAL }, /* Logitech MouseMan+ */ 81659607e0Smrg { "LGI8051", PROT_IMSERIAL }, /* Logitech FirstMouse+ */ 82659607e0Smrg { "LGI8001", PROT_LOGIMAN }, /* Logitech serial */ 83659607e0Smrg { "A4W0005", PROT_IMSERIAL }, /* A4 Tech 4D/4D+ Mouse */ 84659607e0Smrg { "PEC9802", PROT_IMSERIAL }, /* 8D Scroll Mouse */ 85659607e0Smrg 86659607e0Smrg { "PNP0F00", PROT_BM }, /* MS bus */ 87659607e0Smrg { "PNP0F01", PROT_MS }, /* MS serial */ 88659607e0Smrg { "PNP0F02", PROT_BM }, /* MS InPort */ 89659607e0Smrg { "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 */ 94659607e0Smrg { "PNP0F04", PROT_MSC }, /* MouseSystems */ 95659607e0Smrg { "PNP0F05", PROT_MSC }, /* MouseSystems */ 96659607e0Smrg#ifdef notyet 97659607e0Smrg { "PNP0F06", PROT_??? }, /* Genius Mouse */ 98659607e0Smrg { "PNP0F07", PROT_??? }, /* Genius Mouse */ 99659607e0Smrg#endif 100659607e0Smrg { "PNP0F08", PROT_LOGIMAN }, /* Logitech serial */ 101659607e0Smrg { "PNP0F09", PROT_MS }, /* MS BallPoint serial */ 102659607e0Smrg { "PNP0F0A", PROT_MS }, /* MS PnP serial */ 103659607e0Smrg { "PNP0F0B", PROT_MS }, /* MS PnP BallPoint serial */ 1046aab59a7Smrg { "PNP0F0C", PROT_MS }, /* MS serial compatible */ 1056aab59a7Smrg { "PNP0F0D", PROT_BM }, /* MS InPort compatible */ 1066aab59a7Smrg { "PNP0F0E", PROT_PS2 }, /* MS PS/2 compatible */ 1076aab59a7Smrg { "PNP0F0F", PROT_MS }, /* MS BallPoint compatible */ 108659607e0Smrg#ifdef notyet 109659607e0Smrg { "PNP0F10", PROT_??? }, /* TI QuickPort */ 110659607e0Smrg#endif 1116aab59a7Smrg { "PNP0F11", PROT_BM }, /* MS bus compatible */ 112659607e0Smrg { "PNP0F12", PROT_PS2 }, /* Logitech PS/2 */ 113659607e0Smrg { "PNP0F13", PROT_PS2 }, /* PS/2 */ 114659607e0Smrg#ifdef notyet 115659607e0Smrg { "PNP0F14", PROT_??? }, /* MS Kids Mouse */ 116659607e0Smrg#endif 117659607e0Smrg { "PNP0F15", PROT_BM }, /* Logitech bus */ 118659607e0Smrg#ifdef notyet 119659607e0Smrg { "PNP0F16", PROT_??? }, /* Logitech SWIFT */ 120659607e0Smrg#endif 121659607e0Smrg { "PNP0F17", PROT_LOGIMAN }, /* Logitech serial compat */ 122659607e0Smrg { "PNP0F18", PROT_BM }, /* Logitech bus compatible */ 123659607e0Smrg { "PNP0F19", PROT_PS2 }, /* Logitech PS/2 compatible */ 124659607e0Smrg#ifdef notyet 125659607e0Smrg { "PNP0F1A", PROT_??? }, /* Logitech SWIFT compatible */ 126659607e0Smrg { "PNP0F1B", PROT_??? }, /* HP Omnibook */ 127659607e0Smrg { "PNP0F1C", PROT_??? }, /* Compaq LTE TrackBall PS/2 */ 128659607e0Smrg { "PNP0F1D", PROT_??? }, /* Compaq LTE TrackBall serial */ 129659607e0Smrg { "PNP0F1E", PROT_??? }, /* MS Kids Trackball */ 130659607e0Smrg#endif 131659607e0Smrg { NULL, PROT_UNKNOWN }, 132659607e0Smrg}; 133659607e0Smrg 134659607e0Smrgstatic const char *pnpSerial[] = { 135659607e0Smrg "BaudRate", "1200", 136659607e0Smrg "DataBits", "7", 137659607e0Smrg "StopBits", "1", 138659607e0Smrg "Parity", "None", 139659607e0Smrg "FlowControl", "None", 140659607e0Smrg "VTime", "0", 141659607e0Smrg "VMin", "1", 142659607e0Smrg 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{ 156659607e0Smrg 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 162659607e0Smrg if ((len = pnpgets(pInfo, buf, &prePNP)) > 0) 163659607e0Smrg { 164659607e0Smrg if (!prePNP) { 165659607e0Smrg if (pnpparse(pInfo, &pnpid, buf, len) && 166659607e0Smrg (t = pnpproto(&pnpid)) != NULL) { 167659607e0Smrg xf86MsgVerb(X_INFO, 2, "%s: PnP-detected protocol ID: %d\n", 168659607e0Smrg pInfo->name, t->val); 169659607e0Smrg return (t->val); 170659607e0Smrg } 171659607e0Smrg } else 172659607e0Smrg 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; 184659607e0Smrg 185659607e0Smrg if ((val = MouseGetSerialPnpProtocol(pInfo)) != PROT_UNKNOWN) { 186659607e0Smrg if (val == MouseGetSerialPnpProtocol(pInfo)) 187659607e0Smrg return val; 188659607e0Smrg } 189659607e0Smrg 190659607e0Smrg last = mPriv->pnpLast; 191659607e0Smrg mPriv->pnpLast = currentTime.milliseconds; 192659607e0Smrg 193659607e0Smrg if (last) { 194659607e0Smrg if (last - currentTime.milliseconds < 100 195659607e0Smrg || (mPriv->disablePnPauto 196659607e0Smrg && (last - currentTime.milliseconds < 10000))) { 197659607e0Smrg#ifdef EXTMOUSEDEBUG 198659607e0Smrg xf86ErrorF("Mouse: Disabling PnP\n"); 199659607e0Smrg#endif 200659607e0Smrg mPriv->disablePnPauto = TRUE; 201659607e0Smrg return PROT_UNKNOWN; 202659607e0Smrg } 203659607e0Smrg } 204659607e0Smrg 205659607e0Smrg#ifdef EXTMOUSEDEBUG 206659607e0Smrg if (mPriv->disablePnPauto) 207659607e0Smrg xf86ErrorF("Mouse: Enabling PnP\n"); 208659607e0Smrg#endif 209659607e0Smrg mPriv->disablePnPauto = FALSE; 210659607e0Smrg 211659607e0Smrg if (mPriv->soft) 212659607e0Smrg return getPs2ProtocolPnP(pInfo); 213659607e0Smrg else 214659607e0Smrg return probePs2ProtocolPnP(pInfo); 215659607e0Smrg} 216659607e0Smrg 217659607e0Smrg/* 218659607e0Smrg * Try to elicit a PnP ID as described in 219659607e0Smrg * 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 224659607e0Smrg * the driver software monitors the com port for dynamic connection or 225659607e0Smrg * removal of a device at the port, because `moused' simply quits if no 226659607e0Smrg * device is found. 227659607e0Smrg * 228659607e0Smrg * In addition, as PnP COM device enumeration procedure slightly has 229659607e0Smrg * changed since its first publication, devices which follow earlier 230659607e0Smrg * 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; 239659607e0Smrg 240659607e0Smrg#if 0 241659607e0Smrg /* 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) 249659607e0Smrg return 0; 250659607e0Smrg i |= XF86_M_DTR; /* DTR = 1 */ 251659607e0Smrg i &= ~XF86_M_RTS; /* RTS = 0 */ 252659607e0Smrg if (xf86SetSerialModemState(pInfo->fd, i) == -1) 253659607e0Smrg goto disconnect_idle; 254659607e0Smrg usleep(200000); 255659607e0Smrg if ((i = xf86GetSerialModemState(pInfo->fd)) == -1 || 256659607e0Smrg (i & XF86_M_DSR) == 0) 257659607e0Smrg goto disconnect_idle; 258659607e0Smrg 259659607e0Smrg /* port setup, 1st phase (2.1.3) */ 260659607e0Smrg pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1); 261659607e0Smrg xf86SetSerial(pInfo->fd, pnpOpts); 262659607e0Smrg i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */ 263659607e0Smrg xf86SerialModemClearBits(pInfo->fd, i); 264659607e0Smrg usleep(200000); 265659607e0Smrg 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); 271659607e0Smrg 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 277659607e0Smrg /* port setup, 2nd phase (2.1.5) */ 278659607e0Smrg i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */ 279659607e0Smrg xf86SerialModemClearBits(pInfo->fd, i); 280659607e0Smrg usleep(200000); 281659607e0Smrg 2826aab59a7Smrg /* wait for response, 2nd phase (2.1.6) */ 283659607e0Smrg xf86FlushInput(pInfo->fd); 284659607e0Smrg i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */ 285659607e0Smrg xf86SerialModemSetBits(pInfo->fd, i); 286659607e0Smrg 287659607e0Smrg /* try to read something */ 288659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 289659607e0Smrg 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) 297659607e0Smrg return 0; 298659607e0Smrg i |= XF86_M_DTR; /* DTR = 1 */ 299659607e0Smrg i &= ~XF86_M_RTS; /* RTS = 0 */ 300659607e0Smrg if (xf86SetSerialModemState(pInfo->fd, i) == -1) 301659607e0Smrg 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); 309659607e0Smrg 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; 320659607e0Smrg 321659607e0Smrg usleep(200000); /* the mouse must send `Begin ID' within 200msec */ 322659607e0Smrg while (xf86ReadSerial(pInfo->fd, &c, 1) == 1) { 323659607e0Smrg /* we may see "M", or "M3..." before `Begin ID' */ 324659607e0Smrg if (c == 'M') 325659607e0Smrg *prePNP = TRUE; 326659607e0Smrg 327659607e0Smrg if ((c == 0x08) || (c == 0x28)) { /* Begin ID */ 328659607e0Smrg *prePNP = FALSE; 329659607e0Smrg buf[0] = c; 330659607e0Smrg i = 1; 331659607e0Smrg break; 332659607e0Smrg } 333659607e0Smrg if (*prePNP) 334659607e0Smrg buf[i++] = c; 335659607e0Smrg 336659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 337659607e0Smrg break; 338659607e0Smrg } 339659607e0Smrg if (i <= 0) { 340659607e0Smrg /* we haven't seen `Begin ID' in time... */ 341659607e0Smrg goto connect_idle; 342659607e0Smrg } 343659607e0Smrg if (*prePNP) 344659607e0Smrg return i; 345659607e0Smrg 346659607e0Smrg ++c; /* make it `End ID' */ 347659607e0Smrg for (;;) { 348659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 349659607e0Smrg break; 350659607e0Smrg 351659607e0Smrg xf86ReadSerial(pInfo->fd, &buf[i], 1); 352659607e0Smrg if (buf[i++] == c) /* End ID */ 353659607e0Smrg break; 354659607e0Smrg if (i >= 256) 355659607e0Smrg break; 356659607e0Smrg } 357659607e0Smrg if (buf[i - 1] != c) 358659607e0Smrg goto connect_idle; 359659607e0Smrg return i; 360659607e0Smrg 361659607e0Smrg /* 362659607e0Smrg * According to PnP spec, we should set DTR = 1 and RTS = 0 while 363659607e0Smrg * in idle state. But, `moused' shall set DTR = RTS = 1 and proceed, 364659607e0Smrg * assuming there is something at the port even if it didn't 365659607e0Smrg * respond to the PnP enumeration procedure. 366659607e0Smrg */ 367659607e0Smrgdisconnect_idle: 368659607e0Smrg 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) { 398659607e0Smrg sum += buf[i]; 399659607e0Smrg buf[i] += offset; 400659607e0Smrg } 401659607e0Smrg sum += buf[len - 1]; 402659607e0Smrg for (; i < len; ++i) 403659607e0Smrg buf[i] += offset; 404659607e0Smrg xf86MsgVerb(X_INFO, 2, "%s: PnP ID string: `%*.*s'\n", pInfo->name, 405659607e0Smrg 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, 412659607e0Smrg id->revision / 100, id->revision % 100); 413659607e0Smrg 414659607e0Smrg /* EISA vender 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] == '\\') 424659607e0Smrg break; 425659607e0Smrg } 426659607e0Smrg if (i >= len) 427659607e0Smrg i -= 3; 428659607e0Smrg if (i - j == 8) { 429659607e0Smrg id->serial = &buf[j]; 430659607e0Smrg id->nserial = 8; 431659607e0Smrg } 432659607e0Smrg } 433659607e0Smrg if (buf[i] == '\\') { 434659607e0Smrg /* PnP class */ 435659607e0Smrg for (j = ++i; i < len; ++i) { 436659607e0Smrg if (buf[i] == '\\') 437659607e0Smrg break; 438659607e0Smrg } 439659607e0Smrg if (i >= len) 440659607e0Smrg i -= 3; 441659607e0Smrg if (i > j + 1) { 442659607e0Smrg id->class = &buf[j]; 443659607e0Smrg id->nclass = i - j; 444659607e0Smrg } 445659607e0Smrg } 446659607e0Smrg if (buf[i] == '\\') { 447659607e0Smrg /* compatible driver */ 448659607e0Smrg for (j = ++i; i < len; ++i) { 449659607e0Smrg if (buf[i] == '\\') 450659607e0Smrg break; 451659607e0Smrg } 452659607e0Smrg /* 453659607e0Smrg * PnP COM spec prior to v0.96 allowed '*' in this field, 454659607e0Smrg * it's not allowed now; just ignore it. 455659607e0Smrg */ 456659607e0Smrg if (buf[j] == '*') 457659607e0Smrg ++j; 458659607e0Smrg if (i >= len) 459659607e0Smrg i -= 3; 460659607e0Smrg if (i > j + 1) { 461659607e0Smrg id->compat = &buf[j]; 462659607e0Smrg id->ncompat = i - j; 463659607e0Smrg } 464659607e0Smrg } 465659607e0Smrg if (buf[i] == '\\') { 466659607e0Smrg /* product description */ 467659607e0Smrg for (j = ++i; i < len; ++i) { 468659607e0Smrg if (buf[i] == ';') 469659607e0Smrg break; 470659607e0Smrg } 471659607e0Smrg if (i >= len) 472659607e0Smrg i -= 3; 473659607e0Smrg 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) 481659607e0Smrg || (id->ncompat > 0) || (id->ndescription > 0)) { 482659607e0Smrg 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 486659607e0Smrg /* 487659607e0Smrg * Checksum error!! 488659607e0Smrg * I found some mice do not comply with the PnP COM device 489659607e0Smrg * spec regarding checksum... XXX 490659607e0Smrg */ 491659607e0Smrg 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') 504659607e0Smrg 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) 516659607e0Smrg if (strncmp(id->class, "MOUSE", id->nclass) != 0) 517659607e0Smrg /* this is not a mouse! */ 518659607e0Smrg return NULL; 519659607e0Smrg 520659607e0Smrg if (id->neisaid > 0) { 521659607e0Smrg t = gettoken(pnpprod, id->eisaid, id->neisaid); 522659607e0Smrg 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) 531659607e0Smrg return NULL; 532659607e0Smrg for (i = 0; i < id->ncompat; ++i) { 533659607e0Smrg for (j = i; id->compat[i] != ','; ++i) 534659607e0Smrg if (i >= id->ncompat) 535659607e0Smrg break; 536659607e0Smrg if (i > j) { 537659607e0Smrg t = gettoken(pnpprod, id->compat + j, i - j); 538659607e0Smrg if (t->val != -1) 539659607e0Smrg return t; 540659607e0Smrg } 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) { 554659607e0Smrg if (strncmp(tab[i].name, s, len) == 0) 555659607e0Smrg 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) 567659607e0Smrg 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; 585659607e0Smrg 586659607e0Smrg#ifdef DEBUG 587659607e0Smrg xf86ErrorF("Ps/2 data package:"); 588659607e0Smrg for (i = 0; i < len; i++) 589659607e0Smrg xf86ErrorF(" %x", *(bytes + i)); 590659607e0Smrg xf86ErrorF("\n"); 591659607e0Smrg#endif 592659607e0Smrg 593659607e0Smrg for (i = 0; i < len; i++) { 594659607e0Smrg for (j = 0; j < 10; j++) { 595659607e0Smrg xf86WriteSerial(pInfo->fd, bytes + i, 1); 596659607e0Smrg usleep(10000); 597659607e0Smrg if (!readMouse(pInfo,&c)) { 598659607e0Smrg#ifdef DEBUG 599659607e0Smrg xf86ErrorF("sending 0x%x to PS/2 unsuccessful\n",*(bytes + i)); 600659607e0Smrg#endif 601659607e0Smrg return FALSE; 602659607e0Smrg } 603659607e0Smrg#ifdef DEBUG 604659607e0Smrg xf86ErrorF("Recieved: 0x%x\n",c); 605659607e0Smrg#endif 606659607e0Smrg if (c == 0xFA) /* ACK */ 607659607e0Smrg break; 608659607e0Smrg 609659607e0Smrg if (c == 0xFE) /* resend */ 610659607e0Smrg continue; 611659607e0Smrg 612659607e0Smrg 613659607e0Smrg if (c == 0xFC) /* error */ 614659607e0Smrg return FALSE; 615659607e0Smrg 616659607e0Smrg /* Some mice accidently enter wrap mode during init */ 617659607e0Smrg if (c == *(bytes + i) /* wrap mode */ 618659607e0Smrg && (*(bytes + i) != 0xEC)) /* avoid recursion */ 619659607e0Smrg ps2DisableWrapMode(pInfo); 620659607e0Smrg 621659607e0Smrg return FALSE; 622659607e0Smrg } 623659607e0Smrg if (j == 10) 624659607e0Smrg return FALSE; 625659607e0Smrg } 626659607e0Smrg 627659607e0Smrg return TRUE; 628659607e0Smrg} 629659607e0Smrg 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); 652659607e0Smrg if (!ps2SendPacket(pInfo, packet, sizeof(packet))) 653659607e0Smrg return -1; 654659607e0Smrg while (1) { 655659607e0Smrg if (!readMouse(pInfo,&u)) 656659607e0Smrg return -1; 657659607e0Smrg if (u != 0xFA) 658659607e0Smrg 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 676659607e0Smrg if (!ps2SendPacket(pInfo, packet, sizeof(packet))) 677659607e0Smrg return FALSE; 678659607e0Smrg /* we need a little delay here */ 679659607e0Smrg xf86WaitForInput(pInfo->fd, 500000); 680659607e0Smrg for (i = 0; i < sizeof(reply) ; i++) { 681659607e0Smrg if (!readMouse(pInfo,&u)) { 682659607e0Smrg goto EXIT; 683659607e0Smrg } 684659607e0Smrg if (u != reply[i]) 685659607e0Smrg goto EXIT; 686659607e0Smrg } 687659607e0Smrg return TRUE; 688659607e0Smrg 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); 703659607e0Smrg 704659607e0Smrg if (ps2Reset(pInfo)) { /* Reset PS2 device */ 705659607e0Smrg unsigned char seq[] = { 243, 200, 243, 100, 243, 80 }; 706659607e0Smrg /* Try to identify Intelli Mouse */ 707659607e0Smrg if (ps2SendPacket(pInfo, seq, sizeof(seq))) { 708659607e0Smrg u = ps2GetDeviceID(pInfo); 709659607e0Smrg if (u == 0x03) { 710659607e0Smrg /* found IntelliMouse now try IntelliExplorer */ 711659607e0Smrg unsigned char seq[] = { 243, 200, 243, 200, 243, 80 }; 712659607e0Smrg if (ps2SendPacket(pInfo,seq,sizeof(seq))) { 713659607e0Smrg u = ps2GetDeviceID(pInfo); 714659607e0Smrg if (u == 0x04) 715659607e0Smrg ret = PROT_EXPPS2; 716659607e0Smrg else 717659607e0Smrg ret = PROT_IMPS2; 718659607e0Smrg } 719659607e0Smrg } else if (ps2Reset(pInfo)) /* reset again to find sane state */ 720659607e0Smrg ret = PROT_PS2; 721659607e0Smrg } 722659607e0Smrg 723659607e0Smrg if (ret != PROT_UNKNOWN) 724659607e0Smrg ps2EnableDataReporting(pInfo); 725659607e0Smrg } 726659607e0Smrg return ret; 727659607e0Smrg} 728659607e0Smrg 729659607e0Smrgstatic struct ps2protos { 730659607e0Smrg 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) 751659607e0Smrg if (ps2DisableDataReporting(pInfo)) 752659607e0Smrg break; 753659607e0Smrg 754659607e0Smrg if (!count) { 755659607e0Smrg proto = PROT_UNKNOWN; 756659607e0Smrg goto EXIT; 757659607e0Smrg } 758659607e0Smrg 759659607e0Smrg if ((Id = ps2GetDeviceID(pInfo)) == -1) { 760659607e0Smrg proto = PROT_UNKNOWN; 761659607e0Smrg goto EXIT; 762659607e0Smrg } 763659607e0Smrg 764659607e0Smrg if (-1 == ps2EnableDataReporting(pInfo)) { 765659607e0Smrg proto = PROT_UNKNOWN; 766659607e0Smrg goto EXIT; 767659607e0Smrg } 768659607e0Smrg 769659607e0Smrg for (i = 0; ps2[i].protoID != PROT_UNKNOWN; i++) { 770659607e0Smrg if (ps2[i].Id == Id) { 771659607e0Smrg xf86MsgVerb(X_PROBED,2,"Found PS/2 proto ID %x\n",Id); 772659607e0Smrg proto = ps2[i].protoID; 773659607e0Smrg goto EXIT; 774659607e0Smrg } 775659607e0Smrg } 776659607e0Smrg 777659607e0Smrg proto = PROT_UNKNOWN; 778659607e0Smrg xf86Msg(X_ERROR,"Found unknown PS/2 proto ID %x\n",Id); 779659607e0Smrg 780659607e0Smrg EXIT: 781659607e0Smrg xf86FlushInput(pInfo->fd); 782659607e0Smrg return proto; 783659607e0Smrg} 784659607e0Smrg 785