Sixty-five headers sat side by side in include/, so `#include "irq.h"` said nothing about where irq.h was or what it belonged to, and errno.h, stat.h and fcntl.h at the top shadowed the system's for any host tool built with -I./include. They are in folders now -- arch/ core/ fs/ dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and every include names the folder, as Linux's do: "core/proc.h". Done mechanically and checked the hard way: the kernel built from this tree disassembles identically to the one built from the tree before it. The full image boots, and userland is the same set of programs. Paths in docs and comments follow; docs/guides/layout.md has a table of what is where, and tools/fix-includes.py rewrites old-style includes from the folders in the tree, for code written before this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
265 lines
No EOL
6 KiB
C
265 lines
No EOL
6 KiB
C
#include "core/types.h"
|
|
#include "core/defs.h"
|
|
#include "gfx/gfx.h"
|
|
#include "core/irq.h"
|
|
#include "arch/x86.h"
|
|
#include "arch/traps.h"
|
|
#include "core/spinlock.h"
|
|
#include "core/sleeplock.h"
|
|
#include "fs/fs.h"
|
|
#include "fs/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)gfx_width();
|
|
int maxy = (int)gfx_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)gfx_width();
|
|
int maxy = (int)gfx_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, uint64 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;
|
|
|
|
// No 8042 at all: the port floats and reads 0xFF. That is a machine with
|
|
// USB input only -- most laptops and desktops built in the last few years,
|
|
// whenever the firmware's legacy emulation is off -- and every bit set means
|
|
// "output full" for ever, so the flush below never ended and boot stopped
|
|
// here. There is no mouse to set up, so do not.
|
|
if(inb(KBD_S_STAT) == 0xFF){
|
|
kinfo("ps2: no controller; mouse not set up\n");
|
|
return;
|
|
}
|
|
|
|
// Drain whatever the controller is holding. Bounded, because a controller
|
|
// that keeps producing bytes is broken and still not a reason to hang.
|
|
for(int i = 0; i < 1024 && (inb(KBD_S_STAT) & 0x01); i++)
|
|
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);
|
|
} |