PatchworkOS
Loading...
Searching...
No Matches
ps2.c
Go to the documentation of this file.
2#include <kernel/drivers/ps2/ps2.h>
3#include <kernel/drivers/ps2/ps2_kbd.h>
4#include <kernel/drivers/ps2/ps2_mouse.h>
5
7#include <kernel/log/log.h>
8#include <kernel/log/panic.h>
10
11#include <errno.h>
12#include <string.h>
13
14static bool isDualChannel = false;
16
17// TODO: Add more known devices
19 {.type = PS2_DEV_TYPE_MOUSE_STANDARD, .name = "Standard PS/2 mouse", .firstIdByte = 0x00},
20 {.type = PS2_DEV_TYPE_MOUSE_SCROLL, .name = "Mouse with scroll wheel", .firstIdByte = 0x03},
21 {.type = PS2_DEV_TYPE_MOUSE_5BUTTON, .name = "5-button mouse", .firstIdByte = 0x04},
22 {.type = PS2_DEV_TYPE_KEYBOARD, .name = "Standard PS/2 keyboard", .firstIdByte = 0xAB},
23 {.type = PS2_DEV_TYPE_KEYBOARD, .name = "NCD Sun keyboard", .firstIdByte = 0xAC},
24 {.type = PS2_DEV_TYPE_KEYBOARD, .name = "Trust keyboard", .firstIdByte = 0x5D},
25 {.type = PS2_DEV_TYPE_KEYBOARD, .name = "NMB SGI keyboard", .firstIdByte = 0x47},
26};
27
28#define PS2_KNOWN_DEVICE_AMOUNT (sizeof(knownDevices) / sizeof(knownDevices[0]))
29
31{
32 switch (response)
33 {
35 return "pass";
37 return "clock stuck low";
39 return "clock stuck high";
41 return "data stuck low";
43 return "data stuck high";
44 default:
45 return "invalid response";
46 }
47}
48
50{
51 switch (response)
52 {
54 return "pass";
56 return "fail";
57 default:
58 return "invalid response";
59 }
60}
61
62static const char* ps2_device_to_string(ps2_device_t device)
63{
64 switch (device)
65 {
66 case PS2_DEV_FIRST:
67 return "first";
68 case PS2_DEV_SECOND:
69 return "second";
70 default:
71 return "invalid device";
72 }
73}
74
76{
77 switch (type)
78 {
80 return "unknown";
82 return "keyboard";
84 return "mouse standard";
86 return "mouse scroll";
88 return "mouse 5button";
89 default:
90 return "invalid device type";
91 }
92}
93
95{
97 {
98 LOG_ERR("ps2 failed to read initial config\n");
99 return ERR;
100 }
101
102 LOG_DEBUG("ps2 initial config byte: 0x%02x\n", *cfg);
104 LOG_DEBUG("ps2 setting config byte to: 0x%02x\n", *cfg);
105
107 {
108 LOG_ERR("ps2 failed to write initial config\n");
109 return ERR;
110 }
111
112 return 0;
113}
114
116{
119 {
120 LOG_ERR("ps2 failed to read config byte.\n");
121 return ERR;
122 }
123
125 if (PS2_CMD_AND_READ(PS2_CMD_SELF_TEST, &response) == ERR)
126 {
127 LOG_ERR("ps2 failed to send self test command\n");
128 return ERR;
129 }
130
131 if (response != PS2_SELF_TEST_PASS)
132 {
133 LOG_ERR("ps2 self test failed %s", ps2_self_test_response_to_string(response));
134 return ERR;
135 }
136
138 {
139 LOG_ERR("ps2 failed to write config byte.\n");
140 return ERR;
141 }
142
143 return 0;
144}
145
147{
150 {
151 LOG_ERR("ps2 failed to read config for dual channel check\n");
152 return ERR;
153 }
154
156 {
158 {
159 LOG_ERR("ps2 failed to send second port enable command\n");
160 return ERR;
161 }
162
164 {
165 LOG_ERR("ps2 failed to read config after second port enable\n");
166 return ERR;
167 }
168
169 if (!(cfg & PS2_CFG_SECOND_CLOCK_DISABLE))
170 {
171 isDualChannel = true;
172 LOG_INFO("dual channel PS/2 controller detected\n");
173
175 {
176 LOG_ERR("ps2 failed to disable second port after detection\n");
177 return ERR;
178 }
179 }
180 else
181 {
182 isDualChannel = false;
183 LOG_INFO("single channel PS/2 controller detected\n");
184 }
185 }
186 else
187 {
188 isDualChannel = false;
189 LOG_INFO("single channel PS/2 controller detected (second port clock not disabled)\n");
190 }
191
192 return 0;
193}
194
196{
197 ps2_device_test_response_t response = 0;
198 if (PS2_CMD_AND_READ(PS2_CMD_FIRST_TEST, &response) == ERR || response != PS2_DEV_TEST_PASS)
199 {
200 devices[0].active = false;
201 LOG_WARN("first port test failed with response %s.\n", ps2_device_test_response_to_string(response));
202 }
203 else
204 {
205 devices[0].active = true;
206 }
207
208 if (!isDualChannel)
209 {
210 return 0;
211 }
212
213 if (PS2_CMD_AND_READ(PS2_CMD_SECOND_TEST, &response) == ERR || response != PS2_DEV_TEST_PASS)
214 {
215 devices[1].active = false;
216 LOG_WARN("second port test failed with response %s.\n", ps2_device_test_response_to_string(response));
217 }
218 else
219 {
220 devices[1].active = true;
221 }
222
223 return 0;
224}
225
227{
228 ps2_device_info_t* info = &devices[device];
229 info->device = device;
230
231 if (PS2_DEV_CMD(device, PS2_DEV_CMD_RESET) == ERR)
232 {
233 LOG_ERR("%s port reset failed\n", ps2_device_to_string(device));
234 return ERR;
235 }
237
238 ps2_device_response_t response = 0;
239 if (PS2_READ(&response) == ERR)
240 {
241 LOG_ERR("ps2 %s device reset failed to read response\n", ps2_device_to_string(device));
242 return ERR;
243 }
244
245 if (response != PS2_DEV_RESPONSE_BAT_OK)
246 {
247 LOG_ERR("ps2 %s device reset failed, invalid response 0x%02x\n", ps2_device_to_string(device), response);
248 return ERR;
249 }
250
251 // The device might send its id bytes here, but we use the identify command to get the id for consistency so we just
252 // drain the data instead.
253 ps2_drain();
254
256 {
257 LOG_ERR("ps2 %s device disable scanning failed\n", ps2_device_to_string(device));
258 return ERR;
259 }
260
261 errno = EOK;
262 if (PS2_DEV_CMD_AND_READ(device, PS2_DEV_CMD_IDENTIFY, &info->firstIdByte) == ERR)
263 {
264 if (errno != ETIMEDOUT)
265 {
266 LOG_ERR("ps2 %s device identify failed\n", ps2_device_to_string(device));
267 return ERR;
268 }
269
270 info->firstIdByte = 0xFF;
272 info->name = "Ancient AT keyboard";
273 LOG_INFO("ps2 %s device identify timed out, probably 'Ancient AT keyboard'\n", ps2_device_to_string(device));
274 }
275
276 // We ignore the rest of the id, as only the first byte is needed to know the device type.
277 ps2_drain();
278
280 info->name = "Unknown";
281 for (uint8_t i = 0; i < PS2_KNOWN_DEVICE_AMOUNT; i++)
282 {
283 if (info->firstIdByte == knownDevices[i].firstIdByte)
284 {
285 info->type = knownDevices[i].type;
287 }
288 }
289
290 LOG_INFO("ps2 %s device identified as '%s' with first ID byte 0x%02x\n", ps2_device_to_string(device), info->name,
291 info->firstIdByte);
292
293 if (info->type == PS2_DEV_TYPE_UNKNOWN)
294 {
295 LOG_ERR("ps2 %s device type unknown\n", ps2_device_to_string(device));
296 return ERR;
297 }
298 else if (info->type == PS2_DEV_TYPE_KEYBOARD)
299 {
300 if (ps2_kbd_init(info) == ERR)
301 {
302 LOG_ERR("ps2 %s device keyboard initialization failed\n", ps2_device_to_string(device));
303 return ERR;
304 }
305 }
306 else
307 {
308 if (ps2_mouse_init(info) == ERR)
309 {
310 LOG_ERR("ps2 %s device mouse initialization failed\n", ps2_device_to_string(device));
311 return ERR;
312 }
313 }
314
316 {
317 LOG_ERR("ps2 %s device enable scanning failed\n", ps2_device_to_string(device));
318 return ERR;
319 }
320
321 return 0;
322}
323
324void ps2_init(void)
325{
326 fadt_t* fadt = FADT_GET();
327
329 {
330 panic(NULL, "ps2 not supported by hardware (ACPI FADT indicates no PS/2 controller)");
331 }
332 LOG_INFO("ps2 controller detected via FADT\n");
333
335 {
336 panic(NULL, "ps2 first device disable failed");
337 }
339 {
340 panic(NULL, "ps2 second device disable failed");
341 }
342 ps2_drain();
343
344 ps2_config_bits_t cfg = 0;
345 if (ps2_set_initial_config(&cfg) == ERR)
346 {
347 panic(NULL, "ps2 initial configuration failed");
348 }
349 if (ps2_self_test() == ERR)
350 {
351 panic(NULL, "ps2 controller self test failed");
352 }
354 {
355 panic(NULL, "ps2 dual channel detection failed");
356 }
357 if (ps2_devices_test() == ERR)
358 {
359 panic(NULL, "ps2 devices test failed");
360 }
361
363
364 if (devices[0].active)
365 {
367 {
368 panic(NULL, "ps2 first device initialization failed");
369 }
370
371 cfg |= PS2_CFG_FIRST_IRQ;
372 }
373 else
374 {
376 }
377
378 if (devices[1].active)
379 {
381 {
382 panic(NULL, "ps2 second device initialization failed");
383 }
384
385 cfg |= PS2_CFG_SECOND_IRQ;
386 }
387 else
388 {
390 }
391
393 {
394 panic(NULL, "ps2 failed to write config byte");
395 }
396
397 if (devices[0].active)
398 {
400 {
401 panic(NULL, "ps2 first device enable failed");
402 }
403 }
404
405 if (devices[1].active)
406 {
408 {
409 panic(NULL, "ps2 second device enable failed");
410 }
411 }
412}
413
423
425{
427 while ((port_inb(PS2_PORT_STATUS) & status) == 0)
428 {
430 {
432 return ERR;
433 }
434 asm volatile("pause");
435 }
436 return 0;
437}
438
440{
442 while ((port_inb(PS2_PORT_STATUS) & status) != 0)
443 {
445 {
447 return ERR;
448 }
449 asm volatile("pause");
450 }
451 return 0;
452}
453
455{
457 {
458 return ERR;
459 }
460 port_outb(PS2_PORT_CMD, command);
461 return 0;
462}
463
465{
466 for (uint8_t i = 0; i < PS2_COMMAND_RETRIES; i++)
467 {
468 if (device == PS2_DEV_SECOND)
469 {
471 {
472 return ERR;
473 }
474 }
475 if (PS2_WRITE(command) == ERR)
476 {
477 return ERR;
478 }
479
480 ps2_device_response_t response;
481 if (PS2_READ(&response) == ERR)
482 {
483 continue;
484 }
485
486 if (response == PS2_DEV_RESPONSE_ACK)
487 {
488 return 0;
489 }
490
491 if (response == PS2_DEV_RESPONSE_RESEND)
492 {
493 continue;
494 }
495
496 LOG_WARN("%s device cmd 0x%02x got unexpected response 0x%02x\n", ps2_device_to_string(device), command,
497 response);
498 return ERR;
499 }
500
501 LOG_ERR("%s device cmd 0x%02x failed after %d retries.\n", ps2_device_to_string(device), command,
503 return ERR;
504}
static clock_t startTime
Definition clock.c:5
#define FADT_GET()
Type safe way to get the FADT table.
Definition tables.h:105
@ FADT_BOOT_ARCH_PS2_EXISTS
Definition tables.h:21
static uint8_t port_inb(uint16_t port)
Definition port.h:34
static void port_outb(uint16_t port, uint8_t val)
Definition port.h:29
void hpet_wait(clock_t nanoseconds)
Wait for a specified number of nanoseconds using the HPET.
Definition hpet.c:104
uint64_t ps2_kbd_init(ps2_device_info_t *info)
Initialize a PS/2 keyboard device.
Definition ps2_kbd.c:52
uint64_t ps2_mouse_init(ps2_device_info_t *info)
Initialize a PS/2 mouse device.
Definition ps2_mouse.c:86
ps2_device_t
PS/2 device identifiers.
Definition ps2.h:126
#define PS2_CMD(command)
Send a command to the PS/2 controller without reading response.
Definition ps2.h:232
ps2_status_bits_t
PS/2 controller status register bits.
Definition ps2.h:81
#define PS2_COMMAND_RETRIES
Number of retries for commands.
Definition ps2.h:47
#define PS2_CMD_AND_WRITE(command, data)
Send a command to the PS/2 controller and write data.
Definition ps2.h:262
ps2_device_test_response_t
PS/2 device test responses.
Definition ps2.h:170
ps2_cmd_t
PS/2 controller commands.
Definition ps2.h:63
#define PS2_WAIT_TIMEOUT
Wait timeout for PS/2 controller.
Definition ps2.h:32
ps2_config_bits_t
PS/2 controller configuration bits.
Definition ps2.h:94
uint64_t ps2_wait_until_set(ps2_status_bits_t status)
Wait until status bit(s) is set.
Definition ps2.c:424
uint64_t ps2_send_device_cmd(ps2_device_t device, ps2_device_cmd_t command)
Send a command to a PS/2 device.
Definition ps2.c:464
#define PS2_LARGE_DELAY
Large delay for various operations.
Definition ps2.h:42
#define PS2_DEV_CMD_AND_READ(device, command, data)
Send a command to a PS/2 device and read response.
Definition ps2.h:293
#define PS2_WRITE(data)
Write data to PS/2 controller.
Definition ps2.h:216
ps2_self_test_response_t
PS/2 controller self-test responses.
Definition ps2.h:161
ps2_device_type_t
PS/2 device types.
Definition ps2.h:137
uint64_t ps2_send_cmd(ps2_cmd_t command)
Send a command to the PS/2 controller.
Definition ps2.c:454
void ps2_drain(void)
Drain the PS/2 output buffer.
Definition ps2.c:414
#define PS2_DEV_CMD(device, command)
Send a command to a PS/2 device without reading response.
Definition ps2.h:279
#define PS2_SMALL_DELAY
Small delay for various operations.
Definition ps2.h:37
#define PS2_READ(data)
Read data from PS/2 controller.
Definition ps2.h:198
#define PS2_CMD_AND_READ(command, data)
Send a command to the PS/2 controller and read response.
Definition ps2.h:245
uint64_t ps2_wait_until_clear(ps2_status_bits_t status)
Wait until status bit(s) is clear.
Definition ps2.c:439
ps2_device_cmd_t
PS/2 device commands.
Definition ps2.h:109
ps2_device_response_t
PS/2 device command responses.
Definition ps2.h:182
void ps2_init(void)
Initialize the PS/2 controller.
Definition ps2.c:324
@ PS2_DEV_COUNT
Total number of ports.
Definition ps2.h:130
@ PS2_DEV_SECOND
Second PS/2 port.
Definition ps2.h:129
@ PS2_DEV_FIRST
First PS/2 port.
Definition ps2.h:128
@ PS2_STATUS_IN_FULL
Input buffer status (0 = empty, 1 = full)
Definition ps2.h:83
@ PS2_STATUS_OUT_FULL
Output buffer status (0 = empty, 1 = full)
Definition ps2.h:82
@ PS2_DEV_TEST_DATA_STUCK_LOW
Definition ps2.h:174
@ PS2_DEV_TEST_PASS
Definition ps2.h:171
@ PS2_DEV_TEST_DATA_STUCK_HIGH
Definition ps2.h:175
@ PS2_DEV_TEST_CLOCK_STUCK_HIGH
Definition ps2.h:173
@ PS2_DEV_TEST_CLOCK_STUCK_LOW
Definition ps2.h:172
@ PS2_CMD_SECOND_TEST
Definition ps2.h:70
@ PS2_CMD_SECOND_WRITE
Definition ps2.h:74
@ PS2_CMD_SELF_TEST
Definition ps2.h:71
@ PS2_CMD_FIRST_ENABLE
Definition ps2.h:69
@ PS2_CMD_FIRST_DISABLE
Definition ps2.h:68
@ PS2_CMD_CFG_READ
Definition ps2.h:64
@ PS2_CMD_SECOND_ENABLE
Definition ps2.h:67
@ PS2_CMD_SECOND_DISABLE
Definition ps2.h:66
@ PS2_CMD_FIRST_TEST
Definition ps2.h:72
@ PS2_CMD_CFG_WRITE
Definition ps2.h:65
@ PS2_CFG_SECOND_IRQ
Second PS/2 port interrupt enable.
Definition ps2.h:96
@ PS2_CFG_FIRST_IRQ
First PS/2 port interrupt enable.
Definition ps2.h:95
@ PS2_CFG_SECOND_CLOCK_DISABLE
Second PS/2 port clock disable.
Definition ps2.h:100
@ PS2_CFG_FIRST_CLOCK_DISABLE
First PS/2 port clock disable.
Definition ps2.h:99
@ PS2_CFG_FIRST_TRANSLATION
First PS/2 port translation enable.
Definition ps2.h:101
@ PS2_SELF_TEST_FAIL
Definition ps2.h:163
@ PS2_SELF_TEST_PASS
Definition ps2.h:162
@ PS2_DEV_TYPE_MOUSE_5BUTTON
Definition ps2.h:142
@ PS2_DEV_TYPE_KEYBOARD
Definition ps2.h:139
@ PS2_DEV_TYPE_MOUSE_STANDARD
Definition ps2.h:140
@ PS2_DEV_TYPE_MOUSE_SCROLL
Definition ps2.h:141
@ PS2_DEV_TYPE_UNKNOWN
Definition ps2.h:138
@ PS2_PORT_STATUS
Definition ps2.h:55
@ PS2_PORT_CMD
Definition ps2.h:56
@ PS2_PORT_DATA
Definition ps2.h:54
@ PS2_DEV_CMD_RESET
Definition ps2.h:119
@ PS2_DEV_CMD_IDENTIFY
Definition ps2.h:113
@ PS2_DEV_CMD_DISABLE_SCANNING
Definition ps2.h:116
@ PS2_DEV_CMD_ENABLE_SCANNING
Definition ps2.h:115
@ PS2_DEV_RESPONSE_RESEND
Definition ps2.h:184
@ PS2_DEV_RESPONSE_ACK
Definition ps2.h:183
@ PS2_DEV_RESPONSE_BAT_OK
Definition ps2.h:185
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:362
#define LOG_ERR(format,...)
Definition log.h:89
#define LOG_WARN(format,...)
Definition log.h:88
#define LOG_INFO(format,...)
Definition log.h:87
#define LOG_DEBUG(format,...)
Definition log.h:81
clock_t timer_uptime(void)
Time since boot.
Definition timer.c:73
#define ETIMEDOUT
Connection timed out.
Definition errno.h:577
#define errno
Error number variable.
Definition errno.h:27
#define EOK
No error.
Definition errno.h:32
#define NULL
Pointer error value.
Definition NULL.h:23
#define ERR
Integer error value.
Definition ERR.h:17
static fb_info_t info
Definition gop.c:41
static bool isDualChannel
Definition ps2.c:14
static const ps2_device_info_t knownDevices[]
Definition ps2.c:18
static uint64_t ps2_device_init(ps2_device_t device)
Definition ps2.c:226
static const char * ps2_device_type_to_string(ps2_device_type_t type)
Definition ps2.c:75
static uint64_t ps2_self_test(void)
Definition ps2.c:115
static const char * ps2_self_test_response_to_string(ps2_self_test_response_t response)
Definition ps2.c:49
static uint64_t ps2_check_if_dual_channel(void)
Definition ps2.c:146
#define PS2_KNOWN_DEVICE_AMOUNT
Definition ps2.c:28
static uint64_t ps2_devices_test(void)
Definition ps2.c:195
static const char * ps2_device_test_response_to_string(ps2_device_test_response_t response)
Definition ps2.c:30
static const char * ps2_device_to_string(ps2_device_t device)
Definition ps2.c:62
static ps2_device_info_t devices[PS2_DEV_COUNT]
Definition ps2.c:15
static uint64_t ps2_set_initial_config(ps2_config_bits_t *cfg)
Definition ps2.c:94
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
__UINT8_TYPE__ uint8_t
Definition stdint.h:11
Fixed ACPI Description Table.
Definition tables.h:45
uint16_t bootArchFlags
Definition tables.h:82
char name[MAX_NAME]
Definition fb.h:44
PS/2 device information structure.
Definition ps2.h:149
ps2_device_type_t type
Definition ps2.h:153
uint8_t firstIdByte
Definition ps2.h:151
const char * name
Definition ps2.h:152