2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 __FBSDID("$FreeBSD$");
33 #include <sys/types.h>
35 #include <machine/vmm.h>
36 #include <machine/vmm_snapshot.h>
48 #include <pthread_np.h>
59 #define KBD_DATA_PORT 0x60
61 #define KBD_STS_CTL_PORT 0x64
63 #define KBDC_RESET 0xfe
66 #define AUX_DEV_IRQ 12
68 /* controller commands */
69 #define KBDC_SET_COMMAND_BYTE 0x60
70 #define KBDC_GET_COMMAND_BYTE 0x20
71 #define KBDC_DISABLE_AUX_PORT 0xa7
72 #define KBDC_ENABLE_AUX_PORT 0xa8
73 #define KBDC_TEST_AUX_PORT 0xa9
74 #define KBDC_TEST_CTRL 0xaa
75 #define KBDC_TEST_KBD_PORT 0xab
76 #define KBDC_DISABLE_KBD_PORT 0xad
77 #define KBDC_ENABLE_KBD_PORT 0xae
78 #define KBDC_READ_INPORT 0xc0
79 #define KBDC_READ_OUTPORT 0xd0
80 #define KBDC_WRITE_OUTPORT 0xd1
81 #define KBDC_WRITE_KBD_OUTBUF 0xd2
82 #define KBDC_WRITE_AUX_OUTBUF 0xd3
83 #define KBDC_WRITE_TO_AUX 0xd4
85 /* controller command byte (set by KBDC_SET_COMMAND_BYTE) */
86 #define KBD_TRANSLATION 0x40
87 #define KBD_SYS_FLAG_BIT 0x04
88 #define KBD_DISABLE_KBD_PORT 0x10
89 #define KBD_DISABLE_AUX_PORT 0x20
90 #define KBD_ENABLE_AUX_INT 0x02
91 #define KBD_ENABLE_KBD_INT 0x01
92 #define KBD_KBD_CONTROL_BITS (KBD_DISABLE_KBD_PORT | KBD_ENABLE_KBD_INT)
93 #define KBD_AUX_CONTROL_BITS (KBD_DISABLE_AUX_PORT | KBD_ENABLE_AUX_INT)
95 /* controller status bits */
96 #define KBDS_KBD_BUFFER_FULL 0x01
97 #define KBDS_SYS_FLAG 0x04
98 #define KBDS_CTRL_FLAG 0x08
99 #define KBDS_AUX_BUFFER_FULL 0x20
101 /* controller output port */
102 #define KBDO_KBD_OUTFULL 0x10
103 #define KBDO_AUX_OUTFULL 0x20
107 #define CTRL_CMD_FLAG 0x8000
113 uint8_t buffer[FIFOSZ];
123 struct atkbdc_softc {
127 struct ps2kbd_softc *ps2kbd_sc;
128 struct ps2mouse_softc *ps2mouse_sc;
130 uint8_t status; /* status register */
131 uint8_t outport; /* controller output port */
132 uint8_t ram[RAMSZ]; /* byte0 = controller config */
134 uint32_t curcmd; /* current command for next byte */
141 #ifdef BHYVE_SNAPSHOT
142 static struct atkbdc_softc *atkbdc_sc = NULL;
146 atkbdc_assert_kbd_intr(struct atkbdc_softc *sc)
148 if ((sc->ram[0] & KBD_ENABLE_KBD_INT) != 0) {
149 sc->kbd.irq_active = true;
150 vm_isa_pulse_irq(sc->ctx, sc->kbd.irq, sc->kbd.irq);
155 atkbdc_assert_aux_intr(struct atkbdc_softc *sc)
157 if ((sc->ram[0] & KBD_ENABLE_AUX_INT) != 0) {
158 sc->aux.irq_active = true;
159 vm_isa_pulse_irq(sc->ctx, sc->aux.irq, sc->aux.irq);
164 atkbdc_kbd_queue_data(struct atkbdc_softc *sc, uint8_t val)
166 assert(pthread_mutex_isowned_np(&sc->mtx));
168 if (sc->kbd.bcnt < FIFOSZ) {
169 sc->kbd.buffer[sc->kbd.bwr] = val;
170 sc->kbd.bwr = (sc->kbd.bwr + 1) % FIFOSZ;
172 sc->status |= KBDS_KBD_BUFFER_FULL;
173 sc->outport |= KBDO_KBD_OUTFULL;
175 printf("atkbd data buffer full\n");
178 return (sc->kbd.bcnt < FIFOSZ);
182 atkbdc_kbd_read(struct atkbdc_softc *sc)
184 const uint8_t translation[256] = {
185 0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
186 0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
187 0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
188 0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
189 0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
190 0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
191 0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
192 0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
193 0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
194 0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
195 0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
196 0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
197 0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
198 0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
199 0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
200 0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
201 0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
202 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
203 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
204 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
205 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
206 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
207 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
208 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
209 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
210 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
211 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
212 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
213 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
214 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
215 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
216 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
221 assert(pthread_mutex_isowned_np(&sc->mtx));
223 if (sc->ram[0] & KBD_TRANSLATION) {
224 while (ps2kbd_read(sc->ps2kbd_sc, &val) != -1) {
229 val = translation[val] | release;
231 atkbdc_kbd_queue_data(sc, val);
235 while (sc->kbd.bcnt < FIFOSZ) {
236 if (ps2kbd_read(sc->ps2kbd_sc, &val) != -1)
237 atkbdc_kbd_queue_data(sc, val);
243 if (((sc->ram[0] & KBD_DISABLE_AUX_PORT) ||
244 ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) && sc->kbd.bcnt > 0)
245 atkbdc_assert_kbd_intr(sc);
249 atkbdc_aux_poll(struct atkbdc_softc *sc)
251 if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0) {
252 sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
253 sc->outport |= KBDO_AUX_OUTFULL;
254 atkbdc_assert_aux_intr(sc);
259 atkbdc_kbd_poll(struct atkbdc_softc *sc)
261 assert(pthread_mutex_isowned_np(&sc->mtx));
267 atkbdc_poll(struct atkbdc_softc *sc)
274 atkbdc_dequeue_data(struct atkbdc_softc *sc, uint8_t *buf)
276 assert(pthread_mutex_isowned_np(&sc->mtx));
278 if (ps2mouse_read(sc->ps2mouse_sc, buf) == 0) {
279 if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) {
280 if (sc->kbd.bcnt == 0)
281 sc->status &= ~(KBDS_AUX_BUFFER_FULL |
282 KBDS_KBD_BUFFER_FULL);
284 sc->status &= ~(KBDS_AUX_BUFFER_FULL);
285 sc->outport &= ~KBDO_AUX_OUTFULL;
292 if (sc->kbd.bcnt > 0) {
293 *buf = sc->kbd.buffer[sc->kbd.brd];
294 sc->kbd.brd = (sc->kbd.brd + 1) % FIFOSZ;
296 if (sc->kbd.bcnt == 0) {
297 sc->status &= ~KBDS_KBD_BUFFER_FULL;
298 sc->outport &= ~KBDO_KBD_OUTFULL;
304 if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0 && sc->kbd.bcnt == 0) {
305 sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
310 atkbdc_data_handler(struct vmctx *ctx __unused, int vcpu __unused, int in,
311 int port __unused, int bytes, uint32_t *eax, void *arg)
313 struct atkbdc_softc *sc;
322 pthread_mutex_lock(&sc->mtx);
325 if (sc->ctrlbyte != 0) {
326 *eax = sc->ctrlbyte & 0xff;
329 /* read device buffer; includes kbd cmd responses */
330 atkbdc_dequeue_data(sc, &buf);
334 sc->status &= ~KBDS_CTRL_FLAG;
335 pthread_mutex_unlock(&sc->mtx);
339 if (sc->status & KBDS_CTRL_FLAG) {
341 * Command byte for the controller.
343 switch (sc->curcmd) {
344 case KBDC_SET_COMMAND_BYTE:
346 if (sc->ram[0] & KBD_SYS_FLAG_BIT)
347 sc->status |= KBDS_SYS_FLAG;
349 sc->status &= ~KBDS_SYS_FLAG;
351 case KBDC_WRITE_OUTPORT:
354 case KBDC_WRITE_TO_AUX:
355 ps2mouse_write(sc->ps2mouse_sc, *eax, 0);
358 case KBDC_WRITE_KBD_OUTBUF:
359 atkbdc_kbd_queue_data(sc, *eax);
361 case KBDC_WRITE_AUX_OUTBUF:
362 ps2mouse_write(sc->ps2mouse_sc, *eax, 1);
363 sc->status |= (KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
367 /* write to particular RAM byte */
368 if (sc->curcmd >= 0x61 && sc->curcmd <= 0x7f) {
371 byten = (sc->curcmd - 0x60) & 0x1f;
372 sc->ram[byten] = *eax & 0xff;
378 sc->status &= ~KBDS_CTRL_FLAG;
380 pthread_mutex_unlock(&sc->mtx);
385 * Data byte for the device.
387 ps2kbd_write(sc->ps2kbd_sc, *eax);
390 pthread_mutex_unlock(&sc->mtx);
396 atkbdc_sts_ctl_handler(struct vmctx *ctx, int vcpu __unused, int in,
397 int port __unused, int bytes, uint32_t *eax, void *arg)
399 struct atkbdc_softc *sc;
408 pthread_mutex_lock(&sc->mtx);
411 /* read status register */
413 pthread_mutex_unlock(&sc->mtx);
419 sc->status |= KBDS_CTRL_FLAG;
423 case KBDC_GET_COMMAND_BYTE:
424 sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[0];
427 sc->ctrlbyte = CTRL_CMD_FLAG | 0x55;
429 case KBDC_TEST_AUX_PORT:
430 case KBDC_TEST_KBD_PORT:
431 sc->ctrlbyte = CTRL_CMD_FLAG | 0;
433 case KBDC_READ_INPORT:
434 sc->ctrlbyte = CTRL_CMD_FLAG | 0;
436 case KBDC_READ_OUTPORT:
437 sc->ctrlbyte = CTRL_CMD_FLAG | sc->outport;
439 case KBDC_SET_COMMAND_BYTE:
440 case KBDC_WRITE_OUTPORT:
441 case KBDC_WRITE_KBD_OUTBUF:
442 case KBDC_WRITE_AUX_OUTBUF:
445 case KBDC_DISABLE_KBD_PORT:
446 sc->ram[0] |= KBD_DISABLE_KBD_PORT;
448 case KBDC_ENABLE_KBD_PORT:
449 sc->ram[0] &= ~KBD_DISABLE_KBD_PORT;
450 if (sc->kbd.bcnt > 0)
451 sc->status |= KBDS_KBD_BUFFER_FULL;
454 case KBDC_WRITE_TO_AUX:
457 case KBDC_DISABLE_AUX_PORT:
458 sc->ram[0] |= KBD_DISABLE_AUX_PORT;
459 ps2mouse_toggle(sc->ps2mouse_sc, 0);
460 sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
461 sc->outport &= ~KBDS_AUX_BUFFER_FULL;
463 case KBDC_ENABLE_AUX_PORT:
464 sc->ram[0] &= ~KBD_DISABLE_AUX_PORT;
465 ps2mouse_toggle(sc->ps2mouse_sc, 1);
466 if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0)
467 sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
469 case KBDC_RESET: /* Pulse "reset" line */
470 error = vm_suspend(ctx, VM_SUSPEND_RESET);
471 assert(error == 0 || errno == EALREADY);
474 if (*eax >= 0x21 && *eax <= 0x3f) {
475 /* read "byte N" from RAM */
478 byten = (*eax - 0x20) & 0x1f;
479 sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[byten];
484 pthread_mutex_unlock(&sc->mtx);
486 if (sc->ctrlbyte != 0) {
487 sc->status |= KBDS_KBD_BUFFER_FULL;
488 sc->status &= ~KBDS_AUX_BUFFER_FULL;
489 atkbdc_assert_kbd_intr(sc);
490 } else if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0 &&
491 (sc->ram[0] & KBD_DISABLE_AUX_PORT) == 0) {
492 sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
493 atkbdc_assert_aux_intr(sc);
494 } else if (sc->kbd.bcnt > 0 && (sc->ram[0] & KBD_DISABLE_KBD_PORT) == 0) {
495 sc->status |= KBDS_KBD_BUFFER_FULL;
496 atkbdc_assert_kbd_intr(sc);
503 atkbdc_event(struct atkbdc_softc *sc, int iskbd)
505 pthread_mutex_lock(&sc->mtx);
511 pthread_mutex_unlock(&sc->mtx);
515 atkbdc_init(struct vmctx *ctx)
517 struct inout_port iop;
518 struct atkbdc_softc *sc;
521 sc = calloc(1, sizeof(struct atkbdc_softc));
524 pthread_mutex_init(&sc->mtx, NULL);
526 bzero(&iop, sizeof(struct inout_port));
528 iop.port = KBD_STS_CTL_PORT;
530 iop.flags = IOPORT_F_INOUT;
531 iop.handler = atkbdc_sts_ctl_handler;
534 error = register_inout(&iop);
537 bzero(&iop, sizeof(struct inout_port));
539 iop.port = KBD_DATA_PORT;
541 iop.flags = IOPORT_F_INOUT;
542 iop.handler = atkbdc_data_handler;
545 error = register_inout(&iop);
548 pci_irq_reserve(KBD_DEV_IRQ);
549 sc->kbd.irq = KBD_DEV_IRQ;
551 pci_irq_reserve(AUX_DEV_IRQ);
552 sc->aux.irq = AUX_DEV_IRQ;
554 sc->ps2kbd_sc = ps2kbd_init(sc);
555 sc->ps2mouse_sc = ps2mouse_init(sc);
557 #ifdef BHYVE_SNAPSHOT
558 assert(atkbdc_sc == NULL);
563 #ifdef BHYVE_SNAPSHOT
565 atkbdc_snapshot(struct vm_snapshot_meta *meta)
569 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->status, meta, ret, done);
570 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->outport, meta, ret, done);
571 SNAPSHOT_BUF_OR_LEAVE(atkbdc_sc->ram,
572 sizeof(atkbdc_sc->ram), meta, ret, done);
573 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->curcmd, meta, ret, done);
574 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->ctrlbyte, meta, ret, done);
575 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd, meta, ret, done);
577 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.irq_active, meta, ret, done);
578 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.irq, meta, ret, done);
579 SNAPSHOT_BUF_OR_LEAVE(atkbdc_sc->kbd.buffer,
580 sizeof(atkbdc_sc->kbd.buffer), meta, ret, done);
581 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.brd, meta, ret, done);
582 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.bwr, meta, ret, done);
583 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.bcnt, meta, ret, done);
585 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->aux.irq_active, meta, ret, done);
586 SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->aux.irq, meta, ret, done);
588 ret = ps2kbd_snapshot(atkbdc_sc->ps2kbd_sc, meta);
592 ret = ps2mouse_snapshot(atkbdc_sc->ps2mouse_sc, meta);
604 dsdt_line("Device (KBD)");
606 dsdt_line(" Name (_HID, EisaId (\"PNP0303\"))");
607 dsdt_line(" Name (_CRS, ResourceTemplate ()");
610 dsdt_fixed_ioport(KBD_DATA_PORT, 1);
611 dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1);
618 dsdt_line("Device (MOU)");
620 dsdt_line(" Name (_HID, EisaId (\"PNP0F13\"))");
621 dsdt_line(" Name (_CRS, ResourceTemplate ()");
624 dsdt_fixed_ioport(KBD_DATA_PORT, 1);
625 dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1);
631 LPC_DSDT(atkbdc_dsdt);