sortix-mirror/sortix/kb/ps2.cpp
Jonas 'Sortie' Termansen ead0e1523f Refactored the kernel keyboard API, but kept system calls compatible.
Caps lock now works as caps lock, not as shift lock.

This new design will allow implementing a working tty, such that stdin is
the only way to access the keyboard, instead of the current hacky way of
using a special system call to read from the keyboard.

Added a new system header file <sys/keycodes.h> defining the constants for
every key on the keyboard. This will be used in future APIs.

The main change is to split the keyboard driver into a class that reads
from the keyboard, while another class handles the translation into
printable characters (if possible). This allows a terminal driver based
on logical key presses and printable characters, instead of a terminal
driver based only on unicode-ish codes.
2012-01-22 15:53:50 +01:00

194 lines
4.8 KiB
C++

/******************************************************************************
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along
with Sortix. If not, see <http://www.gnu.org/licenses/>.
kb/ps2.cpp
A driver for the PS2 Keyboard.
******************************************************************************/
#include "../platform.h"
#include <libmaxsi/memory.h>
#include "../interrupt.h"
#include "../keyboard.h"
#include "../keycodes.h"
#include "ps2.h"
using namespace Maxsi;
namespace Sortix
{
const uint16_t DATA = 0x0;
const uint16_t COMMAND = 0x0;
const uint16_t STATUS = 0x4;
const uint8_t CMD_SETLED = 0xED;
const uint8_t LED_SCRLCK = (1<<0);
const uint8_t LED_NUMLCK = (1<<1);
const uint8_t LED_CAPSLCK = (1<<2);
void PS2Keyboard__OnInterrupt(CPU::InterruptRegisters* regs, void* user)
{
((PS2Keyboard*) user)->OnInterrupt(regs);
}
PS2Keyboard::PS2Keyboard(uint16_t iobase, uint8_t interrupt)
{
this->queue = NULL;
this->queuelength = 0;
this->queueoffset = 0;
this->queueused = 0;
this->owner = NULL;
this->ownerptr = NULL;
this->iobase = iobase;
this->interrupt = interrupt;
this->leds = 0;
this->scancodeescaped = false;
Interrupt::RegisterHandler(interrupt, PS2Keyboard__OnInterrupt, this);
// If any scancodes were already pending, our interrupt handler will
// never be called. Let's just discard anything pending.
PopScancode();
}
PS2Keyboard::~PS2Keyboard()
{
Interrupt::RegisterHandler(interrupt, NULL, NULL);
delete[] queue;
}
void PS2Keyboard::OnInterrupt(CPU::InterruptRegisters* /*regs*/)
{
uint8_t scancode = PopScancode();
int kbkey = DecodeScancode(scancode);
if ( !kbkey ) { return; }
if ( !PushKey(kbkey) )
{
Log::PrintF("Warning: dropping keystroke due to insufficient "
"storage space in PS2 keyboard driver.\n");
return;
}
uint8_t newleds = leds;
if ( kbkey == KBKEY_CAPSLOCK ) { newleds ^= LED_CAPSLCK; }
if ( kbkey == KBKEY_SCROLLLOCK ) { newleds ^= LED_SCRLCK; }
if ( kbkey == KBKEY_NUMLOCK ) { newleds ^= LED_NUMLCK; }
if ( newleds != leds ) { UpdateLEDs(leds = newleds); }
NotifyOwner();
}
void PS2Keyboard::NotifyOwner()
{
if ( !owner) { return; }
owner->OnKeystroke(this, ownerptr);
}
int PS2Keyboard::DecodeScancode(uint8_t scancode)
{
const uint8_t SCANCODE_ESCAPE = 0xE0;
if ( scancode == SCANCODE_ESCAPE )
{
scancodeescaped = true;
return 0;
}
int offset = (scancodeescaped) ? 0x80 : 0;
int kbkey = scancode & 0x7F;
if ( scancode & 0x80 ) { kbkey = -kbkey - offset; }
else { kbkey = kbkey + offset; }
scancodeescaped = false;
// kbkey is now in the format specified in <sortix/keycodes.h>.
return kbkey;
}
uint8_t PS2Keyboard::PopScancode()
{
return CPU::InPortB(iobase + DATA);
}
void PS2Keyboard::UpdateLEDs(int ledval)
{
while ( (CPU::InPortB(iobase + STATUS) & (1<<1)) != 0 ) { }
CPU::OutPortB(iobase + COMMAND, CMD_SETLED);
while ( (CPU::InPortB(iobase + STATUS) & (1<<1)) != 0 ) { }
CPU::OutPortB(iobase + COMMAND, ledval);
}
void PS2Keyboard::SetOwner(KeyboardOwner* owner, void* user)
{
this->owner = owner;
this->ownerptr = user;
if ( queueused ) { NotifyOwner(); }
}
bool PS2Keyboard::PushKey(int key)
{
// Check if we need to allocate or resize the circular queue.
if ( queueused == queuelength )
{
size_t newqueuelength = (queuelength) ? queuelength * 2 : 32UL;
int* newqueue = new int[newqueuelength];
if ( !newqueue ) { return false; }
size_t elemsize = sizeof(*queue);
size_t leadingavai = queuelength-queueoffset;
size_t leading = (leadingavai < queueused) ? leadingavai : queueused;
size_t trailing = queueused - leading;
Memory::Copy(newqueue, queue + queueoffset, leading * elemsize);
Memory::Copy(newqueue + leading, queue, trailing * elemsize);
delete[] queue;
queue = newqueue;
queuelength = newqueuelength;
queueoffset = 0;
}
queue[(queueoffset + queueused++) % queuelength] = key;
return true;
}
int PS2Keyboard::PopKey()
{
if ( !queueused ) { return 0; }
int kbkey = queue[queueoffset];
queueoffset = (queueoffset + 1) % queuelength;
queueused--;
return kbkey;
}
int PS2Keyboard::Read()
{
return PopKey();
}
size_t PS2Keyboard::GetPending() const
{
return queueused;
}
bool PS2Keyboard::HasPending() const
{
return queueused;
}
}