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