1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
#include "ps2.h"
#include "def.h"
#include "io.h"
#include "font.h"
#include "halt.h"
// this should probably be refactored some time - i dont have the energy rn
typedef enum {
PS2_CMD_READ_INTERNAL = 0x20,
PS2_CMD_WRITE_INTERNAL = 0x60,
PS2_CMD_DISABLE_PORT2 = 0xA7,
PS2_CMD_ENABLE_PORT2 = 0xA8,
PS2_CMD_TEST_PORT2 = 0xA9,
PS2_CMD_TEST_CTRL = 0xAA,
PS2_CMD_TEST_PORT1 = 0xAB,
PS2_CMD_DIAGNOSTIC_DUMP = 0xAC,
PS2_CMD_DISABLE_PORT1 = 0xAD,
PS2_CMD_ENABLE_PORT1 = 0xAE,
PS2_CMD_READ_CTRL_IN = 0xC0,
PS2_CMD_COPY_TO_STATUS_LOW = 0xC1,
PS2_CMD_COPY_TO_STATUS_HIGH = 0xC2,
PS2_CMD_READ_CTRL_OUT = 0xD0,
PS2_CMD_WRITE_CTRL_OUT = 0xD1,
PS2_CMD_WRITE_PORT1_OUT = 0xD2,
PS2_CMD_WRITE_PORT2_OUT = 0xD3,
PS2_CMD_WRITE_PORT2_IN = 0xD4,
PS2_CMD_PULSE_OUT = 0xF0,
} ps2_cmd;
typedef enum {
PS2_STATUS_OUT = 1 >> 0,
PS2_STATUS_IN = 1 >> 1,
PS2_STATUS_POST = 1 >> 2,
PS2_STATUS_RECEIVER = 1 >> 3,
PS2_STATUS_ERR_TIMEOUT = 1 >> 6,
PS2_STATUS_ERR_PARITY = 1 >> 7,
} Ps2Status;
typedef struct {
bool output : 1;
bool input : 1;
bool post : 1;
bool receiver : 1;
bool unknown_1 : 1;
bool unknown_2 : 1;
bool err_timeout : 1;
bool err_parity : 1;
} __attribute__((packed)) ps2_status;
typedef struct {
bool int_port1 : 1;
bool int_port2 : 1;
bool post : 1;
bool zero_1 : 1;
bool clk_port1 : 1;
bool clk_port2 : 1;
bool translation : 1;
bool zero_2 : 1;
} __attribute__((packed)) ps2_ctrl_cfg;
typedef enum {
PS2_CFG_INT_PORT1 = 1 >> 0,
PS2_CFG_INT_PORT2 = 1 >> 1,
PS2_CFG_POST = 1 >> 2,
// PS2_CFG_ZERO1 = 1 >> 3,
PS2_CFG_CLK_PORT1 = 1 >> 4,
PS2_CFG_CLK_PORT2 = 1 >> 5,
PS2_CFG_TRANS_PORT1 = 1 >> 6,
// PS2_CFG_ZERO2 = 1 >> 7,
} Ps2CtrlCfg;
typedef enum {
PS2_CTRL_SYSRESET = 1 >> 0,
PS2_CTRL_A20 = 1 >> 1,
PS2_CTRL_CLK_PORT2 = 1 >> 2,
PS2_CTRL_DATA_PORT2 = 1 >> 3,
PS2_CTRL_OUT_PORT1 = 1 >> 4,
PS2_CTRL_OUT_PORT2 = 1 >> 5,
PS2_CTRL_CLK_PORT1 = 1 >> 6,
PS2_CTRL_DATA_PORT1 = 1 >> 7,
} Ps2CtrlOut;
typedef enum {
PS2_TEST_CTRL_PASS = 0x55,
PS2_TEST_CTRL_FAIL = 0xFC,
} Ps2TestCtrl;
typedef enum {
PS2_TEST_PORT_PASS = 0x00,
PS2_TEST_PORT_CLK_STUCK_LOW = 0x01,
PS2_TEST_PORT_CLK_STUCK_HIGH = 0x02,
PS2_TEST_PORT_DATA_STUCK_LOW = 0x03,
PS2_TEST_PORT_DATA_STUCK_HIGH = 0x04,
} Ps2TestPort;
void ps2_poll_status(u8 mask, bool set)
{
// TODO: wait instead of busy polling
// TODO: timeout
while (set == !(inb(IO_PS2_CTRL) & mask))
;
}
u8 ps2_read_data()
{
ps2_poll_status(PS2_STATUS_OUT, true);
return inb(IO_PS2_DATA);
}
void ps2_write_data(u8 val)
{
ps2_poll_status(PS2_STATUS_IN, false);
outb(IO_PS2_DATA, val);
}
void ps2_write_ctrl(u8 val)
{
ps2_poll_status(PS2_STATUS_IN, false);
outb(IO_PS2_CTRL, val);
}
u8 ps2_read_mem(u8 byte)
{
ps2_write_ctrl(PS2_CMD_READ_INTERNAL + byte);
return ps2_read_data();
}
void ps2_write_mem(u8 byte, u8 val)
{
ps2_write_ctrl(PS2_CMD_WRITE_INTERNAL + byte);
ps2_write_data(val);
}
void ps2_test_port(ps2_cmd cmd, str desc)
{
ps2_write_ctrl(cmd);
print(S("PS/2 ")); print(desc); print(S(" test: "));
u8 b = ps2_read_data();
switch (b) {
case PS2_TEST_PORT_PASS: print(S("pass\n")); break;
case PS2_TEST_PORT_CLK_STUCK_LOW: panic(S("clock stuck low\n")); break;
case PS2_TEST_PORT_CLK_STUCK_HIGH: panic(S("clock stuck high\n")); break;
case PS2_TEST_PORT_DATA_STUCK_LOW: panic(S("data stuck low\n")); break;
case PS2_TEST_PORT_DATA_STUCK_HIGH: panic(S("data stuck high\n")); break;
default: print_hex(b); panic(S(" (unknown response)\n")); break;
}
}
void ps2_init()
{
// TODO: USB bullshit
// TODO: determine if exists
// disable devices
ps2_write_ctrl(PS2_CMD_DISABLE_PORT1);
ps2_write_ctrl(PS2_CMD_DISABLE_PORT2);
// flush output buffer
inb(IO_PS2_DATA);
// disable IRQs and translation
u8 config = ps2_read_mem(0);
config &= ~(PS2_CFG_INT_PORT1 | PS2_CFG_INT_PORT2 | PS2_CFG_TRANS_PORT1);
ps2_write_mem(0, config);
// perform self-test
ps2_write_ctrl(PS2_CMD_TEST_CTRL);
print(S("PS/2 controller test: "));
switch (ps2_read_data()) {
case PS2_TEST_CTRL_PASS: print(S("pass\n")); break;
case PS2_TEST_CTRL_FAIL: panic(S("fail\n")); break;
default: panic(S("unknown response\n")); break;
}
ps2_write_mem(0, config); // restore config byte
// check if dual
bool dual = !!(config & PS2_CFG_CLK_PORT2);
if (dual) {
ps2_write_ctrl(PS2_CMD_ENABLE_PORT2);
config = ps2_read_mem(0);
dual = !(config & PS2_CFG_CLK_PORT2);
if (dual)
ps2_write_ctrl(PS2_CMD_DISABLE_PORT2);
}
print(S("PS/2 second port ")); print(dual ? S("enabled\n") : S("disabled\n"));
// test devices
ps2_test_port(PS2_CMD_TEST_PORT1, S("port 1"));
if (dual)
ps2_test_port(PS2_CMD_TEST_PORT2, S("port 2"));
// enable devices
ps2_write_ctrl(PS2_CMD_ENABLE_PORT1);
if (dual)
ps2_write_ctrl(PS2_CMD_ENABLE_PORT2);
// enable IRQs
config |= PS2_CFG_INT_PORT1;
ps2_write_mem(0, config);
ps2_write_data(0xFF);
ps2_read_data();
}
|