blob: dbe5e8f1c8764b828445e23d97eb857b3ab3ec99 [file] [log] [blame] [edit]
/*
* Copyright 2018, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
#include <stdlib.h>
#include <keyboard/keyboard.h>
#include <assert.h>
/* This code only provides very basic control of the keyboard controller. If
* you are intending on extending this driver, more information about the
* keyboard controller can be found at
* http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html.
* This driver takes complete control of both ps/2 ports, and assumes that the keyboard
* is attached to the first port. It places the keyboard in mode 2 scancode generator
*/
/* Directives can be written to this IO port to inform the keyboard controller
* of how your next write to one of the keyboard ports should be interpreted.
*/
#define KEYBOARD_INPUT_CONTROL 0x64
/* This byte can be written to port KEYBOARD_INPUT_CONTROL to tell the keyboard
* controller that the next byte you write to port KEYBOARD_OUTPUT_BUFFER
* should be interpreted as a write to the Controller Command Byte.
*/
#define KEYBOARD_CONTROL_WRITE_CCB 0x60
/* This IO port can be written to in order to pass data to the keyboard
* controller. Its data is interpreted differently depending on what you
* previously wrote to the port KEYBOARD_INPUT_CONTROL. It can be read from to
* get the last character received from the keyboard.
*/
#define KEYBOARD_OUTPUT_BUFFER 0x60
/* The following values can be written to the Controller Command Byte to enable
* or disable keyboard interrupt delivery respectively. Without interrupts
* enabled, the controller registers need to be polled for input which is not
* ideal. Note that every bit in the Controller Command Byte is significant and
* so these writes may have unintended side-effects if you are using this
* driver in combination with, say a mouse driver. If this is the case I
* suggest you improve this driver to read the CCB before writing to it in
* order to only modify bit 0.
*/
#define KEYBOARD_CCB_IRQ_ENABLE 0x01
#define KEYBOARD_CCB_IRQ_DISABLE 0x00
static in8_fn io_in8;
static out8_fn io_out8;
static inline uint8_t ps2_poll_output(void)
{
return io_in8(KEYBOARD_OUTPUT_BUFFER);
}
static uint8_t _ps2_read_control(void)
{
return io_in8(KEYBOARD_INPUT_CONTROL);
}
static inline uint8_t ps2_read_output(void)
{
while ((_ps2_read_control() & 0x1) == 0);
return ps2_poll_output();
}
/* some helper macros for manipulating io ports
* your opinion on macros may vary. but the goal is to make the keyboard setup code look somewhat
* readable */
#define ps2_single_control(byte) io_out8(KEYBOARD_INPUT_CONTROL, byte)
#define ps2_dual_control(byte1, byte2) do {ps2_single_control(byte1);io_out8(KEYBOARD_CONTROL_WRITE_CCB, byte2);} while(0)
#define ps2_write_output(byte) io_out8(KEYBOARD_OUTPUT_BUFFER, byte)
void sel4keyboard_init(int enable_interrupt, in8_fn in8, out8_fn out8)
{
io_in8 = in8;
io_out8 = out8;
int error;
uint8_t config;
/* initialize the keyboard */
//disable both ps2 devices
ps2_single_control(0xAD);
ps2_single_control(0xA7);
// flush the output buffer by reading and discarding
ps2_poll_output();
// get old internal configuration
ps2_single_control(0x20);
config = ps2_read_output();
// disable irqs and translation (bits 0, 1, 6)
config &= 0xBC;
ps2_dual_control(0x60, config);
// perform a test
ps2_single_control(0xAA);
error = ps2_read_output();
assert(error == 0x55);
// test port
ps2_single_control(0xAB);
error = ps2_read_output();
assert(error == 0x00);
// enable port
ps2_single_control(0xAE);
if (enable_interrupt) {
config |= 0x1;
ps2_dual_control(0x60, config);
}
// switch to mode 2 scancode
ps2_write_output(0xf0);
ps2_write_output(2);
error = ps2_read_output();
assert(error == 0xfa);
}
void sel4keyboard_reset()
{
uint8_t config;
/* disable ports and interrupts */
// get old internal configuration
ps2_single_control(0x20);
config = ps2_read_output();
config &= 0xBC;
ps2_dual_control(0x60, config);
//disable both ps2 devices
ps2_single_control(0xAD);
ps2_single_control(0xA7);
}
int sel4keyboard_get_scancode(int *scancode)
{
/* see if there is a waiting scancode */
if ((_ps2_read_control() & 0x1) == 0) {
return 0;
}
*scancode = ps2_poll_output();
return 1;
}