351 lines
9.6 KiB
C
351 lines
9.6 KiB
C
/*
|
|
* Copyright 2002-2006 by VMware, Inc.
|
|
*
|
|
* 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
|
|
*
|
|
* Except as contained in this notice, the name of the copyright holder(s)
|
|
* and author(s) shall not be used in advertising or otherwise to promote
|
|
* the sale, use or other dealings in this Software without prior written
|
|
* authorization from the copyright holder(s) and author(s).
|
|
*/
|
|
|
|
/*
|
|
* vmmouse_client.c --
|
|
*
|
|
* VMware Virtual Mouse Client
|
|
*
|
|
* This module provides functions to enable, operate and process
|
|
* packets via the VMMouse module hosted in the VMX.
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "vmmouse_client.h"
|
|
#include "vmmouse_proto.h"
|
|
|
|
/*
|
|
*----------------------------------------------------------------------------
|
|
*
|
|
* VMMouseClientVMCheck --
|
|
*
|
|
* Checks if we're running in a VM by sending the GETVERSION command.
|
|
*
|
|
* Returns:
|
|
* 0 if we're running natively/the version command failed,
|
|
* 1 if we're in a VM.
|
|
*
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
|
|
static Bool
|
|
VMMouseClientVMCheck(void)
|
|
{
|
|
VMMouseProtoCmd vmpc;
|
|
|
|
vmpc.in.vEbx = ~VMMOUSE_PROTO_MAGIC;
|
|
vmpc.in.command = VMMOUSE_PROTO_CMD_GETVERSION;
|
|
VMMouseProto_SendCmd(&vmpc);
|
|
|
|
/*
|
|
* ebx should contain VMMOUSE_PROTO_MAGIC
|
|
* eax should contain version
|
|
*/
|
|
if (vmpc.out.vEbx != VMMOUSE_PROTO_MAGIC || vmpc.out.vEax == 0xffffffff) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* VMMouseClient_Disable --
|
|
*
|
|
* Tries to disable VMMouse communication mode on the host.
|
|
* The caller is responsible for maintaining state (we don't check
|
|
* if we're enabled before attempting to disable the VMMouse).
|
|
*
|
|
* Results:
|
|
* TRUE if we successfully disable the VMMouse communication mode,
|
|
* FALSE if something went wrong.
|
|
*
|
|
* Side effects:
|
|
* Disables the absolute positioning mode.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
VMMouseClient_Disable(void)
|
|
{
|
|
uint32_t status;
|
|
VMMouseProtoCmd vmpc;
|
|
|
|
VMwareLog(("VMMouseClient_Disable: writing disable command to port\n"));
|
|
vmpc.in.vEbx = VMMOUSE_CMD_DISABLE;
|
|
vmpc.in.command = VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND;
|
|
VMMouseProto_SendCmd(&vmpc);
|
|
/*
|
|
* We should get 0xffff in the flags now.
|
|
*/
|
|
vmpc.in.vEbx = 0;
|
|
vmpc.in.command = VMMOUSE_PROTO_CMD_ABSPOINTER_STATUS;
|
|
VMMouseProto_SendCmd(&vmpc);
|
|
status = vmpc.out.vEax;
|
|
if ((status & VMMOUSE_ERROR) != VMMOUSE_ERROR) {
|
|
VMwareLog(("VMMouseClient_Disable: wrong status returned\n"));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* VMMouseClient_Enable --
|
|
*
|
|
* Public Enable entry point. The driver calls this once it feels
|
|
* ready to deal with VMMouse stuff. For now, we just try to enable
|
|
* and return the result, but conceivably we could do more.
|
|
*
|
|
* Results:
|
|
* TRUE if the enable succeeds, FALSE otherwise.
|
|
*
|
|
* Side effects:
|
|
* Causes host-side state change.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
Bool
|
|
VMMouseClient_Enable(void) {
|
|
|
|
uint32_t status;
|
|
uint32_t data;
|
|
VMMouseProtoCmd vmpc;
|
|
|
|
/*
|
|
* First, make sure we're in a VM; i.e. in dualboot configurations we might
|
|
* find ourselves running on real hardware.
|
|
*/
|
|
|
|
if (!VMMouseClientVMCheck()) {
|
|
return FALSE;
|
|
}
|
|
|
|
VMwareLog(("VMMouseClientVMCheck succeeded, checking VMMOUSE version\n"));
|
|
VMwareLog(("VMMouseClient_Enable: READ_ID 0x%08x, VERSION_ID 0x%08x\n",
|
|
VMMOUSE_CMD_READ_ID, VMMOUSE_VERSION_ID));
|
|
|
|
/*
|
|
* We probe for the VMMouse backend by sending the ENABLE
|
|
* command to the mouse. We should get back the VERSION_ID on
|
|
* the data port.
|
|
*/
|
|
vmpc.in.vEbx = VMMOUSE_CMD_READ_ID;
|
|
vmpc.in.command = VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND;
|
|
VMMouseProto_SendCmd(&vmpc);
|
|
|
|
/*
|
|
* Check whether the VMMOUSE_VERSION_ID is available to read
|
|
*/
|
|
vmpc.in.vEbx = 0;
|
|
vmpc.in.command = VMMOUSE_PROTO_CMD_ABSPOINTER_STATUS;
|
|
VMMouseProto_SendCmd(&vmpc);
|
|
status = vmpc.out.vEax;
|
|
if ((status & 0x0000ffff) == 0) {
|
|
VMwareLog(("VMMouseClient_Enable: no data on port."));
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Get the VMMOUSE_VERSION_ID then
|
|
*/
|
|
/* Get just one item */
|
|
vmpc.in.vEbx = 1;
|
|
vmpc.in.command = VMMOUSE_PROTO_CMD_ABSPOINTER_DATA;
|
|
VMMouseProto_SendCmd(&vmpc);
|
|
data = vmpc.out.vEax;
|
|
if (data!= VMMOUSE_VERSION_ID) {
|
|
VMwareLog(("VMMouseClient_Enable: data was not VERSION_ID"));
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Restrict access to the VMMouse backdoor handler.
|
|
*/
|
|
vmpc.in.vEbx = VMMOUSE_RESTRICT_IOPL;
|
|
vmpc.in.command = VMMOUSE_PROTO_CMD_ABSPOINTER_RESTRICT;
|
|
VMMouseProto_SendCmd(&vmpc);
|
|
|
|
/*
|
|
* To quote Jeremy, "Go Go Go!"
|
|
*/
|
|
|
|
VMwareLog(("VMMouseClient_Enable: go go go!\n"));
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* VMMouseClient_GetInput --
|
|
*
|
|
* Retrieves a 4-word input packet from the VMMouse data port and
|
|
* stores it in the specified input structure.
|
|
*
|
|
* Results:
|
|
* The number of packets in the queue, including the retrieved
|
|
* packet.
|
|
*
|
|
* Side effects:
|
|
* Could cause host state change.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
unsigned int
|
|
VMMouseClient_GetInput (PVMMOUSE_INPUT_DATA pvmmouseInput) {
|
|
|
|
uint32_t status;
|
|
uint16_t numWords;
|
|
uint32_t packetInfo;
|
|
VMMouseProtoCmd vmpc;
|
|
|
|
/*
|
|
* The status dword has two parts: the high 16 bits are
|
|
* for flags, the low 16-bits are the number of DWORDs
|
|
* waiting in the data queue. VMMOUSE_ERROR is a special
|
|
* case that indicates there's something wrong on the
|
|
* host end, e.g. the VMMouse was disabled on the host-side.
|
|
*/
|
|
vmpc.in.vEbx = 0;
|
|
vmpc.in.command = VMMOUSE_PROTO_CMD_ABSPOINTER_STATUS;
|
|
VMMouseProto_SendCmd(&vmpc);
|
|
status = vmpc.out.vEax;
|
|
if ((status & VMMOUSE_ERROR) == VMMOUSE_ERROR) {
|
|
VMwareLog(("VMMouseClient_GetInput: VMMOUSE_ERROR status, abort!\n"));
|
|
return VMMOUSE_ERROR;
|
|
}
|
|
|
|
/*
|
|
* We don't use the status flags, just get the words
|
|
*/
|
|
numWords = status & 0x0000ffff;
|
|
|
|
if ((numWords % 4) != 0) {
|
|
VMwareLog(("VMMouseClient_GetInput: invalid status numWords, abort!\n"));
|
|
return (0);
|
|
}
|
|
|
|
if (numWords == 0) {
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* The VMMouse uses a 4-dword packet protocol:
|
|
* DWORD 0: Button State and per-packet flags
|
|
* DWORD 1: X position (absolute or relative)
|
|
* DWORD 2: Y position (absolute or relative)
|
|
* DWORD 3: Z position (relative)
|
|
*/
|
|
/* Get 4 items at once */
|
|
vmpc.in.vEbx = 4;
|
|
vmpc.in.command = VMMOUSE_PROTO_CMD_ABSPOINTER_DATA;
|
|
VMMouseProto_SendCmd(&vmpc);
|
|
packetInfo = vmpc.out.vEax;
|
|
pvmmouseInput->Flags = (packetInfo & 0xffff0000) >> 16;
|
|
pvmmouseInput->Buttons = (packetInfo & 0x0000ffff);
|
|
|
|
/* Note that Z is always signed, and X/Y are signed in relative mode. */
|
|
pvmmouseInput->X = (int)vmpc.out.vEbx;
|
|
pvmmouseInput->Y = (int)vmpc.out.vEcx;
|
|
pvmmouseInput->Z = (int)vmpc.out.vEdx;
|
|
|
|
/*
|
|
* Return number of packets (including this one) in queue.
|
|
*/
|
|
return (numWords >> 2);
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------------
|
|
*
|
|
* VMMouseClient_RequestRelative --
|
|
*
|
|
* Request that the host switch to posting relative packets. It's just
|
|
* advisory, so we make no guarantees about if/when the switch will
|
|
* happen.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Host may start posting relative packets in the near future.
|
|
*
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
VMMouseClient_RequestRelative(void)
|
|
{
|
|
VMMouseProtoCmd vmpc;
|
|
|
|
VMwareLog(("VMMouseClient: requesting relative mode\n"));
|
|
vmpc.in.vEbx = VMMOUSE_CMD_REQUEST_RELATIVE;
|
|
vmpc.in.command = VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND;
|
|
VMMouseProto_SendCmd(&vmpc);
|
|
}
|
|
|
|
|
|
/*
|
|
*----------------------------------------------------------------------------
|
|
*
|
|
* VMMouseClient_RequestAbsolute --
|
|
*
|
|
* Request that the host switch to posting absolute packets. It's just
|
|
* advisory, so we make no guarantees about if/when the switch will
|
|
* happen.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Host may start posting absolute packets in the near future.
|
|
*
|
|
*----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
VMMouseClient_RequestAbsolute(void)
|
|
{
|
|
VMMouseProtoCmd vmpc;
|
|
|
|
VMwareLog(("VMMouseClient: requesting absolute mode\n"));
|
|
vmpc.in.vEbx = VMMOUSE_CMD_REQUEST_ABSOLUTE;
|
|
vmpc.in.command = VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND;
|
|
VMMouseProto_SendCmd(&vmpc);
|
|
}
|