#include "types.h" #include "defs.h" #include "irq.h" #include "x86.h" #include "traps.h" #include "spinlock.h" #include "sleeplock.h" #include "fs.h" #include "file.h" #define KBD_S_STAT 0x64 #define KBD_S_DATA 0x60 #define KBD_C_WRITE 0xD4 #define MOUSE_ACK 0xFA struct { struct spinlock lock; int x; int y; uint8 buttons; } mouse_state; static void mouse_wait(int type) { int timeout = 100000; if(type == 0){ while(timeout-- && !(inb(KBD_S_STAT) & 0x01)); } else { while(timeout-- && (inb(KBD_S_STAT) & 0x02)); } } static void mouse_write(uchar data) { mouse_wait(1); outb(KBD_S_STAT, KBD_C_WRITE); mouse_wait(1); outb(KBD_S_DATA, data); } static uchar mouse_read(void) { mouse_wait(0); return inb(KBD_S_DATA); } static void mouse_irq(void *arg) { (void)arg; mouseintr(); } // Move the pointer. Caller holds mouse_state.lock. // // Positive dy is *down*, matching both the screen and USB HID. The PS/2 // packet reports the opposite, which is why its caller negates first -- // there is one pointer and two protocols disagreeing about which way is up, // and the conversion belongs at the edge rather than in here. static void mouse_move_locked(int dx, int dy) { mouse_state.x += dx; mouse_state.y += dy; // Clamp to the screen that actually exists. These were the literals 800 and // 600, which was correct while that was the only mode the loader could set. // It is a boot-time choice now (press R), and at 1280x800 the pointer simply // stopped two thirds of the way across -- which reads as the cursor drifting // away from the host's, rather than as a clamp. int maxx = (int)vga_get_width(); int maxy = (int)vga_get_height(); if(maxx <= 0) maxx = 800; if(maxy <= 0) maxy = 600; if(mouse_state.x < 0) mouse_state.x = 0; if(mouse_state.y < 0) mouse_state.y = 0; if(mouse_state.x >= maxx) mouse_state.x = maxx - 1; if(mouse_state.y >= maxy) mouse_state.y = maxy - 1; } // The same pointer, moved by something that is not the PS/2 port. USB HID // uses this; anything else that produces motion events should too. void mouse_inject(int dx, int dy, int buttons) { acquire(&mouse_state.lock); mouse_state.buttons = buttons; mouse_move_locked(dx, dy); release(&mouse_state.lock); input_mouse(dx, dy, buttons); } // Put the pointer somewhere, rather than nudging it. // // The clamp is the same as the relative path's, and for the same reason: the // screen size is a boot-time choice now. The coordinates arrive already scaled // to pixels -- the HID driver does that, because it is the only place that // knows the device's logical range. void mouse_set_abs(int x, int y, int buttons) { int maxx = (int)vga_get_width(); int maxy = (int)vga_get_height(); if(maxx <= 0) maxx = 800; if(maxy <= 0) maxy = 600; if(x < 0) x = 0; if(y < 0) y = 0; if(x >= maxx) x = maxx - 1; if(y >= maxy) y = maxy - 1; acquire(&mouse_state.lock); mouse_state.buttons = buttons; mouse_state.x = x; mouse_state.y = y; release(&mouse_state.lock); input_mouse_abs(x, y, buttons); } void mouseintr(void) { static uchar buf[3]; static int phase = 0; uchar status; status = inb(KBD_S_STAT); if(!(status & 0x01) || !(status & 0x20)){ return; } uchar data = inb(KBD_S_DATA); acquire(&mouse_state.lock); if(phase == 0 && !(data & 0x08)){ release(&mouse_state.lock); return; } buf[phase++] = data; if(phase == 3){ phase = 0; mouse_state.buttons = buf[0] & 0x07; int dx = (buf[0] & 0x10) ? (int)buf[1] - 256 : buf[1]; int dy = (buf[0] & 0x20) ? (int)buf[2] - 256 : buf[2]; // The overflow bits mean the delta saturated and is meaningless; moving // by it would throw the pointer across the screen. if(!(buf[0] & 0x40) && !(buf[0] & 0x80)) mouse_move_locked(dx, -dy); else dx = dy = 0; release(&mouse_state.lock); // Outside the lock: input_report takes its own, and holding two at once // is how a lock ordering rule gets invented by accident. input_mouse(dx, -dy, buf[0] & 0x07); return; } release(&mouse_state.lock); } int get_mouse_x(void) { int x; acquire(&mouse_state.lock); x = mouse_state.x; release(&mouse_state.lock); return x; } int get_mouse_y(void) { int y; acquire(&mouse_state.lock); y = mouse_state.y; release(&mouse_state.lock); return y; } uint8 get_mouse_buttons(void) { uint8 b; acquire(&mouse_state.lock); b = mouse_state.buttons; release(&mouse_state.lock); return b; } int mouseread(struct inode *ip, uint off, char *dst, int n) { struct mouse_data m; int size = sizeof(struct mouse_data); if(n < size) { return -1; } m.x = get_mouse_x(); m.y = get_mouse_y(); m.buttons = get_mouse_buttons(); memmove(dst, &m, size); return size; } void mouseinit(void) { initlock(&mouse_state.lock, "mouse"); mouse_state.x = 400; mouse_state.y = 300; while(inb(KBD_S_STAT) & 0x01) inb(KBD_S_DATA); mouse_wait(1); outb(KBD_S_STAT, 0xA8); mouse_wait(1); outb(KBD_S_STAT, 0x20); mouse_wait(0); uchar cb = inb(KBD_S_DATA); cb |= 0x02; cb |= 0x01; cb &= ~0x20; mouse_wait(1); outb(KBD_S_STAT, 0x60); mouse_wait(1); outb(KBD_S_DATA, cb); mouse_write(0xF6); mouse_read(); mouse_write(0xF4); mouse_read(); irq_register(IRQ_MOUSE, mouse_irq, 0, "ps2-mouse", 0); }