1 /* $NetBSD: hvkbd.c,v 1.8 2021/04/24 23:36:54 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Microsoft Corp. 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 unmodified, this list of conditions, and the following 12 * 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 ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * $FreeBSD: head/sys/dev/hyperv/input/hv_kbd.c 317821 2017-05-05 03:28:30Z sephe $ 29 * $FreeBSD: head/sys/dev/hyperv/input/hv_kbdc.c 320490 2017-06-30 03:01:22Z sephe $ 30 * $FreeBSD: head/sys/dev/hyperv/input/hv_kbdc.h 316515 2017-04-05 05:01:23Z sephe $ 31 */ 32 33 #ifdef _KERNEL_OPT 34 #include "opt_pckbd_layout.h" 35 #include "opt_wsdisplay_compat.h" 36 #endif /* _KERNEL_OPT */ 37 38 #include <sys/cdefs.h> 39 __KERNEL_RCSID(0, "$NetBSD: hvkbd.c,v 1.8 2021/04/24 23:36:54 thorpej Exp $"); 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/device.h> 44 #include <sys/mutex.h> 45 #include <sys/kernel.h> 46 #include <sys/kmem.h> 47 #include <sys/module.h> 48 #include <sys/pmf.h> 49 #include <sys/proc.h> 50 #include <sys/queue.h> 51 52 #include <dev/hyperv/vmbusvar.h> 53 #include <dev/hyperv/hvkbdvar.h> 54 55 #include <dev/wscons/wsconsio.h> 56 #include <dev/wscons/wskbdvar.h> 57 #include <dev/wscons/wsksymdef.h> 58 #include <dev/wscons/wsksymvar.h> 59 #include <dev/pckbport/wskbdmap_mfii.h> 60 61 #define HVKBD_BUFSIZE (4 * PAGE_SIZE) 62 #define HVKBD_TX_RING_SIZE (10 * PAGE_SIZE) 63 #define HVKBD_RX_RING_SIZE (10 * PAGE_SIZE) 64 65 #define HVKBD_VER_MAJOR (1) 66 #define HVKBD_VER_MINOR (0) 67 #define HVKBD_VERSION ((HVKBD_VER_MAJOR << 16) | HVKBD_VER_MINOR) 68 69 enum hvkbd_msg_type { 70 HVKBD_PROTO_REQUEST = 1, 71 HVKBD_PROTO_RESPONSE = 2, 72 HVKBD_PROTO_EVENT = 3, 73 HVKBD_PROTO_LED_INDICATORS = 4 74 }; 75 76 struct hvkbd_msg_hdr { 77 uint32_t type; 78 } __packed; 79 80 struct hvkbd_proto_req { 81 struct hvkbd_msg_hdr hdr; 82 uint32_t ver; 83 } __packed; 84 85 struct hvkbd_proto_resp { 86 struct hvkbd_msg_hdr hdr; 87 uint32_t status; 88 #define RESP_STATUS_ACCEPTED __BIT(0) 89 } __packed; 90 91 struct keystroke { 92 uint16_t makecode; 93 uint16_t pad0; 94 uint32_t info; 95 #define KS_INFO_UNICODE __BIT(0) 96 #define KS_INFO_BREAK __BIT(1) 97 #define KS_INFO_E0 __BIT(2) 98 #define KS_INFO_E1 __BIT(3) 99 } __packed; 100 101 struct hvkbd_keystroke { 102 struct hvkbd_msg_hdr hdr; 103 struct keystroke ks; 104 } __packed; 105 106 struct hvkbd_keystroke_info { 107 LIST_ENTRY(hvkbd_keystroke_info) link; 108 STAILQ_ENTRY(hvkbd_keystroke_info) slink; 109 struct keystroke ks; 110 }; 111 112 #define HVKBD_KEYBUF_SIZE 16 113 114 struct hvkbd_softc { 115 device_t sc_dev; 116 117 struct vmbus_channel *sc_chan; 118 void *sc_buf; 119 120 kmutex_t sc_ks_lock; 121 LIST_HEAD(, hvkbd_keystroke_info) sc_ks_free; 122 STAILQ_HEAD(, hvkbd_keystroke_info) sc_ks_queue; 123 124 int sc_enabled; 125 int sc_polling; 126 int sc_console_keyboard; 127 #if defined(WSDISPLAY_COMPAT_RAWKBD) 128 int sc_rawkbd; 129 #endif 130 131 int sc_connected; 132 uint32_t sc_connect_status; 133 134 device_t sc_wskbddev; 135 }; 136 137 static int hvkbd_match(device_t, cfdata_t, void *); 138 static void hvkbd_attach(device_t, device_t, void *); 139 140 CFATTACH_DECL_NEW(hvkbd, sizeof(struct hvkbd_softc), 141 hvkbd_match, hvkbd_attach, NULL, NULL); 142 143 static int hvkbd_alloc_keybuf(struct hvkbd_softc *); 144 static void hvkbd_free_keybuf(struct hvkbd_softc *); 145 146 static int hvkbd_enable(void *, int); 147 static void hvkbd_set_leds(void *, int); 148 static int hvkbd_ioctl(void *, u_long, void *, int, struct lwp *); 149 150 static const struct wskbd_accessops hvkbd_accessops = { 151 hvkbd_enable, 152 hvkbd_set_leds, 153 hvkbd_ioctl, 154 }; 155 156 static const struct wskbd_mapdata hvkbd_keymapdata = { 157 pckbd_keydesctab, 158 #if defined(PCKBD_LAYOUT) 159 PCKBD_LAYOUT, 160 #else 161 KB_US, 162 #endif 163 }; 164 165 static int hvkbd_connect(struct hvkbd_softc *); 166 static void hvkbd_intr(void *); 167 168 static void hvkbd_cngetc(void *, u_int *, int *); 169 static void hvkbd_cnpollc(void *, int); 170 171 static const struct wskbd_consops hvkbd_consops = { 172 .getc = hvkbd_cngetc, 173 .pollc = hvkbd_cnpollc, 174 .bell = NULL, 175 }; 176 177 static int hvkbd_is_console; 178 179 static int 180 hvkbd_match(device_t parent, cfdata_t cf, void *aux) 181 { 182 struct vmbus_attach_args *aa = aux; 183 184 if (memcmp(aa->aa_type, &hyperv_guid_kbd, sizeof(*aa->aa_type)) != 0) 185 return 0; 186 187 /* If hvkbd(4) is not console, we use pckbd(4) in Gen.1 VM. */ 188 if (!hvkbd_is_console && hyperv_is_gen1()) 189 return 0; 190 191 return 1; 192 } 193 194 static void 195 hvkbd_attach(device_t parent, device_t self, void *aux) 196 { 197 struct hvkbd_softc *sc = device_private(self); 198 struct vmbus_attach_args *aa = aux; 199 struct wskbddev_attach_args a; 200 201 sc->sc_dev = self; 202 sc->sc_chan = aa->aa_chan; 203 204 aprint_naive("\n"); 205 aprint_normal(": Hyper-V Synthetic Keyboard\n"); 206 207 mutex_init(&sc->sc_ks_lock, MUTEX_DEFAULT, IPL_TTY); 208 LIST_INIT(&sc->sc_ks_free); 209 STAILQ_INIT(&sc->sc_ks_queue); 210 hvkbd_alloc_keybuf(sc); 211 212 sc->sc_buf = kmem_zalloc(HVKBD_BUFSIZE, KM_SLEEP); 213 214 sc->sc_chan->ch_flags &= ~CHF_BATCHED; 215 if (vmbus_channel_open(sc->sc_chan, 216 HVKBD_TX_RING_SIZE + HVKBD_RX_RING_SIZE, NULL, 0, hvkbd_intr, sc)) { 217 aprint_error_dev(self, "failed to open channel\n"); 218 goto free_buf; 219 } 220 221 if (hvkbd_connect(sc)) 222 goto free_buf; 223 224 if (!pmf_device_register(self, NULL, NULL)) 225 aprint_error_dev(self, "couldn't establish power handler\n"); 226 227 sc->sc_console_keyboard = hvkbd_is_console; 228 if (hvkbd_is_console) 229 hvkbd_is_console = 0; 230 231 if (sc->sc_console_keyboard) { 232 wskbd_cnattach(&hvkbd_consops, sc, &hvkbd_keymapdata); 233 hvkbd_enable(sc, 1); 234 } 235 236 a.console = sc->sc_console_keyboard; 237 a.keymap = &hvkbd_keymapdata; 238 a.accessops = &hvkbd_accessops; 239 a.accesscookie = sc; 240 sc->sc_wskbddev = config_found(self, &a, wskbddevprint, CFARG_EOL); 241 return; 242 243 free_buf: 244 if (sc->sc_buf != NULL) { 245 kmem_free(sc->sc_buf, HVKBD_BUFSIZE); 246 sc->sc_buf = NULL; 247 } 248 hvkbd_free_keybuf(sc); 249 } 250 251 static int 252 hvkbd_alloc_keybuf(struct hvkbd_softc *sc) 253 { 254 struct hvkbd_keystroke_info *ksi; 255 int i; 256 257 for (i = 0; i < HVKBD_KEYBUF_SIZE; i++) { 258 ksi = kmem_zalloc(sizeof(*ksi), KM_SLEEP); 259 LIST_INSERT_HEAD(&sc->sc_ks_free, ksi, link); 260 } 261 262 return 0; 263 } 264 265 static void 266 hvkbd_free_keybuf(struct hvkbd_softc *sc) 267 { 268 struct hvkbd_keystroke_info *ksi; 269 270 while ((ksi = STAILQ_FIRST(&sc->sc_ks_queue)) != NULL) { 271 STAILQ_REMOVE(&sc->sc_ks_queue, ksi, hvkbd_keystroke_info, 272 slink); 273 kmem_free(ksi, sizeof(*ksi)); 274 } 275 while ((ksi = LIST_FIRST(&sc->sc_ks_free)) != NULL) { 276 LIST_REMOVE(ksi, link); 277 kmem_free(ksi, sizeof(*ksi)); 278 } 279 } 280 281 int 282 hvkbd_enable(void *v, int on) 283 { 284 struct hvkbd_softc *sc = v; 285 286 sc->sc_enabled = on; 287 288 return 0; 289 } 290 291 static void 292 hvkbd_set_leds(void *v, int leds) 293 { 294 } 295 296 static int 297 hvkbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 298 { 299 #if defined(WSDISPLAY_COMPAT_RAWKBD) 300 struct hvkbd_softc *sc = v; 301 #endif 302 303 switch (cmd) { 304 case WSKBDIO_GTYPE: 305 *(int *)data = WSKBD_TYPE_HYPERV; 306 return 0; 307 308 case WSKBDIO_SETLEDS: 309 hvkbd_set_leds(v, *(int *)data); 310 return 0; 311 312 case WSKBDIO_GETLEDS: 313 *(int *)data = 0; 314 return 0; 315 316 #if defined(WSDISPLAY_COMPAT_RAWKBD) 317 case WSKBDIO_SETMODE: 318 sc->sc_rawkbd = (*(int *)data == WSKBD_RAW); 319 return 0; 320 #endif 321 } 322 323 return EPASSTHROUGH; 324 } 325 326 static int 327 hvkbd_connect(struct hvkbd_softc *sc) 328 { 329 struct hvkbd_proto_req req; 330 int timo = 100; 331 int error, s; 332 333 sc->sc_connected = 0; 334 335 memset(&req, 0, sizeof(req)); 336 req.hdr.type = HVKBD_PROTO_REQUEST; 337 req.ver = HVKBD_VERSION; 338 error = vmbus_channel_send(sc->sc_chan, &req, sizeof(req), 339 0, VMBUS_CHANPKT_TYPE_INBAND, VMBUS_CHANPKT_FLAG_RC); 340 if (error) { 341 aprint_error_dev(sc->sc_dev, "failed to send connect: %d\n", 342 error); 343 return error; 344 } 345 346 do { 347 if (cold) { 348 delay(1000); 349 s = spltty(); 350 hvkbd_intr(sc); 351 splx(s); 352 } else 353 tsleep(sc, PRIBIO | PCATCH, "hvkbdcon", 354 uimax(1, mstohz(1))); 355 } while (--timo > 0 && sc->sc_connected == 0); 356 357 if (timo == 0 && sc->sc_connected == 0) { 358 aprint_error_dev(sc->sc_dev, "connect timed out\n"); 359 return ETIMEDOUT; 360 } 361 362 if (!(sc->sc_connect_status & RESP_STATUS_ACCEPTED)) { 363 aprint_error_dev(sc->sc_dev, "protocol request failed\n"); 364 return ENODEV; 365 } 366 367 return 0; 368 } 369 370 static int 371 hvkbd_keybuf_add_keystroke(struct hvkbd_softc *sc, const struct keystroke *ks) 372 { 373 struct hvkbd_keystroke_info *ksi; 374 375 mutex_enter(&sc->sc_ks_lock); 376 ksi = LIST_FIRST(&sc->sc_ks_free); 377 if (ksi != NULL) { 378 LIST_REMOVE(ksi, link); 379 ksi->ks = *ks; 380 STAILQ_INSERT_TAIL(&sc->sc_ks_queue, ksi, slink); 381 } 382 mutex_exit(&sc->sc_ks_lock); 383 384 return (ksi != NULL) ? 0 : 1; 385 } 386 387 static int 388 hvkbd_decode(struct hvkbd_softc *sc, u_int *type, int *scancode) 389 { 390 struct hvkbd_keystroke_info *ksi; 391 struct keystroke ks; 392 393 mutex_enter(&sc->sc_ks_lock); 394 ksi = STAILQ_FIRST(&sc->sc_ks_queue); 395 if (ksi != NULL) { 396 STAILQ_REMOVE_HEAD(&sc->sc_ks_queue, slink); 397 ks = ksi->ks; 398 LIST_INSERT_HEAD(&sc->sc_ks_free, ksi, link); 399 } 400 mutex_exit(&sc->sc_ks_lock); 401 402 if (ksi == NULL) 403 return 0; 404 405 /* 406 * XXX: Hyper-V host send unicode to VM through 'Type clipboard text', 407 * the mapping from unicode to scancode depends on the keymap. 408 * It is so complicated that we do not plan to support it yet. 409 */ 410 if (ks.info & KS_INFO_UNICODE) 411 return 0; 412 413 *type = (ks.info & KS_INFO_BREAK) ? 414 WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; 415 *scancode = ks.makecode; 416 return 1; 417 } 418 419 #if defined(WSDISPLAY_COMPAT_RAWKBD) 420 static int 421 hvkbd_encode(struct hvkbd_softc *sc, u_char *buf, int *len) 422 { 423 struct hvkbd_keystroke_info *ksi; 424 struct keystroke ks; 425 int i; 426 427 mutex_enter(&sc->sc_ks_lock); 428 ksi = STAILQ_FIRST(&sc->sc_ks_queue); 429 if (ksi != NULL) { 430 STAILQ_REMOVE_HEAD(&sc->sc_ks_queue, slink); 431 ks = ksi->ks; 432 LIST_INSERT_HEAD(&sc->sc_ks_free, ksi, link); 433 } 434 mutex_exit(&sc->sc_ks_lock); 435 436 if (ksi == NULL) 437 return 0; 438 439 /* 440 * XXX: Hyper-V host send unicode to VM through 'Type clipboard text', 441 * the mapping from unicode to scancode depends on the keymap. 442 * It is so complicated that we do not plan to support it yet. 443 */ 444 if (ks.info & KS_INFO_UNICODE) 445 return 0; 446 447 i = 0; 448 if (ks.info & (KS_INFO_E0|KS_INFO_E1)) { 449 if (ks.info & KS_INFO_E0) 450 buf[i++] = 0xe0; 451 else 452 buf[i++] = 0xe1; 453 } 454 if (ks.info & KS_INFO_BREAK) 455 buf[i++] = (u_char)ks.makecode & 0x80; 456 else 457 buf[i++] = (u_char)ks.makecode; 458 459 KDASSERT(i <= *len); 460 *len = i; 461 462 return 1; 463 } 464 #endif 465 466 static void 467 hvkbd_intr(void *xsc) 468 { 469 struct hvkbd_softc *sc = xsc; 470 struct vmbus_chanpkt_hdr *cph; 471 const struct hvkbd_msg_hdr *hdr; 472 const struct hvkbd_proto_resp *rsp; 473 const struct hvkbd_keystroke *ks; 474 uint64_t rid; 475 uint32_t rlen; 476 u_int type; 477 int key, error; 478 479 for (;;) { 480 error = vmbus_channel_recv(sc->sc_chan, sc->sc_buf, 481 HVKBD_BUFSIZE, &rlen, &rid, 1); 482 if (error != 0 || rlen == 0) { 483 if (error != EAGAIN) 484 device_printf(sc->sc_dev, 485 "failed to receive a reply packet\n"); 486 return; 487 } 488 489 cph = (struct vmbus_chanpkt_hdr *)sc->sc_buf; 490 switch (cph->cph_type) { 491 case VMBUS_CHANPKT_TYPE_INBAND: 492 hdr = VMBUS_CHANPKT_CONST_DATA(cph); 493 if (rlen < sizeof(*hdr)) { 494 device_printf(sc->sc_dev, "Illegal packet\n"); 495 continue; 496 } 497 498 switch (hdr->type) { 499 case HVKBD_PROTO_RESPONSE: 500 if (!sc->sc_connected) { 501 rsp = VMBUS_CHANPKT_CONST_DATA(cph); 502 if (rlen < sizeof(*rsp)) { 503 device_printf(sc->sc_dev, 504 "Illegal resp packet\n"); 505 break; 506 } 507 sc->sc_connect_status = rsp->status; 508 sc->sc_connected = 1; 509 wakeup(sc); 510 } 511 break; 512 513 case HVKBD_PROTO_EVENT: 514 if (sc->sc_wskbddev == NULL || !sc->sc_enabled) 515 break; 516 517 ks = VMBUS_CHANPKT_CONST_DATA(cph); 518 hvkbd_keybuf_add_keystroke(sc, &ks->ks); 519 if (sc->sc_polling) 520 break; 521 522 #if defined(WSDISPLAY_COMPAT_RAWKBD) 523 if (sc->sc_rawkbd) { 524 u_char buf[2]; 525 int len; 526 527 len = sizeof(buf); 528 if (hvkbd_encode(sc, buf, &len)) { 529 wskbd_rawinput(sc->sc_wskbddev, 530 buf, len); 531 } 532 break; 533 } 534 #endif 535 if (hvkbd_decode(sc, &type, &key)) 536 wskbd_input(sc->sc_wskbddev, type, key); 537 break; 538 539 case HVKBD_PROTO_REQUEST: 540 case HVKBD_PROTO_LED_INDICATORS: 541 device_printf(sc->sc_dev, 542 "unhandled message: %d\n", hdr->type); 543 break; 544 545 default: 546 device_printf(sc->sc_dev, 547 "unknown message: %d\n", hdr->type); 548 break; 549 } 550 break; 551 552 case VMBUS_CHANPKT_TYPE_COMP: 553 case VMBUS_CHANPKT_TYPE_RXBUF: 554 device_printf(sc->sc_dev, "unhandled event: %d\n", 555 cph->cph_type); 556 break; 557 558 default: 559 device_printf(sc->sc_dev, "unknown event: %d\n", 560 cph->cph_type); 561 break; 562 } 563 } 564 } 565 566 int 567 hvkbd_cnattach(void) 568 { 569 570 hvkbd_is_console = 1; 571 572 return 0; 573 } 574 575 static void 576 hvkbd_cngetc(void *v, u_int *type, int *data) 577 { 578 struct hvkbd_softc *sc = v; 579 580 while (!hvkbd_decode(sc, type, data)) 581 hvkbd_intr(sc); 582 } 583 584 static void 585 hvkbd_cnpollc(void *v, int on) 586 { 587 struct hvkbd_softc *sc = v; 588 589 sc->sc_polling = on; 590 } 591 592 MODULE(MODULE_CLASS_DRIVER, hvkbd, "vmbus"); 593 594 #ifdef _MODULE 595 #include "ioconf.c" 596 #endif 597 598 static int 599 hvkbd_modcmd(modcmd_t cmd, void *aux) 600 { 601 int error = 0; 602 603 switch (cmd) { 604 case MODULE_CMD_INIT: 605 #ifdef _MODULE 606 error = config_init_component(cfdriver_ioconf_hvkbd, 607 cfattach_ioconf_hvkbd, cfdata_ioconf_hvkbd); 608 #endif 609 break; 610 611 case MODULE_CMD_FINI: 612 #ifdef _MODULE 613 error = config_fini_component(cfdriver_ioconf_hvtkbd, 614 cfattach_ioconf_hvkbd, cfdata_ioconf_hvkbd); 615 #endif 616 break; 617 618 case MODULE_CMD_AUTOUNLOAD: 619 error = EBUSY; 620 break; 621 622 default: 623 error = ENOTTY; 624 break; 625 } 626 627 return error; 628 } 629