ps2kbd.c revision 336189
1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 5 * Copyright (c) 2015 Nahanni Systems Inc. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: stable/11/usr.sbin/bhyve/ps2kbd.c 336189 2018-07-11 07:16:13Z araujo $"); 32 33#include <sys/types.h> 34 35#include <assert.h> 36#include <stdbool.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <strings.h> 40#include <pthread.h> 41#include <pthread_np.h> 42 43#include "atkbdc.h" 44#include "console.h" 45 46/* keyboard device commands */ 47#define PS2KC_RESET_DEV 0xff 48#define PS2KC_DISABLE 0xf5 49#define PS2KC_ENABLE 0xf4 50#define PS2KC_SET_TYPEMATIC 0xf3 51#define PS2KC_SEND_DEV_ID 0xf2 52#define PS2KC_SET_SCANCODE_SET 0xf0 53#define PS2KC_ECHO 0xee 54#define PS2KC_SET_LEDS 0xed 55 56#define PS2KC_BAT_SUCCESS 0xaa 57#define PS2KC_ACK 0xfa 58 59#define PS2KBD_FIFOSZ 16 60 61struct fifo { 62 uint8_t buf[PS2KBD_FIFOSZ]; 63 int rindex; /* index to read from */ 64 int windex; /* index to write to */ 65 int num; /* number of bytes in the fifo */ 66 int size; /* size of the fifo */ 67}; 68 69struct ps2kbd_softc { 70 struct atkbdc_softc *atkbdc_sc; 71 pthread_mutex_t mtx; 72 73 bool enabled; 74 struct fifo fifo; 75 76 uint8_t curcmd; /* current command for next byte */ 77}; 78 79static void 80fifo_init(struct ps2kbd_softc *sc) 81{ 82 struct fifo *fifo; 83 84 fifo = &sc->fifo; 85 fifo->size = sizeof(((struct fifo *)0)->buf); 86} 87 88static void 89fifo_reset(struct ps2kbd_softc *sc) 90{ 91 struct fifo *fifo; 92 93 fifo = &sc->fifo; 94 bzero(fifo, sizeof(struct fifo)); 95 fifo->size = sizeof(((struct fifo *)0)->buf); 96} 97 98static void 99fifo_put(struct ps2kbd_softc *sc, uint8_t val) 100{ 101 struct fifo *fifo; 102 103 fifo = &sc->fifo; 104 if (fifo->num < fifo->size) { 105 fifo->buf[fifo->windex] = val; 106 fifo->windex = (fifo->windex + 1) % fifo->size; 107 fifo->num++; 108 } 109} 110 111static int 112fifo_get(struct ps2kbd_softc *sc, uint8_t *val) 113{ 114 struct fifo *fifo; 115 116 fifo = &sc->fifo; 117 if (fifo->num > 0) { 118 *val = fifo->buf[fifo->rindex]; 119 fifo->rindex = (fifo->rindex + 1) % fifo->size; 120 fifo->num--; 121 return (0); 122 } 123 124 return (-1); 125} 126 127int 128ps2kbd_read(struct ps2kbd_softc *sc, uint8_t *val) 129{ 130 int retval; 131 132 pthread_mutex_lock(&sc->mtx); 133 retval = fifo_get(sc, val); 134 pthread_mutex_unlock(&sc->mtx); 135 136 return (retval); 137} 138 139void 140ps2kbd_write(struct ps2kbd_softc *sc, uint8_t val) 141{ 142 pthread_mutex_lock(&sc->mtx); 143 if (sc->curcmd) { 144 switch (sc->curcmd) { 145 case PS2KC_SET_TYPEMATIC: 146 fifo_put(sc, PS2KC_ACK); 147 break; 148 case PS2KC_SET_SCANCODE_SET: 149 fifo_put(sc, PS2KC_ACK); 150 break; 151 case PS2KC_SET_LEDS: 152 fifo_put(sc, PS2KC_ACK); 153 break; 154 default: 155 fprintf(stderr, "Unhandled ps2 keyboard current " 156 "command byte 0x%02x\n", val); 157 break; 158 } 159 sc->curcmd = 0; 160 } else { 161 switch (val) { 162 case 0x00: 163 fifo_put(sc, PS2KC_ACK); 164 break; 165 case PS2KC_RESET_DEV: 166 fifo_reset(sc); 167 fifo_put(sc, PS2KC_ACK); 168 fifo_put(sc, PS2KC_BAT_SUCCESS); 169 break; 170 case PS2KC_DISABLE: 171 sc->enabled = false; 172 fifo_put(sc, PS2KC_ACK); 173 break; 174 case PS2KC_ENABLE: 175 sc->enabled = true; 176 fifo_reset(sc); 177 fifo_put(sc, PS2KC_ACK); 178 break; 179 case PS2KC_SET_TYPEMATIC: 180 sc->curcmd = val; 181 fifo_put(sc, PS2KC_ACK); 182 break; 183 case PS2KC_SEND_DEV_ID: 184 fifo_put(sc, PS2KC_ACK); 185 fifo_put(sc, 0xab); 186 fifo_put(sc, 0x83); 187 break; 188 case PS2KC_SET_SCANCODE_SET: 189 sc->curcmd = val; 190 fifo_put(sc, PS2KC_ACK); 191 break; 192 case PS2KC_ECHO: 193 fifo_put(sc, PS2KC_ECHO); 194 break; 195 case PS2KC_SET_LEDS: 196 sc->curcmd = val; 197 fifo_put(sc, PS2KC_ACK); 198 break; 199 default: 200 fprintf(stderr, "Unhandled ps2 keyboard command " 201 "0x%02x\n", val); 202 break; 203 } 204 } 205 pthread_mutex_unlock(&sc->mtx); 206} 207 208/* 209 * Translate keysym to type 2 scancode and insert into keyboard buffer. 210 */ 211static void 212ps2kbd_keysym_queue(struct ps2kbd_softc *sc, 213 int down, uint32_t keysym) 214{ 215 /* ASCII to type 2 scancode lookup table */ 216 const uint8_t translation[128] = { 217 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 218 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 219 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 220 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 221 0x29, 0x16, 0x52, 0x26, 0x25, 0x2e, 0x3d, 0x52, 222 0x46, 0x45, 0x3e, 0x55, 0x41, 0x4e, 0x49, 0x4a, 223 0x45, 0x16, 0x1e, 0x26, 0x25, 0x2e, 0x36, 0x3d, 224 0x3e, 0x46, 0x4c, 0x4c, 0x41, 0x55, 0x49, 0x4a, 225 0x1e, 0x1c, 0x32, 0x21, 0x23, 0x24, 0x2b, 0x34, 226 0x33, 0x43, 0x3b, 0x42, 0x4b, 0x3a, 0x31, 0x44, 227 0x4d, 0x15, 0x2d, 0x1b, 0x2c, 0x3c, 0x2a, 0x1d, 228 0x22, 0x35, 0x1a, 0x54, 0x5d, 0x5b, 0x36, 0x4e, 229 0x0e, 0x1c, 0x32, 0x21, 0x23, 0x24, 0x2b, 0x34, 230 0x33, 0x43, 0x3b, 0x42, 0x4b, 0x3a, 0x31, 0x44, 231 0x4d, 0x15, 0x2d, 0x1b, 0x2c, 0x3c, 0x2a, 0x1d, 232 0x22, 0x35, 0x1a, 0x54, 0x5d, 0x5b, 0x0e, 0x00, 233 }; 234 235 assert(pthread_mutex_isowned_np(&sc->mtx)); 236 237 switch (keysym) { 238 case 0x0 ... 0x7f: 239 if (!down) 240 fifo_put(sc, 0xf0); 241 fifo_put(sc, translation[keysym]); 242 break; 243 case 0xff08: /* Back space */ 244 if (!down) 245 fifo_put(sc, 0xf0); 246 fifo_put(sc, 0x66); 247 break; 248 case 0xff09: /* Tab */ 249 if (!down) 250 fifo_put(sc, 0xf0); 251 fifo_put(sc, 0x0d); 252 break; 253 case 0xff0d: /* Return */ 254 if (!down) 255 fifo_put(sc, 0xf0); 256 fifo_put(sc, 0x5a); 257 break; 258 case 0xff1b: /* Escape */ 259 if (!down) 260 fifo_put(sc, 0xf0); 261 fifo_put(sc, 0x76); 262 break; 263 case 0xff50: /* Home */ 264 fifo_put(sc, 0xe0); 265 if (!down) 266 fifo_put(sc, 0xf0); 267 fifo_put(sc, 0x6c); 268 break; 269 case 0xff51: /* Left arrow */ 270 fifo_put(sc, 0xe0); 271 if (!down) 272 fifo_put(sc, 0xf0); 273 fifo_put(sc, 0x6b); 274 break; 275 case 0xff52: /* Up arrow */ 276 fifo_put(sc, 0xe0); 277 if (!down) 278 fifo_put(sc, 0xf0); 279 fifo_put(sc, 0x75); 280 break; 281 case 0xff53: /* Right arrow */ 282 fifo_put(sc, 0xe0); 283 if (!down) 284 fifo_put(sc, 0xf0); 285 fifo_put(sc, 0x74); 286 break; 287 case 0xff54: /* Down arrow */ 288 fifo_put(sc, 0xe0); 289 if (!down) 290 fifo_put(sc, 0xf0); 291 fifo_put(sc, 0x72); 292 break; 293 case 0xff55: /* PgUp */ 294 fifo_put(sc, 0xe0); 295 if (!down) 296 fifo_put(sc, 0xf0); 297 fifo_put(sc, 0x7d); 298 break; 299 case 0xff56: /* PgDwn */ 300 fifo_put(sc, 0xe0); 301 if (!down) 302 fifo_put(sc, 0xf0); 303 fifo_put(sc, 0x7a); 304 break; 305 case 0xff57: /* End */ 306 fifo_put(sc, 0xe0); 307 if (!down) 308 fifo_put(sc, 0xf0); 309 fifo_put(sc, 0x69); 310 break; 311 case 0xff63: /* Ins */ 312 fifo_put(sc, 0xe0); 313 if (!down) 314 fifo_put(sc, 0xf0); 315 fifo_put(sc, 0x70); 316 break; 317 case 0xff8d: /* Keypad Enter */ 318 fifo_put(sc, 0xe0); 319 if (!down) 320 fifo_put(sc, 0xf0); 321 fifo_put(sc, 0x5a); 322 break; 323 case 0xffe1: /* Left shift */ 324 if (!down) 325 fifo_put(sc, 0xf0); 326 fifo_put(sc, 0x12); 327 break; 328 case 0xffe2: /* Right shift */ 329 if (!down) 330 fifo_put(sc, 0xf0); 331 fifo_put(sc, 0x59); 332 break; 333 case 0xffe3: /* Left control */ 334 if (!down) 335 fifo_put(sc, 0xf0); 336 fifo_put(sc, 0x14); 337 break; 338 case 0xffe4: /* Right control */ 339 fifo_put(sc, 0xe0); 340 if (!down) 341 fifo_put(sc, 0xf0); 342 fifo_put(sc, 0x14); 343 break; 344 case 0xffe7: /* Left meta */ 345 /* XXX */ 346 break; 347 case 0xffe8: /* Right meta */ 348 /* XXX */ 349 break; 350 case 0xffe9: /* Left alt */ 351 if (!down) 352 fifo_put(sc, 0xf0); 353 fifo_put(sc, 0x11); 354 break; 355 case 0xfe03: /* AltGr */ 356 case 0xffea: /* Right alt */ 357 fifo_put(sc, 0xe0); 358 if (!down) 359 fifo_put(sc, 0xf0); 360 fifo_put(sc, 0x11); 361 break; 362 case 0xffeb: /* Left Windows */ 363 fifo_put(sc, 0xe0); 364 if (!down) 365 fifo_put(sc, 0xf0); 366 fifo_put(sc, 0x1f); 367 break; 368 case 0xffec: /* Right Windows */ 369 fifo_put(sc, 0xe0); 370 if (!down) 371 fifo_put(sc, 0xf0); 372 fifo_put(sc, 0x27); 373 break; 374 case 0xffbe: /* F1 */ 375 if (!down) 376 fifo_put(sc, 0xf0); 377 fifo_put(sc, 0x05); 378 break; 379 case 0xffbf: /* F2 */ 380 if (!down) 381 fifo_put(sc, 0xf0); 382 fifo_put(sc, 0x06); 383 break; 384 case 0xffc0: /* F3 */ 385 if (!down) 386 fifo_put(sc, 0xf0); 387 fifo_put(sc, 0x04); 388 break; 389 case 0xffc1: /* F4 */ 390 if (!down) 391 fifo_put(sc, 0xf0); 392 fifo_put(sc, 0x0C); 393 break; 394 case 0xffc2: /* F5 */ 395 if (!down) 396 fifo_put(sc, 0xf0); 397 fifo_put(sc, 0x03); 398 break; 399 case 0xffc3: /* F6 */ 400 if (!down) 401 fifo_put(sc, 0xf0); 402 fifo_put(sc, 0x0B); 403 break; 404 case 0xffc4: /* F7 */ 405 if (!down) 406 fifo_put(sc, 0xf0); 407 fifo_put(sc, 0x83); 408 break; 409 case 0xffc5: /* F8 */ 410 if (!down) 411 fifo_put(sc, 0xf0); 412 fifo_put(sc, 0x0A); 413 break; 414 case 0xffc6: /* F9 */ 415 if (!down) 416 fifo_put(sc, 0xf0); 417 fifo_put(sc, 0x01); 418 break; 419 case 0xffc7: /* F10 */ 420 if (!down) 421 fifo_put(sc, 0xf0); 422 fifo_put(sc, 0x09); 423 break; 424 case 0xffc8: /* F11 */ 425 if (!down) 426 fifo_put(sc, 0xf0); 427 fifo_put(sc, 0x78); 428 break; 429 case 0xffc9: /* F12 */ 430 if (!down) 431 fifo_put(sc, 0xf0); 432 fifo_put(sc, 0x07); 433 break; 434 case 0xffff: /* Del */ 435 fifo_put(sc, 0xe0); 436 if (!down) 437 fifo_put(sc, 0xf0); 438 fifo_put(sc, 0x71); 439 break; 440 default: 441 fprintf(stderr, "Unhandled ps2 keyboard keysym 0x%x\n", 442 keysym); 443 break; 444 } 445} 446 447static void 448ps2kbd_event(int down, uint32_t keysym, void *arg) 449{ 450 struct ps2kbd_softc *sc = arg; 451 int fifo_full; 452 453 pthread_mutex_lock(&sc->mtx); 454 if (!sc->enabled) { 455 pthread_mutex_unlock(&sc->mtx); 456 return; 457 } 458 fifo_full = sc->fifo.num == PS2KBD_FIFOSZ; 459 ps2kbd_keysym_queue(sc, down, keysym); 460 pthread_mutex_unlock(&sc->mtx); 461 462 if (!fifo_full) 463 atkbdc_event(sc->atkbdc_sc, 1); 464} 465 466struct ps2kbd_softc * 467ps2kbd_init(struct atkbdc_softc *atkbdc_sc) 468{ 469 struct ps2kbd_softc *sc; 470 471 sc = calloc(1, sizeof (struct ps2kbd_softc)); 472 pthread_mutex_init(&sc->mtx, NULL); 473 fifo_init(sc); 474 sc->atkbdc_sc = atkbdc_sc; 475 476 console_kbd_register(ps2kbd_event, sc, 1); 477 478 return (sc); 479} 480 481