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