xref: /netbsd/sys/arch/sparc/dev/ms_pckbport.c (revision 6550d01e)
1 /*	$NetBSD: ms_pckbport.c,v 1.5 2009/01/17 14:35:06 he 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.5 2009/01/17 14:35:06 he 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 <machine/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  */
60 struct 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 
70 static int	ms_pckbport_match(device_t, cfdata_t, void *);
71 static void	ms_pckbport_attach(device_t, device_t, void *);
72 
73 CFATTACH_DECL_NEW(ms_pckbport, sizeof(struct ms_pckbport_softc),
74     ms_pckbport_match, ms_pckbport_attach, NULL, NULL);
75 
76 
77 static int	ms_pckbport_iopen(struct device *, int);
78 static int	ms_pckbport_iclose(struct device *, int);
79 static void	ms_pckbport_input(void *, int);
80 
81 
82 static int
83 ms_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 
91 static void
92 ms_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 
137 static int
138 ms_pckbport_iopen(struct device *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("pms_enable: command error\n");
156 		return (res);
157 	}
158 
159 	sc->sc_enabled = 1;
160 	return (0);
161 }
162 
163 
164 static int
165 ms_pckbport_iclose(struct device *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("pms_disable: command error\n");
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  */
192 static void
193 ms_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