1/*	$NetBSD: ms_pckbport.c,v 1.8 2011/09/08 15:36:42 jakllsch Exp $ */
2
3/*
4 * Copyright (c) 2002 Valeriy E. Ushakov
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 * 3. The name of the author may not be used to endorse or promote products
16 *    derived from this software without specific prior written permission
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: ms_pckbport.c,v 1.8 2011/09/08 15:36:42 jakllsch Exp $");
31
32/*
33 * Attach PS/2 mouse at pckbport aux port
34 * and convert PS/2 mouse protocol to Sun firm events.
35 */
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/conf.h>
40#include <sys/device.h>
41#include <sys/kernel.h>
42#include <sys/select.h>
43#include <sys/proc.h>
44
45#include <machine/autoconf.h>
46#include <sys/bus.h>
47#include <machine/intr.h>
48
49#include <dev/pckbport/pckbportvar.h>
50#include <dev/pckbport/pmsreg.h>
51
52#include <machine/vuid_event.h>
53#include <dev/sun/event_var.h>
54#include <dev/sun/msvar.h>
55
56/*
57 * NB: we {re,ab}use ms_softc input translator state and ignore its
58 * zs-related members.  Not quite clean, but what the heck.
59 */
60struct ms_pckbport_softc {
61	struct ms_softc sc_ms;
62
63	/* pckbport attachment */
64	pckbport_tag_t		sc_kbctag;
65	pckbport_slot_t		sc_kbcslot;
66
67	int sc_enabled;			/* input enabled? */
68};
69
70static int	ms_pckbport_match(device_t, cfdata_t, void *);
71static void	ms_pckbport_attach(device_t, device_t, void *);
72
73CFATTACH_DECL_NEW(ms_pckbport, sizeof(struct ms_pckbport_softc),
74    ms_pckbport_match, ms_pckbport_attach, NULL, NULL);
75
76
77static int	ms_pckbport_iopen(device_t, int);
78static int	ms_pckbport_iclose(device_t, int);
79static void	ms_pckbport_input(void *, int);
80
81
82static int
83ms_pckbport_match(device_t parent, cfdata_t cf, void *aux)
84{
85	struct pckbport_attach_args *pa = aux;
86
87	return (pa->pa_slot == PCKBPORT_AUX_SLOT);
88}
89
90
91static void
92ms_pckbport_attach(device_t parent, device_t self, void *aux)
93{
94	struct ms_pckbport_softc *sc = device_private(self);
95	struct ms_softc *ms = &sc->sc_ms;
96	struct pckbport_attach_args *pa = aux;
97
98	u_char cmd[1], resp[2];
99	int res;
100
101	ms->ms_dev = self;
102
103	/* save our pckbport attachment */
104	sc->sc_kbctag = pa->pa_tag;
105	sc->sc_kbcslot = pa->pa_slot;
106
107	/* Hooks called by upper layer on device open/close */
108	ms->ms_deviopen = ms_pckbport_iopen;
109	ms->ms_deviclose = ms_pckbport_iclose;
110
111	aprint_normal("\n");
112
113	/* reset the device */
114	cmd[0] = PMS_RESET;
115	res = pckbport_poll_cmd(sc->sc_kbctag, sc->sc_kbcslot,
116			     cmd, 1, 2, resp, 1);
117#ifdef DIAGNOSTIC
118	if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) {
119		aprint_error("%s: reset error\n", __func__);
120		/* return; */
121	}
122#endif
123
124	pckbport_set_inputhandler(sc->sc_kbctag, sc->sc_kbcslot,
125			       ms_pckbport_input, sc, device_xname(self));
126
127	/* no interrupts until device is actually opened */
128	cmd[0] = PMS_DEV_DISABLE;
129	res = pckbport_poll_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
130			     1, 0, 0, 0);
131	if (res)
132		aprint_error("%s: failed to disable interrupts\n", __func__);
133	pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0);
134}
135
136
137static int
138ms_pckbport_iopen(device_t self, int flags)
139{
140	struct ms_pckbport_softc *sc = device_private(self);
141	struct ms_softc *ms = &sc->sc_ms;
142	u_char cmd[1];
143	int res;
144
145	ms->ms_byteno = 0;
146	ms->ms_dx = ms->ms_dy = 0;
147	ms->ms_ub = ms->ms_mb = 0;
148
149	pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 1);
150
151	cmd[0] = PMS_DEV_ENABLE;
152	res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot,
153				cmd, 1, 0, 1, NULL);
154	if (res) {
155		printf("%s: command error\n", __func__);
156		return (res);
157	}
158
159	sc->sc_enabled = 1;
160	return (0);
161}
162
163
164static int
165ms_pckbport_iclose(device_t self, int flags)
166{
167	struct ms_pckbport_softc *sc = device_private(self);
168	u_char cmd[1];
169	int res;
170
171	cmd[0] = PMS_DEV_DISABLE;
172	res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot,
173				cmd, 1, 0, 1, NULL);
174	if (res)
175		printf("%s: command error\n", __func__);
176
177	pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0);
178
179	sc->sc_enabled = 0;
180	return (0);
181}
182
183
184/* Masks for the first byte of a PS/2 mouse packet */
185#define PS2LBUTMASK 0x01
186#define PS2RBUTMASK 0x02
187#define PS2MBUTMASK 0x04
188
189/*
190 * Got a receive interrupt - pckbport wants to give us a byte.
191 */
192static void
193ms_pckbport_input(void *vsc, int data)
194{
195	struct ms_pckbport_softc *sc = vsc;
196	struct ms_softc *ms = &sc->sc_ms;
197	struct firm_event *fe;
198	int mb, ub, d, get, put, any;
199
200	/* map changed buttons mask to the highest bit */
201	static const char to_one[] = { 1, 2, 2, 4, 4, 4, 4 };
202
203	/* map bits to mouse buttons */
204	static const int to_id[] = { MS_LEFT, MS_MIDDLE, 0, MS_RIGHT };
205
206	if (!sc->sc_enabled) {
207		/* Interrupts are not expected.  Discard the byte. */
208		return;
209	}
210
211	switch (ms->ms_byteno) {
212
213	case 0:
214		if ((data & 0xc0) == 0) { /* no ovfl, bit 3 == 1 too? */
215			ms->ms_mb =
216				((data & PS2LBUTMASK) ? 0x1 : 0) |
217				((data & PS2MBUTMASK) ? 0x2 : 0) |
218				((data & PS2RBUTMASK) ? 0x4 : 0) ;
219			++ms->ms_byteno;
220		}
221		return;
222
223	case 1:
224		ms->ms_dx += (int8_t)data;
225		++ms->ms_byteno;
226		return;
227
228	case 2:
229		ms->ms_dy += (int8_t)data;
230		ms->ms_byteno = 0;
231		break;		/* last byte processed, report changes */
232	}
233
234	any = 0;
235	get = ms->ms_events.ev_get;
236	put = ms->ms_events.ev_put;
237	fe = &ms->ms_events.ev_q[put];
238
239	/* NEXT prepares to put the next event, backing off if necessary */
240#define	NEXT	do {						\
241			if ((++put) % EV_QSIZE == get) {	\
242				--put;				\
243				goto out;			\
244			}					\
245		} while (0)
246
247	/* ADVANCE completes the `put' of the event */
248#define	ADVANCE do {						\
249			++fe;					\
250			if (put >= EV_QSIZE) {			\
251				put = 0;			\
252				fe = &ms->ms_events.ev_q[0];	\
253			}					\
254			any = 1;				\
255		} while (0)
256
257	ub = ms->ms_ub;		/* old buttons state */
258	mb = ms->ms_mb;		/* new buttons state */
259	while ((d = mb ^ ub) != 0) {
260		/*
261		 * Mouse button change.  Convert up to three state changes
262		 * to the `first' change, and drop it into the event queue.
263		 */
264		NEXT;
265		d = to_one[d - 1];		/* from 1..7 to {1,2,4} */
266		fe->id = to_id[d - 1];		/* from {1,2,4} to ID */
267		fe->value = (mb & d) ? VKEY_DOWN : VKEY_UP;
268		firm_gettime(fe);
269		ADVANCE;
270		ub ^= d;	/* reflect the button state change */
271	}
272
273	if (ms->ms_dx != 0) {
274		NEXT;
275		fe->id = LOC_X_DELTA;
276		fe->value = ms->ms_dx;
277		firm_gettime(fe);
278		ADVANCE;
279		ms->ms_dx = 0;
280	}
281
282	if (ms->ms_dy != 0) {
283		NEXT;
284		fe->id = LOC_Y_DELTA;
285		fe->value = ms->ms_dy;
286		firm_gettime(fe);
287		ADVANCE;
288		ms->ms_dy = 0;
289	}
290
291  out:
292	if (any) {
293		ms->ms_ub = ub;	/* save button state */
294		ms->ms_events.ev_put = put;
295		EV_WAKEUP(&ms->ms_events);
296	}
297}
298