[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v2 1/1] genius: add genius serial mouse emulation
From: |
Romain Naour |
Subject: |
[Qemu-devel] [PATCH v2 1/1] genius: add genius serial mouse emulation |
Date: |
Tue, 14 Jan 2014 23:05:40 +0100 |
This patch adds the emulation for a serial Genius mouse using
Mouse Systems protocol (5bytes).
This protocol is compatible with most 3-button serial mouse.
Signed-off-by: Romain Naour <address@hidden>
---
Changes v1 -> v2:
Fixes documentation (Paolo Bonzini)
Fixes typos
backends/Makefile.objs | 2 +-
backends/gnmouse.c | 339 +++++++++++++++++++++++++++++++++++++++++++++++++
include/sysemu/char.h | 3 +
qapi-schema.json | 1 +
qemu-char.c | 4 +
qemu-options.hx | 14 +-
6 files changed, 360 insertions(+), 3 deletions(-)
create mode 100644 backends/gnmouse.c
diff --git a/backends/Makefile.objs b/backends/Makefile.objs
index 42557d5..e4b072c 100644
--- a/backends/Makefile.objs
+++ b/backends/Makefile.objs
@@ -1,7 +1,7 @@
common-obj-y += rng.o rng-egd.o
common-obj-$(CONFIG_POSIX) += rng-random.o
-common-obj-y += msmouse.o
+common-obj-y += msmouse.o gnmouse.o
common-obj-$(CONFIG_BRLAPI) += baum.o
$(obj)/baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
diff --git a/backends/gnmouse.c b/backends/gnmouse.c
new file mode 100644
index 0000000..9581419
--- /dev/null
+++ b/backends/gnmouse.c
@@ -0,0 +1,339 @@
+/*
+ * QEMU Genius GM-6 serial mouse emulation
+ *
+ * Adapted from msmouse
+ *
+ * Copyright (c) 2014 Romain Naour
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "qemu-common.h"
+#include "sysemu/char.h"
+#include "ui/console.h"
+#include "qemu/timer.h"
+
+/* #define DEBUG_GENIUS_MOUSE */
+
+#ifdef DEBUG_GENIUS_MOUSE
+#define DPRINTF(fmt, ...) \
+do { fprintf(stderr, "gnmouse: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+do {} while (0)
+#endif
+
+/*
+ * struct gnmouse_save:
+ * This structure is used to save private info for Genius mouse.
+ *
+ * dx: deltas on x-axis saved since last frame send to emulated system.
+ * dy: deltas on y-axis saved since last frame send to emulated system.
+ * transmit_timer: QEMU's timer
+ * transmit_time: reload value for transmit_timer
+ * data: frame to be sent
+ * index: used to save current state of the state machine. see type states
below
+ */
+typedef struct gnmouse_save {
+ int dx;
+ int dy;
+ int button;
+ struct QEMUTimer *transmit_timer; /* QEMU timer */
+ uint64_t transmit_time; /* time to transmit a char in ticks */
+ unsigned char data[5];
+ int index;
+} gnmouse_save;
+
+
+/* states */
+typedef enum {
+ START, /* 0 */
+ CHAR_1, /* 1 : BP */
+ CHAR_2, /* 2 : Dx */
+ CHAR_3, /* 3 : Dy */
+ CHAR_4, /* 4 : Dx */
+ CHAR_5, /* 5 : Dy */
+ STOP /* 6 */
+}
+states;
+
+/**
+ * gnmouse_chr_write: this function is used when QEMU
+ * try to write something to mouse port.
+ * Nothing is send to the emulated mouse.
+ *
+ * Return: lengh of the buffer
+ *
+ * @s: address of the CharDriverState used by the mouse
+ * @buf: buffer to write
+ * @len: lengh of the buffer to write
+ */
+static int gnmouse_chr_write(struct CharDriverState *s, const uint8_t *buf,
+ int len)
+{
+ /* Ignore writes to mouse port */
+ return len;
+}
+
+/**
+ * gnmouse_chr_close: this function close the mouse port.
+ * It stop and free the QEMU's timer and free gnmouse_save struct.
+ *
+ * Return: void
+ *
+ * @chr: address of the CharDriverState used by the mouse
+ */
+static void gnmouse_chr_close(struct CharDriverState *chr)
+{
+ /* stop and free the QEMU's timer */
+ timer_del(((gnmouse_save *)chr->opaque)->transmit_timer);
+ timer_free(((gnmouse_save *)chr->opaque)->transmit_timer);
+ /* free gnmouse_save struct */
+ g_free(chr->opaque);
+ g_free(chr);
+}
+
+/**
+ * gnmouse_handler: send a byte on serial port to the guest system
+ * This handler is called on each timer timeout or directly by gnmouse_event()
+ * when no transmission is underway.
+ * It use a state machine in order to know which byte of the frame must be
send.
+ *
+ * Returns void
+ *
+ * @opaque: address of the CharDriverState used by the mouse
+ */
+static void gnmouse_handler(void *opaque)
+{
+ CharDriverState *chr = (CharDriverState *)opaque;
+ gnmouse_save *save = (gnmouse_save *)chr->opaque;
+ unsigned char *data = save->data;
+ int dx_tmp, dy_tmp;
+/*
+ * Byte 0: 1, 0, 0, 0, 0, L, M, R
+ * Byte 1: X7, X6, X5, X4, X3, X2, X1, X0
+ * Byte 2: Y7, Y6, Y5, Y4, Y3, Y2, Y1, Y0
+ * Byte 3: X7, X6, X5, X4, X3, X2, X1, X0
+ * Byte 4: Y7, Y6, Y5, Y4, Y3, Y2, Y1, Y0
+ */
+ switch (save->index) {
+ case CHAR_4:
+ if (save->dx && save->dy) {
+ if (save->dx >= 128) {
+ DPRINTF("overflow dx= %d\n", save->dx);
+ save->dx -= 128;
+ dx_tmp = 128;
+ } else if (save->dx <= -127) {
+ DPRINTF("overflow dx= %d\n", save->dx);
+ save->dx += 127;
+ dx_tmp = -127;
+ } else {
+ dx_tmp = save->dx;
+ save->dx = 0;
+ }
+
+ if (save->dy >= 128) {
+ DPRINTF("overflow dy= %d\n", save->dy);
+ save->dy -= 128;
+ dy_tmp = 128;
+ } else if (save->dy <= -127) {
+ DPRINTF("overflow dy= %d\n", save->dy);
+ save->dy += 127;
+ dy_tmp = -127;
+ } else {
+ dy_tmp = save->dy;
+ save->dy = 0;
+ }
+
+ DPRINTF("dx= %d\n", save->dx);
+ DPRINTF("dy= %d\n", save->dy);
+
+ data[3] = dx_tmp;
+ data[4] = -(dy_tmp);
+ }
+ break;
+
+ case STOP:
+ if (!(save->dx && save->dy)) {
+ /* no more data */
+ DPRINTF("no more data\n");
+ return;
+ } else {
+ /* data saved */
+ DPRINTF("data saved\n");
+ save->index = START;
+ }
+ /* No break, pass-through START */
+
+ case START:
+ /* New serial frame */
+ /* Buttons */
+ data[0] = save->button;
+ save->index = CHAR_1;
+ /* No break, pass-through CHAR_1 */
+
+ case CHAR_1:
+ /* avoid overflow on dx or dy */
+ if (save->dx >= 128) {
+ DPRINTF("overflow dx= %d\n", save->dx);
+ save->dx -= 128;
+ dx_tmp = 128;
+ } else if (save->dx <= -127) {
+ DPRINTF("overflow dx= %d\n", save->dx);
+ save->dx += 127;
+ dx_tmp = -127;
+ } else{
+ dx_tmp = save->dx;
+ save->dx = 0;
+ }
+
+ if (save->dy >= 128) {
+ DPRINTF("overflow dy= %d\n", save->dy);
+ save->dy -= 128;
+ dy_tmp = 128;
+ } else if (save->dy <= -127) {
+ DPRINTF("overflow dy= %d\n", save->dy);
+ save->dy += 127;
+ dy_tmp = -127;
+ } else{
+ dy_tmp = save->dy;
+ save->dy = 0;
+ }
+
+ DPRINTF("dx= %d\n", save->dx);
+ DPRINTF("dy= %d\n", save->dy);
+
+ /* Movement deltas */
+ data[1] = dx_tmp;
+ data[2] = -(dy_tmp);
+ data[3] = 0;
+ data[4] = 0;
+
+ case CHAR_2:
+ case CHAR_3:
+ case CHAR_5:
+ break;
+ default:
+ return;
+ }
+
+ /* reload timer */
+ timer_mod(save->transmit_timer,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + save->transmit_time);
+ DPRINTF("mod_timer: %d\n", save->index);
+ /* write data on serial port */
+ qemu_chr_be_write(chr, &(data[save->index - 1]), 1);
+ DPRINTF("write :%x\n", data[save->index - 1]);
+ /* next state */
+ save->index += 1;
+}
+
+/**
+ * gnmouse_event: event handler called by SDL functions
+ * on each mouse movement or button press.
+ *
+ * Return void
+ *
+ * @opaque: address of the CharDriverState used by the mouse
+ * @dx: deltas on the x-axis since last event
+ * @dy: deltas on the y-axis since last event
+ * @dz: deltas on the z-axis since last event (not used)
+ * @button_state: status of mouse button
+ */
+static void gnmouse_event(void *opaque,
+ int dx, int dy, int dz, int buttons_state)
+{
+ CharDriverState *chr = (CharDriverState *)opaque;
+ gnmouse_save *save = (gnmouse_save *)chr->opaque;
+ char BP = 0x80;
+
+ /* save deltas */
+ save->dx += dx;
+ save->dy += dy;
+
+ DPRINTF("dx= %d; dy= %d; buttons=%x\n", dx, dy, buttons_state);
+
+ /* Buttons */
+ BP |= (buttons_state & 0x01 ? 0x00 : 0x04); /* BP1 = L */
+ BP |= (buttons_state & 0x02 ? 0x00 : 0x01); /* BP2 = R */
+ BP |= (buttons_state & 0x04 ? 0x00 : 0x02); /* BP4 = M */
+
+ save->button = BP;
+ if (save->index == STOP) {
+ /* no transmission is underway, start a new transmission */
+ save->index = START;
+ gnmouse_handler((void *) chr);
+ }
+}
+
+/**
+ * qemu_chr_open_gnmouse: Init function for Genius mouse
+ * allocate a gnmouse_save structure to save data used by gnmouse emulation.
+ * allocate a new CharDriverState.
+ * create a new QEMU's timer with gnmouse_handler() as timeout handler.
+ * calculate the transmit_time for 1200 bauds transmission.
+ *
+ * Return address of the initialized CharDriverState
+ *
+ * @opts: argument not used
+ */
+CharDriverState *qemu_chr_open_gnmouse(void)
+{
+ CharDriverState *chr;
+ gnmouse_save *save;
+
+ DPRINTF("qemu_chr_open_gnmouse\n");
+
+ /* allocate CharDriverState and gnmouse_save */
+ chr = g_malloc0(sizeof(CharDriverState));
+ save = g_malloc0(sizeof(gnmouse_save));
+
+ chr->chr_write = gnmouse_chr_write;
+ chr->chr_close = gnmouse_chr_close;
+ chr->explicit_be_open = true;
+
+ /* create a new QEMU's timer with gnmouse_handler() as timeout handler. */
+ save->transmit_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ (QEMUTimerCB *) gnmouse_handler, chr);
+ /* calculate the transmit_time for 1200 bauds transmission */
+ save->transmit_time = (get_ticks_per_sec() / 1200) * 10; /* 1200 bauds */
+
+ DPRINTF("transmit_time = %lld\n", save->transmit_time);
+
+ /* init state machine */
+ save->index = STOP;
+
+ /* keep address of gnmouse_save */
+ chr->opaque = save;
+
+ qemu_add_mouse_event_handler(gnmouse_event, chr, 0,
+ "QEMU Genius GM-6 Mouse");
+
+ return chr;
+}
+
+static void register_types(void)
+{
+ register_char_driver_qapi("gnmouse", CHARDEV_BACKEND_KIND_GNMOUSE, NULL);
+}
+
+type_init(register_types);
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index b81a6ff..f769e3b 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -306,6 +306,9 @@ CharDriverState *qemu_char_get_next_serial(void);
/* msmouse */
CharDriverState *qemu_chr_open_msmouse(void);
+/* gnmouse */
+CharDriverState *qemu_chr_open_gnmouse(void);
+
/* baum.c */
CharDriverState *chr_baum_init(void);
diff --git a/qapi-schema.json b/qapi-schema.json
index c3c939c..5a5db7f 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3617,6 +3617,7 @@
'null' : 'ChardevDummy',
'mux' : 'ChardevMux',
'msmouse': 'ChardevDummy',
+ 'gnmouse': 'ChardevDummy',
'braille': 'ChardevDummy',
'stdio' : 'ChardevStdio',
'console': 'ChardevDummy',
diff --git a/qemu-char.c b/qemu-char.c
index 418dc69..9766815 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -2961,6 +2961,7 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const
char *filename)
if (strcmp(filename, "null") == 0 ||
strcmp(filename, "pty") == 0 ||
strcmp(filename, "msmouse") == 0 ||
+ strcmp(filename, "gnmouse") == 0 ||
strcmp(filename, "braille") == 0 ||
strcmp(filename, "stdio") == 0) {
qemu_opt_set(opts, "backend", filename);
@@ -3732,6 +3733,9 @@ ChardevReturn *qmp_chardev_add(const char *id,
ChardevBackend *backend,
case CHARDEV_BACKEND_KIND_MSMOUSE:
chr = qemu_chr_open_msmouse();
break;
+ case CHARDEV_BACKEND_KIND_GNMOUSE:
+ chr = qemu_chr_open_gnmouse();
+ break;
#ifdef CONFIG_BRLAPI
case CHARDEV_BACKEND_KIND_BRAILLE:
chr = chr_baum_init();
diff --git a/qemu-options.hx b/qemu-options.hx
index bcfe9ea..17cb8aa 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1792,6 +1792,7 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
"-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n"
" [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n"
"-chardev msmouse,id=id[,mux=on|off]\n"
+ "-chardev gnmouse,id=id[,mux=on|off]\n"
"-chardev
vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]\n"
" [,mux=on|off]\n"
"-chardev ringbuf,id=id[,size=size]\n"
@@ -1831,6 +1832,7 @@ Backend is one of:
@option{socket},
@option{udp},
@option{msmouse},
address@hidden,
@option{vc},
@option{ringbuf},
@option{file},
@@ -1927,8 +1929,13 @@ If neither is specified the device may use either
protocol.
@item -chardev msmouse ,address@hidden
-Forward QEMU's emulated msmouse events to the guest. @option{msmouse} does not
-take any options.
+Forward events from QEMU's emulated mouse to the guest using the
+Microsoft protocol. @option{msmouse} does not take any options.
+
address@hidden -chardev gnmouse ,address@hidden
+
+Forward events from QEMU's emulated mouse to the guest using the Genius
+(Mouse Systems) protocol. @option{gnmouse} does not take any options.
@item -chardev vc ,address@hidden [[,address@hidden [,address@hidden
[[,address@hidden [,address@hidden
@@ -2514,6 +2521,9 @@ or fake device.
@item msmouse
Three button serial mouse. Configure the guest to use Microsoft protocol.
+
address@hidden gnmouse
+Three button serial mouse. Configure the guest to use Genius (Mouse Systems)
protocol.
@end table
ETEXI
--
1.8.4.2
- [Qemu-devel] [PATCH v2 1/1] genius: add genius serial mouse emulation,
Romain Naour <=