|
1 /* |
|
2 * QEMU VMMouse emulation |
|
3 * |
|
4 * Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws> |
|
5 * |
|
6 * Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 * of this software and associated documentation files (the "Software"), to deal |
|
8 * in the Software without restriction, including without limitation the rights |
|
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 * copies of the Software, and to permit persons to whom the Software is |
|
11 * furnished to do so, subject to the following conditions: |
|
12 * |
|
13 * The above copyright notice and this permission notice shall be included in |
|
14 * all copies or substantial portions of the Software. |
|
15 * |
|
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 * THE SOFTWARE. |
|
23 */ |
|
24 #include "hw.h" |
|
25 #include "console.h" |
|
26 #include "ps2.h" |
|
27 #include "pc.h" |
|
28 |
|
29 /* debug only vmmouse */ |
|
30 //#define DEBUG_VMMOUSE |
|
31 |
|
32 /* VMMouse Commands */ |
|
33 #define VMMOUSE_GETVERSION 10 |
|
34 #define VMMOUSE_DATA 39 |
|
35 #define VMMOUSE_STATUS 40 |
|
36 #define VMMOUSE_COMMAND 41 |
|
37 |
|
38 #define VMMOUSE_READ_ID 0x45414552 |
|
39 #define VMMOUSE_DISABLE 0x000000f5 |
|
40 #define VMMOUSE_REQUEST_RELATIVE 0x4c455252 |
|
41 #define VMMOUSE_REQUEST_ABSOLUTE 0x53424152 |
|
42 |
|
43 #define VMMOUSE_QUEUE_SIZE 1024 |
|
44 |
|
45 #define VMMOUSE_VERSION 0x3442554a |
|
46 |
|
47 #ifdef DEBUG_VMMOUSE |
|
48 #define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) |
|
49 #else |
|
50 #define DPRINTF(fmt, ...) do { } while (0) |
|
51 #endif |
|
52 |
|
53 typedef struct _VMMouseState |
|
54 { |
|
55 uint32_t queue[VMMOUSE_QUEUE_SIZE]; |
|
56 uint16_t nb_queue; |
|
57 uint16_t status; |
|
58 uint8_t absolute; |
|
59 QEMUPutMouseEntry *entry; |
|
60 void *ps2_mouse; |
|
61 } VMMouseState; |
|
62 |
|
63 static uint32_t vmmouse_get_status(VMMouseState *s) |
|
64 { |
|
65 DPRINTF("vmmouse_get_status()\n"); |
|
66 return (s->status << 16) | s->nb_queue; |
|
67 } |
|
68 |
|
69 static void vmmouse_mouse_event(void *opaque, int x, int y, int dz, int buttons_state) |
|
70 { |
|
71 VMMouseState *s = opaque; |
|
72 int buttons = 0; |
|
73 |
|
74 if (s->nb_queue > (VMMOUSE_QUEUE_SIZE - 4)) |
|
75 return; |
|
76 |
|
77 DPRINTF("vmmouse_mouse_event(%d, %d, %d, %d)\n", |
|
78 x, y, dz, buttons_state); |
|
79 |
|
80 if ((buttons_state & MOUSE_EVENT_LBUTTON)) |
|
81 buttons |= 0x20; |
|
82 if ((buttons_state & MOUSE_EVENT_RBUTTON)) |
|
83 buttons |= 0x10; |
|
84 if ((buttons_state & MOUSE_EVENT_MBUTTON)) |
|
85 buttons |= 0x08; |
|
86 |
|
87 if (s->absolute) { |
|
88 x <<= 1; |
|
89 y <<= 1; |
|
90 } |
|
91 |
|
92 s->queue[s->nb_queue++] = buttons; |
|
93 s->queue[s->nb_queue++] = x; |
|
94 s->queue[s->nb_queue++] = y; |
|
95 s->queue[s->nb_queue++] = dz; |
|
96 |
|
97 /* need to still generate PS2 events to notify driver to |
|
98 read from queue */ |
|
99 ps2_mouse_fake_event(s->ps2_mouse); |
|
100 } |
|
101 |
|
102 static void vmmouse_update_handler(VMMouseState *s) |
|
103 { |
|
104 if (s->entry) { |
|
105 qemu_remove_mouse_event_handler(s->entry); |
|
106 s->entry = NULL; |
|
107 } |
|
108 if (s->status == 0) |
|
109 s->entry = qemu_add_mouse_event_handler(vmmouse_mouse_event, |
|
110 s, s->absolute, |
|
111 "vmmouse"); |
|
112 } |
|
113 |
|
114 static void vmmouse_read_id(VMMouseState *s) |
|
115 { |
|
116 DPRINTF("vmmouse_read_id()\n"); |
|
117 |
|
118 if (s->nb_queue == VMMOUSE_QUEUE_SIZE) |
|
119 return; |
|
120 |
|
121 s->queue[s->nb_queue++] = VMMOUSE_VERSION; |
|
122 s->status = 0; |
|
123 vmmouse_update_handler(s); |
|
124 } |
|
125 |
|
126 static void vmmouse_request_relative(VMMouseState *s) |
|
127 { |
|
128 DPRINTF("vmmouse_request_relative()\n"); |
|
129 s->absolute = 0; |
|
130 vmmouse_update_handler(s); |
|
131 } |
|
132 |
|
133 static void vmmouse_request_absolute(VMMouseState *s) |
|
134 { |
|
135 DPRINTF("vmmouse_request_absolute()\n"); |
|
136 s->absolute = 1; |
|
137 vmmouse_update_handler(s); |
|
138 } |
|
139 |
|
140 static void vmmouse_disable(VMMouseState *s) |
|
141 { |
|
142 DPRINTF("vmmouse_disable()\n"); |
|
143 s->status = 0xffff; |
|
144 vmmouse_update_handler(s); |
|
145 } |
|
146 |
|
147 static void vmmouse_data(VMMouseState *s, uint32_t *data, uint32_t size) |
|
148 { |
|
149 int i; |
|
150 |
|
151 DPRINTF("vmmouse_data(%d)\n", size); |
|
152 |
|
153 if (size == 0 || size > 6 || size > s->nb_queue) { |
|
154 printf("vmmouse: driver requested too much data %d\n", size); |
|
155 s->status = 0xffff; |
|
156 vmmouse_update_handler(s); |
|
157 return; |
|
158 } |
|
159 |
|
160 for (i = 0; i < size; i++) |
|
161 data[i] = s->queue[i]; |
|
162 |
|
163 s->nb_queue -= size; |
|
164 if (s->nb_queue) |
|
165 memmove(s->queue, &s->queue[size], sizeof(s->queue[0]) * s->nb_queue); |
|
166 } |
|
167 |
|
168 static void vmmouse_get_data(uint32_t *data) |
|
169 { |
|
170 CPUState *env = cpu_single_env; |
|
171 |
|
172 data[0] = env->regs[R_EAX]; data[1] = env->regs[R_EBX]; |
|
173 data[2] = env->regs[R_ECX]; data[3] = env->regs[R_EDX]; |
|
174 data[4] = env->regs[R_ESI]; data[5] = env->regs[R_EDI]; |
|
175 |
|
176 DPRINTF("get_data = {%x, %x, %x, %x, %x, %x}\n", |
|
177 data[0], data[1], data[2], data[3], data[4], data[5]); |
|
178 } |
|
179 |
|
180 static void vmmouse_set_data(const uint32_t *data) |
|
181 { |
|
182 CPUState *env = cpu_single_env; |
|
183 |
|
184 DPRINTF("set_data = {%x, %x, %x, %x, %x, %x}\n", |
|
185 data[0], data[1], data[2], data[3], data[4], data[5]); |
|
186 |
|
187 env->regs[R_EAX] = data[0]; env->regs[R_EBX] = data[1]; |
|
188 env->regs[R_ECX] = data[2]; env->regs[R_EDX] = data[3]; |
|
189 env->regs[R_ESI] = data[4]; env->regs[R_EDI] = data[5]; |
|
190 } |
|
191 |
|
192 static uint32_t vmmouse_ioport_read(void *opaque, uint32_t addr) |
|
193 { |
|
194 VMMouseState *s = opaque; |
|
195 uint32_t data[6]; |
|
196 uint16_t command; |
|
197 |
|
198 vmmouse_get_data(data); |
|
199 |
|
200 command = data[2] & 0xFFFF; |
|
201 |
|
202 switch (command) { |
|
203 case VMMOUSE_STATUS: |
|
204 data[0] = vmmouse_get_status(s); |
|
205 break; |
|
206 case VMMOUSE_COMMAND: |
|
207 switch (data[1]) { |
|
208 case VMMOUSE_DISABLE: |
|
209 vmmouse_disable(s); |
|
210 break; |
|
211 case VMMOUSE_READ_ID: |
|
212 vmmouse_read_id(s); |
|
213 break; |
|
214 case VMMOUSE_REQUEST_RELATIVE: |
|
215 vmmouse_request_relative(s); |
|
216 break; |
|
217 case VMMOUSE_REQUEST_ABSOLUTE: |
|
218 vmmouse_request_absolute(s); |
|
219 break; |
|
220 default: |
|
221 printf("vmmouse: unknown command %x\n", data[1]); |
|
222 break; |
|
223 } |
|
224 break; |
|
225 case VMMOUSE_DATA: |
|
226 vmmouse_data(s, data, data[1]); |
|
227 break; |
|
228 default: |
|
229 printf("vmmouse: unknown command %x\n", command); |
|
230 break; |
|
231 } |
|
232 |
|
233 vmmouse_set_data(data); |
|
234 return data[0]; |
|
235 } |
|
236 |
|
237 static void vmmouse_save(QEMUFile *f, void *opaque) |
|
238 { |
|
239 VMMouseState *s = opaque; |
|
240 int i; |
|
241 |
|
242 qemu_put_be32(f, VMMOUSE_QUEUE_SIZE); |
|
243 for (i = 0; i < VMMOUSE_QUEUE_SIZE; i++) |
|
244 qemu_put_be32s(f, &s->queue[i]); |
|
245 qemu_put_be16s(f, &s->nb_queue); |
|
246 qemu_put_be16s(f, &s->status); |
|
247 qemu_put_8s(f, &s->absolute); |
|
248 } |
|
249 |
|
250 static int vmmouse_load(QEMUFile *f, void *opaque, int version_id) |
|
251 { |
|
252 VMMouseState *s = opaque; |
|
253 int i; |
|
254 |
|
255 if (version_id != 0) |
|
256 return -EINVAL; |
|
257 |
|
258 if (qemu_get_be32(f) != VMMOUSE_QUEUE_SIZE) |
|
259 return -EINVAL; |
|
260 for (i = 0; i < VMMOUSE_QUEUE_SIZE; i++) |
|
261 qemu_get_be32s(f, &s->queue[i]); |
|
262 qemu_get_be16s(f, &s->nb_queue); |
|
263 qemu_get_be16s(f, &s->status); |
|
264 qemu_get_8s(f, &s->absolute); |
|
265 |
|
266 vmmouse_update_handler(s); |
|
267 |
|
268 return 0; |
|
269 } |
|
270 |
|
271 void *vmmouse_init(void *m) |
|
272 { |
|
273 VMMouseState *s = NULL; |
|
274 |
|
275 DPRINTF("vmmouse_init\n"); |
|
276 |
|
277 s = qemu_mallocz(sizeof(VMMouseState)); |
|
278 if (!s) |
|
279 return NULL; |
|
280 |
|
281 s->status = 0xffff; |
|
282 s->ps2_mouse = m; |
|
283 |
|
284 vmport_register(VMMOUSE_STATUS, vmmouse_ioport_read, s); |
|
285 vmport_register(VMMOUSE_COMMAND, vmmouse_ioport_read, s); |
|
286 vmport_register(VMMOUSE_DATA, vmmouse_ioport_read, s); |
|
287 register_savevm("vmmouse", 0, 0, vmmouse_save, vmmouse_load, s); |
|
288 |
|
289 return s; |
|
290 } |