ps2: improvements

This commit is contained in:
Matheus
2024-09-05 23:13:29 -03:00
parent 9df8018ece
commit d65c29197d
5 changed files with 369 additions and 179 deletions
+226 -72
View File
@@ -3,121 +3,275 @@
#include <arch/ps2kbd.h>
#include <arch/ps2mouse.h>
static bool reset_selftest(int port) {
uint8_t r = RESEND;
bool timeout = false;
int attempts = 0;
static void ps2_disablescanning(int port) {
ps2_device_command(port, PS2_DEVICE_CMD_DISABLESCANNING);
while (r == RESEND && attempts < 5) {
device_command(port, DEVICE_CMD_RESETSELFTEST);
r = read_data_timeout(5, &timeout);
++attempts;
}
int resendtries = 0;
if (attempts == 5)
timeout = true;
// 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 (!timeout) {
if (read_data() != DEVICE_SELFTEST_OK) {
printf("ps2: %s port device self test failed!\n", port == 1 ? "First" : "Second");
} else {
return true;
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);
}
}
return false;
// 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
write_command(CTLR_CMD_DISABLEP1);
write_command(CTLR_CMD_DISABLEP2);
ps2_write_command(PS2_CTLR_CMD_DISABLEP1);
ps2_write_command(PS2_CTLR_CMD_DISABLEP2);
inb(PS2_PORT_DATA); // flush output buffer
// just to make sure
ps2_flush_outbuffer();
write_command(CTLR_CMD_READCFG);
uint8_t control = read_data();
ps2_write_command(PS2_CTLR_CMD_READCFG);
uint8_t control = ps2_read_data();
bool dualport = control & CONFIG_CLOCKP2;
bool dualport = control & PS2_CONFIG_CLOCKP2;
control &= ~(CONFIG_IRQP1 | CONFIG_IRQP2 | CONFIG_TRANSLATION);
control &= ~(PS2_CONFIG_IRQP1 | PS2_CONFIG_IRQP2 | PS2_CONFIG_TRANSLATION);
write_command(CTLR_CMD_WRITECFG);
write_data(control);
ps2_write_command(PS2_CTLR_CMD_WRITECFG);
ps2_write_data(control);
write_command(CTLR_CMD_SELFTEST);
if (read_data() != CTLR_SELFTEST_OK) {
printf("ps2: controller self test failed.\n");
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;
}
write_command(CTLR_CMD_WRITECFG);
write_data(control); // restore config if it was reset
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();
write_command(CTLR_CMD_ENABLEP2);
write_command(CTLR_CMD_READCFG);
control = read_data();
if (dualport && (control & CONFIG_CLOCKP2)) // not enabled, not dual port.
dualport = false;
else
write_command(CTLR_CMD_DISABLEP2); // disable second port again
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;
write_command(CTLR_CMD_P1SELFTEST);
if (read_data() == 0)
ps2_write_command(PS2_CTLR_CMD_P1SELFTEST);
result = ps2_read_data();
if (result == 0)
workingflag |= 1;
else
printf("First PS/2 port self test failed!\n");
printf("ps2: first PS/2 port self test failed! Expected 0 got %x\n", result);
if (dualport) {
write_command(CTLR_CMD_P2SELFTEST);
if (read_data() == 0)
ps2_write_command(PS2_CTLR_CMD_P2SELFTEST);
result = ps2_read_data();
if (result == 0)
workingflag |= 2;
else
printf("ps2: second port self test failed!\n");
printf("ps2: second port self test failed! Expected 0 got %x\n", result);
}
if (workingflag == 0) {
printf("ps2: no working ports.\n");
printf("ps2: no working ports\n");
return;
}
printf("ps2: controller with %d ports\n", dualport ? 2 : 1);
// enable ports
write_command(CTLR_CMD_ENABLEP1);
if (dualport)
write_command(CTLR_CMD_ENABLEP2);
// enable irqs and translation in config
write_command(CTLR_CMD_READCFG);
control = read_data();
control |= (dualport ? CONFIG_IRQP1 | CONFIG_IRQP2 : CONFIG_IRQP1) | CONFIG_TRANSLATION;
write_command(CTLR_CMD_WRITECFG);
write_data(control);
// reset and self test devices
// the ports leave this with scanning disabled
int connectedflag = 0;
if ((workingflag & 1) && reset_selftest(1))
connectedflag |= 1;
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) && reset_selftest(2))
connectedflag |= 2;
if (workingflag & 2) {
if (ps2_device_reset_selftest(2) == false) {
printf("ps2: device self test for port 2 failed\n");
} else {
connectedflag |= 2;
}
}
// XXX should id each one for proper identification
if (connectedflag & 1)
ps2kbd_init();
if (connectedflag & 2)
ps2mouse_init();
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);
}
-10
View File
@@ -116,7 +116,6 @@ static char extendedcodes[128] = {
};
#define KEYBOARDIRQ 1
#define KEYBOARD_CMD_REPORTDATA 0xf4
keyboard_t *kb;
static bool extended = false;
@@ -153,15 +152,6 @@ static void kbdisr(isr_t *isr, context_t *ctx) {
}
void ps2kbd_init() {
// clear PS/2 buffer to make sure its empty
inb(PS2_PORT_DATA);
int response = device_write_response(1, KEYBOARD_CMD_REPORTDATA);
if (response != ACK) {
printf("ps2keyboard: expected ACK, got %x\n", response);
return;
}
isr_t *isr = interrupt_allocate(kbdisr, arch_apic_eoi, IPL_KEYBOARD);
__assert(isr);
arch_ioapic_setirq(KEYBOARDIRQ, isr->id & 0xff, _cpu()->id, false);
+34 -39
View File
@@ -2,35 +2,23 @@
#include <logging.h>
#include <kernel/mouse.h>
#define MOUSE_CMD_SAMPLERATE 0xF3
#define MOUSE_CMD_REPORTDATA 0xF4
#define PS2_MOUSE_CMD_SAMPLERATE 0xF3
#define MOUSE 0
#define MOUSE_Z 3
#define MOUSE_5B 4
#define PS2_MOUSE 0
#define PS2_MOUSE_Z 3
#define PS2_MOUSE_5B 4
static bool haswheel = false;
static bool fivebuttons = false;
static void mouse_setrate(int port, uint8_t rate) {
device_write_response(port, MOUSE_CMD_SAMPLERATE);
device_write_response(port, rate);
}
static uint8_t identify(int port) {
bool timeout;
device_command(port, DEVICE_CMD_IDENTIFY);
uint8_t b = read_data_timeout(5, &timeout);
if (b != ACK) {
printf("ps2mouse: identify failed: %x\n", b);
return RESEND;
static void ps2_mouse_setrate(int port, uint8_t rate) {
if (ps2_device_write_ok(port, PS2_MOUSE_CMD_SAMPLERATE) == false) {
printf("ps2mouse: setting rate at port %d failed\n", port);
}
b = read_data_timeout(5, &timeout);
return timeout ? RESEND : b;
if (ps2_device_write_ok(port, rate) == false) {
printf("ps2mouse: setting rate at port %d failed\n", port);
}
}
static int datac;
@@ -81,42 +69,49 @@ static void mouseisr() {
mouse_packet(mouse, &packet);
}
#define DO_IDENTIFY_CHECK(step) \
if (ps2_identify(2, identity) == false) { \
printf("ps2: ps2mouse_init: failed to identify mouse at port %d (step \"%s\")\n", port, step); \
return; \
}
void ps2mouse_init() {
inb(PS2_PORT_DATA);
int port = 2;
uint8_t identity[2];
if (identify(2) != MOUSE) {
DO_IDENTIFY_CHECK("first");
if (identity[0] != PS2_MOUSE) {
printf("Not a mouse!\n");
return;
}
// check if mouse has scroll wheel
mouse_setrate(2, 200);
mouse_setrate(2, 100);
mouse_setrate(2, 80);
ps2_mouse_setrate(2, 200);
ps2_mouse_setrate(2, 100);
ps2_mouse_setrate(2, 80);
DO_IDENTIFY_CHECK("has scroll");
if (identify(2) == MOUSE_Z) {
if (identity[0] == PS2_MOUSE_Z) {
haswheel = true;
// check if mouse has 5 buttons
mouse_setrate(2, 200);
mouse_setrate(2, 200);
mouse_setrate(2, 80);
ps2_mouse_setrate(2, 200);
ps2_mouse_setrate(2, 200);
ps2_mouse_setrate(2, 80);
if (identify(2) == MOUSE_5B)
DO_IDENTIFY_CHECK("5 buttons");
if (identity[0] == PS2_MOUSE_5B)
fivebuttons = true;
}
mouse_setrate(2, 60);
int response = device_write_response(2, MOUSE_CMD_REPORTDATA);
if (response != ACK) {
printf("ps2mouse: expected ACK, got %x\n", response);
return;
}
ps2_mouse_setrate(2, 60);
isr_t *isr = interrupt_allocate(mouseisr, arch_apic_eoi, IPL_MOUSE);
__assert(isr);
arch_ioapic_setirq(MOUSEIRQ, isr->id & 0xff, _cpu()->id, false);
arch_ioapic_setirq(PS2_MOUSEIRQ, isr->id & 0xff, _cpu()->id, false);
mouse = mouse_new();
__assert(mouse);
printf("ps2mouse: irq enabled with vector %u\n", isr->id & 0xff);
+109 -43
View File
@@ -12,68 +12,79 @@
#define PS2_PORT_DATA 0x60
#define PS2_PORT_COMMAND 0x64
#define PS2_PORT_STATUS 0x64
#define RESEND 0xfe
#define ACK 0xfa
#define ECHO 0xee
#define KEYBOARDIRQ 1
#define MOUSEIRQ 12
#define PS2_RESEND 0xfe
#define PS2_ACK 0xfa
#define PS2_ECHO 0xee
#define PS2_KEYBOARDIRQ 1
#define PS2_MOUSEIRQ 12
#define CTLR_CMD_SECONDPORTSELECT 0xd4
#define CTLR_CMD_DISABLEP1 0xad
#define CTLR_CMD_DISABLEP2 0xa7
#define CTLR_CMD_READCFG 0x20
#define CTLR_CMD_WRITECFG 0x60
#define CTLR_CMD_SELFTEST 0xaa
#define CTLR_CMD_ENABLEP1 0xae
#define CTLR_CMD_ENABLEP2 0xa8
#define CTLR_CMD_P1SELFTEST 0xab
#define CTLR_CMD_P2SELFTEST 0xa9
#define PS2_CTLR_CMD_SECONDPORTSELECT 0xd4
#define PS2_CTLR_CMD_DISABLEP1 0xad
#define PS2_CTLR_CMD_DISABLEP2 0xa7
#define PS2_CTLR_CMD_READCFG 0x20
#define PS2_CTLR_CMD_WRITECFG 0x60
#define PS2_CTLR_CMD_SELFTEST 0xaa
#define PS2_CTLR_CMD_ENABLEP1 0xae
#define PS2_CTLR_CMD_ENABLEP2 0xa8
#define PS2_CTLR_CMD_P1SELFTEST 0xab
#define PS2_CTLR_CMD_P2SELFTEST 0xa9
#define CTLR_SELFTEST_OK 0x55
#define PS2_CTLR_SELFTEST_OK 0x55
#define CONFIG_IRQP1 1
#define CONFIG_IRQP2 2
#define CONFIG_TRANSLATION 0x40
#define CONFIG_CLOCKP2 0x20
#define PS2_CONFIG_IRQP1 1
#define PS2_CONFIG_IRQP2 2
#define PS2_CONFIG_TRANSLATION 0x40
#define PS2_CONFIG_CLOCKP2 0x20
#define DEVICE_CMD_RESETSELFTEST 0xff
#define DEVICE_CMD_IDENTIFY 0xf2
#define DEVICE_SELFTEST_OK 0xaa
#define PS2_DEVICE_CMD_RESETSELFTEST 0xff
#define PS2_DEVICE_CMD_DISABLESCANNING 0xf5
#define PS2_DEVICE_CMD_ENABLESCANNING 0xf4
#define PS2_DEVICE_CMD_IDENTIFY 0xf2
#define PS2_DEVICE_SELFTEST_OK 0xaa
#define PS2_DEVICE_SELFTEST_FAIL 0xfc
static inline bool inbuffer_full() {
// needs to be waited to read
static inline bool ps2_outbuffer_empty() {
return (inb(PS2_PORT_STATUS) & 1) == 0;
}
// needs to be waited to write
static inline bool ps2_inbuffer_full() {
return (inb(PS2_PORT_STATUS) & 2) == 2;
}
static inline bool outbuffer_full() {
return (inb(PS2_PORT_STATUS) & 1) == 1;
static inline void ps2_flush_outbuffer() {
while (ps2_outbuffer_empty() == false)
inb(PS2_PORT_DATA);
}
static inline void write_command(uint8_t cmd) {
while (inbuffer_full());
static inline void ps2_write_command(uint8_t cmd) {
while (ps2_inbuffer_full());
outb(PS2_PORT_COMMAND, cmd);
}
static inline void write_data(uint8_t cmd) {
while (inbuffer_full());
static inline void ps2_write_data(uint8_t cmd) {
while (ps2_inbuffer_full());
outb(PS2_PORT_DATA, cmd);
}
static inline void device_command(int port, uint8_t command) {
static inline void ps2_device_command(int port, uint8_t command) {
if (port == 2)
write_command(CTLR_CMD_SECONDPORTSELECT);
ps2_write_command(PS2_CTLR_CMD_SECONDPORTSELECT);
write_data(command);
ps2_write_data(command);
}
static inline uint8_t read_data() {
while (!outbuffer_full());
static inline uint8_t ps2_read_data() {
while (ps2_outbuffer_empty());
return inb(PS2_PORT_DATA);
}
static inline uint8_t read_data_timeout(int ms, bool *timeout) {
static inline uint8_t ps2_read_data_timeout(int ms, bool *timeout) {
timespec_t initial = timekeeper_timefromboot();
while (outbuffer_full() == false) {
while (ps2_outbuffer_empty()) {
timespec_t current = timekeeper_timefromboot();
if (timespec_diffms(initial, current) >= ms) {
*timeout = true;
@@ -82,22 +93,77 @@ static inline uint8_t read_data_timeout(int ms, bool *timeout) {
}
*timeout = false;
return read_data();
return ps2_read_data();
}
static inline uint8_t device_write_response(int port, uint8_t command) {
uint8_t r = RESEND;
static inline bool ps2_device_write_ok(int port, uint8_t command) {
ps2_device_command(port, command);
int resends = 0;
bool timeout;
for (;;) {
uint8_t data = ps2_read_data_timeout(100, &timeout);
if (timeout)
return false;
if (data == PS2_RESEND) {
if (resends++ > 5)
return false;
ps2_device_command(port, command);
}
if (data == PS2_ACK)
break;
}
return true;
}
static inline uint8_t ps2_device_write_response(int port, uint8_t command) {
uint8_t r = PS2_RESEND;
int tries = 0;
while (r == RESEND && tries < 5) {
device_command(port, command);
r = read_data();
while (r == PS2_RESEND && tries < 5) {
ps2_device_command(port, command);
r = ps2_read_data();
++tries;
}
return r;
}
static bool ps2_identify(int port, uint8_t results[2]) {
ps2_device_command(port, PS2_DEVICE_CMD_IDENTIFY);
int resends = 0;
bool timeout;
for (;;) {
uint8_t data = ps2_read_data_timeout(100, &timeout);
if (timeout)
return false;
if (data == PS2_RESEND) {
if (resends++ > 5)
return false;
ps2_device_command(port, PS2_DEVICE_CMD_IDENTIFY);
}
if (data == PS2_ACK)
break;
}
// now read the actual data bytes
results[0] = ps2_read_data_timeout(100, &timeout);
if (timeout)
return false;
// secon read can timeout just fine.
results[1] = ps2_read_data_timeout(100, &timeout);
return true;
}
void arch_ps2_init();
#endif
-15
View File
@@ -1,15 +0,0 @@
name=xfontmiscmisc
revision=1
from_source=xfontmiscmisc
imagedeps="gcc xorg-mkfontscale"
hostdeps="xgcc xbinutils pkgconfig util-macros fontutil"
deps="mlibc"
build() {
${source_dir}/configure --prefix=${prefix} --host=x86_64-astral
make -j${parallelism}
}
package() {
make install DESTDIR=${dest_dir}
}