mse_isa.c revision 158651
150826Smdodd/*-
250826Smdodd * Copyright (c) 2004 M. Warner Losh
350826Smdodd * All rights reserved.
450826Smdodd *
550826Smdodd * Redistribution and use in source and binary forms, with or without
650826Smdodd * modification, are permitted provided that the following conditions
750826Smdodd * are met:
850826Smdodd * 1. Redistributions of source code must retain the above copyright
950826Smdodd *    notice, this list of conditions and the following disclaimer.
1050826Smdodd * 2. Redistributions in binary form must reproduce the above copyright
1150826Smdodd *    notice, this list of conditions and the following disclaimer in the
1250826Smdodd *    documentation and/or other materials provided with the distribution.
1350826Smdodd *
1450826Smdodd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1550826Smdodd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1650826Smdodd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1750826Smdodd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1850826Smdodd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1950826Smdodd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2050826Smdodd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2150826Smdodd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2250826Smdodd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2350826Smdodd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2450826Smdodd * SUCH DAMAGE.
2550826Smdodd *
2650826Smdodd * $FreeBSD: head/sys/dev/mse/mse_isa.c 158651 2006-05-16 14:37:58Z phk $
2750826Smdodd */
2850826Smdodd
2950826Smdodd/*-
3050826Smdodd * Copyright 1992 by the University of Guelph
3150826Smdodd *
3250826Smdodd * Permission to use, copy and modify this
3350826Smdodd * software and its documentation for any purpose and without
3450826Smdodd * fee is hereby granted, provided that the above copyright
3550826Smdodd * notice appear in all copies and that both that copyright
3650826Smdodd * notice and this permission notice appear in supporting
3750826Smdodd * documentation.
3850826Smdodd * University of Guelph makes no representations about the suitability of
3950826Smdodd * this software for any purpose.  It is provided "as is"
4050826Smdodd * without express or implied warranty.
4150826Smdodd */
4250826Smdodd/*
4350826Smdodd * Driver for the Logitech and ATI Inport Bus mice for use with 386bsd and
4450826Smdodd * the X386 port, courtesy of
4550826Smdodd * Rick Macklem, rick@snowhite.cis.uoguelph.ca
4650826Smdodd * Caveats: The driver currently uses spltty(), but doesn't use any
4750826Smdodd * generic tty code. It could use splmse() (that only masks off the
4850826Smdodd * bus mouse interrupt, but that would require hacking in i386/isa/icu.s.
4950826Smdodd * (This may be worth the effort, since the Logitech generates 30/60
5050826Smdodd * interrupts/sec continuously while it is open.)
5150826Smdodd * NB: The ATI has NOT been tested yet!
5250826Smdodd */
5350826Smdodd
5450826Smdodd/*
5550826Smdodd * Modification history:
5650826Smdodd * Sep 6, 1994 -- Lars Fredriksen(fredriks@mcs.com)
5750826Smdodd *   improved probe based on input from Logitech.
5850826Smdodd *
5950826Smdodd * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu)
6050826Smdodd *   fixes to make it work with Microsoft InPort busmouse
6150826Smdodd *
6250826Smdodd * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu)
6350826Smdodd *   added patches for new "select" interface
6450826Smdodd *
6550826Smdodd * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu)
6650826Smdodd *   changed position of some spl()'s in mseread
6750826Smdodd *
6850826Smdodd * October 8, 1993 -- E. Stark (stark@cs.sunysb.edu)
6950826Smdodd *   limit maximum negative x/y value to -127 to work around XFree problem
7050826Smdodd *   that causes spurious button pushes.
7150826Smdodd */
7250826Smdodd
7350826Smdodd#include <sys/param.h>
7450826Smdodd#include <sys/systm.h>
7550826Smdodd#include <sys/conf.h>
7650826Smdodd#include <sys/kernel.h>
7750826Smdodd#include <sys/module.h>
7850826Smdodd#include <sys/bus.h>
7950826Smdodd#include <sys/poll.h>
8050826Smdodd#include <sys/selinfo.h>
8150826Smdodd#include <sys/uio.h>
8250826Smdodd#include <sys/mouse.h>
8350826Smdodd
8450826Smdodd#include <machine/bus.h>
8550826Smdodd#include <machine/resource.h>
8650826Smdodd#include <sys/rman.h>
8750826Smdodd
8850826Smdodd#include <isa/isavar.h>
8950826Smdodd
9050826Smdodd#include <dev/mse/msevar.h>
9150826Smdodd
9250826Smdoddstatic	int		mse_isa_probe(device_t dev);
9350826Smdoddstatic	int		mse_isa_attach(device_t dev);
9450826Smdoddstatic	int		mse_isa_detach(device_t dev);
9550826Smdodd
9650826Smdoddstatic	device_method_t	mse_methods[] = {
9750826Smdodd	DEVMETHOD(device_probe,		mse_isa_probe),
9850826Smdodd	DEVMETHOD(device_attach,	mse_isa_attach),
9950826Smdodd	DEVMETHOD(device_detach,	mse_isa_detach),
10050826Smdodd	{ 0, 0 }
10150826Smdodd};
10250826Smdodd
10350826Smdoddstatic	driver_t	mse_driver = {
10450826Smdodd	"mse",
10550826Smdodd	mse_methods,
10650826Smdodd	sizeof(mse_softc_t),
10750826Smdodd};
10850826Smdodd
10950826SmdoddDRIVER_MODULE(mse, isa, mse_driver, mse_devclass, 0, 0);
11050826Smdodd
11150826Smdoddstatic struct isa_pnp_id mse_ids[] = {
11250826Smdodd	{ 0x000fd041, "Bus mouse" },			/* PNP0F00 */
11350826Smdodd	{ 0x020fd041, "InPort mouse" },			/* PNP0F02 */
11450826Smdodd	{ 0x0d0fd041, "InPort mouse compatible" },	/* PNP0F0D */
11550826Smdodd	{ 0x110fd041, "Bus mouse compatible" },		/* PNP0F11 */
11650826Smdodd	{ 0x150fd041, "Logitech bus mouse" },		/* PNP0F15 */
11750826Smdodd	{ 0x180fd041, "Logitech bus mouse compatible" },/* PNP0F18 */
11850826Smdodd	{ 0 }
11950826Smdodd};
12050826Smdodd
12150826Smdodd/*
12250826Smdodd * Logitech bus mouse definitions
12350826Smdodd */
12450826Smdodd#define	MSE_SETUP	0x91	/* What does this mean? */
12550826Smdodd				/* The definition for the control port */
12650826Smdodd				/* is as follows: */
12750826Smdodd
12850826Smdodd				/* D7 	 =  Mode set flag (1 = active) 	*/
12950826Smdodd				/* D6,D5 =  Mode selection (port A) 	*/
13050826Smdodd				/* 	    00 = Mode 0 = Basic I/O 	*/
13150826Smdodd				/* 	    01 = Mode 1 = Strobed I/O 	*/
13250826Smdodd				/* 	    10 = Mode 2 = Bi-dir bus 	*/
13350826Smdodd				/* D4	 =  Port A direction (1 = input)*/
13450826Smdodd				/* D3	 =  Port C (upper 4 bits) 	*/
13550826Smdodd				/*	    direction. (1 = input)	*/
13650826Smdodd				/* D2	 =  Mode selection (port B & C) */
13750826Smdodd				/*	    0 = Mode 0 = Basic I/O	*/
13850826Smdodd				/*	    1 = Mode 1 = Strobed I/O	*/
13950826Smdodd				/* D1	 =  Port B direction (1 = input)*/
14050826Smdodd				/* D0	 =  Port C (lower 4 bits)	*/
14150826Smdodd				/*	    direction. (1 = input)	*/
14250826Smdodd
14350826Smdodd				/* So 91 means Basic I/O on all 3 ports,*/
14450826Smdodd				/* Port A is an input port, B is an 	*/
14550826Smdodd				/* output port, C is split with upper	*/
14650826Smdodd				/* 4 bits being an output port and lower*/
14750826Smdodd				/* 4 bits an input port, and enable the */
14850826Smdodd				/* sucker.				*/
14950826Smdodd				/* Courtesy Intel 8255 databook. Lars   */
15050826Smdodd#define	MSE_HOLD	0x80
15150826Smdodd#define	MSE_RXLOW	0x00
15250826Smdodd#define	MSE_RXHIGH	0x20
15350826Smdodd#define	MSE_RYLOW	0x40
15450826Smdodd#define	MSE_RYHIGH	0x60
15550826Smdodd#define	MSE_DISINTR	0x10
15650826Smdodd#define MSE_INTREN	0x00
15750826Smdodd
15850826Smdoddstatic	int		mse_probelogi(device_t dev, mse_softc_t *sc);
15950826Smdoddstatic	void		mse_disablelogi(bus_space_tag_t t,
16050826Smdodd			    bus_space_handle_t h);
16150826Smdoddstatic	void		mse_getlogi(bus_space_tag_t t, bus_space_handle_t h,
16250826Smdodd			    int *dx, int *dy, int *but);
16350826Smdoddstatic	void		mse_enablelogi(bus_space_tag_t t,
16450826Smdodd			    bus_space_handle_t h);
16550826Smdodd
16650826Smdodd/*
16750826Smdodd * ATI Inport mouse definitions
16850826Smdodd */
16950826Smdodd#define	MSE_INPORT_RESET	0x80
17050826Smdodd#define	MSE_INPORT_STATUS	0x00
17150826Smdodd#define	MSE_INPORT_DX		0x01
17250826Smdodd#define	MSE_INPORT_DY		0x02
17350826Smdodd#define	MSE_INPORT_MODE		0x07
17450826Smdodd#define	MSE_INPORT_HOLD		0x20
17550826Smdodd#define	MSE_INPORT_INTREN	0x09
17650826Smdodd
17750826Smdoddstatic	int		mse_probeati(device_t dev, mse_softc_t *sc);
17850826Smdoddstatic	void		mse_enableati(bus_space_tag_t t, bus_space_handle_t h);
17950826Smdoddstatic	void		mse_disableati(bus_space_tag_t t, bus_space_handle_t h);
18050826Smdoddstatic	void		mse_getati(bus_space_tag_t t, bus_space_handle_t h,
18150826Smdodd			    int *dx, int *dy, int *but);
18250826Smdodd
18350826Smdoddstatic struct mse_types mse_types[] = {
18450826Smdodd	{ MSE_ATIINPORT,
18550826Smdodd	  mse_probeati, mse_enableati, mse_disableati, mse_getati,
18650826Smdodd	  { 2, MOUSE_IF_INPORT, MOUSE_MOUSE, MOUSE_MODEL_GENERIC, 0, },
18750826Smdodd	  { MOUSE_PROTO_INPORT, -1, -1, 0, 0, MOUSE_MSC_PACKETSIZE,
18850826Smdodd	    { MOUSE_MSC_SYNCMASK, MOUSE_MSC_SYNC, }, }, },
18950826Smdodd	{ MSE_LOGITECH,
19050826Smdodd	  mse_probelogi, mse_enablelogi, mse_disablelogi, mse_getlogi,
19150826Smdodd	  { 2, MOUSE_IF_BUS, MOUSE_MOUSE, MOUSE_MODEL_GENERIC, 0, },
19250826Smdodd	  { MOUSE_PROTO_BUS, -1, -1, 0, 0, MOUSE_MSC_PACKETSIZE,
19350826Smdodd	    { MOUSE_MSC_SYNCMASK, MOUSE_MSC_SYNC, }, }, },
19450826Smdodd	{ 0, },
19550826Smdodd};
19650826Smdodd
19750826Smdoddstatic	int
19850826Smdoddmse_isa_probe(device_t dev)
19950826Smdodd{
20050826Smdodd	mse_softc_t *sc;
20150826Smdodd	int error;
20250826Smdodd	int rid;
20350826Smdodd	int i;
20450826Smdodd
20550826Smdodd	/* check PnP IDs */
20650826Smdodd	error = ISA_PNP_PROBE(device_get_parent(dev), dev, mse_ids);
20750826Smdodd	if (error == ENXIO)
20850826Smdodd		return error;
20950826Smdodd
21050826Smdodd	sc = device_get_softc(dev);
21150826Smdodd	rid = 0;
21250826Smdodd	sc->sc_port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0,
21350826Smdodd					 MSE_IOSIZE, RF_ACTIVE);
21450826Smdodd	if (sc->sc_port == NULL)
21550826Smdodd		return ENXIO;
21650826Smdodd	sc->sc_iot = rman_get_bustag(sc->sc_port);
21750826Smdodd	sc->sc_ioh = rman_get_bushandle(sc->sc_port);
21850826Smdodd
21950826Smdodd	/*
22050826Smdodd	 * Check for each mouse type in the table.
22150826Smdodd	 */
22250826Smdodd	i = 0;
22350826Smdodd	while (mse_types[i].m_type) {
22450826Smdodd		if ((*mse_types[i].m_probe)(dev, sc)) {
22550826Smdodd			sc->sc_mousetype = mse_types[i].m_type;
22650826Smdodd			sc->sc_enablemouse = mse_types[i].m_enable;
22750826Smdodd			sc->sc_disablemouse = mse_types[i].m_disable;
22850826Smdodd			sc->sc_getmouse = mse_types[i].m_get;
22950826Smdodd			sc->hw = mse_types[i].m_hw;
23050826Smdodd			sc->mode = mse_types[i].m_mode;
23150826Smdodd			bus_release_resource(dev, SYS_RES_IOPORT, rid,
23250826Smdodd					     sc->sc_port);
23350826Smdodd			device_set_desc(dev, "Bus/InPort Mouse");
23450826Smdodd			return 0;
23550826Smdodd		}
23650826Smdodd		i++;
23750826Smdodd	}
23850826Smdodd	bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port);
23950826Smdodd	return ENXIO;
24050826Smdodd}
24150826Smdodd
24250826Smdoddstatic	int
24350826Smdoddmse_isa_attach(device_t dev)
24450826Smdodd{
24550826Smdodd	mse_softc_t *sc;
24650826Smdodd	int rid;
24750826Smdodd
24850826Smdodd	sc = device_get_softc(dev);
24950826Smdodd
25050826Smdodd	rid = 0;
25150826Smdodd	sc->sc_port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0,
25250826Smdodd					 MSE_IOSIZE, RF_ACTIVE);
25350826Smdodd	if (sc->sc_port == NULL)
25450826Smdodd		return ENXIO;
25550826Smdodd	sc->sc_iot = rman_get_bustag(sc->sc_port);
25650826Smdodd	sc->sc_ioh = rman_get_bushandle(sc->sc_port);
25750826Smdodd
25850826Smdodd	return (mse_common_attach(dev));
25950826Smdodd}
26050826Smdodd
26150826Smdoddstatic	int
26250826Smdoddmse_isa_detach(device_t dev)
26350826Smdodd{
26450826Smdodd	mse_softc_t *sc;
26550826Smdodd	int rid;
26650826Smdodd
26750826Smdodd	sc = device_get_softc(dev);
26850826Smdodd	if (sc->sc_flags & MSESC_OPEN)
26950826Smdodd		return EBUSY;
27050826Smdodd
27150826Smdodd	rid = 0;
27250826Smdodd	BUS_TEARDOWN_INTR(device_get_parent(dev), dev, sc->sc_intr, sc->sc_ih);
27350826Smdodd	bus_release_resource(dev, SYS_RES_IRQ, rid, sc->sc_intr);
27450826Smdodd	bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port);
27550826Smdodd
27650826Smdodd	destroy_dev(sc->sc_dev);
27750826Smdodd	destroy_dev(sc->sc_ndev);
27850826Smdodd
27950826Smdodd	return 0;
28050826Smdodd}
28150826Smdodd
28250826Smdodd/*
28350826Smdodd * Routines for the Logitech mouse.
28450826Smdodd */
28550826Smdodd/*
28650826Smdodd * Test for a Logitech bus mouse and return 1 if it is.
28750826Smdodd * (until I know how to use the signature port properly, just disable
28850826Smdodd *  interrupts and return 1)
28950826Smdodd */
29050826Smdoddstatic int
29150826Smdoddmse_probelogi(device_t dev, mse_softc_t *sc)
29250826Smdodd{
29350826Smdodd
29450826Smdodd	int sig;
29550826Smdodd
29650826Smdodd	bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSE_PORTD, MSE_SETUP);
29750826Smdodd		/* set the signature port */
29850826Smdodd	bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSE_PORTB, MSE_LOGI_SIG);
29950826Smdodd
30050826Smdodd	DELAY(30000); /* 30 ms delay */
30150826Smdodd	sig = bus_space_read_1(sc->sc_iot, sc->sc_ioh, MSE_PORTB) & 0xFF;
30250826Smdodd	if (sig == MSE_LOGI_SIG) {
30350826Smdodd		bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSE_PORTC,
30450826Smdodd				  MSE_DISINTR);
30550826Smdodd		return(1);
30650826Smdodd	} else {
30750826Smdodd		if (bootverbose)
30850826Smdodd			device_printf(dev, "wrong signature %x\n", sig);
30950826Smdodd		return(0);
31050826Smdodd	}
31150826Smdodd}
31250826Smdodd
31350826Smdodd/*
31450826Smdodd * Initialize Logitech mouse and enable interrupts.
31550826Smdodd */
31650826Smdoddstatic void
31750826Smdoddmse_enablelogi(bus_space_tag_t tag, bus_space_handle_t handle)
31850826Smdodd{
31950826Smdodd	int dx, dy, but;
32050826Smdodd
32150826Smdodd	bus_space_write_1(tag, handle, MSE_PORTD, MSE_SETUP);
32250826Smdodd	mse_getlogi(tag, handle, &dx, &dy, &but);
32350826Smdodd}
32450826Smdodd
325/*
326 * Disable interrupts for Logitech mouse.
327 */
328static void
329mse_disablelogi(bus_space_tag_t tag, bus_space_handle_t handle)
330{
331
332	bus_space_write_1(tag, handle, MSE_PORTC, MSE_DISINTR);
333}
334
335/*
336 * Get the current dx, dy and button up/down state.
337 */
338static void
339mse_getlogi(bus_space_tag_t tag, bus_space_handle_t handle, int *dx, int *dy,
340    int *but)
341{
342	register char x, y;
343
344	bus_space_write_1(tag, handle, MSE_PORTC, MSE_HOLD | MSE_RXLOW);
345	x = bus_space_read_1(tag, handle, MSE_PORTA);
346	*but = (x >> 5) & MOUSE_MSC_BUTTONS;
347	x &= 0xf;
348	bus_space_write_1(tag, handle, MSE_PORTC, MSE_HOLD | MSE_RXHIGH);
349	x |= (bus_space_read_1(tag, handle, MSE_PORTA) << 4);
350	bus_space_write_1(tag, handle, MSE_PORTC, MSE_HOLD | MSE_RYLOW);
351	y = (bus_space_read_1(tag, handle, MSE_PORTA) & 0xf);
352	bus_space_write_1(tag, handle, MSE_PORTC, MSE_HOLD | MSE_RYHIGH);
353	y |= (bus_space_read_1(tag, handle, MSE_PORTA) << 4);
354	*dx = x;
355	*dy = y;
356	bus_space_write_1(tag, handle, MSE_PORTC, MSE_INTREN);
357}
358
359/*
360 * Routines for the ATI Inport bus mouse.
361 */
362/*
363 * Test for an ATI Inport bus mouse and return 1 if it is.
364 * (do not enable interrupts)
365 */
366static int
367mse_probeati(device_t dev, mse_softc_t *sc)
368{
369	int i;
370
371	for (i = 0; i < 2; i++)
372		if (bus_space_read_1(sc->sc_iot, sc->sc_ioh, MSE_PORTC) == 0xde)
373			return (1);
374	return (0);
375}
376
377/*
378 * Initialize ATI Inport mouse and enable interrupts.
379 */
380static void
381mse_enableati(bus_space_tag_t tag, bus_space_handle_t handle)
382{
383
384	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_RESET);
385	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_MODE);
386	bus_space_write_1(tag, handle, MSE_PORTB, MSE_INPORT_INTREN);
387}
388
389/*
390 * Disable interrupts for ATI Inport mouse.
391 */
392static void
393mse_disableati(bus_space_tag_t tag, bus_space_handle_t handle)
394{
395
396	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_MODE);
397	bus_space_write_1(tag, handle, MSE_PORTB, 0);
398}
399
400/*
401 * Get current dx, dy and up/down button state.
402 */
403static void
404mse_getati(bus_space_tag_t tag, bus_space_handle_t handle, int *dx, int *dy,
405    int *but)
406{
407	char byte;
408
409	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_MODE);
410	bus_space_write_1(tag, handle, MSE_PORTB, MSE_INPORT_HOLD);
411	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_STATUS);
412	*but = ~bus_space_read_1(tag, handle, MSE_PORTB) & MOUSE_MSC_BUTTONS;
413	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_DX);
414	byte = bus_space_read_1(tag, handle, MSE_PORTB);
415	*dx = byte;
416	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_DY);
417	byte = bus_space_read_1(tag, handle, MSE_PORTB);
418	*dy = byte;
419	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_MODE);
420	bus_space_write_1(tag, handle, MSE_PORTB, MSE_INPORT_INTREN);
421}
422