1 /* $NetBSD: ms_pckbport.c,v 1.2 2005/11/16 01:39:27 uwe 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.2 2005/11/16 01:39:27 uwe 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 fe->time = 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 fe->time = 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 fe->time = 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