From a78033acc941fc4bcf6188f1d48fd8e012673fff Mon Sep 17 00:00:00 2001 From: EmulatedSeasons <89668582+EmulatedSeasons@users.noreply.github.com> Date: Thu, 15 Jun 2023 22:36:39 -0400 Subject: initial commit --- kernel/arch/i386/ps2_controller.cpp | 191 ++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 kernel/arch/i386/ps2_controller.cpp (limited to 'kernel/arch/i386/ps2_controller.cpp') diff --git a/kernel/arch/i386/ps2_controller.cpp b/kernel/arch/i386/ps2_controller.cpp new file mode 100644 index 0000000..9424233 --- /dev/null +++ b/kernel/arch/i386/ps2_controller.cpp @@ -0,0 +1,191 @@ +#include +#include +#include +#include + +#define PS2_DATA 0x60 // data port +#define PS2_STATUS 0x64 // status register +#define PS2_COMMAND 0x64 // command register + +// set to true if 2nd ps2 port is available +bool ps2SecondaryEnabled = false; + +void initialize_ps2_controller() { + inputbuffer_wait(); + outb(PS2_COMMAND, 0xAD); // disable devices + inputbuffer_wait(); + outb(PS2_COMMAND, 0xA7); + + outputbuffer_wait(); + inb(PS2_DATA); // flush output buffer + inb(PS2_DATA); + inb(PS2_DATA); + inb(PS2_DATA); + inb(PS2_DATA); + inb(PS2_DATA); + inb(PS2_DATA); + inb(PS2_DATA); + inb(PS2_DATA); + inb(PS2_DATA); + + + inputbuffer_wait(); + outb(PS2_COMMAND, 0x20); // set controller configuration byte + outputbuffer_wait(); + uint8_t conconfig = inb(PS2_DATA); + conconfig &= 0b00110100; + inputbuffer_wait(); + outb(PS2_COMMAND, 0x60); + inputbuffer_wait(); + outb(PS2_DATA, conconfig); + + inputbuffer_wait(); + outb(PS2_COMMAND, 0xAA); // performs controller self test + outputbuffer_wait(); + if (inb(PS2_DATA) == 0xFC) { + printf("PS/2 controller failed self test\n"); + } + inputbuffer_wait(); + outb(PS2_COMMAND, 0x60); // in case the controller resets from the test + inputbuffer_wait(); + outb(PS2_DATA, conconfig); + + int portcount = 1; + inputbuffer_wait(); + outb(PS2_COMMAND, 0xA8); // check if ps2 controller is dual channel + inputbuffer_wait(); + outb(PS2_COMMAND, 0x20); + outputbuffer_wait(); + conconfig = inb(PS2_DATA); + if ((conconfig & 0b00100000) == 0b00000000) { // checks if bit 5 is cleared + ps2SecondaryEnabled = true; + //printf("Dual channel PS/2\n"); + portcount = 2; + } else { + //printf("Single channel PS/2\n"); + } + + inputbuffer_wait(); + outb(PS2_COMMAND, 0xAB); // tests ps2 ports + outputbuffer_wait(); + if (inb(PS2_DATA) != 0x00) { + printf("PS/2 port 1 failed self test\n"); + --portcount; + } + if (ps2SecondaryEnabled) { + inputbuffer_wait(); + outb(PS2_COMMAND, 0xA9); + outputbuffer_wait(); + if (inb(PS2_DATA) != 0x00) { + printf("PS/2 port 2 failed self test\n"); + --portcount; + } + } + if (!portcount) { + printf("PS/2 initialization failed\n"); + return; + } + + conconfig |= 0b00000011; + inputbuffer_wait(); + outb(PS2_COMMAND, 0xAE); // enable ps2 ports + conconfig |= 0b00000001; + if (ps2SecondaryEnabled) { + inputbuffer_wait(); + outb(PS2_COMMAND, 0xA8); + outb(PS2_COMMAND, 0x60); + conconfig |= 0b00000011; + outb(PS2_DATA, conconfig); + } else { + inputbuffer_wait(); + outb(PS2_COMMAND, 0x60); + conconfig |= 0b00000001; + outb(PS2_DATA, conconfig); + } + + //ps2_dev_send_command(0, 0xFF); // reset devices + //inb(PS2_DATA); + //io_wait(); + if (ps2SecondaryEnabled) { + //ps2_dev_send_command(1, 0xFF); + //inb(PS2_DATA); + //io_wait(); + } + + //printf("PS/2 controller initialized\n"); +} + +// waits for input buffer (write) to be filled +void inputbuffer_wait() { + for (int i = 0; i < 15; i++) { + uint8_t status_reg = inb(PS2_STATUS); + if ((status_reg & 0b00000010)) { + return; + } + io_wait(); + } +} + +// waits for outbut buffer (read) to be filled +void outputbuffer_wait() { + for (int i = 0; i < 15; i++) { + uint8_t status_reg = inb(PS2_STATUS); + if ((status_reg & 0b00000001)) { + return; + } + io_wait(); + } +} + +// checks if inpbut buffer is set +bool inputbuffer_check() { + uint8_t status_reg = inb(PS2_STATUS); + if ((status_reg & 0b00000010)) { + return true; + } else { + return false; + } +} + +bool outputbuffer_check() { + uint8_t status_reg = inb(PS2_STATUS); + if ((status_reg & 0b00000001)) { + return true; + } else { + return false; + } +} + +/* Sends a command byte to a ps2 device +Port numbers start from 0 */ +int ps2_dev_send_command(int port, unsigned char command) { + //bool commandRecieved = false; + switch (port) + { + case 0: + for (int i = 0; i < 5; i++) { + uint8_t status_reg = inb(PS2_STATUS); + if ((status_reg & 0b00000010) == 0b00000000) { + outb(PS2_DATA, command); + return 0; + } + inb(PS2_DATA); + io_wait(); + } + return 1; // error returned if timeout expires + case 1: + outb(PS2_COMMAND, 0xD4); + for (int i = 0; i < 5; i++) { + uint8_t status_reg = inb(PS2_STATUS); + if ((status_reg & 0b00000010) == 0b00000000) { + outb(PS2_DATA, command); + return 0; + } + inb(PS2_DATA); + io_wait(); + } + return 1; + default: + return 2; // out of port range + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2