2 * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3 * Copyright (c) 2015 Nahanni Systems Inc.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <sys/types.h>
33 #include <machine/vmm.h>
45 #include <pthread_np.h>
55 #define KBD_DATA_PORT 0x60
57 #define KBD_STS_CTL_PORT 0x64
59 #define KBDC_RESET 0xfe
62 #define AUX_DEV_IRQ 12
64 /* controller commands */
65 #define KBDC_SET_COMMAND_BYTE 0x60
66 #define KBDC_GET_COMMAND_BYTE 0x20
67 #define KBDC_DISABLE_AUX_PORT 0xa7
68 #define KBDC_ENABLE_AUX_PORT 0xa8
69 #define KBDC_TEST_AUX_PORT 0xa9
70 #define KBDC_TEST_CTRL 0xaa
71 #define KBDC_TEST_KBD_PORT 0xab
72 #define KBDC_DISABLE_KBD_PORT 0xad
73 #define KBDC_ENABLE_KBD_PORT 0xae
74 #define KBDC_READ_INPORT 0xc0
75 #define KBDC_READ_OUTPORT 0xd0
76 #define KBDC_WRITE_OUTPORT 0xd1
77 #define KBDC_WRITE_KBD_OUTBUF 0xd2
78 #define KBDC_WRITE_AUX_OUTBUF 0xd3
79 #define KBDC_WRITE_TO_AUX 0xd4
81 /* controller command byte (set by KBDC_SET_COMMAND_BYTE) */
82 #define KBD_TRANSLATION 0x40
83 #define KBD_SYS_FLAG_BIT 0x04
84 #define KBD_DISABLE_KBD_PORT 0x10
85 #define KBD_DISABLE_AUX_PORT 0x20
86 #define KBD_ENABLE_AUX_INT 0x02
87 #define KBD_ENABLE_KBD_INT 0x01
88 #define KBD_KBD_CONTROL_BITS (KBD_DISABLE_KBD_PORT | KBD_ENABLE_KBD_INT)
89 #define KBD_AUX_CONTROL_BITS (KBD_DISABLE_AUX_PORT | KBD_ENABLE_AUX_INT)
91 /* controller status bits */
92 #define KBDS_KBD_BUFFER_FULL 0x01
93 #define KBDS_SYS_FLAG 0x04
94 #define KBDS_CTRL_FLAG 0x08
95 #define KBDS_AUX_BUFFER_FULL 0x20
97 /* controller output port */
98 #define KBDO_KBD_OUTFULL 0x10
99 #define KBDO_AUX_OUTFULL 0x20
103 #define CTRL_CMD_FLAG 0x8000
109 uint8_t buffer[FIFOSZ];
119 struct atkbdc_softc {
123 struct ps2kbd_softc *ps2kbd_sc;
124 struct ps2mouse_softc *ps2mouse_sc;
126 uint8_t status; /* status register */
127 uint8_t outport; /* controller output port */
128 uint8_t ram[RAMSZ]; /* byte0 = controller config */
130 uint32_t curcmd; /* current command for next byte */
138 atkbdc_assert_kbd_intr(struct atkbdc_softc *sc)
140 if ((sc->ram[0] & KBD_ENABLE_KBD_INT) != 0) {
141 sc->kbd.irq_active = true;
142 vm_isa_pulse_irq(sc->ctx, sc->kbd.irq, sc->kbd.irq);
147 atkbdc_assert_aux_intr(struct atkbdc_softc *sc)
149 if ((sc->ram[0] & KBD_ENABLE_AUX_INT) != 0) {
150 sc->aux.irq_active = true;
151 vm_isa_pulse_irq(sc->ctx, sc->aux.irq, sc->aux.irq);
156 atkbdc_kbd_queue_data(struct atkbdc_softc *sc, uint8_t val)
158 assert(pthread_mutex_isowned_np(&sc->mtx));
160 if (sc->kbd.bcnt < FIFOSZ) {
161 sc->kbd.buffer[sc->kbd.bwr] = val;
162 sc->kbd.bwr = (sc->kbd.bwr + 1) % FIFOSZ;
164 sc->status |= KBDS_KBD_BUFFER_FULL;
165 sc->outport |= KBDO_KBD_OUTFULL;
167 printf("atkbd data buffer full\n");
170 return (sc->kbd.bcnt < FIFOSZ);
174 atkbdc_kbd_read(struct atkbdc_softc *sc)
176 const uint8_t translation[256] = {
177 0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
178 0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
179 0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
180 0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
181 0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
182 0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
183 0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
184 0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
185 0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
186 0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
187 0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
188 0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
189 0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
190 0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
191 0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
192 0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
193 0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
194 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
195 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
196 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
197 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
198 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
199 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
200 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
201 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
202 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
203 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
204 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
205 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
206 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
207 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
208 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
213 assert(pthread_mutex_isowned_np(&sc->mtx));
215 if (sc->ram[0] & KBD_TRANSLATION) {
216 while (ps2kbd_read(sc->ps2kbd_sc, &val) != -1) {
221 val = translation[val] | release;
223 atkbdc_kbd_queue_data(sc, val);
227 while (sc->kbd.bcnt < FIFOSZ) {
228 if (ps2kbd_read(sc->ps2kbd_sc, &val) != -1)
229 atkbdc_kbd_queue_data(sc, val);
235 if (((sc->ram[0] & KBD_DISABLE_AUX_PORT) ||
236 ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) && sc->kbd.bcnt > 0)
237 atkbdc_assert_kbd_intr(sc);
241 atkbdc_aux_poll(struct atkbdc_softc *sc)
243 if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0) {
244 sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
245 sc->outport |= KBDO_AUX_OUTFULL;
246 atkbdc_assert_aux_intr(sc);
251 atkbdc_kbd_poll(struct atkbdc_softc *sc)
253 assert(pthread_mutex_isowned_np(&sc->mtx));
259 atkbdc_poll(struct atkbdc_softc *sc)
266 atkbdc_dequeue_data(struct atkbdc_softc *sc, uint8_t *buf)
268 assert(pthread_mutex_isowned_np(&sc->mtx));
270 if (ps2mouse_read(sc->ps2mouse_sc, buf) == 0) {
271 if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) {
272 if (sc->kbd.bcnt == 0)
273 sc->status &= ~(KBDS_AUX_BUFFER_FULL |
274 KBDS_KBD_BUFFER_FULL);
276 sc->status &= ~(KBDS_AUX_BUFFER_FULL);
277 sc->outport &= ~KBDO_AUX_OUTFULL;
284 if (sc->kbd.bcnt > 0) {
285 *buf = sc->kbd.buffer[sc->kbd.brd];
286 sc->kbd.brd = (sc->kbd.brd + 1) % FIFOSZ;
288 if (sc->kbd.bcnt == 0) {
289 sc->status &= ~KBDS_KBD_BUFFER_FULL;
290 sc->outport &= ~KBDO_KBD_OUTFULL;
296 if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0 && sc->kbd.bcnt == 0) {
297 sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
302 atkbdc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
303 uint32_t *eax, void *arg)
305 struct atkbdc_softc *sc;
314 pthread_mutex_lock(&sc->mtx);
317 if (sc->ctrlbyte != 0) {
318 *eax = sc->ctrlbyte & 0xff;
321 /* read device buffer; includes kbd cmd responses */
322 atkbdc_dequeue_data(sc, &buf);
326 sc->status &= ~KBDS_CTRL_FLAG;
327 pthread_mutex_unlock(&sc->mtx);
331 if (sc->status & KBDS_CTRL_FLAG) {
333 * Command byte for the controller.
335 switch (sc->curcmd) {
336 case KBDC_SET_COMMAND_BYTE:
338 if (sc->ram[0] & KBD_SYS_FLAG_BIT)
339 sc->status |= KBDS_SYS_FLAG;
341 sc->status &= ~KBDS_SYS_FLAG;
343 case KBDC_WRITE_OUTPORT:
346 case KBDC_WRITE_TO_AUX:
347 ps2mouse_write(sc->ps2mouse_sc, *eax, 0);
350 case KBDC_WRITE_KBD_OUTBUF:
351 atkbdc_kbd_queue_data(sc, *eax);
353 case KBDC_WRITE_AUX_OUTBUF:
354 ps2mouse_write(sc->ps2mouse_sc, *eax, 1);
355 sc->status |= (KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
359 /* write to particular RAM byte */
360 if (sc->curcmd >= 0x61 && sc->curcmd <= 0x7f) {
363 byten = (sc->curcmd - 0x60) & 0x1f;
364 sc->ram[byten] = *eax & 0xff;
370 sc->status &= ~KBDS_CTRL_FLAG;
372 pthread_mutex_unlock(&sc->mtx);
377 * Data byte for the device.
379 ps2kbd_write(sc->ps2kbd_sc, *eax);
382 pthread_mutex_unlock(&sc->mtx);
388 atkbdc_sts_ctl_handler(struct vmctx *ctx, int vcpu, int in, int port,
389 int bytes, uint32_t *eax, void *arg)
391 struct atkbdc_softc *sc;
400 pthread_mutex_lock(&sc->mtx);
403 /* read status register */
405 pthread_mutex_unlock(&sc->mtx);
411 sc->status |= KBDS_CTRL_FLAG;
415 case KBDC_GET_COMMAND_BYTE:
416 sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[0];
419 sc->ctrlbyte = CTRL_CMD_FLAG | 0x55;
421 case KBDC_TEST_AUX_PORT:
422 case KBDC_TEST_KBD_PORT:
423 sc->ctrlbyte = CTRL_CMD_FLAG | 0;
425 case KBDC_READ_INPORT:
426 sc->ctrlbyte = CTRL_CMD_FLAG | 0;
428 case KBDC_READ_OUTPORT:
429 sc->ctrlbyte = CTRL_CMD_FLAG | sc->outport;
431 case KBDC_SET_COMMAND_BYTE:
432 case KBDC_WRITE_OUTPORT:
433 case KBDC_WRITE_KBD_OUTBUF:
434 case KBDC_WRITE_AUX_OUTBUF:
437 case KBDC_DISABLE_KBD_PORT:
438 sc->ram[0] |= KBD_DISABLE_KBD_PORT;
440 case KBDC_ENABLE_KBD_PORT:
441 sc->ram[0] &= ~KBD_DISABLE_KBD_PORT;
442 if (sc->kbd.bcnt > 0)
443 sc->status |= KBDS_KBD_BUFFER_FULL;
446 case KBDC_WRITE_TO_AUX:
449 case KBDC_DISABLE_AUX_PORT:
450 sc->ram[0] |= KBD_DISABLE_AUX_PORT;
451 ps2mouse_toggle(sc->ps2mouse_sc, 0);
452 sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
453 sc->outport &= ~KBDS_AUX_BUFFER_FULL;
455 case KBDC_ENABLE_AUX_PORT:
456 sc->ram[0] &= ~KBD_DISABLE_AUX_PORT;
457 ps2mouse_toggle(sc->ps2mouse_sc, 1);
458 if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0)
459 sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
461 case KBDC_RESET: /* Pulse "reset" line */
462 error = vm_suspend(ctx, VM_SUSPEND_RESET);
463 assert(error == 0 || errno == EALREADY);
466 if (*eax >= 0x21 && *eax <= 0x3f) {
467 /* read "byte N" from RAM */
470 byten = (*eax - 0x20) & 0x1f;
471 sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[byten];
476 pthread_mutex_unlock(&sc->mtx);
478 if (sc->ctrlbyte != 0) {
479 sc->status |= KBDS_KBD_BUFFER_FULL;
480 sc->status &= ~KBDS_AUX_BUFFER_FULL;
481 atkbdc_assert_kbd_intr(sc);
482 } else if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0 &&
483 (sc->ram[0] & KBD_DISABLE_AUX_PORT) == 0) {
484 sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
485 atkbdc_assert_aux_intr(sc);
486 } else if (sc->kbd.bcnt > 0 && (sc->ram[0] & KBD_DISABLE_KBD_PORT) == 0) {
487 sc->status |= KBDS_KBD_BUFFER_FULL;
488 atkbdc_assert_kbd_intr(sc);
495 atkbdc_event(struct atkbdc_softc *sc, int iskbd)
497 pthread_mutex_lock(&sc->mtx);
503 pthread_mutex_unlock(&sc->mtx);
507 atkbdc_init(struct vmctx *ctx)
509 struct inout_port iop;
510 struct atkbdc_softc *sc;
513 sc = calloc(1, sizeof(struct atkbdc_softc));
516 pthread_mutex_init(&sc->mtx, NULL);
518 bzero(&iop, sizeof(struct inout_port));
520 iop.port = KBD_STS_CTL_PORT;
522 iop.flags = IOPORT_F_INOUT;
523 iop.handler = atkbdc_sts_ctl_handler;
526 error = register_inout(&iop);
529 bzero(&iop, sizeof(struct inout_port));
531 iop.port = KBD_DATA_PORT;
533 iop.flags = IOPORT_F_INOUT;
534 iop.handler = atkbdc_data_handler;
537 error = register_inout(&iop);
540 pci_irq_reserve(KBD_DEV_IRQ);
541 sc->kbd.irq = KBD_DEV_IRQ;
543 pci_irq_reserve(AUX_DEV_IRQ);
544 sc->aux.irq = AUX_DEV_IRQ;
546 sc->ps2kbd_sc = ps2kbd_init(sc);
547 sc->ps2mouse_sc = ps2mouse_init(sc);
555 dsdt_line("Device (KBD)");
557 dsdt_line(" Name (_HID, EisaId (\"PNP0303\"))");
558 dsdt_line(" Name (_CRS, ResourceTemplate ()");
561 dsdt_fixed_ioport(KBD_DATA_PORT, 1);
562 dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1);
569 dsdt_line("Device (MOU)");
571 dsdt_line(" Name (_HID, EisaId (\"PNP0F13\"))");
572 dsdt_line(" Name (_CRS, ResourceTemplate ()");
575 dsdt_fixed_ioport(KBD_DATA_PORT, 1);
576 dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1);
582 LPC_DSDT(atkbdc_dsdt);