1 /* $OpenBSD: sunkbd.c,v 1.28 2020/04/06 19:03:09 cheloha 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_setleds(void *, int); 65 66 struct wskbd_accessops sunkbd_accessops = { 67 sunkbd_enable, 68 sunkbd_setleds, 69 sunkbd_ioctl 70 }; 71 72 void 73 sunkbd_attach(struct sunkbd_softc *sc, struct wskbddev_attach_args *waa) 74 { 75 if (ISTYPE5(sc->sc_layout)) 76 sc->sc_decode = sunkbd_decode5; 77 else 78 sc->sc_decode = sunkbd_decode; 79 80 sc->sc_wskbddev = config_found((struct device *)sc, waa, 81 wskbddevprint); 82 } 83 84 void 85 sunkbd_bell(struct sunkbd_softc *sc, u_int period, u_int pitch, u_int volume) 86 { 87 int s; 88 u_int8_t c = SKBD_CMD_BELLON; 89 90 #if NTCTRL > 0 91 if (tadpole_bell(period / 10, pitch, volume) != 0) 92 return; 93 #endif 94 95 s = spltty(); 96 if (sc->sc_bellactive) { 97 if (sc->sc_belltimeout == 0) 98 timeout_del(&sc->sc_bellto); 99 } 100 if (pitch == 0 || period == 0) { 101 sunkbd_bellstop(sc); 102 splx(s); 103 return; 104 } 105 if (sc->sc_bellactive == 0) { 106 sc->sc_bellactive = 1; 107 sc->sc_belltimeout = 1; 108 (*sc->sc_sendcmd)(sc, &c, 1); 109 timeout_add_msec(&sc->sc_bellto, period); 110 } 111 splx(s); 112 } 113 114 void 115 sunkbd_bellstop(void *v) 116 { 117 struct sunkbd_softc *sc = v; 118 int s; 119 u_int8_t c; 120 121 s = spltty(); 122 sc->sc_belltimeout = 0; 123 c = SKBD_CMD_BELLOFF; 124 (*sc->sc_sendcmd)(v, &c, 1); 125 sc->sc_bellactive = 0; 126 splx(s); 127 } 128 129 void 130 sunkbd_decode(u_int8_t c, u_int *type, int *value) 131 { 132 switch (c) { 133 case SKBD_RSP_IDLE: 134 *type = WSCONS_EVENT_ALL_KEYS_UP; 135 *value = 0; 136 break; 137 default: 138 *type = (c & 0x80) ? 139 WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; 140 *value = c & 0x7f; 141 break; 142 } 143 } 144 145 void 146 sunkbd_decode5(u_int8_t c, u_int *type, int *value) 147 { 148 sunkbd_decode(c, type, value); 149 /* 150 * Scancode 0x2d is KS_KP_Equal on type 4, and KS_AudioMute on 151 * type 5. Rather than provide two distinct maps, we remap the 152 * scancode here. 153 */ 154 if (*value == 0x2d) 155 *value = 0x7f; 156 } 157 158 int 159 sunkbd_enable(void *v, int on) 160 { 161 return (0); 162 } 163 164 int 165 sunkbd_getleds(struct sunkbd_softc *sc) 166 { 167 return (sc->sc_leds); 168 } 169 170 void 171 sunkbd_input(struct sunkbd_softc *sc, u_int8_t *buf, u_int buflen) 172 { 173 u_int type; 174 int value; 175 int s; 176 177 if (sc->sc_wskbddev == NULL) 178 return; /* why bother */ 179 180 #ifdef WSDISPLAY_COMPAT_RAWKBD 181 if (sc->sc_rawkbd) { 182 u_char rbuf[SUNKBD_MAX_INPUT_SIZE * 2]; 183 int c, rlen = 0; 184 185 while (buflen-- != 0) { 186 (*sc->sc_decode)(*buf++, &type, &value); 187 c = sunkbd_rawmap[value]; 188 if (c == RAWKEY_Null) 189 continue; 190 /* fake extended scancode if necessary */ 191 if (c & 0x80) 192 rbuf[rlen++] = 0xe0; 193 rbuf[rlen] = c & 0x7f; 194 if (type == WSCONS_EVENT_KEY_UP) 195 rbuf[rlen] |= 0x80; 196 rlen++; 197 } 198 199 s = spltty(); 200 wskbd_rawinput(sc->sc_wskbddev, rbuf, rlen); 201 splx(s); 202 } else 203 #endif 204 { 205 s = spltty(); 206 while (buflen-- != 0) { 207 (*sc->sc_decode)(*buf++, &type, &value); 208 wskbd_input(sc->sc_wskbddev, type, value); 209 } 210 splx(s); 211 } 212 } 213 214 int 215 sunkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 216 { 217 struct sunkbd_softc *sc = v; 218 int *d_int = (int *)data; 219 struct wskbd_bell_data *d_bell = (struct wskbd_bell_data *)data; 220 221 switch (cmd) { 222 case WSKBDIO_GTYPE: 223 if (ISTYPE5(sc->sc_layout)) { 224 *d_int = WSKBD_TYPE_SUN5; 225 } else { 226 *d_int = WSKBD_TYPE_SUN; 227 } 228 return (0); 229 case WSKBDIO_SETLEDS: 230 sunkbd_setleds(sc, *d_int); 231 return (0); 232 case WSKBDIO_GETLEDS: 233 *d_int = sunkbd_getleds(sc); 234 return (0); 235 case WSKBDIO_COMPLEXBELL: 236 sunkbd_bell(sc, d_bell->period, d_bell->pitch, d_bell->volume); 237 return (0); 238 #ifdef WSDISPLAY_COMPAT_RAWKBD 239 case WSKBDIO_SETMODE: 240 sc->sc_rawkbd = *(int *)data == WSKBD_RAW; 241 return (0); 242 #endif 243 } 244 245 return (-1); 246 } 247 248 void 249 sunkbd_raw(struct sunkbd_softc *sc, u_int8_t c) 250 { 251 int claimed = 0; 252 253 if (sc->sc_kbdstate == SKBD_STATE_LAYOUT) { 254 sc->sc_kbdstate = SKBD_STATE_GETKEY; 255 sc->sc_layout = c; 256 return; 257 } 258 259 switch (c) { 260 case SKBD_RSP_RESET: 261 sc->sc_kbdstate = SKBD_STATE_RESET; 262 claimed = 1; 263 break; 264 case SKBD_RSP_LAYOUT: 265 sc->sc_kbdstate = SKBD_STATE_LAYOUT; 266 claimed = 1; 267 break; 268 case SKBD_RSP_IDLE: 269 sc->sc_kbdstate = SKBD_STATE_GETKEY; 270 claimed = 1; 271 } 272 273 if (claimed) 274 return; 275 276 switch (sc->sc_kbdstate) { 277 case SKBD_STATE_RESET: 278 sc->sc_kbdstate = SKBD_STATE_GETKEY; 279 if (c < KB_SUN2 || c > KB_SUN4) 280 printf("%s: reset: invalid keyboard type 0x%02x\n", 281 sc->sc_dev.dv_xname, c); 282 else 283 sc->sc_id = c; 284 break; 285 case SKBD_STATE_GETKEY: 286 break; 287 } 288 } 289 290 int 291 sunkbd_setclick(struct sunkbd_softc *sc, int click) 292 { 293 u_int8_t c; 294 295 /* Type 2 keyboards do not support keyclick */ 296 if (sc->sc_id == KB_SUN2) 297 return (ENXIO); 298 299 c = click ? SKBD_CMD_CLICKON : SKBD_CMD_CLICKOFF; 300 (*sc->sc_sendcmd)(sc, &c, 1); 301 return (0); 302 } 303 304 void 305 sunkbd_setleds(void *v, int wled) 306 { 307 struct sunkbd_softc *sc = v; 308 u_int8_t sled = 0; 309 u_int8_t cmd[2]; 310 311 sc->sc_leds = wled; 312 313 if (wled & WSKBD_LED_CAPS) 314 sled |= SKBD_LED_CAPSLOCK; 315 if (wled & WSKBD_LED_NUM) 316 sled |= SKBD_LED_NUMLOCK; 317 if (wled & WSKBD_LED_SCROLL) 318 sled |= SKBD_LED_SCROLLLOCK; 319 if (wled & WSKBD_LED_COMPOSE) 320 sled |= SKBD_LED_COMPOSE; 321 322 cmd[0] = SKBD_CMD_SETLED; 323 cmd[1] = sled; 324 (*sc->sc_sendcmd)(sc, cmd, sizeof(cmd)); 325 } 326