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>
47 #include <pthread_np.h>
58 #define KBD_DATA_PORT 0x60
60 #define KBD_STS_CTL_PORT 0x64
62 #define KBDC_RESET 0xfe
65 #define AUX_DEV_IRQ 12
67 /* controller commands */
68 #define KBDC_SET_COMMAND_BYTE 0x60
69 #define KBDC_GET_COMMAND_BYTE 0x20
70 #define KBDC_DISABLE_AUX_PORT 0xa7
71 #define KBDC_ENABLE_AUX_PORT 0xa8
72 #define KBDC_TEST_AUX_PORT 0xa9
73 #define KBDC_TEST_CTRL 0xaa
74 #define KBDC_TEST_KBD_PORT 0xab
75 #define KBDC_DISABLE_KBD_PORT 0xad
76 #define KBDC_ENABLE_KBD_PORT 0xae
77 #define KBDC_READ_INPORT 0xc0
78 #define KBDC_READ_OUTPORT 0xd0
79 #define KBDC_WRITE_OUTPORT 0xd1
80 #define KBDC_WRITE_KBD_OUTBUF 0xd2
81 #define KBDC_WRITE_AUX_OUTBUF 0xd3
82 #define KBDC_WRITE_TO_AUX 0xd4
84 /* controller command byte (set by KBDC_SET_COMMAND_BYTE) */
85 #define KBD_TRANSLATION 0x40
86 #define KBD_SYS_FLAG_BIT 0x04
87 #define KBD_DISABLE_KBD_PORT 0x10
88 #define KBD_DISABLE_AUX_PORT 0x20
89 #define KBD_ENABLE_AUX_INT 0x02
90 #define KBD_ENABLE_KBD_INT 0x01
91 #define KBD_KBD_CONTROL_BITS (KBD_DISABLE_KBD_PORT | KBD_ENABLE_KBD_INT)
92 #define KBD_AUX_CONTROL_BITS (KBD_DISABLE_AUX_PORT | KBD_ENABLE_AUX_INT)
94 /* controller status bits */
95 #define KBDS_KBD_BUFFER_FULL 0x01
96 #define KBDS_SYS_FLAG 0x04
97 #define KBDS_CTRL_FLAG 0x08
98 #define KBDS_AUX_BUFFER_FULL 0x20
100 /* controller output port */
101 #define KBDO_KBD_OUTFULL 0x10
102 #define KBDO_AUX_OUTFULL 0x20
106 #define CTRL_CMD_FLAG 0x8000
112 uint8_t buffer[FIFOSZ];
122 struct atkbdc_softc {
126 struct ps2kbd_softc *ps2kbd_sc;
127 struct ps2mouse_softc *ps2mouse_sc;
129 uint8_t status; /* status register */
130 uint8_t outport; /* controller output port */
131 uint8_t ram[RAMSZ]; /* byte0 = controller config */
133 uint32_t curcmd; /* current command for next byte */
141 atkbdc_assert_kbd_intr(struct atkbdc_softc *sc)
143 if ((sc->ram[0] & KBD_ENABLE_KBD_INT) != 0) {
144 sc->kbd.irq_active = true;
145 vm_isa_pulse_irq(sc->ctx, sc->kbd.irq, sc->kbd.irq);
150 atkbdc_assert_aux_intr(struct atkbdc_softc *sc)
152 if ((sc->ram[0] & KBD_ENABLE_AUX_INT) != 0) {
153 sc->aux.irq_active = true;
154 vm_isa_pulse_irq(sc->ctx, sc->aux.irq, sc->aux.irq);
159 atkbdc_kbd_queue_data(struct atkbdc_softc *sc, uint8_t val)
161 assert(pthread_mutex_isowned_np(&sc->mtx));
163 if (sc->kbd.bcnt < FIFOSZ) {
164 sc->kbd.buffer[sc->kbd.bwr] = val;
165 sc->kbd.bwr = (sc->kbd.bwr + 1) % FIFOSZ;
167 sc->status |= KBDS_KBD_BUFFER_FULL;
168 sc->outport |= KBDO_KBD_OUTFULL;
170 printf("atkbd data buffer full\n");
173 return (sc->kbd.bcnt < FIFOSZ);
177 atkbdc_kbd_read(struct atkbdc_softc *sc)
179 const uint8_t translation[256] = {
180 0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
181 0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
182 0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
183 0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
184 0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
185 0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
186 0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
187 0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
188 0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
189 0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
190 0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
191 0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
192 0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
193 0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
194 0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
195 0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
196 0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
197 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
198 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
199 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
200 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
201 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
202 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
203 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
204 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
205 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
206 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
207 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
208 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
209 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
210 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
211 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
216 assert(pthread_mutex_isowned_np(&sc->mtx));
218 if (sc->ram[0] & KBD_TRANSLATION) {
219 while (ps2kbd_read(sc->ps2kbd_sc, &val) != -1) {
224 val = translation[val] | release;
226 atkbdc_kbd_queue_data(sc, val);
230 while (sc->kbd.bcnt < FIFOSZ) {
231 if (ps2kbd_read(sc->ps2kbd_sc, &val) != -1)
232 atkbdc_kbd_queue_data(sc, val);
238 if (((sc->ram[0] & KBD_DISABLE_AUX_PORT) ||
239 ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) && sc->kbd.bcnt > 0)
240 atkbdc_assert_kbd_intr(sc);
244 atkbdc_aux_poll(struct atkbdc_softc *sc)
246 if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0) {
247 sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
248 sc->outport |= KBDO_AUX_OUTFULL;
249 atkbdc_assert_aux_intr(sc);
254 atkbdc_kbd_poll(struct atkbdc_softc *sc)
256 assert(pthread_mutex_isowned_np(&sc->mtx));
262 atkbdc_poll(struct atkbdc_softc *sc)
269 atkbdc_dequeue_data(struct atkbdc_softc *sc, uint8_t *buf)
271 assert(pthread_mutex_isowned_np(&sc->mtx));
273 if (ps2mouse_read(sc->ps2mouse_sc, buf) == 0) {
274 if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) {
275 if (sc->kbd.bcnt == 0)
276 sc->status &= ~(KBDS_AUX_BUFFER_FULL |
277 KBDS_KBD_BUFFER_FULL);
279 sc->status &= ~(KBDS_AUX_BUFFER_FULL);
280 sc->outport &= ~KBDO_AUX_OUTFULL;
287 if (sc->kbd.bcnt > 0) {
288 *buf = sc->kbd.buffer[sc->kbd.brd];
289 sc->kbd.brd = (sc->kbd.brd + 1) % FIFOSZ;
291 if (sc->kbd.bcnt == 0) {
292 sc->status &= ~KBDS_KBD_BUFFER_FULL;
293 sc->outport &= ~KBDO_KBD_OUTFULL;
299 if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0 && sc->kbd.bcnt == 0) {
300 sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
305 atkbdc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
306 uint32_t *eax, void *arg)
308 struct atkbdc_softc *sc;
317 pthread_mutex_lock(&sc->mtx);
320 if (sc->ctrlbyte != 0) {
321 *eax = sc->ctrlbyte & 0xff;
324 /* read device buffer; includes kbd cmd responses */
325 atkbdc_dequeue_data(sc, &buf);
329 sc->status &= ~KBDS_CTRL_FLAG;
330 pthread_mutex_unlock(&sc->mtx);
334 if (sc->status & KBDS_CTRL_FLAG) {
336 * Command byte for the controller.
338 switch (sc->curcmd) {
339 case KBDC_SET_COMMAND_BYTE:
341 if (sc->ram[0] & KBD_SYS_FLAG_BIT)
342 sc->status |= KBDS_SYS_FLAG;
344 sc->status &= ~KBDS_SYS_FLAG;
346 case KBDC_WRITE_OUTPORT:
349 case KBDC_WRITE_TO_AUX:
350 ps2mouse_write(sc->ps2mouse_sc, *eax, 0);
353 case KBDC_WRITE_KBD_OUTBUF:
354 atkbdc_kbd_queue_data(sc, *eax);
356 case KBDC_WRITE_AUX_OUTBUF:
357 ps2mouse_write(sc->ps2mouse_sc, *eax, 1);
358 sc->status |= (KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
362 /* write to particular RAM byte */
363 if (sc->curcmd >= 0x61 && sc->curcmd <= 0x7f) {
366 byten = (sc->curcmd - 0x60) & 0x1f;
367 sc->ram[byten] = *eax & 0xff;
373 sc->status &= ~KBDS_CTRL_FLAG;
375 pthread_mutex_unlock(&sc->mtx);
380 * Data byte for the device.
382 ps2kbd_write(sc->ps2kbd_sc, *eax);
385 pthread_mutex_unlock(&sc->mtx);
391 atkbdc_sts_ctl_handler(struct vmctx *ctx, int vcpu, int in, int port,
392 int bytes, uint32_t *eax, void *arg)
394 struct atkbdc_softc *sc;
403 pthread_mutex_lock(&sc->mtx);
406 /* read status register */
408 pthread_mutex_unlock(&sc->mtx);
414 sc->status |= KBDS_CTRL_FLAG;
418 case KBDC_GET_COMMAND_BYTE:
419 sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[0];
422 sc->ctrlbyte = CTRL_CMD_FLAG | 0x55;
424 case KBDC_TEST_AUX_PORT:
425 case KBDC_TEST_KBD_PORT:
426 sc->ctrlbyte = CTRL_CMD_FLAG | 0;
428 case KBDC_READ_INPORT:
429 sc->ctrlbyte = CTRL_CMD_FLAG | 0;
431 case KBDC_READ_OUTPORT:
432 sc->ctrlbyte = CTRL_CMD_FLAG | sc->outport;
434 case KBDC_SET_COMMAND_BYTE:
435 case KBDC_WRITE_OUTPORT:
436 case KBDC_WRITE_KBD_OUTBUF:
437 case KBDC_WRITE_AUX_OUTBUF:
440 case KBDC_DISABLE_KBD_PORT:
441 sc->ram[0] |= KBD_DISABLE_KBD_PORT;
443 case KBDC_ENABLE_KBD_PORT:
444 sc->ram[0] &= ~KBD_DISABLE_KBD_PORT;
445 if (sc->kbd.bcnt > 0)
446 sc->status |= KBDS_KBD_BUFFER_FULL;
449 case KBDC_WRITE_TO_AUX:
452 case KBDC_DISABLE_AUX_PORT:
453 sc->ram[0] |= KBD_DISABLE_AUX_PORT;
454 ps2mouse_toggle(sc->ps2mouse_sc, 0);
455 sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
456 sc->outport &= ~KBDS_AUX_BUFFER_FULL;
458 case KBDC_ENABLE_AUX_PORT:
459 sc->ram[0] &= ~KBD_DISABLE_AUX_PORT;
460 ps2mouse_toggle(sc->ps2mouse_sc, 1);
461 if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0)
462 sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
464 case KBDC_RESET: /* Pulse "reset" line */
465 error = vm_suspend(ctx, VM_SUSPEND_RESET);
466 assert(error == 0 || errno == EALREADY);
469 if (*eax >= 0x21 && *eax <= 0x3f) {
470 /* read "byte N" from RAM */
473 byten = (*eax - 0x20) & 0x1f;
474 sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[byten];
479 pthread_mutex_unlock(&sc->mtx);
481 if (sc->ctrlbyte != 0) {
482 sc->status |= KBDS_KBD_BUFFER_FULL;
483 sc->status &= ~KBDS_AUX_BUFFER_FULL;
484 atkbdc_assert_kbd_intr(sc);
485 } else if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0 &&
486 (sc->ram[0] & KBD_DISABLE_AUX_PORT) == 0) {
487 sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
488 atkbdc_assert_aux_intr(sc);
489 } else if (sc->kbd.bcnt > 0 && (sc->ram[0] & KBD_DISABLE_KBD_PORT) == 0) {
490 sc->status |= KBDS_KBD_BUFFER_FULL;
491 atkbdc_assert_kbd_intr(sc);
498 atkbdc_event(struct atkbdc_softc *sc, int iskbd)
500 pthread_mutex_lock(&sc->mtx);
506 pthread_mutex_unlock(&sc->mtx);
510 atkbdc_init(struct vmctx *ctx)
512 struct inout_port iop;
513 struct atkbdc_softc *sc;
516 sc = calloc(1, sizeof(struct atkbdc_softc));
519 pthread_mutex_init(&sc->mtx, NULL);
521 bzero(&iop, sizeof(struct inout_port));
523 iop.port = KBD_STS_CTL_PORT;
525 iop.flags = IOPORT_F_INOUT;
526 iop.handler = atkbdc_sts_ctl_handler;
529 error = register_inout(&iop);
532 bzero(&iop, sizeof(struct inout_port));
534 iop.port = KBD_DATA_PORT;
536 iop.flags = IOPORT_F_INOUT;
537 iop.handler = atkbdc_data_handler;
540 error = register_inout(&iop);
543 pci_irq_reserve(KBD_DEV_IRQ);
544 sc->kbd.irq = KBD_DEV_IRQ;
546 pci_irq_reserve(AUX_DEV_IRQ);
547 sc->aux.irq = AUX_DEV_IRQ;
549 sc->ps2kbd_sc = ps2kbd_init(sc);
550 sc->ps2mouse_sc = ps2mouse_init(sc);
558 dsdt_line("Device (KBD)");
560 dsdt_line(" Name (_HID, EisaId (\"PNP0303\"))");
561 dsdt_line(" Name (_CRS, ResourceTemplate ()");
564 dsdt_fixed_ioport(KBD_DATA_PORT, 1);
565 dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1);
572 dsdt_line("Device (MOU)");
574 dsdt_line(" Name (_HID, EisaId (\"PNP0F13\"))");
575 dsdt_line(" Name (_CRS, ResourceTemplate ()");
578 dsdt_fixed_ioport(KBD_DATA_PORT, 1);
579 dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1);
585 LPC_DSDT(atkbdc_dsdt);