diff --git a/src/userland/ps2/main.c b/src/userland/ps2/main.c index 54465e3..69cb8a6 100644 --- a/src/userland/ps2/main.c +++ b/src/userland/ps2/main.c @@ -1,41 +1,191 @@ #include #include +#include "ps2.h" -typedef union { - uint8_t byte; - struct { - uint8_t outputBufferStatus:1; - uint8_t inputBufferStatus: 1; - uint8_t sytemFlag: 1; - uint8_t commandData: 1; - uint8_t unused: 2; - uint8_t timeoutError: 1; - uint8_t parityError: 1; - } __attribute__((packed)) data; - } Status; +const char* deviceTypeNames[] = { + "Unknown PS2 Device", + "AT Keyboard", + "Standard PS2 Mouse", + "Standard PS2 Mouse with Scroll Wheel", + "Mouse with 5 Buttons", + "IBM Thinkpad Keyboard", + "NCD N97 Keyboard", + "Keyboard 122 Keys", + "Japanese G Keyboard", + "Japanese P Keyboard", + "Japanese A Keyboard", + "QEMU keyboard", +}; + Status readStatus() { - Status result = {.byte = ioIn(0x64, 1)}; + Status result = {.byte = ioIn(COMMAND, 1)}; return result; } -void waitForRead() { +uint8_t waitForRead() { uint32_t timeout = 100000; while (!readStatus().data.outputBufferStatus) { if (--timeout == 0) { printf("PS/2 read timeout\n"); + return 1; + } + } + return 0; +} + +void waitForWrite() { + uint32_t timeout = 100000; + while (readStatus().data.inputBufferStatus) { + if (--timeout == 0) { + printf("PS/2 write timeout\n"); return; } } } uint8_t read(uint8_t device) { - waitForRead(); - return ioIn(0x60, 1); + if (waitForRead() == 1) { + return 0xFF; + } + return ioIn(DATA, 1); +} + +void writeController(uint8_t command) { + waitForWrite(); + ioOut(COMMAND, command, 1); +} + +void writeDevice(uint8_t device, uint8_t data) { + if (device == 1) { + writeController(0xD4); + } + waitForWrite(); + ioOut(DATA, data, 1); +} + +Configuration readConfiguration() { + writeController(0x20); + Configuration result = {.byte = read(0)}; + return result; +} + +void writeConfiguration(uint8_t data) { + writeController(0x60); + writeDevice(0, data); +} + +uint8_t checkPort(uint8_t device) { + return read(0); +} + +void flushOutputBuffer() { + while (readStatus().data.outputBufferStatus) { + ioIn(DATA, 1); + } +} + +DeviceType getDeviceType(uint8_t device) { + // disable scanning + writeDevice(device, 0xF5); + // wait for ACK + while (read(device) != 0xFA); + // send identify command + writeDevice(device, 0xF2); + // wait for ACK + while (read(device) != 0xFA); + // read data + uint8_t result1 = read(device); + switch (result1) { + case 0xFF: return ATKeyboard; + case 0x00: return StandardPS2Mouse; + case 0x03: return StandardPS2MouseWithScrollWheel; + case 0x04: return MouseWith5Buttons; + } + if (result1 != 0xAB && result1 != 0xAC) { + return UnknownPS2Device; + } + uint8_t result2 = read(device); + switch (result2) { + case 0x83: return QEMUKeyboard; + case 0x84: return IBMThinkpadKeyboard; + case 0x85: return NCDN97Keyboard; + case 0x86: return Keyboard122Keys; + case 0x90: return JapaneseGKeyboard; + case 0x91: return JapanesePKeyboard; + case 0x92: return JapaneseAKeyboard; + } + return UnknownPS2Device; +} + +void initDevice(uint8_t device) { + flushOutputBuffer(); + // test port + writeController(device == 0 ? 0xAB : 0xA9); + uint8_t deviceHealth = read(0); + if (deviceHealth) { + printf("device %i interface test failed: %i\n", device, deviceHealth); + return; + } + // send enable command + writeController(device == 0 ? 0xAE : 0xA8); + read(device); + flushOutputBuffer(); + // send reset command to device + writeDevice(device, 0xFF); + uint8_t ack = read(device); + deviceHealth = read(device); + if (ack != 0xFA || deviceHealth != 0xAA) { + printf("device %i reset failed with response %x, %x\n", device, ack, deviceHealth); + return; + } + + DeviceType deviceType = getDeviceType(device); + printf("device %i has type '%s'\n", device, deviceTypeNames[deviceType]); } int32_t main() { createFunction("read", (void *)read); + + // disable all devices + writeController(0xAD); + writeController(0xA7); + + flushOutputBuffer(); + + // configure controller + Configuration config = readConfiguration(); + config.data.firstInterruptEnabled = 0; + config.data.secondInterruptEnabled = 0; + config.data.firstPortTranslation = 0; + writeConfiguration(config.byte); + config = readConfiguration(); + + // perform self test + writeController(0xAA); + uint8_t result = read(0); + if (result != 0x55) { + printf("controller self test failed: %x\n", result); + return -1; + } + + initDevice(0); + + if (config.data.secondPortClock) { + // enable second device + writeController(0xA8); + config = readConfiguration(); + if (config.data.secondPortClock) { + printf("controller has support for 2 ports, but the second port isn't in use\n"); + } else { + printf("controller has support for 2 ports, and the second port is in use\n"); + // shut down the second port again + writeController(0xA7); + + initDevice(1); + } + } + loadFromInitrd("ps2kb"); return 0; -} +} \ No newline at end of file diff --git a/src/userland/ps2/main.c b/src/userland/ps2/main.c index 54465e3..69cb8a6 100644 --- a/src/userland/ps2/main.c +++ b/src/userland/ps2/main.c @@ -1,41 +1,191 @@ #include #include +#include "ps2.h" -typedef union { - uint8_t byte; - struct { - uint8_t outputBufferStatus:1; - uint8_t inputBufferStatus: 1; - uint8_t sytemFlag: 1; - uint8_t commandData: 1; - uint8_t unused: 2; - uint8_t timeoutError: 1; - uint8_t parityError: 1; - } __attribute__((packed)) data; - } Status; +const char* deviceTypeNames[] = { + "Unknown PS2 Device", + "AT Keyboard", + "Standard PS2 Mouse", + "Standard PS2 Mouse with Scroll Wheel", + "Mouse with 5 Buttons", + "IBM Thinkpad Keyboard", + "NCD N97 Keyboard", + "Keyboard 122 Keys", + "Japanese G Keyboard", + "Japanese P Keyboard", + "Japanese A Keyboard", + "QEMU keyboard", +}; + Status readStatus() { - Status result = {.byte = ioIn(0x64, 1)}; + Status result = {.byte = ioIn(COMMAND, 1)}; return result; } -void waitForRead() { +uint8_t waitForRead() { uint32_t timeout = 100000; while (!readStatus().data.outputBufferStatus) { if (--timeout == 0) { printf("PS/2 read timeout\n"); + return 1; + } + } + return 0; +} + +void waitForWrite() { + uint32_t timeout = 100000; + while (readStatus().data.inputBufferStatus) { + if (--timeout == 0) { + printf("PS/2 write timeout\n"); return; } } } uint8_t read(uint8_t device) { - waitForRead(); - return ioIn(0x60, 1); + if (waitForRead() == 1) { + return 0xFF; + } + return ioIn(DATA, 1); +} + +void writeController(uint8_t command) { + waitForWrite(); + ioOut(COMMAND, command, 1); +} + +void writeDevice(uint8_t device, uint8_t data) { + if (device == 1) { + writeController(0xD4); + } + waitForWrite(); + ioOut(DATA, data, 1); +} + +Configuration readConfiguration() { + writeController(0x20); + Configuration result = {.byte = read(0)}; + return result; +} + +void writeConfiguration(uint8_t data) { + writeController(0x60); + writeDevice(0, data); +} + +uint8_t checkPort(uint8_t device) { + return read(0); +} + +void flushOutputBuffer() { + while (readStatus().data.outputBufferStatus) { + ioIn(DATA, 1); + } +} + +DeviceType getDeviceType(uint8_t device) { + // disable scanning + writeDevice(device, 0xF5); + // wait for ACK + while (read(device) != 0xFA); + // send identify command + writeDevice(device, 0xF2); + // wait for ACK + while (read(device) != 0xFA); + // read data + uint8_t result1 = read(device); + switch (result1) { + case 0xFF: return ATKeyboard; + case 0x00: return StandardPS2Mouse; + case 0x03: return StandardPS2MouseWithScrollWheel; + case 0x04: return MouseWith5Buttons; + } + if (result1 != 0xAB && result1 != 0xAC) { + return UnknownPS2Device; + } + uint8_t result2 = read(device); + switch (result2) { + case 0x83: return QEMUKeyboard; + case 0x84: return IBMThinkpadKeyboard; + case 0x85: return NCDN97Keyboard; + case 0x86: return Keyboard122Keys; + case 0x90: return JapaneseGKeyboard; + case 0x91: return JapanesePKeyboard; + case 0x92: return JapaneseAKeyboard; + } + return UnknownPS2Device; +} + +void initDevice(uint8_t device) { + flushOutputBuffer(); + // test port + writeController(device == 0 ? 0xAB : 0xA9); + uint8_t deviceHealth = read(0); + if (deviceHealth) { + printf("device %i interface test failed: %i\n", device, deviceHealth); + return; + } + // send enable command + writeController(device == 0 ? 0xAE : 0xA8); + read(device); + flushOutputBuffer(); + // send reset command to device + writeDevice(device, 0xFF); + uint8_t ack = read(device); + deviceHealth = read(device); + if (ack != 0xFA || deviceHealth != 0xAA) { + printf("device %i reset failed with response %x, %x\n", device, ack, deviceHealth); + return; + } + + DeviceType deviceType = getDeviceType(device); + printf("device %i has type '%s'\n", device, deviceTypeNames[deviceType]); } int32_t main() { createFunction("read", (void *)read); + + // disable all devices + writeController(0xAD); + writeController(0xA7); + + flushOutputBuffer(); + + // configure controller + Configuration config = readConfiguration(); + config.data.firstInterruptEnabled = 0; + config.data.secondInterruptEnabled = 0; + config.data.firstPortTranslation = 0; + writeConfiguration(config.byte); + config = readConfiguration(); + + // perform self test + writeController(0xAA); + uint8_t result = read(0); + if (result != 0x55) { + printf("controller self test failed: %x\n", result); + return -1; + } + + initDevice(0); + + if (config.data.secondPortClock) { + // enable second device + writeController(0xA8); + config = readConfiguration(); + if (config.data.secondPortClock) { + printf("controller has support for 2 ports, but the second port isn't in use\n"); + } else { + printf("controller has support for 2 ports, and the second port is in use\n"); + // shut down the second port again + writeController(0xA7); + + initDevice(1); + } + } + loadFromInitrd("ps2kb"); return 0; -} +} \ No newline at end of file diff --git a/src/userland/ps2/ps2.h b/src/userland/ps2/ps2.h new file mode 100644 index 0000000..f5c3471 --- /dev/null +++ b/src/userland/ps2/ps2.h @@ -0,0 +1,54 @@ +#ifndef PS2_H +#define PS2_H + +#include + +#define COMMAND 0x64 +#define DATA 0x60 + +#define PORTS 2 + +typedef union { + uint8_t byte; + struct { + uint8_t outputBufferStatus:1; + uint8_t inputBufferStatus: 1; + uint8_t sytemFlag: 1; + uint8_t commandData: 1; + uint8_t unused: 2; + uint8_t timeoutError: 1; + uint8_t parityError: 1; + } __attribute__((packed)) data; + } Status; + +typedef union { + uint8_t byte; + struct { + uint8_t firstInterruptEnabled:1; + uint8_t secondInterruptEnabled: 1; + uint8_t sytemFlag: 1; + uint8_t zero1: 1; + uint8_t firstPortClock: 1; + uint8_t secondPortClock: 1; + uint8_t firstPortTranslation: 1; + uint8_t zero2: 1; + } __attribute__((packed)) data; + } Configuration; + +typedef enum { + UnknownPS2Device, + ATKeyboard, + StandardPS2Mouse, + StandardPS2MouseWithScrollWheel, + MouseWith5Buttons, + IBMThinkpadKeyboard, + NCDN97Keyboard, + Keyboard122Keys, + JapaneseGKeyboard, + JapanesePKeyboard, + JapaneseAKeyboard, + QEMUKeyboard, +} DeviceType; + + +#endif \ No newline at end of file