Files
Astral/kernel-src/arch/x86-64/ps2.c
T

287 lines
6.7 KiB
C

#include <arch/ps2.h>
#include <logging.h>
#include <arch/ps2kbd.h>
#include <arch/ps2mouse.h>
#include <kernel/init.h>
static void ps2_disablescanning(int port) {
ps2_device_command(port, PS2_DEVICE_CMD_DISABLESCANNING);
int resendtries = 0;
// loop until timeout, resend if we need to
// this is done to clear the buffer and make sure no more data will be read
for (;;) {
bool timeout;
uint8_t data = ps2_read_data_timeout(100, &timeout);
if (timeout)
break;
if (data == PS2_RESEND) {
if (resendtries++ > 5) {
printf("ps2: ps2_disablescanning: port %d: too many resends\n", port);
return;
}
ps2_device_command(port, PS2_DEVICE_CMD_DISABLESCANNING);
}
}
// just to make sure
ps2_flush_outbuffer();
}
static bool ps2_enablescanning(int port) {
ps2_device_command(port, PS2_DEVICE_CMD_ENABLESCANNING);
int resendtries = 0;
for (;;) {
bool timeout;
uint8_t data = ps2_read_data_timeout(100, &timeout);
if (timeout) {
printf("ps2: ps2_enablescanning: port %d: timeout\n", port);
return false;
}
if (data == PS2_ACK)
break;
if (data == PS2_RESEND) {
if (resendtries++ > 5) {
printf("ps2: ps2_enablescanning: port %d: too many resends\n", port);
return false;
}
}
}
// just to make sure
ps2_flush_outbuffer();
return true;
}
static bool ps2_device_reset_selftest(int port) {
ps2_disablescanning(port);
ps2_device_command(port, PS2_DEVICE_CMD_RESETSELFTEST);
int resendtries = 0;
// loop until ACK or timeout, RESEND if needed
// this will make sure the reset command is ACK'd by the device
for (;;) {
bool timeout;
uint8_t data = ps2_read_data_timeout(500, &timeout);
if (timeout) {
printf("ps2: ps2_reset_selftest: port %d: timed out waiting for ACK\n", port);
return false;
}
if (data == PS2_RESEND) {
if (resendtries > 5) {
printf("ps2: ps2_reset_selftest: port %d: too many resend tries for self test command\n", port);
return false;
}
ps2_device_command(port, PS2_DEVICE_CMD_RESETSELFTEST);
++resendtries;
continue;
}
if (data == PS2_ACK)
break;
}
// now wait for the reset to be done
for (;;) {
bool timeout;
uint8_t data = ps2_read_data_timeout(2000, &timeout);
if (timeout) {
printf("ps2: ps2_reset_selftest: port %d: timed out waiting for result\n", port);
return false;
}
if (data == PS2_DEVICE_SELFTEST_OK)
break;
if (data == PS2_DEVICE_SELFTEST_FAIL) {
printf("ps2: ps2_reset_selftest: port %d: self test failed\n");
return false;
}
}
// and clear whatever could come our way
for (;;) {
bool timeout;
ps2_read_data_timeout(100, &timeout);
if (timeout)
break;
}
// just to make sure
ps2_flush_outbuffer();
return true;
}
bool ps2_init_device(int port, bool *mouse) {
if (ps2_device_reset_selftest(port) == false)
return false;
// to make sure it is still disabled
ps2_disablescanning(port);
return true;
}
void arch_ps2_init() {
// XXX actually check if the controller is there
bool t;
ps2_write_command_timeout(PS2_CTLR_CMD_DISABLEP1, 10, &t);
if (t) {
printf("ps2: no controller on this machine\n");
return;
}
ps2_write_command(PS2_CTLR_CMD_DISABLEP2);
// just to make sure
ps2_flush_outbuffer();
ps2_write_command(PS2_CTLR_CMD_READCFG);
uint8_t control = ps2_read_data();
bool dualport = control & PS2_CONFIG_CLOCKP2;
control &= ~(PS2_CONFIG_IRQP1 | PS2_CONFIG_IRQP2 | PS2_CONFIG_TRANSLATION);
ps2_write_command(PS2_CTLR_CMD_WRITECFG);
ps2_write_data(control);
ps2_write_command(PS2_CTLR_CMD_SELFTEST);
uint8_t result = ps2_read_data();
if (result != PS2_CTLR_SELFTEST_OK) {
printf("ps2: controller self test failed (expected %x got %x)\n", PS2_CTLR_SELFTEST_OK, result);
return;
}
ps2_write_command(PS2_CTLR_CMD_WRITECFG);
ps2_write_data(control); // restore config if it was reset
// confirm existance of second port
if (dualport) {
ps2_write_command(PS2_CTLR_CMD_ENABLEP2);
ps2_write_command(PS2_CTLR_CMD_READCFG);
control = ps2_read_data();
if (dualport && (control & PS2_CONFIG_CLOCKP2)) // not enabled, not dual port.
dualport = false;
else
ps2_write_command(PS2_CTLR_CMD_DISABLEP2); // disable second port again
}
// do port self tests
int workingflag = 0;
ps2_write_command(PS2_CTLR_CMD_P1SELFTEST);
result = ps2_read_data();
if (result == 0)
workingflag |= 1;
else
printf("ps2: first PS/2 port self test failed! Expected 0 got %x\n", result);
if (dualport) {
ps2_write_command(PS2_CTLR_CMD_P2SELFTEST);
result = ps2_read_data();
if (result == 0)
workingflag |= 2;
else
printf("ps2: second port self test failed! Expected 0 got %x\n", result);
}
if (workingflag == 0) {
printf("ps2: no working ports\n");
return;
}
printf("ps2: controller with %d ports\n", dualport ? 2 : 1);
// reset and self test devices
// the ports leave this with scanning disabled
int connectedflag = 0;
if (workingflag & 1) {
if (ps2_device_reset_selftest(1) == false) {
printf("ps2: device self test for port 1 failed\n");
} else {
connectedflag |= 1;
}
}
if (workingflag & 2) {
if (ps2_device_reset_selftest(2) == false) {
printf("ps2: device self test for port 2 failed\n");
} else {
connectedflag |= 2;
}
}
if (connectedflag == 0) {
printf("ps2: no working devices\n");
return;
}
uint8_t okflag = 0;
// now initialize and enable the device stuff
if (connectedflag & 1) {
ps2_write_command(PS2_CTLR_CMD_ENABLEP1);
// figure out what the device is TODO and actually use the result for something
uint8_t identity[2];
if (ps2_identify(1, identity) == false) {
printf("ps2: failed to identify first port\n");
goto do_2;
} else {
ps2kbd_init();
ps2_write_command(PS2_CTLR_CMD_DISABLEP2);
okflag |= 1;
}
}
do_2:
if (connectedflag & 2) {
ps2_write_command(PS2_CTLR_CMD_ENABLEP2);
// figure out what the device is TODO and actually use the result for something
uint8_t identity[2];
if (ps2_identify(1, identity) == false) {
printf("ps2: failed to identify second port\n");
} else {
ps2mouse_init();
ps2_write_command(PS2_CTLR_CMD_DISABLEP2);
okflag |= 2;
}
}
if (okflag & 1) {
ps2_write_command(PS2_CTLR_CMD_ENABLEP1);
if (ps2_enablescanning(1) == false) {
printf("ps2: failed to enable scanning for port 1\n");
}
}
if (okflag & 2) {
ps2_write_command(PS2_CTLR_CMD_ENABLEP2);
if (ps2_enablescanning(2) == false) {
printf("ps2: failed to enable scanning for port 2\n");
}
}
// enable irqs and translation in config
ps2_write_command(PS2_CTLR_CMD_READCFG);
control = ps2_read_data();
control |= (dualport ? PS2_CONFIG_IRQP1 | PS2_CONFIG_IRQP2 : PS2_CONFIG_IRQP1) | PS2_CONFIG_TRANSLATION;
ps2_write_command(PS2_CTLR_CMD_WRITECFG);
ps2_write_data(control);
}
INIT_ROUTINE_DEFINE(ps2, INIT_ROUTINE_FLAGS_NONE, arch_ps2_init, acpi, input);