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