ms_pckbport.c revision 1.3 1 /* $NetBSD: ms_pckbport.c,v 1.3 2006/06/07 22:38:49 kardel 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.3 2006/06/07 22:38:49 kardel 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(struct device *, struct cfdata *, void *);
71 static void ms_pckbport_attach(struct device *, struct device *, void *);
72
73 CFATTACH_DECL(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(struct device *parent, struct cfdata *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(struct device *parent, struct device *self, void *aux)
93 {
94 struct ms_pckbport_softc *sc = (struct ms_pckbport_softc *)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 /* save our pckbport attachment */
102 sc->sc_kbctag = pa->pa_tag;
103 sc->sc_kbcslot = pa->pa_slot;
104
105 /* Hooks called by upper layer on device open/close */
106 ms->ms_deviopen = ms_pckbport_iopen;
107 ms->ms_deviclose = ms_pckbport_iclose;
108
109 printf("\n");
110
111 /* reset the device */
112 cmd[0] = PMS_RESET;
113 res = pckbport_poll_cmd(sc->sc_kbctag, sc->sc_kbcslot,
114 cmd, 1, 2, resp, 1);
115 #ifdef DIAGNOSTIC
116 if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) {
117 printf("ms_pckbport_attach: reset error\n");
118 /* return; */
119 }
120 #endif
121
122 pckbport_set_inputhandler(sc->sc_kbctag, sc->sc_kbcslot,
123 ms_pckbport_input, sc, ms->ms_dev.dv_xname);
124
125 /* no interrupts until device is actually opened */
126 cmd[0] = PMS_DEV_DISABLE;
127 res = pckbport_poll_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd,
128 1, 0, 0, 0);
129 if (res)
130 printf("ms_pckbport_attach: failed to disable interrupts\n");
131 pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0);
132 }
133
134
135 static int
136 ms_pckbport_iopen(struct device *self, int flags)
137 {
138 struct ms_pckbport_softc *sc = (struct ms_pckbport_softc *)self;
139 struct ms_softc *ms = &sc->sc_ms;
140 u_char cmd[1];
141 int res;
142
143 ms->ms_byteno = 0;
144 ms->ms_dx = ms->ms_dy = 0;
145 ms->ms_ub = ms->ms_mb = 0;
146
147 pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 1);
148
149 cmd[0] = PMS_DEV_ENABLE;
150 res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot,
151 cmd, 1, 0, 1, NULL);
152 if (res) {
153 printf("pms_enable: command error\n");
154 return (res);
155 }
156
157 sc->sc_enabled = 1;
158 return (0);
159 }
160
161
162 static int
163 ms_pckbport_iclose(struct device *self, int flags)
164 {
165 struct ms_pckbport_softc *sc = (struct ms_pckbport_softc *)self;
166 u_char cmd[1];
167 int res;
168
169 cmd[0] = PMS_DEV_DISABLE;
170 res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot,
171 cmd, 1, 0, 1, NULL);
172 if (res)
173 printf("pms_disable: command error\n");
174
175 pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0);
176
177 sc->sc_enabled = 0;
178 return (0);
179 }
180
181
182 /* Masks for the first byte of a PS/2 mouse packet */
183 #define PS2LBUTMASK 0x01
184 #define PS2RBUTMASK 0x02
185 #define PS2MBUTMASK 0x04
186
187 /*
188 * Got a receive interrupt - pckbport wants to give us a byte.
189 */
190 static void
191 ms_pckbport_input(void *vsc, int data)
192 {
193 struct ms_pckbport_softc *sc = vsc;
194 struct ms_softc *ms = &sc->sc_ms;
195 struct firm_event *fe;
196 int mb, ub, d, get, put, any;
197
198 /* map changed buttons mask to the highest bit */
199 static const char to_one[] = { 1, 2, 2, 4, 4, 4, 4 };
200
201 /* map bits to mouse buttons */
202 static const int to_id[] = { MS_LEFT, MS_MIDDLE, 0, MS_RIGHT };
203
204 if (!sc->sc_enabled) {
205 /* Interrupts are not expected. Discard the byte. */
206 return;
207 }
208
209 switch (ms->ms_byteno) {
210
211 case 0:
212 if ((data & 0xc0) == 0) { /* no ovfl, bit 3 == 1 too? */
213 ms->ms_mb =
214 ((data & PS2LBUTMASK) ? 0x1 : 0) |
215 ((data & PS2MBUTMASK) ? 0x2 : 0) |
216 ((data & PS2RBUTMASK) ? 0x4 : 0) ;
217 ++ms->ms_byteno;
218 }
219 return;
220
221 case 1:
222 ms->ms_dx += (int8_t)data;
223 ++ms->ms_byteno;
224 return;
225
226 case 2:
227 ms->ms_dy += (int8_t)data;
228 ms->ms_byteno = 0;
229 break; /* last byte processed, report changes */
230 }
231
232 any = 0;
233 get = ms->ms_events.ev_get;
234 put = ms->ms_events.ev_put;
235 fe = &ms->ms_events.ev_q[put];
236
237 /* NEXT prepares to put the next event, backing off if necessary */
238 #define NEXT do { \
239 if ((++put) % EV_QSIZE == get) { \
240 --put; \
241 goto out; \
242 } \
243 } while (0)
244
245 /* ADVANCE completes the `put' of the event */
246 #define ADVANCE do { \
247 ++fe; \
248 if (put >= EV_QSIZE) { \
249 put = 0; \
250 fe = &ms->ms_events.ev_q[0]; \
251 } \
252 any = 1; \
253 } while (0)
254
255 ub = ms->ms_ub; /* old buttons state */
256 mb = ms->ms_mb; /* new buttons state */
257 while ((d = mb ^ ub) != 0) {
258 /*
259 * Mouse button change. Convert up to three state changes
260 * to the `first' change, and drop it into the event queue.
261 */
262 NEXT;
263 d = to_one[d - 1]; /* from 1..7 to {1,2,4} */
264 fe->id = to_id[d - 1]; /* from {1,2,4} to ID */
265 fe->value = (mb & d) ? VKEY_DOWN : VKEY_UP;
266 getmicrotime(&fe->time);
267 ADVANCE;
268 ub ^= d; /* reflect the button state change */
269 }
270
271 if (ms->ms_dx != 0) {
272 NEXT;
273 fe->id = LOC_X_DELTA;
274 fe->value = ms->ms_dx;
275 getmicrotime(&fe->time);
276 ADVANCE;
277 ms->ms_dx = 0;
278 }
279
280 if (ms->ms_dy != 0) {
281 NEXT;
282 fe->id = LOC_Y_DELTA;
283 fe->value = ms->ms_dy;
284 getmicrotime(&fe->time);
285 ADVANCE;
286 ms->ms_dy = 0;
287 }
288
289 out:
290 if (any) {
291 ms->ms_ub = ub; /* save button state */
292 ms->ms_events.ev_put = put;
293 EV_WAKEUP(&ms->ms_events);
294 }
295 }
296