Files
Espresso/drivers/ps2_keyboard.c

261 lines
5.2 KiB
C
Raw Normal View History

2025-05-28 14:41:02 -05:00
#include <stdio.h>
#include <port_io.h>
2025-06-13 19:53:54 -05:00
#include <tty.h>
2025-06-17 15:50:07 -05:00
#include <stdlib.h>
2025-05-28 14:41:02 -05:00
#include <drivers/ps2_keyboard.h>
/*
2025-06-13 18:03:39 -05:00
PS/2 Controller IO Ports
2025-05-28 14:41:02 -05:00
2025-06-13 18:03:39 -05:00
The PS/2 Controller itself uses 2 IO ports (IO ports 0x60 and 0x64). Like many IO ports, reads and writes may access different internal registers.
2025-05-28 14:41:02 -05:00
2025-06-13 18:03:39 -05:00
Historical note: The PC-XT PPI had used port 0x61 to reset the keyboard interrupt request signal (among other unrelated functions). Port 0x61 has no keyboard related functions on AT and PS/2 compatibles.
IO Port Access Type Purpose
0x60 Read/Write Data Port
0x64 Read Status Register
0x64 Write Command Register
2025-05-28 14:41:02 -05:00
*/
2025-06-13 19:53:54 -05:00
/*
Note:
The interrupt number for keyboard input in 1.
*/
#define PS2_DATA_PORT 0x60
#define PS2_STATUS_PORT 0x64
#define KEYBOARD_IRQ 1
2025-06-17 15:50:07 -05:00
bool ps2keyboard_initialized = false;
2025-06-13 19:53:54 -05:00
/* State for shift key */
static bool shift_pressed = false;
2025-06-17 15:50:07 -05:00
/* State for caps-lock key */
static bool capslock_pressed = false;
2025-07-03 20:30:21 -05:00
/* Used for arrow keys among others */
static bool extended = false;
static bool is_new_char = false;
static bool is_new_key = false;
volatile unsigned char current_char;
2025-06-17 15:50:07 -05:00
volatile char* current_string = NULL;
volatile int32_t current_length = 0;
volatile int32_t capacity = 0;
2025-07-03 20:30:21 -05:00
volatile uint16_t current_key;
2025-06-17 15:50:07 -05:00
2025-06-13 19:53:54 -05:00
static const char scancode_map[128] = {
0, 27, '1','2','3','4','5','6','7','8','9','0','-','=','\b', /* Backspace */
2025-07-03 20:30:21 -05:00
'\t', /* Tab */
2025-06-13 19:53:54 -05:00
'q','w','e','r','t','y','u','i','o','p','[',']','\n', /* Enter */
0, /* Control */
'a','s','d','f','g','h','j','k','l',';','\'','`',
0, /* Left shift */
'\\','z','x','c','v','b','n','m',',','.','/',
0, /* Right shift */
'*',
0, /* Alt */
' ', /* Spacebar */
0, /* Caps lock */
/* The rest are function and control keys */
};
2025-05-28 14:41:02 -05:00
void keyboard_init(void)
{
2025-06-13 19:53:54 -05:00
outb(0x64, 0xAE); /* Enable keyboard interface (often optional, but here for safety) */
2025-06-17 15:50:07 -05:00
ps2keyboard_initialized = true;
}
char get_char(void)
{
2025-07-03 20:30:21 -05:00
/*
ASCII code 5 (Enquiry) is/was used in legacy systems meant for requesting a response from a remote terminal or device.
For example, one system might send ENQ (ASCII 5), and the receiver could reply with ACK (ASCII 6) to indicate its ready or still online.
In modern computing, ENQ is largely obsolete:
- Terminal emulators, shells, operating systems, and network protocols generally do not use ENQ.
- It's not used in text files, programming, or standard communications.
- It may still appear in legacy systems, embedded devices, or proprietary serial protocols, but that's niche.
*/
char temp = 5;
if (is_new_char)
{
temp = current_char;
}
is_new_char = false;
return temp;
}
uint16_t get_key(void)
{
uint16_t temp = 0xFFFA;
if (is_new_key)
{
temp = current_key;
is_new_key = false;
}
else if (is_new_char)
{
temp = (uint16_t)current_char;
is_new_char = false;
}
return temp;
2025-06-17 15:50:07 -05:00
}
char* get_string(void)
{
return current_string;
}
static void append_char(char c)
{
if (current_length + 1 >= capacity)
{
2025-06-27 14:48:06 -05:00
/* Need more space (+1 for the null zero) */
2025-06-17 15:50:07 -05:00
int new_capacity = (capacity == 0) ? 16 : capacity * 2;
char* new_str = (char*)malloc(new_capacity);
if (!new_str) {
return;
}
if (current_string)
{
memcpy(new_str, current_string, current_length);
free(current_string);
}
2025-06-27 14:48:06 -05:00
if (!current_string)
{
return;
}
2025-06-17 15:50:07 -05:00
current_string = new_str;
capacity = new_capacity;
}
current_string[current_length] = c;
current_length++;
current_string[current_length] = '\0'; /* Maintain null terminator */
}
static void free_current_string(void)
{
if (current_string)
{
free(current_string);
current_string = NULL;
current_length = 0;
capacity = 0;
}
2025-06-13 19:53:54 -05:00
}
2025-07-03 20:30:21 -05:00
void keyboard_handler(void)
{
2025-06-13 19:53:54 -05:00
uint8_t scancode = inb(PS2_DATA_PORT);
/* Handle shift press/release */
if (scancode == 0x2A || scancode == 0x36)
{
shift_pressed = true;
return;
}
else if (scancode == 0xAA || scancode == 0xB6)
{
shift_pressed = false;
return;
}
2025-06-17 15:50:07 -05:00
else if (scancode == 0x3A)
{
capslock_pressed = !capslock_pressed;
return;
}
2025-07-03 20:30:21 -05:00
else if (scancode == 0xE0)
{
extended = true;
return;
}
2025-06-17 15:50:07 -05:00
2025-06-13 19:53:54 -05:00
if (scancode & 0x80)
{
2025-07-03 20:30:21 -05:00
extended = false;
}
else
2025-06-13 19:53:54 -05:00
{
2025-07-03 20:30:21 -05:00
uint16_t c = (uint16_t)scancode_map[scancode];
if ((shift_pressed ^ capslock_pressed) && ((char)c >= 'a') && ((char)c <= 'z'))
{
2025-06-13 19:53:54 -05:00
c -= 32; /* Convert to uppercase */
}
else if (shift_pressed)
{
2025-07-03 20:30:21 -05:00
c = (uint16_t) terminal_get_shifted((unsigned char) c);
2025-06-13 19:53:54 -05:00
}
2025-06-27 14:48:06 -05:00
2025-07-03 20:30:21 -05:00
/*if (scancode == 0x1C) {
2025-06-27 14:48:06 -05:00
printf("\n");
return;
2025-07-03 20:30:21 -05:00
}*/
2025-06-27 14:48:06 -05:00
2025-06-13 19:53:54 -05:00
2025-07-03 20:30:21 -05:00
if (extended)
{
switch (scancode)
{
case 0x48:
c = KEY_ARROW_UP;
break;
case 0x50:
c = KEY_ARROW_DOWN;
break;
case 0x4B:
c = KEY_ARROW_LEFT;
break;
case 0x4D:
c = KEY_ARROW_RIGHT;
break;
default:
c = KEY_NONE;
break;
}
current_key = c;
is_new_key = true;
extended = false;
return;
}
2025-06-13 19:53:54 -05:00
if (c)
{
2025-06-17 15:50:07 -05:00
current_char = c;
2025-07-03 20:30:21 -05:00
is_new_char = true;
2025-06-17 15:50:07 -05:00
if (c == '\n')
{
free_current_string();
}
2025-07-03 20:30:21 -05:00
append_char(c);
2025-06-13 19:53:54 -05:00
}
}
2025-05-28 14:41:02 -05:00
}
2025-06-27 14:48:06 -05:00