mse.c revision 144783
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 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/dev/mse/mse.c 144783 2005-04-08 05:22:58Z imp $
27 */
28
29/*
30 * Copyright 1992 by the University of Guelph
31 *
32 * Permission to use, copy and modify this
33 * software and its documentation for any purpose and without
34 * fee is hereby granted, provided that the above copyright
35 * notice appear in all copies and that both that copyright
36 * notice and this permission notice appear in supporting
37 * documentation.
38 * University of Guelph makes no representations about the suitability of
39 * this software for any purpose.  It is provided "as is"
40 * without express or implied warranty.
41 */
42/*
43 * Driver for the Logitech and ATI Inport Bus mice for use with 386bsd and
44 * the X386 port, courtesy of
45 * Rick Macklem, rick@snowhite.cis.uoguelph.ca
46 * Caveats: The driver currently uses spltty(), but doesn't use any
47 * generic tty code. It could use splmse() (that only masks off the
48 * bus mouse interrupt, but that would require hacking in i386/isa/icu.s.
49 * (This may be worth the effort, since the Logitech generates 30/60
50 * interrupts/sec continuously while it is open.)
51 * NB: The ATI has NOT been tested yet!
52 */
53
54/*
55 * Modification history:
56 * Sep 6, 1994 -- Lars Fredriksen(fredriks@mcs.com)
57 *   improved probe based on input from Logitech.
58 *
59 * Oct 19, 1992 -- E. Stark (stark@cs.sunysb.edu)
60 *   fixes to make it work with Microsoft InPort busmouse
61 *
62 * Jan, 1993 -- E. Stark (stark@cs.sunysb.edu)
63 *   added patches for new "select" interface
64 *
65 * May 4, 1993 -- E. Stark (stark@cs.sunysb.edu)
66 *   changed position of some spl()'s in mseread
67 *
68 * October 8, 1993 -- E. Stark (stark@cs.sunysb.edu)
69 *   limit maximum negative x/y value to -127 to work around XFree problem
70 *   that causes spurious button pushes.
71 */
72
73#include <sys/param.h>
74#include <sys/systm.h>
75#include <sys/conf.h>
76#include <sys/kernel.h>
77#include <sys/module.h>
78#include <sys/bus.h>
79#include <sys/poll.h>
80#include <sys/selinfo.h>
81#include <sys/uio.h>
82#include <sys/mouse.h>
83
84#include <machine/bus.h>
85#include <machine/clock.h>
86#include <machine/resource.h>
87#include <sys/rman.h>
88
89#include <isa/isavar.h>
90
91#include <dev/mse/msevar.h>
92
93devclass_t	mse_devclass;
94
95static	d_open_t	mseopen;
96static	d_close_t	mseclose;
97static	d_read_t	mseread;
98static  d_ioctl_t	mseioctl;
99static	d_poll_t	msepoll;
100
101static struct cdevsw mse_cdevsw = {
102	.d_version =	D_VERSION,
103	.d_flags =	D_NEEDGIANT,
104	.d_open =	mseopen,
105	.d_close =	mseclose,
106	.d_read =	mseread,
107	.d_ioctl =	mseioctl,
108	.d_poll =	msepoll,
109	.d_name =	"mse",
110};
111
112static	void		mseintr(void *);
113static	timeout_t	msetimeout;
114
115#define	MSE_UNIT(dev)		(minor(dev) >> 1)
116#define	MSE_NBLOCKIO(dev)	(minor(dev) & 0x1)
117
118#define	MSEPRI	(PZERO + 3)
119
120int
121mse_common_attach(device_t dev)
122{
123	mse_softc_t *sc;
124	int unit, flags, rid;
125
126	sc = device_get_softc(dev);
127	unit = device_get_unit(dev);
128
129	rid = 0;
130	sc->sc_intr = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
131					     RF_ACTIVE);
132	if (sc->sc_intr == NULL) {
133		bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port);
134		return ENXIO;
135	}
136
137	if (BUS_SETUP_INTR(device_get_parent(dev), dev, sc->sc_intr,
138	    INTR_TYPE_TTY, mseintr, sc, &sc->sc_ih)) {
139		bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port);
140		bus_release_resource(dev, SYS_RES_IRQ, rid, sc->sc_intr);
141		return ENXIO;
142	}
143	flags = device_get_flags(dev);
144	sc->mode.accelfactor = (flags & MSE_CONFIG_ACCEL) >> 4;
145	callout_handle_init(&sc->sc_callout);
146
147	sc->sc_dev = make_dev(&mse_cdevsw, unit << 1, 0, 0, 0600,
148	  "mse%d", unit);
149	sc->sc_ndev = make_dev(&mse_cdevsw, (unit<<1)+1, 0, 0, 0600,
150	  "nmse%d", unit);
151	return 0;
152}
153
154/*
155 * Exclusive open the mouse, initialize it and enable interrupts.
156 */
157static	int
158mseopen(struct cdev *dev, int flags, int fmt, struct thread *td)
159{
160	mse_softc_t *sc;
161	int s;
162
163	sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
164	if (sc == NULL)
165		return (ENXIO);
166	if (sc->sc_mousetype == MSE_NONE)
167		return (ENXIO);
168	if (sc->sc_flags & MSESC_OPEN)
169		return (EBUSY);
170	sc->sc_flags |= MSESC_OPEN;
171	sc->sc_obuttons = sc->sc_buttons = MOUSE_MSC_BUTTONS;
172	sc->sc_deltax = sc->sc_deltay = 0;
173	sc->sc_bytesread = sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
174	sc->sc_watchdog = FALSE;
175	sc->sc_callout = timeout(msetimeout, dev, hz*2);
176	sc->mode.level = 0;
177	sc->status.flags = 0;
178	sc->status.button = sc->status.obutton = 0;
179	sc->status.dx = sc->status.dy = sc->status.dz = 0;
180
181	/*
182	 * Initialize mouse interface and enable interrupts.
183	 */
184	s = spltty();
185	(*sc->sc_enablemouse)(sc->sc_iot, sc->sc_ioh);
186	splx(s);
187	return (0);
188}
189
190/*
191 * mseclose: just turn off mouse innterrupts.
192 */
193static	int
194mseclose(struct cdev *dev, int flags, int fmt, struct thread *td)
195{
196	mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
197	int s;
198
199	untimeout(msetimeout, dev, sc->sc_callout);
200	callout_handle_init(&sc->sc_callout);
201	s = spltty();
202	(*sc->sc_disablemouse)(sc->sc_iot, sc->sc_ioh);
203	sc->sc_flags &= ~MSESC_OPEN;
204	splx(s);
205	return(0);
206}
207
208/*
209 * mseread: return mouse info using the MSC serial protocol, but without
210 * using bytes 4 and 5.
211 * (Yes this is cheesy, but it makes the X386 server happy, so...)
212 */
213static	int
214mseread(struct cdev *dev, struct uio *uio, int ioflag)
215{
216	mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
217	int xfer, s, error;
218
219	/*
220	 * If there are no protocol bytes to be read, set up a new protocol
221	 * packet.
222	 */
223	s = spltty(); /* XXX Should be its own spl, but where is imlXX() */
224	if (sc->sc_bytesread >= sc->mode.packetsize) {
225		while (sc->sc_deltax == 0 && sc->sc_deltay == 0 &&
226		       (sc->sc_obuttons ^ sc->sc_buttons) == 0) {
227			if (MSE_NBLOCKIO(dev)) {
228				splx(s);
229				return (0);
230			}
231			sc->sc_flags |= MSESC_WANT;
232			error = tsleep(sc, MSEPRI | PCATCH,
233				"mseread", 0);
234			if (error) {
235				splx(s);
236				return (error);
237			}
238		}
239
240		/*
241		 * Generate protocol bytes.
242		 * For some reason X386 expects 5 bytes but never uses
243		 * the fourth or fifth?
244		 */
245		sc->sc_bytes[0] = sc->mode.syncmask[1]
246		    | (sc->sc_buttons & ~sc->mode.syncmask[0]);
247		if (sc->sc_deltax > 127)
248			sc->sc_deltax = 127;
249		if (sc->sc_deltax < -127)
250			sc->sc_deltax = -127;
251		sc->sc_deltay = -sc->sc_deltay;	/* Otherwise mousey goes wrong way */
252		if (sc->sc_deltay > 127)
253			sc->sc_deltay = 127;
254		if (sc->sc_deltay < -127)
255			sc->sc_deltay = -127;
256		sc->sc_bytes[1] = sc->sc_deltax;
257		sc->sc_bytes[2] = sc->sc_deltay;
258		sc->sc_bytes[3] = sc->sc_bytes[4] = 0;
259		sc->sc_bytes[5] = sc->sc_bytes[6] = 0;
260		sc->sc_bytes[7] = MOUSE_SYS_EXTBUTTONS;
261		sc->sc_obuttons = sc->sc_buttons;
262		sc->sc_deltax = sc->sc_deltay = 0;
263		sc->sc_bytesread = 0;
264	}
265	splx(s);
266	xfer = min(uio->uio_resid, sc->mode.packetsize - sc->sc_bytesread);
267	error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio);
268	if (error)
269		return (error);
270	sc->sc_bytesread += xfer;
271	return(0);
272}
273
274/*
275 * mseioctl: process ioctl commands.
276 */
277static int
278mseioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
279{
280	mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
281	mousestatus_t status;
282	int err = 0;
283	int s;
284
285	switch (cmd) {
286
287	case MOUSE_GETHWINFO:
288		s = spltty();
289		*(mousehw_t *)addr = sc->hw;
290		if (sc->mode.level == 0)
291			((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC;
292		splx(s);
293		break;
294
295	case MOUSE_GETMODE:
296		s = spltty();
297		*(mousemode_t *)addr = sc->mode;
298		switch (sc->mode.level) {
299		case 0:
300			break;
301		case 1:
302			((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE;
303	    		((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK;
304	    		((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC;
305			break;
306		}
307		splx(s);
308		break;
309
310	case MOUSE_SETMODE:
311		switch (((mousemode_t *)addr)->level) {
312		case 0:
313		case 1:
314			break;
315		default:
316			return (EINVAL);
317		}
318		if (((mousemode_t *)addr)->accelfactor < -1)
319			return (EINVAL);
320		else if (((mousemode_t *)addr)->accelfactor >= 0)
321			sc->mode.accelfactor =
322			    ((mousemode_t *)addr)->accelfactor;
323		sc->mode.level = ((mousemode_t *)addr)->level;
324		switch (sc->mode.level) {
325		case 0:
326			sc->sc_bytesread = sc->mode.packetsize
327			    = MOUSE_MSC_PACKETSIZE;
328			break;
329		case 1:
330			sc->sc_bytesread = sc->mode.packetsize
331			    = MOUSE_SYS_PACKETSIZE;
332			break;
333		}
334		break;
335
336	case MOUSE_GETLEVEL:
337		*(int *)addr = sc->mode.level;
338		break;
339
340	case MOUSE_SETLEVEL:
341		switch (*(int *)addr) {
342		case 0:
343			sc->mode.level = *(int *)addr;
344			sc->sc_bytesread = sc->mode.packetsize
345			    = MOUSE_MSC_PACKETSIZE;
346			break;
347		case 1:
348			sc->mode.level = *(int *)addr;
349			sc->sc_bytesread = sc->mode.packetsize
350			    = MOUSE_SYS_PACKETSIZE;
351			break;
352		default:
353			return (EINVAL);
354		}
355		break;
356
357	case MOUSE_GETSTATUS:
358		s = spltty();
359		status = sc->status;
360		sc->status.flags = 0;
361		sc->status.obutton = sc->status.button;
362		sc->status.button = 0;
363		sc->status.dx = 0;
364		sc->status.dy = 0;
365		sc->status.dz = 0;
366		splx(s);
367		*(mousestatus_t *)addr = status;
368		break;
369
370	case MOUSE_READSTATE:
371	case MOUSE_READDATA:
372		return (ENODEV);
373
374#if (defined(MOUSE_GETVARS))
375	case MOUSE_GETVARS:
376	case MOUSE_SETVARS:
377		return (ENODEV);
378#endif
379
380	default:
381		return (ENOTTY);
382	}
383	return (err);
384}
385
386/*
387 * msepoll: check for mouse input to be processed.
388 */
389static	int
390msepoll(struct cdev *dev, int events, struct thread *td)
391{
392	mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
393	int s;
394	int revents = 0;
395
396	s = spltty();
397	if (events & (POLLIN | POLLRDNORM)) {
398		if (sc->sc_bytesread != sc->mode.packetsize ||
399		    sc->sc_deltax != 0 || sc->sc_deltay != 0 ||
400		    (sc->sc_obuttons ^ sc->sc_buttons) != 0)
401			revents |= events & (POLLIN | POLLRDNORM);
402		else {
403			/*
404			 * Since this is an exclusive open device, any previous
405			 * proc pointer is trash now, so we can just assign it.
406			 */
407			selrecord(td, &sc->sc_selp);
408		}
409	}
410	splx(s);
411	return (revents);
412}
413
414/*
415 * msetimeout: watchdog timer routine.
416 */
417static void
418msetimeout(void *arg)
419{
420	struct cdev *dev;
421	mse_softc_t *sc;
422
423	dev = (struct cdev *)arg;
424	sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
425	if (sc->sc_watchdog) {
426		if (bootverbose)
427			printf("mse%d: lost interrupt?\n", MSE_UNIT(dev));
428		mseintr(sc);
429	}
430	sc->sc_watchdog = TRUE;
431	sc->sc_callout = timeout(msetimeout, dev, hz);
432}
433
434/*
435 * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative.
436 */
437static void
438mseintr(void *arg)
439{
440	/*
441	 * the table to turn MouseSystem button bits (MOUSE_MSC_BUTTON?UP)
442	 * into `mousestatus' button bits (MOUSE_BUTTON?DOWN).
443	 */
444	static int butmap[8] = {
445		0,
446		MOUSE_BUTTON3DOWN,
447		MOUSE_BUTTON2DOWN,
448		MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
449		MOUSE_BUTTON1DOWN,
450		MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
451		MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
452        	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
453	};
454	mse_softc_t *sc = arg;
455	int dx, dy, but;
456	int sign;
457
458#ifdef DEBUG
459	static int mse_intrcnt = 0;
460	if((mse_intrcnt++ % 10000) == 0)
461		printf("mseintr\n");
462#endif /* DEBUG */
463	if ((sc->sc_flags & MSESC_OPEN) == 0)
464		return;
465
466	(*sc->sc_getmouse)(sc->sc_iot, sc->sc_ioh, &dx, &dy, &but);
467	if (sc->mode.accelfactor > 0) {
468		sign = (dx < 0);
469		dx = dx * dx / sc->mode.accelfactor;
470		if (dx == 0)
471			dx = 1;
472		if (sign)
473			dx = -dx;
474		sign = (dy < 0);
475		dy = dy * dy / sc->mode.accelfactor;
476		if (dy == 0)
477			dy = 1;
478		if (sign)
479			dy = -dy;
480	}
481	sc->sc_deltax += dx;
482	sc->sc_deltay += dy;
483	sc->sc_buttons = but;
484
485	but = butmap[~but & MOUSE_MSC_BUTTONS];
486	sc->status.dx += dx;
487	sc->status.dy += dy;
488	sc->status.flags |= ((dx || dy) ? MOUSE_POSCHANGED : 0)
489	    | (sc->status.button ^ but);
490	sc->status.button = but;
491
492	sc->sc_watchdog = FALSE;
493
494	/*
495	 * If mouse state has changed, wake up anyone wanting to know.
496	 */
497	if (sc->sc_deltax != 0 || sc->sc_deltay != 0 ||
498	    (sc->sc_obuttons ^ sc->sc_buttons) != 0) {
499		if (sc->sc_flags & MSESC_WANT) {
500			sc->sc_flags &= ~MSESC_WANT;
501			wakeup(sc);
502		}
503		selwakeuppri(&sc->sc_selp, MSEPRI);
504	}
505}
506