1/**
2 * \file
3 */
4
5/*
6 * Copyright (c) 2009, ETH Zurich.
7 * All rights reserved.
8 *
9 * This file is distributed under the terms in the attached LICENSE file.
10 * If you do not find this file, copies can be found by writing to:
11 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
12 */
13
14#include <stdio.h> // for printf()
15#include "vmkitmon.h"
16#include "keyboard.h"
17#include <stdlib.h>
18#include <barrelfish/terminal.h>
19
20struct key_map_entry {
21    uint8_t ascii, scan_code;
22};
23
24// this table maps from input through the BF kernel to a key in the guest
25// scan codes taken from http://heim.ifi.uio.no/~stanisls/helppc/scan_codes.html
26static struct key_map_entry key_map[128] = {
27    // capital letters
28    ['A'] = { 0x41, 0x1E },
29    ['B'] = { 0x42, 0x30 },
30    ['C'] = { 0x43, 0x2E },
31    ['D'] = { 0x44, 0x20 },
32    ['E'] = { 0x45, 0x12 },
33    ['F'] = { 0x46, 0x21 },
34    ['G'] = { 0x47, 0x22 },
35    ['H'] = { 0x48, 0x23 },
36    ['I'] = { 0x49, 0x17 },
37    ['J'] = { 0x4A, 0x24 },
38    ['K'] = { 0x4B, 0x25 },
39    ['L'] = { 0x4C, 0x26 },
40    ['M'] = { 0x4D, 0x32 },
41    ['N'] = { 0x4E, 0x31 },
42    ['O'] = { 0x4F, 0x18 },
43    ['P'] = { 0x50, 0x19 },
44    ['Q'] = { 0x51, 0x10 },
45    ['R'] = { 0x52, 0x13 },
46    ['S'] = { 0x53, 0x1F },
47    ['T'] = { 0x54, 0x14 },
48    ['U'] = { 0x55, 0x16 },
49    ['V'] = { 0x56, 0x2F },
50    ['W'] = { 0x57, 0x11 },
51    ['X'] = { 0x58, 0x2D },
52    ['Y'] = { 0x59, 0x15 },
53    ['Z'] = { 0x5A, 0x2C },
54    // small letters
55    ['a'] = { 0x61, 0x1E },
56    ['b'] = { 0x62, 0x30 },
57    ['c'] = { 0x63, 0x2E },
58    ['d'] = { 0x64, 0x20 },
59    ['e'] = { 0x65, 0x12 },
60    ['f'] = { 0x66, 0x21 },
61    ['g'] = { 0x67, 0x22 },
62    ['h'] = { 0x68, 0x23 },
63    ['i'] = { 0x69, 0x17 },
64    ['j'] = { 0x6A, 0x24 },
65    ['k'] = { 0x6B, 0x25 },
66    ['l'] = { 0x6C, 0x26 },
67    ['m'] = { 0x6D, 0x32 },
68    ['n'] = { 0x6E, 0x31 },
69    ['o'] = { 0x6F, 0x18 },
70    ['p'] = { 0x70, 0x19 },
71    ['q'] = { 0x71, 0x10 },
72    ['r'] = { 0x72, 0x13 },
73    ['s'] = { 0x73, 0x1F },
74    ['t'] = { 0x74, 0x14 },
75    ['u'] = { 0x75, 0x16 },
76    ['v'] = { 0x76, 0x2F },
77    ['w'] = { 0x77, 0x11 },
78    ['x'] = { 0x78, 0x2D },
79    ['y'] = { 0x79, 0x15 },
80    ['z'] = { 0x7A, 0x2C },
81    // control characters
82    [0x08] = { 0x08, 0x0e },    // back-space
83    [0x7f] = { 0x7f, 0x0e },    // delete
84    [0x0d] = { 0x0d, 0x1c },    // enter
85    [0x1b] = { 0x1b, 0x01 },    // escape
86    [0x20] = { 0x20, 0x39 },    // space
87    [0x09] = { 0x09, 0x0f }     // tab1
88};
89
90struct keyboard *
91keyboard_new (struct guest *g)
92{
93    struct keyboard *ret = calloc(1, sizeof(struct keyboard));
94
95    ret->guest = g;
96    ret->status = KEYBOARD_STATUS_ENABLED;
97
98    return ret;
99}
100
101static inline int
102handle_60h_out (struct keyboard *k, uint32_t val)
103{
104    if (!(k->status & KEYBOARD_STATUS_IN_CMD)) {
105        printf("keyboard: non command input\n");
106        return -1;
107    }
108
109    switch (k->write_cmd) {
110        // Write Output Port
111        case 0xd1:
112            /* ------------------------------------------------------------------
113             * |KBD data|KBD clk|in buf empty|out buf empty|ud|ud|gate a20|reset|
114             * ------------------------------------------------------------------
115             */
116            // check for gate a20
117            if (val & 0x2) {
118                k->guest->a20_gate_enabled = true;
119            } else {
120                k->guest->a20_gate_enabled = false;
121            }
122            // check for reset
123            if (!(val & 0x1)) {
124                assert(!"System reset requested");
125            }
126            break;
127        default:
128            printf("keyboard: Unhandeled keyboard command");
129            return 1;
130    }
131
132    k->write_cmd = 0;
133    k->status &= ~KEYBOARD_STATUS_IN_CMD;
134
135    return 0;
136}
137
138int
139keyboard_handle_pio_read (struct keyboard *k, uint16_t port,
140                         enum opsize size, uint32_t *val)
141{
142    switch (port) {
143    case 0x64:
144        assert(size & OPSIZE_8);
145        *val = k->status;
146        return HANDLER_ERR_OK;
147    }
148
149    return -1;
150}
151
152int
153keyboard_handle_pio_write (struct keyboard *k, uint16_t port,
154                           enum opsize size, uint32_t val)
155{
156    switch (port) {
157    case 0x60:
158        assert(size & OPSIZE_8);
159        return handle_60h_out(k, val);
160    case 0x64:
161        assert(size & OPSIZE_8);
162        k->write_cmd = val;
163        k->status |= KEYBOARD_STATUS_IN_CMD;
164        return HANDLER_ERR_OK;
165    }
166
167    return -1;
168}
169
170/* waits until a character arrives in the monitor */
171/* FIXME: perhaps it is not such a good idea to stall the whole monitor for
172          this */
173static int
174handle_get_keystroke (struct keyboard *k, struct guest *g)
175{
176    int r;
177    uint8_t chr;
178
179    // loop for now here since we have too many unhandeled keys atm
180    // FIXME: use a complete keymap to avoid this loop
181    while (1) {
182        // wait for input
183        r = terminal_read((char*)&chr, 1);
184        assert(r == 1);
185
186        // check whether our keymap contains the entered key
187        if (key_map[chr].scan_code != 0) {
188            guest_set_al(g, key_map[chr].ascii);
189            guest_set_ah(g, key_map[chr].scan_code);
190            break;
191        } else {
192            printf("keyboard: Unhandeled input char with code %x\n", chr);
193        }
194    }
195
196    return HANDLER_ERR_OK;
197}
198
199int
200keyboard_handle_int16 (struct keyboard *k, struct guest *g) {
201    switch (guest_get_ah(g)) {
202        // KEYBOARD - GET KEYSTROKE
203        case 0:
204            return handle_get_keystroke(k, g);
205        default:
206            break;
207    }
208
209    return HANDLER_ERR_UNHANDELED;
210}
211