genode/repos/os/src/drivers/input/ps2/ps2_keyboard.h

460 lines
10 KiB
C++

/*
* \brief PS/2 keyboard protocol handler
* \author Norman Feske
* \date 2007-10-08
*/
/*
* Copyright (C) 2007-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _PS2_KEYBOARD_H_
#define _PS2_KEYBOARD_H_
#include <base/printf.h>
#include <input/event_queue.h>
#include <input/keycodes.h>
#include "input_driver.h"
#include "serial_interface.h"
#include "scan_code_set_1.h"
#include "scan_code_set_2.h"
class Ps2_keyboard : public Input_driver
{
private:
static const bool verbose = false;
static const bool verbose_scan_codes = false;
Serial_interface &_kbd;
Input::Event_queue &_ev_queue;
bool _xlate_mode;
/**
* Array for tracking the current keyboard state
*/
bool _key_state[Input::KEY_MAX + 1];
/**
* Interface for keyboard-packet state machine
*/
class Scan_code_state_machine
{
public:
/**
* Constructor
*/
Scan_code_state_machine() { reset(); }
virtual ~Scan_code_state_machine() { }
/**
* Reset state machine
*/
virtual void reset() { }
/**
* Process value received from the keyboard
*/
virtual void process(unsigned char v) = 0;
/**
* Return true if packet is complete
*/
virtual bool ready() = 0;
/**
* Return true if event is a press event
*/
virtual bool press() = 0;
/**
* Return key code of current packet
*/
virtual unsigned int key_code() = 0;
};
/**
* State machine for parsing scan-code-set 1 packets
*/
class Scan_code_set_1_state_machine : public Scan_code_state_machine
{
enum State {
READ_FIRST, READ_E0_VALUE, READ_E1_VALUE,
PAUSE_READ_ADDITIONAL_VALUE
};
enum Type { NORMAL, EXT_E0, EXT_E1, PAUSE } _type;
State _state; /* current state of packet processing */
bool _press; /* true if key-press event */
bool _ready; /* packet complete */
unsigned _key_code; /* key code of complete packet */
public:
/**
* Constructor
*/
Scan_code_set_1_state_machine()
{
/* init scan-code table */
init_scan_code_set_1_0xe0();
}
/******************************************
** Interface of Scan-code state machine **
******************************************/
void reset()
{
_state = READ_FIRST;
_ready = false;
_press = false;
_key_code = 0;
}
void process(unsigned char v)
{
if (verbose_scan_codes)
PLOG("process %02x", v);
switch (_state) {
case READ_FIRST:
if (v == 0xe0) {
_state = READ_E0_VALUE;
return;
}
if (v == 0xe1) {
_state = READ_E1_VALUE;
return;
}
/* normal packet (one byte) is complete */
_type = NORMAL;
break;
case READ_E0_VALUE:
/* drop fake shifts */
if ((v & 0x7f) == 0x2a) {
reset();
return;
}
/* e0 packet is complete */
_type = EXT_E0;
break;
case READ_E1_VALUE:
/*
* Pause is a sequence of 6 bytes. The first three
* bytes represent the press event and the second three
* bytes represent an artificial release event that
* immediately follows the press event (the real
* release event cannot be detected). Both sub
* sequences start with 0xe1 such that we can handle
* each sub sequence as an e1 packet except that we
* have to read an additional argument (0x2a or 0x37
* respectively).
*/
if (v == 0x1d || v == 0x9d) {
_state = PAUSE_READ_ADDITIONAL_VALUE;
return;
}
/* no pause, e1 packet is complete */
_type = EXT_E1;
break;
case PAUSE_READ_ADDITIONAL_VALUE:
/* pause sub sequence complete */
_type = PAUSE;
break;
}
/* the most significant bit signals release event */
_press = !(v & 0x80);
/* keep the remaining bits for scan-code translation */
v &= ~0x80;
/* convert scan code to unified key code */
switch (_type) {
case NORMAL: _key_code = scan_code_set_1[v]; break;
case EXT_E0: _key_code = scan_code_set_1_0xe0[v]; break;
case EXT_E1: _key_code = Input::KEY_UNKNOWN; break;
case PAUSE: _key_code = Input::KEY_PAUSE; break;
}
/* packet is ready */
_ready = true;
}
bool ready() { return _ready; }
bool press() { return _press; }
unsigned int key_code() { return ready() ? _key_code : Input::KEY_UNKNOWN; }
} _scan_code_set_1_state_machine;
/**
* State machine for parsing scan-code-set 2 packets
*/
class Scan_code_set_2_state_machine : public Scan_code_state_machine
{
enum State {
READ_FIRST, READ_EXT, READ_RELEASE_VALUE,
READ_PAUSE, READ_RELEASE_PAUSE,
};
State _state; /* current state of packet processing */
bool _press; /* true if key-press event */
bool _extended; /* true if extended packet */
bool _pause; /* true if pause key packet */
bool _ready; /* packet complete */
unsigned _key_code; /* key code of complete packet */
public:
/**
* Constructor
*/
Scan_code_set_2_state_machine()
{
/* init sparsely populated extended scan-code table */
init_scan_code_set_2_ext();
}
/******************************************
** Interface of Scan-code state machine **
******************************************/
void reset()
{
_state = READ_FIRST;
_press = true;
_extended = false;
_pause = false;
_ready = false;
_key_code = 0;
}
void process(unsigned char v)
{
if (verbose_scan_codes)
PLOG("process %02x", v);
enum {
EXTENDED_KEY_PREFIX = 0xe0,
RELEASE_PREFIX = 0xf0,
};
switch (_state) {
case READ_FIRST:
if (v == EXTENDED_KEY_PREFIX) {
_state = READ_EXT;
_extended = true;
return;
}
if (v == RELEASE_PREFIX) {
_state = READ_RELEASE_VALUE;
_press = false;
return;
}
/*
* Pause key produces e1 14 77 resp. e1 f0 14 f0 77 and is
* folded into extended table.
*/
if (v == 0xe1) {
_state = READ_PAUSE;
_extended = true;
return;
}
break;
case READ_EXT:
/* drop fake shifts */
if (v == 0x12) {
reset();
return;
}
if (v == RELEASE_PREFIX) {
_state = READ_RELEASE_VALUE;
_press = false;
return;
}
break;
case READ_RELEASE_VALUE:
break;
case READ_PAUSE:
if (v == RELEASE_PREFIX) {
_state = READ_RELEASE_PAUSE;
_press = false;
return;
}
/* eat 14 but stay in READ_PAUSE */
if (v == 0x14)
return;
break;
case READ_RELEASE_PAUSE:
/* eat 14 and go to READ_PAUSE */
if (v == 0x14) {
_state = READ_PAUSE;
return;
}
break;
}
/*
* Packet is complete, translate hardware scan code to
* unified key code.
*/
_ready = true;
_key_code = (_extended ? scan_code_set_2_ext : scan_code_set_2)[v];
}
bool ready() { return _ready; }
bool press() { return _press; }
unsigned int key_code() {
return ready() ? _key_code : Input::KEY_UNKNOWN; }
} _scan_code_set_2_state_machine;
/**
* Used keyboard-packet state machine
*/
Scan_code_state_machine *_state_machine;
public:
/**
* Constructor
*
* \param xlate_mode serial interface does only support scan-code set 1
*
* If 'xlate_mode' is true, we do not attempt to manually switch the
* keyboard to scan code set 2 but just decode the scan-code set 1.
*/
Ps2_keyboard(Serial_interface &kbd, Input::Event_queue &ev_queue, bool xlate_mode)
:
_kbd(kbd), _ev_queue(ev_queue), _xlate_mode(xlate_mode)
{
for (int i = 0; i <= Input::KEY_MAX; i++)
_key_state[i] = false;
/* select state machine to use for packet processing */
reset();
/* prepare state machine for processing the first packet */
_state_machine->reset();
Genode::printf("Using keyboard with scan code set %s.\n",
_state_machine == &_scan_code_set_1_state_machine
? _xlate_mode ? "1 (xlate)" : "1"
: "2");
}
void reset()
{
/* acknowledge code from keyboard */
enum { ACK = 0xfa };
/* scan-code request/config commands */
enum { SCAN_CODE_REQUEST = 0, SCAN_CODE_SET_1 = 1, SCAN_CODE_SET_2 = 2 };
_state_machine = &_scan_code_set_1_state_machine;
if (_xlate_mode)
return;
/* try to enable scan-code set 2 */
_kbd.write(0xf0);
if (_kbd.read() != ACK) {
PWRN("Scan code setting not supported");
return;
}
_kbd.write(SCAN_CODE_SET_2);
if (_kbd.read() != ACK) {
PWRN("Scan code 2 not supported");
return;
}
/*
* If configuration of scan-code set 2 was successful, select
* the corresponding state machine for decoding the packets.
*/
_state_machine = &_scan_code_set_2_state_machine;
}
/****************************
** Input driver interface **
****************************/
void handle_event()
{
_state_machine->process(_kbd.read());
if (!_state_machine->ready())
return;
bool press = _state_machine->press();
unsigned key_code = _state_machine->key_code();
/*
* The old key state should not equal the state after the event.
* Key-repeat events trigger this condition and are discarded.
*/
if (_key_state[key_code] == press) {
_state_machine->reset();
return;
}
/* remember new key state */
_key_state[key_code] = _state_machine->press();
if (verbose)
PLOG("post %s, key_code = %d\n",
press ? "PRESS" : "RELEASE", key_code);
/* post event to event queue */
_ev_queue.add(Input::Event(press ? Input::Event::PRESS
: Input::Event::RELEASE,
key_code, 0, 0, 0, 0));
/* start with new packet */
_state_machine->reset();
}
bool event_pending() { return _kbd.data_read_ready(); }
};
#endif /* _PS2_KEYBOARD_H_ */