2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
5 * Copyright (c) 2015 Nahanni Systems Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/cdefs.h>
31 #include <sys/types.h>
33 #include <machine/vmm.h>
34 #include <machine/vmm_snapshot.h>
46 #include <pthread_np.h>
57 #define KBD_DATA_PORT 0x60
59 #define KBD_STS_CTL_PORT 0x64
61 #define KBDC_RESET 0xfe
64 #define AUX_DEV_IRQ 12
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
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)
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
99 /* controller output port */
100 #define KBDO_KBD_OUTFULL 0x10
101 #define KBDO_AUX_OUTFULL 0x20
105 #define CTRL_CMD_FLAG 0x8000
111 uint8_t buffer[FIFOSZ];
121 struct atkbdc_softc {
125 struct ps2kbd_softc *ps2kbd_sc;
126 struct ps2mouse_softc *ps2mouse_sc;
128 uint8_t status; /* status register */
129 uint8_t outport; /* controller output port */
130 uint8_t ram[RAMSZ]; /* byte0 = controller config */
132 uint32_t curcmd; /* current command for next byte */
139 #ifdef BHYVE_SNAPSHOT
140 static struct atkbdc_softc *atkbdc_sc = NULL;
144 atkbdc_assert_kbd_intr(struct atkbdc_softc *sc)
146 if ((sc->ram[0] & KBD_ENABLE_KBD_INT) != 0) {
147 sc->kbd.irq_active = true;
148 vm_isa_pulse_irq(sc->ctx, sc->kbd.irq, sc->kbd.irq);
153 atkbdc_assert_aux_intr(struct atkbdc_softc *sc)
155 if ((sc->ram[0] & KBD_ENABLE_AUX_INT) != 0) {
156 sc->aux.irq_active = true;
157 vm_isa_pulse_irq(sc->ctx, sc->aux.irq, sc->aux.irq);
162 atkbdc_kbd_queue_data(struct atkbdc_softc *sc, uint8_t val)
164 assert(pthread_mutex_isowned_np(&sc->mtx));
166 if (sc->kbd.bcnt < FIFOSZ) {
167 sc->kbd.buffer[sc->kbd.bwr] = val;
168 sc->kbd.bwr = (sc->kbd.bwr + 1) % FIFOSZ;
170 sc->status |= KBDS_KBD_BUFFER_FULL;
171 sc->outport |= KBDO_KBD_OUTFULL;
173 printf("atkbd data buffer full\n");
176 return (sc->kbd.bcnt < FIFOSZ);
180 atkbdc_kbd_read(struct atkbdc_softc *sc)
182 const uint8_t translation[256] = {
183 0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
184 0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
185 0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
186 0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
187 0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
188 0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
189 0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
190 0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
191 0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
192 0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
193 0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
194 0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
195 0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
196 0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
197 0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
198 0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
199 0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
200 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
201 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
202 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
203 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
204 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
205 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
206 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
207 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
208 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
209 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
210 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
211 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
212 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
213 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
214 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
219 assert(pthread_mutex_isowned_np(&sc->mtx));
221 if (sc->ram[0] & KBD_TRANSLATION) {
222 while (ps2kbd_read(sc->ps2kbd_sc, &val) != -1) {
227 val = translation[val] | release;
229 atkbdc_kbd_queue_data(sc, val);
233 while (sc->kbd.bcnt < FIFOSZ) {
234 if (ps2kbd_read(sc->ps2kbd_sc, &val) != -1)
235 atkbdc_kbd_queue_data(sc, val);
241 if (((sc->ram[0] & KBD_DISABLE_AUX_PORT) ||
242 ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) && sc->kbd.bcnt > 0)
243 atkbdc_assert_kbd_intr(sc);
247 atkbdc_aux_poll(struct atkbdc_softc *sc)
249 if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0) {
250 sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
251 sc->outport |= KBDO_AUX_OUTFULL;
252 atkbdc_assert_aux_intr(sc);
257 atkbdc_kbd_poll(struct atkbdc_softc *sc)
259 assert(pthread_mutex_isowned_np(&sc->mtx));
265 atkbdc_poll(struct atkbdc_softc *sc)
272 atkbdc_dequeue_data(struct atkbdc_softc *sc, uint8_t *buf)
274 assert(pthread_mutex_isowned_np(&sc->mtx));
276 if (ps2mouse_read(sc->ps2mouse_sc, buf) == 0) {
277 if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) {
278 if (sc->kbd.bcnt == 0)
279 sc->status &= ~(KBDS_AUX_BUFFER_FULL |
280 KBDS_KBD_BUFFER_FULL);
282 sc->status &= ~(KBDS_AUX_BUFFER_FULL);
283 sc->outport &= ~KBDO_AUX_OUTFULL;
290 if (sc->kbd.bcnt > 0) {
291 *buf = sc->kbd.buffer[sc->kbd.brd];
292 sc->kbd.brd = (sc->kbd.brd + 1) % FIFOSZ;
294 if (sc->kbd.bcnt == 0) {
295 sc->status &= ~KBDS_KBD_BUFFER_FULL;
296 sc->outport &= ~KBDO_KBD_OUTFULL;
302 if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0 && sc->kbd.bcnt == 0) {
303 sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
308 atkbdc_data_handler(struct vmctx *ctx __unused, int in,
309 int port __unused, int bytes, uint32_t *eax, void *arg)
311 struct atkbdc_softc *sc;
320 pthread_mutex_lock(&sc->mtx);
323 if (sc->ctrlbyte != 0) {
324 *eax = sc->ctrlbyte & 0xff;
327 /* read device buffer; includes kbd cmd responses */
328 atkbdc_dequeue_data(sc, &buf);
332 sc->status &= ~KBDS_CTRL_FLAG;
333 pthread_mutex_unlock(&sc->mtx);
337 if (sc->status & KBDS_CTRL_FLAG) {
339 * Command byte for the controller.
341 switch (sc->curcmd) {
342 case KBDC_SET_COMMAND_BYTE:
344 if (sc->ram[0] & KBD_SYS_FLAG_BIT)
345 sc->status |= KBDS_SYS_FLAG;
347 sc->status &= ~KBDS_SYS_FLAG;
349 case KBDC_WRITE_OUTPORT:
352 case KBDC_WRITE_TO_AUX:
353 ps2mouse_write(sc->ps2mouse_sc, *eax, 0);
356 case KBDC_WRITE_KBD_OUTBUF:
357 atkbdc_kbd_queue_data(sc, *eax);
359 case KBDC_WRITE_AUX_OUTBUF:
360 ps2mouse_write(sc->ps2mouse_sc, *eax, 1);
361 sc->status |= (KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
365 /* write to particular RAM byte */
366 if (sc->curcmd >= 0x61 && sc->curcmd <= 0x7f) {
369 byten = (sc->curcmd - 0x60) & 0x1f;
370 sc->ram[byten] = *eax & 0xff;
376 sc->status &= ~KBDS_CTRL_FLAG;
378 pthread_mutex_unlock(&sc->mtx);
383 * Data byte for the device.
385 ps2kbd_write(sc->ps2kbd_sc, *eax);
388 pthread_mutex_unlock(&sc->mtx);
394 atkbdc_sts_ctl_handler(struct vmctx *ctx, int in,
395 int port __unused, int bytes, uint32_t *eax, void *arg)
397 struct atkbdc_softc *sc;
406 pthread_mutex_lock(&sc->mtx);
409 /* read status register */
411 pthread_mutex_unlock(&sc->mtx);
417 sc->status |= KBDS_CTRL_FLAG;
421 case KBDC_GET_COMMAND_BYTE:
422 sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[0];
425 sc->ctrlbyte = CTRL_CMD_FLAG | 0x55;
427 case KBDC_TEST_AUX_PORT:
428 case KBDC_TEST_KBD_PORT:
429 sc->ctrlbyte = CTRL_CMD_FLAG | 0;
431 case KBDC_READ_INPORT:
432 sc->ctrlbyte = CTRL_CMD_FLAG | 0;
434 case KBDC_READ_OUTPORT:
435 sc->ctrlbyte = CTRL_CMD_FLAG | sc->outport;
437 case KBDC_SET_COMMAND_BYTE:
438 case KBDC_WRITE_OUTPORT:
439 case KBDC_WRITE_KBD_OUTBUF:
440 case KBDC_WRITE_AUX_OUTBUF:
443 case KBDC_DISABLE_KBD_PORT:
444 sc->ram[0] |= KBD_DISABLE_KBD_PORT;
446 case KBDC_ENABLE_KBD_PORT:
447 sc->ram[0] &= ~KBD_DISABLE_KBD_PORT;
448 if (sc->kbd.bcnt > 0)
449 sc->status |= KBDS_KBD_BUFFER_FULL;
452 case KBDC_WRITE_TO_AUX:
455 case KBDC_DISABLE_AUX_PORT:
456 sc->ram[0] |= KBD_DISABLE_AUX_PORT;
457 ps2mouse_toggle(sc->ps2mouse_sc, 0);
458 sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
459 sc->outport &= ~KBDS_AUX_BUFFER_FULL;
461 case KBDC_ENABLE_AUX_PORT:
462 sc->ram[0] &= ~KBD_DISABLE_AUX_PORT;
463 ps2mouse_toggle(sc->ps2mouse_sc, 1);
464 if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0)
465 sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
467 case KBDC_RESET: /* Pulse "reset" line */
468 error = vm_suspend(ctx, VM_SUSPEND_RESET);
469 assert(error == 0 || errno == EALREADY);
472 if (*eax >= 0x21 && *eax <= 0x3f) {
473 /* read "byte N" from RAM */
476 byten = (*eax - 0x20) & 0x1f;
477 sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[byten];
482 pthread_mutex_unlock(&sc->mtx);
484 if (sc->ctrlbyte != 0) {
485 sc->status |= KBDS_KBD_BUFFER_FULL;
486 sc->status &= ~KBDS_AUX_BUFFER_FULL;
487 atkbdc_assert_kbd_intr(sc);
488 } else if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0 &&
489 (sc->ram[0] & KBD_DISABLE_AUX_PORT) == 0) {
490 sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
491 atkbdc_assert_aux_intr(sc);
492 } else if (sc->kbd.bcnt > 0 && (sc->ram[0] & KBD_DISABLE_KBD_PORT) == 0) {
493 sc->status |= KBDS_KBD_BUFFER_FULL;
494 atkbdc_assert_kbd_intr(sc);
501 atkbdc_event(struct atkbdc_softc *sc, int iskbd)
503 pthread_mutex_lock(&sc->mtx);
509 pthread_mutex_unlock(&sc->mtx);
513 atkbdc_init(struct vmctx *ctx)
515 struct inout_port iop;
516 struct atkbdc_softc *sc;
519 sc = calloc(1, sizeof(struct atkbdc_softc));
522 pthread_mutex_init(&sc->mtx, NULL);
524 bzero(&iop, sizeof(struct inout_port));
526 iop.port = KBD_STS_CTL_PORT;
528 iop.flags = IOPORT_F_INOUT;
529 iop.handler = atkbdc_sts_ctl_handler;
532 error = register_inout(&iop);
535 bzero(&iop, sizeof(struct inout_port));
537 iop.port = KBD_DATA_PORT;
539 iop.flags = IOPORT_F_INOUT;
540 iop.handler = atkbdc_data_handler;
543 error = register_inout(&iop);
546 pci_irq_reserve(KBD_DEV_IRQ);
547 sc->kbd.irq = KBD_DEV_IRQ;
549 pci_irq_reserve(AUX_DEV_IRQ);
550 sc->aux.irq = AUX_DEV_IRQ;
552 sc->ps2kbd_sc = ps2kbd_init(sc);
553 sc->ps2mouse_sc = ps2mouse_init(sc);
555 #ifdef BHYVE_SNAPSHOT
556 assert(atkbdc_sc == NULL);
561 #ifdef BHYVE_SNAPSHOT
563 atkbdc_snapshot(struct vm_snapshot_meta *meta)
567 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->status, meta, ret, done);
568 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->outport, meta, ret, done);
569 SNAPSHOT_BUF_OR_LEAVE(atkbdc_sc->ram,
570 sizeof(atkbdc_sc->ram), meta, ret, done);
571 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->curcmd, meta, ret, done);
572 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->ctrlbyte, meta, ret, done);
573 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd, meta, ret, done);
575 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.irq_active, meta, ret, done);
576 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.irq, meta, ret, done);
577 SNAPSHOT_BUF_OR_LEAVE(atkbdc_sc->kbd.buffer,
578 sizeof(atkbdc_sc->kbd.buffer), meta, ret, done);
579 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.brd, meta, ret, done);
580 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.bwr, meta, ret, done);
581 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.bcnt, meta, ret, done);
583 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->aux.irq_active, meta, ret, done);
584 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->aux.irq, meta, ret, done);
586 ret = ps2kbd_snapshot(atkbdc_sc->ps2kbd_sc, meta);
590 ret = ps2mouse_snapshot(atkbdc_sc->ps2mouse_sc, meta);
602 dsdt_line("Device (KBD)");
604 dsdt_line(" Name (_HID, EisaId (\"PNP0303\"))");
605 dsdt_line(" Name (_CRS, ResourceTemplate ()");
608 dsdt_fixed_ioport(KBD_DATA_PORT, 1);
609 dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1);
616 dsdt_line("Device (MOU)");
618 dsdt_line(" Name (_HID, EisaId (\"PNP0F13\"))");
619 dsdt_line(" Name (_CRS, ResourceTemplate ()");
622 dsdt_fixed_ioport(KBD_DATA_PORT, 1);
623 dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1);
629 LPC_DSDT(atkbdc_dsdt);