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