1 /* $NetBSD: ms_pckbport.c,v 1.1 2004/03/13 17:31:33 bjh21 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.1 2004/03/13 17:31:33 bjh21 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/malloc.h> 43 #include <sys/select.h> 44 #include <sys/proc.h> 45 #include <sys/signalvar.h> 46 47 #include <machine/autoconf.h> 48 #include <machine/bus.h> 49 #include <machine/intr.h> 50 51 #include <dev/pckbport/pckbportvar.h> 52 #include <dev/pckbport/pmsreg.h> 53 54 #include <machine/vuid_event.h> 55 #include <dev/sun/event_var.h> 56 #include <dev/sun/msvar.h> 57 58 /* 59 * NB: we {re,ab}use ms_softc input translator state and ignore its 60 * zs-related members. Not quite clean, but what the heck. 61 */ 62 struct ms_pckbport_softc { 63 struct ms_softc sc_ms; 64 65 /* pckbport attachment */ 66 pckbport_tag_t sc_kbctag; 67 pckbport_slot_t sc_kbcslot; 68 69 int sc_enabled; /* input enabled? */ 70 }; 71 72 static int ms_pckbport_match(struct device *, struct cfdata *, void *); 73 static void ms_pckbport_attach(struct device *, struct device *, void *); 74 75 CFATTACH_DECL(ms_pckbport, sizeof(struct ms_pckbport_softc), 76 ms_pckbport_match, ms_pckbport_attach, NULL, NULL); 77 78 79 static int ms_pckbport_iopen(struct device *, int); 80 static int ms_pckbport_iclose(struct device *, int); 81 static void ms_pckbport_input(void *, int); 82 83 84 static int 85 ms_pckbport_match(parent, cf, aux) 86 struct device *parent; 87 struct cfdata *cf; 88 void *aux; 89 { 90 struct pckbport_attach_args *pa = aux; 91 92 return (pa->pa_slot == PCKBPORT_AUX_SLOT); 93 } 94 95 96 static void 97 ms_pckbport_attach(parent, self, aux) 98 struct device *parent, *self; 99 void *aux; 100 { 101 struct ms_pckbport_softc *sc = (struct ms_pckbport_softc *)self; 102 struct ms_softc *ms = &sc->sc_ms; 103 struct pckbport_attach_args *pa = aux; 104 105 u_char cmd[1], resp[2]; 106 int res; 107 108 /* save our pckbport attachment */ 109 sc->sc_kbctag = pa->pa_tag; 110 sc->sc_kbcslot = pa->pa_slot; 111 112 /* Hooks called by upper layer on device open/close */ 113 ms->ms_deviopen = ms_pckbport_iopen; 114 ms->ms_deviclose = ms_pckbport_iclose; 115 116 printf("\n"); 117 118 /* reset the device */ 119 cmd[0] = PMS_RESET; 120 res = pckbport_poll_cmd(sc->sc_kbctag, sc->sc_kbcslot, 121 cmd, 1, 2, resp, 1); 122 #ifdef DIAGNOSTIC 123 if (res || resp[0] != PMS_RSTDONE || resp[1] != 0) { 124 printf("ms_pckbport_attach: reset error\n"); 125 /* return; */ 126 } 127 #endif 128 129 pckbport_set_inputhandler(sc->sc_kbctag, sc->sc_kbcslot, 130 ms_pckbport_input, sc, ms->ms_dev.dv_xname); 131 132 /* no interrupts until device is actually opened */ 133 cmd[0] = PMS_DEV_DISABLE; 134 res = pckbport_poll_cmd(sc->sc_kbctag, sc->sc_kbcslot, cmd, 135 1, 0, 0, 0); 136 if (res) 137 printf("ms_pckbport_attach: failed to disable interrupts\n"); 138 pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0); 139 } 140 141 142 static int 143 ms_pckbport_iopen(self, flags) 144 struct device *self; 145 int flags; 146 { 147 struct ms_pckbport_softc *sc = (struct ms_pckbport_softc *)self; 148 struct ms_softc *ms = &sc->sc_ms; 149 u_char cmd[1]; 150 int res; 151 152 ms->ms_byteno = 0; 153 ms->ms_dx = ms->ms_dy = 0; 154 ms->ms_ub = ms->ms_mb = 0; 155 156 pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 1); 157 158 cmd[0] = PMS_DEV_ENABLE; 159 res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, 160 cmd, 1, 0, 1, NULL); 161 if (res) { 162 printf("pms_enable: command error\n"); 163 return (res); 164 } 165 166 sc->sc_enabled = 1; 167 return (0); 168 } 169 170 171 static int 172 ms_pckbport_iclose(self, flags) 173 struct device *self; 174 int flags; 175 { 176 struct ms_pckbport_softc *sc = (struct ms_pckbport_softc *)self; 177 u_char cmd[1]; 178 int res; 179 180 cmd[0] = PMS_DEV_DISABLE; 181 res = pckbport_enqueue_cmd(sc->sc_kbctag, sc->sc_kbcslot, 182 cmd, 1, 0, 1, NULL); 183 if (res) 184 printf("pms_disable: command error\n"); 185 186 pckbport_slot_enable(sc->sc_kbctag, sc->sc_kbcslot, 0); 187 188 sc->sc_enabled = 0; 189 return (0); 190 } 191 192 193 /* Masks for the first byte of a PS/2 mouse packet */ 194 #define PS2LBUTMASK 0x01 195 #define PS2RBUTMASK 0x02 196 #define PS2MBUTMASK 0x04 197 198 /* 199 * Got a receive interrupt - pckbport wants to give us a byte. 200 */ 201 static void 202 ms_pckbport_input(vsc, data) 203 void *vsc; 204 int data; 205 { 206 struct ms_pckbport_softc *sc = vsc; 207 struct ms_softc *ms = &sc->sc_ms; 208 struct firm_event *fe; 209 int mb, ub, d, get, put, any; 210 211 /* map changed buttons mask to the highest bit */ 212 static const char to_one[] = { 1, 2, 2, 4, 4, 4, 4 }; 213 214 /* map bits to mouse buttons */ 215 static const int to_id[] = { MS_LEFT, MS_MIDDLE, 0, MS_RIGHT }; 216 217 if (!sc->sc_enabled) { 218 /* Interrupts are not expected. Discard the byte. */ 219 return; 220 } 221 222 switch (ms->ms_byteno) { 223 224 case 0: 225 if ((data & 0xc0) == 0) { /* no ovfl, bit 3 == 1 too? */ 226 ms->ms_mb = 227 ((data & PS2LBUTMASK) ? 0x1 : 0) | 228 ((data & PS2MBUTMASK) ? 0x2 : 0) | 229 ((data & PS2RBUTMASK) ? 0x4 : 0) ; 230 ++ms->ms_byteno; 231 } 232 return; 233 234 case 1: 235 ms->ms_dx += (int8_t)data; 236 ++ms->ms_byteno; 237 return; 238 239 case 2: 240 ms->ms_dy += (int8_t)data; 241 ms->ms_byteno = 0; 242 break; /* last byte processed, report changes */ 243 } 244 245 any = 0; 246 get = ms->ms_events.ev_get; 247 put = ms->ms_events.ev_put; 248 fe = &ms->ms_events.ev_q[put]; 249 250 /* NEXT prepares to put the next event, backing off if necessary */ 251 #define NEXT do { \ 252 if ((++put) % EV_QSIZE == get) { \ 253 --put; \ 254 goto out; \ 255 } \ 256 } while (0) 257 258 /* ADVANCE completes the `put' of the event */ 259 #define ADVANCE do { \ 260 ++fe; \ 261 if (put >= EV_QSIZE) { \ 262 put = 0; \ 263 fe = &ms->ms_events.ev_q[0]; \ 264 } \ 265 any = 1; \ 266 } while (0) 267 268 ub = ms->ms_ub; /* old buttons state */ 269 mb = ms->ms_mb; /* new buttons state */ 270 while ((d = mb ^ ub) != 0) { 271 /* 272 * Mouse button change. Convert up to three state changes 273 * to the `first' change, and drop it into the event queue. 274 */ 275 NEXT; 276 d = to_one[d - 1]; /* from 1..7 to {1,2,4} */ 277 fe->id = to_id[d - 1]; /* from {1,2,4} to ID */ 278 fe->value = (mb & d) ? VKEY_DOWN : VKEY_UP; 279 fe->time = time; 280 ADVANCE; 281 ub ^= d; /* reflect the button state change */ 282 } 283 284 if (ms->ms_dx != 0) { 285 NEXT; 286 fe->id = LOC_X_DELTA; 287 fe->value = ms->ms_dx; 288 fe->time = time; 289 ADVANCE; 290 ms->ms_dx = 0; 291 } 292 293 if (ms->ms_dy != 0) { 294 NEXT; 295 fe->id = LOC_Y_DELTA; 296 fe->value = ms->ms_dy; 297 fe->time = time; 298 ADVANCE; 299 ms->ms_dy = 0; 300 } 301 302 out: 303 if (any) { 304 ms->ms_ub = ub; /* save button state */ 305 ms->ms_events.ev_put = put; 306 EV_WAKEUP(&ms->ms_events); 307 } 308 } 309