Subject: touch-panel driver and manpage for PERSONA SH3 mahicne
To: None <uwe@ptc.spbu.ru, port-hpcsh@netbsd.org>
From: KIYOHARA Takashi <kiyohara@kk.iij4u.or.jp>
List: port-hpcsh
Date: 05/18/2005 14:38:54
----Next_Part(Wed_May_18_14:38:54_2005_639)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Hi!


I wrote touch-panel driver and manpage for HITACHI PERSONA SH3 machine.
I'll be commit next week. (Mon or Tue)

--
kiyohara


----Next_Part(Wed_May_18_14:38:54_2005_639)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="psh3tp.diff"

Index: share/man/man4/man4.hpcsh/Makefile
===================================================================
RCS file: /cvsroot/src/share/man/man4/man4.hpcsh/Makefile,v
retrieving revision 1.2
diff -u -r1.2 Makefile
--- share/man/man4/man4.hpcsh/Makefile	4 Apr 2004 17:34:43 -0000	1.2
+++ share/man/man4/man4.hpcsh/Makefile	16 May 2005 03:36:30 -0000
@@ -1,6 +1,6 @@
 #	$NetBSD: Makefile,v 1.2 2004/04/04 17:34:43 uwe Exp $
 
-MAN=	intro.4 j6x0lcd.4 j6x0tp.4
+MAN=	intro.4 j6x0lcd.4 j6x0tp.4 psh3tp.4
 
 MANSUBDIR=/hpcsh
 
Index: share/man/man4/man4.hpcsh/intro.4
===================================================================
RCS file: /cvsroot/src/share/man/man4/man4.hpcsh/intro.4,v
retrieving revision 1.1
diff -u -r1.1 intro.4
--- share/man/man4/man4.hpcsh/intro.4	4 Apr 2004 17:03:39 -0000	1.1
+++ share/man/man4/man4.hpcsh/intro.4	16 May 2005 03:36:30 -0000
@@ -33,7 +33,7 @@
 .\"
 .\" <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
 .\"
-.Dd April 4, 2004
+.Dd May 14, 2005
 .Dt INTRO 4 hpcsh
 .Os
 .Sh NAME
@@ -179,6 +179,8 @@
 LCD screen of HP Jornada 680 series machines
 .It j6x0tp
 touch screen of HP Jornada 680 series machines
+.It psh3tp
+touch screen of HITACHI PERSONA SH3 series machines
 .El
 .\"
 .Sh SEE ALSO
@@ -186,6 +188,7 @@
 .Xr adc 4 ,
 .Xr j6x0lcd 4 ,
 .Xr j6x0tp 4 ,
+.Xr psh3tp 4 ,
 .Xr shb 4
 .\"
 .Sh BUGS
Index: sys/arch/hpcsh/conf/files.hpcsh
===================================================================
RCS file: /cvsroot/src/sys/arch/hpcsh/conf/files.hpcsh,v
retrieving revision 1.39
diff -u -r1.39 files.hpcsh
--- sys/arch/hpcsh/conf/files.hpcsh	6 Jul 2004 13:09:19 -0000	1.39
+++ sys/arch/hpcsh/conf/files.hpcsh	16 May 2005 03:36:45 -0000
@@ -74,6 +74,10 @@
 attach	j6x0lcd at shb
 file	arch/hpcsh/dev/j6x0lcd.c		j6x0lcd
 
+device	h50lcd
+attach	h50lcd at shb
+file	arch/hpcsh/dev/h50lcd.c			h50lcd
+
 device	j6x0pwr
 attach	j6x0pwr at adc
 file	arch/hpcsh/dev/j6x0pwr.c		j6x0pwr
@@ -91,6 +95,11 @@
 				J6X0TP_PGDN_ICON_KEYSYM
 				J6X0TP_SWITCH_ICON_KEYSYM
 
+device	psh3tp: hpctpanel, wsmousedev
+attach	psh3tp at adc
+file	arch/hpcsh/dev/psh3tp.c			psh3tp
+defflag	opt_psh3tp.h		PSH3TP_DEBUG
+
 device	pfckbd: hpckbdif
 attach	pfckbd at mainbus
 file	arch/hpcsh/dev/pfckbd.c			pfckbd	needs-flag
Index: sys/dev/DEVNAMES
===================================================================
RCS file: /cvsroot/src/sys/dev/DEVNAMES,v
retrieving revision 1.177
diff -u -r1.177 DEVNAMES
--- sys/dev/DEVNAMES	3 Apr 2005 11:40:09 -0000	1.177
+++ sys/dev/DEVNAMES	16 May 2005 03:37:06 -0000
@@ -965,6 +964,7 @@
 ppi			hp300
 pps			MI
 ps			vax
+psh3tp			hpcsh
 pss			MI
 psycho			sparc64
 ptsc			acorn32

----Next_Part(Wed_May_18_14:38:54_2005_639)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="psh3tp.c"

/*	$NetBSD$	*/
/*
 * Copyright (c) 2005 KIYOHARA Takashi
 * 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 ``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 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.
 * 
 */

#include <sys/cdefs.h>

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/callout.h>

#include "opt_psh3tp.h"

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsmousevar.h>
#include <dev/hpc/hpctpanelvar.h>

#include <machine/platid.h>
#include <machine/platid_mask.h>

#include <machine/intr.h>

#include <sh3/exception.h>
#include <sh3/intcreg.h>
#include <sh3/pfcreg.h>
#include <sh3/adcreg.h>

#include <sh3/dev/adcvar.h>


#ifdef PSH3TP_DEBUG
volatile int psh3tp_debug = 4;
#define DPRINTF_PRINTF		printf_nolog
#define DPRINTF(arg)		if (psh3tp_debug) DPRINTF_PRINTF arg
#define DPRINTFN(n, arg)	if (psh3tp_debug > (n)) DPRINTF_PRINTF arg
#else
#define DPRINTF(arg)		((void)0)
#define DPRINTFN(n, arg)	((void)0)
#endif


/*
 * PFC bits pertinent to PERSONA HPW-50PA touch-panel
 */
#define PHDR_TP_PEN_UP		0x40
#define SCPDR_TP_SCAN_ENABLE	0x20
#define SCPDR_TP_SCAN_DISABLE	0x01
#define SCPDR_TP_SCAN_X		0x06
#define SCPDR_TP_SCAN_Y		0x09

/*
 * A/D converter channels to get x/y from
 */
#define ADC_CHANNEL_TP_X	1
#define ADC_CHANNEL_TP_Y	0

/*
 * Default (read: my device) raw X/Y values for framebuffer edges.
 */
#define PSH3TP_FB_RIGHT		 56
#define PSH3TP_FB_LEFT		969
#define PSH3TP_FB_TOP		848
#define PSH3TP_FB_BOTTOM	121


struct psh3tp_softc {
	struct device sc_dev;

#define PSH3TP_WSMOUSE_ENABLED	0x01
	int sc_enabled;
	struct callout sc_touch_ch;
	struct device *sc_wsmousedev;
	struct tpcalib_softc sc_tpcalib; /* calibration info for wsmouse */
};


/* config machinery */
static int psh3tp_match(struct device *, struct cfdata *, void *);
static void psh3tp_attach(struct device *, struct device *, void *);
static int psh3tp_wsmouse_submatch(struct device *, struct cfdata *, void *);

/* wsmouse accessops */
static int psh3tp_wsmouse_enable(void *);
static int psh3tp_wsmouse_ioctl(void *, u_long, caddr_t, int, struct proc *);
static void psh3tp_wsmouse_disable(void *);

/* internal driver routines */
static void psh3tp_enable(struct psh3tp_softc *);
static void psh3tp_disable(struct psh3tp_softc *);
static int psh3tp_set_enable(struct psh3tp_softc *, int, int);
static int psh3tp_intr(void *);
static void psh3tp_start_polling(void *);
static void psh3tp_stop_polling(struct psh3tp_softc *);
static void psh3tp_callout_wsmouse(void *);
static void psh3tp_wsmouse_input(struct psh3tp_softc *, int, int);
static void psh3tp_get_raw_xy(int *, int *);


const struct wsmouse_accessops psh3tp_accessops = {
	psh3tp_wsmouse_enable,
	psh3tp_wsmouse_ioctl,
	psh3tp_wsmouse_disable
};

static const struct wsmouse_calibcoords psh3tp_default_calib = {
	0, 0, 639, 239,
	4,
	{{ PSH3TP_FB_LEFT,  PSH3TP_FB_TOP,      0,   0 },
	 { PSH3TP_FB_RIGHT, PSH3TP_FB_TOP,    639,   0 },
	 { PSH3TP_FB_LEFT,  PSH3TP_FB_BOTTOM,   0, 239 },
	 { PSH3TP_FB_RIGHT, PSH3TP_FB_BOTTOM, 639, 239 }}
};


CFATTACH_DECL(psh3tp, sizeof(struct psh3tp_softc),
    psh3tp_match, psh3tp_attach, NULL, NULL);


static int
psh3tp_match(struct device *parent, struct cfdata *cf, void *aux)
{

	if (!platid_match(&platid, &platid_mask_MACH_HITACHI_PERSONA))
		return (0);

	if (strcmp(cf->cf_name, "psh3tp") != 0)
		return (0);

	return (1);
}


/*
 * Attach the touch panel driver and its wsmouse child.
 *
 * Note that we have to use submatch to distinguish between child because
 * wsmouse_match match unconditionally.
 */
static void
psh3tp_attach(struct device *parent, struct device *self, void *aux)
{
	struct psh3tp_softc *sc = (struct psh3tp_softc *)self;
	struct wsmousedev_attach_args wsma;

	printf("\n");

	sc->sc_enabled = 0;

	/* touch-panel as a pointing device */
	wsma.accessops = &psh3tp_accessops;
	wsma.accesscookie = sc;

	sc->sc_wsmousedev = config_found_sm(
	    self, &wsma, wsmousedevprint, psh3tp_wsmouse_submatch);
	if (sc->sc_wsmousedev == NULL)
		return;

	/* init calibration, set default parameters */
	tpcalib_init(&sc->sc_tpcalib);
	tpcalib_ioctl(&sc->sc_tpcalib,
	    WSMOUSEIO_SCALIBCOORDS, (caddr_t)&psh3tp_default_calib, 0, 0);

	/* used when in polling mode */
	callout_init(&sc->sc_touch_ch);

	/* establish interrupt handler, but disable until opened */
	intc_intr_establish(SH7709_INTEVT2_IRQ2,
	    IST_EDGE, IPL_TTY, psh3tp_intr, sc);
	intc_intr_disable(SH7709_INTEVT2_IRQ2);
}


static int
psh3tp_wsmouse_submatch(struct device *parent, struct cfdata *cf, void *aux)
{

	return (!strcmp(cf->cf_name, "wsmouse"));
}


/*
 * Enable touch panel:  we start in interrupt mode.
 * Must be called as spltty().
 */
static void
psh3tp_enable(struct psh3tp_softc *sc)
{

	DPRINTFN(2, ("%s: enable\n", sc->sc_dev.dv_xname));
	intc_intr_enable(SH7709_INTEVT2_IRQ2);
}


/*
 * Disable touch panel: disable interrupt, cancel pending callout.
 * Must be called as spltty().
 */
static void
psh3tp_disable(struct psh3tp_softc *sc)
{

	DPRINTFN(2, ("%s: disable\n", sc->sc_dev.dv_xname));
	intc_intr_disable(SH7709_INTEVT2_IRQ2);
	callout_stop(&sc->sc_touch_ch);
}


static int
psh3tp_set_enable(struct psh3tp_softc *sc, int on, int child)
{
	int s = spltty();

	if (on) {
		if (!sc->sc_enabled)
			psh3tp_enable(sc);
		sc->sc_enabled |= child;
	} else {
		sc->sc_enabled &= ~child;
		if (!sc->sc_enabled)
			psh3tp_disable(sc);
	}

	splx(s);
	return (0);
}


static int
psh3tp_wsmouse_enable(void *self)
{
	struct psh3tp_softc *sc = (struct psh3tp_softc *)self;

	DPRINTFN(1, ("%s: wsmouse enable\n", sc->sc_dev.dv_xname));
	return (psh3tp_set_enable(sc, 1, PSH3TP_WSMOUSE_ENABLED));
}


static void
psh3tp_wsmouse_disable(void *self)
{
	struct psh3tp_softc *sc = (struct psh3tp_softc *)self;

	DPRINTFN(1, ("%s: wsmouse disable\n", sc->sc_dev.dv_xname));
	psh3tp_set_enable(sc, 0, PSH3TP_WSMOUSE_ENABLED);
}


static int
psh3tp_intr(void *self)
{
	struct psh3tp_softc *sc = (struct psh3tp_softc *)self;

	uint8_t irr0;
	uint8_t phdr, touched;
	unsigned int steady, tremor_timeout;

	irr0 = _reg_read_1(SH7709_IRR0);
	if ((irr0 & IRR0_IRQ2) == 0) {
#ifdef DIAGNOSTIC
		printf("%s: irr0 %02x?\n", sc->sc_dev.dv_xname, irr0);
#endif
		return (0);
	}

	if (!sc->sc_enabled) {
		DPRINTFN(1, ("%s: intr: !sc_enabled\n", sc->sc_dev.dv_xname));
		intc_intr_disable(SH7709_INTEVT2_IRQ2);
		goto served;
	}

	/*
	 * Number of times the "touched" bit should be read
	 * consecutively.
	 */
#define TREMOR_THRESHOLD 0x300
	steady = 0;
	tremor_timeout = TREMOR_THRESHOLD * 16;	/* XXX: arbitrary */
	touched = TRUE;		/* we start with "touched" state */

	do {
		uint8_t state;

		phdr = _reg_read_1(SH7709_PHDR);
		state = ((phdr & PHDR_TP_PEN_UP) != PHDR_TP_PEN_UP);

		if (state == touched)
			++steady;
		else {
			steady = 0;
			touched = state;
		}

		if (--tremor_timeout == 0) {
			DPRINTF(("%s: tremor timeout!\n", sc->sc_dev.dv_xname));
			goto served;
		}
	} while (steady < TREMOR_THRESHOLD);

	if (touched) {
		intc_intr_disable(SH7709_INTEVT2_IRQ2);

		/*
		 * ADC readings are not stable yet, so schedule
		 * callout instead of accessing ADC from the interrupt
		 * handler only to immediately delay().
		 */
		callout_reset(&sc->sc_touch_ch,
		    hz/32, psh3tp_start_polling, sc);
	} else
		DPRINTFN(1, ("%s: tremor\n", sc->sc_dev.dv_xname));
served:
	/* clear the interrupt */
	_reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ2);

	return (1);
}


/*
 * Called from the interrupt handler at spltty() upon first touch.
 * Decide if we are going to report this touch as a mouse click/drag.
 */
static void
psh3tp_start_polling(void *self)
{
	struct psh3tp_softc *sc = (struct psh3tp_softc *)self;
	uint8_t phdr;
	int rawx, rawy;

	phdr = _reg_read_1(SH7709_PHDR);
	if ((phdr & PHDR_TP_PEN_UP) == PHDR_TP_PEN_UP) {
		DPRINTFN(2, ("%s: start: pen is not down\n",
		    sc->sc_dev.dv_xname));
		psh3tp_stop_polling(sc);
		return;
	}

	psh3tp_get_raw_xy(&rawx, &rawy);
	DPRINTFN(2, ("%s: start: %4d %4d -> ",
	    sc->sc_dev.dv_xname, rawx, rawy));

	if (sc->sc_enabled & PSH3TP_WSMOUSE_ENABLED) {
		DPRINTFN(2, ("mouse\n"));
		psh3tp_wsmouse_input(sc, rawx, rawy);
		callout_reset(&sc->sc_touch_ch,
		    hz/32, psh3tp_callout_wsmouse, sc);
	} else {
		DPRINTFN(2, ("ignore\n"));
		psh3tp_stop_polling(sc);
	}
}


/*
 * Re-enable touch panel interrupt.
 * Called as spltty() when polling code detects pen-up.
 */
static void
psh3tp_stop_polling(struct psh3tp_softc *sc)
{
	uint8_t irr0;

	DPRINTFN(2, ("%s: stop\n", sc->sc_dev.dv_xname));

	/* clear pending interrupt signal before re-enabling the interrupt */
	irr0 = _reg_read_1(SH7709_IRR0);
	if ((irr0 & IRR0_IRQ2) != 0)
		_reg_write_1(SH7709_IRR0, irr0 & ~IRR0_IRQ2);

	intc_intr_enable(SH7709_INTEVT2_IRQ2);
}


/*
 * We are reporting this touch as a mouse click/drag.
 */
static void
psh3tp_callout_wsmouse(void *self)
{
	struct psh3tp_softc *sc = (struct psh3tp_softc *)self;
	uint8_t phdr;
	int rawx, rawy;
	int s;

	s = spltty();

	if (!sc->sc_enabled) {
		DPRINTFN(1, ("%s: wsmouse callout: !sc_enabled\n",
		    sc->sc_dev.dv_xname));
		splx(s);
		return;
	}

	phdr = _reg_read_1(SH7709_PHDR);
	if ((phdr & PHDR_TP_PEN_UP) != PHDR_TP_PEN_UP) {
		psh3tp_get_raw_xy(&rawx, &rawy);
		psh3tp_wsmouse_input(sc, rawx, rawy); /* mouse dragged */
		callout_schedule(&sc->sc_touch_ch, hz/32);
	} else {
		wsmouse_input( /* button up */
		    sc->sc_wsmousedev, 0, 0, 0, 0, WSMOUSE_INPUT_DELTA);
		psh3tp_stop_polling(sc);
	}
	splx(s);
}


/*
 * Report mouse click/drag.
 */
static void
psh3tp_wsmouse_input(struct psh3tp_softc *sc, int rawx, int rawy)
{
	int x, y;

	tpcalib_trans(&sc->sc_tpcalib, rawx, rawy, &x, &y);
		
	DPRINTFN(3, ("%s: %4d %4d -> %3d %3d\n",
	     sc->sc_dev.dv_xname, rawx, rawy, x, y));

	wsmouse_input(sc->sc_wsmousedev,
	    1,	/* button */
	    x, y,
	    0,	/* flags */
	    WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
}


/*
 * Read raw X/Y coordinates from the ADC.
 */
static void
psh3tp_get_raw_xy(int *rawxp, int *rawyp)
{
	uint8_t scpdr;

	/* X axis */
	scpdr = _reg_read_1(SH7709_SCPDR);
	scpdr &= ~SCPDR_TP_SCAN_DISABLE;
	scpdr |= (SCPDR_TP_SCAN_ENABLE | SCPDR_TP_SCAN_X);
	_reg_write_1(SH7709_SCPDR, scpdr);
	delay(40);

	*rawxp = adc_sample_channel(ADC_CHANNEL_TP_X);

	/* Y axis */
	scpdr = _reg_read_1(SH7709_SCPDR);
	scpdr &=  ~SCPDR_TP_SCAN_X;
	scpdr |= (SCPDR_TP_SCAN_ENABLE | SCPDR_TP_SCAN_Y);
	_reg_write_1(SH7709_SCPDR, scpdr);
	delay(40);

	*rawyp = adc_sample_channel(ADC_CHANNEL_TP_Y);

	/* restore SCPDR */
	scpdr = _reg_read_1(SH7709_SCPDR);
	scpdr &= ~(SCPDR_TP_SCAN_ENABLE | SCPDR_TP_SCAN_Y);
	scpdr |= SCPDR_TP_SCAN_DISABLE;
	_reg_write_1(SH7709_SCPDR, scpdr);
}


static int
psh3tp_wsmouse_ioctl(
    void *self, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	struct psh3tp_softc *sc = (struct psh3tp_softc *)self;

	return (hpc_tpanel_ioctl(&sc->sc_tpcalib, cmd, data, flag, p));
}

----Next_Part(Wed_May_18_14:38:54_2005_639)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="psh3tp.4"

.\"	$NetBSD$
.\"
.\" Copyright (c) 2005 KIYOHARA Takashi
.\" 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 ``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 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.
.\"
.Dd May 14, 2005
.Dt PSH3TP 4 hpcsh
.Os
.Sh NAME
.Nm psh3tp
.Nd driver for PERSONA SH3 touch-panel
.Sh SYNOPSIS
.Cd "psh3tp* at adc?"
.Cd "wsmouse* at psh3tp? mux 0"
.Pp
.Sh DESCRIPTION
The
.Nm
driver provides support for the PERSONA SH3 touch-panel.
.Pp
Pen movements are passed to
.Xr wsmouse 4
as mouse clicks and drags.
.El
.Sh SEE ALSO
.Xr adc 4 ,
.Xr wsmouse 4 ,
.Xr tpctl 8
.Sh HISTORY
The
.Nm
driver first appeared in
.Nx 3.0 .

----Next_Part(Wed_May_18_14:38:54_2005_639)----