#include "mouse.hpp" #include #include #include "../../DAPI.hpp" #include "../drv.hpp" /* https://wiki.osdev.org/VMware_tools */ namespace VMwareMouse { KernelAPI KAPI; void CommandSend(VMwareCommand *cmd) { cmd->magic = VMWARE_MAGIC; cmd->port = VMWARE_PORT; asm volatile("in %%dx, %0" : "+a"(cmd->ax), "+b"(cmd->bx), "+c"(cmd->cx), "+d"(cmd->dx), "+S"(cmd->si), "+D"(cmd->di)); } bool IsVMwareBackdoorAvailable(void) { VMwareCommand cmd; cmd.bx = ~VMWARE_MAGIC; cmd.command = CMD_GETVERSION; CommandSend(&cmd); if (cmd.bx != VMWARE_MAGIC || cmd.ax == 0xFFFFFFFF) return false; return true; } int DriverEntry(void *Data) { if (!Data) return INVALID_KERNEL_API; KAPI = *(KernelAPI *)Data; if (KAPI.Version.Major < 0 || KAPI.Version.Minor < 0 || KAPI.Version.Patch < 0) return KERNEL_API_VERSION_NOT_SUPPORTED; if (!IsVMwareBackdoorAvailable()) return SYSTEM_NOT_SUPPORTED; return OK; } void Absolute(void) { VMwareCommand cmd; /* Enable */ cmd.bx = ABSPOINTER_ENABLE; cmd.command = CMD_ABSPOINTER_COMMAND; CommandSend(&cmd); /* Status */ cmd.bx = 0; cmd.command = CMD_ABSPOINTER_STATUS; CommandSend(&cmd); /* Read data (1) */ cmd.bx = 1; cmd.command = CMD_ABSPOINTER_DATA; CommandSend(&cmd); /* Enable absolute */ cmd.bx = ABSPOINTER_ABSOLUTE; cmd.command = CMD_ABSPOINTER_COMMAND; CommandSend(&cmd); } void Relative(void) { VMwareCommand cmd; cmd.bx = ABSPOINTER_RELATIVE; cmd.command = CMD_ABSPOINTER_COMMAND; CommandSend(&cmd); } enum Config { READ_CONFIG = 0x20, WRITE_CONFIG = 0x60 }; enum Ports { DATA = 0x60, STATUS = 0x64, COMMAND = 0x64, }; enum State { OUTPUT_FULL = (1 << 0), INPUT_FULL = (1 << 1), MOUSE_BYTE = (1 << 5) }; void WaitRead() { uint64_t Timeout = 100000; while (Timeout--) if (inb(Ports::STATUS) & State::OUTPUT_FULL) return; } void WaitWrite() { uint64_t Timeout = 100000; while (Timeout--) if ((inb(Ports::STATUS) & State::INPUT_FULL) == 0) return; } void Write(uint16_t Port, uint8_t Value) { WaitWrite(); outb(Port, Value); } uint8_t Read() { WaitRead(); return inb(Ports::DATA); } int MouseX = 0, MouseY = 0, MouseZ = 0; int MouseButton = 0; int CallbackHandler(KernelCallback *Data) { switch (Data->Reason) { case AcknowledgeReason: { debug("Kernel acknowledged the driver."); break; } case ConfigurationReason: { outb(COMMAND, 0xA8); Write(COMMAND, READ_CONFIG); uint8_t Status = Read(); Status |= 0b10; Write(COMMAND, WRITE_CONFIG); Write(DATA, Status); Write(COMMAND, 0xD4); Write(DATA, 0xF6); Read(); Write(COMMAND, 0xD4); Write(DATA, 0xF4); Read(); Absolute(); trace("VMware mouse configured."); break; } case FetchReason: { Data->InputCallback.Mouse.X = (MouseX * KAPI.Display.GetWidth()) / 0xFFFF; Data->InputCallback.Mouse.Y = (MouseY * KAPI.Display.GetHeight()) / 0xFFFF; Data->InputCallback.Mouse.Z = MouseZ; Data->InputCallback.Mouse.Buttons.Left = MouseButton & 0x20; Data->InputCallback.Mouse.Buttons.Right = MouseButton & 0x10; Data->InputCallback.Mouse.Buttons.Middle = MouseButton & 0x08; break; } case StopReason: { Relative(); // TODO: UNTESTED!!! outb(COMMAND, 0xA8); Write(COMMAND, READ_CONFIG); uint8_t Status = Read(); Status &= ~0b10; Write(COMMAND, WRITE_CONFIG); Write(DATA, Status); Write(COMMAND, 0xD4); Write(DATA, 0xF5); Read(); debug("Driver stopped."); break; } default: { warn("Unknown reason."); break; } } return OK; } int InterruptCallback(CPURegisters *) { uint8_t Data = inb(0x60); (void)Data; VMwareCommand cmd; cmd.bx = 0; cmd.command = CMD_ABSPOINTER_STATUS; CommandSend(&cmd); if (cmd.ax == 0xFFFF0000) { warn("VMware mouse is not connected?"); Relative(); Absolute(); return ERROR; } if ((cmd.ax & 0xFFFF) < 4) return ERROR; cmd.bx = 4; cmd.command = CMD_ABSPOINTER_DATA; CommandSend(&cmd); int flags = (cmd.ax & 0xFFFF0000) >> 16; /* Not important */ (void)flags; MouseButton = (cmd.ax & 0xFFFF); /* 0x10 = Right, 0x20 = Left, 0x08 = Middle */ MouseX = cmd.bx; /* Both X and Y are scaled from 0 to 0xFFFF */ MouseY = cmd.cx; /* You should map these somewhere to the actual resolution. */ MouseZ = (int8_t)cmd.dx; /* Z is a single signed byte indicating scroll direction. */ return OK; } }