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