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