mse_isa.c revision 138755
1/*
2 * Copyright (c) 2004 M. Warner Losh
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions, and the following disclaimer,
10 *    without modification, immediately at the beginning of the file.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in
13 *    the documentation and/or other materials provided with the
14 *    distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: head/sys/dev/mse/mse_isa.c 138755 2004-12-12 20:05:50Z imp $
29 */
30
31/*
32 * Copyright 1992 by the University of Guelph
33 *
34 * Permission to use, copy and modify this
35 * software and its documentation for any purpose and without
36 * fee is hereby granted, provided that the above copyright
37 * notice appear in all copies and that both that copyright
38 * notice and this permission notice appear in supporting
39 * documentation.
40 * University of Guelph makes no representations about the suitability of
41 * this software for any purpose.  It is provided "as is"
42 * without express or implied warranty.
43 */
44/*
45 * Driver for the Logitech and ATI Inport Bus mice for use with 386bsd and
46 * the X386 port, courtesy of
47 * Rick Macklem, rick@snowhite.cis.uoguelph.ca
48 * Caveats: The driver currently uses spltty(), but doesn't use any
49 * generic tty code. It could use splmse() (that only masks off the
50 * bus mouse interrupt, but that would require hacking in i386/isa/icu.s.
51 * (This may be worth the effort, since the Logitech generates 30/60
52 * interrupts/sec continuously while it is open.)
53 * NB: The ATI has NOT been tested yet!
54 */
55
56/*
57 * Modification history:
58 * Sep 6, 1994 -- Lars Fredriksen(fredriks@mcs.com)
59 *   improved probe based on input from Logitech.
60 *
61 * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu)
62 *   fixes to make it work with Microsoft InPort busmouse
63 *
64 * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu)
65 *   added patches for new "select" interface
66 *
67 * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu)
68 *   changed position of some spl()'s in mseread
69 *
70 * October 8, 1993 -- E. Stark (stark@cs.sunysb.edu)
71 *   limit maximum negative x/y value to -127 to work around XFree problem
72 *   that causes spurious button pushes.
73 */
74
75#include <sys/param.h>
76#include <sys/systm.h>
77#include <sys/conf.h>
78#include <sys/kernel.h>
79#include <sys/module.h>
80#include <sys/bus.h>
81#include <sys/poll.h>
82#include <sys/selinfo.h>
83#include <sys/uio.h>
84#include <sys/mouse.h>
85
86#include <machine/bus.h>
87#include <machine/clock.h>
88#include <machine/resource.h>
89#include <sys/rman.h>
90
91#include <isa/isavar.h>
92
93#include <dev/mse/msevar.h>
94
95static	int		mse_probe(device_t dev);
96static	int		mse_attach(device_t dev);
97static	int		mse_detach(device_t dev);
98
99static	device_method_t	mse_methods[] = {
100	DEVMETHOD(device_probe,		mse_probe),
101	DEVMETHOD(device_attach,	mse_attach),
102	DEVMETHOD(device_detach,	mse_detach),
103	{ 0, 0 }
104};
105
106static	driver_t	mse_driver = {
107	"mse",
108	mse_methods,
109	sizeof(mse_softc_t),
110};
111
112DRIVER_MODULE(mse, isa, mse_driver, mse_devclass, 0, 0);
113
114static struct isa_pnp_id mse_ids[] = {
115	{ 0x000fd041, "Bus mouse" },			/* PNP0F00 */
116	{ 0x020fd041, "InPort mouse" },			/* PNP0F02 */
117	{ 0x0d0fd041, "InPort mouse compatible" },	/* PNP0F0D */
118	{ 0x110fd041, "Bus mouse compatible" },		/* PNP0F11 */
119	{ 0x150fd041, "Logitech bus mouse" },		/* PNP0F15 */
120	{ 0x180fd041, "Logitech bus mouse compatible" },/* PNP0F18 */
121	{ 0 }
122};
123
124/*
125 * Logitech bus mouse definitions
126 */
127#define	MSE_SETUP	0x91	/* What does this mean? */
128				/* The definition for the control port */
129				/* is as follows: */
130
131				/* D7 	 =  Mode set flag (1 = active) 	*/
132				/* D6,D5 =  Mode selection (port A) 	*/
133				/* 	    00 = Mode 0 = Basic I/O 	*/
134				/* 	    01 = Mode 1 = Strobed I/O 	*/
135				/* 	    10 = Mode 2 = Bi-dir bus 	*/
136				/* D4	 =  Port A direction (1 = input)*/
137				/* D3	 =  Port C (upper 4 bits) 	*/
138				/*	    direction. (1 = input)	*/
139				/* D2	 =  Mode selection (port B & C) */
140				/*	    0 = Mode 0 = Basic I/O	*/
141				/*	    1 = Mode 1 = Strobed I/O	*/
142				/* D1	 =  Port B direction (1 = input)*/
143				/* D0	 =  Port C (lower 4 bits)	*/
144				/*	    direction. (1 = input)	*/
145
146				/* So 91 means Basic I/O on all 3 ports,*/
147				/* Port A is an input port, B is an 	*/
148				/* output port, C is split with upper	*/
149				/* 4 bits being an output port and lower*/
150				/* 4 bits an input port, and enable the */
151				/* sucker.				*/
152				/* Courtesy Intel 8255 databook. Lars   */
153#define	MSE_HOLD	0x80
154#define	MSE_RXLOW	0x00
155#define	MSE_RXHIGH	0x20
156#define	MSE_RYLOW	0x40
157#define	MSE_RYHIGH	0x60
158#define	MSE_DISINTR	0x10
159#define MSE_INTREN	0x00
160
161static	int		mse_probelogi(device_t dev, mse_softc_t *sc);
162static	void		mse_disablelogi(bus_space_tag_t t,
163			    bus_space_handle_t h);
164static	void		mse_getlogi(bus_space_tag_t t, bus_space_handle_t h,
165			    int *dx, int *dy, int *but);
166static	void		mse_enablelogi(bus_space_tag_t t,
167			    bus_space_handle_t h);
168
169/*
170 * ATI Inport mouse definitions
171 */
172#define	MSE_INPORT_RESET	0x80
173#define	MSE_INPORT_STATUS	0x00
174#define	MSE_INPORT_DX		0x01
175#define	MSE_INPORT_DY		0x02
176#define	MSE_INPORT_MODE		0x07
177#define	MSE_INPORT_HOLD		0x20
178#define	MSE_INPORT_INTREN	0x09
179
180static	int		mse_probeati(device_t dev, mse_softc_t *sc);
181static	void		mse_enableati(bus_space_tag_t t, bus_space_handle_t h);
182static	void		mse_disableati(bus_space_tag_t t, bus_space_handle_t h);
183static	void		mse_getati(bus_space_tag_t t, bus_space_handle_t h,
184			    int *dx, int *dy, int *but);
185
186static struct mse_types mse_types[] = {
187	{ MSE_ATIINPORT,
188	  mse_probeati, mse_enableati, mse_disableati, mse_getati,
189	  { 2, MOUSE_IF_INPORT, MOUSE_MOUSE, MOUSE_MODEL_GENERIC, 0, },
190	  { MOUSE_PROTO_INPORT, -1, -1, 0, 0, MOUSE_MSC_PACKETSIZE,
191	    { MOUSE_MSC_SYNCMASK, MOUSE_MSC_SYNC, }, }, },
192	{ MSE_LOGITECH,
193	  mse_probelogi, mse_enablelogi, mse_disablelogi, mse_getlogi,
194	  { 2, MOUSE_IF_BUS, MOUSE_MOUSE, MOUSE_MODEL_GENERIC, 0, },
195	  { MOUSE_PROTO_BUS, -1, -1, 0, 0, MOUSE_MSC_PACKETSIZE,
196	    { MOUSE_MSC_SYNCMASK, MOUSE_MSC_SYNC, }, }, },
197	{ 0, },
198};
199
200static	int
201mse_probe(dev)
202	device_t dev;
203{
204	mse_softc_t *sc;
205	int error;
206	int rid;
207	int i;
208
209	/* check PnP IDs */
210	error = ISA_PNP_PROBE(device_get_parent(dev), dev, mse_ids);
211	if (error == ENXIO)
212		return error;
213
214	sc = device_get_softc(dev);
215	rid = 0;
216	sc->sc_port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0,
217					 MSE_IOSIZE, RF_ACTIVE);
218	if (sc->sc_port == NULL)
219		return ENXIO;
220	sc->sc_iot = rman_get_bustag(sc->sc_port);
221	sc->sc_ioh = rman_get_bushandle(sc->sc_port);
222
223	/*
224	 * Check for each mouse type in the table.
225	 */
226	i = 0;
227	while (mse_types[i].m_type) {
228		if ((*mse_types[i].m_probe)(dev, sc)) {
229			sc->sc_mousetype = mse_types[i].m_type;
230			sc->sc_enablemouse = mse_types[i].m_enable;
231			sc->sc_disablemouse = mse_types[i].m_disable;
232			sc->sc_getmouse = mse_types[i].m_get;
233			sc->hw = mse_types[i].m_hw;
234			sc->mode = mse_types[i].m_mode;
235			bus_release_resource(dev, SYS_RES_IOPORT, rid,
236					     sc->sc_port);
237			device_set_desc(dev, "Bus/InPort Mouse");
238			return 0;
239		}
240		i++;
241	}
242	bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port);
243	return ENXIO;
244}
245
246static	int
247mse_attach(dev)
248	device_t dev;
249{
250	mse_softc_t *sc;
251	int rid;
252
253	sc = device_get_softc(dev);
254
255	rid = 0;
256	sc->sc_port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0,
257					 MSE_IOSIZE, RF_ACTIVE);
258	if (sc->sc_port == NULL)
259		return ENXIO;
260	sc->sc_iot = rman_get_bustag(sc->sc_port);
261	sc->sc_ioh = rman_get_bushandle(sc->sc_port);
262
263	return (mse_common_attach(dev));
264}
265
266static	int
267mse_detach(dev)
268	device_t dev;
269{
270	mse_softc_t *sc;
271	int rid;
272
273	sc = device_get_softc(dev);
274	if (sc->sc_flags & MSESC_OPEN)
275		return EBUSY;
276
277	rid = 0;
278	BUS_TEARDOWN_INTR(device_get_parent(dev), dev, sc->sc_intr, sc->sc_ih);
279	bus_release_resource(dev, SYS_RES_IRQ, rid, sc->sc_intr);
280	bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port);
281
282	destroy_dev(sc->sc_dev);
283	destroy_dev(sc->sc_ndev);
284
285	return 0;
286}
287
288/*
289 * Routines for the Logitech mouse.
290 */
291/*
292 * Test for a Logitech bus mouse and return 1 if it is.
293 * (until I know how to use the signature port properly, just disable
294 *  interrupts and return 1)
295 */
296static int
297mse_probelogi(dev, sc)
298	device_t dev;
299	mse_softc_t *sc;
300{
301
302	int sig;
303
304	bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSE_PORTD, MSE_SETUP);
305		/* set the signature port */
306	bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSE_PORTB, MSE_LOGI_SIG);
307
308	DELAY(30000); /* 30 ms delay */
309	sig = bus_space_read_1(sc->sc_iot, sc->sc_ioh, MSE_PORTB) & 0xFF;
310	if (sig == MSE_LOGI_SIG) {
311		bus_space_write_1(sc->sc_iot, sc->sc_ioh, MSE_PORTC,
312				  MSE_DISINTR);
313		return(1);
314	} else {
315		if (bootverbose)
316			device_printf(dev, "wrong signature %x\n", sig);
317		return(0);
318	}
319}
320
321/*
322 * Initialize Logitech mouse and enable interrupts.
323 */
324static void
325mse_enablelogi(tag, handle)
326	bus_space_tag_t tag;
327	bus_space_handle_t handle;
328{
329	int dx, dy, but;
330
331	bus_space_write_1(tag, handle, MSE_PORTD, MSE_SETUP);
332	mse_getlogi(tag, handle, &dx, &dy, &but);
333}
334
335/*
336 * Disable interrupts for Logitech mouse.
337 */
338static void
339mse_disablelogi(tag, handle)
340	bus_space_tag_t tag;
341	bus_space_handle_t handle;
342{
343
344	bus_space_write_1(tag, handle, MSE_PORTC, MSE_DISINTR);
345}
346
347/*
348 * Get the current dx, dy and button up/down state.
349 */
350static void
351mse_getlogi(tag, handle, dx, dy, but)
352	bus_space_tag_t tag;
353	bus_space_handle_t handle;
354	int *dx;
355	int *dy;
356	int *but;
357{
358	register char x, y;
359
360	bus_space_write_1(tag, handle, MSE_PORTC, MSE_HOLD | MSE_RXLOW);
361	x = bus_space_read_1(tag, handle, MSE_PORTA);
362	*but = (x >> 5) & MOUSE_MSC_BUTTONS;
363	x &= 0xf;
364	bus_space_write_1(tag, handle, MSE_PORTC, MSE_HOLD | MSE_RXHIGH);
365	x |= (bus_space_read_1(tag, handle, MSE_PORTA) << 4);
366	bus_space_write_1(tag, handle, MSE_PORTC, MSE_HOLD | MSE_RYLOW);
367	y = (bus_space_read_1(tag, handle, MSE_PORTA) & 0xf);
368	bus_space_write_1(tag, handle, MSE_PORTC, MSE_HOLD | MSE_RYHIGH);
369	y |= (bus_space_read_1(tag, handle, MSE_PORTA) << 4);
370	*dx = x;
371	*dy = y;
372	bus_space_write_1(tag, handle, MSE_PORTC, MSE_INTREN);
373}
374
375/*
376 * Routines for the ATI Inport bus mouse.
377 */
378/*
379 * Test for an ATI Inport bus mouse and return 1 if it is.
380 * (do not enable interrupts)
381 */
382static int
383mse_probeati(dev, sc)
384	device_t dev;
385	mse_softc_t *sc;
386{
387	int i;
388
389	for (i = 0; i < 2; i++)
390		if (bus_space_read_1(sc->sc_iot, sc->sc_ioh, MSE_PORTC) == 0xde)
391			return (1);
392	return (0);
393}
394
395/*
396 * Initialize ATI Inport mouse and enable interrupts.
397 */
398static void
399mse_enableati(tag, handle)
400	bus_space_tag_t tag;
401	bus_space_handle_t handle;
402{
403
404	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_RESET);
405	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_MODE);
406	bus_space_write_1(tag, handle, MSE_PORTB, MSE_INPORT_INTREN);
407}
408
409/*
410 * Disable interrupts for ATI Inport mouse.
411 */
412static void
413mse_disableati(tag, handle)
414	bus_space_tag_t tag;
415	bus_space_handle_t handle;
416{
417
418	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_MODE);
419	bus_space_write_1(tag, handle, MSE_PORTB, 0);
420}
421
422/*
423 * Get current dx, dy and up/down button state.
424 */
425static void
426mse_getati(tag, handle, dx, dy, but)
427	bus_space_tag_t tag;
428	bus_space_handle_t handle;
429	int *dx;
430	int *dy;
431	int *but;
432{
433	register char byte;
434
435	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_MODE);
436	bus_space_write_1(tag, handle, MSE_PORTB, MSE_INPORT_HOLD);
437	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_STATUS);
438	*but = ~bus_space_read_1(tag, handle, MSE_PORTB) & MOUSE_MSC_BUTTONS;
439	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_DX);
440	byte = bus_space_read_1(tag, handle, MSE_PORTB);
441	*dx = byte;
442	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_DY);
443	byte = bus_space_read_1(tag, handle, MSE_PORTB);
444	*dy = byte;
445	bus_space_write_1(tag, handle, MSE_PORTA, MSE_INPORT_MODE);
446	bus_space_write_1(tag, handle, MSE_PORTB, MSE_INPORT_INTREN);
447}
448