1/* $NetBSD: ms_pckbport.c,v 1.8 2011/09/08 15:36:42 jakllsch 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.8 2011/09/08 15:36:42 jakllsch 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 <sys/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 */ 60struct 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 70static int ms_pckbport_match(device_t, cfdata_t, void *); 71static void ms_pckbport_attach(device_t, device_t, void *); 72 73CFATTACH_DECL_NEW(ms_pckbport, sizeof(struct ms_pckbport_softc), 74 ms_pckbport_match, ms_pckbport_attach, NULL, NULL); 75 76 77static int ms_pckbport_iopen(device_t, int); 78static int ms_pckbport_iclose(device_t, int); 79static void ms_pckbport_input(void *, int); 80 81 82static int 83ms_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 91static void 92ms_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 137static int 138ms_pckbport_iopen(device_t 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("%s: command error\n", __func__); 156 return (res); 157 } 158 159 sc->sc_enabled = 1; 160 return (0); 161} 162 163 164static int 165ms_pckbport_iclose(device_t 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("%s: command error\n", __func__); 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 */ 192static void 193ms_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