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