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