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
92devclass_t	mse_devclass;
93
94static	d_open_t	mseopen;
95static	d_close_t	mseclose;
96static	d_read_t	mseread;
97static  d_ioctl_t	mseioctl;
98static	d_poll_t	msepoll;
99
100static 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
110static	void		mseintr(void *);
111static	void		mseintr_locked(mse_softc_t *sc);
112static	void		msetimeout(void *);
113
114#define	MSE_NBLOCKIO(dev)	(dev2unit(dev) != 0)
115
116#define	MSEPRI	(PZERO + 3)
117
118int
119mse_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
157int
158mse_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 */
191static	int
192mseopen(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 */
227static	int
228mseclose(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 */
245static	int
246mseread(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);
312out:
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 */
328static int
329mseioctl(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 */
448static	int
449msepoll(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 */
470static void
471msetimeout(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 */
491static void
492mseintr(void *arg)
493{
494	mse_softc_t *sc = arg;
495
496	MSE_LOCK(sc);
497	mseintr_locked(sc);
498	MSE_UNLOCK(sc);
499}
500
501static void
502mseintr_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