PS/2
On most x86_64 computers there is still a legacy PS/2 port on it and a good bit of laptops will have the internal keyboard connected with it and some UEFI firmware in CSM mode will convert USB to PS/2 for use.
PS/2 is a legacy interface and might not be on all systems with the advent of USB but it is very easy to make a driver for it and get keyboard & mouse input on your system
Setting up the controller
The following code will setup the PS/2 controller to get keyboard inputs obtained using scancode set 1. Make sure you have functions for I/O ports and the PIC is setup along with your IDT
#define PS2_DATA_PORT 0x60
#define PS2_STATUS_PORT 0x64
#define PS2_COMMAND_PORT 0x64
#define PS2_STATUS_INPUT_BUFFER_FULL 0x02
#define PS2_STATUS_OUTPUT_BUFFER_FULL 0x01
static inline void io_wait() {
outb(0x80, 0);
}
void setup_ps2() {
outb(PS2_COMMAND_PORT, 0xAD);
io_wait();
outb(PS2_COMMAND_PORT, 0xA7);
io_wait();
inb(PS2_DATA_PORT);
io_wait();
outb(PS2_COMMAND_PORT, 0x20);
io_wait();
uint8_t status = inb(PS2_DATA_PORT);
io_wait();
status |=1;
status &= ~(1 << 1);
outb(PS2_COMMAND_PORT, 0x60);
io_wait();
outb(PS2_DATA_PORT, status);
io_wait();
outb(PS2_COMMAND_PORT, 0xAE);
io_wait();
outb(PS2_DATA_PORT, 0xFF);
io_wait();
int timeout = 1000;
uint8_t response;
while (timeout--) {
if ((inb(PS2_STATUS_PORT) & 1) != 0) {
response = inb(PS2_DATA_PORT);
if (response == 0xFA) break;
}
pit_sleep_ms(1);
}
outb(PS2_DATA_PORT, 0xF0);
io_wait();
outb(PS2_DATA_PORT, 0x02);
io_wait();
while ((inb(PS2_STATUS_PORT) & 1) != 0) {
inb(PS2_DATA_PORT);
}
unmask_irq(1);
__asm__ __volatile__("sti");
}
Scan Codes to Ascii
The PS/2 keyboard will not give standard ascii scancodes or ANSI escape sequences but instead gives its own mappings you will need to convert the previous code tells it to use set 1 which is the easiest and for a basic conversion you can use the following arrays and functions to convert.
These arrays let you index into them with the scancode to get the according ASCII character assuming a US keyboard.
static const unsigned char asciiNoShift[256] = { [0x02]='1',[0x03]='2',[0x04]='3',[0x05]='4',[0x06]='5',[0x07]='6',[0x08]='7',[0x09]='8',[0x0A]='9',[0x0B]='0',[0x0C]='-',[0x0D]='=',[0x10]='q',[0x11]='w',[0x12]='e',[0x13]='r',[0x14]='t',[0x15]='y',[0x16]='u',[0x17]='i',[0x18]='o',[0x19]='p',[0x1A]='[',[0x1B]=']',[0x1E]='a',[0x1F]='s',[0x20]='d',[0x21]='f',[0x22]='g',[0x23]='h',[0x24]='j',[0x25]='k',[0x26]='l',[0x27]=';',[0x28]='\'',[0x29]='`',[0x2C]='z',[0x2D]='x',[0x2E]='c',[0x2F]='v',[0x30]='b',[0x31]='n',[0x32]='m',[0x33]=',',[0x34]='.',[0x35]='/',[0x39]=' ',[0x1C] = '\n', [0x0E] = '\b'};
static const unsigned char asciiShift[256] = { [0x02]='!',[0x03]='@',[0x04]='#',[0x05]='$',[0x06]='%',[0x07]='^',[0x08]='&',[0x09]='*',[0x0A]='(',[0x0B]=')',[0x0C]='_',[0x0D]='+',[0x10]='Q',[0x11]='W',[0x12]='E',[0x13]='R',[0x14]='T',[0x15]='Y',[0x16]='U',[0x17]='I',[0x18]='O',[0x19]='P',[0x1A]='{',[0x1B]='}',[0x1E]='A',[0x1F]='S',[0x20]='D',[0x21]='F',[0x22]='G',[0x23]='H',[0x24]='J',[0x25]='K',[0x26]='L',[0x27]=':',[0x28]='\"',[0x29]='~',[0x2C]='Z',[0x2D]='X',[0x2E]='C',[0x2F]='V',[0x30]='B',[0x31]='N',[0x32]='M',[0x33]='<',[0x34]='>',[0x35]='?',[0x39]=' ',[0x1C] = '\n', [0x0E] = '\b'};
When processing scan codes you can check for scancode 0x2A for when shift is pressed and then 0xAA for when it is released something like this
// as a global somewhere
volatile int shiftPressed = 0;
// inside your PS/2 ISR routine perhaps
if (scancode == 0x2A) {
shiftPressed = 1;
}
if (scancode == 0xAA) {
shiftPressed = 0;
}
Getting key presses
To get key presses without constantly asking the PS/2 controller you will use an interrupt to have it tell you when keys are ready. The previous setup code will set it up to do that for you so you just need to implement a basic PS/2 ISR something like this
volatile uint8_t kbd_buffer_index;
volatile char kbd_buffer[256] = {'\0'};
__attribute__((interrupt))
void ps2_isr(__attribute__((unused)) void* frame) {
unsigned char scancode = inb(0x60);
if (scancode == 0x2A) {
shiftPressed = 1;
}
if (scancode == 0xAA) {
shiftPressed = 0;
}
if (shiftPressed) {
kbd_buffer[kbd_buffer_index] = asciiShift[scancode];
kbd_buffer_index++;
} else {
kbd_buffer[kbd_buffer_index] = asciiNoShift[scancode];
kbd_buffer_index++;
}
outb(0x20,0x20);
}
kbd_buffer with keys and because kbd_buffer_index is an 8 bit integer it will automatically handle the ring buffer behavior.When you take a key out of the buffer it should be replaced with a
\0 aka a null character
Polling?
no. don't do it. just dont.