1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include "keyboard_ps2.h"
14#include "keyboard_vkey.h"
15#include <stdbool.h>
16#include <stdint.h>
17#include <stdlib.h>
18#include <string.h>
19#include <assert.h>
20
21static void
22ps2_single_control(ps_io_ops_t *ops, int8_t byte)
23{
24    ps_io_port_out(&ops->io_port_ops, PS2_IOPORT_CONTROL, 1, byte);
25}
26
27static void
28ps2_dual_control(ps_io_ops_t *ops, int8_t byte1, int8_t byte2)
29{
30    ps_io_port_out(&ops->io_port_ops, PS2_IOPORT_CONTROL, 1, byte1);
31    ps_io_port_out(&ops->io_port_ops, PS2_IOPORT_DATA, 1, byte2);
32}
33
34static uint8_t
35ps2_read_control_status(ps_io_ops_t *ops)
36{
37    uint32_t res = 0;
38    int error = ps_io_port_in(&ops->io_port_ops, PS2_IOPORT_CONTROL, 1, &res);
39    assert(!error);
40    (void) error;
41    return (uint8_t) res;
42}
43
44static uint8_t
45ps2_read_data(ps_io_ops_t *ops)
46{
47    uint32_t res = 0;
48    int error = ps_io_port_in(&ops->io_port_ops, PS2_IOPORT_DATA, 1, &res);
49    assert(!error);
50    (void) error;
51    return (uint8_t) res;
52}
53
54static void
55ps2_write_output(ps_io_ops_t *ops, uint8_t byte)
56{
57    while ( (ps2_read_control_status(ops) & 0x2) != 0);
58    ps_io_port_out(&ops->io_port_ops, PS2_IOPORT_DATA, 1, byte);
59}
60
61static uint8_t
62ps2_read_output(ps_io_ops_t *ops)
63{
64    while ( (ps2_read_control_status(ops) & 0x1) == 0);
65    return ps2_read_data(ops);
66}
67
68static void
69ps2_send_keyboard_cmd(ps_io_ops_t *ops, uint8_t cmd)
70{
71    do {
72        ps2_write_output(ops, cmd);
73    } while (ps2_read_output(ops) != KEYBOARD_ACK);
74}
75
76static void
77ps2_send_keyboard_cmd_param(ps_io_ops_t *ops, uint8_t cmd, uint8_t param)
78{
79    do {
80        ps2_write_output(ops, cmd);
81        ps2_write_output(ops, param);
82    } while (ps2_read_output(ops) != KEYBOARD_ACK);
83}
84
85/* ---------------------------------------------------------------------------------------------- */
86
87static keyboard_key_event_t
88keyboard_state_push_ps2_keyevent(struct keyboard_state *s, uint16_t ps2_keyevent)
89{
90    keyboard_key_event_t ev_none = { .vkey = -1, .pressed = false };
91
92    if (s->state == KEYBOARD_PS2_STATE_IGNORE) {
93        s->num_ignore--;
94        if (s->num_ignore == 0) {
95            s->state = KEYBOARD_PS2_STATE_NORMAL;
96        }
97        return ev_none;
98    }
99
100    assert(s->state & KEYBOARD_PS2_STATE_NORMAL);
101
102    /* Handle release / extended mode keys. */
103    switch (ps2_keyevent) {
104    case KEYBOARD_PS2_EVENTCODE_RELEASE:
105        s->state |= KEYBOARD_PS2_STATE_RELEASE_KEY;
106        return ev_none;
107    case KEYBOARD_PS2_EVENTCODE_EXTENDED:
108        s->state |= KEYBOARD_PS2_STATE_EXTENDED_MODE;
109        return ev_none;
110    case KEYBOARD_PS2_EVENTCODE_EXTENDED_PAUSE:
111        s->state = KEYBOARD_PS2_STATE_IGNORE;
112        s->num_ignore = 7; /* Ignore the next 7 characters of pause seq. */
113        keyboard_key_event_t ev = { .vkey = VK_PAUSE, .pressed = true };
114        return ev;
115    }
116
117    /* Prepend 0xE0 to ps2 keycode if in extended mode. */
118    if (s->state & KEYBOARD_PS2_STATE_EXTENDED_MODE) {
119        ps2_keyevent = 0xE000 + (ps2_keyevent & 0xFF);
120        s->state &= ~KEYBOARD_PS2_STATE_EXTENDED_MODE;
121    }
122
123    int16_t vkey = keycode_ps2_to_vkey(ps2_keyevent);
124    if (vkey < 0) {
125        /* No associated vkey with this PS2 key. */
126        s->state = KEYBOARD_PS2_STATE_NORMAL;
127        return ev_none;
128    }
129
130    /* Set keystate according to press or release. */
131    if (s->state & KEYBOARD_PS2_STATE_RELEASE_KEY) {
132        /* Release event. */
133        keyboard_key_event_t ev = { .vkey = vkey, .pressed = false };
134        s->state &= ~KEYBOARD_PS2_STATE_RELEASE_KEY;
135        return ev;
136    }
137
138    /* Press event. */
139    keyboard_key_event_t ev = { .vkey = vkey, .pressed = true };
140    return ev;
141}
142
143/* ---------------------------------------------------------------------------------------------- */
144
145int
146keyboard_init(struct keyboard_state *state, const ps_io_ops_t* ops,
147              void (*handle_event_callback)(keyboard_key_event_t ev, void *cookie))
148{
149    assert(state && ops);
150    memset(state, 0, sizeof(struct keyboard_state));
151
152    state->state = KEYBOARD_PS2_STATE_NORMAL;
153    state->ops = *ops;
154    state->handle_event_callback = handle_event_callback;
155
156    /* Initialise the PS2 keyboard device. */
157
158    /* Disable both PS2 devices. */
159    ps2_single_control(&state->ops, PS2_CMD_DISABLE_KEYBOARD_INTERFACE);
160    ps2_single_control(&state->ops, PS2_CMD_DISABLE_MOUSE_INTERFACE);
161
162    /* Flush the output buffer. */
163    ps2_read_data(&state->ops);
164
165    /* Enable IRQs and disable translation (IRQ bits 0, 1, translation 6). */
166    ps2_single_control(&state->ops, PS2_READ_CMD_BYTE);
167    uint8_t config = ps2_read_output(&state->ops);
168    config |= 0x1;
169    config &= 0xBF;
170    ps2_dual_control(&state->ops, PS2_WRITE_CMD_BYTE, config);
171
172    /* Run a controller self test. */
173    ps2_single_control(&state->ops, PS2_CMD_CONTROLLER_SELF_TEST);
174    uint8_t res = ps2_read_output(&state->ops);
175    if (res != PS2_CONTROLLER_SELF_TEST_OK) {
176        return -1;
177    }
178
179    /* Run keyboard interface test. */
180    ps2_single_control(&state->ops, PS2_CMD_KEYBOARD_INTERFACE_TEST);
181    res = ps2_read_output(&state->ops);
182    if (res != 0) {
183        return -1;
184    }
185
186    /* Enable keyboard interface. */
187    ps2_single_control(&state->ops, PS2_CMD_ENABLE_KEYBOARD_INTERFACE);
188    ps2_read_data(&state->ops);
189
190    /* Reset the keyboard device. */
191    if (keyboard_reset(state)) {
192        return -1;
193    }
194
195    /* Set scanmode 2. */
196    keyboard_set_scanmode(state, 2);
197    return 0;
198}
199
200void
201keyboard_set_led(struct keyboard_state *state, char scroll_lock, char num_lock, char caps_lock)
202{
203    ps2_send_keyboard_cmd_param(&state->ops, KEYBOARD_SET_LEDS,
204                                scroll_lock | num_lock << 1 | caps_lock << 2);
205}
206
207void
208keyboard_set_scanmode(struct keyboard_state *state, uint8_t mode)
209{
210    ps2_send_keyboard_cmd(&state->ops, KEYBOARD_DISABLE_SCAN); /* Disable scanning. */
211    ps2_send_keyboard_cmd_param(&state->ops, KEYBOARD_SET_SCANCODE_MODE, mode); /* Set scan code. */
212    ps2_send_keyboard_cmd(&state->ops, KEYBOARD_ENABLE_SCAN); /* Re-Enable scanning. */
213}
214
215int
216keyboard_reset(struct keyboard_state *state)
217{
218    /* Reset the keyboard device. */
219    ps2_send_keyboard_cmd(&state->ops, KEYBOARD_RESET);
220
221    /* Wait for the Basic Assurance Test. */
222    while (1) {
223        uint8_t res = ps2_read_output(&state->ops);
224        if (res == KEYBOARD_BAT_SUCCESSFUL) {
225            break;
226        }
227        if (res == KEYBOARD_ERROR) {
228            assert(!"keyboard init keyboard BAT failed.");
229            return -1;
230        }
231    }
232
233    return 0;
234}
235
236keyboard_key_event_t
237keyboard_poll_ps2_keyevent(struct keyboard_state *state)
238{
239    if ((ps2_read_control_status(&state->ops) & 0x1) == 0) {
240        /* No key events generated. */
241        keyboard_key_event_t ev = { .vkey = -1, .pressed = false };
242        return ev;
243    }
244    return keyboard_state_push_ps2_keyevent(state, ps2_read_data(&state->ops));
245}
246
247void
248keyboard_poll_ps2_keyevents(struct keyboard_state *state, void *cookie)
249{
250    keyboard_key_event_t ev = { .vkey = -1, .pressed = false };
251    do {
252        ev = keyboard_poll_ps2_keyevent(state);
253        if (ev.vkey != -1 && state->handle_event_callback) {
254            state->handle_event_callback(ev, cookie);
255        }
256    } while (ev.vkey != -1);
257}
258