1 /* $OpenBSD: sunkbd.c,v 1.25 2009/01/12 21:11:58 miod Exp $ */ 2 3 /* 4 * Copyright (c) 2002 Jason L. Wright (jason@thought.net) 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 * 28 * Effort sponsored in part by the Defense Advanced Research Projects 29 * Agency (DARPA) and Air Force Research Laboratory, Air Force 30 * Materiel Command, USAF, under agreement number F30602-01-2-0537. 31 * 32 */ 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/device.h> 37 #include <sys/kernel.h> 38 #include <sys/timeout.h> 39 40 #include <dev/wscons/wsconsio.h> 41 #include <dev/wscons/wskbdvar.h> 42 #ifdef WSDISPLAY_COMPAT_RAWKBD 43 #include <dev/wscons/wskbdraw.h> 44 #endif 45 46 #include <dev/sun/sunkbdreg.h> 47 #include <dev/sun/sunkbdvar.h> 48 49 #ifdef __sparc64__ 50 #define NTCTRL 0 51 #else 52 #include "tctrl.h" 53 #endif 54 55 #if NTCTRL > 0 56 #include <sparc/dev/tctrlvar.h> /* XXX for tadpole_bell() */ 57 #endif 58 59 void sunkbd_bell(struct sunkbd_softc *, u_int, u_int, u_int); 60 void sunkbd_decode5(u_int8_t, u_int *, int *); 61 int sunkbd_enable(void *, int); 62 int sunkbd_getleds(struct sunkbd_softc *); 63 int sunkbd_ioctl(void *, u_long, caddr_t, int, struct proc *); 64 void sunkbd_rawrepeat(void *); 65 void sunkbd_setleds(void *, int); 66 67 struct wskbd_accessops sunkbd_accessops = { 68 sunkbd_enable, 69 sunkbd_setleds, 70 sunkbd_ioctl 71 }; 72 73 void 74 sunkbd_attach(struct sunkbd_softc *sc, struct wskbddev_attach_args *waa) 75 { 76 #ifdef WSDISPLAY_COMPAT_RAWKBD 77 timeout_set(&sc->sc_rawrepeat_tmo, sunkbd_rawrepeat, sc); 78 #endif 79 80 if (ISTYPE5(sc->sc_layout)) 81 sc->sc_decode = sunkbd_decode5; 82 else 83 sc->sc_decode = sunkbd_decode; 84 85 sc->sc_wskbddev = config_found((struct device *)sc, waa, 86 wskbddevprint); 87 } 88 89 void 90 sunkbd_bell(struct sunkbd_softc *sc, u_int period, u_int pitch, u_int volume) 91 { 92 int ticks, s; 93 u_int8_t c = SKBD_CMD_BELLON; 94 95 #if NTCTRL > 0 96 if (tadpole_bell(period / 10, pitch, volume) != 0) 97 return; 98 #endif 99 100 s = spltty(); 101 if (sc->sc_bellactive) { 102 if (sc->sc_belltimeout == 0) 103 timeout_del(&sc->sc_bellto); 104 } 105 if (pitch == 0 || period == 0) { 106 sunkbd_bellstop(sc); 107 splx(s); 108 return; 109 } 110 if (sc->sc_bellactive == 0) { 111 ticks = (period * hz) / 1000; 112 if (ticks <= 0) 113 ticks = 1; 114 115 sc->sc_bellactive = 1; 116 sc->sc_belltimeout = 1; 117 (*sc->sc_sendcmd)(sc, &c, 1); 118 timeout_add(&sc->sc_bellto, ticks); 119 } 120 splx(s); 121 } 122 123 void 124 sunkbd_bellstop(void *v) 125 { 126 struct sunkbd_softc *sc = v; 127 int s; 128 u_int8_t c; 129 130 s = spltty(); 131 sc->sc_belltimeout = 0; 132 c = SKBD_CMD_BELLOFF; 133 (*sc->sc_sendcmd)(v, &c, 1); 134 sc->sc_bellactive = 0; 135 splx(s); 136 } 137 138 void 139 sunkbd_decode(u_int8_t c, u_int *type, int *value) 140 { 141 switch (c) { 142 case SKBD_RSP_IDLE: 143 *type = WSCONS_EVENT_ALL_KEYS_UP; 144 *value = 0; 145 break; 146 default: 147 *type = (c & 0x80) ? 148 WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; 149 *value = c & 0x7f; 150 break; 151 } 152 } 153 154 void 155 sunkbd_decode5(u_int8_t c, u_int *type, int *value) 156 { 157 sunkbd_decode(c, type, value); 158 /* 159 * Scancode 0x2d is KS_KP_Equal on type 4, and KS_AudioMute on 160 * type 5. Rather than provide two distinct maps, we remap the 161 * scancode here. 162 */ 163 if (*value == 0x2d) 164 *value = 0x7f; 165 } 166 167 int 168 sunkbd_enable(void *v, int on) 169 { 170 return (0); 171 } 172 173 int 174 sunkbd_getleds(struct sunkbd_softc *sc) 175 { 176 return (sc->sc_leds); 177 } 178 179 void 180 sunkbd_input(struct sunkbd_softc *sc, u_int8_t *buf, u_int buflen) 181 { 182 u_int type; 183 int value; 184 int s; 185 186 if (sc->sc_wskbddev == NULL) 187 return; /* why bother */ 188 189 #ifdef WSDISPLAY_COMPAT_RAWKBD 190 if (sc->sc_rawkbd) { 191 u_char rbuf[SUNKBD_MAX_INPUT_SIZE * 2]; 192 int c, rlen, npress; 193 194 timeout_del(&sc->sc_rawrepeat_tmo); 195 196 npress = rlen = 0; 197 while (buflen-- != 0) { 198 (*sc->sc_decode)(*buf++, &type, &value); 199 c = sunkbd_rawmap[value]; 200 if (c == RAWKEY_Null) 201 continue; 202 /* fake extended scancode if necessary */ 203 if (c & 0x80) 204 rbuf[rlen++] = 0xe0; 205 rbuf[rlen] = c & 0x7f; 206 if (type == WSCONS_EVENT_KEY_UP) 207 rbuf[rlen] |= 0x80; 208 else { 209 /* remember down keys for autorepeat */ 210 if (c & 0x80) 211 sc->sc_rep[npress++] = 0xe0; 212 sc->sc_rep[npress++] = c & 0x7f; 213 } 214 rlen++; 215 } 216 217 s = spltty(); 218 wskbd_rawinput(sc->sc_wskbddev, rbuf, rlen); 219 splx(s); 220 sc->sc_nrep = npress; 221 if (npress != 0) 222 timeout_add_msec(&sc->sc_rawrepeat_tmo, REP_DELAY1); 223 } else 224 #endif 225 { 226 s = spltty(); 227 while (buflen-- != 0) { 228 (*sc->sc_decode)(*buf++, &type, &value); 229 wskbd_input(sc->sc_wskbddev, type, value); 230 } 231 splx(s); 232 } 233 } 234 235 int 236 sunkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 237 { 238 struct sunkbd_softc *sc = v; 239 int *d_int = (int *)data; 240 struct wskbd_bell_data *d_bell = (struct wskbd_bell_data *)data; 241 242 switch (cmd) { 243 case WSKBDIO_GTYPE: 244 if (ISTYPE5(sc->sc_layout)) { 245 *d_int = WSKBD_TYPE_SUN5; 246 } else { 247 *d_int = WSKBD_TYPE_SUN; 248 } 249 return (0); 250 case WSKBDIO_SETLEDS: 251 sunkbd_setleds(sc, *d_int); 252 return (0); 253 case WSKBDIO_GETLEDS: 254 *d_int = sunkbd_getleds(sc); 255 return (0); 256 case WSKBDIO_COMPLEXBELL: 257 sunkbd_bell(sc, d_bell->period, d_bell->pitch, d_bell->volume); 258 return (0); 259 #ifdef WSDISPLAY_COMPAT_RAWKBD 260 case WSKBDIO_SETMODE: 261 sc->sc_rawkbd = *(int *)data == WSKBD_RAW; 262 timeout_del(&sc->sc_rawrepeat_tmo); 263 return (0); 264 #endif 265 } 266 267 return (-1); 268 } 269 270 void 271 sunkbd_raw(struct sunkbd_softc *sc, u_int8_t c) 272 { 273 int claimed = 0; 274 275 if (sc->sc_kbdstate == SKBD_STATE_LAYOUT) { 276 sc->sc_kbdstate = SKBD_STATE_GETKEY; 277 sc->sc_layout = c; 278 return; 279 } 280 281 switch (c) { 282 case SKBD_RSP_RESET: 283 sc->sc_kbdstate = SKBD_STATE_RESET; 284 claimed = 1; 285 break; 286 case SKBD_RSP_LAYOUT: 287 sc->sc_kbdstate = SKBD_STATE_LAYOUT; 288 claimed = 1; 289 break; 290 case SKBD_RSP_IDLE: 291 sc->sc_kbdstate = SKBD_STATE_GETKEY; 292 claimed = 1; 293 } 294 295 if (claimed) 296 return; 297 298 switch (sc->sc_kbdstate) { 299 case SKBD_STATE_RESET: 300 sc->sc_kbdstate = SKBD_STATE_GETKEY; 301 if (c < KB_SUN2 || c > KB_SUN4) 302 printf("%s: reset: invalid keyboard type 0x%02x\n", 303 sc->sc_dev.dv_xname, c); 304 else 305 sc->sc_id = c; 306 break; 307 case SKBD_STATE_GETKEY: 308 break; 309 } 310 } 311 312 #ifdef WSDISPLAY_COMPAT_RAWKBD 313 void 314 sunkbd_rawrepeat(void *v) 315 { 316 struct sunkbd_softc *sc = v; 317 int s; 318 319 s = spltty(); 320 wskbd_rawinput(sc->sc_wskbddev, sc->sc_rep, sc->sc_nrep); 321 splx(s); 322 timeout_add_msec(&sc->sc_rawrepeat_tmo, REP_DELAYN); 323 } 324 #endif 325 326 int 327 sunkbd_setclick(struct sunkbd_softc *sc, int click) 328 { 329 u_int8_t c; 330 331 /* Type 2 keyboards do not support keyclick */ 332 if (sc->sc_id == KB_SUN2) 333 return (ENXIO); 334 335 c = click ? SKBD_CMD_CLICKON : SKBD_CMD_CLICKOFF; 336 (*sc->sc_sendcmd)(sc, &c, 1); 337 return (0); 338 } 339 340 void 341 sunkbd_setleds(void *v, int wled) 342 { 343 struct sunkbd_softc *sc = v; 344 u_int8_t sled = 0; 345 u_int8_t cmd[2]; 346 347 sc->sc_leds = wled; 348 349 if (wled & WSKBD_LED_CAPS) 350 sled |= SKBD_LED_CAPSLOCK; 351 if (wled & WSKBD_LED_NUM) 352 sled |= SKBD_LED_NUMLOCK; 353 if (wled & WSKBD_LED_SCROLL) 354 sled |= SKBD_LED_SCROLLLOCK; 355 if (wled & WSKBD_LED_COMPOSE) 356 sled |= SKBD_LED_COMPOSE; 357 358 cmd[0] = SKBD_CMD_SETLED; 359 cmd[1] = sled; 360 (*sc->sc_sendcmd)(sc, cmd, sizeof(cmd)); 361 } 362