From 548df2acc6fcd12169583e7e7e25b33600cc20e7 Mon Sep 17 00:00:00 2001 From: ths Date: Tue, 20 Mar 2007 16:45:27 +0000 Subject: [PATCH] VMMouse Emulation, by Anthony Liguori. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2514 c046a42c-6fe2-441c-8c8c-71466251a162 --- Makefile.target | 2 +- hw/pckbd.c | 3 + hw/ps2.c | 5 + hw/vmmouse.c | 300 ++++++++++++++++++++++++++++++++++++++++++++++++ vl.h | 3 + 5 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 hw/vmmouse.c diff --git a/Makefile.target b/Makefile.target index 8be283577..b52656434 100644 --- a/Makefile.target +++ b/Makefile.target @@ -377,7 +377,7 @@ ifeq ($(TARGET_BASE_ARCH), i386) VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o acpi.o piix_pci.o -VL_OBJS+= usb-uhci.o smbus_eeprom.o +VL_OBJS+= usb-uhci.o smbus_eeprom.o vmmouse.o CPPFLAGS += -DHAS_AUDIO endif ifeq ($(TARGET_BASE_ARCH), ppc) diff --git a/hw/pckbd.c b/hw/pckbd.c index de40a09aa..0ace3c0c9 100644 --- a/hw/pckbd.c +++ b/hw/pckbd.c @@ -372,6 +372,9 @@ void i8042_init(int kbd_irq_lvl, int mouse_irq_lvl, uint32_t io_base) s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s); s->mouse = ps2_mouse_init(kbd_update_aux_irq, s); +#ifdef TARGET_I386 + vmmouse_init(s->mouse); +#endif qemu_register_reset(kbd_reset, s); } diff --git a/hw/ps2.c b/hw/ps2.c index 3794c6036..2d87f1f12 100644 --- a/hw/ps2.c +++ b/hw/ps2.c @@ -324,6 +324,11 @@ static void ps2_mouse_event(void *opaque, } } +void ps2_mouse_fake_event(void *opaque) +{ + ps2_mouse_event(opaque, 1, 0, 0, 0); +} + void ps2_write_mouse(void *opaque, int val) { PS2MouseState *s = (PS2MouseState *)opaque; diff --git a/hw/vmmouse.c b/hw/vmmouse.c new file mode 100644 index 000000000..cc783008d --- /dev/null +++ b/hw/vmmouse.c @@ -0,0 +1,300 @@ +/* + * QEMU VMMouse emulation + * + * Copyright (C) 2007 Anthony Liguori + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +/* debug only vmmouse */ +//#define DEBUG_VMMOUSE + +/* VMMouse Commands */ +#define VMMOUSE_GETVERSION 10 +#define VMMOUSE_DATA 39 +#define VMMOUSE_STATUS 40 +#define VMMOUSE_COMMAND 41 + +#define VMMOUSE_READ_ID 0x45414552 +#define VMMOUSE_DISABLE 0x000000f5 +#define VMMOUSE_REQUEST_RELATIVE 0x4c455252 +#define VMMOUSE_REQUEST_ABSOLUTE 0x53424152 + +#define VMMOUSE_QUEUE_SIZE 1024 + +#define VMMOUSE_MAGIC 0x564D5868 +#define VMMOUSE_VERSION 0x3442554a + +#ifdef DEBUG_VMMOUSE +#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) do { } while (0) +#endif + +typedef struct _VMMouseState +{ + uint32_t queue[VMMOUSE_QUEUE_SIZE]; + uint16_t nb_queue; + uint16_t status; + uint8_t absolute; + QEMUPutMouseEntry *entry; + void *ps2_mouse; +} VMMouseState; + +static uint32_t vmmouse_get_version(VMMouseState *s, uint32_t *magic) +{ + DPRINTF("vmmouse_get_version(%x)\n", *magic); + *magic = VMMOUSE_MAGIC; + return VMMOUSE_VERSION; +} + +static uint32_t vmmouse_get_status(VMMouseState *s) +{ + DPRINTF("vmmouse_get_status()\n"); + return (s->status << 16) | s->nb_queue; +} + +static void vmmouse_mouse_event(void *opaque, int x, int y, int dz, int buttons_state) +{ + VMMouseState *s = opaque; + int buttons = 0; + + if (s->nb_queue > (VMMOUSE_QUEUE_SIZE - 4)) + return; + + DPRINTF("vmmouse_mouse_event(%d, %d, %d, %d)\n", + x, y, dz, buttons_state); + + if ((buttons_state & MOUSE_EVENT_LBUTTON)) + buttons |= 0x20; + if ((buttons_state & MOUSE_EVENT_RBUTTON)) + buttons |= 0x10; + if ((buttons_state & MOUSE_EVENT_MBUTTON)) + buttons |= 0x08; + + if (s->absolute) { + x <<= 1; + y <<= 1; + } + + s->queue[s->nb_queue++] = buttons; + s->queue[s->nb_queue++] = x; + s->queue[s->nb_queue++] = y; + s->queue[s->nb_queue++] = dz; + + /* need to still generate PS2 events to notify driver to + read from queue */ + ps2_mouse_fake_event(s->ps2_mouse); +} + +static void vmmouse_update_handler(VMMouseState *s) +{ + if (s->entry) { + qemu_remove_mouse_event_handler(s->entry); + s->entry = NULL; + } + if (s->status == 0) + s->entry = qemu_add_mouse_event_handler(vmmouse_mouse_event, + s, s->absolute, + "vmmouse"); +} + +static void vmmouse_read_id(VMMouseState *s) +{ + DPRINTF("vmmouse_read_id()\n"); + + if (s->nb_queue == VMMOUSE_QUEUE_SIZE) + return; + + s->queue[s->nb_queue++] = VMMOUSE_VERSION; + s->status = 0; + vmmouse_update_handler(s); +} + +static void vmmouse_request_relative(VMMouseState *s) +{ + DPRINTF("vmmouse_request_relative()\n"); + s->absolute = 0; + vmmouse_update_handler(s); +} + +static void vmmouse_request_absolute(VMMouseState *s) +{ + DPRINTF("vmmouse_request_absolute()\n"); + s->absolute = 1; + vmmouse_update_handler(s); +} + +static void vmmouse_disable(VMMouseState *s) +{ + DPRINTF("vmmouse_disable()\n"); + s->status = 0xffff; + vmmouse_update_handler(s); +} + +static void vmmouse_data(VMMouseState *s, uint32_t *data, uint32_t size) +{ + int i; + + DPRINTF("vmmouse_data(%d)\n", size); + + if (size == 0 || size > 6 || size > s->nb_queue) { + printf("vmmouse: driver requested too much data %d\n", size); + s->status = 0xffff; + vmmouse_update_handler(s); + return; + } + + for (i = 0; i < size; i++) + data[i] = s->queue[i]; + + s->nb_queue -= size; + if (s->nb_queue) + memmove(s->queue, &s->queue[size], sizeof(s->queue[0]) * s->nb_queue); +} + +static void vmmouse_get_data(uint32_t *data) +{ + CPUState *env = cpu_single_env; + + data[0] = env->regs[R_EAX]; data[1] = env->regs[R_EBX]; + data[2] = env->regs[R_ECX]; data[3] = env->regs[R_EDX]; + data[4] = env->regs[R_ESI]; data[5] = env->regs[R_EDI]; + + DPRINTF("get_data = {%x, %x, %x, %x, %x, %x}\n", + data[0], data[1], data[2], data[3], data[4], data[5]); +} + +static void vmmouse_set_data(const uint32_t *data) +{ + CPUState *env = cpu_single_env; + + DPRINTF("set_data = {%x, %x, %x, %x, %x, %x}\n", + data[0], data[1], data[2], data[3], data[4], data[5]); + + env->regs[R_EAX] = data[0]; env->regs[R_EBX] = data[1]; + env->regs[R_ECX] = data[2]; env->regs[R_EDX] = data[3]; + env->regs[R_ESI] = data[4]; env->regs[R_EDI] = data[5]; +} + +static uint32_t vmmouse_ioport_read(void *opaque, uint32_t addr) +{ + VMMouseState *s = opaque; + uint32_t data[6]; + uint16_t command; + + vmmouse_get_data(data); + if (data[0] != VMMOUSE_MAGIC) + goto error; + + command = data[2] & 0xFFFF; + + switch (command) { + case VMMOUSE_GETVERSION: + data[0] = vmmouse_get_version(s, &data[1]); + break; + case VMMOUSE_STATUS: + data[0] = vmmouse_get_status(s); + break; + case VMMOUSE_COMMAND: + switch (data[1]) { + case VMMOUSE_DISABLE: + vmmouse_disable(s); + break; + case VMMOUSE_READ_ID: + vmmouse_read_id(s); + break; + case VMMOUSE_REQUEST_RELATIVE: + vmmouse_request_relative(s); + break; + case VMMOUSE_REQUEST_ABSOLUTE: + vmmouse_request_absolute(s); + break; + default: + printf("vmmouse: unknown command %x\n", data[1]); + break; + } + break; + case VMMOUSE_DATA: + vmmouse_data(s, data, data[1]); + break; + default: + printf("vmmouse: unknown command %x\n", command); + break; + } + +error: + vmmouse_set_data(data); + return data[0]; +} + +static void vmmouse_save(QEMUFile *f, void *opaque) +{ + VMMouseState *s = opaque; + int i; + + qemu_put_be32(f, VMMOUSE_QUEUE_SIZE); + for (i = 0; i < VMMOUSE_QUEUE_SIZE; i++) + qemu_put_be32s(f, &s->queue[i]); + qemu_put_be16s(f, &s->nb_queue); + qemu_put_be16s(f, &s->status); + qemu_put_8s(f, &s->absolute); +} + +static int vmmouse_load(QEMUFile *f, void *opaque, int version_id) +{ + VMMouseState *s = opaque; + int i; + + if (version_id != 0) + return -EINVAL; + + if (qemu_get_be32(f) != VMMOUSE_QUEUE_SIZE) + return -EINVAL; + for (i = 0; i < VMMOUSE_QUEUE_SIZE; i++) + qemu_get_be32s(f, &s->queue[i]); + qemu_get_be16s(f, &s->nb_queue); + qemu_get_be16s(f, &s->status); + qemu_get_8s(f, &s->absolute); + + vmmouse_update_handler(s); + + return 0; +} + +void *vmmouse_init(void *m) +{ + VMMouseState *s = NULL; + + DPRINTF("vmmouse_init\n"); + + s = qemu_mallocz(sizeof(VMMouseState)); + if (!s) + return NULL; + + s->status = 0xffff; + s->ps2_mouse = m; + + register_ioport_read(0x5658, 1, 4, vmmouse_ioport_read, s); + register_savevm("vmmouse", 0, 0, vmmouse_save, vmmouse_load, s); + + return s; +} + diff --git a/vl.h b/vl.h index b911f92b4..61b5b80f7 100644 --- a/vl.h +++ b/vl.h @@ -1005,6 +1005,8 @@ void pci_pcnet_init(PCIBus *bus, NICInfo *nd, int devfn); void pcnet_h_reset(void *opaque); void *lance_init(NICInfo *nd, uint32_t leaddr, void *dma_opaque); +/* vmmouse.c */ +void *vmmouse_init(void *m); /* pckbd.c */ @@ -1326,6 +1328,7 @@ void ps2_write_keyboard(void *, int val); uint32_t ps2_read_data(void *); void ps2_queue(void *, int b); void ps2_keyboard_set_translation(void *opaque, int mode); +void ps2_mouse_fake_event(void *opaque); /* smc91c111.c */ void smc91c111_init(NICInfo *, uint32_t, void *, int); -- 2.39.2