diff options
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | README.md | 56 | ||||
-rw-r--r-- | makefile | 26 | ||||
-rw-r--r-- | mousew16.c | 215 | ||||
-rw-r--r-- | mousew16.h | 53 | ||||
-rw-r--r-- | oemsetup.inf | 8 | ||||
-rw-r--r-- | pci.h | 122 | ||||
-rw-r--r-- | ps2.h | 124 | ||||
-rw-r--r-- | vbmouse.lnk | 9 | ||||
-rw-r--r-- | vbox.c | 239 | ||||
-rw-r--r-- | vbox.h | 73 | ||||
-rw-r--r-- | vboxdev.h | 495 |
12 files changed, 1425 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0fd4460 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +vbmouse.drv +vbmouse.map +vbmouse.flp +*.o +runvm.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..d26c58f --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +This is a mouse driver for Windows 3.x with VirtualBox mouse integration support. + +I have tested it with Windows 3.0 in real and 386 enhanced modes, as well as Windows 3.11 in 386 enhanced mode. + +# Install + +Just copy vbmouse.drv to your WINDOWS\SYSTEM directory and edit WINDOWS\SYSTEM.INI 's mouse.drv line to point to it, e.g. + + [boot] + mouse.drv = vbmouse.drv + +For a "proper" installation, you may create a floppy image containing oemsetup.inf and vbmouse.drv +and point the Windows Setup program to it when it asks for a 3rd party mouse driver disk. + +# Building + +This requires [http://open-watcom.github.io/](OpenWatcom 2.0), albeit it may work with an older version, +and was only tested on a Linux host. + +The included makefile is a wmake makefile. To build it just enter the OpenWatcom environment and run `wmake vbmouse.drv`. +`wmake flp` may be used to build a floppy image containg oemsetup.inf and vbmouse.drv for easier installation. + +# Design + +This is at its core a driver for a plain PS/2 mouse driver, using the PS/2 BIOS. +If running outside VirtualBox, in fact it will behave like a PS/2 mouse driver. +However, it removes a lot of checks for older platforms and is mostly written in C rather than assembly, +so hopefully it is easier to understand than the Windows sample drivers. + +The VirtualBox guest integrations present itself as a PCI device to the guest. +Thanks to the BIOS, the device is already pre-configured. +The driver uses the real-mode PCI BIOS to request the current configuration of this PCI device. +To communicate with the host, the guest must send the PCI device the (physical) address +of a buffer containing commands to be sent to the host. +The host will write back the response in the same buffer. +Further details are available in [https://wiki.osdev.org/VirtualBox_Guest_Additions](OSDev). +The only challenge here is getting the physical address from inside 386 protected mode Windows, +but turns out this can be done with the `GetSelectorBase()` WINAPI. + +When VirtualBox is told that the guest wants absolute mouse information, VirtualBox will stop sending +relative mouse information via the PS/2 mouse. However, the PS/2 controller will still send interrupts +whenever mouse motion happens, and it will still report mouse button presses. In fact, the only way +to obtain mouse button presses is still through the PS/2 controller. +Thus, the only difference between this driver and a standard PS/2 mouse driver is that, +when an interrupt from the mouse comes in, we won't report the relative mouse motion to Windows. +Rather, we call into VirtualBox (right from the PS/2 BIOS interrupt handler) +to obtain the absolution mouse position, and report that to Windows. + +### Known issues + +Unfortunately, when a MS-DOS old application is foregrounded full-screen, this mouse driver will remain active, +and therefore VirtualBox will not send relative motion data via the PS/2 protocol. +This means full-screen MS-DOS programs will see a PS/2 mouse and will receive button presses from it, but will not +receive mouse motion. The driver would need to deactivative itself in response to a full-screen MS-DOS VM, perhaps +by hooking interrupt 2Fh function 4002h (Notify Foreground Switch) or the like. + diff --git a/makefile b/makefile new file mode 100644 index 0000000..1366864 --- /dev/null +++ b/makefile @@ -0,0 +1,26 @@ +# This is an Open Watcom wmake makefile, not GNU make. +# Assuming you have sourced `owsetenv` beforehand. + +.BEFORE: + # We need DOS and Windows headers, not host platform's + set include=$(%watcom)/h/win;$(%watcom)/h + +# The main driver file +vbmouse.drv: mousew16.c mousew16.h vbox.c vbox.h vboxdev.h ps2.h pci.h + # -bd to build DLL + # -mc to use compact memory model (far data pointers, since ss != ds) + # -zu for DLL calling convention (ss != ds) + # -zc put constants on the code segment (cs) + # -s to disable stack checks, since the runtime uses MessageBox() to abort (which we can't call from mouse.drv) + wcl -6 -mc -bd -zu -zc -s -bt=windows -l=windows_dll @vbmouse.lnk -fe=$^@ mousew16.c vbox.c + +clean: .SYMBOLIC + rm -f vbmouse.drv vbmouse.flp *.o + +vbmouse.flp: + mformat -C -f 1440 -v VBMOUSE -i $^@ :: + +# Build a floppy image containing the driver +flp: vbmouse.flp vbmouse.drv oemsetup.inf .SYMBOLIC + mcopy -i vbmouse.flp -o oemsetup.inf vbmouse.drv :: + diff --git a/mousew16.c b/mousew16.c new file mode 100644 index 0000000..94bd309 --- /dev/null +++ b/mousew16.c @@ -0,0 +1,215 @@ +/* + * VBMouse - win16 mouse driver entry points + * Copyright (C) 2022 Javier S. Pedro + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <windows.h> + +#include "vbox.h" +#include "vboxdev.h" +#include "ps2.h" +#include "mousew16.h" + +/** If this is 0, this should behave like a plain PS/2 mouse driver. */ +#define ENABLE_VBOX 1 + +/** Logging through the virtualbox backdoor. */ +#define log(...) vbox_logs(__VA_ARGS__) + +#define MOUSE_NUM_BUTTONS 2 + +/** The routine Windows gave us which we should use to report events. */ +static LPFN_MOUSEEVENT eventproc; +/** Current status of the mouse driver (see MOUSEFLAGS_*). */ +static unsigned char mouseflags; +enum { + MOUSEFLAGS_ENABLED = 1 << 0, + MOUSEFLAGS_HAS_VBOX = 1 << 1, + MOUSEFLAGS_VBOX_ENABLED = 1 << 2, +}; +/** Last received pressed button status (to compare and see which buttons have been pressed). */ +static unsigned char mousebtnstatus; + +/* This is how events are delivered to Windows */ + +static void send_event(unsigned short Status, short deltaX, short deltaY, short ButtonCount, short extra1, short extra2); +#pragma aux (MOUSEEVENTPROC) send_event = \ + "call dword ptr [eventproc]" + +/* PS/2 BIOS mouse callback. */ + +#pragma code_seg ( "CALLBACKS" ) +static void FAR ps2_mouse_callback(uint8_t status, uint8_t x, uint8_t y, uint8_t z) +{ +#pragma aux (PS2_CB) ps2_mouse_callback + + int sstatus = 0; + int sx = status & PS2M_STATUS_X_NEG ? 0xFF00 | x : x; + int sy = -(status & PS2M_STATUS_Y_NEG ? 0xFF00 | y : y); + + if (!(mouseflags & MOUSEFLAGS_ENABLED)) { + // Likely eventproc is invalid + return; + } + + if (sx || sy) { + sstatus |= SF_MOVEMENT; + } + +#if ENABLE_VBOX + if ((sstatus & SF_MOVEMENT) && (mouseflags & MOUSEFLAGS_VBOX_ENABLED)) { + bool abs; + uint16_t vbx, vby; + // Even if we are connected to VBox, the user may have selected to disable abs positioning + // So only report abs coordinates if it is still enabled. + if (vbox_get_mouse_locked(&abs, &vbx, &vby) == 0 && abs) { + sx = vbx; + sy = vby; + sstatus |= SF_ABSOLUTE; + } + } +#endif + + // Now proceed to see which buttons have been pressed down and/or released + if ((mousebtnstatus & PS2M_STATUS_BUTTON_1) && !(status & PS2M_STATUS_BUTTON_1)) { + sstatus |= SF_B1_UP; + } else if (!(mousebtnstatus & PS2M_STATUS_BUTTON_1) && (status & PS2M_STATUS_BUTTON_1)) { + sstatus |= SF_B1_DOWN; + } + + if ((mousebtnstatus & PS2M_STATUS_BUTTON_2) && !(status & PS2M_STATUS_BUTTON_2)) { + sstatus |= SF_B2_UP; + } else if (!(mousebtnstatus & PS2M_STATUS_BUTTON_2) && (status & PS2M_STATUS_BUTTON_2)) { + sstatus |= SF_B2_DOWN; + } + + mousebtnstatus = status & (PS2M_STATUS_BUTTON_1 | PS2M_STATUS_BUTTON_2); + + if (sstatus) { + send_event(sstatus, sx, sy, MOUSE_NUM_BUTTONS, 0, 0); + } +} + +#pragma code_seg () + +/* Driver exported functions. */ + +/** DLL entry point (or driver initialization routine). + * The initialization routine should check whether a mouse exists. + * @return nonzero value indicates a mouse exists. + */ +#pragma off (unreferenced); +BOOL FAR PASCAL LibMain(HINSTANCE hInstance, WORD wDataSegment, + WORD wHeapSize, LPSTR lpszCmdLine) +#pragma pop (unreferenced); +{ + // We are not going to bother checking whether a PS2 mouse exists and just assume it does + +#if ENABLE_VBOX + // However we will check whether VirtualBox exists: + if (vbox_init() == 0) { + vbox_logs("VirtualBox found\n"); + vbox_report_guest_info(VBOXOSTYPE_Win31); + + // VirtualBox connection was succesful, remember that + mouseflags |= MOUSEFLAGS_HAS_VBOX; + } +#endif + + return 1; +} + +/** Called by Windows to retrieve information about the mouse hardware. */ +WORD FAR PASCAL Inquire(LPMOUSEINFO lpMouseInfo) +{ + lpMouseInfo->msExist = 1; + lpMouseInfo->msRelative = mouseflags & MOUSEFLAGS_HAS_VBOX ? 0 : 1; + lpMouseInfo->msNumButtons = MOUSE_NUM_BUTTONS; + lpMouseInfo->msRate = 40; + return sizeof(MOUSEINFO); +} + +/** Called by Windows to enable the mouse driver. + * @param lpEventProc Callback function to call when a mouse event happens. */ +VOID FAR PASCAL Enable(LPFN_MOUSEEVENT lpEventProc) +{ + cli(); // Write to far pointer may not be atomic, and we could be interrupted mid-write + eventproc = lpEventProc; + sti(); + + if (!(mouseflags & MOUSEFLAGS_ENABLED)) { + int err; + if ((err = ps2_init())) { + vbox_logs("PS2 init failure\n"); + return; + } + if ((err = ps2_set_callback(ps2_mouse_callback))) { + vbox_logs("PS2 set handler failure\n"); + return; + } + if ((err = ps2_enable(true))) { + vbox_logs("PS2 enable failure\n"); + return; + } + + vbox_logs("PS/2 Enabled!\n"); + mouseflags |= MOUSEFLAGS_ENABLED; + +#if ENABLE_VBOX + if (mouseflags & MOUSEFLAGS_HAS_VBOX) { + vbox_init_callbacks(); + + if ((err = vbox_set_mouse(true))) { + vbox_logs("VBox enable failure\n"); + vbox_deinit_callbacks(); + return; + } + + vbox_logs("VBOX Enabled!\n"); + mouseflags |= MOUSEFLAGS_VBOX_ENABLED; + } +#endif + } +} + +/** Called by Windows to disable the mouse driver. */ +VOID FAR PASCAL Disable(VOID) +{ + if (mouseflags & MOUSEFLAGS_ENABLED) { + ps2_enable(false); + ps2_set_callback(NULL); + vbox_logs("PS2 Disabled!\n"); + + mouseflags &= ~MOUSEFLAGS_ENABLED; + +#if ENABLE_VBOX + if (mouseflags & MOUSEFLAGS_VBOX_ENABLED) { + vbox_set_mouse(false); + vbox_deinit_callbacks(); + vbox_logs("VBOX Disabled!\n"); + mouseflags &= ~MOUSEFLAGS_VBOX_ENABLED; + } +#endif + } +} + +/** Called by Window to retrieve the interrupt vector number used by this driver, or -1. */ +int FAR PASCAL MouseGetIntVect(VOID) +{ + return -1; +} + diff --git a/mousew16.h b/mousew16.h new file mode 100644 index 0000000..54b262f --- /dev/null +++ b/mousew16.h @@ -0,0 +1,53 @@ +#ifndef MOUSEW16_H +#define MOUSEW16_H + +/* Win16's mouse driver interface. */ + +/** Contains information about the mouse, used by Inquire(). */ +typedef _Packed struct MOUSEINFO +{ + /** Whether a mouse exists. */ + char msExist; + /** Whether the mouse returns absolute or relative coordinates. */ + char msRelative; + /** Number of buttons. */ + short msNumButtons; + /** Maximum number of events per second. */ + short msRate; + // Reserved: + short msXThreshold; + short msYThreshold; + short msXRes; + short msYRes; + // The following are available in Windows >= 3.1 only: + #if 0 + /** Specifies the COM port used, or 0 for none. */ + short msMouseCommPort; + #endif +} MOUSEINFO; +typedef MOUSEINFO __far *LPMOUSEINFO; + +/** Movement occurred. */ +#define SF_MOVEMENT 0x0001 +/** Button 1 changed to down. */ +#define SF_B1_DOWN 0x0002 +/** Button 1 changed to up. */ +#define SF_B1_UP 0x0004 +/** Button 2 changed to down. */ +#define SF_B2_DOWN 0x0008 +/** Button 2 changed to up. */ +#define SF_B2_UP 0x0010 +/** Event coordinates are absolute instead of relative. */ +#define SF_ABSOLUTE 0x8000 + +/** Driver should call this callback when there are new mouse events to report. + * @param Status What happened. Combination of SF_MOVEMENT, SF_ABSOLUTE, etc. + * @param deltaX either number of mickeys moved or absolute coordinate if SB_ABSOLUTE. + * @param deltaY either number of mickeys moved or absolute coordinate if SB_ABSOLUTE. + * @param ButtonCount number of buttons + * @param extra1,extra2 leave as zero + */ +typedef void (__far *LPFN_MOUSEEVENT)(unsigned short Status, short deltaX, short deltaY, short ButtonCount, short extra1, short extra2); +#pragma aux MOUSEEVENTPROC parm [ax] [bx] [cx] [dx] [di] [si] + +#endif diff --git a/oemsetup.inf b/oemsetup.inf new file mode 100644 index 0000000..d78d067 --- /dev/null +++ b/oemsetup.inf @@ -0,0 +1,8 @@ +[data]
+ Version = "3.0"
+
+[disks]
+ 1 =., "VBMouse driver disk", disk1
+
+[pointing.device]
+ vbmouse = 1:vbmouse.drv, "VirtualBox PS/2 Mouse", x:*vmd, vbmouse
@@ -0,0 +1,122 @@ +/* + * VBMouse - PCI BIOS communication routines + * Copyright (C) 2022 Javier S. Pedro + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PCI_H +#define PCI_H + +typedef unsigned char pcierr; +enum { + PCI_SUCCESSFUL = 0, + PCI_GENERIC_ERROR = 1, + PCI_FUNC_NOT_SUPPORTED = 0x81, + PCI_BAD_VENDOR_ID = 0x83, + PCI_DEVICE_NOT_FOUND = 0x86, + PCI_BAD_REGISTER_NUMBER = 0x87, + PCI_SET_FAILED = 0x88, + PCI_BUFFER_TOO_SMALL = 0x89 +}; + +typedef unsigned short pcisel; + +static pcierr pci_init_bios(void); +#pragma aux pci_init_bios = \ + "mov ax, 0xB101" \ + "int 0x1A" \ + "jc not_found" \ + "cmp ah, 0" \ + "jne not_found" \ + "cmp edx, ' ICP'" \ + "jne not_found" \ + "mov ah, 0" \ + "jmp end" \ + "not_found: mov ah, 1" \ + "end:" \ + __value [ah] \ + __modify [ax bx cx dx] + +static pcierr pci_find_device(pcisel *sel, unsigned short vendor_id, unsigned short dev_id, unsigned short index); +#pragma aux pci_find_device = \ + "mov ax, 0xB102" \ + "int 0x1A" \ + "jnc success" \ + "mov ah, 1" \ + "success: mov [es:di], bx" \ + __parm [es di] [dx] [cx] [si] \ + __value [ah] \ + __modify [ax bx] + +/* Reading from configuration space */ + +static pcierr pci_read_config_byte(pcisel sel, unsigned char reg, unsigned char *data); +#pragma aux pci_read_config_byte = \ + "mov ax, 0xB108" \ + "int 0x1A" \ + "mov [es:si], cl" \ + __parm [bx] [di] [es si] \ + __value [ah] \ + __modify [ax cx] + +static pcierr pci_read_config_word(pcisel sel, unsigned char reg, unsigned short *data); +#pragma aux pci_read_config_word = \ + "mov ax, 0xB109" \ + "int 0x1A" \ + "mov [es:si], cx" \ + __parm [bx] [di] [es si] \ + __value [ah] \ + __modify [ax cx] + +static pcierr pci_read_config_dword(pcisel sel, unsigned char reg, unsigned long *data); +#pragma aux pci_read_config_dword = \ + "mov ax, 0xB10A" \ + "int 0x1A" \ + "mov [es:si], ecx" \ + __parm [bx] [di] [es si] \ + __value [ah] \ + __modify [ax cx] + +/* Writing to configuration space */ + +static pcierr pci_write_config_byte(pcisel sel, unsigned char reg, unsigned char data); +#pragma aux pci_write_config_byte = \ + "mov ax, 0xB10B" \ + "int 0x1A" \ + __parm [bx] [di] [cl] \ + __value [ah] \ + __modify [ax] + +static pcierr pci_write_config_word(pcisel sel, unsigned char reg, unsigned short data); +#pragma aux pci_write_config_word = \ + "mov ax, 0xB10C" \ + "int 0x1A" \ + __parm [bx] [di] [cx] \ + __value [ah] \ + __modify [ax cx] + +static pcierr pci_write_config_dword(pcisel sel, unsigned char reg, unsigned long data); +#pragma aux pci_write_config_dword = \ + "mov ax, 0xB10D" \ + "shl esi, 16" \ + "and ecx, 0xFFFF" \ + "or ecx, esi" \ + "int 0x1A" \ + __parm [bx] [di] [si cx] \ + __value [ah] \ + __modify [ax cx] + +#endif @@ -0,0 +1,124 @@ +/* + * VBMouse - Routines to access the PS2 BIOS services + * Copyright (C) 2022 Javier S. Pedro + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PS2_H +#define PS2_H + +#include <stdbool.h> +#include <stdint.h> + +enum { + PS2M_STATUS_BUTTON_1 = 1 << 0, + PS2M_STATUS_BUTTON_2 = 1 << 1, + PS2M_STATUS_BUTTON_3 = 1 << 2, + PS2M_STATUS_X_NEG = 1 << 4, + PS2M_STATUS_Y_NEG = 1 << 5, + PS2M_STATUS_X_OVF = 1 << 6, + PS2M_STATUS_Y_OVF = 1 << 7, +}; + +#pragma aux PS2_CB far loadds parm reverse caller [] +// TODO: ax and es look not be preserved with this. VBox BIOS already preserves them though. + +/** Invoked by the BIOS when there is a mouse event. + * @param status combination of PS2M_STATUS_* flags */ +typedef void (__far * LPFN_PS2CALLBACK)(uint8_t status, uint8_t x, uint8_t y, uint8_t z); + +static inline void cli(void); +#pragma aux cli = "cli" + +static inline void sti(void); +#pragma aux sti = "sti" + +static uint8_t ps2_init(void); +#pragma aux ps2_init = \ + "stc" /* If nothing happens, assume failure */ \ + "mov ax, 0xC205" /* Pointing device: initialization */ \ + "mov bh, 3" /* Use 3 byte packets */ \ + "int 0x15" \ + "jc fail" \ + \ + "mov ax, 0xC201" /* Pointing device: reset */ \ + "int 0x15" \ + "jc fail" \ + \ + "mov ax, 0xC203" /* Pointing device: set resolution */ \ + "mov bh, 3" /* 3 count per mm = ~ 200 ppi */ \ + "int 0x15" \ + "jc fail" \ + \ + "mov ax, 0xC202" /* Pointing device: set sample rate*/ \ + "mov bh, 5" /* 2: 40 samples per second */ \ + "int 0x15" \ + "jc fail" \ + \ + "mov ax, 0xC206" /* Pointing device: extended commands */ \ + "mov bh, 1" /* Set 1:1 scaling */ \ + "int 0x15" \ + "jc fail" \ + \ + "mov ah, 0" /* Success */ \ + "jmp end" \ + \ + "fail: test ah, ah" /* Ensure we have some error code back, set ah to ff if we don't */ \ + "jnz end" \ + "mov ah, 0xFF" \ + "end:" \ + __value [ah] \ + __modify [ax] + +static uint8_t ps2_set_callback(LPFN_PS2CALLBACK callback); +#pragma aux ps2_set_callback = \ + "stc" /* If nothing happens, assume failure */ \ + "mov ax, 0xC207" /* Pointing device: set interrupt callback (in es:bx) */ \ + "int 0x15" \ + "jc fail" \ + \ + "mov ah, 0" /* Success */ \ + "jmp end" \ + \ + "fail: test ah, ah" /* Ensure we have some error code back */ \ + "jnz end" \ + "mov ah, 0xFF" \ + "end:" \ + __parm [es bx] \ + __value [ah] \ + __modify [ax] + +static uint8_t ps2_enable(bool enable); +#pragma aux ps2_enable = \ + "test bh, bh" /* Ensure enable is either 1 or 0 */ \ + "setnz bh" \ + "stc" /* If nothing happens, assume failure */ \ + "mov ax, 0xC200" /* Pointing device enable/disable (in bh) */ \ + "int 0x15" \ + "jc fail" \ + \ + "mov ah, 0" /* Success */ \ + "jmp end" \ + \ + "fail: test ah, ah" /* Ensure we have some error code back */ \ + "jnz end" \ + "mov ah, 0xFF" \ + "end:" \ + __parm [bh] \ + __value [ah] \ + __modify [ax] + +#endif diff --git a/vbmouse.lnk b/vbmouse.lnk new file mode 100644 index 0000000..1f260ee --- /dev/null +++ b/vbmouse.lnk @@ -0,0 +1,9 @@ +OPTION MODNAME=MOUSE # This is necessary; USER.EXE imports mouse functions using this module name +OPTION DESCRIPTION 'VirtualBox Mouse driver' + +SEGMENT CALLBACKS FIXED SHARED # We need a non-moveable segment to store our PS/2 BIOS callbacks + +EXPORT Inquire.1 +EXPORT Enable.2 +EXPORT Disable.3 +EXPORT MouseGetIntVect.4 @@ -0,0 +1,239 @@ +/* + * VBMouse - VirtualBox communication routines + * Copyright (C) 2022 Javier S. Pedro + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <windows.h> +#include <i86.h> + +#include "pci.h" +#include "vboxdev.h" +#include "vbox.h" + +/** VBox's PCI bus, device number, etc. */ +static pcisel vbpci; +/** IO base of VBox's PCI device. */ +static uint16_t vbiobase; +/** IRQ number of VBox's PCI device. Unused. */ +static uint8_t vbirq; + +/** Keep a pre-allocated VBox VMMDevRequest struct for the benefit of the callbacks, + which may be called in a IRQ handler where I'd prefer to avoid making API calls. */ +static VMMDevReqMouseStatus __far * reqMouse; +static HANDLE reqMouseH; +static uint32_t reqMouseAddr; + +/** Actually send a request to the VirtualBox VMM device. + * @param addr 32-bit linear address containing the VMMDevRequest struct. + */ +static void vbox_send_request(uint32_t addr); +#pragma aux vbox_send_request = \ + "movzx eax, ax" /* Need to make a single 32-bit write so combine both regs. */ \ + "movzx ebx, bx" \ + "shl ebx, 16" \ + "or eax, ebx" \ + "mov dx, vbiobase" \ + "out dx, eax" \ + "shr ebx, 16" \ + __parm [bx ax] \ + __modify [dx] + +/** Acknowledge an interrupt from the VirtualBox VMM device at iobase. + * @return bitmask with all pending events (e.g. VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED). */ +static uint32_t vbox_irq_ack(); +#pragma aux vbox_irq_ack = \ + "mov dx, vbiobase" \ + "add dx, 8" \ + "in eax, dx" \ + "mov edx, eax" \ + "shr edx, 16" \ + __value [dx ax] \ + +// Classic PCI defines +#define VBOX_PCI_VEND_ID 0x80ee +#define VBOX_PCI_PROD_ID 0xcafe +enum { + CFG_COMMAND = 0x04, /* Word */ + CFG_INTERRUPT = 0x3C, /* Byte */ + CFG_BAR0 = 0x10, /* DWord */ + CFG_BAR1 = 0x14, /* DWord */ +}; + +/** Gets the 32-bit linear address of a 16:16 far pointer. + Uses GetSelectorBase() since we are supposed to work under protected mode. */ +static inline uint32_t get_physical_addr(void far *obj) +{ + return GetSelectorBase(FP_SEG(obj)) + FP_OFF(obj); +} + +/** Send a request to the VirtualBox VMM device at vbiobase. + * @param request must be a VMMDevRequest (e.g. VMMDevReqMouseStatus). */ +static inline void vbox_request(void far *request) +{ + uint32_t addr = get_physical_addr(request); + vbox_send_request(addr); +} + +/** Finds the VirtualBox PCI device and reads the current IO base. + * @returns 0 if the device was found. */ +int vbox_init(void) +{ + int err; + uint16_t command; + uint32_t bar; + + if ((err = pci_init_bios())) { + return -1; + } + + if ((err = pci_find_device(&vbpci, VBOX_PCI_VEND_ID, VBOX_PCI_PROD_ID, 0))) { + return -1; + } + + if ((err = pci_read_config_word(vbpci, CFG_COMMAND, &command)) + || !(command & 1)) { + return -2; + } + + if ((err = pci_read_config_byte(vbpci, CFG_INTERRUPT, &vbirq))) { + return -2; + } + + if ((err = pci_read_config_dword(vbpci, CFG_BAR0, &bar))) { + return -2; + } + + if (!(bar & 1)) { + return -2; + } + + vbiobase = bar & 0xFFFC; + + return 0; +} + +/** Lets VirtualBox know that there are VirtualBox Guest Additions on this guest. + * @param osType os installed on this guest. */ +int vbox_report_guest_info(uint32_t osType) +{ + VMMDevReportGuestInfo req = {0}; + + req.header.size = sizeof(req); + req.header.version = VMMDEV_REQUEST_HEADER_VERSION; + req.header.requestType = VMMDevReq_ReportGuestInfo; + req.header.rc = -1; + req.guestInfo.interfaceVersion = VMMDEV_VERSION; + req.guestInfo.osType = osType; + + vbox_request(&req); + + return req.header.rc; +} + +/** Tells VirtualBox about the events we are interested in receiving. + These events are notified via the PCI IRQ which we are not using, so this is not used either. */ +int vbox_set_filter_mask(uint32_t add, uint32_t remove) +{ + VMMDevCtlGuestFilterMask req = {0}; + + req.header.size = sizeof(req); + req.header.version = VMMDEV_REQUEST_HEADER_VERSION; + req.header.requestType = VMMDevReq_CtlGuestFilterMask; + req.header.rc = -1; + req.u32OrMask = add; + req.u32NotMask = remove; + + vbox_request(&req); + + return req.header.rc; +} + +/** Tells VirtualBox whether we want absolute mouse information or not. */ +int vbox_set_mouse(bool enable) +{ + VMMDevReqMouseStatus req = {0}; + + req.header.size = sizeof(req); + req.header.version = VMMDEV_REQUEST_HEADER_VERSION; + req.header.requestType = VMMDevReq_SetMouseStatus; + req.header.rc = -1; + req.mouseFeatures = enable ? VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE : 0; + + vbox_request(&req); + + return req.header.rc; +} + +/** Gets the current absolute mouse position from VirtualBox. + * @param abs false if user has disabled mouse integration in VirtualBox, + * in which case we should fallback to PS/2 relative events. */ +int vbox_get_mouse(bool *abs, uint16_t *xpos, uint16_t *ypos) +{ + VMMDevReqMouseStatus req = {0}; + + req.header.size = sizeof(req); + req.header.version = VMMDEV_REQUEST_HEADER_VERSION; + req.header.requestType = VMMDevReq_GetMouseStatus; + req.header.rc = -1; + + vbox_request(&req); + + *abs = req.mouseFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE; + *xpos = req.pointerXPos; + *ypos = req.pointerYPos; + + return req.header.rc; +} + +/** Just allocates the memory used by vbox_get_mouse_locked below. */ +void vbox_init_callbacks(void) +{ + reqMouseH = GlobalAlloc(GMEM_FIXED|GMEM_SHARE, sizeof(VMMDevReqMouseStatus)); + reqMouse = (LPVOID) GlobalLock(reqMouseH); + reqMouseAddr = get_physical_addr(reqMouse); +} + +void vbox_deinit_callbacks(void) +{ + GlobalFree(reqMouseH); + reqMouse = NULL; + reqMouseAddr = 0; +} + +#pragma code_seg ( "CALLBACKS" ) + +/** This is a version of vbox_get_mouse() that does not call any other functions. */ +int vbox_get_mouse_locked(bool *abs, uint16_t *xpos, uint16_t *ypos) +{ + // Note that this may be called with interrupts disabled + reqMouse->header.size = sizeof(VMMDevReqMouseStatus); + reqMouse->header.version = VMMDEV_REQUEST_HEADER_VERSION; + reqMouse->header.requestType = VMMDevReq_GetMouseStatus; + reqMouse->header.rc = -1; + reqMouse->mouseFeatures = 0; + reqMouse->pointerXPos = 0; + reqMouse->pointerYPos = 0; + + vbox_send_request(reqMouseAddr); + + *abs = reqMouse->mouseFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE; + *xpos = reqMouse->pointerXPos; + *ypos = reqMouse->pointerYPos; + + return reqMouse->header.rc; +} + @@ -0,0 +1,73 @@ +/* + * VBMouse - VirtualBox communication routines + * Copyright (C) 2022 Javier S. Pedro + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef VBOX_H +#define VBOX_H + +#include <stdbool.h> +#include <stdint.h> + +/** Logs a single character to the VBox debug message port. */ +static void vbox_logc(char c); +#pragma aux vbox_logc = \ + "mov dx, 0x504" \ + "out dx, al" \ + __parm [al] \ + __modify [dx] + +/** Logs a string to the VBox debug message port. */ +static void vbox_logs(const char __far *str); +#pragma aux vbox_logs = \ + "mov di, si" \ + "xor cx, cx" \ + "not cx" /* First we have to strlen(str), prepare -1 as max length */ \ + "xor al, al" /* The '\0' that we're searching for */ \ + "cld" \ + "repne scas byte ptr es:[di]" /* afterwards, cx = -(len+2) */ \ + "not cx" /* cx = (len+1) */ \ + "dec cx" /* cx = len */ \ + "mov dx, 0x504" /* And now we can write the string */ \ + "rep outs dx, byte ptr es:[si]" \ + __parm [es si] \ + __modify [ax cx dx si di] + +/** Logs a single character followed by '\n' to force log flush. + * A life-saver when there is no data segment available. */ +#define vbox_putc(c) do { \ + vbox_logc(c); vbox_logc('\n'); \ + } while (0); + +extern int vbox_init(void); + +extern int vbox_report_guest_info(uint32_t osType); + +extern int vbox_set_filter_mask(uint32_t add, uint32_t remove); + +extern int vbox_set_mouse(bool enable); + +extern int vbox_get_mouse(bool *abs, uint16_t *xpos, uint16_t *ypos); + +extern void vbox_init_callbacks(); +extern void vbox_deinit_callbacks(); + +// In CALLBACKS segment: + +extern int vbox_get_mouse_locked(bool *abs, uint16_t *xpos, uint16_t *ypos); + +#endif diff --git a/vboxdev.h b/vboxdev.h new file mode 100644 index 0000000..627486f --- /dev/null +++ b/vboxdev.h @@ -0,0 +1,495 @@ +/* + * Definitions in this file come from VirtualBox. + * Copyright (C) 2006-2020 Oracle Corporation + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef VBOXDEV_H +#define VBOXDEV_H + +#include <stdint.h> + +/* Basic defines required for interoperability with VirtualBox's VMM device */ + +#define AssertCompileSize(type, size) /**/ +#define RT_BIT(bit) ( 1U << (bit) ) + +#define VMMDEV_VERSION 0x00010004UL + +/** Version of VMMDevRequestHeader structure. */ +#define VMMDEV_REQUEST_HEADER_VERSION (0x10001) + +/** PC port for debug output */ +#define RTLOG_DEBUG_PORT 0x504 + +/** Port for generic request interface (relative offset). */ +#define VMMDEV_PORT_OFF_REQUEST 0 +/** Port for requests that can be handled w/o going to ring-3 (relative offset). + * This works like VMMDevReq_AcknowledgeEvents when read. */ +#define VMMDEV_PORT_OFF_REQUEST_FAST 8 + +/** Version of VMMDevMemory structure (VMMDevMemory::u32Version). */ +# define VMMDEV_MEMORY_VERSION (1) + +/** @name VMMDev events. + * + * Used mainly by VMMDevReq_AcknowledgeEvents/VMMDevEvents and version 1.3 of + * VMMDevMemory. + * + * @{ + */ +/** Host mouse capabilities has been changed. */ +#define VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED RT_BIT(0) +/** HGCM event. */ +#define VMMDEV_EVENT_HGCM RT_BIT(1) +/** A display change request has been issued. */ +#define VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST RT_BIT(2) +/** Credentials are available for judgement. */ +#define VMMDEV_EVENT_JUDGE_CREDENTIALS RT_BIT(3) +/** The guest has been restored. */ +#define VMMDEV_EVENT_RESTORED RT_BIT(4) +/** Seamless mode state changed. */ +#define VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST RT_BIT(5) +/** Memory balloon size changed. */ +#define VMMDEV_EVENT_BALLOON_CHANGE_REQUEST RT_BIT(6) +/** Statistics interval changed. */ +#define VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST RT_BIT(7) +/** VRDP status changed. */ +#define VMMDEV_EVENT_VRDP RT_BIT(8) +/** New mouse position data available. */ +#define VMMDEV_EVENT_MOUSE_POSITION_CHANGED RT_BIT(9) +/** CPU hotplug event occurred. */ +#define VMMDEV_EVENT_CPU_HOTPLUG RT_BIT(10) +/** The mask of valid events, for sanity checking. */ +#define VMMDEV_EVENT_VALID_EVENT_MASK UINT32_C(0x000007ff) +/** @} */ + + +/** + * The layout of VMMDEV RAM region that contains information for guest. + */ +typedef struct VMMDevMemory +{ + /** The size of this structure. */ + uint32_t u32Size; + /** The structure version. (VMMDEV_MEMORY_VERSION) */ + uint32_t u32Version; + + union + { + struct + { + /** Flag telling that VMMDev set the IRQ and acknowlegment is required */ + uint32_t fHaveEvents; + } V1_04; + + struct + { + /** Pending events flags, set by host. */ + uint32_t u32HostEvents; + /** Mask of events the guest wants to see, set by guest. */ + uint32_t u32GuestEventMask; + } V1_03; + } V; + + //VBVAMEMORY vbvaMemory; + +} VMMDevMemory; + +typedef enum VBOXOSTYPE +{ + VBOXOSTYPE_Unknown = 0, + VBOXOSTYPE_Unknown_x64 = 0x00100, + /** @name DOS and it's descendants + * @{ */ + VBOXOSTYPE_DOS = 0x10000, + VBOXOSTYPE_Win31 = 0x15000, + VBOXOSTYPE_Win9x = 0x20000, + VBOXOSTYPE_Win95 = 0x21000, + VBOXOSTYPE_Win98 = 0x22000, + VBOXOSTYPE_WinMe = 0x23000, + VBOXOSTYPE_WinNT = 0x30000, + VBOXOSTYPE_WinNT_x64 = 0x30100, + VBOXOSTYPE_WinNT3x = 0x30800, + VBOXOSTYPE_WinNT4 = 0x31000, + VBOXOSTYPE_Win2k = 0x32000, + VBOXOSTYPE_WinXP = 0x33000, + VBOXOSTYPE_WinXP_x64 = 0x33100, + VBOXOSTYPE_Win2k3 = 0x34000, + VBOXOSTYPE_Win2k3_x64 = 0x34100, + VBOXOSTYPE_WinVista = 0x35000, + VBOXOSTYPE_WinVista_x64 = 0x35100, + VBOXOSTYPE_Win2k8 = 0x36000, + VBOXOSTYPE_Win2k8_x64 = 0x36100, + VBOXOSTYPE_Win7 = 0x37000, + VBOXOSTYPE_Win7_x64 = 0x37100, + VBOXOSTYPE_Win8 = 0x38000, + VBOXOSTYPE_Win8_x64 = 0x38100, + VBOXOSTYPE_Win2k12_x64 = 0x39100, + VBOXOSTYPE_Win81 = 0x3A000, + VBOXOSTYPE_Win81_x64 = 0x3A100, + VBOXOSTYPE_Win10 = 0x3B000, + VBOXOSTYPE_Win10_x64 = 0x3B100, + VBOXOSTYPE_Win2k16_x64 = 0x3C100, + VBOXOSTYPE_Win2k19_x64 = 0x3D100, + VBOXOSTYPE_Win11_x64 = 0x3E100, + VBOXOSTYPE_OS2 = 0x40000, + VBOXOSTYPE_OS2Warp3 = 0x41000, + VBOXOSTYPE_OS2Warp4 = 0x42000, + VBOXOSTYPE_OS2Warp45 = 0x43000, + VBOXOSTYPE_ECS = 0x44000, + VBOXOSTYPE_ArcaOS = 0x45000, + VBOXOSTYPE_OS21x = 0x48000, + /** @} */ + /** @name Unixy related OSes + * @{ */ + VBOXOSTYPE_Linux = 0x50000, + VBOXOSTYPE_Linux_x64 = 0x50100, + VBOXOSTYPE_Linux22 = 0x51000, + VBOXOSTYPE_Linux24 = 0x52000, + VBOXOSTYPE_Linux24_x64 = 0x52100, + VBOXOSTYPE_Linux26 = 0x53000, + VBOXOSTYPE_Linux26_x64 = 0x53100, + VBOXOSTYPE_ArchLinux = 0x54000, + VBOXOSTYPE_ArchLinux_x64 = 0x54100, + VBOXOSTYPE_Debian = 0x55000, + VBOXOSTYPE_Debian_x64 = 0x55100, + VBOXOSTYPE_OpenSUSE = 0x56000, + VBOXOSTYPE_OpenSUSE_x64 = 0x56100, + VBOXOSTYPE_FedoraCore = 0x57000, + VBOXOSTYPE_FedoraCore_x64 = 0x57100, + VBOXOSTYPE_Gentoo = 0x58000, + VBOXOSTYPE_Gentoo_x64 = 0x58100, + VBOXOSTYPE_Mandriva = 0x59000, + VBOXOSTYPE_Mandriva_x64 = 0x59100, + VBOXOSTYPE_RedHat = 0x5A000, + VBOXOSTYPE_RedHat_x64 = 0x5A100, + VBOXOSTYPE_Turbolinux = 0x5B000, + VBOXOSTYPE_Turbolinux_x64 = 0x5B100, + VBOXOSTYPE_Ubuntu = 0x5C000, + VBOXOSTYPE_Ubuntu_x64 = 0x5C100, + VBOXOSTYPE_Xandros = 0x5D000, + VBOXOSTYPE_Xandros_x64 = 0x5D100, + VBOXOSTYPE_Oracle = 0x5E000, + VBOXOSTYPE_Oracle_x64 = 0x5E100, + VBOXOSTYPE_FreeBSD = 0x60000, + VBOXOSTYPE_FreeBSD_x64 = 0x60100, + VBOXOSTYPE_OpenBSD = 0x61000, + VBOXOSTYPE_OpenBSD_x64 = 0x61100, + VBOXOSTYPE_NetBSD = 0x62000, + VBOXOSTYPE_NetBSD_x64 = 0x62100, + VBOXOSTYPE_Netware = 0x70000, + VBOXOSTYPE_Solaris = 0x80000, + VBOXOSTYPE_Solaris_x64 = 0x80100, + VBOXOSTYPE_OpenSolaris = 0x81000, + VBOXOSTYPE_OpenSolaris_x64 = 0x81100, + VBOXOSTYPE_Solaris11_x64 = 0x82100, + VBOXOSTYPE_L4 = 0x90000, + VBOXOSTYPE_QNX = 0xA0000, + VBOXOSTYPE_MacOS = 0xB0000, + VBOXOSTYPE_MacOS_x64 = 0xB0100, + VBOXOSTYPE_MacOS106 = 0xB2000, + VBOXOSTYPE_MacOS106_x64 = 0xB2100, + VBOXOSTYPE_MacOS107_x64 = 0xB3100, + VBOXOSTYPE_MacOS108_x64 = 0xB4100, + VBOXOSTYPE_MacOS109_x64 = 0xB5100, + VBOXOSTYPE_MacOS1010_x64 = 0xB6100, + VBOXOSTYPE_MacOS1011_x64 = 0xB7100, + VBOXOSTYPE_MacOS1012_x64 = 0xB8100, + VBOXOSTYPE_MacOS1013_x64 = 0xB9100, + /** @} */ + /** @name Other OSes and stuff + * @{ */ + VBOXOSTYPE_JRockitVE = 0xC0000, + VBOXOSTYPE_Haiku = 0xD0000, + VBOXOSTYPE_Haiku_x64 = 0xD0100, + VBOXOSTYPE_VBoxBS_x64 = 0xE0100, + /** @} */ + +/** The bit number which indicates 64-bit or 32-bit. */ +#define VBOXOSTYPE_x64_BIT 8 + /** The mask which indicates 64-bit. */ + VBOXOSTYPE_x64 = 1 << VBOXOSTYPE_x64_BIT, + + /** The usual 32-bit hack. */ + VBOXOSTYPE_32BIT_HACK = 0x7fffffff +} VBOXOSTYPE; + +typedef enum VMMDevRequestType +{ + VMMDevReq_InvalidRequest = 0, + VMMDevReq_GetMouseStatus = 1, + VMMDevReq_SetMouseStatus = 2, + VMMDevReq_SetPointerShape = 3, + VMMDevReq_GetHostVersion = 4, + VMMDevReq_Idle = 5, + VMMDevReq_GetHostTime = 10, + VMMDevReq_GetHypervisorInfo = 20, + VMMDevReq_SetHypervisorInfo = 21, + VMMDevReq_RegisterPatchMemory = 22, /**< @since version 3.0.6 */ + VMMDevReq_DeregisterPatchMemory = 23, /**< @since version 3.0.6 */ + VMMDevReq_SetPowerStatus = 30, + VMMDevReq_AcknowledgeEvents = 41, + VMMDevReq_CtlGuestFilterMask = 42, + VMMDevReq_ReportGuestInfo = 50, + VMMDevReq_ReportGuestInfo2 = 58, /**< @since version 3.2.0 */ + VMMDevReq_ReportGuestStatus = 59, /**< @since version 3.2.8 */ + VMMDevReq_ReportGuestUserState = 74, /**< @since version 4.3 */ + VMMDevReq_SizeHack = 0x7fffffff +} VMMDevRequestType; + +typedef struct VMMDevRequestHeader +{ + /** IN: Size of the structure in bytes (including body). + * (VBGLREQHDR uses this for input size and output if reserved1 is zero). */ + uint32_t size; + /** IN: Version of the structure. */ + uint32_t version; + /** IN: Type of the request. + * @note VBGLREQHDR uses this for optional output size. */ + VMMDevRequestType requestType; + /** OUT: VBox status code. */ + int32_t rc; + /** Reserved field no.1. MBZ. + * @note VBGLREQHDR uses this for optional output size, however never for a + * real VMMDev request, only in the I/O control interface. */ + uint32_t reserved1; + /** IN: Requestor information (VMMDEV_REQUESTOR_XXX) when + * VBOXGSTINFO2_F_REQUESTOR_INFO is set, otherwise ignored by the host. */ + uint32_t fRequestor; +} VMMDevRequestHeader; + +/** + * Mouse status request structure. + * + * Used by VMMDevReq_GetMouseStatus and VMMDevReq_SetMouseStatus. + */ +typedef struct +{ + /** header */ + VMMDevRequestHeader header; + /** Mouse feature mask. See VMMDEV_MOUSE_*. */ + uint32_t mouseFeatures; + /** Mouse x position. */ + int32_t pointerXPos; + /** Mouse y position. */ + int32_t pointerYPos; +} VMMDevReqMouseStatus; +AssertCompileSize(VMMDevReqMouseStatus, 24+12); + +/** @name Mouse capability bits (VMMDevReqMouseStatus::mouseFeatures). + * @{ */ +/** The guest can (== wants to) handle absolute coordinates. */ +#define VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE RT_BIT(0) +/** The host can (== wants to) send absolute coordinates. + * (Input not captured.) */ +#define VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE RT_BIT(1) +/** The guest can *NOT* switch to software cursor and therefore depends on the + * host cursor. + * + * When guest additions are installed and the host has promised to display the + * cursor itself, the guest installs a hardware mouse driver. Don't ask the + * guest to switch to a software cursor then. */ +#define VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR RT_BIT(2) +/** The host does NOT provide support for drawing the cursor itself. */ +#define VMMDEV_MOUSE_HOST_CANNOT_HWPOINTER RT_BIT(3) +/** The guest can read VMMDev events to find out about pointer movement */ +#define VMMDEV_MOUSE_NEW_PROTOCOL RT_BIT(4) +/** If the guest changes the status of the + * VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR bit, the host will honour this */ +#define VMMDEV_MOUSE_HOST_RECHECKS_NEEDS_HOST_CURSOR RT_BIT(5) +/** The host supplies an absolute pointing device. The Guest Additions may + * wish to use this to decide whether to install their own driver */ +#define VMMDEV_MOUSE_HOST_HAS_ABS_DEV RT_BIT(6) +/** The mask of all VMMDEV_MOUSE_* flags */ +#define VMMDEV_MOUSE_MASK UINT32_C(0x0000007f) +/** The mask of guest capability changes for which notification events should + * be sent */ +#define VMMDEV_MOUSE_NOTIFY_HOST_MASK \ + (VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE | VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR) +/** The mask of all capabilities which the guest can legitimately change */ +#define VMMDEV_MOUSE_GUEST_MASK \ + (VMMDEV_MOUSE_NOTIFY_HOST_MASK | VMMDEV_MOUSE_NEW_PROTOCOL) +/** The mask of host capability changes for which notification events should + * be sent */ +#define VMMDEV_MOUSE_NOTIFY_GUEST_MASK \ + VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE +/** The mask of all capabilities which the host can legitimately change */ +#define VMMDEV_MOUSE_HOST_MASK \ + ( VMMDEV_MOUSE_NOTIFY_GUEST_MASK \ + | VMMDEV_MOUSE_HOST_CANNOT_HWPOINTER \ + | VMMDEV_MOUSE_HOST_RECHECKS_NEEDS_HOST_CURSOR \ + | VMMDEV_MOUSE_HOST_HAS_ABS_DEV) +/** @} */ + +/** @name Absolute mouse reporting range + * @{ */ +/** @todo Should these be here? They are needed by both host and guest. */ +/** The minumum value our pointing device can return. */ +#define VMMDEV_MOUSE_RANGE_MIN 0 +/** The maximum value our pointing device can return. */ +#define VMMDEV_MOUSE_RANGE_MAX 0xFFFF +/** The full range our pointing device can return. */ +#define VMMDEV_MOUSE_RANGE (VMMDEV_MOUSE_RANGE_MAX - VMMDEV_MOUSE_RANGE_MIN) +/** @} */ + + +/** + * Mouse pointer shape/visibility change request. + * + * Used by VMMDevReq_SetPointerShape. The size is variable. + */ +typedef struct VMMDevReqMousePointer +{ + /** Header. */ + VMMDevRequestHeader header; + /** VBOX_MOUSE_POINTER_* bit flags from VBox/Graphics/VBoxVideo.h. */ + uint32_t fFlags; + /** x coordinate of hot spot. */ + uint32_t xHot; + /** y coordinate of hot spot. */ + uint32_t yHot; + /** Width of the pointer in pixels. */ + uint32_t width; + /** Height of the pointer in scanlines. */ + uint32_t height; + /** Pointer data. + * + **** + * The data consists of 1 bpp AND mask followed by 32 bpp XOR (color) mask. + * + * For pointers without alpha channel the XOR mask pixels are 32 bit values: (lsb)BGR0(msb). + * For pointers with alpha channel the XOR mask consists of (lsb)BGRA(msb) 32 bit values. + * + * Guest driver must create the AND mask for pointers with alpha channel, so if host does not + * support alpha, the pointer could be displayed as a normal color pointer. The AND mask can + * be constructed from alpha values. For example alpha value >= 0xf0 means bit 0 in the AND mask. + * + * The AND mask is 1 bpp bitmap with byte aligned scanlines. Size of AND mask, + * therefore, is cbAnd = (width + 7) / 8 * height. The padding bits at the + * end of any scanline are undefined. + * + * The XOR mask follows the AND mask on the next 4 bytes aligned offset: + * uint8_t *pXor = pAnd + (cbAnd + 3) & ~3 + * Bytes in the gap between the AND and the XOR mask are undefined. + * XOR mask scanlines have no gap between them and size of XOR mask is: + * cXor = width * 4 * height. + **** + * + * Preallocate 4 bytes for accessing actual data as p->pointerData. + */ + char pointerData[4]; +} VMMDevReqMousePointer; + +/** + * Pending events structure. + * + * Used by VMMDevReq_AcknowledgeEvents. + */ +typedef struct +{ + /** Header. */ + VMMDevRequestHeader header; + /** OUT: Pending event mask. */ + uint32_t events; +} VMMDevEvents; +AssertCompileSize(VMMDevEvents, 24+4); + + +/** + * Guest event filter mask control. + * + * Used by VMMDevReq_CtlGuestFilterMask. + */ +typedef struct +{ + /** Header. */ + VMMDevRequestHeader header; + /** Mask of events to be added to the filter. */ + uint32_t u32OrMask; + /** Mask of events to be removed from the filter. */ + uint32_t u32NotMask; +} VMMDevCtlGuestFilterMask; +AssertCompileSize(VMMDevCtlGuestFilterMask, 24+8); + +/** + * Guest information structure. + * + * Used by VMMDevReportGuestInfo and PDMIVMMDEVCONNECTOR::pfnUpdateGuestVersion. + */ +typedef struct VBoxGuestInfo +{ + /** The VMMDev interface version expected by additions. + * *Deprecated*, do not use anymore! Will be removed. */ + uint32_t interfaceVersion; + /** Guest OS type. */ + VBOXOSTYPE osType; +} VBoxGuestInfo; +AssertCompileSize(VBoxGuestInfo, 8); + +/** + * Guest information report. + * + * Used by VMMDevReq_ReportGuestInfo. + */ +typedef struct +{ + /** Header. */ + VMMDevRequestHeader header; + /** Guest information. */ + VBoxGuestInfo guestInfo; +} VMMDevReportGuestInfo; +AssertCompileSize(VMMDevReportGuestInfo, 24+8); + + +/** + * Guest information structure, version 2. + * + * Used by VMMDevReportGuestInfo2 and PDMIVMMDEVCONNECTOR::pfnUpdateGuestVersion2. + */ +typedef struct VBoxGuestInfo2 +{ + /** Major version. */ + uint16_t additionsMajor; + /** Minor version. */ + uint16_t additionsMinor; + /** Build number. */ + uint32_t additionsBuild; + /** SVN revision. */ + uint32_t additionsRevision; + /** Feature mask, VBOXGSTINFO2_F_XXX. */ + uint32_t additionsFeatures; + /** The intentional meaning of this field was: + * Some additional information, for example 'Beta 1' or something like that. + * + * The way it was implemented was implemented: VBOX_VERSION_STRING. + * + * This means the first three members are duplicated in this field (if the guest + * build config is sane). So, the user must check this and chop it off before + * usage. There is, because of the Main code's blind trust in the field's + * content, no way back. */ + char szName[128]; +} VBoxGuestInfo2; +AssertCompileSize(VBoxGuestInfo2, 144); + +#endif |