sun_mouse.c revision b73be646
1/*
2 * Copyright (c) 2004-2005, 2008-2010, Oracle and/or its affiliates.
3 * All rights reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24/*
25 * Copyright 1999-2001 The XFree86 Project, Inc.  All Rights Reserved.
26 *
27 * Permission is hereby granted, free of charge, to any person obtaining a copy
28 * of this software and associated documentation files (the "Software"), to
29 * deal in the Software without restriction, including without limitation the
30 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
31 * sell copies of the Software, and to permit persons to whom the Software is
32 * furnished to do so, subject to the following conditions:
33 *
34 * The above copyright notice and this permission notice shall be included in
35 * all copies or substantial portions of the Software.
36 *
37 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
40 * XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
41 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
42 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
43 *
44 * Except as contained in this notice, the name of the XFree86 Project shall
45 * not be used in advertising or otherwise to promote the sale, use or other
46 * dealings in this Software without prior written authorization from the
47 * XFree86 Project.
48 */
49
50#ifdef HAVE_XORG_CONFIG_H
51#include <xorg-config.h>
52#endif
53
54#include "xorg-server.h"
55#include "xf86.h"
56#include "xf86_OSlib.h"
57#include "xf86OSmouse.h"
58#include "xisb.h"
59#include "mipointer.h"
60#include <sys/stropts.h>
61#include <sys/vuid_event.h>
62#include <sys/msio.h>
63
64/* Wheel mouse support in VUID drivers in Solaris 9 updates & Solaris 10 */
65#ifdef WHEEL_DEVID /* Defined in vuid_event.h if VUID wheel support present */
66# define HAVE_VUID_WHEEL
67#endif
68#ifdef HAVE_VUID_WHEEL
69# include <sys/vuid_wheel.h>
70#endif
71
72/* Support for scaling absolute coordinates to screen size in
73 * Solaris 10 updates and beyond */
74#if !defined(HAVE_ABSOLUTE_MOUSE_SCALING)
75# ifdef MSIOSRESOLUTION /* Defined in msio.h if scaling support present */
76#  define HAVE_ABSOLUTE_MOUSE_SCALING
77# endif
78#endif
79
80/* Names of protocols that are handled internally here. */
81
82static const char *internalNames[] = {
83	"VUID",
84	NULL
85};
86
87static const char *solarisMouseDevs[] = {
88    /* Device file:	Protocol: 			*/
89    "/dev/mouse",	"VUID",		/* USB or SPARC */
90#if defined(__i386) || defined(__x86)
91    "/dev/kdmouse",	"PS/2",		/* PS/2 */
92#endif
93    NULL
94};
95
96typedef struct _VuidMseRec {
97    struct _VuidMseRec *next;
98    InputInfoPtr	pInfo;
99    Firm_event 		event;
100    unsigned char *	buffer;
101    char *		strmod;
102    Bool(*wrapped_device_control)(DeviceIntPtr device, int what);
103#ifdef HAVE_ABSOLUTE_MOUSE_SCALING
104    Ms_screen_resolution	 absres;
105#endif
106    OsTimerPtr		remove_timer;   /* Callback for removal on ENODEV */
107} VuidMseRec, *VuidMsePtr;
108
109static VuidMsePtr	vuidMouseList = NULL;
110
111static int  vuidMouseProc(DeviceIntPtr pPointer, int what);
112static void vuidReadInput(InputInfoPtr pInfo);
113
114#ifdef HAVE_ABSOLUTE_MOUSE_SCALING
115static void vuidMouseSendScreenSize(ScreenPtr pScreen, VuidMsePtr pVuidMse);
116static void vuidMouseAdjustFrame(int index, int x, int y, int flags);
117
118static int vuidMouseGeneration = 0;
119
120#if HAS_DEVPRIVATEKEYREC
121static DevPrivateKeyRec vuidMouseScreenIndex;
122#else
123static int vuidMouseScreenIndex;
124#endif /* HAS_DEVPRIVATEKEYREC */
125
126#define vuidMouseGetScreenPrivate(s) ( \
127    dixLookupPrivate(&(s)->devPrivates, &vuidMouseScreenIndex))
128#define vuidMouseSetScreenPrivate(s,p) \
129    dixSetPrivate(&(s)->devPrivates, &vuidMouseScreenIndex, (void *) p)
130#endif /* HAVE_ABSOLUTE_MOUSE_SCALING */
131
132static inline
133VuidMsePtr getVuidMsePriv(InputInfoPtr pInfo)
134{
135    VuidMsePtr m = vuidMouseList;
136
137    while ((m != NULL) && (m->pInfo != pInfo)) {
138	m = m->next;
139    }
140
141    return m;
142}
143
144/* Called from OsTimer callback, since removing a device from the device
145   list or changing pInfo->fd while xf86Wakeup is looping through the list
146   causes server crashes */
147static CARD32
148vuidRemoveMouse(OsTimerPtr timer, CARD32 time, pointer arg)
149{
150    InputInfoPtr pInfo = (InputInfoPtr) arg;
151
152    xf86DisableDevice(pInfo->dev, TRUE);
153
154    return 0;  /* All done, don't set to run again */
155}
156
157/*
158 * Initialize and enable the mouse wheel, if present.
159 *
160 * Returns 1 if mouse wheel was successfully enabled.
161 * Returns 0 if an error occurred or if there is no mouse wheel.
162 */
163static int
164vuidMouseWheelInit(InputInfoPtr pInfo)
165{
166#ifdef HAVE_VUID_WHEEL
167    wheel_state wstate;
168    int nwheel = -1;
169    int i;
170
171    wstate.vers = VUID_WHEEL_STATE_VERS;
172    wstate.id = 0;
173    wstate.stateflags = (uint32_t) -1;
174
175    SYSCALL(i = ioctl(pInfo->fd, VUIDGWHEELCOUNT, &nwheel));
176    if (i != 0)
177	return (0);
178
179    SYSCALL(i = ioctl(pInfo->fd, VUIDGWHEELSTATE, &wstate));
180    if (i != 0) {
181	xf86Msg(X_WARNING, "%s: couldn't get wheel state\n", pInfo->name);
182	return (0);
183    }
184
185    wstate.stateflags |= VUID_WHEEL_STATE_ENABLED;
186
187    SYSCALL(i = ioctl(pInfo->fd, VUIDSWHEELSTATE, &wstate));
188    if (i != 0) {
189	xf86Msg(X_WARNING, "%s: couldn't enable wheel\n", pInfo->name);
190	return (0);
191    }
192
193    return (1);
194#else
195    return (0);
196#endif
197}
198
199
200/* This function is called when the protocol is "VUID". */
201static Bool
202vuidPreInit(InputInfoPtr pInfo, const char *protocol, int flags)
203{
204    MouseDevPtr pMse = pInfo->private;
205    VuidMsePtr pVuidMse;
206    int buttons, i;
207
208    pVuidMse = xcalloc(sizeof(VuidMseRec), 1);
209    if (pVuidMse == NULL) {
210	xf86Msg(X_ERROR, "%s: cannot allocate VuidMouseRec\n", pInfo->name);
211	xfree(pMse);
212	return FALSE;
213    }
214
215    pMse->protocol = protocol;
216    xf86Msg(X_CONFIG, "%s: Protocol: %s\n", pInfo->name, protocol);
217
218    /* Collect the options, and process the common options. */
219    xf86CollectInputOptions(pInfo, NULL, NULL);
220    xf86ProcessCommonOptions(pInfo, pInfo->options);
221
222    pVuidMse->buffer = (unsigned char *)&pVuidMse->event;
223    pVuidMse->strmod = xf86SetStrOption(pInfo->options, "StreamsModule", NULL);
224
225    /* Check if the device can be opened. */
226    pInfo->fd = xf86OpenSerial(pInfo->options);
227    if (pInfo->fd == -1) {
228	if (xf86GetAllowMouseOpenFail()) {
229	    xf86Msg(X_WARNING, "%s: cannot open input device\n", pInfo->name);
230	} else {
231	    xf86Msg(X_ERROR, "%s: cannot open input device\n", pInfo->name);
232	    xfree(pVuidMse->strmod);
233	    xfree(pVuidMse);
234	    xfree(pMse);
235	    return FALSE;
236	}
237    } else {
238	if (pVuidMse->strmod) {
239	    /* Check to see if module is already pushed */
240	    SYSCALL(i = ioctl(pInfo->fd, I_FIND, pVuidMse->strmod));
241
242	    if (i == 0) { /* Not already pushed */
243		SYSCALL(i = ioctl(pInfo->fd, I_PUSH, pVuidMse->strmod));
244		if (i < 0) {
245		    xf86Msg(X_ERROR,
246			"%s: cannot push module '%s' onto mouse device: %s\n",
247			pInfo->name, pVuidMse->strmod, strerror(errno));
248		    xf86CloseSerial(pInfo->fd);
249		    pInfo->fd = -1;
250		    xfree(pVuidMse->strmod);
251		    xfree(pVuidMse);
252		    xfree(pMse);
253		    return FALSE;
254		}
255	    }
256	}
257
258	buttons = xf86SetIntOption(pInfo->options, "Buttons", 0);
259	if (buttons == 0) {
260	    SYSCALL(i = ioctl(pInfo->fd, MSIOBUTTONS, &buttons));
261	    if (i == 0) {
262		pInfo->conf_idev->commonOptions =
263		    xf86ReplaceIntOption(pInfo->conf_idev->commonOptions,
264					 "Buttons", buttons);
265		xf86Msg(X_INFO, "%s: Setting Buttons option to \"%d\"\n",
266			pInfo->name, buttons);
267	    }
268	}
269
270	if (pVuidMse->strmod) {
271	    SYSCALL(i = ioctl(pInfo->fd, I_POP, pVuidMse->strmod));
272	    if (i == -1) {
273		xf86Msg(X_WARNING,
274			"%s: cannot pop module '%s' off mouse device: %s\n",
275			pInfo->name, pVuidMse->strmod, strerror(errno));
276	    }
277	}
278
279	xf86CloseSerial(pInfo->fd);
280	pInfo->fd = -1;
281    }
282
283    /* Process common mouse options (like Emulate3Buttons, etc). */
284    pMse->CommonOptions(pInfo);
285
286    /* Setup the local procs. */
287    pVuidMse->wrapped_device_control = pInfo->device_control;
288    pInfo->device_control = vuidMouseProc;
289    pInfo->read_input = vuidReadInput;
290
291    pMse->xisbscale = sizeof(Firm_event);
292
293#ifdef HAVE_ABSOLUTE_MOUSE_SCALING
294    pVuidMse->absres.height = pVuidMse->absres.width = 0;
295#endif
296    pVuidMse->pInfo = pInfo;
297    pVuidMse->next = vuidMouseList;
298    vuidMouseList = pVuidMse;
299
300    pInfo->flags |= XI86_CONFIGURED;
301    return TRUE;
302}
303
304static void
305vuidFlushAbsEvents(InputInfoPtr pInfo, int absX, int absY,
306		   Bool *absXset, Bool *absYset)
307{
308#ifdef DEBUG
309    ErrorF("vuidFlushAbsEvents: %d,%d (set: %d, %d)\n", absX, absY,
310	   *absXset, *absYset);
311#endif
312    if ((*absXset) && (*absYset)) {
313	xf86PostMotionEvent(pInfo->dev,
314			    /* is_absolute: */    TRUE,
315			    /* first_valuator: */ 0,
316			    /* num_valuators: */  2,
317			    absX, absY);
318    } else if (*absXset) {
319	xf86PostMotionEvent(pInfo->dev,
320			    /* is_absolute: */    TRUE,
321			    /* first_valuator: */ 0,
322			    /* num_valuators: */  1,
323			    absX);
324    } else if (*absYset) {
325	xf86PostMotionEvent(pInfo->dev,
326			    /* is_absolute: */    TRUE,
327			    /* first_valuator: */ 1,
328			    /* num_valuators: */  1,
329			    absY);
330    }
331
332    *absXset = FALSE;
333    *absYset = FALSE;
334}
335
336static void
337vuidReadInput(InputInfoPtr pInfo)
338{
339    MouseDevPtr pMse;
340    VuidMsePtr pVuidMse;
341    int buttons;
342    int dx = 0, dy = 0, dz = 0, dw = 0;
343    unsigned int n;
344    int c;
345    unsigned char *pBuf;
346    int absX = 0, absY = 0;
347    Bool absXset = FALSE, absYset = FALSE;
348
349    pMse = pInfo->private;
350    pVuidMse = getVuidMsePriv(pInfo);
351    buttons = pMse->lastButtons;
352    pBuf = pVuidMse->buffer;
353    n = 0;
354
355    do {
356	n = read(pInfo->fd, pBuf, sizeof(Firm_event));
357
358	if (n == 0) {
359	    break;
360	} else if (n == -1) {
361	    switch (errno) {
362		case EAGAIN: /* Nothing to read now */
363		    n = 0;   /* End loop, go on to flush events & return */
364		    continue;
365		case EINTR:  /* Interrupted, try again */
366		    continue;
367		case ENODEV: /* May happen when USB mouse is unplugged */
368		    /* We use X_NONE here because it doesn't alloc since we
369		       may be called from SIGIO handler */
370		    xf86MsgVerb(X_NONE, 0,
371				"%s: Device no longer present - removing.\n",
372				pInfo->name);
373		    xf86RemoveEnabledDevice(pInfo);
374		    pVuidMse->remove_timer =
375			TimerSet(pVuidMse->remove_timer, 0, 1,
376				 vuidRemoveMouse, pInfo);
377		    return;
378		default:     /* All other errors */
379		    /* We use X_NONE here because it doesn't alloc since we
380		       may be called from SIGIO handler */
381		    xf86MsgVerb(X_NONE, 0, "%s: Read error: %s\n", pInfo->name,
382				strerror(errno));
383		    return;
384	    }
385	} else if (n != sizeof(Firm_event)) {
386	    xf86Msg(X_WARNING, "%s: incomplete packet, size %d\n",
387			pInfo->name, n);
388	}
389
390#ifdef DEBUG
391	ErrorF("vuidReadInput: event type: %3d value: %5d\n",
392	       pVuidMse->event.id, pVuidMse->event.value);
393#endif
394
395	if (pVuidMse->event.id >= BUT_FIRST && pVuidMse->event.id <= BUT_LAST) {
396	    /* button */
397	    int butnum = pVuidMse->event.id - BUT_FIRST;
398
399	    if (butnum < 3)
400		butnum = 2 - butnum;
401	    if (!pVuidMse->event.value)
402		buttons &= ~(1 << butnum);
403	    else
404		buttons |= (1 << butnum);
405	} else if (pVuidMse->event.id >= VLOC_FIRST &&
406		   pVuidMse->event.id <= VLOC_LAST) {
407	    /* axis */
408	    int delta = pVuidMse->event.value;
409	    switch(pVuidMse->event.id) {
410	    case LOC_X_DELTA:
411		dx += delta;
412		break;
413	    case LOC_Y_DELTA:
414		dy -= delta;
415		break;
416	    case LOC_X_ABSOLUTE:
417		if (absXset) {
418		    vuidFlushAbsEvents(pInfo, absX, absY, &absXset, &absYset);
419		}
420		absX = delta;
421		absXset = TRUE;
422		break;
423	    case LOC_Y_ABSOLUTE:
424		if (absYset) {
425		    vuidFlushAbsEvents(pInfo, absX, absY, &absXset, &absYset);
426		}
427		absY = delta;
428		absYset = TRUE;
429		break;
430	    }
431	}
432#ifdef HAVE_VUID_WHEEL
433	else if (vuid_in_range(VUID_WHEEL, pVuidMse->event.id)) {
434	    if (vuid_id_offset(pVuidMse->event.id) == 0)
435		dz -= VUID_WHEEL_GETDELTA(pVuidMse->event.value);
436	    else
437		dw -= VUID_WHEEL_GETDELTA(pVuidMse->event.value);
438	}
439#endif
440#ifdef HAVE_ABSOLUTE_MOUSE_SCALING
441	else if (pVuidMse->event.id == MOUSE_TYPE_ABSOLUTE) {
442	    ScreenPtr 	ptrCurScreen;
443
444	    /* force sending absolute resolution scaling ioctl */
445	    pVuidMse->absres.height = pVuidMse->absres.width = 0;
446#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) == 0
447	    ptrCurScreen = miPointerCurrentScreen();
448#else
449	    ptrCurScreen = miPointerGetScreen(pInfo->dev);
450#endif
451	    vuidMouseSendScreenSize(ptrCurScreen, pVuidMse);
452	}
453#endif
454
455    } while (n != 0);
456
457    if (absXset || absYset) {
458	vuidFlushAbsEvents(pInfo, absX, absY, &absXset, &absYset);
459    }
460
461    pMse->PostEvent(pInfo, buttons, dx, dy, dz, dw);
462    return;
463}
464
465#ifdef HAVE_ABSOLUTE_MOUSE_SCALING
466static void vuidMouseSendScreenSize(ScreenPtr pScreen, VuidMsePtr pVuidMse)
467{
468    InputInfoPtr pInfo = pVuidMse->pInfo;
469    ScrnInfoPtr pScr = XF86SCRNINFO(pScreen);
470    int result;
471
472    if (!pScr->currentMode)
473	return;
474
475    if ((pVuidMse->absres.width != pScr->currentMode->HDisplay) ||
476	(pVuidMse->absres.height != pScr->currentMode->VDisplay))
477    {
478	pVuidMse->absres.width = pScr->currentMode->HDisplay;
479	pVuidMse->absres.height = pScr->currentMode->VDisplay;
480
481	do {
482	    result = ioctl(pInfo->fd, MSIOSRESOLUTION, &(pVuidMse->absres));
483	} while ( (result != 0) && (errno == EINTR) );
484
485	if (result != 0) {
486	    xf86Msg(X_WARNING,
487		    "%s: couldn't set absolute mouse scaling resolution: %s\n",
488		    pInfo->name, strerror(errno));
489#ifdef DEBUG
490	} else {
491	    xf86Msg(X_INFO,
492		    "%s: absolute mouse scaling resolution set to %d x %d\n",
493		    pInfo->name,
494		    pVuidMse->absres.width, pVuidMse->absres.height);
495#endif
496	}
497    }
498}
499
500static void vuidMouseAdjustFrame(int index, int x, int y, int flags)
501{
502      ScrnInfoPtr	pScrn = xf86Screens[index];
503      ScreenPtr		pScreen = pScrn->pScreen;
504      xf86AdjustFrameProc *wrappedAdjustFrame
505	  = (xf86AdjustFrameProc *) vuidMouseGetScreenPrivate(pScreen);
506      VuidMsePtr	m;
507      ScreenPtr 	ptrCurScreen;
508
509      if(wrappedAdjustFrame) {
510        pScrn->AdjustFrame = wrappedAdjustFrame;
511        (*pScrn->AdjustFrame)(index, x, y, flags);
512        pScrn->AdjustFrame = vuidMouseAdjustFrame;
513      }
514
515#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) == 0
516      ptrCurScreen = miPointerCurrentScreen();
517#endif
518
519      for (m = vuidMouseList; m != NULL ; m = m->next) {
520#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 0
521	  ptrCurScreen = miPointerGetScreen(m->pInfo->dev);
522#endif
523	  if (ptrCurScreen == pScreen)
524	  {
525	      vuidMouseSendScreenSize(pScreen, m);
526	  }
527      }
528}
529#endif /* HAVE_ABSOLUTE_MOUSE_SCALING */
530
531
532static int
533vuidMouseProc(DeviceIntPtr pPointer, int what)
534{
535    InputInfoPtr pInfo;
536    MouseDevPtr pMse;
537    VuidMsePtr pVuidMse;
538    int ret = Success;
539    int i;
540
541    pInfo = pPointer->public.devicePrivate;
542    pMse = pInfo->private;
543    pMse->device = pPointer;
544
545    pVuidMse = getVuidMsePriv(pInfo);
546    if (pVuidMse == NULL) {
547	return BadImplementation;
548    }
549
550    switch (what) {
551
552    case DEVICE_INIT:
553#ifdef HAVE_ABSOLUTE_MOUSE_SCALING
554
555#if HAS_DEVPRIVATEKEYREC
556	if (!dixRegisterPrivateKey(&vuidMouseScreenIndex, PRIVATE_SCREEN, 0))
557		return BadAlloc;
558#endif  /* HAS_DEVPRIVATEKEYREC */
559
560	if (vuidMouseGeneration != serverGeneration) {
561		for (i = 0; i < screenInfo.numScreens; i++) {
562		    ScreenPtr pScreen = screenInfo.screens[i];
563		    ScrnInfoPtr pScrn = XF86SCRNINFO(pScreen);
564		    vuidMouseSetScreenPrivate(pScreen, pScrn->AdjustFrame);
565		    pScrn->AdjustFrame = vuidMouseAdjustFrame;
566		}
567	    vuidMouseGeneration = serverGeneration;
568	}
569#endif
570	ret = pVuidMse->wrapped_device_control(pPointer, what);
571	break;
572
573    case DEVICE_ON:
574	ret = pVuidMse->wrapped_device_control(pPointer, DEVICE_ON);
575
576	if ((ret == Success) && (pInfo->fd != -1)) {
577	    int fmt = VUID_FIRM_EVENT;
578
579	    if (pVuidMse->strmod) {
580		/* Check to see if module is already pushed */
581		SYSCALL(i = ioctl(pInfo->fd, I_FIND, pVuidMse->strmod));
582
583		if (i == 0) { /* Not already pushed */
584		    SYSCALL(i = ioctl(pInfo->fd, I_PUSH, pVuidMse->strmod));
585		    if (i < 0) {
586			xf86Msg(X_WARNING, "%s: cannot push module '%s' "
587				"onto mouse device: %s\n", pInfo->name,
588				pVuidMse->strmod, strerror(errno));
589			xfree(pVuidMse->strmod);
590			pVuidMse->strmod = NULL;
591		    }
592		}
593	    }
594
595	    SYSCALL(i = ioctl(pInfo->fd, VUIDSFORMAT, &fmt));
596	    if (i < 0) {
597		xf86Msg(X_WARNING,
598			"%s: cannot set mouse device to VUID mode: %s\n",
599			pInfo->name, strerror(errno));
600	    }
601	    vuidMouseWheelInit(pInfo);
602#ifdef HAVE_ABSOLUTE_MOUSE_SCALING
603	    vuidMouseSendScreenSize(screenInfo.screens[0], pVuidMse);
604#endif
605	    xf86FlushInput(pInfo->fd);
606
607	    /* Allocate here so we don't alloc in ReadInput which may be called
608	       from SIGIO handler. */
609	    if (pVuidMse->remove_timer == NULL) {
610		pVuidMse->remove_timer = TimerSet(pVuidMse->remove_timer,
611						  0, 0, NULL, NULL);
612	    }
613	}
614	break;
615
616    case DEVICE_OFF:
617    case DEVICE_CLOSE:
618	if (pInfo->fd != -1) {
619	    if (pVuidMse->strmod) {
620		SYSCALL(i = ioctl(pInfo->fd, I_POP, pVuidMse->strmod));
621		if (i == -1) {
622		    xf86Msg(X_WARNING,
623		      "%s: cannot pop module '%s' off mouse device: %s\n",
624		      pInfo->name, pVuidMse->strmod, strerror(errno));
625		}
626	    }
627	}
628	if (pVuidMse->remove_timer) {
629	    TimerFree(pVuidMse->remove_timer);
630	    pVuidMse->remove_timer = NULL;
631	}
632	ret = pVuidMse->wrapped_device_control(pPointer, what);
633	break;
634
635    default: /* Should never be called, but just in case */
636	ret = pVuidMse->wrapped_device_control(pPointer, what);
637	break;
638    }
639    return ret;
640}
641
642static Bool
643sunMousePreInit(InputInfoPtr pInfo, const char *protocol, int flags)
644{
645    /* The protocol is guaranteed to be one of the internalNames[] */
646    if (xf86NameCmp(protocol, "VUID") == 0) {
647	return vuidPreInit(pInfo, protocol, flags);
648    }
649    return TRUE;
650}
651
652static const char **
653BuiltinNames(void)
654{
655    return internalNames;
656}
657
658static Bool
659CheckProtocol(const char *protocol)
660{
661    int i;
662
663    for (i = 0; internalNames[i]; i++)
664	if (xf86NameCmp(protocol, internalNames[i]) == 0)
665	    return TRUE;
666
667    return FALSE;
668}
669
670static const char *
671DefaultProtocol(void)
672{
673    return "Auto";
674}
675
676static Bool
677solarisMouseAutoProbe(InputInfoPtr pInfo, const char **protocol,
678	const char **device)
679{
680    const char **pdev, **pproto;
681    int fd = -1;
682    Bool found;
683    char *strmod;
684
685    if (*device == NULL) {
686	/* Check to see if xorg.conf or HAL specified a device to use */
687	*device = xf86CheckStrOption(pInfo->options, "Device", NULL);
688	if (*device == NULL) {
689	    *device = xf86CheckStrOption(pInfo->conf_idev->commonOptions,
690					 "Device", NULL);
691	}
692    }
693
694    if (*device != NULL) {
695	strmod = xf86CheckStrOption(pInfo->options, "StreamsModule", NULL);
696	if (strmod == NULL) {
697	    strmod = xf86CheckStrOption(pInfo->conf_idev->commonOptions,
698					"StreamsModule", NULL);
699	}
700	if (strmod) {
701	    /* if a device name is already known, and a StreamsModule is
702	       specified to convert events to VUID, then we don't need to
703	       probe further */
704	    *protocol = "VUID";
705	    return TRUE;
706	}
707    }
708
709
710    for (pdev = solarisMouseDevs; *pdev; pdev += 2) {
711	pproto = pdev + 1;
712	if ((*protocol != NULL) && (strcmp(*protocol, "Auto") != 0) &&
713	  (*pproto != NULL) && (strcmp(*pproto, *protocol) != 0)) {
714	    continue;
715	}
716	if ((*device != NULL) && (strcmp(*device, *pdev) != 0)) {
717	    continue;
718	}
719        SYSCALL (fd = open(*pdev, O_RDWR | O_NONBLOCK));
720	if (fd == -1) {
721#ifdef DEBUG
722	    ErrorF("Cannot open %s (%s)\n", pdev, strerror(errno));
723#endif
724	} else {
725	    found = TRUE;
726	    if ((*pproto != NULL) && (strcmp(*pproto, "VUID") == 0)) {
727		int i, r;
728		SYSCALL(r = ioctl(fd, VUIDGFORMAT, &i));
729    		if (r < 0) {
730		    found = FALSE;
731		}
732	    }
733	    close(fd);
734	    if (found == TRUE) {
735		if (*pproto != NULL) {
736		    *protocol = *pproto;
737		}
738		*device = *pdev;
739		return TRUE;
740	    }
741	}
742    }
743    return FALSE;
744}
745
746static const char *
747SetupAuto(InputInfoPtr pInfo, int *protoPara)
748{
749    const char *pdev = NULL;
750    const char *pproto = NULL;
751    MouseDevPtr pMse = pInfo->private;
752
753    if (pInfo->fd == -1) {
754	/* probe to find device/protocol to use */
755	if (solarisMouseAutoProbe(pInfo, &pproto, &pdev) != FALSE) {
756	    /* Set the Device option. */
757	    pInfo->conf_idev->commonOptions =
758	     xf86AddNewOption(pInfo->conf_idev->commonOptions, "Device", pdev);
759	    xf86Msg(X_INFO, "%s: Setting Device option to \"%s\"\n",
760	      pInfo->name, pdev);
761	}
762    } else if (pMse->protocolID == PROT_AUTO) {
763	pdev = xf86CheckStrOption(pInfo->conf_idev->commonOptions,
764		"Device", NULL);
765	solarisMouseAutoProbe(pInfo, &pproto, &pdev);
766    }
767    return pproto;
768}
769
770static const char *
771FindDevice(InputInfoPtr pInfo, const char *protocol, int flags)
772{
773    const char *pdev = NULL;
774    const char *pproto = protocol;
775
776    if (solarisMouseAutoProbe(pInfo, &pproto, &pdev) != FALSE) {
777	/* Set the Device option. */
778	pInfo->conf_idev->commonOptions =
779	  xf86AddNewOption(pInfo->conf_idev->commonOptions, "Device", pdev);
780	xf86Msg(X_INFO, "%s: Setting Device option to \"%s\"\n",
781	  pInfo->name, pdev);
782    }
783    return pdev;
784}
785
786static int
787SupportedInterfaces(void)
788{
789    /* XXX This needs to be checked. */
790    return MSE_SERIAL | MSE_BUS | MSE_PS2 | MSE_AUTO | MSE_XPS2 | MSE_MISC;
791}
792
793_X_EXPORT OSMouseInfoPtr
794xf86OSMouseInit(int flags)
795{
796    OSMouseInfoPtr p;
797
798    p = xcalloc(sizeof(OSMouseInfoRec), 1);
799    if (!p)
800	return NULL;
801    p->SupportedInterfaces = SupportedInterfaces;
802    p->BuiltinNames = BuiltinNames;
803    p->CheckProtocol = CheckProtocol;
804    p->PreInit = sunMousePreInit;
805    p->DefaultProtocol = DefaultProtocol;
806    p->SetupAuto = SetupAuto;
807    p->FindDevice = FindDevice;
808
809    return p;
810}
811
812