Subject: xsrc/23964: XFree86 4.x input driver for USB graphic tablets
To: None <gnats-bugs@gnats.NetBSD.org>
From: Dave Huang <khym@azeotrope.org>
List: netbsd-bugs
Date: 01/03/2004 18:28:05
>Number:         23964
>Category:       xsrc
>Synopsis:       XFree86 4.x input driver for USB graphic tablets
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    xsrc-manager
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sun Jan 04 00:29:00 UTC 2004
>Closed-Date:
>Last-Modified:
>Originator:     Dave Huang
>Release:        NetBSD 1.6ZG
>Organization:
	
>Environment:
	
	
System: NetBSD fluff.azeotrope.org 1.6ZG NetBSD 1.6ZG (FLUFF) #47: Sat Jan 3 03:52:35 CST 2004 khym@fluff.azeotrope.org:/usr/src.local/sys/arch/i386/compile/obj.i386/FLUFF i386
Architecture: i386
Machine: i386
>Description:
	Lennart Augustsson wrote the original XFree86 3.3.6 version of
this USB tablet driver, which is in NetBSD as
xsrc/xc/programs/Xserver/hw/xfree86/common/xf86USBtablet.c.  Matthieu
Herrb then adapted it to XFree86 4.x and committed it to the OpenBSD
tree; it never made it back into NetBSD though, so here it is.
>How-To-Repeat:
	Try to use a USB tablet such as a Wacom Graphire with XFree86 4.3.0.
>Fix:
diff --unidirectional -ur /mnt/xsrc/xfree/xc/config/cf/NetBSD.cf xsrc/xfree/xc/config/cf/NetBSD.cf
--- /mnt/xsrc/xfree/xc/config/cf/NetBSD.cf	2003-12-29 23:35:42.000000000 -0600
+++ xsrc/xfree/xc/config/cf/NetBSD.cf	2004-01-03 04:11:30.000000000 -0600
@@ -130,6 +130,21 @@
 # define HasArc4random
 #endif
 
+#if (OSMajorVersion >= 2 || \
+      (OSMajorVersion == 1 && OSMinorVersion >= 5))
+# define HasLibUsb		YES
+# ifndef HasLibUsbHid
+#  if (OSMajorVersion >= 2 || \
+        (OSMajorVersion == 1 && OSMinorVersion == 6))
+#   define HasLibUsbHid		YES
+#   define UsbHidLib		-lusbhid
+#  else
+#   define HasLibUsbHid		NO
+#   define UsbHidLib		-lusb
+#  endif
+# endif
+#endif
+
 /*
  * Compiler Features
  */
@@ -473,6 +488,13 @@
 # endif
 #endif
 
+/* USB tablets */
+#ifndef OSXInputDrivers
+# if HasLibUsb
+#  define OSXInputDrivers	usbtablet
+# endif
+#endif
+
 /*
  * ForceSubdirs - force make to build subdirectories
  * 
diff --unidirectional -ur /mnt/xsrc/xfree/xc/programs/Xserver/Imakefile xsrc/xfree/xc/programs/Xserver/Imakefile
--- /mnt/xsrc/xfree/xc/programs/Xserver/Imakefile	2003-12-29 23:43:22.000000000 -0600
+++ xsrc/xfree/xc/programs/Xserver/Imakefile	2003-12-30 01:36:44.000000000 -0600
@@ -275,13 +275,11 @@
       SERVERFT2 = $(FREETYPE2LIB)
 #endif
        FONTLIBS = $(FONT) $(XPFBLIBS) $(SERVERFT2)
-#if UsbMouseSupport 
 #if !HasLibUsb
             USB = $(XF86OSSRC)/bsd/libusb/LibraryTargetName(usb)
 #else
             USB = UsbHidLib
 #endif
-#endif
 #ifdef ServerExtraSysLibs
    EXTRASYSLIBS = ServerExtraSysLibs
 #endif
diff --unidirectional -ur /mnt/xsrc/xfree/xc/programs/Xserver/hw/xfree86/input/usbtablet/Imakefile xsrc/xfree/xc/programs/Xserver/hw/xfree86/input/usbtablet/Imakefile
--- /mnt/xsrc/xfree/xc/programs/Xserver/hw/xfree86/input/usbtablet/Imakefile	1969-12-31 18:00:00.000000000 -0600
+++ xsrc/xfree/xc/programs/Xserver/hw/xfree86/input/usbtablet/Imakefile	2004-01-03 04:20:37.000000000 -0600
@@ -0,0 +1,32 @@
+XCOMM $XFree86: xc/programs/Xserver/hw/xfree86/input/wacom/Imakefile,v 1.8 2001/01/24 00:06:39 dawes Exp $
+
+#define IHaveModules
+#include <Server.tmpl>
+
+
+SRCS = xf86USBtablet.c
+OBJS = xf86USBtablet.o
+
+DRIVER = usbtablet
+
+INCLUDES = -I. -I$(XF86COMSRC) -I$(XF86SRC)/loader -I$(XF86OSSRC) \
+         -I$(SERVERSRC)/include -I$(SERVERSRC)/mi -I$(XINCLUDESRC) -I$(EXTINCSRC)
+
+#if MakeHasPosixVariableSubstitutions
+SubdirLibraryRule($(OBJS))
+#endif
+
+ModuleObjectRule()
+
+ObjectModuleTarget($(DRIVER),$(OBJS))
+
+InstallObjectModule($(DRIVER),$(MODULEDIR),input)
+
+#if !defined(XF86DriverSDK)
+InstallModuleManPage($(DRIVER))
+#endif
+
+
+DependTarget()
+
+InstallDriverSDKObjectModule($(DRIVER),$(DRIVERSDKMODULEDIR),input)
diff --unidirectional -ur /mnt/xsrc/xfree/xc/programs/Xserver/hw/xfree86/input/usbtablet/usbtablet.man xsrc/xfree/xc/programs/Xserver/hw/xfree86/input/usbtablet/usbtablet.man
--- /mnt/xsrc/xfree/xc/programs/Xserver/hw/xfree86/input/usbtablet/usbtablet.man	1969-12-31 18:00:00.000000000 -0600
+++ xsrc/xfree/xc/programs/Xserver/hw/xfree86/input/usbtablet/usbtablet.man	2004-01-03 04:20:37.000000000 -0600
@@ -0,0 +1,107 @@
+.\" $XFree86$
+.\"
+.\" Copyright (C) 2002 The XFree86 Project, Inc.  All Rights Reserved.
+.\" 
+.\" Permission is hereby granted, free of charge, to any person
+.\" obtaining a copy of this software and associated documentation
+.\" files (the "Software"), to deal in the Software without
+.\" restriction, including without limitation the rights to use, copy,
+.\" modify, merge, publish, distribute, sublicense, and/or sell copies
+.\" of the Software, and to permit persons to whom the Software is
+.\" furnished to do so, subject to the following conditions:
+.\" 
+.\" The above copyright notice and this permission notice shall be
+.\" included in all copies or substantial portions of the Software.
+.\" 
+.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+.\" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+.\" NONINFRINGEMENT.  IN NO EVENT SHALL THE XFREE86 PROJECT BE LIABLE
+.\" FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+.\" CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+.\" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+.\" 
+.\" Except as contained in this notice, the name of the XFree86 Project
+.\" shall not be used in advertising or otherwise to promote the sale,
+.\" use or other dealings in this Software without prior written
+.\" authorization from the XFree86 Project.  
+.\"
+.\" shorthand for double quote that works everywhere.
+.ds q \N'34'
+.TH USBTABLET __drivermansuffix__ __vendorversion__
+.SH NAME
+usbtablet \- USB tablet input driver for *BSD
+.SH SYNOPSIS
+.nf
+.B "Section \*qInputDevice\*q"
+.BI "  Identifier \*q" idevname \*q
+.B  "  Driver \*qusbtablet\*q"
+.BI "  Option \*qDevice\*q   \*q" devpath \*q
+\ \ ...
+.B EndSection
+.fi
+.SH DESCRIPTION
+.B usbtablet 
+is an XFree86 input driver for USB tablet devices.
+.PP
+The
+.B usbtablet
+driver functions as a pointer input device, and may be used as the
+X server's core pointer.
+.SH SUPPORTED HARDWARE
+This driver supports most USB tablets like the Wacom Graphire or
+PenPartner tablets. 
+.SH CONFIGURATION DETAILS
+Please refer to XF86Config(__filemansuffix__) for general configuration
+details and for options that can be used with all input drivers.  This
+section only covers configuration details specific to this driver.
+.PP
+Each device
+supports the following entries:
+.RS 8
+.TP 4
+.B Option \fI"Type"\fP \fI"stylus"|"eraser"\fP
+sets the type of tool the device represent. This option is mandatory.
+.TP 4
+.B Option \fI"Device"\fP \fI"path"\fP
+sets the path to the special file which represents serial line where
+the tablet is plugged.  You have to specify it for each subsection with
+the same value if you want to have multiple devices with the same tablet.
+This option is mandatory.
+.TP 4
+.B Option \fI"Suppress"\fP \fI"Inumber"\fP
+sets the position increment under which not to transmit coordinates.
+ If you don't specify this entry, the default value is 2.
+.TP 4
+.B Option \fI"Threshold"\fP \fI"number"\fP
+sets the pressure threshold used to generate a button 1 events of stylus
+devices. The default value is 5. 
+.TP 4
+.B Option \fI"Mode"\fP \fI"Relative"|"Absolute"\fP
+sets the mode of the device.
+.TP 4
+.B Option \fI"HistorySize"\fP \fI"number"\fP
+sets the motion history size. By default the value is zero.
+.TP 4
+.B Option \fI"FactorX"\fP \fI"number"\fP
+Scaling factor between tablet coordinates and X screen coordinates. If
+you don't specify it, it will be calculated to match the width of your
+tablet to the width of your screen. 
+.TP 4
+.B Option \fI"FactorY"\fP \fI"number"\fP
+Scaling factor between tablet coordinates and X screen coordinates. If
+you don't specify it, it will be calculated to match the height of your
+tablet to the height of your screen. 
+.TP 4
+.TP 4
+.B Option \fI"DebugLevel"\fP \fInumber \fP
+sets the level of debugging info reported.
+.TP 4
+.RE
+.SH "SEE ALSO"
+XFree86(1), XF86Config(__filemansuffix__), xf86config(1), Xserver(1), X(__miscmansuffix__).
+.SH AUTHORS
+Lennart Augustsson <augustss@netbsd.org> wrote the original version
+for XFree86 3.3.6, Matthieu Herrb <matthieu@openbsd.org> adapted it to
+XFree86 4.x. 
+
diff --unidirectional -ur /mnt/xsrc/xfree/xc/programs/Xserver/hw/xfree86/input/usbtablet/xf86USBtablet.c xsrc/xfree/xc/programs/Xserver/hw/xfree86/input/usbtablet/xf86USBtablet.c
--- /mnt/xsrc/xfree/xc/programs/Xserver/hw/xfree86/input/usbtablet/xf86USBtablet.c	1969-12-31 18:00:00.000000000 -0600
+++ xsrc/xfree/xc/programs/Xserver/hw/xfree86/input/usbtablet/xf86USBtablet.c	2004-01-03 04:20:37.000000000 -0600
@@ -0,0 +1,1035 @@
+/*
+ * Copyright (c) 1999 Lennart Augustsson <augustss@netbsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $OpenBSD: xf86USBtablet.c,v 1.2 2002/01/05 19:22:47 matthieu Exp $ */
+
+/*
+ * Driver for USB HID tablet devices.
+ * Works for:
+ *   Wacom PenPartner
+ *   Wacom Graphire
+ */
+
+#ifndef XFree86LOADER
+#include <unistd.h>
+#include <errno.h>
+#endif
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <misc.h>
+#include <xf86.h>
+#define NEED_XF86_TYPES
+#include <xf86_ansic.h>
+#include <xf86_OSproc.h>
+#include <xf86Xinput.h>
+#include <xisb.h>
+#include <exevents.h>		/* Needed for InitValuator/Proximity stuff */
+#include <extnsionst.h>
+#include <extinit.h>
+
+#ifdef XFree86LOADER
+#include "xf86Module.h"
+#endif
+
+
+#ifdef USB_GET_REPORT_ID
+#define USB_NEW_HID
+#endif
+
+#include <usbhid.h>
+
+#define SYSCALL(call) while(((call) == -1) && (errno == EINTR))
+#define ABS(x) ((x) > 0 ? (x) : -(x))
+#define mils(res) (res * 1000 / 2.54) /* resolution */
+
+#define STYLUS_SEC	"usbstylus" /* config section name */
+#define STYLUS_XI	"Stylus"    /* X device name for the stylus */
+#define STYLUS_ID	1	    /* local id */
+
+#define ERASER_SEC	"usberaser" /* config section name */
+#define ERASER_XI	"Eraser"    /* X device name for the stylus */
+#define ERASER_ID	2	    /* local id */
+
+#define ABSOLUTE_FLAG	0x10000
+
+#define NBUTTONS 4
+#define NAXES 5	/* X, Y, Pressure, Tilt-X, Tilt-Y */
+
+typedef struct USBTDevice USBTDevice, *USBTDevicePtr;
+
+typedef struct {
+	char		*devName;
+	int		nDevs;
+	InputInfoPtr	*devices;
+	double		factorX;
+	double		factorY;
+	hid_item_t	hidX;
+	hid_item_t	hidY;
+	hid_item_t	hidTiltX;
+	hid_item_t	hidTiltY;
+	hid_item_t	hidIn_Range;
+	hid_item_t	hidTip_Pressure;
+	hid_item_t	hidBarrel_Switch[NBUTTONS];
+	hid_item_t	hidInvert;
+	int		reportSize;
+	int		reportId;
+	int 		nSwitch;
+	USBTDevicePtr	currentProxDev;
+} USBTCommon, *USBTCommonPtr;
+
+typedef struct {
+	int	x, y, pressure, buttons, xTilt, yTilt, proximity;
+} USBTState;
+
+struct USBTDevice {
+	USBTCommonPtr	comm;
+	USBTDevicePtr	next;
+	InputInfoPtr	info;
+	USBTState	state;
+	int		threshold;
+	int		thresCent;
+	int		suppress;
+	int		flags;
+};
+
+/* Function prototypes */
+#ifdef XFree86LOADER
+static MODULESETUPPROTO(SetupProc);
+static void TearDownProc(pointer);
+static const OptionInfoRec *UsbTabletAvailableOptions(void *);
+#endif
+
+static LocalDevicePtr UsbTabletAllocateStylus(InputDriverPtr);
+static LocalDevicePtr UsbTabletAllocateEraser(InputDriverPtr);
+static LocalDevicePtr UsbTabletAllocate(InputDriverPtr, char *, int);
+static InputInfoPtr UsbTabletPreInit(InputDriverPtr, IDevPtr, int);
+static int UsbTabletProc(DeviceIntPtr, int);
+static void UsbTabletReadInput(InputInfoPtr);
+static int UsbTabletChangeControl(InputInfoPtr, xDeviceCtl *);
+static int UsbTabletSwitchMode(ClientPtr, DeviceIntPtr, int);
+static void UsbTabletClose(InputInfoPtr);
+static Bool UsbTabletConvert(InputInfoPtr, int, int, int, int, int,
+			   int, int, int, int *, int *);
+static Bool UsbTabletReverseConvert(InputInfoPtr, int, int, int *);
+static void UsbTabletControlProc(DeviceIntPtr, PtrCtrl *);
+static int UsbTabletOpenDevice(DeviceIntPtr);
+static void UsbTabletSendEvents(InputInfoPtr, int, USBTState *);
+static void UsbTabletSendButtons(InputInfoPtr, int, int, int, int, int, int);
+static void UsbTabletOutOfProx(USBTDevicePtr prx);
+static void UsbTabletIntoProx(USBTDevicePtr prx, USBTState *ds);
+
+
+static XF86ModuleVersionInfo VersionRec = {
+	"usbtablet",
+	MODULEVENDORSTRING,
+	MODINFOSTRING1,
+	MODINFOSTRING2,
+	XF86_VERSION_CURRENT,
+	1, 0, 0,
+	ABI_CLASS_XINPUT,
+	ABI_XINPUT_VERSION,
+	MOD_CLASS_XINPUT,
+	{0, 0, 0, 0}		/* signature to be patched into the file */
+};
+
+typedef enum {
+	USBTOPT_DEVICE,
+	USBTOPT_DEBUG_LEVEL,
+	USBTOPT_HISTORY_SIZE,
+	USBTOPT_THRESHOLD,
+	USBTOPT_SUPPRESS,
+} USBTOpts;
+
+static const OptionInfoRec USBTOptions[] = {
+	{ USBTOPT_DEVICE, "device", OPTV_STRING, {0}, FALSE },
+	{ USBTOPT_DEBUG_LEVEL, "debuglevel", OPTV_INTEGER, {0}, FALSE},
+	{ USBTOPT_HISTORY_SIZE, "historysize", OPTV_INTEGER, {0}, FALSE},
+	{ USBTOPT_THRESHOLD, "threshold", OPTV_INTEGER, {0}, FALSE}, 
+	{ USBTOPT_SUPPRESS, "suppress", OPTV_INTEGER, {0}, FALSE},
+	{ -1, NULL, OPTV_NONE, {0}, FALSE}
+};
+
+
+#ifdef XFree86LOADER
+
+XF86ModuleData usbtabletModuleData = {&VersionRec,
+				      SetupProc, TearDownProc };
+
+ModuleInfoRec UsbTabletInfo = {
+	1,
+	"USBTABLET",
+	NULL,
+	0,
+	UsbTabletAvailableOptions,
+};
+
+#endif
+
+InputDriverRec USBTABLET = {
+	1,
+	"usbtablet",
+	NULL,
+	UsbTabletPreInit,
+	NULL,
+	0
+};
+
+/* 
+ * Debugging macro
+ */
+#ifdef DBG
+#undef DBG
+#endif
+#ifdef DEBUG
+#undef DEBUG
+#endif
+
+static int debug_level = 0;
+#define DEBUG 1
+#if DEBUG
+#define DBG(lvl, f) {if ((lvl) <= debug_level) f;}
+#else
+#define DBG(lvl, f)
+#endif
+
+
+/**
+ ** Function definitions
+ **/
+
+#ifdef XFree86LOADER
+static pointer
+SetupProc(pointer module,
+	  pointer options,
+	  int *errmaj,
+	  int *errmin)
+{
+	static Bool Initialised = FALSE;
+
+	if (!Initialised) {
+		Initialised = TRUE;
+#ifndef REMOVE_LOADER_CHECK_MODULE_INFO
+		if (xf86LoaderCheckSymbol("xf86AddModuleInfo")) 
+#endif
+			xf86AddModuleInfo(&UsbTabletInfo, module);
+		
+		xf86Msg(X_INFO, "USB Tablet driver\n");
+		xf86AddInputDriver(&USBTABLET, module, 0);
+	}
+	return module;
+}
+
+static void 
+TearDownProc(pointer p)
+{
+	DBG(1, ErrorF("USB Tablet TearDownProc Called\n"));
+}
+	
+#endif /* XFree86LOADER */
+
+static int
+UsbTabletProc(DeviceIntPtr pUSBT, int what)
+{
+	InputInfoPtr pInfo = (InputInfoPtr)pUSBT->public.devicePrivate;
+	USBTDevicePtr priv = (USBTDevicePtr)XI_PRIVATE(pUSBT);
+	CARD8 map[NBUTTONS+1];
+	int i;
+
+	switch (what) {
+	case DEVICE_INIT:
+		DBG(1, ErrorF("UsbTabletProc DEVICE_INIT\n"));
+		pUSBT->public.on = FALSE;
+		for (i = 1; i <= NBUTTONS; i++) {
+			map[i] = i;
+		}
+		if (InitButtonClassDeviceStruct(pUSBT,
+						NBUTTONS, 
+						map) == FALSE) {
+			xf86Msg(X_ERROR, 
+				"unable to allocate Button class device\n");
+			return !Success;
+		}
+		if (InitFocusClassDeviceStruct(pUSBT) == FALSE) {
+			xf86Msg(X_ERROR, 
+				"unable to init Focus class device\n");
+			return !Success;
+		}
+          
+		if (InitPtrFeedbackClassDeviceStruct(pUSBT,
+					UsbTabletControlProc) == FALSE) {
+			xf86Msg(X_ERROR, "unable to init ptr feedback\n");
+			return !Success;
+		}
+	    
+		if (InitProximityClassDeviceStruct(pUSBT) == FALSE) {
+			xf86Msg(X_ERROR, 
+				"unable to init proximity class device\n");
+			return !Success;
+		}
+
+		if (InitValuatorClassDeviceStruct(
+			pUSBT, NAXES, xf86GetMotionEvents, 
+			pInfo->history_size,
+			((priv->flags & ABSOLUTE_FLAG) ? Absolute : Relative) |
+			 OutOfProximity) == FALSE) {
+			xf86Msg(X_ERROR,
+				"unable to allocate Valuator class device\n"); 
+			return !Success;
+		} else {
+			/* allocate the motion history buffer if needed */
+			xf86MotionHistoryAllocate(pInfo);
+
+			AssignTypeAndName(pUSBT, pInfo->atom, pInfo->name);
+		}
+
+		/* open the device to gather informations */
+		UsbTabletOpenDevice(pUSBT);
+		break;
+
+	case DEVICE_ON:
+		DBG(1, ErrorF("UsbTabletProc DEVICE_ON\n"));
+		if ((pInfo->fd < 0) && (!UsbTabletOpenDevice(pUSBT))) {
+			return !Success;
+		}
+		xf86AddEnabledDevice(pInfo);
+		pUSBT->public.on = TRUE;
+		
+		break;
+	case DEVICE_OFF:
+		DBG(1, ErrorF("UsbTabletProc DEVICE_OFF\n"));
+		if (pInfo->fd >= 0) {
+			xf86RemoveEnabledDevice(pInfo);
+			UsbTabletClose(pInfo);
+		}
+		pUSBT->public.on = FALSE;
+		break;
+
+	case DEVICE_CLOSE:
+		DBG(1, ErrorF("UsbTabletProc DEVICE_CLOSE\n"));
+		UsbTabletClose(pInfo);
+		break;
+		
+	default:
+		xf86Msg(X_ERROR, "UsbTabletProc: unsupported mode %d\n",
+			what);
+		return !Success;
+	} /* switch */
+	return Success;
+}
+
+static void
+UsbTabletReadInput(InputInfoPtr pInfo)
+{
+	USBTDevicePtr	priv = (USBTDevicePtr)pInfo->private;
+	USBTCommonPtr	comm = priv->comm;
+	int		invert, len, i;
+	unsigned char	buffer[200], *p;
+	USBTState	ds;
+  
+	DBG(7, ErrorF("UsbTabletReadInput BEGIN device=%s fd=%d\n",
+		      comm->devName, pInfo->fd));
+
+	for(;;) {
+		p = buffer;
+		DBG(10, ErrorF("UsbTabletReadInput reading fd=%d len=%d\n",
+			       pInfo->fd, comm->reportSize));
+
+		len = xf86ReadSerial(pInfo->fd, p, comm->reportSize);
+#ifdef XFree86LOADER
+		/* XXX there's a bug in XFree86 4.1 that requires this magic */
+		/* It's fixed in 4.2 */
+		errno = xf86GetErrno();
+#endif
+		DBG(8, ErrorF("UsbTabletReadInput len=%d\n", len));
+	    
+		if (len <= 0) {
+			if (errno != EAGAIN) {
+				Error("error reading USBT device");
+			}
+			break;
+		}
+	    
+#ifndef USB_NEW_HID
+		if (comm->reportId)
+			p++;
+#endif
+		ds.x = hid_get_data(p, &comm->hidX);
+		ds.y = hid_get_data(p, &comm->hidY);
+		ds.buttons = 0;
+		for (i = 0; i < comm->nSwitch; i++) {
+			if (hid_get_data(p, &comm->hidBarrel_Switch[i])) {
+				ds.buttons |= (1 << (i+1));
+			}
+		}
+		invert = hid_get_data(p, &comm->hidInvert);
+		ds.pressure = hid_get_data(p, &comm->hidTip_Pressure);
+		if (ds.pressure > priv->threshold)
+			ds.buttons |= 1;
+		ds.proximity = hid_get_data(p, &comm->hidIn_Range);
+		ds.xTilt = hid_get_data(p, &comm->hidTiltX);
+		ds.yTilt = hid_get_data(p, &comm->hidTiltY);
+
+		if (!ds.proximity)
+			UsbTabletOutOfProx(comm->currentProxDev);
+
+		for (i = 0; i < comm->nDevs; i++) {
+			DBG(7, ErrorF("UsbTabletReadInput sending to %s\n",
+				      comm->devices[i]->name));
+
+			UsbTabletSendEvents(comm->devices[i], invert, &ds);
+		}
+	}
+	DBG(7, ErrorF("UsbTabletReadInput END   pInfo=0x%x priv=0x%x\n",
+		      pInfo, priv));
+}
+
+static void
+UsbTabletOutOfProx(USBTDevicePtr prx)
+{
+	USBTState *ods;
+
+	if (!prx)
+		return;
+
+	DBG(1, ErrorF("Out of proximity %s\n", prx->info->name));
+
+	ods = &prx->state;
+	prx->comm->currentProxDev = 0;
+
+	if (prx->state.buttons) {
+		/* Report buttons up when the device goes out of proximity. */
+		DBG(9, ErrorF("xf86USBTOutOfProx: reset buttons\n"));
+		UsbTabletSendButtons(prx->info, 0, 
+				   ods->x, ods->y, ods->pressure, 
+				   ods->xTilt, ods->yTilt);
+		prx->state.buttons = 0;
+	}
+	if (!xf86IsCorePointer(prx->info->dev)) {
+		DBG(1, ErrorF("xf86USBTSendEvents: out proximity\n"));
+		xf86PostProximityEvent(prx->info->dev, 0, 0, 5, 
+				       ods->x, ods->y, ods->pressure,
+				       ods->xTilt, ods->yTilt);
+	}
+}
+
+static void
+UsbTabletIntoProx(USBTDevicePtr prx, USBTState *ds)
+{
+	if (prx->comm->currentProxDev == prx)
+		return;
+	UsbTabletOutOfProx(prx->comm->currentProxDev);
+	prx->comm->currentProxDev = prx;
+
+	DBG(1, ErrorF("Into proximity %s\n", prx->info->name));
+
+	if (!xf86IsCorePointer(prx->info->dev)) {
+		DBG(1, ErrorF("xf86USBTSendEvents: in proximity\n"));
+		xf86PostProximityEvent(prx->info->dev, 1, 0, 5, 
+				       ds->x, ds->y, ds->pressure,
+				       ds->xTilt, ds->yTilt);
+	}
+	
+}
+
+static void
+UsbTabletSendButtons(InputInfoPtr pInfo, int buttons, 
+		     int rx, int ry, int rz,
+		     int rtx, int rty)
+{
+	USBTDevicePtr  priv = (USBTDevicePtr) pInfo->private;
+	int           button, mask;
+
+	for (button = 1; button < NBUTTONS; button++) {
+		mask = 1 << (button-1);
+	
+		if ((mask & priv->state.buttons) != (mask & buttons)) {
+			DBG(4, ErrorF("UsbTabletSendButtons button=%d is %d\n", 
+				      button, (buttons & mask) != 0));
+			xf86PostButtonEvent(pInfo->dev,
+					    (priv->flags & ABSOLUTE_FLAG),
+					    button, (buttons & mask) != 0,
+					    0, 5, rx, ry, rz, rtx, rty);
+		}
+	}
+}
+
+static void
+UsbTabletSendEvents(InputInfoPtr pInfo, int invert, USBTState *ds)
+{
+	USBTDevicePtr	priv = (USBTDevicePtr)pInfo->private;
+	USBTState	*ods = &priv->state;
+	int		is_abs;
+	int		rx, ry, rz, rtx, rty;
+
+	DBG(9, ErrorF("UsbTabletSendEvents %s x=%d y=%d pressure=%d buttons=%x "
+		      "xTilt=%d yTilt=%d proximity=%d, invert=%d, eraser=%d\n",
+		      pInfo->name,
+		      ds->x, ds->y, ds->pressure, ds->buttons,
+		      ds->xTilt, ds->yTilt, ds->proximity,
+		      invert, (priv->flags & ERASER_ID) != 0));
+
+	if (!ds->proximity)
+		return;
+	if (((priv->flags & ERASER_ID) != 0) != invert)
+		return;
+
+	UsbTabletIntoProx(priv, ds);
+
+	if (ds->buttons == ods->buttons && ds->proximity == ods->proximity &&
+	    ABS(ds->x - ods->x) < priv->suppress &&
+	    ABS(ds->y - ods->y) < priv->suppress &&
+	    ds->pressure == ods->pressure &&
+	    ds->xTilt == ods->xTilt &&
+	    ds->yTilt == ods->yTilt) {
+		DBG(9, ErrorF("UsbTabletSendEvents no change\n"));
+		return;
+	}
+	is_abs = 1;
+	rx = ds->x; ry = ds->y; rz = ds->pressure; 
+	rtx = ds->xTilt; rty = ds->yTilt;
+
+	if (rx != ods->x || ry != ods->y || rz != ods->pressure ||
+	    rtx != ods->xTilt || rty != ods->yTilt) {
+		DBG(9, ErrorF("UsbTabletSendEvents: motion\n"));
+		xf86PostMotionEvent(pInfo->dev, is_abs, 0, 5, 
+				    rx, ry, rz, rtx, rty); 
+		
+	}
+	if (ds->buttons != ods->buttons)
+		UsbTabletSendButtons(pInfo, ds->buttons, 
+				   rx, ry, rz, rtx, rty);
+
+	*ods = *ds;
+}
+
+static int
+UsbTabletChangeControl(InputInfoPtr pInfo, xDeviceCtl *control)
+{
+	return BadMatch;
+}
+
+static int
+UsbTabletSwitchMode(ClientPtr client, DeviceIntPtr dev, int mode)
+{
+	return BadMatch;
+}
+
+static void
+UsbTabletClose(InputInfoPtr pInfo)
+{
+	USBTDevicePtr	priv = (USBTDevicePtr)pInfo->private;
+	USBTCommonPtr	comm = priv->comm;
+	int		num, i;
+    
+	for (num = 0, i = 0; i < comm->nDevs;  i++)
+		if (comm->devices[i]->fd >= 0)
+			num++;
+	DBG(4, ErrorF("USB tablet number of open devices = %d\n", num));
+	
+	if (num == 1) {		    
+		SYSCALL(close(pInfo->fd));
+	}
+	
+	pInfo->fd = -1;
+}
+
+static Bool
+UsbTabletConvert(InputInfoPtr pInfo, int first, int num, 
+		 int v0, int v1, int v2, int v3, int v4, int v5,
+		 int *x, int *y)
+{
+	USBTCommonPtr comm = ((USBTDevicePtr)pInfo->private)->comm;
+
+	DBG(6, ErrorF("UsbTabletConvert\n"));
+
+	if (first != 0 || num == 1)
+		return FALSE;
+
+	*x = v0 * comm->factorX;
+	*y = v1 * comm->factorY;
+
+	DBG(6, ErrorF("USB tablet converted v0=%d v1=%d to x=%d y=%d\n",
+		      v0, v1, *x, *y));
+
+	return TRUE;
+}
+
+static Bool
+UsbTabletReverseConvert(InputInfoPtr pInfo, int x, int y,
+			int *valuators)
+{
+	USBTCommonPtr comm = ((USBTDevicePtr)pInfo->private)->comm;
+
+	valuators[0] = x / comm->factorX;
+	valuators[1] = y / comm->factorY;
+
+	DBG(6, ErrorF("USB tablet converted x=%d y=%d to v0=%d v1=%d\n", x, y,
+		      valuators[0], valuators[1]));
+
+	return TRUE;
+}
+
+static void
+UsbTabletControlProc(DeviceIntPtr device, PtrCtrl *ctrl)
+{
+	DBG(2, ErrorF("UsbTabletControlProc\n"));
+}
+
+static Bool
+UsbTabletOpen(InputInfoPtr pInfo)
+{
+	USBTDevicePtr	priv = (USBTDevicePtr)pInfo->private;
+	USBTCommonPtr	comm = priv->comm;
+	hid_data_t     d;
+	hid_item_t     h;
+	report_desc_t rd;
+	int nSwitch = 0;
+#ifdef USB_NEW_HID
+	int            r;
+#endif
+	int i;
+
+	DBG(1, ErrorF("opening %s\n", comm->devName));
+
+	/* Use this since O_NDELAY is not implemented by libc open wrapper */
+	pInfo->fd = xf86OpenSerial(pInfo->options);
+	if (pInfo->fd == -1) {
+		xf86Msg(X_ERROR, "Error opening %s: %s\n",
+			comm->devName, strerror(errno));
+		return !Success;
+	}
+#ifdef USB_NEW_HID
+	SYSCALL(r = ioctl(pInfo->fd, USB_GET_REPORT_ID, &comm->reportId));
+	if (r == -1) {
+		ErrorF("Error ioctl USB_GET_REPORT_ID on %s : %s\n", 
+		       comm->devName, strerror(errno));
+		return !Success;
+	}
+#endif
+
+	DBG(1, ErrorF("initializing tablet\n"));
+    
+	rd = hid_get_report_desc(pInfo->fd);
+	if (rd == 0) {
+		Error(comm->devName);
+		SYSCALL(close(pInfo->fd));
+		return !Success;
+	}
+    
+	memset(&comm->hidX, 0, sizeof (hid_item_t));
+	memset(&comm->hidY, 0, sizeof (hid_item_t));
+	memset(&comm->hidTiltX, 0, sizeof (hid_item_t));
+	memset(&comm->hidTiltY, 0, sizeof (hid_item_t));
+	memset(&comm->hidIn_Range, 0, sizeof (hid_item_t));
+	memset(&comm->hidInvert, 0, sizeof (hid_item_t));
+	memset(&comm->hidTip_Pressure, 0, sizeof (hid_item_t));
+	for (i = 0; i < NBUTTONS; i++) {
+		memset(&comm->hidBarrel_Switch[i], 0, sizeof (hid_item_t));
+	}
+	for (d = hid_start_parse(rd, 1<<hid_input
+#ifdef USB_NEW_HID
+				 , comm->reportId
+#endif
+		); hid_get_item(d, &h); ) {
+		if (h.kind != hid_input || (h.flags & HIO_CONST))
+			continue;
+		if (h.usage == HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X))
+			comm->hidX = h;
+		if (h.usage == HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y))
+			comm->hidY = h;
+		if (h.usage == HID_USAGE2(HUP_GENERIC_DESKTOP, HUD_X_TILT))
+			comm->hidTiltX = h;
+		if (h.usage == HID_USAGE2(HUP_GENERIC_DESKTOP, HUD_Y_TILT))
+			comm->hidTiltY = h;
+		if (h.usage == HID_USAGE2(HUP_DIGITIZERS, HUD_INVERT))
+			comm->hidInvert = h;
+		if (h.usage == HID_USAGE2(HUP_DIGITIZERS, HUD_IN_RANGE))
+			comm->hidIn_Range = h;
+		if (h.usage == HID_USAGE2(HUP_DIGITIZERS, HUD_TIP_PRESSURE))
+			comm->hidTip_Pressure = h;
+		if (h.usage == HID_USAGE2(HUP_DIGITIZERS, HUD_BARREL_SWITCH))
+			comm->hidBarrel_Switch[nSwitch++] = h;
+	}
+	hid_end_parse(d);
+	comm->nSwitch = nSwitch;
+#ifdef USB_NEW_HID
+	comm->reportSize = hid_report_size(rd, hid_input, comm->reportId);
+#else
+	comm->reportSize = hid_report_size(rd, hid_input, &comm->reportId);
+#endif
+	hid_dispose_report_desc(rd);
+	if (comm->hidX.report_size == 0 ||
+	    comm->hidY.report_size == 0 ||
+	    comm->hidIn_Range.report_size == 0) {
+		xf86Msg(X_ERROR, "%s has no X, Y, or In_Range report\n", 
+			comm->devName);
+		return !Success;
+	}
+	DBG(2, ErrorF("Found X at %d, size=%d\n", 
+		      comm->hidX.pos, comm->hidX.report_size));
+	DBG(2, ErrorF("Found Y at %d, size=%d\n", 
+		      comm->hidY.pos, comm->hidY.report_size));
+	DBG(2, ErrorF("Found Invert at %d, size=%d\n", 
+		      comm->hidInvert.pos, comm->hidInvert.report_size));
+	DBG(2, ErrorF("Found In_Range at %d, size=%d\n", 
+		      comm->hidIn_Range.pos, comm->hidIn_Range.report_size));
+	DBG(2, ErrorF("Found Tip_Pressure at %d, size=%d\n", 
+		      comm->hidTip_Pressure.pos, 
+		      comm->hidTip_Pressure.report_size));
+	for (i = 0; i < comm->nSwitch; i++) {
+		DBG(2, ErrorF("Found Barrel_Switch at %d, size=%d\n", 
+			      comm->hidBarrel_Switch[i].pos, 
+			      comm->hidBarrel_Switch[i].report_size));
+	}
+	DBG(2, ErrorF("Report size=%d, report id=%d\n",
+		      comm->reportSize, comm->reportId));
+
+	comm->factorX = ((double) screenInfo.screens[0]->width)
+		/ (comm->hidX.logical_maximum - comm->hidX.logical_minimum);
+	comm->factorY = ((double) screenInfo.screens[0]->height)
+		/ (comm->hidY.logical_maximum - comm->hidY.logical_minimum);
+    
+	xf86Msg(X_PROBED, "USBT tablet X=%d..%d, Y=%d..%d",
+		    comm->hidX.logical_minimum, 
+		    comm->hidX.logical_maximum, 
+		    comm->hidY.logical_minimum, 
+		    comm->hidY.logical_maximum);
+	if (comm->hidTip_Pressure.report_size != 0)
+		xf86Msg(X_NONE, ", pressure=%d..%d",
+			    comm->hidTip_Pressure.logical_minimum, 
+			    comm->hidTip_Pressure.logical_maximum);
+	xf86Msg(X_NONE, "\n");
+  
+	return Success;
+}
+
+static int
+UsbTabletOpenDevice(DeviceIntPtr pUSBT)
+{
+	InputInfoPtr pInfo = (InputInfoPtr)pUSBT->public.devicePrivate;
+	USBTDevicePtr priv = (USBTDevicePtr)XI_PRIVATE(pUSBT);
+	USBTCommonPtr comm = priv->comm;
+	int i;
+
+	hid_item_t	*h = &comm->hidTip_Pressure;
+    
+	DBG(1, ErrorF("UsbTabletOpenDevice start\n"));
+	if (pInfo->fd < 0) {
+		DBG(2, ErrorF("UsbTabletOpenDevice really open\n"));
+		if (UsbTabletOpen(pInfo) != Success) {
+			if (pInfo->fd >= 0) {
+				SYSCALL(close(pInfo->fd));
+			}
+			pInfo->fd = -1;
+			return 0;
+		}
+		/* report the file descriptor to all devices */
+		for (i = 0; i < comm->nDevs; i++) {
+			comm->devices[i]->fd = pInfo->fd;
+		}
+	}
+
+	priv->threshold = 
+	    h->logical_minimum + 
+	    (h->logical_maximum - h->logical_minimum) * priv->thresCent / 100;
+	if (h->report_size != 0)
+		xf86Msg(X_PROBED, 
+			"USBT %s pressure threshold=%d, suppress=%d\n",
+			pInfo->name, priv->threshold, priv->suppress);
+
+	
+	/* Set the real values */
+	InitValuatorAxisStruct(pUSBT,
+			       0,
+			       comm->hidX.logical_minimum, /* min val */
+			       comm->hidX.logical_maximum, /* max val */
+			       mils(1000), /* resolution */
+			       0, /* min_res */
+			       mils(1000)); /* max_res */
+	InitValuatorAxisStruct(pUSBT,
+			       1,
+			       comm->hidY.logical_minimum, /* min val */
+			       comm->hidY.logical_maximum, /* max val */
+			       mils(1000), /* resolution */
+			       0, /* min_res */
+			       mils(1000)); /* max_res */
+	InitValuatorAxisStruct(pUSBT,
+			       2,
+			       h->logical_minimum, /* min val */
+			       h->logical_maximum, /* max val */
+			       mils(1000), /* resolution */
+			       0, /* min_res */
+			       mils(1000)); /* max_res */
+	InitValuatorAxisStruct(pUSBT,
+			       3,
+			       comm->hidTiltX.logical_minimum, /* min val */
+			       comm->hidTiltX.logical_maximum, /* max val */
+			       mils(1000), /* resolution */
+			       0, /* min_res */
+			       mils(1000)); /* max_res */
+	InitValuatorAxisStruct(pUSBT,
+			       4,
+			       comm->hidTiltY.logical_minimum, /* min val */
+			       comm->hidTiltY.logical_maximum, /* max val */
+			       mils(1000), /* resolution */
+			       0, /* min_res */
+			       mils(1000)); /* max_res */
+	return 1;
+}
+
+#ifdef XFree86LOADER
+static const OptionInfoRec *
+UsbTabletAvailableOptions(void *unused)
+{
+	return USBTOptions;
+}
+#endif
+static InputInfoPtr
+UsbTabletAllocate(InputDriverPtr drv, char *name, int flag)
+{
+	InputInfoPtr pInfo = xf86AllocateInput(drv, 0);
+	USBTDevicePtr priv;
+	USBTCommonPtr comm;
+	
+	if (pInfo == NULL) {
+		return NULL;
+	}
+	
+	priv = (USBTDevicePtr)xalloc(sizeof(USBTDevice));
+	if (priv == NULL) {
+		return NULL;
+	}
+	
+	comm = (USBTCommonPtr)xalloc(sizeof(USBTCommon));
+	if (comm == NULL) {
+		xfree(priv);
+		return NULL;
+	}
+	memset(priv, 0, sizeof *priv);
+	memset(comm, 0, sizeof *comm);
+	
+	pInfo->name = name;
+	pInfo->device_control = UsbTabletProc;
+	pInfo->read_input = UsbTabletReadInput;
+	pInfo->control_proc = UsbTabletChangeControl;
+	pInfo->switch_mode = UsbTabletSwitchMode;
+	pInfo->conversion_proc = UsbTabletConvert;
+	pInfo->reverse_conversion_proc = UsbTabletReverseConvert;
+	pInfo->fd = -1;
+	pInfo->private = priv;
+	pInfo->old_x = -1;
+	pInfo->old_y = -1;
+	
+	priv->info = pInfo;
+	priv->comm = comm;
+	priv->flags = ABSOLUTE_FLAG | flag;
+	priv->suppress = 2;
+	priv->thresCent = 5;
+	
+	comm->nDevs = 1;
+	comm->devices = (InputInfoPtr*)xalloc(sizeof(InputInfoPtr));
+	comm->devices[0] = pInfo;
+	
+	return pInfo;
+}
+
+static InputInfoPtr
+UsbTabletAllocateStylus(InputDriverPtr drv)
+{
+	InputInfoPtr pInfo = UsbTabletAllocate(drv, STYLUS_XI, STYLUS_ID);
+	
+	if (pInfo == NULL) {
+		return NULL;
+	}
+	pInfo->type_name = "USBT Stylus";
+	return pInfo;
+}
+
+static InputInfoPtr
+UsbTabletAllocateEraser(InputDriverPtr drv)
+{
+	InputInfoPtr pInfo = UsbTabletAllocate(drv, ERASER_XI, ERASER_ID);
+	
+	if (pInfo == NULL) {
+		return NULL;
+	}
+	pInfo->type_name = "USBT Eraser";
+	return pInfo;
+}
+
+/* 
+ * Called when the InputDevice Section is found in XF86Config 
+ */
+static InputInfoPtr
+UsbTabletPreInit(InputDriverPtr drv, IDevPtr dev, int flags)
+{
+	InputInfoPtr pInfo = NULL;
+	InputInfoPtr fake = NULL;
+	USBTCommonPtr comm = NULL, c;
+	USBTDevicePtr priv = NULL, p;
+	InputInfoPtr localDevices;
+	char *s;
+	int i;
+
+	fake = (InputInfoPtr)xcalloc(1, sizeof(InputInfoRec));
+	if (fake == NULL) {
+		return NULL;
+	}
+	fake->conf_idev = dev;
+	xf86CollectInputOptions(fake, NULL, NULL);
+	s = xf86FindOptionValue(fake->options, "Type");
+	
+	if (s != NULL) {
+		if (xf86NameCmp(s, "stylus") == 0) {
+			pInfo = UsbTabletAllocateStylus(drv);
+		} else if (xf86NameCmp(s, "eraser") == 0) {
+			pInfo = UsbTabletAllocateEraser(drv);
+		} else {
+			xf86Msg(X_ERROR, "%s: Invalid type specified.\n",
+				"Must be one of stylus or eraser.\n",
+				dev->identifier);
+			goto PreInit_fail;
+		}
+	} else {
+		xf86Msg(X_ERROR, "%s: No type specified.\n", dev->identifier);
+		goto PreInit_fail;
+	}
+
+	if (pInfo == NULL) {
+		xfree(fake);
+		return NULL;
+	}
+
+
+	pInfo->options = fake->options;
+	pInfo->conf_idev = fake->conf_idev;
+	pInfo->name = dev->identifier;
+	xfree(fake);
+
+	priv = (USBTDevicePtr)pInfo->private;
+	comm = priv->comm;
+
+	comm->devName = xf86FindOptionValue(pInfo->options, "Device");
+	if (comm->devName == NULL) {
+		xf86Msg(X_ERROR, "%s: No Device specified.\n", 
+			dev->identifier);
+		goto PreInit_fail;
+	}
+
+	/* Lookup to see if there is another device sharing 
+	  * the same device
+	  */
+	localDevices = xf86FirstLocalDevice();
+
+	while (localDevices) {
+		p = (USBTDevicePtr)localDevices->private;
+		c = p->comm;
+
+		if ((pInfo != localDevices) && 
+		    (localDevices->device_control == UsbTabletProc) &&
+		    (strcmp(c->devName, comm->devName) == 0)) {
+			DBG(2, ErrorF("UsbTabletPreInit port share between"
+				      " %s and %s\n",
+				      pInfo->name, localDevices->name));
+			xfree(comm->devices);
+			xfree(comm);
+			comm = priv->comm = c;
+			comm->nDevs++;
+			comm->devices = (InputInfoPtr *)
+				xrealloc(comm->devices, 
+					 sizeof(InputInfoPtr)* comm->nDevs);
+			comm->devices[comm->nDevs - 1] = pInfo;
+			break;
+		}
+		localDevices = localDevices->next;
+	} /* while */
+
+	xf86ProcessCommonOptions(pInfo, pInfo->options);
+
+	xf86Msg(X_CONFIG, "%s device is %s\n", dev->identifier, comm->devName);
+
+	/* XXX Handle options */
+	debug_level = xf86SetIntOption(pInfo->options, "DebugLevel", 
+				       debug_level);
+	if (debug_level > 0) {
+		xf86Msg(X_CONFIG, "UsbTablet: debug level set to %d\n",
+			debug_level);
+	}
+
+	s = xf86FindOptionValue(pInfo->options, "Mode");
+	if (s != NULL) {
+		if (xf86NameCmp(s, "absolute") == 0) {
+			priv->flags |= ABSOLUTE_FLAG;
+		} else if (xf86NameCmp(s, "relative") == 0) {
+			priv->flags &= ~ABSOLUTE_FLAG;
+		} else {
+			xf86Msg(X_ERROR, "%s: invalid Mode "
+				"(should be absolute or relative). "
+				"Using default.\n", dev->identifier);
+		}
+	}
+	xf86Msg(X_CONFIG, "%s is in %s mode\n", pInfo->name, 
+		(priv->flags & ABSOLUTE_FLAG) ? "absolute" : "relative");
+
+	
+#if 0
+	pInfo->history_size = xf86SetIntOption(pInfo->options, "HistorySize",
+					pInfo->history_size);
+#endif
+	i = xf86SetIntOption(pInfo->options, "ThreshHold", -1);
+	if (i != -1) {
+		priv->thresCent = i;
+	}		
+	xf86Msg(i != -1 ? X_CONFIG : X_DEFAULT, "%s: threshold = %d\n",
+		dev->identifier, priv->thresCent);
+	
+
+	i = xf86SetIntOption(pInfo->options, "Suppress", -1);
+	if (i != -1) {
+		priv->suppress = i;
+	}		
+	xf86Msg(i != -1 ? X_CONFIG : X_DEFAULT, "%s: suppress = %d\n",
+		dev->identifier, priv->suppress);
+
+
+	pInfo->flags |= XI86_POINTER_CAPABLE | XI86_CONFIGURED;
+	
+	return pInfo;
+
+PreInit_fail:
+	if (comm) {
+		xfree(comm);
+	}
+	if (priv) {
+		xfree(priv);
+	}
+	if (pInfo) {
+		xfree(pInfo);
+	}
+	return NULL;
+}
diff --unidirectional -ur /mnt/xsrc/xfree/xc/programs/Xserver/hw/xfree86/loader/Imakefile xsrc/xfree/xc/programs/Xserver/hw/xfree86/loader/Imakefile
--- /mnt/xsrc/xfree/xc/programs/Xserver/hw/xfree86/loader/Imakefile	2003-12-29 23:44:04.000000000 -0600
+++ xsrc/xfree/xc/programs/Xserver/hw/xfree86/loader/Imakefile	2004-01-01 05:20:05.000000000 -0600
@@ -22,6 +22,10 @@
 DLOBJ=dlloader.o
 #endif
 
+#if HasLibUsbHid
+USBDEFINES = -DUSB_HID
+#endif
+
 #if FontencCompatibility
  COMPAT_DEFINES = -DFONTENC_COMPATIBILITY
 #endif
@@ -35,7 +39,7 @@
 #endif
 
 DEFINES = $(DBMALLOCDEFINE) $(DLOPENDEFINES) $(OS_DEFINES) $(COMPAT_DEFINES) \
-	  $(SHM_DEFINES) $(ARCHDEFINES)
+	  $(SHM_DEFINES) $(ARCHDEFINES) $(USBDEFINES)
 
 MODULEDEFINES = -DDEFAULT_MODULE_PATH=\"$(MODULEDIR)\"
 
diff --unidirectional -ur /mnt/xsrc/xfree/xc/programs/Xserver/hw/xfree86/loader/xf86sym.c xsrc/xfree/xc/programs/Xserver/hw/xfree86/loader/xf86sym.c
--- /mnt/xsrc/xfree/xc/programs/Xserver/hw/xfree86/loader/xf86sym.c	2003-12-29 23:44:04.000000000 -0600
+++ xsrc/xfree/xc/programs/Xserver/hw/xfree86/loader/xf86sym.c	2004-01-01 05:03:55.000000000 -0600
@@ -76,6 +76,10 @@
 int sysctlbyname(const char*, void *, size_t *, void *, size_t);
 #endif
 
+#ifdef USB_HID
+#include <usbhid.h>
+#endif
+
 #if defined(__alpha__)
 # ifdef linux
 extern unsigned long _bus_base(void);
@@ -1075,6 +1079,24 @@
    SYMFUNC(sysctlbyname)
 #endif
 
+#ifdef USB_HID
+   SYMFUNC(hid_get_report_desc)
+   SYMFUNC(hid_use_report_desc)
+   SYMFUNC(hid_dispose_report_desc)
+   SYMFUNC(hid_start_parse)
+   SYMFUNC(hid_end_parse)
+   SYMFUNC(hid_get_item)
+   SYMFUNC(hid_report_size)
+   SYMFUNC(hid_locate)
+   SYMFUNC(hid_usage_page)
+   SYMFUNC(hid_usage_in_page)
+   SYMFUNC(hid_parse_usage_page)
+   SYMFUNC(hid_parse_usage_in_page)
+   SYMFUNC(hid_init)
+   SYMFUNC(hid_get_data)
+   SYMFUNC(hid_set_data)
+#endif
+
 /*
  * and now some variables
  */

>Release-Note:
>Audit-Trail:
>Unformatted: