1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2014 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 32 #include <sys/types.h> 33 34 #include <machine/vmm.h> 35 36 #include <vmmapi.h> 37 38 #include <assert.h> 39 #include <errno.h> 40 #include <stdbool.h> 41 #include <stdlib.h> 42 #include <stdio.h> 43 #include <string.h> 44 #include <unistd.h> 45 #include <pthread.h> 46 #include <pthread_np.h> 47 48 #include "acpi.h" 49 #include "atkbdc.h" 50 #include "inout.h" 51 #include "pci_emul.h" 52 #include "pci_irq.h" 53 #include "pci_lpc.h" 54 #include "ps2kbd.h" 55 #include "ps2mouse.h" 56 57 #define KBD_DATA_PORT 0x60 58 59 #define KBD_STS_CTL_PORT 0x64 60 61 #define KBDC_RESET 0xfe 62 63 #define KBD_DEV_IRQ 1 64 #define AUX_DEV_IRQ 12 65 66 /* controller commands */ 67 #define KBDC_SET_COMMAND_BYTE 0x60 68 #define KBDC_GET_COMMAND_BYTE 0x20 69 #define KBDC_DISABLE_AUX_PORT 0xa7 70 #define KBDC_ENABLE_AUX_PORT 0xa8 71 #define KBDC_TEST_AUX_PORT 0xa9 72 #define KBDC_TEST_CTRL 0xaa 73 #define KBDC_TEST_KBD_PORT 0xab 74 #define KBDC_DISABLE_KBD_PORT 0xad 75 #define KBDC_ENABLE_KBD_PORT 0xae 76 #define KBDC_READ_INPORT 0xc0 77 #define KBDC_READ_OUTPORT 0xd0 78 #define KBDC_WRITE_OUTPORT 0xd1 79 #define KBDC_WRITE_KBD_OUTBUF 0xd2 80 #define KBDC_WRITE_AUX_OUTBUF 0xd3 81 #define KBDC_WRITE_TO_AUX 0xd4 82 83 /* controller command byte (set by KBDC_SET_COMMAND_BYTE) */ 84 #define KBD_TRANSLATION 0x40 85 #define KBD_SYS_FLAG_BIT 0x04 86 #define KBD_DISABLE_KBD_PORT 0x10 87 #define KBD_DISABLE_AUX_PORT 0x20 88 #define KBD_ENABLE_AUX_INT 0x02 89 #define KBD_ENABLE_KBD_INT 0x01 90 #define KBD_KBD_CONTROL_BITS (KBD_DISABLE_KBD_PORT | KBD_ENABLE_KBD_INT) 91 #define KBD_AUX_CONTROL_BITS (KBD_DISABLE_AUX_PORT | KBD_ENABLE_AUX_INT) 92 93 /* controller status bits */ 94 #define KBDS_KBD_BUFFER_FULL 0x01 95 #define KBDS_SYS_FLAG 0x04 96 #define KBDS_CTRL_FLAG 0x08 97 #define KBDS_AUX_BUFFER_FULL 0x20 98 99 /* controller output port */ 100 #define KBDO_KBD_OUTFULL 0x10 101 #define KBDO_AUX_OUTFULL 0x20 102 103 #define RAMSZ 32 104 #define FIFOSZ 15 105 #define CTRL_CMD_FLAG 0x8000 106 107 struct kbd_dev { 108 bool irq_active; 109 int irq; 110 111 uint8_t buffer[FIFOSZ]; 112 int brd, bwr; 113 int bcnt; 114 }; 115 116 struct aux_dev { 117 bool irq_active; 118 int irq; 119 }; 120 121 struct atkbdc_softc { 122 struct vmctx *ctx; 123 pthread_mutex_t mtx; 124 125 struct ps2kbd_softc *ps2kbd_sc; 126 struct ps2mouse_softc *ps2mouse_sc; 127 128 uint8_t status; /* status register */ 129 uint8_t outport; /* controller output port */ 130 uint8_t ram[RAMSZ]; /* byte0 = controller config */ 131 132 uint32_t curcmd; /* current command for next byte */ 133 uint32_t ctrlbyte; 134 135 struct kbd_dev kbd; 136 struct aux_dev aux; 137 }; 138 139 static void 140 atkbdc_assert_kbd_intr(struct atkbdc_softc *sc) 141 { 142 if ((sc->ram[0] & KBD_ENABLE_KBD_INT) != 0) { 143 sc->kbd.irq_active = true; 144 vm_isa_pulse_irq(sc->ctx, sc->kbd.irq, sc->kbd.irq); 145 } 146 } 147 148 static void 149 atkbdc_assert_aux_intr(struct atkbdc_softc *sc) 150 { 151 if ((sc->ram[0] & KBD_ENABLE_AUX_INT) != 0) { 152 sc->aux.irq_active = true; 153 vm_isa_pulse_irq(sc->ctx, sc->aux.irq, sc->aux.irq); 154 } 155 } 156 157 static int 158 atkbdc_kbd_queue_data(struct atkbdc_softc *sc, uint8_t val) 159 { 160 assert(pthread_mutex_isowned_np(&sc->mtx)); 161 162 if (sc->kbd.bcnt < FIFOSZ) { 163 sc->kbd.buffer[sc->kbd.bwr] = val; 164 sc->kbd.bwr = (sc->kbd.bwr + 1) % FIFOSZ; 165 sc->kbd.bcnt++; 166 sc->status |= KBDS_KBD_BUFFER_FULL; 167 sc->outport |= KBDO_KBD_OUTFULL; 168 } else { 169 printf("atkbd data buffer full\n"); 170 } 171 172 return (sc->kbd.bcnt < FIFOSZ); 173 } 174 175 static void 176 atkbdc_kbd_read(struct atkbdc_softc *sc) 177 { 178 const uint8_t translation[256] = { 179 0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58, 180 0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59, 181 0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a, 182 0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b, 183 0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c, 184 0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d, 185 0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e, 186 0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f, 187 0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60, 188 0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61, 189 0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e, 190 0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76, 191 0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b, 192 0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f, 193 0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45, 194 0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54, 195 0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87, 196 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 197 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 198 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 199 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 200 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 201 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 202 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 203 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 204 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 205 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 206 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 207 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 208 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 209 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 210 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff 211 }; 212 uint8_t val; 213 uint8_t release = 0; 214 215 assert(pthread_mutex_isowned_np(&sc->mtx)); 216 217 if (sc->ram[0] & KBD_TRANSLATION) { 218 while (ps2kbd_read(sc->ps2kbd_sc, &val) != -1) { 219 if (val == 0xf0) { 220 release = 0x80; 221 continue; 222 } else { 223 val = translation[val] | release; 224 } 225 atkbdc_kbd_queue_data(sc, val); 226 break; 227 } 228 } else { 229 while (sc->kbd.bcnt < FIFOSZ) { 230 if (ps2kbd_read(sc->ps2kbd_sc, &val) != -1) 231 atkbdc_kbd_queue_data(sc, val); 232 else 233 break; 234 } 235 } 236 237 if (((sc->ram[0] & KBD_DISABLE_AUX_PORT) || 238 ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) && sc->kbd.bcnt > 0) 239 atkbdc_assert_kbd_intr(sc); 240 } 241 242 static void 243 atkbdc_aux_poll(struct atkbdc_softc *sc) 244 { 245 if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0) { 246 sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL; 247 sc->outport |= KBDO_AUX_OUTFULL; 248 atkbdc_assert_aux_intr(sc); 249 } 250 } 251 252 static void 253 atkbdc_kbd_poll(struct atkbdc_softc *sc) 254 { 255 assert(pthread_mutex_isowned_np(&sc->mtx)); 256 257 atkbdc_kbd_read(sc); 258 } 259 260 static void 261 atkbdc_poll(struct atkbdc_softc *sc) 262 { 263 atkbdc_aux_poll(sc); 264 atkbdc_kbd_poll(sc); 265 } 266 267 static void 268 atkbdc_dequeue_data(struct atkbdc_softc *sc, uint8_t *buf) 269 { 270 assert(pthread_mutex_isowned_np(&sc->mtx)); 271 272 if (ps2mouse_read(sc->ps2mouse_sc, buf) == 0) { 273 if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) { 274 if (sc->kbd.bcnt == 0) 275 sc->status &= ~(KBDS_AUX_BUFFER_FULL | 276 KBDS_KBD_BUFFER_FULL); 277 else 278 sc->status &= ~(KBDS_AUX_BUFFER_FULL); 279 sc->outport &= ~KBDO_AUX_OUTFULL; 280 } 281 282 atkbdc_poll(sc); 283 return; 284 } 285 286 if (sc->kbd.bcnt > 0) { 287 *buf = sc->kbd.buffer[sc->kbd.brd]; 288 sc->kbd.brd = (sc->kbd.brd + 1) % FIFOSZ; 289 sc->kbd.bcnt--; 290 if (sc->kbd.bcnt == 0) { 291 sc->status &= ~KBDS_KBD_BUFFER_FULL; 292 sc->outport &= ~KBDO_KBD_OUTFULL; 293 } 294 295 atkbdc_poll(sc); 296 } 297 298 if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0 && sc->kbd.bcnt == 0) { 299 sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL); 300 } 301 } 302 303 static int 304 atkbdc_data_handler(struct vmctx *ctx __unused, int in, 305 int port __unused, int bytes, uint32_t *eax, void *arg) 306 { 307 struct atkbdc_softc *sc; 308 uint8_t buf; 309 int retval; 310 311 if (bytes != 1) 312 return (-1); 313 sc = arg; 314 retval = 0; 315 316 pthread_mutex_lock(&sc->mtx); 317 if (in) { 318 sc->curcmd = 0; 319 if (sc->ctrlbyte != 0) { 320 *eax = sc->ctrlbyte & 0xff; 321 sc->ctrlbyte = 0; 322 } else { 323 /* read device buffer; includes kbd cmd responses */ 324 atkbdc_dequeue_data(sc, &buf); 325 *eax = buf; 326 } 327 328 sc->status &= ~KBDS_CTRL_FLAG; 329 pthread_mutex_unlock(&sc->mtx); 330 return (retval); 331 } 332 333 if (sc->status & KBDS_CTRL_FLAG) { 334 /* 335 * Command byte for the controller. 336 */ 337 switch (sc->curcmd) { 338 case KBDC_SET_COMMAND_BYTE: 339 sc->ram[0] = *eax; 340 if (sc->ram[0] & KBD_SYS_FLAG_BIT) 341 sc->status |= KBDS_SYS_FLAG; 342 else 343 sc->status &= ~KBDS_SYS_FLAG; 344 break; 345 case KBDC_WRITE_OUTPORT: 346 sc->outport = *eax; 347 break; 348 case KBDC_WRITE_TO_AUX: 349 ps2mouse_write(sc->ps2mouse_sc, *eax, 0); 350 atkbdc_poll(sc); 351 break; 352 case KBDC_WRITE_KBD_OUTBUF: 353 atkbdc_kbd_queue_data(sc, *eax); 354 break; 355 case KBDC_WRITE_AUX_OUTBUF: 356 ps2mouse_write(sc->ps2mouse_sc, *eax, 1); 357 sc->status |= (KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL); 358 atkbdc_aux_poll(sc); 359 break; 360 default: 361 /* write to particular RAM byte */ 362 if (sc->curcmd >= 0x61 && sc->curcmd <= 0x7f) { 363 int byten; 364 365 byten = (sc->curcmd - 0x60) & 0x1f; 366 sc->ram[byten] = *eax & 0xff; 367 } 368 break; 369 } 370 371 sc->curcmd = 0; 372 sc->status &= ~KBDS_CTRL_FLAG; 373 374 pthread_mutex_unlock(&sc->mtx); 375 return (retval); 376 } 377 378 /* 379 * Data byte for the device. 380 */ 381 ps2kbd_write(sc->ps2kbd_sc, *eax); 382 atkbdc_poll(sc); 383 384 pthread_mutex_unlock(&sc->mtx); 385 386 return (retval); 387 } 388 389 static int 390 atkbdc_sts_ctl_handler(struct vmctx *ctx, int in, 391 int port __unused, int bytes, uint32_t *eax, void *arg) 392 { 393 struct atkbdc_softc *sc; 394 int error, retval; 395 396 if (bytes != 1) 397 return (-1); 398 399 sc = arg; 400 retval = 0; 401 402 pthread_mutex_lock(&sc->mtx); 403 404 if (in) { 405 /* read status register */ 406 *eax = sc->status; 407 pthread_mutex_unlock(&sc->mtx); 408 return (retval); 409 } 410 411 412 sc->curcmd = 0; 413 sc->status |= KBDS_CTRL_FLAG; 414 sc->ctrlbyte = 0; 415 416 switch (*eax) { 417 case KBDC_GET_COMMAND_BYTE: 418 sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[0]; 419 break; 420 case KBDC_TEST_CTRL: 421 sc->ctrlbyte = CTRL_CMD_FLAG | 0x55; 422 break; 423 case KBDC_TEST_AUX_PORT: 424 case KBDC_TEST_KBD_PORT: 425 sc->ctrlbyte = CTRL_CMD_FLAG | 0; 426 break; 427 case KBDC_READ_INPORT: 428 sc->ctrlbyte = CTRL_CMD_FLAG | 0; 429 break; 430 case KBDC_READ_OUTPORT: 431 sc->ctrlbyte = CTRL_CMD_FLAG | sc->outport; 432 break; 433 case KBDC_SET_COMMAND_BYTE: 434 case KBDC_WRITE_OUTPORT: 435 case KBDC_WRITE_KBD_OUTBUF: 436 case KBDC_WRITE_AUX_OUTBUF: 437 sc->curcmd = *eax; 438 break; 439 case KBDC_DISABLE_KBD_PORT: 440 sc->ram[0] |= KBD_DISABLE_KBD_PORT; 441 break; 442 case KBDC_ENABLE_KBD_PORT: 443 sc->ram[0] &= ~KBD_DISABLE_KBD_PORT; 444 if (sc->kbd.bcnt > 0) 445 sc->status |= KBDS_KBD_BUFFER_FULL; 446 atkbdc_poll(sc); 447 break; 448 case KBDC_WRITE_TO_AUX: 449 sc->curcmd = *eax; 450 break; 451 case KBDC_DISABLE_AUX_PORT: 452 sc->ram[0] |= KBD_DISABLE_AUX_PORT; 453 ps2mouse_toggle(sc->ps2mouse_sc, 0); 454 sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL); 455 sc->outport &= ~KBDS_AUX_BUFFER_FULL; 456 break; 457 case KBDC_ENABLE_AUX_PORT: 458 sc->ram[0] &= ~KBD_DISABLE_AUX_PORT; 459 ps2mouse_toggle(sc->ps2mouse_sc, 1); 460 if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0) 461 sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL; 462 break; 463 case KBDC_RESET: /* Pulse "reset" line */ 464 error = vm_suspend(ctx, VM_SUSPEND_RESET); 465 assert(error == 0 || errno == EALREADY); 466 break; 467 default: 468 if (*eax >= 0x21 && *eax <= 0x3f) { 469 /* read "byte N" from RAM */ 470 int byten; 471 472 byten = (*eax - 0x20) & 0x1f; 473 sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[byten]; 474 } 475 break; 476 } 477 478 pthread_mutex_unlock(&sc->mtx); 479 480 if (sc->ctrlbyte != 0) { 481 sc->status |= KBDS_KBD_BUFFER_FULL; 482 sc->status &= ~KBDS_AUX_BUFFER_FULL; 483 atkbdc_assert_kbd_intr(sc); 484 } else if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0 && 485 (sc->ram[0] & KBD_DISABLE_AUX_PORT) == 0) { 486 sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL; 487 atkbdc_assert_aux_intr(sc); 488 } else if (sc->kbd.bcnt > 0 && (sc->ram[0] & KBD_DISABLE_KBD_PORT) == 0) { 489 sc->status |= KBDS_KBD_BUFFER_FULL; 490 atkbdc_assert_kbd_intr(sc); 491 } 492 493 return (retval); 494 } 495 496 void 497 atkbdc_event(struct atkbdc_softc *sc, int iskbd) 498 { 499 pthread_mutex_lock(&sc->mtx); 500 501 if (iskbd) 502 atkbdc_kbd_poll(sc); 503 else 504 atkbdc_aux_poll(sc); 505 pthread_mutex_unlock(&sc->mtx); 506 } 507 508 void 509 atkbdc_init(struct vmctx *ctx) 510 { 511 struct inout_port iop; 512 struct atkbdc_softc *sc; 513 int error; 514 515 sc = calloc(1, sizeof(struct atkbdc_softc)); 516 sc->ctx = ctx; 517 518 pthread_mutex_init(&sc->mtx, NULL); 519 520 bzero(&iop, sizeof(struct inout_port)); 521 iop.name = "atkdbc"; 522 iop.port = KBD_STS_CTL_PORT; 523 iop.size = 1; 524 iop.flags = IOPORT_F_INOUT; 525 iop.handler = atkbdc_sts_ctl_handler; 526 iop.arg = sc; 527 528 error = register_inout(&iop); 529 assert(error == 0); 530 531 bzero(&iop, sizeof(struct inout_port)); 532 iop.name = "atkdbc"; 533 iop.port = KBD_DATA_PORT; 534 iop.size = 1; 535 iop.flags = IOPORT_F_INOUT; 536 iop.handler = atkbdc_data_handler; 537 iop.arg = sc; 538 539 error = register_inout(&iop); 540 assert(error == 0); 541 542 pci_irq_reserve(KBD_DEV_IRQ); 543 sc->kbd.irq = KBD_DEV_IRQ; 544 545 pci_irq_reserve(AUX_DEV_IRQ); 546 sc->aux.irq = AUX_DEV_IRQ; 547 548 sc->ps2kbd_sc = ps2kbd_init(sc); 549 sc->ps2mouse_sc = ps2mouse_init(sc); 550 } 551 552 static void 553 atkbdc_dsdt(void) 554 { 555 556 dsdt_line(""); 557 dsdt_line("Device (KBD)"); 558 dsdt_line("{"); 559 dsdt_line(" Name (_HID, EisaId (\"PNP0303\"))"); 560 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 561 dsdt_line(" {"); 562 dsdt_indent(2); 563 dsdt_fixed_ioport(KBD_DATA_PORT, 1); 564 dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1); 565 dsdt_fixed_irq(1); 566 dsdt_unindent(2); 567 dsdt_line(" })"); 568 dsdt_line("}"); 569 570 dsdt_line(""); 571 dsdt_line("Device (MOU)"); 572 dsdt_line("{"); 573 dsdt_line(" Name (_HID, EisaId (\"PNP0F13\"))"); 574 dsdt_line(" Name (_CRS, ResourceTemplate ()"); 575 dsdt_line(" {"); 576 dsdt_indent(2); 577 dsdt_fixed_ioport(KBD_DATA_PORT, 1); 578 dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1); 579 dsdt_fixed_irq(12); 580 dsdt_unindent(2); 581 dsdt_line(" })"); 582 dsdt_line("}"); 583 } 584 LPC_DSDT(atkbdc_dsdt); 585 586