18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for Virtual PS/2 Mouse on VMware and QEMU hypervisors.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2014, VMware, Inc. All Rights Reserved.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Twin device code is hugely inspired by the ALPS driver.
88c2ecf20Sopenharmony_ci * Authors:
98c2ecf20Sopenharmony_ci *   Dmitry Torokhov <dmitry.torokhov@gmail.com>
108c2ecf20Sopenharmony_ci *   Thomas Hellstrom <thellstrom@vmware.com>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/input.h>
148c2ecf20Sopenharmony_ci#include <linux/serio.h>
158c2ecf20Sopenharmony_ci#include <linux/libps2.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <asm/hypervisor.h>
198c2ecf20Sopenharmony_ci#include <asm/vmware.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "psmouse.h"
228c2ecf20Sopenharmony_ci#include "vmmouse.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define VMMOUSE_PROTO_MAGIC			0x564D5868U
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/*
278c2ecf20Sopenharmony_ci * Main commands supported by the vmmouse hypervisor port.
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_ci#define VMMOUSE_PROTO_CMD_GETVERSION		10
308c2ecf20Sopenharmony_ci#define VMMOUSE_PROTO_CMD_ABSPOINTER_DATA	39
318c2ecf20Sopenharmony_ci#define VMMOUSE_PROTO_CMD_ABSPOINTER_STATUS	40
328c2ecf20Sopenharmony_ci#define VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND	41
338c2ecf20Sopenharmony_ci#define VMMOUSE_PROTO_CMD_ABSPOINTER_RESTRICT   86
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * Subcommands for VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_ci#define VMMOUSE_CMD_ENABLE			0x45414552U
398c2ecf20Sopenharmony_ci#define VMMOUSE_CMD_DISABLE			0x000000f5U
408c2ecf20Sopenharmony_ci#define VMMOUSE_CMD_REQUEST_RELATIVE		0x4c455252U
418c2ecf20Sopenharmony_ci#define VMMOUSE_CMD_REQUEST_ABSOLUTE		0x53424152U
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define VMMOUSE_ERROR				0xffff0000U
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define VMMOUSE_VERSION_ID			0x3442554aU
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define VMMOUSE_RELATIVE_PACKET			0x00010000U
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define VMMOUSE_LEFT_BUTTON			0x20
508c2ecf20Sopenharmony_ci#define VMMOUSE_RIGHT_BUTTON			0x10
518c2ecf20Sopenharmony_ci#define VMMOUSE_MIDDLE_BUTTON			0x08
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/*
548c2ecf20Sopenharmony_ci * VMMouse Restrict command
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_ci#define VMMOUSE_RESTRICT_ANY                    0x00
578c2ecf20Sopenharmony_ci#define VMMOUSE_RESTRICT_CPL0                   0x01
588c2ecf20Sopenharmony_ci#define VMMOUSE_RESTRICT_IOPL                   0x02
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define VMMOUSE_MAX_X                           0xFFFF
618c2ecf20Sopenharmony_ci#define VMMOUSE_MAX_Y                           0xFFFF
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#define VMMOUSE_VENDOR "VMware"
648c2ecf20Sopenharmony_ci#define VMMOUSE_NAME   "VMMouse"
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/**
678c2ecf20Sopenharmony_ci * struct vmmouse_data - private data structure for the vmmouse driver
688c2ecf20Sopenharmony_ci *
698c2ecf20Sopenharmony_ci * @abs_dev: "Absolute" device used to report absolute mouse movement.
708c2ecf20Sopenharmony_ci * @phys: Physical path for the absolute device.
718c2ecf20Sopenharmony_ci * @dev_name: Name attribute name for the absolute device.
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_cistruct vmmouse_data {
748c2ecf20Sopenharmony_ci	struct input_dev *abs_dev;
758c2ecf20Sopenharmony_ci	char phys[32];
768c2ecf20Sopenharmony_ci	char dev_name[128];
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/**
808c2ecf20Sopenharmony_ci * Hypervisor-specific bi-directional communication channel
818c2ecf20Sopenharmony_ci * implementing the vmmouse protocol. Should never execute on
828c2ecf20Sopenharmony_ci * bare metal hardware.
838c2ecf20Sopenharmony_ci */
848c2ecf20Sopenharmony_ci#define VMMOUSE_CMD(cmd, in1, out1, out2, out3, out4)	\
858c2ecf20Sopenharmony_ci({							\
868c2ecf20Sopenharmony_ci	unsigned long __dummy1, __dummy2;		\
878c2ecf20Sopenharmony_ci	__asm__ __volatile__ (VMWARE_HYPERCALL :	\
888c2ecf20Sopenharmony_ci		"=a"(out1),				\
898c2ecf20Sopenharmony_ci		"=b"(out2),				\
908c2ecf20Sopenharmony_ci		"=c"(out3),				\
918c2ecf20Sopenharmony_ci		"=d"(out4),				\
928c2ecf20Sopenharmony_ci		"=S"(__dummy1),				\
938c2ecf20Sopenharmony_ci		"=D"(__dummy2) :			\
948c2ecf20Sopenharmony_ci		"a"(VMMOUSE_PROTO_MAGIC),		\
958c2ecf20Sopenharmony_ci		"b"(in1),				\
968c2ecf20Sopenharmony_ci		"c"(VMMOUSE_PROTO_CMD_##cmd),		\
978c2ecf20Sopenharmony_ci		"d"(0) :			        \
988c2ecf20Sopenharmony_ci		"memory");		                \
998c2ecf20Sopenharmony_ci})
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/**
1028c2ecf20Sopenharmony_ci * vmmouse_report_button - report button state on the correct input device
1038c2ecf20Sopenharmony_ci *
1048c2ecf20Sopenharmony_ci * @psmouse:  Pointer to the psmouse struct
1058c2ecf20Sopenharmony_ci * @abs_dev:  The absolute input device
1068c2ecf20Sopenharmony_ci * @rel_dev:  The relative input device
1078c2ecf20Sopenharmony_ci * @pref_dev: The preferred device for reporting
1088c2ecf20Sopenharmony_ci * @code:     Button code
1098c2ecf20Sopenharmony_ci * @value:    Button value
1108c2ecf20Sopenharmony_ci *
1118c2ecf20Sopenharmony_ci * Report @value and @code on @pref_dev, unless the button is already
1128c2ecf20Sopenharmony_ci * pressed on the other device, in which case the state is reported on that
1138c2ecf20Sopenharmony_ci * device.
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_cistatic void vmmouse_report_button(struct psmouse *psmouse,
1168c2ecf20Sopenharmony_ci				  struct input_dev *abs_dev,
1178c2ecf20Sopenharmony_ci				  struct input_dev *rel_dev,
1188c2ecf20Sopenharmony_ci				  struct input_dev *pref_dev,
1198c2ecf20Sopenharmony_ci				  unsigned int code, int value)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	if (test_bit(code, abs_dev->key))
1228c2ecf20Sopenharmony_ci		pref_dev = abs_dev;
1238c2ecf20Sopenharmony_ci	else if (test_bit(code, rel_dev->key))
1248c2ecf20Sopenharmony_ci		pref_dev = rel_dev;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	input_report_key(pref_dev, code, value);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/**
1308c2ecf20Sopenharmony_ci * vmmouse_report_events - process events on the vmmouse communications channel
1318c2ecf20Sopenharmony_ci *
1328c2ecf20Sopenharmony_ci * @psmouse: Pointer to the psmouse struct
1338c2ecf20Sopenharmony_ci *
1348c2ecf20Sopenharmony_ci * This function pulls events from the vmmouse communications channel and
1358c2ecf20Sopenharmony_ci * reports them on the correct (absolute or relative) input device. When the
1368c2ecf20Sopenharmony_ci * communications channel is drained, or if we've processed more than 255
1378c2ecf20Sopenharmony_ci * psmouse commands, the function returns PSMOUSE_FULL_PACKET. If there is a
1388c2ecf20Sopenharmony_ci * host- or synchronization error, the function returns PSMOUSE_BAD_DATA in
1398c2ecf20Sopenharmony_ci * the hope that the caller will reset the communications channel.
1408c2ecf20Sopenharmony_ci */
1418c2ecf20Sopenharmony_cistatic psmouse_ret_t vmmouse_report_events(struct psmouse *psmouse)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	struct input_dev *rel_dev = psmouse->dev;
1448c2ecf20Sopenharmony_ci	struct vmmouse_data *priv = psmouse->private;
1458c2ecf20Sopenharmony_ci	struct input_dev *abs_dev = priv->abs_dev;
1468c2ecf20Sopenharmony_ci	struct input_dev *pref_dev;
1478c2ecf20Sopenharmony_ci	u32 status, x, y, z;
1488c2ecf20Sopenharmony_ci	u32 dummy1, dummy2, dummy3;
1498c2ecf20Sopenharmony_ci	unsigned int queue_length;
1508c2ecf20Sopenharmony_ci	unsigned int count = 255;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	while (count--) {
1538c2ecf20Sopenharmony_ci		/* See if we have motion data. */
1548c2ecf20Sopenharmony_ci		VMMOUSE_CMD(ABSPOINTER_STATUS, 0,
1558c2ecf20Sopenharmony_ci			    status, dummy1, dummy2, dummy3);
1568c2ecf20Sopenharmony_ci		if ((status & VMMOUSE_ERROR) == VMMOUSE_ERROR) {
1578c2ecf20Sopenharmony_ci			psmouse_err(psmouse, "failed to fetch status data\n");
1588c2ecf20Sopenharmony_ci			/*
1598c2ecf20Sopenharmony_ci			 * After a few attempts this will result in
1608c2ecf20Sopenharmony_ci			 * reconnect.
1618c2ecf20Sopenharmony_ci			 */
1628c2ecf20Sopenharmony_ci			return PSMOUSE_BAD_DATA;
1638c2ecf20Sopenharmony_ci		}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		queue_length = status & 0xffff;
1668c2ecf20Sopenharmony_ci		if (queue_length == 0)
1678c2ecf20Sopenharmony_ci			break;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		if (queue_length % 4) {
1708c2ecf20Sopenharmony_ci			psmouse_err(psmouse, "invalid queue length\n");
1718c2ecf20Sopenharmony_ci			return PSMOUSE_BAD_DATA;
1728c2ecf20Sopenharmony_ci		}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci		/* Now get it */
1758c2ecf20Sopenharmony_ci		VMMOUSE_CMD(ABSPOINTER_DATA, 4, status, x, y, z);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		/*
1788c2ecf20Sopenharmony_ci		 * And report what we've got. Prefer to report button
1798c2ecf20Sopenharmony_ci		 * events on the same device where we report motion events.
1808c2ecf20Sopenharmony_ci		 * This doesn't work well with the mouse wheel, though. See
1818c2ecf20Sopenharmony_ci		 * below. Ideally we would want to report that on the
1828c2ecf20Sopenharmony_ci		 * preferred device as well.
1838c2ecf20Sopenharmony_ci		 */
1848c2ecf20Sopenharmony_ci		if (status & VMMOUSE_RELATIVE_PACKET) {
1858c2ecf20Sopenharmony_ci			pref_dev = rel_dev;
1868c2ecf20Sopenharmony_ci			input_report_rel(rel_dev, REL_X, (s32)x);
1878c2ecf20Sopenharmony_ci			input_report_rel(rel_dev, REL_Y, -(s32)y);
1888c2ecf20Sopenharmony_ci		} else {
1898c2ecf20Sopenharmony_ci			pref_dev = abs_dev;
1908c2ecf20Sopenharmony_ci			input_report_abs(abs_dev, ABS_X, x);
1918c2ecf20Sopenharmony_ci			input_report_abs(abs_dev, ABS_Y, y);
1928c2ecf20Sopenharmony_ci		}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci		/* Xorg seems to ignore wheel events on absolute devices */
1958c2ecf20Sopenharmony_ci		input_report_rel(rel_dev, REL_WHEEL, -(s8)((u8) z));
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci		vmmouse_report_button(psmouse, abs_dev, rel_dev,
1988c2ecf20Sopenharmony_ci				      pref_dev, BTN_LEFT,
1998c2ecf20Sopenharmony_ci				      status & VMMOUSE_LEFT_BUTTON);
2008c2ecf20Sopenharmony_ci		vmmouse_report_button(psmouse, abs_dev, rel_dev,
2018c2ecf20Sopenharmony_ci				      pref_dev, BTN_RIGHT,
2028c2ecf20Sopenharmony_ci				      status & VMMOUSE_RIGHT_BUTTON);
2038c2ecf20Sopenharmony_ci		vmmouse_report_button(psmouse, abs_dev, rel_dev,
2048c2ecf20Sopenharmony_ci				      pref_dev, BTN_MIDDLE,
2058c2ecf20Sopenharmony_ci				      status & VMMOUSE_MIDDLE_BUTTON);
2068c2ecf20Sopenharmony_ci		input_sync(abs_dev);
2078c2ecf20Sopenharmony_ci		input_sync(rel_dev);
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	return PSMOUSE_FULL_PACKET;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/**
2148c2ecf20Sopenharmony_ci * vmmouse_process_byte - process data on the ps/2 channel
2158c2ecf20Sopenharmony_ci *
2168c2ecf20Sopenharmony_ci * @psmouse: Pointer to the psmouse struct
2178c2ecf20Sopenharmony_ci *
2188c2ecf20Sopenharmony_ci * When the ps/2 channel indicates that there is vmmouse data available,
2198c2ecf20Sopenharmony_ci * call vmmouse channel processing. Otherwise, continue to accept bytes. If
2208c2ecf20Sopenharmony_ci * there is a synchronization or communication data error, return
2218c2ecf20Sopenharmony_ci * PSMOUSE_BAD_DATA in the hope that the caller will reset the mouse.
2228c2ecf20Sopenharmony_ci */
2238c2ecf20Sopenharmony_cistatic psmouse_ret_t vmmouse_process_byte(struct psmouse *psmouse)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	unsigned char *packet = psmouse->packet;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	switch (psmouse->pktcnt) {
2288c2ecf20Sopenharmony_ci	case 1:
2298c2ecf20Sopenharmony_ci		return (packet[0] & 0x8) == 0x8 ?
2308c2ecf20Sopenharmony_ci			PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	case 2:
2338c2ecf20Sopenharmony_ci		return PSMOUSE_GOOD_DATA;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	default:
2368c2ecf20Sopenharmony_ci		return vmmouse_report_events(psmouse);
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci/**
2418c2ecf20Sopenharmony_ci * vmmouse_disable - Disable vmmouse
2428c2ecf20Sopenharmony_ci *
2438c2ecf20Sopenharmony_ci * @psmouse: Pointer to the psmouse struct
2448c2ecf20Sopenharmony_ci *
2458c2ecf20Sopenharmony_ci * Tries to disable vmmouse mode.
2468c2ecf20Sopenharmony_ci */
2478c2ecf20Sopenharmony_cistatic void vmmouse_disable(struct psmouse *psmouse)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	u32 status;
2508c2ecf20Sopenharmony_ci	u32 dummy1, dummy2, dummy3, dummy4;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_DISABLE,
2538c2ecf20Sopenharmony_ci		    dummy1, dummy2, dummy3, dummy4);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	VMMOUSE_CMD(ABSPOINTER_STATUS, 0,
2568c2ecf20Sopenharmony_ci		    status, dummy1, dummy2, dummy3);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if ((status & VMMOUSE_ERROR) != VMMOUSE_ERROR)
2598c2ecf20Sopenharmony_ci		psmouse_warn(psmouse, "failed to disable vmmouse device\n");
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci/**
2638c2ecf20Sopenharmony_ci * vmmouse_enable - Enable vmmouse and request absolute mode.
2648c2ecf20Sopenharmony_ci *
2658c2ecf20Sopenharmony_ci * @psmouse: Pointer to the psmouse struct
2668c2ecf20Sopenharmony_ci *
2678c2ecf20Sopenharmony_ci * Tries to enable vmmouse mode. Performs basic checks and requests
2688c2ecf20Sopenharmony_ci * absolute vmmouse mode.
2698c2ecf20Sopenharmony_ci * Returns 0 on success, -ENODEV on failure.
2708c2ecf20Sopenharmony_ci */
2718c2ecf20Sopenharmony_cistatic int vmmouse_enable(struct psmouse *psmouse)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	u32 status, version;
2748c2ecf20Sopenharmony_ci	u32 dummy1, dummy2, dummy3, dummy4;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/*
2778c2ecf20Sopenharmony_ci	 * Try enabling the device. If successful, we should be able to
2788c2ecf20Sopenharmony_ci	 * read valid version ID back from it.
2798c2ecf20Sopenharmony_ci	 */
2808c2ecf20Sopenharmony_ci	VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_ENABLE,
2818c2ecf20Sopenharmony_ci		    dummy1, dummy2, dummy3, dummy4);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/*
2848c2ecf20Sopenharmony_ci	 * See if version ID can be retrieved.
2858c2ecf20Sopenharmony_ci	 */
2868c2ecf20Sopenharmony_ci	VMMOUSE_CMD(ABSPOINTER_STATUS, 0, status, dummy1, dummy2, dummy3);
2878c2ecf20Sopenharmony_ci	if ((status & 0x0000ffff) == 0) {
2888c2ecf20Sopenharmony_ci		psmouse_dbg(psmouse, "empty flags - assuming no device\n");
2898c2ecf20Sopenharmony_ci		return -ENXIO;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	VMMOUSE_CMD(ABSPOINTER_DATA, 1 /* single item */,
2938c2ecf20Sopenharmony_ci		    version, dummy1, dummy2, dummy3);
2948c2ecf20Sopenharmony_ci	if (version != VMMOUSE_VERSION_ID) {
2958c2ecf20Sopenharmony_ci		psmouse_dbg(psmouse, "Unexpected version value: %u vs %u\n",
2968c2ecf20Sopenharmony_ci			    (unsigned) version, VMMOUSE_VERSION_ID);
2978c2ecf20Sopenharmony_ci		vmmouse_disable(psmouse);
2988c2ecf20Sopenharmony_ci		return -ENXIO;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/*
3028c2ecf20Sopenharmony_ci	 * Restrict ioport access, if possible.
3038c2ecf20Sopenharmony_ci	 */
3048c2ecf20Sopenharmony_ci	VMMOUSE_CMD(ABSPOINTER_RESTRICT, VMMOUSE_RESTRICT_CPL0,
3058c2ecf20Sopenharmony_ci		    dummy1, dummy2, dummy3, dummy4);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_REQUEST_ABSOLUTE,
3088c2ecf20Sopenharmony_ci		    dummy1, dummy2, dummy3, dummy4);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	return 0;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci/*
3148c2ecf20Sopenharmony_ci * Array of supported hypervisors.
3158c2ecf20Sopenharmony_ci */
3168c2ecf20Sopenharmony_cistatic enum x86_hypervisor_type vmmouse_supported_hypervisors[] = {
3178c2ecf20Sopenharmony_ci	X86_HYPER_VMWARE,
3188c2ecf20Sopenharmony_ci	X86_HYPER_KVM,
3198c2ecf20Sopenharmony_ci};
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci/**
3228c2ecf20Sopenharmony_ci * vmmouse_check_hypervisor - Check if we're running on a supported hypervisor
3238c2ecf20Sopenharmony_ci */
3248c2ecf20Sopenharmony_cistatic bool vmmouse_check_hypervisor(void)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	int i;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vmmouse_supported_hypervisors); i++)
3298c2ecf20Sopenharmony_ci		if (vmmouse_supported_hypervisors[i] == x86_hyper_type)
3308c2ecf20Sopenharmony_ci			return true;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return false;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci/**
3368c2ecf20Sopenharmony_ci * vmmouse_detect - Probe whether vmmouse is available
3378c2ecf20Sopenharmony_ci *
3388c2ecf20Sopenharmony_ci * @psmouse: Pointer to the psmouse struct
3398c2ecf20Sopenharmony_ci * @set_properties: Whether to set psmouse name and vendor
3408c2ecf20Sopenharmony_ci *
3418c2ecf20Sopenharmony_ci * Returns 0 if vmmouse channel is available. Negative error code if not.
3428c2ecf20Sopenharmony_ci */
3438c2ecf20Sopenharmony_ciint vmmouse_detect(struct psmouse *psmouse, bool set_properties)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	u32 response, version, dummy1, dummy2;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (!vmmouse_check_hypervisor()) {
3488c2ecf20Sopenharmony_ci		psmouse_dbg(psmouse,
3498c2ecf20Sopenharmony_ci			    "VMMouse not running on supported hypervisor.\n");
3508c2ecf20Sopenharmony_ci		return -ENXIO;
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/* Check if the device is present */
3548c2ecf20Sopenharmony_ci	response = ~VMMOUSE_PROTO_MAGIC;
3558c2ecf20Sopenharmony_ci	VMMOUSE_CMD(GETVERSION, 0, version, response, dummy1, dummy2);
3568c2ecf20Sopenharmony_ci	if (response != VMMOUSE_PROTO_MAGIC || version == 0xffffffffU)
3578c2ecf20Sopenharmony_ci		return -ENXIO;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (set_properties) {
3608c2ecf20Sopenharmony_ci		psmouse->vendor = VMMOUSE_VENDOR;
3618c2ecf20Sopenharmony_ci		psmouse->name = VMMOUSE_NAME;
3628c2ecf20Sopenharmony_ci		psmouse->model = version;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	return 0;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci/**
3698c2ecf20Sopenharmony_ci * vmmouse_disconnect - Take down vmmouse driver
3708c2ecf20Sopenharmony_ci *
3718c2ecf20Sopenharmony_ci * @psmouse: Pointer to the psmouse struct
3728c2ecf20Sopenharmony_ci *
3738c2ecf20Sopenharmony_ci * Takes down vmmouse driver and frees resources set up in vmmouse_init().
3748c2ecf20Sopenharmony_ci */
3758c2ecf20Sopenharmony_cistatic void vmmouse_disconnect(struct psmouse *psmouse)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	struct vmmouse_data *priv = psmouse->private;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	vmmouse_disable(psmouse);
3808c2ecf20Sopenharmony_ci	psmouse_reset(psmouse);
3818c2ecf20Sopenharmony_ci	input_unregister_device(priv->abs_dev);
3828c2ecf20Sopenharmony_ci	kfree(priv);
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci/**
3868c2ecf20Sopenharmony_ci * vmmouse_reconnect - Reset the ps/2 - and vmmouse connections
3878c2ecf20Sopenharmony_ci *
3888c2ecf20Sopenharmony_ci * @psmouse: Pointer to the psmouse struct
3898c2ecf20Sopenharmony_ci *
3908c2ecf20Sopenharmony_ci * Attempts to reset the mouse connections. Returns 0 on success and
3918c2ecf20Sopenharmony_ci * -1 on failure.
3928c2ecf20Sopenharmony_ci */
3938c2ecf20Sopenharmony_cistatic int vmmouse_reconnect(struct psmouse *psmouse)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	int error;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	psmouse_reset(psmouse);
3988c2ecf20Sopenharmony_ci	vmmouse_disable(psmouse);
3998c2ecf20Sopenharmony_ci	error = vmmouse_enable(psmouse);
4008c2ecf20Sopenharmony_ci	if (error) {
4018c2ecf20Sopenharmony_ci		psmouse_err(psmouse,
4028c2ecf20Sopenharmony_ci			    "Unable to re-enable mouse when reconnecting, err: %d\n",
4038c2ecf20Sopenharmony_ci			    error);
4048c2ecf20Sopenharmony_ci		return error;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	return 0;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci/**
4118c2ecf20Sopenharmony_ci * vmmouse_init - Initialize the vmmouse driver
4128c2ecf20Sopenharmony_ci *
4138c2ecf20Sopenharmony_ci * @psmouse: Pointer to the psmouse struct
4148c2ecf20Sopenharmony_ci *
4158c2ecf20Sopenharmony_ci * Requests the device and tries to enable vmmouse mode.
4168c2ecf20Sopenharmony_ci * If successful, sets up the input device for relative movement events.
4178c2ecf20Sopenharmony_ci * It also allocates another input device and sets it up for absolute motion
4188c2ecf20Sopenharmony_ci * events. Returns 0 on success and -1 on failure.
4198c2ecf20Sopenharmony_ci */
4208c2ecf20Sopenharmony_ciint vmmouse_init(struct psmouse *psmouse)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	struct vmmouse_data *priv;
4238c2ecf20Sopenharmony_ci	struct input_dev *rel_dev = psmouse->dev, *abs_dev;
4248c2ecf20Sopenharmony_ci	int error;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	psmouse_reset(psmouse);
4278c2ecf20Sopenharmony_ci	error = vmmouse_enable(psmouse);
4288c2ecf20Sopenharmony_ci	if (error)
4298c2ecf20Sopenharmony_ci		return error;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
4328c2ecf20Sopenharmony_ci	abs_dev = input_allocate_device();
4338c2ecf20Sopenharmony_ci	if (!priv || !abs_dev) {
4348c2ecf20Sopenharmony_ci		error = -ENOMEM;
4358c2ecf20Sopenharmony_ci		goto init_fail;
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	priv->abs_dev = abs_dev;
4398c2ecf20Sopenharmony_ci	psmouse->private = priv;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/* Set up and register absolute device */
4428c2ecf20Sopenharmony_ci	snprintf(priv->phys, sizeof(priv->phys), "%s/input1",
4438c2ecf20Sopenharmony_ci		 psmouse->ps2dev.serio->phys);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	/* Mimic name setup for relative device in psmouse-base.c */
4468c2ecf20Sopenharmony_ci	snprintf(priv->dev_name, sizeof(priv->dev_name), "%s %s %s",
4478c2ecf20Sopenharmony_ci		 VMMOUSE_PSNAME, VMMOUSE_VENDOR, VMMOUSE_NAME);
4488c2ecf20Sopenharmony_ci	abs_dev->phys = priv->phys;
4498c2ecf20Sopenharmony_ci	abs_dev->name = priv->dev_name;
4508c2ecf20Sopenharmony_ci	abs_dev->id.bustype = BUS_I8042;
4518c2ecf20Sopenharmony_ci	abs_dev->id.vendor = 0x0002;
4528c2ecf20Sopenharmony_ci	abs_dev->id.product = PSMOUSE_VMMOUSE;
4538c2ecf20Sopenharmony_ci	abs_dev->id.version = psmouse->model;
4548c2ecf20Sopenharmony_ci	abs_dev->dev.parent = &psmouse->ps2dev.serio->dev;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/* Set absolute device capabilities */
4578c2ecf20Sopenharmony_ci	input_set_capability(abs_dev, EV_KEY, BTN_LEFT);
4588c2ecf20Sopenharmony_ci	input_set_capability(abs_dev, EV_KEY, BTN_RIGHT);
4598c2ecf20Sopenharmony_ci	input_set_capability(abs_dev, EV_KEY, BTN_MIDDLE);
4608c2ecf20Sopenharmony_ci	input_set_capability(abs_dev, EV_ABS, ABS_X);
4618c2ecf20Sopenharmony_ci	input_set_capability(abs_dev, EV_ABS, ABS_Y);
4628c2ecf20Sopenharmony_ci	input_set_abs_params(abs_dev, ABS_X, 0, VMMOUSE_MAX_X, 0, 0);
4638c2ecf20Sopenharmony_ci	input_set_abs_params(abs_dev, ABS_Y, 0, VMMOUSE_MAX_Y, 0, 0);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	error = input_register_device(priv->abs_dev);
4668c2ecf20Sopenharmony_ci	if (error)
4678c2ecf20Sopenharmony_ci		goto init_fail;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	/* Add wheel capability to the relative device */
4708c2ecf20Sopenharmony_ci	input_set_capability(rel_dev, EV_REL, REL_WHEEL);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	psmouse->protocol_handler = vmmouse_process_byte;
4738c2ecf20Sopenharmony_ci	psmouse->disconnect = vmmouse_disconnect;
4748c2ecf20Sopenharmony_ci	psmouse->reconnect = vmmouse_reconnect;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	return 0;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ciinit_fail:
4798c2ecf20Sopenharmony_ci	vmmouse_disable(psmouse);
4808c2ecf20Sopenharmony_ci	psmouse_reset(psmouse);
4818c2ecf20Sopenharmony_ci	input_free_device(abs_dev);
4828c2ecf20Sopenharmony_ci	kfree(priv);
4838c2ecf20Sopenharmony_ci	psmouse->private = NULL;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	return error;
4868c2ecf20Sopenharmony_ci}
487