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