pnp.c revision b73be646
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 "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 */ 78b73be646Smrg { "KYE0004", PROT_IMSERIAL }, /* Genius NetScroll+ (serial) */ 79659607e0Smrg { "LGI800C", PROT_IMSERIAL }, /* Logitech MouseMan (4 button model) */ 80659607e0Smrg { "LGI8033", PROT_IMSERIAL }, /* Logitech Cordless MouseMan Wheel */ 81659607e0Smrg { "LGI8050", PROT_IMSERIAL }, /* Logitech MouseMan+ */ 82659607e0Smrg { "LGI8051", PROT_IMSERIAL }, /* Logitech FirstMouse+ */ 83659607e0Smrg { "LGI8001", PROT_LOGIMAN }, /* Logitech serial */ 84659607e0Smrg { "A4W0005", PROT_IMSERIAL }, /* A4 Tech 4D/4D+ Mouse */ 85659607e0Smrg { "PEC9802", PROT_IMSERIAL }, /* 8D Scroll Mouse */ 86659607e0Smrg 87659607e0Smrg { "PNP0F00", PROT_BM }, /* MS bus */ 88659607e0Smrg { "PNP0F01", PROT_MS }, /* MS serial */ 89659607e0Smrg { "PNP0F02", PROT_BM }, /* MS InPort */ 90659607e0Smrg { "PNP0F03", PROT_PS2 }, /* MS PS/2 */ 91659607e0Smrg /* 92659607e0Smrg * EzScroll returns PNP0F04 in the compatible device field; but it 93659607e0Smrg * doesn't look compatible... XXX 94659607e0Smrg */ 95659607e0Smrg { "PNP0F04", PROT_MSC }, /* MouseSystems */ 96659607e0Smrg { "PNP0F05", PROT_MSC }, /* MouseSystems */ 97659607e0Smrg#ifdef notyet 98659607e0Smrg { "PNP0F06", PROT_??? }, /* Genius Mouse */ 99659607e0Smrg { "PNP0F07", PROT_??? }, /* Genius Mouse */ 100659607e0Smrg#endif 101659607e0Smrg { "PNP0F08", PROT_LOGIMAN }, /* Logitech serial */ 102659607e0Smrg { "PNP0F09", PROT_MS }, /* MS BallPoint serial */ 103659607e0Smrg { "PNP0F0A", PROT_MS }, /* MS PnP serial */ 104659607e0Smrg { "PNP0F0B", PROT_MS }, /* MS PnP BallPoint serial */ 1056aab59a7Smrg { "PNP0F0C", PROT_MS }, /* MS serial compatible */ 1066aab59a7Smrg { "PNP0F0D", PROT_BM }, /* MS InPort compatible */ 1076aab59a7Smrg { "PNP0F0E", PROT_PS2 }, /* MS PS/2 compatible */ 1086aab59a7Smrg { "PNP0F0F", PROT_MS }, /* MS BallPoint compatible */ 109659607e0Smrg#ifdef notyet 110659607e0Smrg { "PNP0F10", PROT_??? }, /* TI QuickPort */ 111659607e0Smrg#endif 1126aab59a7Smrg { "PNP0F11", PROT_BM }, /* MS bus compatible */ 113659607e0Smrg { "PNP0F12", PROT_PS2 }, /* Logitech PS/2 */ 114659607e0Smrg { "PNP0F13", PROT_PS2 }, /* PS/2 */ 115659607e0Smrg#ifdef notyet 116659607e0Smrg { "PNP0F14", PROT_??? }, /* MS Kids Mouse */ 117659607e0Smrg#endif 118659607e0Smrg { "PNP0F15", PROT_BM }, /* Logitech bus */ 119659607e0Smrg#ifdef notyet 120659607e0Smrg { "PNP0F16", PROT_??? }, /* Logitech SWIFT */ 121659607e0Smrg#endif 122659607e0Smrg { "PNP0F17", PROT_LOGIMAN }, /* Logitech serial compat */ 123659607e0Smrg { "PNP0F18", PROT_BM }, /* Logitech bus compatible */ 124659607e0Smrg { "PNP0F19", PROT_PS2 }, /* Logitech PS/2 compatible */ 125659607e0Smrg#ifdef notyet 126659607e0Smrg { "PNP0F1A", PROT_??? }, /* Logitech SWIFT compatible */ 127659607e0Smrg { "PNP0F1B", PROT_??? }, /* HP Omnibook */ 128659607e0Smrg { "PNP0F1C", PROT_??? }, /* Compaq LTE TrackBall PS/2 */ 129659607e0Smrg { "PNP0F1D", PROT_??? }, /* Compaq LTE TrackBall serial */ 130659607e0Smrg { "PNP0F1E", PROT_??? }, /* MS Kids Trackball */ 131659607e0Smrg#endif 132659607e0Smrg { NULL, PROT_UNKNOWN }, 133659607e0Smrg}; 134659607e0Smrg 135659607e0Smrgstatic const char *pnpSerial[] = { 136659607e0Smrg "BaudRate", "1200", 137659607e0Smrg "DataBits", "7", 138659607e0Smrg "StopBits", "1", 139659607e0Smrg "Parity", "None", 140659607e0Smrg "FlowControl", "None", 141659607e0Smrg "VTime", "0", 142659607e0Smrg "VMin", "1", 143659607e0Smrg NULL 144659607e0Smrg}; 145659607e0Smrg 146659607e0Smrgstatic int pnpgets(InputInfoPtr, char *, Bool *prePNP); 147659607e0Smrgstatic int pnpparse(InputInfoPtr, pnpid_t *, char *, int); 148659607e0Smrgstatic MouseProtocolID prepnpparse(InputInfoPtr pInfo, char *buf); 149659607e0Smrgstatic symtab_t *pnpproto(pnpid_t *); 150659607e0Smrgstatic symtab_t *gettoken(symtab_t *, char *, int); 151659607e0Smrgstatic MouseProtocolID getPs2ProtocolPnP(InputInfoPtr pInfo); 152659607e0Smrgstatic MouseProtocolID probePs2ProtocolPnP(InputInfoPtr pInfo); 153659607e0Smrg 154659607e0Smrgstatic MouseProtocolID 155659607e0SmrgMouseGetSerialPnpProtocol(InputInfoPtr pInfo) 156659607e0Smrg{ 157659607e0Smrg char buf[256]; /* PnP ID string may be up to 256 bytes long */ 158659607e0Smrg pnpid_t pnpid; 159659607e0Smrg symtab_t *t; 160659607e0Smrg int len; 161659607e0Smrg Bool prePNP; 162659607e0Smrg 163659607e0Smrg if ((len = pnpgets(pInfo, buf, &prePNP)) > 0) 164659607e0Smrg { 165659607e0Smrg if (!prePNP) { 166659607e0Smrg if (pnpparse(pInfo, &pnpid, buf, len) && 167659607e0Smrg (t = pnpproto(&pnpid)) != NULL) { 168659607e0Smrg xf86MsgVerb(X_INFO, 2, "%s: PnP-detected protocol ID: %d\n", 169659607e0Smrg pInfo->name, t->val); 170659607e0Smrg return (t->val); 171659607e0Smrg } 172659607e0Smrg } else 173659607e0Smrg return prepnpparse(pInfo,buf); 174659607e0Smrg } 175659607e0Smrg return PROT_UNKNOWN; 176659607e0Smrg} 177659607e0Smrg 178659607e0SmrgMouseProtocolID 179659607e0SmrgMouseGetPnpProtocol(InputInfoPtr pInfo) 180659607e0Smrg{ 181659607e0Smrg MouseDevPtr pMse = pInfo->private; 182659607e0Smrg mousePrivPtr mPriv = (mousePrivPtr)pMse->mousePriv; 183659607e0Smrg MouseProtocolID val; 184659607e0Smrg CARD32 last; 185659607e0Smrg 186659607e0Smrg if ((val = MouseGetSerialPnpProtocol(pInfo)) != PROT_UNKNOWN) { 187659607e0Smrg if (val == MouseGetSerialPnpProtocol(pInfo)) 188659607e0Smrg return val; 189659607e0Smrg } 190659607e0Smrg 191659607e0Smrg last = mPriv->pnpLast; 192659607e0Smrg mPriv->pnpLast = currentTime.milliseconds; 193659607e0Smrg 194659607e0Smrg if (last) { 195659607e0Smrg if (last - currentTime.milliseconds < 100 196659607e0Smrg || (mPriv->disablePnPauto 197659607e0Smrg && (last - currentTime.milliseconds < 10000))) { 198659607e0Smrg#ifdef EXTMOUSEDEBUG 199659607e0Smrg xf86ErrorF("Mouse: Disabling PnP\n"); 200659607e0Smrg#endif 201659607e0Smrg mPriv->disablePnPauto = TRUE; 202659607e0Smrg return PROT_UNKNOWN; 203659607e0Smrg } 204659607e0Smrg } 205659607e0Smrg 206659607e0Smrg#ifdef EXTMOUSEDEBUG 207659607e0Smrg if (mPriv->disablePnPauto) 208659607e0Smrg xf86ErrorF("Mouse: Enabling PnP\n"); 209659607e0Smrg#endif 210659607e0Smrg mPriv->disablePnPauto = FALSE; 211659607e0Smrg 212659607e0Smrg if (mPriv->soft) 213659607e0Smrg return getPs2ProtocolPnP(pInfo); 214659607e0Smrg else 215659607e0Smrg return probePs2ProtocolPnP(pInfo); 216659607e0Smrg} 217659607e0Smrg 218659607e0Smrg/* 219659607e0Smrg * Try to elicit a PnP ID as described in 220659607e0Smrg * Microsoft, Hayes: "Plug and Play External COM Device Specification, 221659607e0Smrg * rev 1.00", 1995. 222659607e0Smrg * 223659607e0Smrg * The routine does not fully implement the COM Enumerator as per Section 224659607e0Smrg * 2.1 of the document. In particular, we don't have idle state in which 225659607e0Smrg * the driver software monitors the com port for dynamic connection or 226659607e0Smrg * removal of a device at the port, because `moused' simply quits if no 227659607e0Smrg * device is found. 228659607e0Smrg * 229659607e0Smrg * In addition, as PnP COM device enumeration procedure slightly has 230659607e0Smrg * changed since its first publication, devices which follow earlier 231659607e0Smrg * revisions of the above spec. may fail to respond if the rev 1.0 232659607e0Smrg * procedure is used. XXX 233659607e0Smrg */ 234659607e0Smrgstatic int 235659607e0Smrgpnpgets(InputInfoPtr pInfo, char *buf, Bool *prePNP) 236659607e0Smrg{ 237659607e0Smrg int i; 238659607e0Smrg char c; 239659607e0Smrg pointer pnpOpts; 240659607e0Smrg 241659607e0Smrg#if 0 242659607e0Smrg /* 243659607e0Smrg * This is the procedure described in rev 1.0 of PnP COM device spec. 2446aab59a7Smrg * Unfortunately, some devices which conform to earlier revisions of 245659607e0Smrg * the spec gets confused and do not return the ID string... 246659607e0Smrg */ 247659607e0Smrg 248659607e0Smrg /* port initialization (2.1.2) */ 249659607e0Smrg if ((i = xf86GetSerialModemState(pInfo->fd)) == -1) 250659607e0Smrg return 0; 251659607e0Smrg i |= XF86_M_DTR; /* DTR = 1 */ 252659607e0Smrg i &= ~XF86_M_RTS; /* RTS = 0 */ 253659607e0Smrg if (xf86SetSerialModemState(pInfo->fd, i) == -1) 254659607e0Smrg goto disconnect_idle; 255659607e0Smrg usleep(200000); 256659607e0Smrg if ((i = xf86GetSerialModemState(pInfo->fd)) == -1 || 257659607e0Smrg (i & XF86_M_DSR) == 0) 258659607e0Smrg goto disconnect_idle; 259659607e0Smrg 260659607e0Smrg /* port setup, 1st phase (2.1.3) */ 261659607e0Smrg pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1); 262659607e0Smrg xf86SetSerial(pInfo->fd, pnpOpts); 263659607e0Smrg i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */ 264659607e0Smrg xf86SerialModemClearBits(pInfo->fd, i); 265659607e0Smrg usleep(200000); 266659607e0Smrg i = TIOCM_DTR; /* DTR = 1, RTS = 0 */ 267659607e0Smrg xf86SerialModemSetBits(pInfo->fd, i); 268659607e0Smrg usleep(200000); 269659607e0Smrg 270659607e0Smrg /* wait for response, 1st phase (2.1.4) */ 271659607e0Smrg xf86FlushInput(pInfo->fd); 272659607e0Smrg i = TIOCM_RTS; /* DTR = 1, RTS = 1 */ 273659607e0Smrg xf86SerialModemSetBits(pInfo->fd, i); 274659607e0Smrg 275659607e0Smrg /* try to read something */ 276659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) { 277659607e0Smrg 278659607e0Smrg /* port setup, 2nd phase (2.1.5) */ 279659607e0Smrg i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */ 280659607e0Smrg xf86SerialModemClearBits(pInfo->fd, i); 281659607e0Smrg usleep(200000); 282659607e0Smrg 2836aab59a7Smrg /* wait for response, 2nd phase (2.1.6) */ 284659607e0Smrg xf86FlushInput(pInfo->fd); 285659607e0Smrg i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */ 286659607e0Smrg xf86SerialModemSetBits(pInfo->fd, i); 287659607e0Smrg 288659607e0Smrg /* try to read something */ 289659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 290659607e0Smrg goto connect_idle; 291659607e0Smrg } 292659607e0Smrg#else 293659607e0Smrg /* 294659607e0Smrg * This is a simplified procedure; it simply toggles RTS. 295659607e0Smrg */ 296659607e0Smrg 297659607e0Smrg if ((i = xf86GetSerialModemState(pInfo->fd)) == -1) 298659607e0Smrg return 0; 299659607e0Smrg i |= XF86_M_DTR; /* DTR = 1 */ 300659607e0Smrg i &= ~XF86_M_RTS; /* RTS = 0 */ 301659607e0Smrg if (xf86SetSerialModemState(pInfo->fd, i) == -1) 302659607e0Smrg goto disconnect_idle; 303659607e0Smrg usleep(200000); 304659607e0Smrg 305659607e0Smrg pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1); 306659607e0Smrg xf86SetSerial(pInfo->fd, pnpOpts); 307659607e0Smrg 3086aab59a7Smrg /* wait for response */ 309659607e0Smrg xf86FlushInput(pInfo->fd); 310659607e0Smrg i = XF86_M_DTR | XF86_M_RTS; /* DTR = 1, RTS = 1 */ 311659607e0Smrg xf86SerialModemSetBits(pInfo->fd, i); 312659607e0Smrg 313659607e0Smrg /* try to read something */ 314659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 315659607e0Smrg goto connect_idle; 316659607e0Smrg#endif 317659607e0Smrg 318659607e0Smrg /* collect PnP COM device ID (2.1.7) */ 319659607e0Smrg i = 0; 320659607e0Smrg *prePNP = FALSE; 321659607e0Smrg 322659607e0Smrg usleep(200000); /* the mouse must send `Begin ID' within 200msec */ 323659607e0Smrg while (xf86ReadSerial(pInfo->fd, &c, 1) == 1) { 324659607e0Smrg /* we may see "M", or "M3..." before `Begin ID' */ 325659607e0Smrg if (c == 'M') 326659607e0Smrg *prePNP = TRUE; 327659607e0Smrg 328659607e0Smrg if ((c == 0x08) || (c == 0x28)) { /* Begin ID */ 329659607e0Smrg *prePNP = FALSE; 330659607e0Smrg buf[0] = c; 331659607e0Smrg i = 1; 332659607e0Smrg break; 333659607e0Smrg } 334659607e0Smrg if (*prePNP) 335659607e0Smrg buf[i++] = c; 336659607e0Smrg 337659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 338659607e0Smrg break; 339659607e0Smrg } 340659607e0Smrg if (i <= 0) { 341659607e0Smrg /* we haven't seen `Begin ID' in time... */ 342659607e0Smrg goto connect_idle; 343659607e0Smrg } 344659607e0Smrg if (*prePNP) 345659607e0Smrg return i; 346659607e0Smrg 347659607e0Smrg ++c; /* make it `End ID' */ 348659607e0Smrg for (;;) { 349659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 350659607e0Smrg break; 351659607e0Smrg 352659607e0Smrg xf86ReadSerial(pInfo->fd, &buf[i], 1); 353659607e0Smrg if (buf[i++] == c) /* End ID */ 354659607e0Smrg break; 355659607e0Smrg if (i >= 256) 356659607e0Smrg break; 357659607e0Smrg } 358659607e0Smrg if (buf[i - 1] != c) 359659607e0Smrg goto connect_idle; 360659607e0Smrg return i; 361659607e0Smrg 362659607e0Smrg /* 363659607e0Smrg * According to PnP spec, we should set DTR = 1 and RTS = 0 while 364659607e0Smrg * in idle state. But, `moused' shall set DTR = RTS = 1 and proceed, 365659607e0Smrg * assuming there is something at the port even if it didn't 366659607e0Smrg * respond to the PnP enumeration procedure. 367659607e0Smrg */ 368659607e0Smrgdisconnect_idle: 369659607e0Smrg i = XF86_M_DTR | XF86_M_RTS; /* DTR = 1, RTS = 1 */ 370659607e0Smrg xf86SerialModemSetBits(pInfo->fd, i); 371659607e0Smrgconnect_idle: 372659607e0Smrg return 0; 373659607e0Smrg} 374659607e0Smrg 375659607e0Smrgstatic int 376659607e0Smrgpnpparse(InputInfoPtr pInfo, pnpid_t *id, char *buf, int len) 377659607e0Smrg{ 378659607e0Smrg char s[3]; 379659607e0Smrg int offset; 380659607e0Smrg int sum = 0; 381659607e0Smrg int i, j; 382659607e0Smrg 383659607e0Smrg id->revision = 0; 384659607e0Smrg id->eisaid = NULL; 385659607e0Smrg id->serial = NULL; 386659607e0Smrg id->class = NULL; 387659607e0Smrg id->compat = NULL; 388659607e0Smrg id->description = NULL; 389659607e0Smrg id->neisaid = 0; 390659607e0Smrg id->nserial = 0; 391659607e0Smrg id->nclass = 0; 392659607e0Smrg id->ncompat = 0; 393659607e0Smrg id->ndescription = 0; 394659607e0Smrg 395659607e0Smrg offset = 0x28 - buf[0]; 396659607e0Smrg 397659607e0Smrg /* calculate checksum */ 398659607e0Smrg for (i = 0; i < len - 3; ++i) { 399659607e0Smrg sum += buf[i]; 400659607e0Smrg buf[i] += offset; 401659607e0Smrg } 402659607e0Smrg sum += buf[len - 1]; 403659607e0Smrg for (; i < len; ++i) 404659607e0Smrg buf[i] += offset; 405659607e0Smrg xf86MsgVerb(X_INFO, 2, "%s: PnP ID string: `%*.*s'\n", pInfo->name, 406659607e0Smrg len, len, buf); 407659607e0Smrg 408659607e0Smrg /* revision */ 409659607e0Smrg buf[1] -= offset; 410659607e0Smrg buf[2] -= offset; 411659607e0Smrg id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f); 412659607e0Smrg xf86MsgVerb(X_INFO, 2, "%s: PnP rev %d.%02d\n", pInfo->name, 413659607e0Smrg id->revision / 100, id->revision % 100); 414659607e0Smrg 415659607e0Smrg /* EISA vender and product ID */ 416659607e0Smrg id->eisaid = &buf[3]; 417659607e0Smrg id->neisaid = 7; 418659607e0Smrg 419659607e0Smrg /* option strings */ 420659607e0Smrg i = 10; 421659607e0Smrg if (buf[i] == '\\') { 422659607e0Smrg /* device serial # */ 423659607e0Smrg for (j = ++i; i < len; ++i) { 424659607e0Smrg if (buf[i] == '\\') 425659607e0Smrg break; 426659607e0Smrg } 427659607e0Smrg if (i >= len) 428659607e0Smrg i -= 3; 429659607e0Smrg if (i - j == 8) { 430659607e0Smrg id->serial = &buf[j]; 431659607e0Smrg id->nserial = 8; 432659607e0Smrg } 433659607e0Smrg } 434659607e0Smrg if (buf[i] == '\\') { 435659607e0Smrg /* PnP class */ 436659607e0Smrg for (j = ++i; i < len; ++i) { 437659607e0Smrg if (buf[i] == '\\') 438659607e0Smrg break; 439659607e0Smrg } 440659607e0Smrg if (i >= len) 441659607e0Smrg i -= 3; 442659607e0Smrg if (i > j + 1) { 443659607e0Smrg id->class = &buf[j]; 444659607e0Smrg id->nclass = i - j; 445659607e0Smrg } 446659607e0Smrg } 447659607e0Smrg if (buf[i] == '\\') { 448659607e0Smrg /* compatible driver */ 449659607e0Smrg for (j = ++i; i < len; ++i) { 450659607e0Smrg if (buf[i] == '\\') 451659607e0Smrg break; 452659607e0Smrg } 453659607e0Smrg /* 454659607e0Smrg * PnP COM spec prior to v0.96 allowed '*' in this field, 455659607e0Smrg * it's not allowed now; just ignore it. 456659607e0Smrg */ 457659607e0Smrg if (buf[j] == '*') 458659607e0Smrg ++j; 459659607e0Smrg if (i >= len) 460659607e0Smrg i -= 3; 461659607e0Smrg if (i > j + 1) { 462659607e0Smrg id->compat = &buf[j]; 463659607e0Smrg id->ncompat = i - j; 464659607e0Smrg } 465659607e0Smrg } 466659607e0Smrg if (buf[i] == '\\') { 467659607e0Smrg /* product description */ 468659607e0Smrg for (j = ++i; i < len; ++i) { 469659607e0Smrg if (buf[i] == ';') 470659607e0Smrg break; 471659607e0Smrg } 472659607e0Smrg if (i >= len) 473659607e0Smrg i -= 3; 474659607e0Smrg if (i > j + 1) { 475659607e0Smrg id->description = &buf[j]; 476659607e0Smrg id->ndescription = i - j; 477659607e0Smrg } 478659607e0Smrg } 479659607e0Smrg 480659607e0Smrg /* checksum exists if there are any optional fields */ 481659607e0Smrg if ((id->nserial > 0) || (id->nclass > 0) 482659607e0Smrg || (id->ncompat > 0) || (id->ndescription > 0)) { 483659607e0Smrg xf86MsgVerb(X_INFO, 4, "%s: PnP checksum: 0x%02X\n", pInfo->name, sum); 484659607e0Smrg sprintf(s, "%02X", sum & 0x0ff); 485659607e0Smrg if (strncmp(s, &buf[len - 3], 2) != 0) { 486659607e0Smrg#if 0 487659607e0Smrg /* 488659607e0Smrg * Checksum error!! 489659607e0Smrg * I found some mice do not comply with the PnP COM device 490659607e0Smrg * spec regarding checksum... XXX 491659607e0Smrg */ 492659607e0Smrg return FALSE; 493659607e0Smrg#endif 494659607e0Smrg } 495659607e0Smrg } 496659607e0Smrg 497659607e0Smrg return TRUE; 498659607e0Smrg} 499659607e0Smrg 500659607e0Smrg/* We can only identify MS at the moment */ 501659607e0Smrgstatic MouseProtocolID 502659607e0Smrgprepnpparse(InputInfoPtr pInfo, char *buf) 503659607e0Smrg{ 504659607e0Smrg if (buf[0] == 'M' && buf[1] == '3') 505659607e0Smrg return PROT_MS; 506659607e0Smrg return PROT_UNKNOWN; 507659607e0Smrg} 508659607e0Smrg 509659607e0Smrg 510659607e0Smrgstatic symtab_t * 511659607e0Smrgpnpproto(pnpid_t *id) 512659607e0Smrg{ 513659607e0Smrg symtab_t *t; 514659607e0Smrg int i, j; 515659607e0Smrg 516659607e0Smrg if (id->nclass > 0) 517659607e0Smrg if (strncmp(id->class, "MOUSE", id->nclass) != 0) 518659607e0Smrg /* this is not a mouse! */ 519659607e0Smrg return NULL; 520659607e0Smrg 521659607e0Smrg if (id->neisaid > 0) { 522659607e0Smrg t = gettoken(pnpprod, id->eisaid, id->neisaid); 523659607e0Smrg if (t->val != -1) 524659607e0Smrg return t; 525659607e0Smrg } 526659607e0Smrg 527659607e0Smrg /* 528659607e0Smrg * The 'Compatible drivers' field may contain more than one 529659607e0Smrg * ID separated by ','. 530659607e0Smrg */ 531659607e0Smrg if (id->ncompat <= 0) 532659607e0Smrg return NULL; 533659607e0Smrg for (i = 0; i < id->ncompat; ++i) { 534659607e0Smrg for (j = i; id->compat[i] != ','; ++i) 535659607e0Smrg if (i >= id->ncompat) 536659607e0Smrg break; 537659607e0Smrg if (i > j) { 538659607e0Smrg t = gettoken(pnpprod, id->compat + j, i - j); 539659607e0Smrg if (t->val != -1) 540659607e0Smrg return t; 541659607e0Smrg } 542659607e0Smrg } 543659607e0Smrg 544659607e0Smrg return NULL; 545659607e0Smrg} 546659607e0Smrg 547659607e0Smrg/* name/val mapping */ 548659607e0Smrg 549659607e0Smrgstatic symtab_t * 5506aab59a7Smrggettoken(symtab_t *tab, char *s, int len) 551659607e0Smrg{ 552659607e0Smrg int i; 553659607e0Smrg 554659607e0Smrg for (i = 0; tab[i].name != NULL; ++i) { 555659607e0Smrg if (strncmp(tab[i].name, s, len) == 0) 556659607e0Smrg break; 557659607e0Smrg } 558659607e0Smrg return &tab[i]; 559659607e0Smrg} 560659607e0Smrg 561659607e0Smrg/******************* PS/2 PnP probing ****************/ 562659607e0Smrg 563659607e0Smrgstatic int 564659607e0SmrgreadMouse(InputInfoPtr pInfo, unsigned char *u) 565659607e0Smrg{ 566659607e0Smrg 567659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 568659607e0Smrg return FALSE; 569659607e0Smrg 570659607e0Smrg xf86ReadSerial(pInfo->fd, u, 1); 571659607e0Smrg return TRUE; 572659607e0Smrg} 573659607e0Smrg 574659607e0Smrgstatic void 575659607e0Smrgps2DisableWrapMode(InputInfoPtr pInfo) 576659607e0Smrg{ 577659607e0Smrg unsigned char reset_wrap_mode[] = { 0xEC }; 578659607e0Smrg ps2SendPacket(pInfo, reset_wrap_mode, sizeof(reset_wrap_mode)); 579659607e0Smrg} 580659607e0Smrg 581659607e0SmrgBool 582659607e0Smrgps2SendPacket(InputInfoPtr pInfo, unsigned char *bytes, int len) 583659607e0Smrg{ 584659607e0Smrg unsigned char c; 585659607e0Smrg int i,j; 586659607e0Smrg 587659607e0Smrg#ifdef DEBUG 588659607e0Smrg xf86ErrorF("Ps/2 data package:"); 589659607e0Smrg for (i = 0; i < len; i++) 590659607e0Smrg xf86ErrorF(" %x", *(bytes + i)); 591659607e0Smrg xf86ErrorF("\n"); 592659607e0Smrg#endif 593659607e0Smrg 594659607e0Smrg for (i = 0; i < len; i++) { 595659607e0Smrg for (j = 0; j < 10; j++) { 596659607e0Smrg xf86WriteSerial(pInfo->fd, bytes + i, 1); 597659607e0Smrg usleep(10000); 598659607e0Smrg if (!readMouse(pInfo,&c)) { 599659607e0Smrg#ifdef DEBUG 600659607e0Smrg xf86ErrorF("sending 0x%x to PS/2 unsuccessful\n",*(bytes + i)); 601659607e0Smrg#endif 602659607e0Smrg return FALSE; 603659607e0Smrg } 604659607e0Smrg#ifdef DEBUG 605659607e0Smrg xf86ErrorF("Recieved: 0x%x\n",c); 606659607e0Smrg#endif 607659607e0Smrg if (c == 0xFA) /* ACK */ 608659607e0Smrg break; 609659607e0Smrg 610659607e0Smrg if (c == 0xFE) /* resend */ 611659607e0Smrg continue; 612659607e0Smrg 613659607e0Smrg 614659607e0Smrg if (c == 0xFC) /* error */ 615659607e0Smrg return FALSE; 616659607e0Smrg 617659607e0Smrg /* Some mice accidently enter wrap mode during init */ 618659607e0Smrg if (c == *(bytes + i) /* wrap mode */ 619659607e0Smrg && (*(bytes + i) != 0xEC)) /* avoid recursion */ 620659607e0Smrg ps2DisableWrapMode(pInfo); 621659607e0Smrg 622659607e0Smrg return FALSE; 623659607e0Smrg } 624659607e0Smrg if (j == 10) 625659607e0Smrg return FALSE; 626659607e0Smrg } 627659607e0Smrg 628659607e0Smrg return TRUE; 629659607e0Smrg} 630659607e0Smrg 631659607e0Smrgstatic Bool 632659607e0Smrgps2DisableDataReporting(InputInfoPtr pInfo) 633659607e0Smrg{ 634659607e0Smrg unsigned char packet[] = { 0xF5 }; 635659607e0Smrg return ps2SendPacket(pInfo, packet, sizeof(packet)); 636659607e0Smrg} 637659607e0Smrg 638659607e0SmrgBool 639659607e0Smrgps2EnableDataReporting(InputInfoPtr pInfo) 640659607e0Smrg{ 641659607e0Smrg unsigned char packet[] = { 0xF4 }; 642659607e0Smrg return ps2SendPacket(pInfo, packet, sizeof(packet)); 643659607e0Smrg} 644659607e0Smrg 645659607e0Smrgint 646659607e0Smrgps2GetDeviceID(InputInfoPtr pInfo) 647659607e0Smrg{ 648659607e0Smrg unsigned char u; 649659607e0Smrg unsigned char packet[] = { 0xf2 }; 650659607e0Smrg 651659607e0Smrg usleep(30000); 652659607e0Smrg xf86FlushInput(pInfo->fd); 653659607e0Smrg if (!ps2SendPacket(pInfo, packet, sizeof(packet))) 654659607e0Smrg return -1; 655659607e0Smrg while (1) { 656659607e0Smrg if (!readMouse(pInfo,&u)) 657659607e0Smrg return -1; 658659607e0Smrg if (u != 0xFA) 659659607e0Smrg break; 660659607e0Smrg } 661659607e0Smrg#ifdef DEBUG 662659607e0Smrg xf86ErrorF("Obtained Mouse Type: %x\n",u); 663659607e0Smrg#endif 664659607e0Smrg return (int) u; 665659607e0Smrg} 666659607e0Smrg 667659607e0SmrgBool 668659607e0Smrgps2Reset(InputInfoPtr pInfo) 669659607e0Smrg{ 670659607e0Smrg unsigned char u; 671659607e0Smrg unsigned char packet[] = { 0xff }; 672659607e0Smrg unsigned char reply[] = { 0xaa, 0x00 }; 673659607e0Smrg unsigned int i; 674659607e0Smrg#ifdef DEBUG 675659607e0Smrg xf86ErrorF("PS/2 Mouse reset\n"); 676659607e0Smrg#endif 677659607e0Smrg if (!ps2SendPacket(pInfo, packet, sizeof(packet))) 678659607e0Smrg return FALSE; 679659607e0Smrg /* we need a little delay here */ 680659607e0Smrg xf86WaitForInput(pInfo->fd, 500000); 681659607e0Smrg for (i = 0; i < sizeof(reply) ; i++) { 682659607e0Smrg if (!readMouse(pInfo,&u)) { 683659607e0Smrg goto EXIT; 684659607e0Smrg } 685659607e0Smrg if (u != reply[i]) 686659607e0Smrg goto EXIT; 687659607e0Smrg } 688659607e0Smrg return TRUE; 689659607e0Smrg 690659607e0Smrg EXIT: 691659607e0Smrg xf86FlushInput(pInfo->fd); 692659607e0Smrg return FALSE; 693659607e0Smrg} 694659607e0Smrg 695659607e0Smrgstatic MouseProtocolID 696659607e0SmrgprobePs2ProtocolPnP(InputInfoPtr pInfo) 697659607e0Smrg{ 698659607e0Smrg unsigned char u; 699659607e0Smrg MouseProtocolID ret = PROT_UNKNOWN; 700659607e0Smrg 701659607e0Smrg xf86FlushInput(pInfo->fd); 702659607e0Smrg 703659607e0Smrg ps2DisableDataReporting(pInfo); 704659607e0Smrg 705659607e0Smrg if (ps2Reset(pInfo)) { /* Reset PS2 device */ 706659607e0Smrg unsigned char seq[] = { 243, 200, 243, 100, 243, 80 }; 707659607e0Smrg /* Try to identify Intelli Mouse */ 708659607e0Smrg if (ps2SendPacket(pInfo, seq, sizeof(seq))) { 709659607e0Smrg u = ps2GetDeviceID(pInfo); 710659607e0Smrg if (u == 0x03) { 711659607e0Smrg /* found IntelliMouse now try IntelliExplorer */ 712659607e0Smrg unsigned char seq[] = { 243, 200, 243, 200, 243, 80 }; 713659607e0Smrg if (ps2SendPacket(pInfo,seq,sizeof(seq))) { 714659607e0Smrg u = ps2GetDeviceID(pInfo); 715659607e0Smrg if (u == 0x04) 716659607e0Smrg ret = PROT_EXPPS2; 717659607e0Smrg else 718659607e0Smrg ret = PROT_IMPS2; 719659607e0Smrg } 720659607e0Smrg } else if (ps2Reset(pInfo)) /* reset again to find sane state */ 721659607e0Smrg ret = PROT_PS2; 722659607e0Smrg } 723659607e0Smrg 724659607e0Smrg if (ret != PROT_UNKNOWN) 725659607e0Smrg ps2EnableDataReporting(pInfo); 726659607e0Smrg } 727659607e0Smrg return ret; 728659607e0Smrg} 729659607e0Smrg 730659607e0Smrgstatic struct ps2protos { 731659607e0Smrg int Id; 732659607e0Smrg MouseProtocolID protoID; 733659607e0Smrg} ps2 [] = { 734659607e0Smrg { 0x0, PROT_PS2 }, 735659607e0Smrg { 0x3, PROT_IMPS2 }, 736659607e0Smrg { 0x4, PROT_EXPPS2 }, 737659607e0Smrg { -1 , PROT_UNKNOWN } 738659607e0Smrg}; 739659607e0Smrg 740659607e0Smrg 741659607e0Smrgstatic MouseProtocolID 742659607e0SmrggetPs2ProtocolPnP(InputInfoPtr pInfo) 743659607e0Smrg{ 744659607e0Smrg int Id; 745659607e0Smrg int i; 746659607e0Smrg MouseProtocolID proto; 747659607e0Smrg int count = 4; 748659607e0Smrg 749659607e0Smrg xf86FlushInput(pInfo->fd); 750659607e0Smrg 751659607e0Smrg while (--count) 752659607e0Smrg if (ps2DisableDataReporting(pInfo)) 753659607e0Smrg break; 754659607e0Smrg 755659607e0Smrg if (!count) { 756659607e0Smrg proto = PROT_UNKNOWN; 757659607e0Smrg goto EXIT; 758659607e0Smrg } 759659607e0Smrg 760659607e0Smrg if ((Id = ps2GetDeviceID(pInfo)) == -1) { 761659607e0Smrg proto = PROT_UNKNOWN; 762659607e0Smrg goto EXIT; 763659607e0Smrg } 764659607e0Smrg 765659607e0Smrg if (-1 == ps2EnableDataReporting(pInfo)) { 766659607e0Smrg proto = PROT_UNKNOWN; 767659607e0Smrg goto EXIT; 768659607e0Smrg } 769659607e0Smrg 770659607e0Smrg for (i = 0; ps2[i].protoID != PROT_UNKNOWN; i++) { 771659607e0Smrg if (ps2[i].Id == Id) { 772659607e0Smrg xf86MsgVerb(X_PROBED,2,"Found PS/2 proto ID %x\n",Id); 773659607e0Smrg proto = ps2[i].protoID; 774659607e0Smrg goto EXIT; 775659607e0Smrg } 776659607e0Smrg } 777659607e0Smrg 778659607e0Smrg proto = PROT_UNKNOWN; 779659607e0Smrg xf86Msg(X_ERROR,"Found unknown PS/2 proto ID %x\n",Id); 780659607e0Smrg 781659607e0Smrg EXIT: 782659607e0Smrg xf86FlushInput(pInfo->fd); 783659607e0Smrg return proto; 784659607e0Smrg} 785659607e0Smrg 786