pnp.c revision 659607e0
1659607e0Smrg/* $XFree86: xc/programs/Xserver/hw/xfree86/input/mouse/pnp.c,v 1.20tsi Exp $ */ 2659607e0Smrg/* 3659607e0Smrg * Copyright 1998 by Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp> 4659607e0Smrg * 5659607e0Smrg * Permission to use, copy, modify, distribute, and sell this software and its 6659607e0Smrg * documentation for any purpose is hereby granted without fee, provided that 7659607e0Smrg * the above copyright notice appear in all copies and that both that 8659607e0Smrg * copyright notice and this permission notice appear in supporting 9659607e0Smrg * documentation, and that the name of Kazutaka YOKOTA not be used in 10659607e0Smrg * advertising or publicity pertaining to distribution of the software without 11659607e0Smrg * specific, written prior permission. Kazutaka YOKOTA makes no representations 12659607e0Smrg * about the suitability of this software for any purpose. It is provided 13659607e0Smrg * "as is" without express or implied warranty. 14659607e0Smrg * 15659607e0Smrg * KAZUTAKA YOKOTA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 16659607e0Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 17659607e0Smrg * EVENT SHALL KAZUTAKA YOKOTA BE LIABLE FOR ANY SPECIAL, INDIRECT OR 18659607e0Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 19659607e0Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 20659607e0Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 21659607e0Smrg * PERFORMANCE OF THIS SOFTWARE. 22659607e0Smrg */ 23659607e0Smrg 24659607e0Smrg#ifdef HAVE_CONFIG_H 25659607e0Smrg#include "config.h" 26659607e0Smrg#endif 27659607e0Smrg 28659607e0Smrg#include <stdio.h> 29659607e0Smrg#include <string.h> 30659607e0Smrg#include <unistd.h> 31659607e0Smrg#define NEED_EVENTS 32659607e0Smrg#include <X11/X.h> 33659607e0Smrg#include <X11/Xproto.h> 34659607e0Smrg#include "inputstr.h" 35659607e0Smrg#include "scrnintstr.h" 36659607e0Smrg#include "xf86.h" 37659607e0Smrg#include "xf86Priv.h" 38659607e0Smrg#include "xf86Xinput.h" 39659607e0Smrg#include "xf86_OSproc.h" 40659607e0Smrg#include "xf86OSmouse.h" 41659607e0Smrg#include "mouse.h" 42659607e0Smrg#include "mousePriv.h" 43659607e0Smrg 44659607e0Smrg#ifdef MOUSEINITDEBUG 45659607e0Smrg# define DEBUG 46659607e0Smrg# define EXTMOUSEDEBUG 47659607e0Smrg#endif 48659607e0Smrg 49659607e0Smrg/* serial PnP ID string */ 50659607e0Smrgtypedef struct { 51659607e0Smrg int revision; /* PnP revision, 100 for 1.00 */ 52659607e0Smrg char *eisaid; /* EISA ID including mfr ID and product ID */ 53659607e0Smrg char *serial; /* serial No, optional */ 54659607e0Smrg char *class; /* device class, optional */ 55659607e0Smrg char *compat; /* list of compatible drivers, optional */ 56659607e0Smrg char *description; /* product description, optional */ 57659607e0Smrg int neisaid; /* length of the above fields... */ 58659607e0Smrg int nserial; 59659607e0Smrg int nclass; 60659607e0Smrg int ncompat; 61659607e0Smrg int ndescription; 62659607e0Smrg} pnpid_t; 63659607e0Smrg 64659607e0Smrg/* symbol table entry */ 65659607e0Smrgtypedef struct { 66659607e0Smrg char *name; 67659607e0Smrg MouseProtocolID val; 68659607e0Smrg} symtab_t; 69659607e0Smrg 70659607e0Smrg/* PnP EISA/product IDs */ 71659607e0Smrgstatic symtab_t pnpprod[] = { 72659607e0Smrg { "KML0001", PROT_THINKING }, /* Kensignton ThinkingMouse */ 73659607e0Smrg { "MSH0001", PROT_IMSERIAL }, /* MS IntelliMouse */ 74659607e0Smrg { "MSH0004", PROT_IMSERIAL }, /* MS IntelliMouse TrackBall */ 75659607e0Smrg { "KYEEZ00", PROT_MS }, /* Genius EZScroll */ 76659607e0Smrg { "KYE0001", PROT_MS }, /* Genius PnP Mouse */ 77659607e0Smrg { "KYE0002", PROT_MS }, /* MouseSystem (Genius?) SmartScroll */ 78659607e0Smrg { "KYE0003", PROT_IMSERIAL }, /* Genius NetMouse */ 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 */ 105659607e0Smrg { "PNP0F0C", PROT_MS }, /* MS serial comatible */ 106659607e0Smrg { "PNP0F0D", PROT_BM }, /* MS InPort comatible */ 107659607e0Smrg { "PNP0F0E", PROT_PS2 }, /* MS PS/2 comatible */ 108659607e0Smrg { "PNP0F0F", PROT_MS }, /* MS BallPoint comatible */ 109659607e0Smrg#ifdef notyet 110659607e0Smrg { "PNP0F10", PROT_??? }, /* TI QuickPort */ 111659607e0Smrg#endif 112659607e0Smrg { "PNP0F11", PROT_BM }, /* MS bus comatible */ 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#if 1 192659607e0Smrg last = mPriv->pnpLast; 193659607e0Smrg mPriv->pnpLast = currentTime.milliseconds; 194659607e0Smrg 195659607e0Smrg if (last) { 196659607e0Smrg if (last - currentTime.milliseconds < 100 197659607e0Smrg || (mPriv->disablePnPauto 198659607e0Smrg && (last - currentTime.milliseconds < 10000))) { 199659607e0Smrg#ifdef EXTMOUSEDEBUG 200659607e0Smrg xf86ErrorF("Mouse: Disabling PnP\n"); 201659607e0Smrg#endif 202659607e0Smrg mPriv->disablePnPauto = TRUE; 203659607e0Smrg return PROT_UNKNOWN; 204659607e0Smrg } 205659607e0Smrg } 206659607e0Smrg 207659607e0Smrg#ifdef EXTMOUSEDEBUG 208659607e0Smrg if (mPriv->disablePnPauto) 209659607e0Smrg xf86ErrorF("Mouse: Enabling PnP\n"); 210659607e0Smrg#endif 211659607e0Smrg mPriv->disablePnPauto = FALSE; 212659607e0Smrg 213659607e0Smrg if (mPriv->soft) 214659607e0Smrg return getPs2ProtocolPnP(pInfo); 215659607e0Smrg else 216659607e0Smrg return probePs2ProtocolPnP(pInfo); 217659607e0Smrg#else 218659607e0Smrg return PROT_UNKNOWN; 219659607e0Smrg#endif 220659607e0Smrg} 221659607e0Smrg 222659607e0Smrg/* 223659607e0Smrg * Try to elicit a PnP ID as described in 224659607e0Smrg * Microsoft, Hayes: "Plug and Play External COM Device Specification, 225659607e0Smrg * rev 1.00", 1995. 226659607e0Smrg * 227659607e0Smrg * The routine does not fully implement the COM Enumerator as per Section 228659607e0Smrg * 2.1 of the document. In particular, we don't have idle state in which 229659607e0Smrg * the driver software monitors the com port for dynamic connection or 230659607e0Smrg * removal of a device at the port, because `moused' simply quits if no 231659607e0Smrg * device is found. 232659607e0Smrg * 233659607e0Smrg * In addition, as PnP COM device enumeration procedure slightly has 234659607e0Smrg * changed since its first publication, devices which follow earlier 235659607e0Smrg * revisions of the above spec. may fail to respond if the rev 1.0 236659607e0Smrg * procedure is used. XXX 237659607e0Smrg */ 238659607e0Smrgstatic int 239659607e0Smrgpnpgets(InputInfoPtr pInfo, char *buf, Bool *prePNP) 240659607e0Smrg{ 241659607e0Smrg int i; 242659607e0Smrg char c; 243659607e0Smrg pointer pnpOpts; 244659607e0Smrg 245659607e0Smrg#if 0 246659607e0Smrg /* 247659607e0Smrg * This is the procedure described in rev 1.0 of PnP COM device spec. 248659607e0Smrg * Unfortunately, some devices which comform to earlier revisions of 249659607e0Smrg * the spec gets confused and do not return the ID string... 250659607e0Smrg */ 251659607e0Smrg 252659607e0Smrg /* port initialization (2.1.2) */ 253659607e0Smrg if ((i = xf86GetSerialModemState(pInfo->fd)) == -1) 254659607e0Smrg return 0; 255659607e0Smrg i |= XF86_M_DTR; /* DTR = 1 */ 256659607e0Smrg i &= ~XF86_M_RTS; /* RTS = 0 */ 257659607e0Smrg if (xf86SetSerialModemState(pInfo->fd, i) == -1) 258659607e0Smrg goto disconnect_idle; 259659607e0Smrg usleep(200000); 260659607e0Smrg if ((i = xf86GetSerialModemState(pInfo->fd)) == -1 || 261659607e0Smrg (i & XF86_M_DSR) == 0) 262659607e0Smrg goto disconnect_idle; 263659607e0Smrg 264659607e0Smrg /* port setup, 1st phase (2.1.3) */ 265659607e0Smrg pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1); 266659607e0Smrg xf86SetSerial(pInfo->fd, pnpOpts); 267659607e0Smrg i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */ 268659607e0Smrg xf86SerialModemClearBits(pInfo->fd, i); 269659607e0Smrg usleep(200000); 270659607e0Smrg i = TIOCM_DTR; /* DTR = 1, RTS = 0 */ 271659607e0Smrg xf86SerialModemSetBits(pInfo->fd, i); 272659607e0Smrg usleep(200000); 273659607e0Smrg 274659607e0Smrg /* wait for response, 1st phase (2.1.4) */ 275659607e0Smrg xf86FlushInput(pInfo->fd); 276659607e0Smrg i = TIOCM_RTS; /* DTR = 1, RTS = 1 */ 277659607e0Smrg xf86SerialModemSetBits(pInfo->fd, i); 278659607e0Smrg 279659607e0Smrg /* try to read something */ 280659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) { 281659607e0Smrg 282659607e0Smrg /* port setup, 2nd phase (2.1.5) */ 283659607e0Smrg i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */ 284659607e0Smrg xf86SerialModemClearBits(pInfo->fd, i); 285659607e0Smrg usleep(200000); 286659607e0Smrg 287659607e0Smrg /* wait for respose, 2nd phase (2.1.6) */ 288659607e0Smrg xf86FlushInput(pInfo->fd); 289659607e0Smrg i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */ 290659607e0Smrg xf86SerialModemSetBits(pInfo->fd, i); 291659607e0Smrg 292659607e0Smrg /* try to read something */ 293659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 294659607e0Smrg goto connect_idle; 295659607e0Smrg } 296659607e0Smrg#else 297659607e0Smrg /* 298659607e0Smrg * This is a simplified procedure; it simply toggles RTS. 299659607e0Smrg */ 300659607e0Smrg 301659607e0Smrg if ((i = xf86GetSerialModemState(pInfo->fd)) == -1) 302659607e0Smrg return 0; 303659607e0Smrg i |= XF86_M_DTR; /* DTR = 1 */ 304659607e0Smrg i &= ~XF86_M_RTS; /* RTS = 0 */ 305659607e0Smrg if (xf86SetSerialModemState(pInfo->fd, i) == -1) 306659607e0Smrg goto disconnect_idle; 307659607e0Smrg usleep(200000); 308659607e0Smrg 309659607e0Smrg pnpOpts = xf86OptionListCreate(pnpSerial, -1, 1); 310659607e0Smrg xf86SetSerial(pInfo->fd, pnpOpts); 311659607e0Smrg 312659607e0Smrg /* wait for respose */ 313659607e0Smrg xf86FlushInput(pInfo->fd); 314659607e0Smrg i = XF86_M_DTR | XF86_M_RTS; /* DTR = 1, RTS = 1 */ 315659607e0Smrg xf86SerialModemSetBits(pInfo->fd, i); 316659607e0Smrg 317659607e0Smrg /* try to read something */ 318659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 319659607e0Smrg goto connect_idle; 320659607e0Smrg#endif 321659607e0Smrg 322659607e0Smrg /* collect PnP COM device ID (2.1.7) */ 323659607e0Smrg i = 0; 324659607e0Smrg *prePNP = FALSE; 325659607e0Smrg 326659607e0Smrg usleep(200000); /* the mouse must send `Begin ID' within 200msec */ 327659607e0Smrg while (xf86ReadSerial(pInfo->fd, &c, 1) == 1) { 328659607e0Smrg /* we may see "M", or "M3..." before `Begin ID' */ 329659607e0Smrg if (c == 'M') 330659607e0Smrg *prePNP = TRUE; 331659607e0Smrg 332659607e0Smrg if ((c == 0x08) || (c == 0x28)) { /* Begin ID */ 333659607e0Smrg *prePNP = FALSE; 334659607e0Smrg buf[0] = c; 335659607e0Smrg i = 1; 336659607e0Smrg break; 337659607e0Smrg } 338659607e0Smrg if (*prePNP) 339659607e0Smrg buf[i++] = c; 340659607e0Smrg 341659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 342659607e0Smrg break; 343659607e0Smrg } 344659607e0Smrg if (i <= 0) { 345659607e0Smrg /* we haven't seen `Begin ID' in time... */ 346659607e0Smrg goto connect_idle; 347659607e0Smrg } 348659607e0Smrg if (*prePNP) 349659607e0Smrg return i; 350659607e0Smrg 351659607e0Smrg ++c; /* make it `End ID' */ 352659607e0Smrg for (;;) { 353659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 354659607e0Smrg break; 355659607e0Smrg 356659607e0Smrg xf86ReadSerial(pInfo->fd, &buf[i], 1); 357659607e0Smrg if (buf[i++] == c) /* End ID */ 358659607e0Smrg break; 359659607e0Smrg if (i >= 256) 360659607e0Smrg break; 361659607e0Smrg } 362659607e0Smrg if (buf[i - 1] != c) 363659607e0Smrg goto connect_idle; 364659607e0Smrg return i; 365659607e0Smrg 366659607e0Smrg /* 367659607e0Smrg * According to PnP spec, we should set DTR = 1 and RTS = 0 while 368659607e0Smrg * in idle state. But, `moused' shall set DTR = RTS = 1 and proceed, 369659607e0Smrg * assuming there is something at the port even if it didn't 370659607e0Smrg * respond to the PnP enumeration procedure. 371659607e0Smrg */ 372659607e0Smrgdisconnect_idle: 373659607e0Smrg i = XF86_M_DTR | XF86_M_RTS; /* DTR = 1, RTS = 1 */ 374659607e0Smrg xf86SerialModemSetBits(pInfo->fd, i); 375659607e0Smrgconnect_idle: 376659607e0Smrg return 0; 377659607e0Smrg} 378659607e0Smrg 379659607e0Smrgstatic int 380659607e0Smrgpnpparse(InputInfoPtr pInfo, pnpid_t *id, char *buf, int len) 381659607e0Smrg{ 382659607e0Smrg char s[3]; 383659607e0Smrg int offset; 384659607e0Smrg int sum = 0; 385659607e0Smrg int i, j; 386659607e0Smrg 387659607e0Smrg id->revision = 0; 388659607e0Smrg id->eisaid = NULL; 389659607e0Smrg id->serial = NULL; 390659607e0Smrg id->class = NULL; 391659607e0Smrg id->compat = NULL; 392659607e0Smrg id->description = NULL; 393659607e0Smrg id->neisaid = 0; 394659607e0Smrg id->nserial = 0; 395659607e0Smrg id->nclass = 0; 396659607e0Smrg id->ncompat = 0; 397659607e0Smrg id->ndescription = 0; 398659607e0Smrg 399659607e0Smrg offset = 0x28 - buf[0]; 400659607e0Smrg 401659607e0Smrg /* calculate checksum */ 402659607e0Smrg for (i = 0; i < len - 3; ++i) { 403659607e0Smrg sum += buf[i]; 404659607e0Smrg buf[i] += offset; 405659607e0Smrg } 406659607e0Smrg sum += buf[len - 1]; 407659607e0Smrg for (; i < len; ++i) 408659607e0Smrg buf[i] += offset; 409659607e0Smrg xf86MsgVerb(X_INFO, 2, "%s: PnP ID string: `%*.*s'\n", pInfo->name, 410659607e0Smrg len, len, buf); 411659607e0Smrg 412659607e0Smrg /* revision */ 413659607e0Smrg buf[1] -= offset; 414659607e0Smrg buf[2] -= offset; 415659607e0Smrg id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f); 416659607e0Smrg xf86MsgVerb(X_INFO, 2, "%s: PnP rev %d.%02d\n", pInfo->name, 417659607e0Smrg id->revision / 100, id->revision % 100); 418659607e0Smrg 419659607e0Smrg /* EISA vender and product ID */ 420659607e0Smrg id->eisaid = &buf[3]; 421659607e0Smrg id->neisaid = 7; 422659607e0Smrg 423659607e0Smrg /* option strings */ 424659607e0Smrg i = 10; 425659607e0Smrg if (buf[i] == '\\') { 426659607e0Smrg /* device serial # */ 427659607e0Smrg for (j = ++i; i < len; ++i) { 428659607e0Smrg if (buf[i] == '\\') 429659607e0Smrg break; 430659607e0Smrg } 431659607e0Smrg if (i >= len) 432659607e0Smrg i -= 3; 433659607e0Smrg if (i - j == 8) { 434659607e0Smrg id->serial = &buf[j]; 435659607e0Smrg id->nserial = 8; 436659607e0Smrg } 437659607e0Smrg } 438659607e0Smrg if (buf[i] == '\\') { 439659607e0Smrg /* PnP class */ 440659607e0Smrg for (j = ++i; i < len; ++i) { 441659607e0Smrg if (buf[i] == '\\') 442659607e0Smrg break; 443659607e0Smrg } 444659607e0Smrg if (i >= len) 445659607e0Smrg i -= 3; 446659607e0Smrg if (i > j + 1) { 447659607e0Smrg id->class = &buf[j]; 448659607e0Smrg id->nclass = i - j; 449659607e0Smrg } 450659607e0Smrg } 451659607e0Smrg if (buf[i] == '\\') { 452659607e0Smrg /* compatible driver */ 453659607e0Smrg for (j = ++i; i < len; ++i) { 454659607e0Smrg if (buf[i] == '\\') 455659607e0Smrg break; 456659607e0Smrg } 457659607e0Smrg /* 458659607e0Smrg * PnP COM spec prior to v0.96 allowed '*' in this field, 459659607e0Smrg * it's not allowed now; just ignore it. 460659607e0Smrg */ 461659607e0Smrg if (buf[j] == '*') 462659607e0Smrg ++j; 463659607e0Smrg if (i >= len) 464659607e0Smrg i -= 3; 465659607e0Smrg if (i > j + 1) { 466659607e0Smrg id->compat = &buf[j]; 467659607e0Smrg id->ncompat = i - j; 468659607e0Smrg } 469659607e0Smrg } 470659607e0Smrg if (buf[i] == '\\') { 471659607e0Smrg /* product description */ 472659607e0Smrg for (j = ++i; i < len; ++i) { 473659607e0Smrg if (buf[i] == ';') 474659607e0Smrg break; 475659607e0Smrg } 476659607e0Smrg if (i >= len) 477659607e0Smrg i -= 3; 478659607e0Smrg if (i > j + 1) { 479659607e0Smrg id->description = &buf[j]; 480659607e0Smrg id->ndescription = i - j; 481659607e0Smrg } 482659607e0Smrg } 483659607e0Smrg 484659607e0Smrg /* checksum exists if there are any optional fields */ 485659607e0Smrg if ((id->nserial > 0) || (id->nclass > 0) 486659607e0Smrg || (id->ncompat > 0) || (id->ndescription > 0)) { 487659607e0Smrg xf86MsgVerb(X_INFO, 4, "%s: PnP checksum: 0x%02X\n", pInfo->name, sum); 488659607e0Smrg sprintf(s, "%02X", sum & 0x0ff); 489659607e0Smrg if (strncmp(s, &buf[len - 3], 2) != 0) { 490659607e0Smrg#if 0 491659607e0Smrg /* 492659607e0Smrg * Checksum error!! 493659607e0Smrg * I found some mice do not comply with the PnP COM device 494659607e0Smrg * spec regarding checksum... XXX 495659607e0Smrg */ 496659607e0Smrg return FALSE; 497659607e0Smrg#endif 498659607e0Smrg } 499659607e0Smrg } 500659607e0Smrg 501659607e0Smrg return TRUE; 502659607e0Smrg} 503659607e0Smrg 504659607e0Smrg/* We can only identify MS at the moment */ 505659607e0Smrgstatic MouseProtocolID 506659607e0Smrgprepnpparse(InputInfoPtr pInfo, char *buf) 507659607e0Smrg{ 508659607e0Smrg if (buf[0] == 'M' && buf[1] == '3') 509659607e0Smrg return PROT_MS; 510659607e0Smrg return PROT_UNKNOWN; 511659607e0Smrg} 512659607e0Smrg 513659607e0Smrg 514659607e0Smrgstatic symtab_t * 515659607e0Smrgpnpproto(pnpid_t *id) 516659607e0Smrg{ 517659607e0Smrg symtab_t *t; 518659607e0Smrg int i, j; 519659607e0Smrg 520659607e0Smrg if (id->nclass > 0) 521659607e0Smrg if (strncmp(id->class, "MOUSE", id->nclass) != 0) 522659607e0Smrg /* this is not a mouse! */ 523659607e0Smrg return NULL; 524659607e0Smrg 525659607e0Smrg if (id->neisaid > 0) { 526659607e0Smrg t = gettoken(pnpprod, id->eisaid, id->neisaid); 527659607e0Smrg if (t->val != -1) 528659607e0Smrg return t; 529659607e0Smrg } 530659607e0Smrg 531659607e0Smrg /* 532659607e0Smrg * The 'Compatible drivers' field may contain more than one 533659607e0Smrg * ID separated by ','. 534659607e0Smrg */ 535659607e0Smrg if (id->ncompat <= 0) 536659607e0Smrg return NULL; 537659607e0Smrg for (i = 0; i < id->ncompat; ++i) { 538659607e0Smrg for (j = i; id->compat[i] != ','; ++i) 539659607e0Smrg if (i >= id->ncompat) 540659607e0Smrg break; 541659607e0Smrg if (i > j) { 542659607e0Smrg t = gettoken(pnpprod, id->compat + j, i - j); 543659607e0Smrg if (t->val != -1) 544659607e0Smrg return t; 545659607e0Smrg } 546659607e0Smrg } 547659607e0Smrg 548659607e0Smrg return NULL; 549659607e0Smrg} 550659607e0Smrg 551659607e0Smrg/* name/val mapping */ 552659607e0Smrg 553659607e0Smrgstatic symtab_t * 554659607e0Smrggettoken(tab, s, len) 555659607e0Smrgsymtab_t *tab; 556659607e0Smrgchar *s; 557659607e0Smrgint len; 558659607e0Smrg{ 559659607e0Smrg int i; 560659607e0Smrg 561659607e0Smrg for (i = 0; tab[i].name != NULL; ++i) { 562659607e0Smrg if (strncmp(tab[i].name, s, len) == 0) 563659607e0Smrg break; 564659607e0Smrg } 565659607e0Smrg return &tab[i]; 566659607e0Smrg} 567659607e0Smrg 568659607e0Smrg/******************* PS/2 PnP probing ****************/ 569659607e0Smrg 570659607e0Smrgstatic int 571659607e0SmrgreadMouse(InputInfoPtr pInfo, unsigned char *u) 572659607e0Smrg{ 573659607e0Smrg 574659607e0Smrg if (xf86WaitForInput(pInfo->fd, 200000) <= 0) 575659607e0Smrg return FALSE; 576659607e0Smrg 577659607e0Smrg xf86ReadSerial(pInfo->fd, u, 1); 578659607e0Smrg return TRUE; 579659607e0Smrg} 580659607e0Smrg 581659607e0Smrgstatic void 582659607e0Smrgps2DisableWrapMode(InputInfoPtr pInfo) 583659607e0Smrg{ 584659607e0Smrg unsigned char reset_wrap_mode[] = { 0xEC }; 585659607e0Smrg ps2SendPacket(pInfo, reset_wrap_mode, sizeof(reset_wrap_mode)); 586659607e0Smrg} 587659607e0Smrg 588659607e0SmrgBool 589659607e0Smrgps2SendPacket(InputInfoPtr pInfo, unsigned char *bytes, int len) 590659607e0Smrg{ 591659607e0Smrg unsigned char c; 592659607e0Smrg int i,j; 593659607e0Smrg 594659607e0Smrg#ifdef DEBUG 595659607e0Smrg xf86ErrorF("Ps/2 data package:"); 596659607e0Smrg for (i = 0; i < len; i++) 597659607e0Smrg xf86ErrorF(" %x", *(bytes + i)); 598659607e0Smrg xf86ErrorF("\n"); 599659607e0Smrg#endif 600659607e0Smrg 601659607e0Smrg for (i = 0; i < len; i++) { 602659607e0Smrg for (j = 0; j < 10; j++) { 603659607e0Smrg xf86WriteSerial(pInfo->fd, bytes + i, 1); 604659607e0Smrg usleep(10000); 605659607e0Smrg if (!readMouse(pInfo,&c)) { 606659607e0Smrg#ifdef DEBUG 607659607e0Smrg xf86ErrorF("sending 0x%x to PS/2 unsuccessful\n",*(bytes + i)); 608659607e0Smrg#endif 609659607e0Smrg return FALSE; 610659607e0Smrg } 611659607e0Smrg#ifdef DEBUG 612659607e0Smrg xf86ErrorF("Recieved: 0x%x\n",c); 613659607e0Smrg#endif 614659607e0Smrg if (c == 0xFA) /* ACK */ 615659607e0Smrg break; 616659607e0Smrg 617659607e0Smrg if (c == 0xFE) /* resend */ 618659607e0Smrg continue; 619659607e0Smrg 620659607e0Smrg 621659607e0Smrg if (c == 0xFC) /* error */ 622659607e0Smrg return FALSE; 623659607e0Smrg 624659607e0Smrg /* Some mice accidently enter wrap mode during init */ 625659607e0Smrg if (c == *(bytes + i) /* wrap mode */ 626659607e0Smrg && (*(bytes + i) != 0xEC)) /* avoid recursion */ 627659607e0Smrg ps2DisableWrapMode(pInfo); 628659607e0Smrg 629659607e0Smrg return FALSE; 630659607e0Smrg } 631659607e0Smrg if (j == 10) 632659607e0Smrg return FALSE; 633659607e0Smrg } 634659607e0Smrg 635659607e0Smrg return TRUE; 636659607e0Smrg} 637659607e0Smrg 638659607e0Smrgstatic Bool 639659607e0Smrgps2DisableDataReporting(InputInfoPtr pInfo) 640659607e0Smrg{ 641659607e0Smrg unsigned char packet[] = { 0xF5 }; 642659607e0Smrg return ps2SendPacket(pInfo, packet, sizeof(packet)); 643659607e0Smrg} 644659607e0Smrg 645659607e0SmrgBool 646659607e0Smrgps2EnableDataReporting(InputInfoPtr pInfo) 647659607e0Smrg{ 648659607e0Smrg unsigned char packet[] = { 0xF4 }; 649659607e0Smrg return ps2SendPacket(pInfo, packet, sizeof(packet)); 650659607e0Smrg} 651659607e0Smrg 652659607e0Smrgint 653659607e0Smrgps2GetDeviceID(InputInfoPtr pInfo) 654659607e0Smrg{ 655659607e0Smrg unsigned char u; 656659607e0Smrg unsigned char packet[] = { 0xf2 }; 657659607e0Smrg 658659607e0Smrg usleep(30000); 659659607e0Smrg xf86FlushInput(pInfo->fd); 660659607e0Smrg if (!ps2SendPacket(pInfo, packet, sizeof(packet))) 661659607e0Smrg return -1; 662659607e0Smrg while (1) { 663659607e0Smrg if (!readMouse(pInfo,&u)) 664659607e0Smrg return -1; 665659607e0Smrg if (u != 0xFA) 666659607e0Smrg break; 667659607e0Smrg } 668659607e0Smrg#ifdef DEBUG 669659607e0Smrg xf86ErrorF("Obtained Mouse Type: %x\n",u); 670659607e0Smrg#endif 671659607e0Smrg return (int) u; 672659607e0Smrg} 673659607e0Smrg 674659607e0SmrgBool 675659607e0Smrgps2Reset(InputInfoPtr pInfo) 676659607e0Smrg{ 677659607e0Smrg unsigned char u; 678659607e0Smrg unsigned char packet[] = { 0xff }; 679659607e0Smrg unsigned char reply[] = { 0xaa, 0x00 }; 680659607e0Smrg unsigned int i; 681659607e0Smrg#ifdef DEBUG 682659607e0Smrg xf86ErrorF("PS/2 Mouse reset\n"); 683659607e0Smrg#endif 684659607e0Smrg if (!ps2SendPacket(pInfo, packet, sizeof(packet))) 685659607e0Smrg return FALSE; 686659607e0Smrg /* we need a little delay here */ 687659607e0Smrg xf86WaitForInput(pInfo->fd, 500000); 688659607e0Smrg for (i = 0; i < sizeof(reply) ; i++) { 689659607e0Smrg if (!readMouse(pInfo,&u)) { 690659607e0Smrg goto EXIT; 691659607e0Smrg } 692659607e0Smrg if (u != reply[i]) 693659607e0Smrg goto EXIT; 694659607e0Smrg } 695659607e0Smrg return TRUE; 696659607e0Smrg 697659607e0Smrg EXIT: 698659607e0Smrg xf86FlushInput(pInfo->fd); 699659607e0Smrg return FALSE; 700659607e0Smrg} 701659607e0Smrg 702659607e0Smrgstatic MouseProtocolID 703659607e0SmrgprobePs2ProtocolPnP(InputInfoPtr pInfo) 704659607e0Smrg{ 705659607e0Smrg unsigned char u; 706659607e0Smrg MouseProtocolID ret = PROT_UNKNOWN; 707659607e0Smrg 708659607e0Smrg xf86FlushInput(pInfo->fd); 709659607e0Smrg 710659607e0Smrg ps2DisableDataReporting(pInfo); 711659607e0Smrg 712659607e0Smrg if (ps2Reset(pInfo)) { /* Reset PS2 device */ 713659607e0Smrg unsigned char seq[] = { 243, 200, 243, 100, 243, 80 }; 714659607e0Smrg /* Try to identify Intelli Mouse */ 715659607e0Smrg if (ps2SendPacket(pInfo, seq, sizeof(seq))) { 716659607e0Smrg u = ps2GetDeviceID(pInfo); 717659607e0Smrg if (u == 0x03) { 718659607e0Smrg /* found IntelliMouse now try IntelliExplorer */ 719659607e0Smrg unsigned char seq[] = { 243, 200, 243, 200, 243, 80 }; 720659607e0Smrg if (ps2SendPacket(pInfo,seq,sizeof(seq))) { 721659607e0Smrg u = ps2GetDeviceID(pInfo); 722659607e0Smrg if (u == 0x04) 723659607e0Smrg ret = PROT_EXPPS2; 724659607e0Smrg else 725659607e0Smrg ret = PROT_IMPS2; 726659607e0Smrg } 727659607e0Smrg } else if (ps2Reset(pInfo)) /* reset again to find sane state */ 728659607e0Smrg ret = PROT_PS2; 729659607e0Smrg } 730659607e0Smrg 731659607e0Smrg if (ret != PROT_UNKNOWN) 732659607e0Smrg ps2EnableDataReporting(pInfo); 733659607e0Smrg } 734659607e0Smrg return ret; 735659607e0Smrg} 736659607e0Smrg 737659607e0Smrgstatic struct ps2protos { 738659607e0Smrg int Id; 739659607e0Smrg MouseProtocolID protoID; 740659607e0Smrg} ps2 [] = { 741659607e0Smrg { 0x0, PROT_PS2 }, 742659607e0Smrg { 0x3, PROT_IMPS2 }, 743659607e0Smrg { 0x4, PROT_EXPPS2 }, 744659607e0Smrg { -1 , PROT_UNKNOWN } 745659607e0Smrg}; 746659607e0Smrg 747659607e0Smrg 748659607e0Smrgstatic MouseProtocolID 749659607e0SmrggetPs2ProtocolPnP(InputInfoPtr pInfo) 750659607e0Smrg{ 751659607e0Smrg int Id; 752659607e0Smrg int i; 753659607e0Smrg MouseProtocolID proto; 754659607e0Smrg int count = 4; 755659607e0Smrg 756659607e0Smrg xf86FlushInput(pInfo->fd); 757659607e0Smrg 758659607e0Smrg while (--count) 759659607e0Smrg if (ps2DisableDataReporting(pInfo)) 760659607e0Smrg break; 761659607e0Smrg 762659607e0Smrg if (!count) { 763659607e0Smrg proto = PROT_UNKNOWN; 764659607e0Smrg goto EXIT; 765659607e0Smrg } 766659607e0Smrg 767659607e0Smrg if ((Id = ps2GetDeviceID(pInfo)) == -1) { 768659607e0Smrg proto = PROT_UNKNOWN; 769659607e0Smrg goto EXIT; 770659607e0Smrg } 771659607e0Smrg 772659607e0Smrg if (-1 == ps2EnableDataReporting(pInfo)) { 773659607e0Smrg proto = PROT_UNKNOWN; 774659607e0Smrg goto EXIT; 775659607e0Smrg } 776659607e0Smrg 777659607e0Smrg for (i = 0; ps2[i].protoID != PROT_UNKNOWN; i++) { 778659607e0Smrg if (ps2[i].Id == Id) { 779659607e0Smrg xf86MsgVerb(X_PROBED,2,"Found PS/2 proto ID %x\n",Id); 780659607e0Smrg proto = ps2[i].protoID; 781659607e0Smrg goto EXIT; 782659607e0Smrg } 783659607e0Smrg } 784659607e0Smrg 785659607e0Smrg proto = PROT_UNKNOWN; 786659607e0Smrg xf86Msg(X_ERROR,"Found unknown PS/2 proto ID %x\n",Id); 787659607e0Smrg 788659607e0Smrg EXIT: 789659607e0Smrg xf86FlushInput(pInfo->fd); 790659607e0Smrg return proto; 791659607e0Smrg} 792659607e0Smrg 793