From 2b4e97735bb7bffe0b273e0debba3d89a8c6683a Mon Sep 17 00:00:00 2001 From: Matheus Date: Thu, 5 Jan 2023 22:58:23 -0300 Subject: [PATCH] ps2 mouse: initial implementation --- src/arch/x86_64/idt.c | 2 + src/arch/x86_64/include/arch/idt.h | 1 + src/arch/x86_64/include/arch/isr.h | 1 + src/arch/x86_64/include/arch/ps2.h | 108 ++++++++++++++++++++++++++++ src/arch/x86_64/isr.asm | 1 + src/arch/x86_64/isr.c | 5 ++ src/arch/x86_64/ps2.c | 84 ++-------------------- src/arch/x86_64/ps2kbd.c | 27 +------ src/arch/x86_64/ps2mouse.c | 112 +++++++++++++++++++++++++++++ 9 files changed, 236 insertions(+), 105 deletions(-) create mode 100644 src/arch/x86_64/include/arch/ps2.h create mode 100644 src/arch/x86_64/ps2mouse.c diff --git a/src/arch/x86_64/idt.c b/src/arch/x86_64/idt.c index 7f704fc1..3a668f38 100644 --- a/src/arch/x86_64/idt.c +++ b/src/arch/x86_64/idt.c @@ -68,6 +68,8 @@ void idt_bspinit(){ idt_setentry(&idt[VECTOR_TIMER], asmisr_timer, 0x28, FLAGS_PRESENT | FLAGS_TYPE_INTERRUPT, TIMER_IST); + idt_setentry(&idt[VECTOR_PS2MOUSE], asmisr_ps2mouse, 0x28, FLAGS_PRESENT | FLAGS_TYPE_INTERRUPT, 0); + idt_setentry(&idt[VECTOR_PS2KBD], asmisr_ps2kbd, 0x28, FLAGS_PRESENT | FLAGS_TYPE_INTERRUPT, 0); idt_reload(); diff --git a/src/arch/x86_64/include/arch/idt.h b/src/arch/x86_64/include/arch/idt.h index a4712b1a..d9649bbb 100644 --- a/src/arch/x86_64/include/arch/idt.h +++ b/src/arch/x86_64/include/arch/idt.h @@ -29,6 +29,7 @@ #define VECTOR_PANIC 0x20 #define VECTOR_LAPICNMI 0x21 #define VECTOR_MMUINVAL 0x22 +#define VECTOR_PS2MOUSE 0x3F #define VECTOR_PS2KBD 0x40 #define VECTOR_TIMER 0x80 diff --git a/src/arch/x86_64/include/arch/isr.h b/src/arch/x86_64/include/arch/isr.h index e4a11e49..7aa3ed11 100644 --- a/src/arch/x86_64/include/arch/isr.h +++ b/src/arch/x86_64/include/arch/isr.h @@ -10,6 +10,7 @@ extern void asmisr_pagefault(); extern void asmisr_panic(); extern void asmisr_lapicnmi(); extern void asmisr_mmuinval(); +extern void asmisr_ps2mouse(); extern void asmisr_ps2kbd(); extern void asmisr_timer(); extern void asmisr_simd(); diff --git a/src/arch/x86_64/include/arch/ps2.h b/src/arch/x86_64/include/arch/ps2.h new file mode 100644 index 00000000..ab74659a --- /dev/null +++ b/src/arch/x86_64/include/arch/ps2.h @@ -0,0 +1,108 @@ +#ifndef _PS2_H_INCLUDE +#define _PS2_H_INCLUDE + +#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 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){ + + struct timespec initial = arch_timekeeper_gettimefromboot(); + + while(outbuffer_full() == false){ + struct timespec current = arch_timekeeper_gettimefromboot(); + if(time_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(); + ++tries; + } + + return r; + +} + +#endif diff --git a/src/arch/x86_64/isr.asm b/src/arch/x86_64/isr.asm index 4f1eafbe..ff9aa64c 100644 --- a/src/arch/x86_64/isr.asm +++ b/src/arch/x86_64/isr.asm @@ -157,6 +157,7 @@ isr asmisr_general, isr_general, 0xFF except asmisr_except, isr_except, 0xFF isr asmisr_lapicnmi, isr_lapicnmi, 0x21 isr asmisr_mmuinval, isr_mmuinval, 0x22 +isr asmisr_ps2mouse, isr_ps2mouse, 0x3F isr asmisr_ps2kbd, isr_ps2kbd, 0x40 isr asmisr_timer, isr_timer, 0x80 isr asmisr_simd, isr_simd, 0x13 diff --git a/src/arch/x86_64/isr.c b/src/arch/x86_64/isr.c index 9682de5e..37c178f6 100644 --- a/src/arch/x86_64/isr.c +++ b/src/arch/x86_64/isr.c @@ -71,6 +71,11 @@ void isr_ps2kbd(arch_regs* reg){ apic_eoi(); } +void isr_ps2mouse(arch_regs* reg){ + ps2mouse_irq(); + apic_eoi(); +} + void isr_timer(arch_regs* reg){ timer_irq(reg); apic_eoi(); diff --git a/src/arch/x86_64/ps2.c b/src/arch/x86_64/ps2.c index ceead785..e5bbe1e5 100644 --- a/src/arch/x86_64/ps2.c +++ b/src/arch/x86_64/ps2.c @@ -10,86 +10,7 @@ #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 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 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){ - - struct timespec initial = arch_timekeeper_gettimefromboot(); - - while(outbuffer_full() == false){ - struct timespec current = arch_timekeeper_gettimefromboot(); - if(time_diffms(initial, current) >= ms){ - printf("spinning!!! diff: %d\n", time_diffms(initial, current)); - *timeout = true; - return 0; - } - } - - *timeout = false; - return read_data(); - -} +#include static bool reset_selftest(int port){ uint8_t r = RESEND; @@ -221,5 +142,8 @@ void ps2_init(){ if(workingflag & 1) ps2kbd_init(); + + if(workingflag & 2) + ps2mouse_init(); } diff --git a/src/arch/x86_64/ps2kbd.c b/src/arch/x86_64/ps2kbd.c index eb14914e..46635739 100644 --- a/src/arch/x86_64/ps2kbd.c +++ b/src/arch/x86_64/ps2kbd.c @@ -16,24 +16,6 @@ static int kb; -static uint8_t ps2cmd(uint8_t cmd){ - - uint8_t response; - - for(size_t i = 0; i < 3; ++i){ - outb(PS2_PORT_DATA, cmd); - - response = inb(PS2_PORT_DATA); - - if(response != RESEND) - break; - - } - - return response; - -} - static char codes[] = { KEYCODE_RESERVED, KEYCODE_ESCAPE, @@ -176,13 +158,8 @@ void ps2kbd_irq(){ } void ps2kbd_init(){ - - // maybe the ps/2 controller and keyboard should be initialised to a known state - // and not be assumed to be set up in a working manner by the firmware/bootloader - - // this probably shouldn't assume the keyboard always being in the first port but oh well - - // clear keyboard buffer data + + // clear PS/2 buffer to make sure its empty inb(PS2_PORT_DATA); diff --git a/src/arch/x86_64/ps2mouse.c b/src/arch/x86_64/ps2mouse.c new file mode 100644 index 00000000..803bbd9b --- /dev/null +++ b/src/arch/x86_64/ps2mouse.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include + +#define MOUSE_CMD_SAMPLERATE 0xF3 +#define MOUSE_CMD_REPORTDATA 0xF4 + +#define MOUSE 0 +#define MOUSE_Z 3 +#define 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){ + + __assert(device_write_response(port, DEVICE_CMD_IDENTIFY) == ACK); + + bool timeout; + + uint8_t b = read_data_timeout(5, &timeout); + + __assert(!timeout); + + return b; +} + +static int datac; + +uint8_t data[4]; + +static inline bool enoughdata(){ + if(!((haswheel && datac == 4) || (haswheel == false && datac == 3))) + return false; + else + return true; +} + +void ps2mouse_irq(){ + + data[datac] = inb(PS2_PORT_DATA); + + // check if the packet is bad + + if((data[0] & 8) == 0){ + datac = 0; + return; + } + + + ++datac; + + if(!enoughdata()) + return; + + datac = 0; + bool left = data[0] & 1; + bool right = data[0] & 2; + bool middle = data[0] & 4; + bool b4 = data[3] & 0x10; + bool b5 = data[3] & 0x20; + + int xoffset = data[1] * (data[0] & 0x10 ? -1 : 1); + int yoffset = data[2] * (data[0] & 0x20 ? -1 : 1); + int zoffset = (data[3] & 0x7) * (data[3] & 0x8 ? -1 : 1); + + + +} + +void ps2mouse_init(){ + + + if(identify(2) != MOUSE){ + printf("Not a mouse!\n"); + return; + } + + mouse_setrate(2, 200); + mouse_setrate(2, 100); + mouse_setrate(2, 80); + + if(identify(2) == MOUSE_Z){ + haswheel = true; + + mouse_setrate(2, 200); + mouse_setrate(2, 200); + mouse_setrate(2, 80); + + if(identify(2) == MOUSE_5B) + fivebuttons = true; + + } + + + mouse_setrate(2, 60); // TODO verify the best packet rate + + __assert(device_write_response(2, MOUSE_CMD_REPORTDATA) == ACK); + + ioapic_setlegacyirq(MOUSEIRQ, VECTOR_PS2MOUSE, arch_getcls()->lapicid, false); + +}