mse.c revision 139749
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.c 139749 2005-01-06 01:43:34Z 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
95devclass_t	mse_devclass;
96
97static	d_open_t	mseopen;
98static	d_close_t	mseclose;
99static	d_read_t	mseread;
100static  d_ioctl_t	mseioctl;
101static	d_poll_t	msepoll;
102
103static struct cdevsw mse_cdevsw = {
104	.d_version =	D_VERSION,
105	.d_flags =	D_NEEDGIANT,
106	.d_open =	mseopen,
107	.d_close =	mseclose,
108	.d_read =	mseread,
109	.d_ioctl =	mseioctl,
110	.d_poll =	msepoll,
111	.d_name =	"mse",
112};
113
114static	void		mseintr(void *);
115static	timeout_t	msetimeout;
116
117#define	MSE_UNIT(dev)		(minor(dev) >> 1)
118#define	MSE_NBLOCKIO(dev)	(minor(dev) & 0x1)
119
120#define	MSEPRI	(PZERO + 3)
121
122int
123mse_common_attach(device_t dev)
124{
125	mse_softc_t *sc;
126	int unit, flags, rid;
127
128	sc = device_get_softc(dev);
129	unit = device_get_unit(dev);
130
131	rid = 0;
132	sc->sc_intr = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
133					     RF_ACTIVE);
134	if (sc->sc_intr == NULL) {
135		bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port);
136		return ENXIO;
137	}
138
139	if (BUS_SETUP_INTR(device_get_parent(dev), dev, sc->sc_intr,
140			   INTR_TYPE_TTY, mseintr, sc, &sc->sc_ih)) {
141		bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->sc_port);
142		bus_release_resource(dev, SYS_RES_IRQ, rid, sc->sc_intr);
143		return ENXIO;
144	}
145	flags = device_get_flags(dev);
146	sc->mode.accelfactor = (flags & MSE_CONFIG_ACCEL) >> 4;
147	callout_handle_init(&sc->sc_callout);
148
149	sc->sc_dev = make_dev(&mse_cdevsw, unit << 1, 0, 0, 0600,
150			      "mse%d", unit);
151	sc->sc_ndev = make_dev(&mse_cdevsw, (unit<<1)+1, 0, 0, 0600,
152			       "nmse%d", unit);
153	return 0;
154}
155
156/*
157 * Exclusive open the mouse, initialize it and enable interrupts.
158 */
159static	int
160mseopen(dev, flags, fmt, td)
161	struct cdev *dev;
162	int flags;
163	int fmt;
164	struct thread *td;
165{
166	mse_softc_t *sc;
167	int s;
168
169	sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
170	if (sc == NULL)
171		return (ENXIO);
172	if (sc->sc_mousetype == MSE_NONE)
173		return (ENXIO);
174	if (sc->sc_flags & MSESC_OPEN)
175		return (EBUSY);
176	sc->sc_flags |= MSESC_OPEN;
177	sc->sc_obuttons = sc->sc_buttons = MOUSE_MSC_BUTTONS;
178	sc->sc_deltax = sc->sc_deltay = 0;
179	sc->sc_bytesread = sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
180	sc->sc_watchdog = FALSE;
181	sc->sc_callout = timeout(msetimeout, dev, hz*2);
182	sc->mode.level = 0;
183	sc->status.flags = 0;
184	sc->status.button = sc->status.obutton = 0;
185	sc->status.dx = sc->status.dy = sc->status.dz = 0;
186
187	/*
188	 * Initialize mouse interface and enable interrupts.
189	 */
190	s = spltty();
191	(*sc->sc_enablemouse)(sc->sc_iot, sc->sc_ioh);
192	splx(s);
193	return (0);
194}
195
196/*
197 * mseclose: just turn off mouse innterrupts.
198 */
199static	int
200mseclose(dev, flags, fmt, td)
201	struct cdev *dev;
202	int flags;
203	int fmt;
204	struct thread *td;
205{
206	mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
207	int s;
208
209	untimeout(msetimeout, dev, sc->sc_callout);
210	callout_handle_init(&sc->sc_callout);
211	s = spltty();
212	(*sc->sc_disablemouse)(sc->sc_iot, sc->sc_ioh);
213	sc->sc_flags &= ~MSESC_OPEN;
214	splx(s);
215	return(0);
216}
217
218/*
219 * mseread: return mouse info using the MSC serial protocol, but without
220 * using bytes 4 and 5.
221 * (Yes this is cheesy, but it makes the X386 server happy, so...)
222 */
223static	int
224mseread(dev, uio, ioflag)
225	struct cdev *dev;
226	struct uio *uio;
227	int ioflag;
228{
229	mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
230	int xfer, s, error;
231
232	/*
233	 * If there are no protocol bytes to be read, set up a new protocol
234	 * packet.
235	 */
236	s = spltty(); /* XXX Should be its own spl, but where is imlXX() */
237	if (sc->sc_bytesread >= sc->mode.packetsize) {
238		while (sc->sc_deltax == 0 && sc->sc_deltay == 0 &&
239		       (sc->sc_obuttons ^ sc->sc_buttons) == 0) {
240			if (MSE_NBLOCKIO(dev)) {
241				splx(s);
242				return (0);
243			}
244			sc->sc_flags |= MSESC_WANT;
245			error = tsleep(sc, MSEPRI | PCATCH,
246				"mseread", 0);
247			if (error) {
248				splx(s);
249				return (error);
250			}
251		}
252
253		/*
254		 * Generate protocol bytes.
255		 * For some reason X386 expects 5 bytes but never uses
256		 * the fourth or fifth?
257		 */
258		sc->sc_bytes[0] = sc->mode.syncmask[1]
259		    | (sc->sc_buttons & ~sc->mode.syncmask[0]);
260		if (sc->sc_deltax > 127)
261			sc->sc_deltax = 127;
262		if (sc->sc_deltax < -127)
263			sc->sc_deltax = -127;
264		sc->sc_deltay = -sc->sc_deltay;	/* Otherwise mousey goes wrong way */
265		if (sc->sc_deltay > 127)
266			sc->sc_deltay = 127;
267		if (sc->sc_deltay < -127)
268			sc->sc_deltay = -127;
269		sc->sc_bytes[1] = sc->sc_deltax;
270		sc->sc_bytes[2] = sc->sc_deltay;
271		sc->sc_bytes[3] = sc->sc_bytes[4] = 0;
272		sc->sc_bytes[5] = sc->sc_bytes[6] = 0;
273		sc->sc_bytes[7] = MOUSE_SYS_EXTBUTTONS;
274		sc->sc_obuttons = sc->sc_buttons;
275		sc->sc_deltax = sc->sc_deltay = 0;
276		sc->sc_bytesread = 0;
277	}
278	splx(s);
279	xfer = min(uio->uio_resid, sc->mode.packetsize - sc->sc_bytesread);
280	error = uiomove(&sc->sc_bytes[sc->sc_bytesread], xfer, uio);
281	if (error)
282		return (error);
283	sc->sc_bytesread += xfer;
284	return(0);
285}
286
287/*
288 * mseioctl: process ioctl commands.
289 */
290static int
291mseioctl(dev, cmd, addr, flag, td)
292	struct cdev *dev;
293	u_long cmd;
294	caddr_t addr;
295	int flag;
296	struct thread *td;
297{
298	mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
299	mousestatus_t status;
300	int err = 0;
301	int s;
302
303	switch (cmd) {
304
305	case MOUSE_GETHWINFO:
306		s = spltty();
307		*(mousehw_t *)addr = sc->hw;
308		if (sc->mode.level == 0)
309			((mousehw_t *)addr)->model = MOUSE_MODEL_GENERIC;
310		splx(s);
311		break;
312
313	case MOUSE_GETMODE:
314		s = spltty();
315		*(mousemode_t *)addr = sc->mode;
316		switch (sc->mode.level) {
317		case 0:
318			break;
319		case 1:
320			((mousemode_t *)addr)->protocol = MOUSE_PROTO_SYSMOUSE;
321	    		((mousemode_t *)addr)->syncmask[0] = MOUSE_SYS_SYNCMASK;
322	    		((mousemode_t *)addr)->syncmask[1] = MOUSE_SYS_SYNC;
323			break;
324		}
325		splx(s);
326		break;
327
328	case MOUSE_SETMODE:
329		switch (((mousemode_t *)addr)->level) {
330		case 0:
331		case 1:
332			break;
333		default:
334			return (EINVAL);
335		}
336		if (((mousemode_t *)addr)->accelfactor < -1)
337			return (EINVAL);
338		else if (((mousemode_t *)addr)->accelfactor >= 0)
339			sc->mode.accelfactor =
340			    ((mousemode_t *)addr)->accelfactor;
341		sc->mode.level = ((mousemode_t *)addr)->level;
342		switch (sc->mode.level) {
343		case 0:
344			sc->sc_bytesread = sc->mode.packetsize
345			    = MOUSE_MSC_PACKETSIZE;
346			break;
347		case 1:
348			sc->sc_bytesread = sc->mode.packetsize
349			    = MOUSE_SYS_PACKETSIZE;
350			break;
351		}
352		break;
353
354	case MOUSE_GETLEVEL:
355		*(int *)addr = sc->mode.level;
356		break;
357
358	case MOUSE_SETLEVEL:
359		switch (*(int *)addr) {
360		case 0:
361			sc->mode.level = *(int *)addr;
362			sc->sc_bytesread = sc->mode.packetsize
363			    = MOUSE_MSC_PACKETSIZE;
364			break;
365		case 1:
366			sc->mode.level = *(int *)addr;
367			sc->sc_bytesread = sc->mode.packetsize
368			    = MOUSE_SYS_PACKETSIZE;
369			break;
370		default:
371			return (EINVAL);
372		}
373		break;
374
375	case MOUSE_GETSTATUS:
376		s = spltty();
377		status = sc->status;
378		sc->status.flags = 0;
379		sc->status.obutton = sc->status.button;
380		sc->status.button = 0;
381		sc->status.dx = 0;
382		sc->status.dy = 0;
383		sc->status.dz = 0;
384		splx(s);
385		*(mousestatus_t *)addr = status;
386		break;
387
388	case MOUSE_READSTATE:
389	case MOUSE_READDATA:
390		return (ENODEV);
391
392#if (defined(MOUSE_GETVARS))
393	case MOUSE_GETVARS:
394	case MOUSE_SETVARS:
395		return (ENODEV);
396#endif
397
398	default:
399		return (ENOTTY);
400	}
401	return (err);
402}
403
404/*
405 * msepoll: check for mouse input to be processed.
406 */
407static	int
408msepoll(dev, events, td)
409	struct cdev *dev;
410	int events;
411	struct thread *td;
412{
413	mse_softc_t *sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
414	int s;
415	int revents = 0;
416
417	s = spltty();
418	if (events & (POLLIN | POLLRDNORM)) {
419		if (sc->sc_bytesread != sc->mode.packetsize ||
420		    sc->sc_deltax != 0 || sc->sc_deltay != 0 ||
421		    (sc->sc_obuttons ^ sc->sc_buttons) != 0)
422			revents |= events & (POLLIN | POLLRDNORM);
423		else {
424			/*
425			 * Since this is an exclusive open device, any previous
426			 * proc pointer is trash now, so we can just assign it.
427			 */
428			selrecord(td, &sc->sc_selp);
429		}
430	}
431	splx(s);
432	return (revents);
433}
434
435/*
436 * msetimeout: watchdog timer routine.
437 */
438static void
439msetimeout(arg)
440	void *arg;
441{
442	struct cdev *dev;
443	mse_softc_t *sc;
444
445	dev = (struct cdev *)arg;
446	sc = devclass_get_softc(mse_devclass, MSE_UNIT(dev));
447	if (sc->sc_watchdog) {
448		if (bootverbose)
449			printf("mse%d: lost interrupt?\n", MSE_UNIT(dev));
450		mseintr(sc);
451	}
452	sc->sc_watchdog = TRUE;
453	sc->sc_callout = timeout(msetimeout, dev, hz);
454}
455
456/*
457 * mseintr: update mouse status. sc_deltax and sc_deltay are accumulative.
458 */
459static void
460mseintr(arg)
461	void *arg;
462{
463	/*
464	 * the table to turn MouseSystem button bits (MOUSE_MSC_BUTTON?UP)
465	 * into `mousestatus' button bits (MOUSE_BUTTON?DOWN).
466	 */
467	static int butmap[8] = {
468		0,
469		MOUSE_BUTTON3DOWN,
470		MOUSE_BUTTON2DOWN,
471		MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
472		MOUSE_BUTTON1DOWN,
473		MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
474		MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
475        	MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
476	};
477	mse_softc_t *sc = arg;
478	int dx, dy, but;
479	int sign;
480
481#ifdef DEBUG
482	static int mse_intrcnt = 0;
483	if((mse_intrcnt++ % 10000) == 0)
484		printf("mseintr\n");
485#endif /* DEBUG */
486	if ((sc->sc_flags & MSESC_OPEN) == 0)
487		return;
488
489	(*sc->sc_getmouse)(sc->sc_iot, sc->sc_ioh, &dx, &dy, &but);
490	if (sc->mode.accelfactor > 0) {
491		sign = (dx < 0);
492		dx = dx * dx / sc->mode.accelfactor;
493		if (dx == 0)
494			dx = 1;
495		if (sign)
496			dx = -dx;
497		sign = (dy < 0);
498		dy = dy * dy / sc->mode.accelfactor;
499		if (dy == 0)
500			dy = 1;
501		if (sign)
502			dy = -dy;
503	}
504	sc->sc_deltax += dx;
505	sc->sc_deltay += dy;
506	sc->sc_buttons = but;
507
508	but = butmap[~but & MOUSE_MSC_BUTTONS];
509	sc->status.dx += dx;
510	sc->status.dy += dy;
511	sc->status.flags |= ((dx || dy) ? MOUSE_POSCHANGED : 0)
512	    | (sc->status.button ^ but);
513	sc->status.button = but;
514
515	sc->sc_watchdog = FALSE;
516
517	/*
518	 * If mouse state has changed, wake up anyone wanting to know.
519	 */
520	if (sc->sc_deltax != 0 || sc->sc_deltay != 0 ||
521	    (sc->sc_obuttons ^ sc->sc_buttons) != 0) {
522		if (sc->sc_flags & MSESC_WANT) {
523			sc->sc_flags &= ~MSESC_WANT;
524			wakeup(sc);
525		}
526		selwakeuppri(&sc->sc_selp, MSEPRI);
527	}
528}
529