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 25 #include "qemu/osdep.h" 26 #include "qapi/error.h" 27 #include "ui/console.h" 28 #include "hw/i386/pc.h" 29 #include "hw/input/i8042.h" 30 #include "hw/qdev-properties.h" 31 #include "migration/vmstate.h" 32 33 /* debug only vmmouse */ 34 //#define DEBUG_VMMOUSE 35 36 /* VMMouse Commands */ 37 #define VMMOUSE_GETVERSION 10 38 #define VMMOUSE_DATA 39 39 #define VMMOUSE_STATUS 40 40 #define VMMOUSE_COMMAND 41 41 42 #define VMMOUSE_READ_ID 0x45414552 43 #define VMMOUSE_DISABLE 0x000000f5 44 #define VMMOUSE_REQUEST_RELATIVE 0x4c455252 45 #define VMMOUSE_REQUEST_ABSOLUTE 0x53424152 46 47 #define VMMOUSE_QUEUE_SIZE 1024 48 49 #define VMMOUSE_VERSION 0x3442554a 50 51 #ifdef DEBUG_VMMOUSE 52 #define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) 53 #else 54 #define DPRINTF(fmt, ...) do { } while (0) 55 #endif 56 57 #define TYPE_VMMOUSE "vmmouse" 58 #define VMMOUSE(obj) OBJECT_CHECK(VMMouseState, (obj), TYPE_VMMOUSE) 59 60 typedef struct VMMouseState 61 { 62 ISADevice parent_obj; 63 64 uint32_t queue[VMMOUSE_QUEUE_SIZE]; 65 int32_t queue_size; 66 uint16_t nb_queue; 67 uint16_t status; 68 uint8_t absolute; 69 QEMUPutMouseEntry *entry; 70 ISAKBDState *i8042; 71 } VMMouseState; 72 73 static uint32_t vmmouse_get_status(VMMouseState *s) 74 { 75 DPRINTF("vmmouse_get_status()\n"); 76 return (s->status << 16) | s->nb_queue; 77 } 78 79 static void vmmouse_mouse_event(void *opaque, int x, int y, int dz, int buttons_state) 80 { 81 VMMouseState *s = opaque; 82 int buttons = 0; 83 84 if (s->nb_queue > (VMMOUSE_QUEUE_SIZE - 4)) 85 return; 86 87 DPRINTF("vmmouse_mouse_event(%d, %d, %d, %d)\n", 88 x, y, dz, buttons_state); 89 90 if ((buttons_state & MOUSE_EVENT_LBUTTON)) 91 buttons |= 0x20; 92 if ((buttons_state & MOUSE_EVENT_RBUTTON)) 93 buttons |= 0x10; 94 if ((buttons_state & MOUSE_EVENT_MBUTTON)) 95 buttons |= 0x08; 96 97 if (s->absolute) { 98 x <<= 1; 99 y <<= 1; 100 } 101 102 s->queue[s->nb_queue++] = buttons; 103 s->queue[s->nb_queue++] = x; 104 s->queue[s->nb_queue++] = y; 105 s->queue[s->nb_queue++] = dz; 106 107 /* need to still generate PS2 events to notify driver to 108 read from queue */ 109 i8042_isa_mouse_fake_event(s->i8042); 110 } 111 112 static void vmmouse_remove_handler(VMMouseState *s) 113 { 114 if (s->entry) { 115 qemu_remove_mouse_event_handler(s->entry); 116 s->entry = NULL; 117 } 118 } 119 120 static void vmmouse_update_handler(VMMouseState *s, int absolute) 121 { 122 if (s->status != 0) { 123 return; 124 } 125 if (s->absolute != absolute) { 126 s->absolute = absolute; 127 vmmouse_remove_handler(s); 128 } 129 if (s->entry == NULL) { 130 s->entry = qemu_add_mouse_event_handler(vmmouse_mouse_event, 131 s, s->absolute, 132 "vmmouse"); 133 qemu_activate_mouse_event_handler(s->entry); 134 } 135 } 136 137 static void vmmouse_read_id(VMMouseState *s) 138 { 139 DPRINTF("vmmouse_read_id()\n"); 140 141 if (s->nb_queue == VMMOUSE_QUEUE_SIZE) 142 return; 143 144 s->queue[s->nb_queue++] = VMMOUSE_VERSION; 145 s->status = 0; 146 } 147 148 static void vmmouse_request_relative(VMMouseState *s) 149 { 150 DPRINTF("vmmouse_request_relative()\n"); 151 vmmouse_update_handler(s, 0); 152 } 153 154 static void vmmouse_request_absolute(VMMouseState *s) 155 { 156 DPRINTF("vmmouse_request_absolute()\n"); 157 vmmouse_update_handler(s, 1); 158 } 159 160 static void vmmouse_disable(VMMouseState *s) 161 { 162 DPRINTF("vmmouse_disable()\n"); 163 s->status = 0xffff; 164 vmmouse_remove_handler(s); 165 } 166 167 static void vmmouse_data(VMMouseState *s, uint32_t *data, uint32_t size) 168 { 169 int i; 170 171 DPRINTF("vmmouse_data(%d)\n", size); 172 173 if (size == 0 || size > 6 || size > s->nb_queue) { 174 printf("vmmouse: driver requested too much data %d\n", size); 175 s->status = 0xffff; 176 vmmouse_remove_handler(s); 177 return; 178 } 179 180 for (i = 0; i < size; i++) 181 data[i] = s->queue[i]; 182 183 s->nb_queue -= size; 184 if (s->nb_queue) 185 memmove(s->queue, &s->queue[size], sizeof(s->queue[0]) * s->nb_queue); 186 } 187 188 static uint32_t vmmouse_ioport_read(void *opaque, uint32_t addr) 189 { 190 VMMouseState *s = opaque; 191 uint32_t data[6]; 192 uint16_t command; 193 194 vmmouse_get_data(data); 195 196 command = data[2] & 0xFFFF; 197 198 switch (command) { 199 case VMMOUSE_STATUS: 200 data[0] = vmmouse_get_status(s); 201 break; 202 case VMMOUSE_COMMAND: 203 switch (data[1]) { 204 case VMMOUSE_DISABLE: 205 vmmouse_disable(s); 206 break; 207 case VMMOUSE_READ_ID: 208 vmmouse_read_id(s); 209 break; 210 case VMMOUSE_REQUEST_RELATIVE: 211 vmmouse_request_relative(s); 212 break; 213 case VMMOUSE_REQUEST_ABSOLUTE: 214 vmmouse_request_absolute(s); 215 break; 216 default: 217 printf("vmmouse: unknown command %x\n", data[1]); 218 break; 219 } 220 break; 221 case VMMOUSE_DATA: 222 vmmouse_data(s, data, data[1]); 223 break; 224 default: 225 printf("vmmouse: unknown command %x\n", command); 226 break; 227 } 228 229 vmmouse_set_data(data); 230 return data[0]; 231 } 232 233 static int vmmouse_post_load(void *opaque, int version_id) 234 { 235 VMMouseState *s = opaque; 236 237 vmmouse_remove_handler(s); 238 vmmouse_update_handler(s, s->absolute); 239 return 0; 240 } 241 242 static const VMStateDescription vmstate_vmmouse = { 243 .name = "vmmouse", 244 .version_id = 0, 245 .minimum_version_id = 0, 246 .post_load = vmmouse_post_load, 247 .fields = (VMStateField[]) { 248 VMSTATE_INT32_EQUAL(queue_size, VMMouseState, NULL), 249 VMSTATE_UINT32_ARRAY(queue, VMMouseState, VMMOUSE_QUEUE_SIZE), 250 VMSTATE_UINT16(nb_queue, VMMouseState), 251 VMSTATE_UINT16(status, VMMouseState), 252 VMSTATE_UINT8(absolute, VMMouseState), 253 VMSTATE_END_OF_LIST() 254 } 255 }; 256 257 static void vmmouse_reset(DeviceState *d) 258 { 259 VMMouseState *s = VMMOUSE(d); 260 261 s->queue_size = VMMOUSE_QUEUE_SIZE; 262 s->nb_queue = 0; 263 264 vmmouse_disable(s); 265 } 266 267 static void vmmouse_realizefn(DeviceState *dev, Error **errp) 268 { 269 VMMouseState *s = VMMOUSE(dev); 270 271 DPRINTF("vmmouse_init\n"); 272 273 if (!object_resolve_path_type("", TYPE_VMPORT, NULL)) { 274 error_setg(errp, "vmmouse needs a machine with vmport"); 275 return; 276 } 277 278 vmport_register(VMMOUSE_STATUS, vmmouse_ioport_read, s); 279 vmport_register(VMMOUSE_COMMAND, vmmouse_ioport_read, s); 280 vmport_register(VMMOUSE_DATA, vmmouse_ioport_read, s); 281 } 282 283 static Property vmmouse_properties[] = { 284 DEFINE_PROP_LINK("i8042", VMMouseState, i8042, TYPE_I8042, ISAKBDState *), 285 DEFINE_PROP_END_OF_LIST(), 286 }; 287 288 static void vmmouse_class_initfn(ObjectClass *klass, void *data) 289 { 290 DeviceClass *dc = DEVICE_CLASS(klass); 291 292 dc->realize = vmmouse_realizefn; 293 dc->reset = vmmouse_reset; 294 dc->vmsd = &vmstate_vmmouse; 295 device_class_set_props(dc, vmmouse_properties); 296 } 297 298 static const TypeInfo vmmouse_info = { 299 .name = TYPE_VMMOUSE, 300 .parent = TYPE_ISA_DEVICE, 301 .instance_size = sizeof(VMMouseState), 302 .class_init = vmmouse_class_initfn, 303 }; 304 305 static void vmmouse_register_types(void) 306 { 307 type_register_static(&vmmouse_info); 308 } 309 310 type_init(vmmouse_register_types) 311