diff --git a/kernel-src/arch/x86-64/main.c b/kernel-src/arch/x86-64/main.c index ad5e4f7f..ed5cf8e9 100644 --- a/kernel-src/arch/x86-64/main.c +++ b/kernel-src/arch/x86-64/main.c @@ -34,6 +34,7 @@ #include #include #include +#include static cpu_t bsp_cpu; @@ -79,6 +80,7 @@ void kernel_entry() { arch_e9_initdev(); pci_init(); keyboard_init(); + mouse_init(); arch_ps2_init(); fb_init(); nvme_init(); diff --git a/kernel-src/arch/x86-64/ps2.c b/kernel-src/arch/x86-64/ps2.c index d610d0f9..5a86f416 100644 --- a/kernel-src/arch/x86-64/ps2.c +++ b/kernel-src/arch/x86-64/ps2.c @@ -1,6 +1,7 @@ #include -#include +#include #include +#include static bool reset_selftest(int port) { uint8_t r = RESEND; @@ -17,10 +18,11 @@ static bool reset_selftest(int port) { timeout = true; if (!timeout) { - if (read_data() != DEVICE_SELFTEST_OK) + if (read_data() != DEVICE_SELFTEST_OK) { printf("ps2: %s port device self test failed!\n", port == 1 ? "First" : "Second"); - else + } else { return true; + } } return false; @@ -116,4 +118,6 @@ void arch_ps2_init() { // XXX should id each one for proper identification if (connectedflag & 1) ps2kbd_init(); + if (connectedflag & 2) + ps2mouse_init(); } diff --git a/kernel-src/arch/x86-64/ps2mouse.c b/kernel-src/arch/x86-64/ps2mouse.c new file mode 100644 index 00000000..53a93d2d --- /dev/null +++ b/kernel-src/arch/x86-64/ps2mouse.c @@ -0,0 +1,112 @@ +#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; + +static uint8_t data[4]; + +static inline bool enoughdata() { + if (!((haswheel && datac == 4) || (haswheel == false && datac == 3))) + return false; + else + return true; +} + +static mouse_t *mouse; + +static void mouseisr() { + data[datac] = inb(PS2_PORT_DATA); + + // check if the packet is bad + if ((data[0] & 8) == 0) { + datac = 0; + return; + } + + ++datac; + + if (!enoughdata()) + return; + + mousepacket_t packet = {0}; + + 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; + + packet.x = data[1] - (data[0] & 0x10 ? 0x100 : 0); + packet.y = data[2] - (data[0] & 0x20 ? 0x100 : 0); + packet.z = (data[3] & 0x7) * (data[3] & 0x8 ? -1 : 1); + + packet.flags |= left ? MOUSE_FLAG_LB : 0; + packet.flags |= middle ? MOUSE_FLAG_MB : 0; + packet.flags |= right ? MOUSE_FLAG_RB : 0; + packet.flags |= b4 ? MOUSE_FLAG_B4 : 0; + packet.flags |= b5 ? MOUSE_FLAG_B5 : 0; + mouse_packet(mouse, &packet); +} + + +void ps2mouse_init() { + if (identify(2) != 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); + + if (identify(2) == MOUSE_Z) { + haswheel = true; + + // check if mouse has 5 buttons + mouse_setrate(2, 200); + mouse_setrate(2, 200); + mouse_setrate(2, 80); + + if (identify(2) == MOUSE_5B) + fivebuttons = true; + } + + mouse_setrate(2, 60); + __assert(device_write_response(2, MOUSE_CMD_REPORTDATA) == ACK); + + isr_t *isr = interrupt_allocate(mouseisr, arch_apic_eoi, IPL_MOUSE); + __assert(isr); + arch_ioapic_setirq(MOUSEIRQ, isr->id & 0xff, _cpu()->id, false); + mouse = mouse_new(); + __assert(mouse); + printf("ps2mouse: irq enabled with vector %u\n", isr->id & 0xff); + printf("ps2mouse: wheel: %d five buttons: %d\n", haswheel, fivebuttons); +} diff --git a/kernel-src/include/kernel/devfs.h b/kernel-src/include/kernel/devfs.h index 7101617e..ae717dc0 100644 --- a/kernel-src/include/kernel/devfs.h +++ b/kernel-src/include/kernel/devfs.h @@ -15,6 +15,7 @@ #define DEV_MAJOR_E9 8 #define DEV_MAJOR_URANDOM 9 #define DEV_MAJOR_NET 10 +#define DEV_MAJOR_MOUSE 11 typedef struct { int (*open)(int minor, int flags); diff --git a/kernel-src/include/kernel/interrupt.h b/kernel-src/include/kernel/interrupt.h index 20877f44..09dcfdac 100644 --- a/kernel-src/include/kernel/interrupt.h +++ b/kernel-src/include/kernel/interrupt.h @@ -15,9 +15,10 @@ #define IPL_IGNORE -1 #define IPL_MAX 0 #define IPL_TIMER 1 -#define IPL_KEYBOARD 2 #define IPL_NET 400 #define IPL_DISK 500 +#define IPL_KEYBOARD 600 +#define IPL_MOUSE 650 #define IPL_DPC 900 #define IPL_NORMAL 1000 diff --git a/kernel-src/include/kernel/mouse.h b/kernel-src/include/kernel/mouse.h new file mode 100644 index 00000000..834abaa0 --- /dev/null +++ b/kernel-src/include/kernel/mouse.h @@ -0,0 +1,29 @@ +#ifndef _MOUSE_H +#define _MOUSE_H + +#include +#include + +#define MOUSE_FLAG_RB 1 +#define MOUSE_FLAG_MB 2 +#define MOUSE_FLAG_LB 4 +#define MOUSE_FLAG_B4 8 +#define MOUSE_FLAG_B5 16 + +typedef struct { + int flags; + int x,y,z; +} mousepacket_t; + +typedef struct { + ringbuffer_t packetbuffer; + spinlock_t lock; + pollheader_t pollheader; + int flags; +} mouse_t; + +void mouse_init(); +mouse_t *mouse_new(); +void mouse_packet(mouse_t *mouse, mousepacket_t *packet); + +#endif diff --git a/kernel-src/include/x86-64/arch/ps2mouse.h b/kernel-src/include/x86-64/arch/ps2mouse.h new file mode 100644 index 00000000..34dac71c --- /dev/null +++ b/kernel-src/include/x86-64/arch/ps2mouse.h @@ -0,0 +1,6 @@ +#ifndef _PS2MOUSE_H +#define _PS2MOUSE_H + +void ps2mouse_init(); + +#endif diff --git a/kernel-src/io/mouse.c b/kernel-src/io/mouse.c new file mode 100644 index 00000000..ed5b0d1d --- /dev/null +++ b/kernel-src/io/mouse.c @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#include +#include +#include + +#define BUFFER_PACKET_CAPACITY 100 +#define BUFFER_SIZE (BUFFER_PACKET_CAPACITY * sizeof(mousepacket_t)) + +static int currentnum; + +static mouse_t *newmouse() { + mouse_t *mouse = alloc(sizeof(mouse_t)); + if (mouse == NULL) + return NULL; + + if (ringbuffer_init(&mouse->packetbuffer, BUFFER_SIZE)) { + free(mouse); + return NULL; + } + + POLL_INITHEADER(&mouse->pollheader); + SPINLOCK_INIT(mouse->lock); + + return mouse; +} + +static void destroymouse(mouse_t *mouse) { + ringbuffer_destroy(&mouse->packetbuffer); + free(mouse); +} + +void mouse_packet(mouse_t *mouse, mousepacket_t *packet) { + int oldipl = interrupt_raiseipl(IPL_MOUSE); + spinlock_acquire(&mouse->lock); + + ringbuffer_write(&mouse->packetbuffer, packet, sizeof(mousepacket_t)); + poll_event(&mouse->pollheader, POLLIN); + + spinlock_release(&mouse->lock); + interrupt_loweripl(oldipl); +} + +static hashtable_t mousetable; +static mutex_t tablelock; + +static mouse_t* getmouse(int mouse) { + void *mousep = NULL; + MUTEX_ACQUIRE(&tablelock, false); + hashtable_get(&mousetable, &mousep, &mouse, sizeof(mouse)); + MUTEX_RELEASE(&tablelock); + return mousep; +} + +static int internalpoll(mouse_t *mouse, polldata_t *data, int events) { + int revents = 0; + + if ((events & POLLIN) && RINGBUFFER_DATACOUNT(&mouse->packetbuffer)) + revents |= POLLIN; + + if (revents == 0 && data) + poll_add(&mouse->pollheader, data, events); + + return revents; +} + +static int read(int minor, void *buffer, size_t size, uintmax_t offset, int flags, size_t *readc) { + mouse_t *mouse = getmouse(minor); + if (mouse == NULL) + return ENODEV; + + int error = 0; + *readc = 0; + size_t packetcount = size / sizeof(mousepacket_t); + if (packetcount == 0) + return error; + + // disable interrupts here as to not lose any interrupts + // and because we cannot sleep with a raised ipl + bool oldintstatus = interrupt_set(false); + spinlock_acquire(&mouse->lock); + + // wait until there is data to read or return if nonblock + for (;;) { + polldesc_t desc = {0}; + error = poll_initdesc(&desc, 1); + if (error) + goto leave; + + int revents = internalpoll(mouse, &desc.data[0], POLLIN); + + if (revents) { + poll_leave(&desc); + poll_destroydesc(&desc); + break; + } + + if (revents == 0 && flags & V_FFLAGS_NONBLOCKING) { + error = EAGAIN; + goto leave; + } + + spinlock_release(&mouse->lock); + error = poll_dowait(&desc, 0); + spinlock_acquire(&mouse->lock); + + poll_leave(&desc); + poll_destroydesc(&desc); + + if (error) + goto leave; + } + + *readc = ringbuffer_read(&mouse->packetbuffer, buffer, sizeof(mousepacket_t) * packetcount); + __assert(*readc); + + leave: + spinlock_release(&mouse->lock); + interrupt_set(oldintstatus); + + return error; +} + +static int poll(int minor, polldata_t *data, int events) { + mouse_t *mouse = getmouse(minor); + if (mouse == NULL) + return POLLERR; + + long ipl = interrupt_raiseipl(IPL_MOUSE); + spinlock_acquire(&mouse->lock); + int revents = internalpoll(mouse, data, events); + spinlock_release(&mouse->lock); + interrupt_loweripl(ipl); + + return revents; +} + +static devops_t devops = { + .read = read, + .poll = poll +}; + +mouse_t *mouse_new() { + mouse_t *mouse = newmouse(); + if (mouse == NULL) + return NULL; + + MUTEX_ACQUIRE(&tablelock, false); + + if (hashtable_set(&mousetable, mouse, ¤tnum, sizeof(currentnum), true)) { + MUTEX_RELEASE(&tablelock); + destroymouse(mouse); + return NULL; + } + + MUTEX_RELEASE(&tablelock); + + char name[12]; + + snprintf(name, 12, "mouse%d", currentnum); + __assert(devfs_register(&devops, name, V_TYPE_CHDEV, DEV_MAJOR_MOUSE, currentnum, 0644) == 0); + + ++currentnum; + return mouse; +} + +void mouse_init() { + MUTEX_INIT(&tablelock); + __assert(hashtable_init(&mousetable, 20) == 0); +} +