1/*	$NetBSD: ms.c,v 1.23 2009/03/14 15:36:03 dsl Exp $	*/
2
3/*
4 * Copyright (c) 1995 Leo Weppelman.
5 * All rights reserved.
6 *
7 * based on:
8 *
9 * Copyright (c) 1992, 1993
10 *	The Regents of the University of California.  All rights reserved.
11 *
12 * This software was developed by the Computer Systems Engineering group
13 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
14 * contributed to Berkeley.
15 *
16 * All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Lawrence Berkeley Laboratory.
20 *
21 * Redistribution and use in source and binary forms, with or without
22 * modification, are permitted provided that the following conditions
23 * are met:
24 * 1. Redistributions of source code must retain the above copyright
25 *    notice, this list of conditions and the following disclaimer.
26 * 2. Redistributions in binary form must reproduce the above copyright
27 *    notice, this list of conditions and the following disclaimer in the
28 *    documentation and/or other materials provided with the distribution.
29 * 3. Neither the name of the University nor the names of its contributors
30 *    may be used to endorse or promote products derived from this software
31 *    without specific prior written permission.
32 *
33 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
34 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
37 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 * SUCH DAMAGE.
44 *
45 *	@(#)ms.c	8.1 (Berkeley) 6/11/93
46 *
47 * Header: ms.c,v 1.5 92/11/26 01:28:47 torek Exp  (LBL)
48 */
49
50/*
51 * Mouse driver.
52 */
53
54#include <sys/cdefs.h>
55__KERNEL_RCSID(0, "$NetBSD: ms.c,v 1.23 2009/03/14 15:36:03 dsl Exp $");
56
57#include <sys/param.h>
58#include <sys/conf.h>
59#include <sys/ioctl.h>
60#include <sys/kernel.h>
61#include <sys/proc.h>
62#include <sys/systm.h>
63#include <sys/callout.h>
64#include <sys/tty.h>
65#include <sys/signalvar.h>
66
67#include <machine/msioctl.h>
68#include <atari/dev/event_var.h>
69#include <atari/dev/vuid_event.h>
70#include <atari/dev/kbdvar.h>
71#include <atari/dev/msvar.h>
72
73#include "mouse.h"
74#if NMOUSE > 0
75
76/* there's really no more physical ports on an atari. */
77#if NMOUSE > 1
78#undef NMOUSE
79#define NMOUSE 1
80#endif
81
82typedef void	(*FPV)(void *);
83
84static struct ms_softc	ms_softc[NMOUSE];
85
86dev_type_open(msopen);
87dev_type_close(msclose);
88dev_type_read(msread);
89dev_type_ioctl(msioctl);
90dev_type_poll(mspoll);
91dev_type_kqfilter(mskqfilter);
92
93const struct cdevsw ms_cdevsw = {
94	msopen, msclose, msread, nowrite, msioctl,
95	nostop, notty, mspoll, nommap, mskqfilter,
96};
97
98static	void	ms_3b_delay(struct ms_softc *);
99
100int
101mouseattach(int cnt)
102{
103	printf("1 mouse configured\n");
104	ms_softc[0].ms_emul3b = 1;
105	callout_init(&ms_softc[0].ms_delay_ch, 0);
106	return(NMOUSE);
107}
108
109static void
110ms_3b_delay(struct ms_softc *ms)
111{
112	REL_MOUSE	rel_ms;
113
114	rel_ms.id = TIMEOUT_ID;
115	rel_ms.dx = rel_ms.dy = 0;
116	mouse_soft(&rel_ms, sizeof(rel_ms), KBD_TIMEO_PKG);
117}
118/*
119 * Note that we are called from the keyboard software interrupt!
120 */
121void
122mouse_soft(REL_MOUSE *rel_ms, int size, int type)
123{
124	struct ms_softc		*ms = &ms_softc[0];
125	struct firm_event	*fe, *fe2;
126	REL_MOUSE		fake_mouse;
127	int			get, put;
128	int			sps;
129	u_char			mbut, bmask;
130	int			flush_buttons;
131
132	if (ms->ms_events.ev_io == NULL)
133		return;
134
135	switch (type) {
136	    case KBD_JOY1_PKG:
137		/*
138		 * Ignore if in emulation mode
139		 */
140		if (ms->ms_emul3b)
141			return;
142
143		/*
144		 * There are some mice that have their middle button
145		 * wired to the 'up' bit of joystick 1....
146		 * Simulate a mouse packet with dx = dy = 0, the middle
147		 * button state set by UP and the other buttons unchanged.
148		 * Flush all button changes.
149		 */
150		flush_buttons = 1;
151		fake_mouse.id = (rel_ms->dx & 1 ? 4 : 0) | (ms->ms_buttons & 3);
152		fake_mouse.dx = fake_mouse.dy = 0;
153		rel_ms = &fake_mouse;
154		break;
155	    case KBD_TIMEO_PKG:
156		/*
157		 * Timeout package. No button changes and no movement.
158		 * Flush all button changes.
159		 */
160		flush_buttons = 1;
161		fake_mouse.id = ms->ms_buttons;
162		fake_mouse.dx = fake_mouse.dy = 0;
163		rel_ms = &fake_mouse;
164		break;
165	    case KBD_RMS_PKG:
166		/*
167		 * Normal mouse package. Always copy the middle button
168		 * status. The emulation code decides if button changes
169		 * must be flushed.
170		 */
171		rel_ms->id = (ms->ms_buttons & 4) | (rel_ms->id & 3);
172		flush_buttons = (ms->ms_emul3b) ? 0 : 1;
173		break;
174	    default:
175		return;
176	}
177
178	sps = splev();
179	get = ms->ms_events.ev_get;
180	put = ms->ms_events.ev_put;
181	fe  = &ms->ms_events.ev_q[put];
182
183	if ((type != KBD_TIMEO_PKG) && ms->ms_emul3b && ms->ms_bq_idx)
184		callout_stop(&ms->ms_delay_ch);
185
186	/*
187	 * Button states are encoded in the lower 3 bits of 'id'
188	 */
189	if (!(mbut = (rel_ms->id ^ ms->ms_buttons)) && (put != get)) {
190		/*
191		 * Compact dx/dy messages. Always generate an event when
192		 * a button is pressed or the event queue is empty.
193		 */
194		ms->ms_dx += rel_ms->dx;
195		ms->ms_dy += rel_ms->dy;
196		goto out;
197	}
198	rel_ms->dx += ms->ms_dx;
199	rel_ms->dy += ms->ms_dy;
200	ms->ms_dx = ms->ms_dy = 0;
201
202	/*
203	 * Output location events _before_ button events ie. make sure
204	 * the button is pressed at the correct location.
205	 */
206	if (rel_ms->dx) {
207		if ((++put) % EV_QSIZE == get) {
208			put--;
209			goto out;
210		}
211		fe->id    = LOC_X_DELTA;
212		fe->value = rel_ms->dx;
213		firm_gettime(fe);
214		if (put >= EV_QSIZE) {
215			put = 0;
216			fe  = &ms->ms_events.ev_q[0];
217		}
218		else fe++;
219	}
220	if (rel_ms->dy) {
221		if ((++put) % EV_QSIZE == get) {
222			put--;
223			goto out;
224		}
225		fe->id    = LOC_Y_DELTA;
226		fe->value = rel_ms->dy;
227		firm_gettime(fe);
228		if (put >= EV_QSIZE) {
229			put = 0;
230			fe  = &ms->ms_events.ev_q[0];
231		}
232		else fe++;
233	}
234	if (mbut && (type != KBD_TIMEO_PKG)) {
235		for (bmask = 1; bmask < 0x08; bmask <<= 1) {
236			if (!(mbut & bmask))
237				continue;
238			fe2 = &ms->ms_bq[ms->ms_bq_idx++];
239			if (bmask == 1)
240				fe2->id = MS_RIGHT;
241			else if (bmask == 2)
242				fe2->id = MS_LEFT;
243			else fe2->id = MS_MIDDLE;
244			fe2->value = rel_ms->id & bmask ? VKEY_DOWN : VKEY_UP;
245			firm_gettime(fe2);
246		}
247	}
248
249	/*
250	 * Handle 3rd button emulation.
251	 */
252	if (ms->ms_emul3b && ms->ms_bq_idx && (type != KBD_TIMEO_PKG)) {
253		/*
254		 * If the middle button is pressed, any change to
255		 * one of the other buttons releases all.
256		 */
257		if ((ms->ms_buttons & 4) && (mbut & 3)) {
258			ms->ms_bq[0].id = MS_MIDDLE;
259			ms->ms_bq_idx   = 1;
260			rel_ms->id      = 0;
261			flush_buttons   = 1;
262			goto out;
263		}
264	    	if (ms->ms_bq_idx == 2) {
265			if (ms->ms_bq[0].value == ms->ms_bq[1].value) {
266				/* Must be 2 button presses! */
267				ms->ms_bq[0].id = MS_MIDDLE;
268				ms->ms_bq_idx   = 1;
269				rel_ms->id      = 7;
270			}
271		}
272		else if (ms->ms_bq[0].value == VKEY_DOWN) {
273			callout_reset(&ms->ms_delay_ch, 10,
274			    (FPV)ms_3b_delay, (void *)ms);
275			goto out;
276		}
277		flush_buttons   = 1;
278	}
279out:
280	if (flush_buttons) {
281		int	i;
282
283		for (i = 0; i < ms->ms_bq_idx; i++) {
284			if ((++put) % EV_QSIZE == get) {
285				ms->ms_bq_idx = 0;
286				put--;
287				goto out;
288			}
289			*fe = ms->ms_bq[i];
290			if (put >= EV_QSIZE) {
291				put = 0;
292				fe  = &ms->ms_events.ev_q[0];
293			}
294			else fe++;
295		}
296		ms->ms_bq_idx = 0;
297	}
298	ms->ms_events.ev_put = put;
299	ms->ms_buttons       = rel_ms->id;
300	splx(sps);
301	EV_WAKEUP(&ms->ms_events);
302}
303
304int
305msopen(dev_t dev, int flags, int mode, struct lwp *l)
306{
307	u_char		report_ms_joy[] = { 0x14, 0x08 };
308	struct ms_softc	*ms;
309	int		unit;
310
311	unit = minor(dev);
312	ms   = &ms_softc[unit];
313
314	if (unit >= NMOUSE)
315		return(EXDEV);
316
317	if (ms->ms_events.ev_io)
318		return(EBUSY);
319
320	ms->ms_events.ev_io = l->l_proc;
321	ms->ms_dx = ms->ms_dy = 0;
322	ms->ms_buttons = 0;
323	ms->ms_bq[0].id = ms->ms_bq[1].id = 0;
324	ms->ms_bq_idx = 0;
325	ev_init(&ms->ms_events);	/* may cause sleep */
326
327	/*
328	 * Enable mouse reporting.
329	 */
330	kbd_write(report_ms_joy, sizeof(report_ms_joy));
331	return(0);
332}
333
334int
335msclose(dev_t dev, int flags, int mode, struct lwp *l)
336{
337	u_char		disable_ms_joy[] = { 0x12, 0x1a };
338	int		unit;
339	struct ms_softc	*ms;
340
341	unit = minor (dev);
342	ms   = &ms_softc[unit];
343
344	/*
345	 * Turn off mouse interrogation.
346	 */
347	kbd_write(disable_ms_joy, sizeof(disable_ms_joy));
348	ev_fini(&ms->ms_events);
349	ms->ms_events.ev_io = NULL;
350	return(0);
351}
352
353int
354msread(dev_t dev, struct uio *uio, int flags)
355{
356	struct ms_softc *ms;
357
358	ms = &ms_softc[minor(dev)];
359	return(ev_read(&ms->ms_events, uio, flags));
360}
361
362int
363msioctl(dev_t dev, u_long cmd, register void * data, int flag, struct lwp *l)
364{
365	struct ms_softc *ms;
366	int		unit;
367
368	unit = minor(dev);
369	ms = &ms_softc[unit];
370
371	switch (cmd) {
372	case  MIOCS3B_EMUL:
373		ms->ms_emul3b = (*(int *)data != 0) ? 1 : 0;
374		return (0);
375	case  MIOCG3B_EMUL:
376		*(int *)data = ms->ms_emul3b;
377		return (0);
378	case FIONBIO:		/* we will remove this someday (soon???) */
379		return(0);
380	case FIOASYNC:
381		ms->ms_events.ev_async = *(int *)data != 0;
382		return(0);
383	case FIOSETOWN:
384		if (-*(int *)data != ms->ms_events.ev_io->p_pgid
385		    && *(int *)data != ms->ms_events.ev_io->p_pid)
386			return(EPERM);
387		return(0);
388	case TIOCSPGRP:
389		if (*(int *)data != ms->ms_events.ev_io->p_pgid)
390			return(EPERM);
391		return(0);
392	case VUIDGFORMAT:	/* we only do firm_events */
393		*(int *)data = VUID_FIRM_EVENT;
394		return(0);
395	case VUIDSFORMAT:
396		if (*(int *)data != VUID_FIRM_EVENT)
397			return(EINVAL);
398		return(0);
399	}
400	return(ENOTTY);
401}
402
403int
404mspoll(dev_t dev, int events, struct lwp *l)
405{
406	struct ms_softc *ms;
407
408	ms = &ms_softc[minor(dev)];
409	return(ev_poll(&ms->ms_events, events, l));
410}
411
412int
413mskqfilter(dev_t dev, struct knote *kn)
414{
415	struct ms_softc *ms;
416
417	ms = &ms_softc[minor(dev)];
418	return (ev_kqfilter(&ms->ms_events, kn));
419}
420#endif /* NMOUSE > 0 */
421