xref: /OK3568_Linux_fs/kernel/drivers/input/mouse/vmmouse.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Driver for Virtual PS/2 Mouse on VMware and QEMU hypervisors.
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2014, VMware, Inc. All Rights Reserved.
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Twin device code is hugely inspired by the ALPS driver.
8*4882a593Smuzhiyun  * Authors:
9*4882a593Smuzhiyun  *   Dmitry Torokhov <dmitry.torokhov@gmail.com>
10*4882a593Smuzhiyun  *   Thomas Hellstrom <thellstrom@vmware.com>
11*4882a593Smuzhiyun  */
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include <linux/input.h>
14*4882a593Smuzhiyun #include <linux/serio.h>
15*4882a593Smuzhiyun #include <linux/libps2.h>
16*4882a593Smuzhiyun #include <linux/slab.h>
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun #include <asm/hypervisor.h>
19*4882a593Smuzhiyun #include <asm/vmware.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #include "psmouse.h"
22*4882a593Smuzhiyun #include "vmmouse.h"
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #define VMMOUSE_PROTO_MAGIC			0x564D5868U
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun /*
27*4882a593Smuzhiyun  * Main commands supported by the vmmouse hypervisor port.
28*4882a593Smuzhiyun  */
29*4882a593Smuzhiyun #define VMMOUSE_PROTO_CMD_GETVERSION		10
30*4882a593Smuzhiyun #define VMMOUSE_PROTO_CMD_ABSPOINTER_DATA	39
31*4882a593Smuzhiyun #define VMMOUSE_PROTO_CMD_ABSPOINTER_STATUS	40
32*4882a593Smuzhiyun #define VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND	41
33*4882a593Smuzhiyun #define VMMOUSE_PROTO_CMD_ABSPOINTER_RESTRICT   86
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun /*
36*4882a593Smuzhiyun  * Subcommands for VMMOUSE_PROTO_CMD_ABSPOINTER_COMMAND
37*4882a593Smuzhiyun  */
38*4882a593Smuzhiyun #define VMMOUSE_CMD_ENABLE			0x45414552U
39*4882a593Smuzhiyun #define VMMOUSE_CMD_DISABLE			0x000000f5U
40*4882a593Smuzhiyun #define VMMOUSE_CMD_REQUEST_RELATIVE		0x4c455252U
41*4882a593Smuzhiyun #define VMMOUSE_CMD_REQUEST_ABSOLUTE		0x53424152U
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun #define VMMOUSE_ERROR				0xffff0000U
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun #define VMMOUSE_VERSION_ID			0x3442554aU
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun #define VMMOUSE_RELATIVE_PACKET			0x00010000U
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun #define VMMOUSE_LEFT_BUTTON			0x20
50*4882a593Smuzhiyun #define VMMOUSE_RIGHT_BUTTON			0x10
51*4882a593Smuzhiyun #define VMMOUSE_MIDDLE_BUTTON			0x08
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun /*
54*4882a593Smuzhiyun  * VMMouse Restrict command
55*4882a593Smuzhiyun  */
56*4882a593Smuzhiyun #define VMMOUSE_RESTRICT_ANY                    0x00
57*4882a593Smuzhiyun #define VMMOUSE_RESTRICT_CPL0                   0x01
58*4882a593Smuzhiyun #define VMMOUSE_RESTRICT_IOPL                   0x02
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun #define VMMOUSE_MAX_X                           0xFFFF
61*4882a593Smuzhiyun #define VMMOUSE_MAX_Y                           0xFFFF
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun #define VMMOUSE_VENDOR "VMware"
64*4882a593Smuzhiyun #define VMMOUSE_NAME   "VMMouse"
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun /**
67*4882a593Smuzhiyun  * struct vmmouse_data - private data structure for the vmmouse driver
68*4882a593Smuzhiyun  *
69*4882a593Smuzhiyun  * @abs_dev: "Absolute" device used to report absolute mouse movement.
70*4882a593Smuzhiyun  * @phys: Physical path for the absolute device.
71*4882a593Smuzhiyun  * @dev_name: Name attribute name for the absolute device.
72*4882a593Smuzhiyun  */
73*4882a593Smuzhiyun struct vmmouse_data {
74*4882a593Smuzhiyun 	struct input_dev *abs_dev;
75*4882a593Smuzhiyun 	char phys[32];
76*4882a593Smuzhiyun 	char dev_name[128];
77*4882a593Smuzhiyun };
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun /**
80*4882a593Smuzhiyun  * Hypervisor-specific bi-directional communication channel
81*4882a593Smuzhiyun  * implementing the vmmouse protocol. Should never execute on
82*4882a593Smuzhiyun  * bare metal hardware.
83*4882a593Smuzhiyun  */
84*4882a593Smuzhiyun #define VMMOUSE_CMD(cmd, in1, out1, out2, out3, out4)	\
85*4882a593Smuzhiyun ({							\
86*4882a593Smuzhiyun 	unsigned long __dummy1, __dummy2;		\
87*4882a593Smuzhiyun 	__asm__ __volatile__ (VMWARE_HYPERCALL :	\
88*4882a593Smuzhiyun 		"=a"(out1),				\
89*4882a593Smuzhiyun 		"=b"(out2),				\
90*4882a593Smuzhiyun 		"=c"(out3),				\
91*4882a593Smuzhiyun 		"=d"(out4),				\
92*4882a593Smuzhiyun 		"=S"(__dummy1),				\
93*4882a593Smuzhiyun 		"=D"(__dummy2) :			\
94*4882a593Smuzhiyun 		"a"(VMMOUSE_PROTO_MAGIC),		\
95*4882a593Smuzhiyun 		"b"(in1),				\
96*4882a593Smuzhiyun 		"c"(VMMOUSE_PROTO_CMD_##cmd),		\
97*4882a593Smuzhiyun 		"d"(0) :			        \
98*4882a593Smuzhiyun 		"memory");		                \
99*4882a593Smuzhiyun })
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun /**
102*4882a593Smuzhiyun  * vmmouse_report_button - report button state on the correct input device
103*4882a593Smuzhiyun  *
104*4882a593Smuzhiyun  * @psmouse:  Pointer to the psmouse struct
105*4882a593Smuzhiyun  * @abs_dev:  The absolute input device
106*4882a593Smuzhiyun  * @rel_dev:  The relative input device
107*4882a593Smuzhiyun  * @pref_dev: The preferred device for reporting
108*4882a593Smuzhiyun  * @code:     Button code
109*4882a593Smuzhiyun  * @value:    Button value
110*4882a593Smuzhiyun  *
111*4882a593Smuzhiyun  * Report @value and @code on @pref_dev, unless the button is already
112*4882a593Smuzhiyun  * pressed on the other device, in which case the state is reported on that
113*4882a593Smuzhiyun  * device.
114*4882a593Smuzhiyun  */
vmmouse_report_button(struct psmouse * psmouse,struct input_dev * abs_dev,struct input_dev * rel_dev,struct input_dev * pref_dev,unsigned int code,int value)115*4882a593Smuzhiyun static void vmmouse_report_button(struct psmouse *psmouse,
116*4882a593Smuzhiyun 				  struct input_dev *abs_dev,
117*4882a593Smuzhiyun 				  struct input_dev *rel_dev,
118*4882a593Smuzhiyun 				  struct input_dev *pref_dev,
119*4882a593Smuzhiyun 				  unsigned int code, int value)
120*4882a593Smuzhiyun {
121*4882a593Smuzhiyun 	if (test_bit(code, abs_dev->key))
122*4882a593Smuzhiyun 		pref_dev = abs_dev;
123*4882a593Smuzhiyun 	else if (test_bit(code, rel_dev->key))
124*4882a593Smuzhiyun 		pref_dev = rel_dev;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	input_report_key(pref_dev, code, value);
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun /**
130*4882a593Smuzhiyun  * vmmouse_report_events - process events on the vmmouse communications channel
131*4882a593Smuzhiyun  *
132*4882a593Smuzhiyun  * @psmouse: Pointer to the psmouse struct
133*4882a593Smuzhiyun  *
134*4882a593Smuzhiyun  * This function pulls events from the vmmouse communications channel and
135*4882a593Smuzhiyun  * reports them on the correct (absolute or relative) input device. When the
136*4882a593Smuzhiyun  * communications channel is drained, or if we've processed more than 255
137*4882a593Smuzhiyun  * psmouse commands, the function returns PSMOUSE_FULL_PACKET. If there is a
138*4882a593Smuzhiyun  * host- or synchronization error, the function returns PSMOUSE_BAD_DATA in
139*4882a593Smuzhiyun  * the hope that the caller will reset the communications channel.
140*4882a593Smuzhiyun  */
vmmouse_report_events(struct psmouse * psmouse)141*4882a593Smuzhiyun static psmouse_ret_t vmmouse_report_events(struct psmouse *psmouse)
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun 	struct input_dev *rel_dev = psmouse->dev;
144*4882a593Smuzhiyun 	struct vmmouse_data *priv = psmouse->private;
145*4882a593Smuzhiyun 	struct input_dev *abs_dev = priv->abs_dev;
146*4882a593Smuzhiyun 	struct input_dev *pref_dev;
147*4882a593Smuzhiyun 	u32 status, x, y, z;
148*4882a593Smuzhiyun 	u32 dummy1, dummy2, dummy3;
149*4882a593Smuzhiyun 	unsigned int queue_length;
150*4882a593Smuzhiyun 	unsigned int count = 255;
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	while (count--) {
153*4882a593Smuzhiyun 		/* See if we have motion data. */
154*4882a593Smuzhiyun 		VMMOUSE_CMD(ABSPOINTER_STATUS, 0,
155*4882a593Smuzhiyun 			    status, dummy1, dummy2, dummy3);
156*4882a593Smuzhiyun 		if ((status & VMMOUSE_ERROR) == VMMOUSE_ERROR) {
157*4882a593Smuzhiyun 			psmouse_err(psmouse, "failed to fetch status data\n");
158*4882a593Smuzhiyun 			/*
159*4882a593Smuzhiyun 			 * After a few attempts this will result in
160*4882a593Smuzhiyun 			 * reconnect.
161*4882a593Smuzhiyun 			 */
162*4882a593Smuzhiyun 			return PSMOUSE_BAD_DATA;
163*4882a593Smuzhiyun 		}
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 		queue_length = status & 0xffff;
166*4882a593Smuzhiyun 		if (queue_length == 0)
167*4882a593Smuzhiyun 			break;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 		if (queue_length % 4) {
170*4882a593Smuzhiyun 			psmouse_err(psmouse, "invalid queue length\n");
171*4882a593Smuzhiyun 			return PSMOUSE_BAD_DATA;
172*4882a593Smuzhiyun 		}
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 		/* Now get it */
175*4882a593Smuzhiyun 		VMMOUSE_CMD(ABSPOINTER_DATA, 4, status, x, y, z);
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 		/*
178*4882a593Smuzhiyun 		 * And report what we've got. Prefer to report button
179*4882a593Smuzhiyun 		 * events on the same device where we report motion events.
180*4882a593Smuzhiyun 		 * This doesn't work well with the mouse wheel, though. See
181*4882a593Smuzhiyun 		 * below. Ideally we would want to report that on the
182*4882a593Smuzhiyun 		 * preferred device as well.
183*4882a593Smuzhiyun 		 */
184*4882a593Smuzhiyun 		if (status & VMMOUSE_RELATIVE_PACKET) {
185*4882a593Smuzhiyun 			pref_dev = rel_dev;
186*4882a593Smuzhiyun 			input_report_rel(rel_dev, REL_X, (s32)x);
187*4882a593Smuzhiyun 			input_report_rel(rel_dev, REL_Y, -(s32)y);
188*4882a593Smuzhiyun 		} else {
189*4882a593Smuzhiyun 			pref_dev = abs_dev;
190*4882a593Smuzhiyun 			input_report_abs(abs_dev, ABS_X, x);
191*4882a593Smuzhiyun 			input_report_abs(abs_dev, ABS_Y, y);
192*4882a593Smuzhiyun 		}
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 		/* Xorg seems to ignore wheel events on absolute devices */
195*4882a593Smuzhiyun 		input_report_rel(rel_dev, REL_WHEEL, -(s8)((u8) z));
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 		vmmouse_report_button(psmouse, abs_dev, rel_dev,
198*4882a593Smuzhiyun 				      pref_dev, BTN_LEFT,
199*4882a593Smuzhiyun 				      status & VMMOUSE_LEFT_BUTTON);
200*4882a593Smuzhiyun 		vmmouse_report_button(psmouse, abs_dev, rel_dev,
201*4882a593Smuzhiyun 				      pref_dev, BTN_RIGHT,
202*4882a593Smuzhiyun 				      status & VMMOUSE_RIGHT_BUTTON);
203*4882a593Smuzhiyun 		vmmouse_report_button(psmouse, abs_dev, rel_dev,
204*4882a593Smuzhiyun 				      pref_dev, BTN_MIDDLE,
205*4882a593Smuzhiyun 				      status & VMMOUSE_MIDDLE_BUTTON);
206*4882a593Smuzhiyun 		input_sync(abs_dev);
207*4882a593Smuzhiyun 		input_sync(rel_dev);
208*4882a593Smuzhiyun 	}
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	return PSMOUSE_FULL_PACKET;
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun /**
214*4882a593Smuzhiyun  * vmmouse_process_byte - process data on the ps/2 channel
215*4882a593Smuzhiyun  *
216*4882a593Smuzhiyun  * @psmouse: Pointer to the psmouse struct
217*4882a593Smuzhiyun  *
218*4882a593Smuzhiyun  * When the ps/2 channel indicates that there is vmmouse data available,
219*4882a593Smuzhiyun  * call vmmouse channel processing. Otherwise, continue to accept bytes. If
220*4882a593Smuzhiyun  * there is a synchronization or communication data error, return
221*4882a593Smuzhiyun  * PSMOUSE_BAD_DATA in the hope that the caller will reset the mouse.
222*4882a593Smuzhiyun  */
vmmouse_process_byte(struct psmouse * psmouse)223*4882a593Smuzhiyun static psmouse_ret_t vmmouse_process_byte(struct psmouse *psmouse)
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun 	unsigned char *packet = psmouse->packet;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	switch (psmouse->pktcnt) {
228*4882a593Smuzhiyun 	case 1:
229*4882a593Smuzhiyun 		return (packet[0] & 0x8) == 0x8 ?
230*4882a593Smuzhiyun 			PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	case 2:
233*4882a593Smuzhiyun 		return PSMOUSE_GOOD_DATA;
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	default:
236*4882a593Smuzhiyun 		return vmmouse_report_events(psmouse);
237*4882a593Smuzhiyun 	}
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun /**
241*4882a593Smuzhiyun  * vmmouse_disable - Disable vmmouse
242*4882a593Smuzhiyun  *
243*4882a593Smuzhiyun  * @psmouse: Pointer to the psmouse struct
244*4882a593Smuzhiyun  *
245*4882a593Smuzhiyun  * Tries to disable vmmouse mode.
246*4882a593Smuzhiyun  */
vmmouse_disable(struct psmouse * psmouse)247*4882a593Smuzhiyun static void vmmouse_disable(struct psmouse *psmouse)
248*4882a593Smuzhiyun {
249*4882a593Smuzhiyun 	u32 status;
250*4882a593Smuzhiyun 	u32 dummy1, dummy2, dummy3, dummy4;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_DISABLE,
253*4882a593Smuzhiyun 		    dummy1, dummy2, dummy3, dummy4);
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	VMMOUSE_CMD(ABSPOINTER_STATUS, 0,
256*4882a593Smuzhiyun 		    status, dummy1, dummy2, dummy3);
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	if ((status & VMMOUSE_ERROR) != VMMOUSE_ERROR)
259*4882a593Smuzhiyun 		psmouse_warn(psmouse, "failed to disable vmmouse device\n");
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun /**
263*4882a593Smuzhiyun  * vmmouse_enable - Enable vmmouse and request absolute mode.
264*4882a593Smuzhiyun  *
265*4882a593Smuzhiyun  * @psmouse: Pointer to the psmouse struct
266*4882a593Smuzhiyun  *
267*4882a593Smuzhiyun  * Tries to enable vmmouse mode. Performs basic checks and requests
268*4882a593Smuzhiyun  * absolute vmmouse mode.
269*4882a593Smuzhiyun  * Returns 0 on success, -ENODEV on failure.
270*4882a593Smuzhiyun  */
vmmouse_enable(struct psmouse * psmouse)271*4882a593Smuzhiyun static int vmmouse_enable(struct psmouse *psmouse)
272*4882a593Smuzhiyun {
273*4882a593Smuzhiyun 	u32 status, version;
274*4882a593Smuzhiyun 	u32 dummy1, dummy2, dummy3, dummy4;
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun 	/*
277*4882a593Smuzhiyun 	 * Try enabling the device. If successful, we should be able to
278*4882a593Smuzhiyun 	 * read valid version ID back from it.
279*4882a593Smuzhiyun 	 */
280*4882a593Smuzhiyun 	VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_ENABLE,
281*4882a593Smuzhiyun 		    dummy1, dummy2, dummy3, dummy4);
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	/*
284*4882a593Smuzhiyun 	 * See if version ID can be retrieved.
285*4882a593Smuzhiyun 	 */
286*4882a593Smuzhiyun 	VMMOUSE_CMD(ABSPOINTER_STATUS, 0, status, dummy1, dummy2, dummy3);
287*4882a593Smuzhiyun 	if ((status & 0x0000ffff) == 0) {
288*4882a593Smuzhiyun 		psmouse_dbg(psmouse, "empty flags - assuming no device\n");
289*4882a593Smuzhiyun 		return -ENXIO;
290*4882a593Smuzhiyun 	}
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	VMMOUSE_CMD(ABSPOINTER_DATA, 1 /* single item */,
293*4882a593Smuzhiyun 		    version, dummy1, dummy2, dummy3);
294*4882a593Smuzhiyun 	if (version != VMMOUSE_VERSION_ID) {
295*4882a593Smuzhiyun 		psmouse_dbg(psmouse, "Unexpected version value: %u vs %u\n",
296*4882a593Smuzhiyun 			    (unsigned) version, VMMOUSE_VERSION_ID);
297*4882a593Smuzhiyun 		vmmouse_disable(psmouse);
298*4882a593Smuzhiyun 		return -ENXIO;
299*4882a593Smuzhiyun 	}
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	/*
302*4882a593Smuzhiyun 	 * Restrict ioport access, if possible.
303*4882a593Smuzhiyun 	 */
304*4882a593Smuzhiyun 	VMMOUSE_CMD(ABSPOINTER_RESTRICT, VMMOUSE_RESTRICT_CPL0,
305*4882a593Smuzhiyun 		    dummy1, dummy2, dummy3, dummy4);
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	VMMOUSE_CMD(ABSPOINTER_COMMAND, VMMOUSE_CMD_REQUEST_ABSOLUTE,
308*4882a593Smuzhiyun 		    dummy1, dummy2, dummy3, dummy4);
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	return 0;
311*4882a593Smuzhiyun }
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun /*
314*4882a593Smuzhiyun  * Array of supported hypervisors.
315*4882a593Smuzhiyun  */
316*4882a593Smuzhiyun static enum x86_hypervisor_type vmmouse_supported_hypervisors[] = {
317*4882a593Smuzhiyun 	X86_HYPER_VMWARE,
318*4882a593Smuzhiyun 	X86_HYPER_KVM,
319*4882a593Smuzhiyun };
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun /**
322*4882a593Smuzhiyun  * vmmouse_check_hypervisor - Check if we're running on a supported hypervisor
323*4882a593Smuzhiyun  */
vmmouse_check_hypervisor(void)324*4882a593Smuzhiyun static bool vmmouse_check_hypervisor(void)
325*4882a593Smuzhiyun {
326*4882a593Smuzhiyun 	int i;
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(vmmouse_supported_hypervisors); i++)
329*4882a593Smuzhiyun 		if (vmmouse_supported_hypervisors[i] == x86_hyper_type)
330*4882a593Smuzhiyun 			return true;
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	return false;
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun /**
336*4882a593Smuzhiyun  * vmmouse_detect - Probe whether vmmouse is available
337*4882a593Smuzhiyun  *
338*4882a593Smuzhiyun  * @psmouse: Pointer to the psmouse struct
339*4882a593Smuzhiyun  * @set_properties: Whether to set psmouse name and vendor
340*4882a593Smuzhiyun  *
341*4882a593Smuzhiyun  * Returns 0 if vmmouse channel is available. Negative error code if not.
342*4882a593Smuzhiyun  */
vmmouse_detect(struct psmouse * psmouse,bool set_properties)343*4882a593Smuzhiyun int vmmouse_detect(struct psmouse *psmouse, bool set_properties)
344*4882a593Smuzhiyun {
345*4882a593Smuzhiyun 	u32 response, version, dummy1, dummy2;
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	if (!vmmouse_check_hypervisor()) {
348*4882a593Smuzhiyun 		psmouse_dbg(psmouse,
349*4882a593Smuzhiyun 			    "VMMouse not running on supported hypervisor.\n");
350*4882a593Smuzhiyun 		return -ENXIO;
351*4882a593Smuzhiyun 	}
352*4882a593Smuzhiyun 
353*4882a593Smuzhiyun 	/* Check if the device is present */
354*4882a593Smuzhiyun 	response = ~VMMOUSE_PROTO_MAGIC;
355*4882a593Smuzhiyun 	VMMOUSE_CMD(GETVERSION, 0, version, response, dummy1, dummy2);
356*4882a593Smuzhiyun 	if (response != VMMOUSE_PROTO_MAGIC || version == 0xffffffffU)
357*4882a593Smuzhiyun 		return -ENXIO;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	if (set_properties) {
360*4882a593Smuzhiyun 		psmouse->vendor = VMMOUSE_VENDOR;
361*4882a593Smuzhiyun 		psmouse->name = VMMOUSE_NAME;
362*4882a593Smuzhiyun 		psmouse->model = version;
363*4882a593Smuzhiyun 	}
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 	return 0;
366*4882a593Smuzhiyun }
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun /**
369*4882a593Smuzhiyun  * vmmouse_disconnect - Take down vmmouse driver
370*4882a593Smuzhiyun  *
371*4882a593Smuzhiyun  * @psmouse: Pointer to the psmouse struct
372*4882a593Smuzhiyun  *
373*4882a593Smuzhiyun  * Takes down vmmouse driver and frees resources set up in vmmouse_init().
374*4882a593Smuzhiyun  */
vmmouse_disconnect(struct psmouse * psmouse)375*4882a593Smuzhiyun static void vmmouse_disconnect(struct psmouse *psmouse)
376*4882a593Smuzhiyun {
377*4882a593Smuzhiyun 	struct vmmouse_data *priv = psmouse->private;
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	vmmouse_disable(psmouse);
380*4882a593Smuzhiyun 	psmouse_reset(psmouse);
381*4882a593Smuzhiyun 	input_unregister_device(priv->abs_dev);
382*4882a593Smuzhiyun 	kfree(priv);
383*4882a593Smuzhiyun }
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun /**
386*4882a593Smuzhiyun  * vmmouse_reconnect - Reset the ps/2 - and vmmouse connections
387*4882a593Smuzhiyun  *
388*4882a593Smuzhiyun  * @psmouse: Pointer to the psmouse struct
389*4882a593Smuzhiyun  *
390*4882a593Smuzhiyun  * Attempts to reset the mouse connections. Returns 0 on success and
391*4882a593Smuzhiyun  * -1 on failure.
392*4882a593Smuzhiyun  */
vmmouse_reconnect(struct psmouse * psmouse)393*4882a593Smuzhiyun static int vmmouse_reconnect(struct psmouse *psmouse)
394*4882a593Smuzhiyun {
395*4882a593Smuzhiyun 	int error;
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	psmouse_reset(psmouse);
398*4882a593Smuzhiyun 	vmmouse_disable(psmouse);
399*4882a593Smuzhiyun 	error = vmmouse_enable(psmouse);
400*4882a593Smuzhiyun 	if (error) {
401*4882a593Smuzhiyun 		psmouse_err(psmouse,
402*4882a593Smuzhiyun 			    "Unable to re-enable mouse when reconnecting, err: %d\n",
403*4882a593Smuzhiyun 			    error);
404*4882a593Smuzhiyun 		return error;
405*4882a593Smuzhiyun 	}
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	return 0;
408*4882a593Smuzhiyun }
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun /**
411*4882a593Smuzhiyun  * vmmouse_init - Initialize the vmmouse driver
412*4882a593Smuzhiyun  *
413*4882a593Smuzhiyun  * @psmouse: Pointer to the psmouse struct
414*4882a593Smuzhiyun  *
415*4882a593Smuzhiyun  * Requests the device and tries to enable vmmouse mode.
416*4882a593Smuzhiyun  * If successful, sets up the input device for relative movement events.
417*4882a593Smuzhiyun  * It also allocates another input device and sets it up for absolute motion
418*4882a593Smuzhiyun  * events. Returns 0 on success and -1 on failure.
419*4882a593Smuzhiyun  */
vmmouse_init(struct psmouse * psmouse)420*4882a593Smuzhiyun int vmmouse_init(struct psmouse *psmouse)
421*4882a593Smuzhiyun {
422*4882a593Smuzhiyun 	struct vmmouse_data *priv;
423*4882a593Smuzhiyun 	struct input_dev *rel_dev = psmouse->dev, *abs_dev;
424*4882a593Smuzhiyun 	int error;
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 	psmouse_reset(psmouse);
427*4882a593Smuzhiyun 	error = vmmouse_enable(psmouse);
428*4882a593Smuzhiyun 	if (error)
429*4882a593Smuzhiyun 		return error;
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
432*4882a593Smuzhiyun 	abs_dev = input_allocate_device();
433*4882a593Smuzhiyun 	if (!priv || !abs_dev) {
434*4882a593Smuzhiyun 		error = -ENOMEM;
435*4882a593Smuzhiyun 		goto init_fail;
436*4882a593Smuzhiyun 	}
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun 	priv->abs_dev = abs_dev;
439*4882a593Smuzhiyun 	psmouse->private = priv;
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	/* Set up and register absolute device */
442*4882a593Smuzhiyun 	snprintf(priv->phys, sizeof(priv->phys), "%s/input1",
443*4882a593Smuzhiyun 		 psmouse->ps2dev.serio->phys);
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	/* Mimic name setup for relative device in psmouse-base.c */
446*4882a593Smuzhiyun 	snprintf(priv->dev_name, sizeof(priv->dev_name), "%s %s %s",
447*4882a593Smuzhiyun 		 VMMOUSE_PSNAME, VMMOUSE_VENDOR, VMMOUSE_NAME);
448*4882a593Smuzhiyun 	abs_dev->phys = priv->phys;
449*4882a593Smuzhiyun 	abs_dev->name = priv->dev_name;
450*4882a593Smuzhiyun 	abs_dev->id.bustype = BUS_I8042;
451*4882a593Smuzhiyun 	abs_dev->id.vendor = 0x0002;
452*4882a593Smuzhiyun 	abs_dev->id.product = PSMOUSE_VMMOUSE;
453*4882a593Smuzhiyun 	abs_dev->id.version = psmouse->model;
454*4882a593Smuzhiyun 	abs_dev->dev.parent = &psmouse->ps2dev.serio->dev;
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 	/* Set absolute device capabilities */
457*4882a593Smuzhiyun 	input_set_capability(abs_dev, EV_KEY, BTN_LEFT);
458*4882a593Smuzhiyun 	input_set_capability(abs_dev, EV_KEY, BTN_RIGHT);
459*4882a593Smuzhiyun 	input_set_capability(abs_dev, EV_KEY, BTN_MIDDLE);
460*4882a593Smuzhiyun 	input_set_capability(abs_dev, EV_ABS, ABS_X);
461*4882a593Smuzhiyun 	input_set_capability(abs_dev, EV_ABS, ABS_Y);
462*4882a593Smuzhiyun 	input_set_abs_params(abs_dev, ABS_X, 0, VMMOUSE_MAX_X, 0, 0);
463*4882a593Smuzhiyun 	input_set_abs_params(abs_dev, ABS_Y, 0, VMMOUSE_MAX_Y, 0, 0);
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun 	error = input_register_device(priv->abs_dev);
466*4882a593Smuzhiyun 	if (error)
467*4882a593Smuzhiyun 		goto init_fail;
468*4882a593Smuzhiyun 
469*4882a593Smuzhiyun 	/* Add wheel capability to the relative device */
470*4882a593Smuzhiyun 	input_set_capability(rel_dev, EV_REL, REL_WHEEL);
471*4882a593Smuzhiyun 
472*4882a593Smuzhiyun 	psmouse->protocol_handler = vmmouse_process_byte;
473*4882a593Smuzhiyun 	psmouse->disconnect = vmmouse_disconnect;
474*4882a593Smuzhiyun 	psmouse->reconnect = vmmouse_reconnect;
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 	return 0;
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun init_fail:
479*4882a593Smuzhiyun 	vmmouse_disable(psmouse);
480*4882a593Smuzhiyun 	psmouse_reset(psmouse);
481*4882a593Smuzhiyun 	input_free_device(abs_dev);
482*4882a593Smuzhiyun 	kfree(priv);
483*4882a593Smuzhiyun 	psmouse->private = NULL;
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	return error;
486*4882a593Smuzhiyun }
487