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