1+ #include < keyboard.h>
2+ #include < idt.h>
3+ #include < apic.h>
4+ #include < io.h>
5+ #include < device.h>
6+ #include < timer.h>
7+ #include < panic.h>
8+ #include < logging.h>
9+ #include < fs/directory_entry.h>
10+ #include < kmath.h>
11+
12+ namespace keyboard {
13+ constexpr uint8_t DATA_PORT = 0x60 ;
14+ constexpr uint8_t CMD_PORT = 0x64 ;
15+ constexpr uint8_t STATUS_PORT = 0x64 ;
16+
17+ // Commands
18+ constexpr uint8_t READ_CCB = 0x20 ;
19+ constexpr uint8_t WRITE_CCB = 0x60 ;
20+ constexpr uint8_t DISABLE_AUX = 0xA7 ;
21+ constexpr uint8_t ENABLE_AUX = 0xA8 ;
22+ constexpr uint8_t DISABLE_KBD = 0xAD ;
23+ constexpr uint8_t ENABLE_KBD = 0xAE ;
24+ constexpr uint8_t SELF_TEST = 0xAA ;
25+
26+ // Responses
27+ constexpr uint8_t SELF_TEST_PASS = 0x55 ;
28+
29+ // Status Bits
30+ constexpr uint8_t OUT_FULL = 0x01 ;
31+ constexpr uint8_t IN_FULL = 0x02 ;
32+
33+ // CCB
34+ constexpr uint8_t ENABLE_P1_INT = 0x01 ;
35+ constexpr uint8_t ENABLE_P2_INT = 0x02 ;
36+
37+ class keyboard_device : public devices ::device {
38+ public:
39+ keyboard_device (const char * name)
40+ :devices::device(devices::device_type::legacy_hid, name)
41+ {
42+ flags = fs::FS_NODE_CHARDEVICE;
43+ _dirent.set_name (name);
44+ _dirent.flags = flags;
45+ _dirent.node = this ;
46+ _device_name = " PS/2 Keyboard Device" ;
47+ }
48+
49+ ssize_t read (size_t offset, size_t size, uint8_t * buffer) override {
50+ size = kstd::min (size, (size_t )_count);
51+ if (size == 0 ) {
52+ return 0 ;
53+ }
54+
55+ uint8_t i;
56+ for (; i < size; i++) {
57+ if (!read_key (buffer++)) {
58+ break ;
59+ }
60+ }
61+
62+ return i;
63+ }
64+
65+ ssize_t write (size_t offset, size_t size, uint8_t * buffer) override {
66+ size = kstd::min (size, 256 - (size_t )_count);
67+ if (size == 0 ) {
68+ return 0 ;
69+ }
70+
71+ for (uint8_t i = 0 ; i < size; i++) {
72+ _buffer[_end++] = *buffer++;
73+ _count++;
74+ }
75+
76+ return size;
77+ }
78+
79+ private:
80+ bool read_key (uint8_t * key) {
81+ if (_count == 0 ) {
82+ return false ;
83+ }
84+
85+ _count--;
86+ *key = _buffer[_start++];
87+ return true ;
88+ }
89+
90+ uint8_t _start {0 };
91+ uint8_t _end {0 };
92+ uint8_t _count {0 };
93+ uint8_t _buffer[256 ];
94+ fs::directory_entry _dirent;
95+ };
96+
97+ static keyboard_device* ps2_kbd;
98+
99+ static uint8_t kbd_read () {
100+ const int max_attempts = 10000 ;
101+ int i = 0 ;
102+ while (i++ < max_attempts) {
103+ uint8_t s = port_read_8 (STATUS_PORT);
104+ if (s & OUT_FULL) {
105+ return port_read_8 (DATA_PORT);
106+ }
107+ }
108+
109+ const char * reasons[1 ] = {" PS/2 read failure" };
110+ kernel_panic (reasons, 1 );
111+ }
112+
113+ static void ps2_kbd_handler (void *, register_context* regs) {
114+ uint8_t sc = kbd_read ();
115+ ps2_kbd->write (0 , 1 , &sc);
116+ }
117+
118+ void ps2_initialize () {
119+ // Properly test to see that we have a PS/2 keyboard
120+
121+ // Disable both PS/2 ports from receiving external data
122+ port_write_8 (CMD_PORT, DISABLE_KBD);
123+ port_write_8 (CMD_PORT, DISABLE_AUX);
124+
125+ // Clear any current input
126+ port_read_8 (DATA_PORT);
127+
128+ port_write_8 (CMD_PORT, SELF_TEST);
129+ uint8_t r = kbd_read ();
130+ if (r != SELF_TEST_PASS) {
131+ log::error (" PS/2 keyboard self-test failed" );
132+ return ;
133+ }
134+
135+ // Ensure keyboard PS/2 port interrupt enabled
136+ port_write_8 (CMD_PORT, READ_CCB);
137+ uint8_t ccb = kbd_read ();
138+ ccb |= ENABLE_P1_INT;
139+ port_write_8 (CMD_PORT, WRITE_CCB);
140+ port_write_8 (DATA_PORT, ccb);
141+
142+ port_write_8 (CMD_PORT, ENABLE_KBD);
143+ port_write_8 (CMD_PORT, ENABLE_AUX);
144+
145+ ps2_kbd = new keyboard_device (" kbd0" );
146+
147+ idt::register_interrupt_handler (IRQ0 + 1 , ps2_kbd_handler);
148+ apic::io::map_legacy_irq (1 );
149+ port_write_8 (0xF0 , 1 );
150+ }
151+ }
0 commit comments