From 26621aeac691c1ac26329380e038a58d65ae5c10 Mon Sep 17 00:00:00 2001 From: Matheus Date: Wed, 3 May 2023 22:41:16 -0300 Subject: [PATCH] kernel: ps2 initialisation --- kernel-src/arch/x86-64/main.c | 3 + kernel-src/arch/x86-64/ps2.c | 104 +++++++++++++++++++++++++++ kernel-src/include/x86-64/arch/ps2.h | 102 ++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 kernel-src/arch/x86-64/ps2.c create mode 100644 kernel-src/include/x86-64/arch/ps2.h diff --git a/kernel-src/arch/x86-64/main.c b/kernel-src/arch/x86-64/main.c index 8b14e81b..1fed4075 100644 --- a/kernel-src/arch/x86-64/main.c +++ b/kernel-src/arch/x86-64/main.c @@ -19,6 +19,7 @@ #include #include #include +#include static cpu_t bsp_cpu; @@ -58,5 +59,7 @@ void kernel_entry() { pseudodevices_init(); + arch_ps2_init(); + __assert(!"kernel entry end"); } diff --git a/kernel-src/arch/x86-64/ps2.c b/kernel-src/arch/x86-64/ps2.c new file mode 100644 index 00000000..dbde1a90 --- /dev/null +++ b/kernel-src/arch/x86-64/ps2.c @@ -0,0 +1,104 @@ +#include +#include + +static bool reset_selftest(int port) { + uint8_t r = RESEND; + bool timeout = false; + int attempts = 0; + + while (r == RESEND && attempts < 10) { + device_command(port, DEVICE_CMD_RESETSELFTEST); + r = read_data_timeout(5, &timeout); + } + + if (attempts == 10) + timeout = true; + + if (!timeout) { + if (read_data() != DEVICE_SELFTEST_OK) + printf("ps2: %s port device self test failed!\n", port == 1 ? "First" : "Second"); + else + return true; + } + + return false; +} + +void arch_ps2_init() { + // XXX actually check if the controller is there + write_command(CTLR_CMD_DISABLEP1); + write_command(CTLR_CMD_DISABLEP2); + + inb(PS2_PORT_DATA); // flush output buffer + + write_command(CTLR_CMD_READCFG); + uint8_t control = read_data(); + + bool dualport = control & CONFIG_CLOCKP2; + + control &= ~(CONFIG_IRQP1 | CONFIG_IRQP2 | CONFIG_TRANSLATION); + + write_command(CTLR_CMD_WRITECFG); + write_data(control); + + write_command(CTLR_CMD_SELFTEST); + if (read_data() != CTLR_SELFTEST_OK) + printf("ps2: controller self test failed.\n"); + + write_command(CTLR_CMD_WRITECFG); + write_data(control); // restore config if it was reset + + // confirm existance of second port + + 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 + + int workingflag = 0; + + if (dualport) { + write_command(CTLR_CMD_P2SELFTEST); + if (read_data() == 0) + workingflag |= 2; + else + printf("ps2: econd port self test failed!\n"); + } + + if (workingflag == 0) { + printf("ps2: no working ports.\n"); + return; + } + + printf("ps2: controller with %d ports\n", dualport ? 2 : 1); + + 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 + + int connectedflag = 0; + + if (workingflag & 1 && reset_selftest(1)) + connectedflag |= 1; + + if (workingflag & 2 && reset_selftest(2)) + connectedflag |= 2; +} diff --git a/kernel-src/include/x86-64/arch/ps2.h b/kernel-src/include/x86-64/arch/ps2.h new file mode 100644 index 00000000..db30d547 --- /dev/null +++ b/kernel-src/include/x86-64/arch/ps2.h @@ -0,0 +1,102 @@ +#ifndef _PS2_H +#define _PS2_H + +#include +#include + +#include +#include +#include +#include + +#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 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 CTLR_SELFTEST_OK 0x55 + +#define CONFIG_IRQP1 1 +#define CONFIG_IRQP2 2 +#define CONFIG_TRANSLATION 0x40 +#define CONFIG_CLOCKP2 0x20 + +#define DEVICE_CMD_RESETSELFTEST 0xff +#define DEVICE_CMD_IDENTIFY 0xf2 +#define DEVICE_SELFTEST_OK 0xaa + +static inline bool inbuffer_full() { + return (inb(PS2_PORT_STATUS) & 2) == 2; +} + +static inline bool outbuffer_full() { + return (inb(PS2_PORT_STATUS) & 1) == 1; +} + +static inline void write_command(uint8_t cmd) { + while (inbuffer_full()); + outb(PS2_PORT_COMMAND, cmd); +} + +static inline void write_data(uint8_t cmd) { + while (inbuffer_full()); + outb(PS2_PORT_DATA, cmd); +} + +static inline void device_command(int port, uint8_t command) { + if (port == 2) + write_command(CTLR_CMD_SECONDPORTSELECT); + + write_data(command); +} + +static inline uint8_t read_data() { + while (!outbuffer_full()); + return inb(PS2_PORT_DATA); +} + +static inline uint8_t read_data_timeout(int ms, bool *timeout) { + timespec_t initial = timekeeper_timefromboot(); + + while (outbuffer_full() == false) { + timespec_t current = timekeeper_timefromboot(); + if (timespec_diffms(initial, current) >= ms) { + *timeout = true; + return 0; + } + } + + *timeout = false; + return read_data(); +} + +static inline uint8_t device_write_response(int port, uint8_t command) { + uint8_t r = RESEND; + int tries = 0; + + while (r == RESEND && tries-- < 5) { + device_command(port, command); + r = read_data(); + } + + return r; +} + +void arch_ps2_init(); + +#endif