1 /* $NetBSD: hpf1275a_tty.c,v 1.17 2006/10/12 21:19:13 uwe Exp $ */ 2 3 /* 4 * Copyright (c) 2004 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 30 #include <sys/cdefs.h> 31 __KERNEL_RCSID(0, "$NetBSD: hpf1275a_tty.c,v 1.17 2006/10/12 21:19:13 uwe Exp $"); 32 33 #include "opt_wsdisplay_compat.h" 34 35 #include <sys/param.h> 36 #include <sys/kernel.h> 37 #include <sys/conf.h> 38 #include <sys/device.h> 39 #include <sys/tty.h> 40 #include <sys/fcntl.h> 41 #include <sys/proc.h> 42 #include <sys/systm.h> 43 #include <sys/kauth.h> 44 #ifdef GPROF 45 #include <sys/gmon.h> 46 #endif 47 48 #include <dev/wscons/wsconsio.h> 49 #include <dev/wscons/wskbdvar.h> 50 #include <dev/wscons/wsksymdef.h> 51 #include <dev/wscons/wsksymvar.h> 52 53 #include <dev/pckbport/wskbdmap_mfii.h> 54 #ifdef WSDISPLAY_COMPAT_RAWKBD 55 #include <dev/hpc/pckbd_encode.h> 56 #endif 57 58 59 extern struct cfdriver hpf1275a_cd; 60 61 struct hpf1275a_softc { 62 struct device sc_dev; 63 64 struct tty *sc_tp; /* back reference to the tty */ 65 struct device *sc_wskbd; /* wskbd child */ 66 int sc_enabled; 67 #ifdef WSDISPLAY_COMPAT_RAWKBD 68 int sc_rawkbd; 69 #endif 70 }; 71 72 73 /* pseudo-device initialization */ 74 extern void hpf1275aattach(int); 75 76 /* line discipline methods */ 77 static int hpf1275a_open(dev_t, struct tty *); 78 static int hpf1275a_close(struct tty *, int); 79 static int hpf1275a_input(int, struct tty *); 80 81 /* autoconf(9) methods */ 82 static int hpf1275a_match(struct device *, struct cfdata *, void *); 83 static void hpf1275a_attach(struct device *, struct device *, void *); 84 static int hpf1275a_detach(struct device *, int); 85 86 /* wskbd(4) accessops */ 87 static int hpf1275a_wskbd_enable(void *, int); 88 static void hpf1275a_wskbd_set_leds(void *, int); 89 static int hpf1275a_wskbd_ioctl(void *, u_long, caddr_t, int, 90 struct lwp *); 91 92 93 /* 94 * It doesn't need to be exported, as only hpf1275aattach() uses it, 95 * but there's no "official" way to make it static. 96 */ 97 CFATTACH_DECL(hpf1275a, sizeof(struct hpf1275a_softc), 98 hpf1275a_match, hpf1275a_attach, hpf1275a_detach, NULL); 99 100 101 static struct linesw hpf1275a_disc = { 102 .l_name = "hpf1275a", 103 .l_open = hpf1275a_open, 104 .l_close = hpf1275a_close, 105 .l_read = ttyerrio, 106 .l_write = ttyerrio, 107 .l_ioctl = ttynullioctl, 108 .l_rint = hpf1275a_input, 109 .l_start = ttstart, 110 .l_modem = nullmodem, 111 .l_poll = ttpoll 112 }; 113 114 115 static const struct wskbd_accessops hpf1275a_wskbd_accessops = { 116 hpf1275a_wskbd_enable, 117 hpf1275a_wskbd_set_leds, 118 hpf1275a_wskbd_ioctl 119 }; 120 121 122 static const struct wskbd_mapdata hpf1275a_wskbd_keymapdata = { 123 pckbd_keydesctab, KB_US 124 }; 125 126 127 /* F1275A scancodes -> XT scancodes so that we can use pckbd_keydesctab. */ 128 static const uint8_t hpf1275a_to_xtscan[128] = { 129 [0x04] = 30, /* a */ 130 [0x05] = 48, /* b */ 131 [0x06] = 46, /* c */ 132 [0x07] = 32, /* d */ 133 [0x08] = 18, /* e */ 134 [0x09] = 33, /* f */ 135 [0x0a] = 34, /* g */ 136 [0x0b] = 35, /* h */ 137 [0x0c] = 23, /* i */ 138 [0x0d] = 36, /* j */ 139 [0x0e] = 37, /* k */ 140 [0x0f] = 38, /* l */ 141 [0x10] = 50, /* m */ 142 [0x11] = 49, /* n */ 143 [0x12] = 24, /* o */ 144 [0x13] = 25, /* p */ 145 [0x14] = 16, /* q */ 146 [0x15] = 19, /* r */ 147 [0x16] = 31, /* s */ 148 [0x17] = 20, /* t */ 149 [0x18] = 22, /* u */ 150 [0x19] = 47, /* v */ 151 [0x1a] = 17, /* w */ 152 [0x1b] = 45, /* x */ 153 [0x1c] = 21, /* y */ 154 [0x1d] = 44, /* z */ 155 156 [0x1e] = 2, /* 1 */ 157 [0x1f] = 3, /* 2 */ 158 [0x20] = 4, /* 3 */ 159 [0x21] = 5, /* 4 */ 160 [0x22] = 6, /* 5 */ 161 [0x23] = 7, /* 6 */ 162 [0x24] = 8, /* 7 */ 163 [0x25] = 9, /* 8 */ 164 [0x26] = 10, /* 9 */ 165 [0x27] = 11, /* 0 */ 166 167 [0x28] = 28, /* Enter */ 168 169 [0x29] = 1, /* ESC */ 170 [0x2a] = 14, /* Backspace */ 171 [0x2b] = 15, /* Tab */ 172 [0x2c] = 57, /* Space */ 173 174 [0x2d] = 12, /* - */ 175 [0x2e] = 13, /* = */ 176 [0x2f] = 26, /* [ */ 177 [0x30] = 27, /* ] */ 178 [0x31] = 43, /* \ */ 179 180 [0x33] = 39, /* ; */ 181 [0x34] = 40, /* ' */ 182 [0x35] = 41, /* ` */ 183 [0x36] = 51, /* , */ 184 [0x37] = 52, /* . */ 185 [0x38] = 53, /* / */ 186 187 [0x3a] = 59, /* F1 */ 188 [0x3b] = 60, /* F2 */ 189 [0x3c] = 61, /* F3 */ 190 [0x3d] = 62, /* F4 */ 191 [0x3e] = 63, /* F5 */ 192 [0x3f] = 64, /* F6 */ 193 [0x40] = 65, /* F7 */ 194 [0x41] = 66, /* F8 */ 195 196 [0x42] = 68, /* "OK" -> F10 */ 197 [0x43] = 87, /* "Cancel" -> F11 */ 198 199 [0x4c] = 211, /* Del */ 200 201 [0x4f] = 205, /* Right */ 202 [0x50] = 203, /* Left */ 203 [0x51] = 208, /* Down */ 204 [0x52] = 200, /* Up */ 205 206 [0x53] = 67, /* "task switch" -> F9 */ 207 208 [0x65] = 221, /* windows */ 209 [0x66] = 88, /* "keyboard" -> F12 */ 210 211 [0x74] = 42, /* Shift (left) */ 212 [0x75] = 54, /* Shift (right) */ 213 [0x76] = 56, /* Alt (left) */ 214 [0x77] = 184, /* Fn -> AltGr == Mode Switch */ 215 [0x78] = 29, /* Control (left) */ 216 }; 217 218 219 /* 220 * Pseudo-device initialization routine called from main(). 221 */ 222 void 223 hpf1275aattach(int n __unused) 224 { 225 int error; 226 227 error = ttyldisc_attach(&hpf1275a_disc); 228 if (error) { 229 printf("%s: unable to register line discipline, error = %d\n", 230 hpf1275a_cd.cd_name, error); 231 return; 232 } 233 234 error = config_cfattach_attach(hpf1275a_cd.cd_name, &hpf1275a_ca); 235 if (error) { 236 printf("%s: unable to register cfattach, error = %d\n", 237 hpf1275a_cd.cd_name, error); 238 config_cfdriver_detach(&hpf1275a_cd); 239 (void) ttyldisc_detach(&hpf1275a_disc); 240 } 241 } 242 243 244 /* 245 * Autoconf match routine. 246 * 247 * XXX: unused: config_attach_pseudo(9) does not call ca_match. 248 */ 249 static int 250 hpf1275a_match(struct device *self __unused, 251 struct cfdata *cfdata __unused, void *arg __unused) 252 { 253 254 /* pseudo-device; always present */ 255 return (1); 256 } 257 258 259 /* 260 * Autoconf attach routine. Called by config_attach_pseudo(9) when we 261 * open the line discipline. 262 */ 263 static void 264 hpf1275a_attach(struct device *parent __unused, 265 struct device *self, void *aux __unused) 266 { 267 struct hpf1275a_softc *sc = device_private(self); 268 struct wskbddev_attach_args wska; 269 270 wska.console = 0; 271 wska.keymap = &hpf1275a_wskbd_keymapdata; 272 wska.accessops = &hpf1275a_wskbd_accessops; 273 wska.accesscookie = sc; 274 275 sc->sc_enabled = 0; 276 #ifdef WSDISPLAY_COMPAT_RAWKBD 277 sc->sc_rawkbd = 0; 278 #endif 279 sc->sc_wskbd = config_found(self, &wska, wskbddevprint); 280 } 281 282 283 /* 284 * Autoconf detach routine. Called when we close the line discipline. 285 */ 286 static int 287 hpf1275a_detach(struct device *self, int flags __unused) 288 { 289 struct hpf1275a_softc *sc = device_private(self); 290 int error; 291 292 if (sc->sc_wskbd == NULL) 293 return (0); 294 295 error = config_detach(sc->sc_wskbd, 0); 296 297 return (error); 298 } 299 300 301 /* 302 * Line discipline open routine. 303 */ 304 static int 305 hpf1275a_open(dev_t dev __unused, struct tty *tp) 306 { 307 static struct cfdata hpf1275a_cfdata = { 308 .cf_name = "hpf1275a", 309 .cf_atname = "hpf1275a", 310 .cf_unit = DVUNIT_ANY, 311 .cf_fstate = FSTATE_STAR, 312 }; 313 struct lwp *l = curlwp; /* XXX */ 314 struct hpf1275a_softc *sc; 315 int error, s; 316 317 if ((error = kauth_authorize_device_tty(l->l_cred, 318 KAUTH_DEVICE_TTY_OPEN, tp))) 319 return (error); 320 321 s = spltty(); 322 323 if (tp->t_linesw == &hpf1275a_disc) { 324 splx(s); 325 return 0; 326 } 327 328 sc = (struct hpf1275a_softc *)config_attach_pseudo(&hpf1275a_cfdata); 329 if (sc == NULL) { 330 splx(s); 331 return (EIO); 332 } 333 334 tp->t_sc = sc; 335 sc->sc_tp = tp; 336 337 splx(s); 338 return (0); 339 } 340 341 342 /* 343 * Line discipline close routine. 344 */ 345 static int 346 hpf1275a_close(struct tty *tp, int flag __unused) 347 { 348 struct hpf1275a_softc *sc = tp->t_sc; 349 int s; 350 351 s = spltty(); 352 ttyflush(tp, FREAD | FWRITE); 353 ttyldisc_release(tp->t_linesw); 354 tp->t_linesw = ttyldisc_default(); 355 if (sc != NULL) { 356 tp->t_sc = NULL; 357 if (sc->sc_tp == tp) 358 config_detach(&sc->sc_dev, 0); 359 } 360 splx(s); 361 return (0); 362 } 363 364 365 /* 366 * Feed input from the keyboard to wskbd(4). 367 */ 368 static int 369 hpf1275a_input(int c, struct tty *tp) 370 { 371 struct hpf1275a_softc *sc = tp->t_sc; 372 int code; 373 u_int type; 374 int xtscan; 375 376 if (!sc->sc_enabled) 377 return (0); 378 379 if (c & TTY_ERRORMASK) 380 return (0); /* TODO? */ 381 382 code = c & TTY_CHARMASK; 383 if (code & 0x80) { 384 type = WSCONS_EVENT_KEY_UP; 385 code &= ~0x80; 386 } else 387 type = WSCONS_EVENT_KEY_DOWN; 388 389 xtscan = hpf1275a_to_xtscan[code]; 390 if (xtscan == 0) { 391 printf("%s: unknown code 0x%x\n", sc->sc_dev.dv_xname, code); 392 return (0); 393 } 394 395 KASSERT(sc->sc_wskbd != NULL); 396 397 #ifdef WSDISPLAY_COMPAT_RAWKBD 398 if (sc->sc_rawkbd) { 399 u_char data[16]; 400 int n; 401 402 n = pckbd_encode(type, xtscan, data); 403 wskbd_rawinput(sc->sc_wskbd, data, n); 404 } else 405 #endif 406 wskbd_input(sc->sc_wskbd, type, xtscan); 407 408 return (0); 409 } 410 411 412 static int 413 hpf1275a_wskbd_enable(void *self, int on) 414 { 415 struct hpf1275a_softc *sc = self; 416 417 sc->sc_enabled = on; 418 return (0); 419 } 420 421 422 /* ARGSUSED */ 423 static void 424 hpf1275a_wskbd_set_leds(void *self __unused, int leds __unused) 425 { 426 427 /* this keyboard has no leds; nothing to do */ 428 return; 429 } 430 431 432 static int 433 hpf1275a_wskbd_ioctl(void *self, u_long cmd, caddr_t data, int flag __unused, 434 struct lwp *l __unused) 435 { 436 #ifdef WSDISPLAY_COMPAT_RAWKBD 437 struct hpf1275a_softc *sc = self; 438 #endif 439 440 switch (cmd) { 441 case WSKBDIO_GTYPE: 442 *(int *)data = WSKBD_TYPE_HPC_KBD; /* may be use new type? */ 443 return (0); 444 445 case WSKBDIO_GETLEDS: 446 *(int *)data = 0; /* this keyboard has no leds */ 447 return (0); 448 449 #ifdef WSDISPLAY_COMPAT_RAWKBD 450 case WSKBDIO_SETMODE: 451 sc->sc_rawkbd = (*(int *)data == WSKBD_RAW); 452 return (0); 453 #endif 454 455 default: 456 return (EPASSTHROUGH); 457 } 458 } 459