Makefile.objs | 1 + backends/Makefile.objs | 2 +- backends/trace-events | 10 ++ backends/wctablet.c | 374 +++++++++++++++++++++++++++++++++++++++++++++++ docs/qdev-device-use.txt | 2 +- qapi-schema.json | 3 +- qemu-char.c | 1 + 7 files changed, 390 insertions(+), 3 deletions(-) create mode 100644 backends/trace-events create mode 100644 backends/wctablet.c
From: Anatoli Huseu1 <avg.tolik@gmail.com>
Add QEMU Wacom Penpartner serial tablet emulation.
GSoC 2016 project.
Signed-off-by: Anatoli Huseu1 <avg.tolik@gmail.com>
Various cleanups.
Add line speed tracking.
Implement ST and SP commands.
Adapted to chardev QOMification.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
Makefile.objs | 1 +
backends/Makefile.objs | 2 +-
backends/trace-events | 10 ++
backends/wctablet.c | 374 +++++++++++++++++++++++++++++++++++++++++++++++
docs/qdev-device-use.txt | 2 +-
qapi-schema.json | 3 +-
qemu-char.c | 1 +
7 files changed, 390 insertions(+), 3 deletions(-)
create mode 100644 backends/trace-events
create mode 100644 backends/wctablet.c
diff --git a/Makefile.objs b/Makefile.objs
index 01cef86..5ac7f34 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -124,6 +124,7 @@ trace-events-y += crypto/trace-events
trace-events-y += io/trace-events
trace-events-y += migration/trace-events
trace-events-y += block/trace-events
+trace-events-y += backends/trace-events
trace-events-y += hw/block/trace-events
trace-events-y += hw/char/trace-events
trace-events-y += hw/intc/trace-events
diff --git a/backends/Makefile.objs b/backends/Makefile.objs
index 1846998..0e0f156 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 testdev.o
+common-obj-y += msmouse.o wctablet.o testdev.o
common-obj-$(CONFIG_BRLAPI) += baum.o
baum.o-cflags := $(SDL_CFLAGS)
diff --git a/backends/trace-events b/backends/trace-events
new file mode 100644
index 0000000..8c3289a
--- /dev/null
+++ b/backends/trace-events
@@ -0,0 +1,10 @@
+# See docs/tracing.txt for syntax documentation.
+
+# backends/wctablet.c
+wct_init(void) ""
+wct_cmd_re(void) ""
+wct_cmd_st(void) ""
+wct_cmd_sp(void) ""
+wct_cmd_ts(int input) "0x%02x"
+wct_cmd_other(const char *cmd) "%s"
+wct_speed(int speed) "%d"
diff --git a/backends/wctablet.c b/backends/wctablet.c
new file mode 100644
index 0000000..7668254
--- /dev/null
+++ b/backends/wctablet.c
@@ -0,0 +1,374 @@
+/*
+ * QEMU Wacom Penpartner serial tablet emulation
+ *
+ * some protocol details:
+ * http://linuxwacom.sourceforge.net/wiki/index.php/Serial_Protocol_IV
+ *
+ * Copyright (c) 2016 Anatoli Huseu1
+ * Copyright (c) 2016,17 Gerd Hoffmann
+ *
+ * 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 <time.h>
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "sysemu/char.h"
+#include "ui/console.h"
+#include "ui/input.h"
+#include "trace.h"
+
+
+#define WC_OUTPUT_BUF_MAX_LEN 512
+#define WC_COMMAND_MAX_LEN 60
+
+#define WC_L7(n) ((n) & 127)
+#define WC_M7(n) (((n) >> 7) & 127)
+#define WC_H2(n) ((n) >> 14)
+
+#define WC_L4(n) ((n) & 15)
+#define WC_H4(n) (((n) >> 4) & 15)
+
+/* Model string and config string */
+#define WC_MODEL_STRING_LENGTH 18
+uint8_t WC_MODEL_STRING[WC_MODEL_STRING_LENGTH + 1] = "~#CT-0045R,V1.3-5,";
+
+#define WC_CONFIG_STRING_LENGTH 8
+uint8_t WC_CONFIG_STRING[WC_CONFIG_STRING_LENGTH + 1] = "96,N,8,0";
+
+#define WC_FULL_CONFIG_STRING_LENGTH 61
+uint8_t WC_FULL_CONFIG_STRING[WC_FULL_CONFIG_STRING_LENGTH + 1] = {
+ 0x5c, 0x39, 0x36, 0x2c, 0x4e, 0x2c, 0x38, 0x2c,
+ 0x31, 0x28, 0x01, 0x24, 0x57, 0x41, 0x43, 0x30,
+ 0x30, 0x34, 0x35, 0x5c, 0x5c, 0x50, 0x45, 0x4e, 0x5c,
+ 0x57, 0x41, 0x43, 0x30, 0x30, 0x30, 0x30, 0x5c,
+ 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x0d, 0x0a,
+ 0x43, 0x54, 0x2d, 0x30, 0x30, 0x34, 0x35, 0x52,
+ 0x2c, 0x56, 0x31, 0x2e, 0x33, 0x2d, 0x35, 0x0d,
+ 0x0a, 0x45, 0x37, 0x29
+};
+
+/* This structure is used to save private info for Wacom Tablet. */
+typedef struct {
+ Chardev parent;
+ QemuInputHandlerState *hs;
+
+ /* Query string from serial */
+ uint8_t query[100];
+ int query_index;
+
+ /* Command to be sent to serial port */
+ uint8_t outbuf[WC_OUTPUT_BUF_MAX_LEN];
+ int outlen;
+
+ int line_speed;
+ bool send_events;
+ int axis[INPUT_AXIS__MAX];
+ bool btns[INPUT_BUTTON__MAX];
+
+} TabletChardev;
+
+#define TYPE_CHARDEV_WCTABLET "chardev-wctablet"
+#define WCTABLET_CHARDEV(obj) \
+ OBJECT_CHECK(TabletChardev, (obj), TYPE_CHARDEV_WCTABLET)
+
+
+static void wctablet_chr_accept_input(Chardev *chr);
+
+static void wctablet_shift_input(TabletChardev *tablet, int count)
+{
+ tablet->query_index -= count;
+ memmove(tablet->query, tablet->query + count, tablet->query_index);
+ tablet->query[tablet->query_index] = 0;
+}
+
+static void wctablet_queue_output(TabletChardev *tablet, uint8_t *buf, int count)
+{
+ if (tablet->outlen + count > sizeof(tablet->outbuf)) {
+ return;
+ }
+
+ memcpy(tablet->outbuf + tablet->outlen, buf, count);
+ tablet->outlen += count;
+ wctablet_chr_accept_input(CHARDEV(tablet));
+}
+
+static void wctablet_reset(TabletChardev *tablet)
+{
+ /* clear buffers */
+ tablet->query_index = 0;
+ tablet->outlen = 0;
+ /* reset state */
+ tablet->send_events = false;
+}
+
+static void wctablet_queue_event(TabletChardev *tablet)
+{
+ uint8_t codes[8] = { 0xe0, 0, 0, 0, 0, 0, 0 };
+
+ if (tablet->line_speed != 9600) {
+ return;
+ }
+
+ int newX = tablet->axis[INPUT_AXIS_X] * 0.1537;
+ int nexY = tablet->axis[INPUT_AXIS_Y] * 0.1152;
+
+ codes[0] = codes[0] | WC_H2(newX);
+ codes[1] = codes[1] | WC_M7(newX);
+ codes[2] = codes[2] | WC_L7(newX);
+
+ codes[3] = codes[3] | WC_H2(nexY);
+ codes[4] = codes[4] | WC_M7(nexY);
+ codes[5] = codes[5] | WC_L7(nexY);
+
+ if (tablet->btns[INPUT_BUTTON_LEFT]) {
+ codes[0] = 0xa0;
+ }
+
+ wctablet_queue_output(tablet, codes, 7);
+}
+
+static void wctablet_input_event(DeviceState *dev, QemuConsole *src,
+ InputEvent *evt)
+{
+ TabletChardev *tablet = (TabletChardev *)dev;
+ InputMoveEvent *move;
+ InputBtnEvent *btn;
+
+ switch (evt->type) {
+ case INPUT_EVENT_KIND_ABS:
+ move = evt->u.abs.data;
+ tablet->axis[move->axis] = move->value;
+ break;
+
+ case INPUT_EVENT_KIND_BTN:
+ btn = evt->u.btn.data;
+ tablet->btns[btn->button] = btn->down;
+ break;
+
+ default:
+ /* keep gcc happy */
+ break;
+ }
+}
+
+static void wctablet_input_sync(DeviceState *dev)
+{
+ TabletChardev *tablet = (TabletChardev *)dev;
+
+ if (tablet->send_events) {
+ wctablet_queue_event(tablet);
+ }
+}
+
+static QemuInputHandler wctablet_handler = {
+ .name = "QEMU Wacome Pen Tablet",
+ .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
+ .event = wctablet_input_event,
+ .sync = wctablet_input_sync,
+};
+
+static void wctablet_chr_accept_input(Chardev *chr)
+{
+ TabletChardev *tablet = WCTABLET_CHARDEV(chr);
+ int len, canWrite;
+
+ canWrite = qemu_chr_be_can_write(chr);
+ len = canWrite;
+ if (len > tablet->outlen) {
+ len = tablet->outlen;
+ }
+
+ if (len) {
+ qemu_chr_be_write(chr, tablet->outbuf, len);
+ tablet->outlen -= len;
+ if (tablet->outlen) {
+ memmove(tablet->outbuf, tablet->outbuf + len, tablet->outlen);
+ }
+ }
+}
+
+static int wctablet_chr_write(struct Chardev *chr,
+ const uint8_t *buf, int len)
+{
+ TabletChardev *tablet = WCTABLET_CHARDEV(chr);
+ unsigned int i, clen;
+ char *pos;
+
+ if (tablet->line_speed != 9600) {
+ return len;
+ }
+ for (i = 0; i < len && tablet->query_index < sizeof(tablet->query) - 1; i++) {
+ tablet->query[tablet->query_index++] = buf[i];
+ }
+ tablet->query[tablet->query_index] = 0;
+
+ while (tablet->query_index > 0 && (tablet->query[0] == '@' ||
+ tablet->query[0] == '\r' ||
+ tablet->query[0] == '\n')) {
+ wctablet_shift_input(tablet, 1);
+ }
+ if (!tablet->query_index) {
+ return len;
+ }
+
+ if (strncmp((char *)tablet->query, "~#", 2) == 0) {
+ /* init / detect sequence */
+ trace_wct_init();
+ wctablet_shift_input(tablet, 2);
+ wctablet_queue_output(tablet, WC_MODEL_STRING,
+ WC_MODEL_STRING_LENGTH);
+ return len;
+ }
+
+ /* detect line */
+ pos = strchr((char *)tablet->query, '\r');
+ if (!pos) {
+ pos = strchr((char *)tablet->query, '\n');
+ }
+ if (!pos) {
+ return len;
+ }
+ clen = pos - (char *)tablet->query;
+
+ /* process commands */
+ if (strncmp((char *)tablet->query, "RE", 2) == 0 &&
+ clen == 2) {
+ trace_wct_cmd_re();
+ wctablet_shift_input(tablet, 3);
+ wctablet_queue_output(tablet, WC_CONFIG_STRING,
+ WC_CONFIG_STRING_LENGTH);
+
+ } else if (strncmp((char *)tablet->query, "ST", 2) == 0 &&
+ clen == 2) {
+ trace_wct_cmd_st();
+ wctablet_shift_input(tablet, 3);
+ tablet->send_events = true;
+ wctablet_queue_event(tablet);
+
+ } else if (strncmp((char *)tablet->query, "SP", 2) == 0 &&
+ clen == 2) {
+ trace_wct_cmd_sp();
+ wctablet_shift_input(tablet, 3);
+ tablet->send_events = false;
+
+ } else if (strncmp((char *)tablet->query, "TS", 2) == 0 &&
+ clen == 3) {
+ unsigned int input = tablet->query[2];
+ uint8_t codes[7] = {
+ 0xa3,
+ ((input & 0x80) == 0) ? 0x7e : 0x7f,
+ (((WC_H4(input) & 0x7) ^ 0x5) << 4) | (WC_L4(input) ^ 0x7),
+ 0x03,
+ 0x7f,
+ 0x7f,
+ 0x00,
+ };
+ trace_wct_cmd_ts(input);
+ wctablet_shift_input(tablet, 4);
+ wctablet_queue_output(tablet, codes, 7);
+
+ } else {
+ tablet->query[clen] = 0; /* terminate line for printing */
+ trace_wct_cmd_other((char *)tablet->query);
+ wctablet_shift_input(tablet, clen + 1);
+
+ }
+
+ return len;
+}
+
+static int wctablet_chr_ioctl(Chardev *chr, int cmd, void *arg)
+{
+ TabletChardev *tablet = WCTABLET_CHARDEV(chr);
+ QEMUSerialSetParams *ssp;
+
+ switch (cmd) {
+ case CHR_IOCTL_SERIAL_SET_PARAMS:
+ ssp = arg;
+ if (tablet->line_speed != ssp->speed) {
+ trace_wct_speed(ssp->speed);
+ wctablet_reset(tablet);
+ tablet->line_speed = ssp->speed;
+ }
+ break;
+ default:
+ return -ENOTSUP;
+ }
+ return 0;
+}
+
+static void wctablet_chr_free(struct Chardev *chr)
+{
+ TabletChardev *tablet = WCTABLET_CHARDEV(chr);
+
+ qemu_input_handler_unregister(tablet->hs);
+ g_free(tablet);
+}
+
+static void wctablet_chr_open(Chardev *chr,
+ ChardevBackend *backend,
+ bool *be_opened,
+ Error **errp)
+{
+ TabletChardev *tablet = WCTABLET_CHARDEV(chr);
+
+ *be_opened = true;
+
+ /* init state machine */
+ memcpy(tablet->outbuf, WC_FULL_CONFIG_STRING, WC_FULL_CONFIG_STRING_LENGTH);
+ tablet->outlen = WC_FULL_CONFIG_STRING_LENGTH;
+ tablet->query_index = 0;
+
+ tablet->hs = qemu_input_handler_register((DeviceState *)tablet,
+ &wctablet_handler);
+}
+
+static void wctablet_chr_class_init(ObjectClass *oc, void *data)
+{
+ ChardevClass *cc = CHARDEV_CLASS(oc);
+
+ cc->open = wctablet_chr_open;
+ cc->chr_write = wctablet_chr_write;
+ cc->chr_ioctl = wctablet_chr_ioctl;
+ cc->chr_free = wctablet_chr_free;
+ cc->chr_accept_input = wctablet_chr_accept_input;
+}
+
+static const TypeInfo wctablet_type_info = {
+ .name = TYPE_CHARDEV_WCTABLET,
+ .parent = TYPE_CHARDEV,
+ .instance_size = sizeof(TabletChardev),
+ .class_init = wctablet_chr_class_init,
+};
+
+static const CharDriver wctablet_char_driver = {
+ .kind = CHARDEV_BACKEND_KIND_WCTABLET,
+};
+
+static void register_types(void)
+{
+ register_char_driver(&wctablet_char_driver);
+ type_register_static(&wctablet_type_info);
+}
+
+type_init(register_types);
diff --git a/docs/qdev-device-use.txt b/docs/qdev-device-use.txt
index 136d271..b059405 100644
--- a/docs/qdev-device-use.txt
+++ b/docs/qdev-device-use.txt
@@ -200,7 +200,7 @@ LEGACY-CHARDEV translates to -chardev HOST-OPTS... as follows:
* null becomes -chardev null
-* pty, msmouse, braille, stdio likewise
+* pty, msmouse, wctablet, braille, stdio likewise
* vc:WIDTHxHEIGHT becomes -chardev vc,width=WIDTH,height=HEIGHT
diff --git a/qapi-schema.json b/qapi-schema.json
index 82fabc6..d2257ee 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -4865,7 +4865,7 @@
#
# Configuration info for the new chardev backend.
#
-# Since: 1.4 (testdev since 2.2)
+# Since: 1.4 (testdev since 2.2, wctablet since 2.9)
##
{ 'union': 'ChardevBackend', 'data': { 'file' : 'ChardevFile',
'serial' : 'ChardevHostdev',
@@ -4877,6 +4877,7 @@
'null' : 'ChardevCommon',
'mux' : 'ChardevMux',
'msmouse': 'ChardevCommon',
+ 'wctablet' : 'ChardevCommon',
'braille': 'ChardevCommon',
'testdev': 'ChardevCommon',
'stdio' : 'ChardevStdio',
diff --git a/qemu-char.c b/qemu-char.c
index 6b4a299..dfb11c4 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -3698,6 +3698,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, "wctablet") == 0 ||
strcmp(filename, "braille") == 0 ||
strcmp(filename, "testdev") == 0 ||
strcmp(filename, "stdio") == 0) {
--
1.8.3.1
Hi On Wed, Feb 1, 2017 at 1:21 PM Gerd Hoffmann <kraxel@redhat.com> wrote: > From: Anatoli Huseu1 <avg.tolik@gmail.com> > > Add QEMU Wacom Penpartner serial tablet emulation. > GSoC 2016 project. > > Signed-off-by: Anatoli Huseu1 <avg.tolik@gmail.com> > > Various cleanups. > Add line speed tracking. > Implement ST and SP commands. > Adapted to chardev QOMification. > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > --- > Makefile.objs | 1 + > backends/Makefile.objs | 2 +- > backends/trace-events | 10 ++ > backends/wctablet.c | 374 > +++++++++++++++++++++++++++++++++++++++++++++++ > docs/qdev-device-use.txt | 2 +- > qapi-schema.json | 3 +- > qemu-char.c | 1 + > 7 files changed, 390 insertions(+), 3 deletions(-) > create mode 100644 backends/trace-events > create mode 100644 backends/wctablet.c > > diff --git a/Makefile.objs b/Makefile.objs > index 01cef86..5ac7f34 100644 > --- a/Makefile.objs > +++ b/Makefile.objs > @@ -124,6 +124,7 @@ trace-events-y += crypto/trace-events > trace-events-y += io/trace-events > trace-events-y += migration/trace-events > trace-events-y += block/trace-events > +trace-events-y += backends/trace-events > trace-events-y += hw/block/trace-events > trace-events-y += hw/char/trace-events > trace-events-y += hw/intc/trace-events > diff --git a/backends/Makefile.objs b/backends/Makefile.objs > index 1846998..0e0f156 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 testdev.o > +common-obj-y += msmouse.o wctablet.o testdev.o > common-obj-$(CONFIG_BRLAPI) += baum.o > baum.o-cflags := $(SDL_CFLAGS) > > diff --git a/backends/trace-events b/backends/trace-events > new file mode 100644 > index 0000000..8c3289a > --- /dev/null > +++ b/backends/trace-events > @@ -0,0 +1,10 @@ > +# See docs/tracing.txt for syntax documentation. > + > +# backends/wctablet.c > +wct_init(void) "" > +wct_cmd_re(void) "" > +wct_cmd_st(void) "" > +wct_cmd_sp(void) "" > +wct_cmd_ts(int input) "0x%02x" > +wct_cmd_other(const char *cmd) "%s" > +wct_speed(int speed) "%d" > diff --git a/backends/wctablet.c b/backends/wctablet.c > new file mode 100644 > index 0000000..7668254 > --- /dev/null > +++ b/backends/wctablet.c > @@ -0,0 +1,374 @@ > +/* > + * QEMU Wacom Penpartner serial tablet emulation > + * > + * some protocol details: > + * http://linuxwacom.sourceforge.net/wiki/index.php/Serial_Protocol_IV > + * > + * Copyright (c) 2016 Anatoli Huseu1 > + * Copyright (c) 2016,17 Gerd Hoffmann > + * > + * 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 <time.h> > + > +#include "qemu/osdep.h" > +#include "qemu-common.h" > +#include "sysemu/char.h" > +#include "ui/console.h" > +#include "ui/input.h" > +#include "trace.h" > + > + > +#define WC_OUTPUT_BUF_MAX_LEN 512 > +#define WC_COMMAND_MAX_LEN 60 > + > +#define WC_L7(n) ((n) & 127) > +#define WC_M7(n) (((n) >> 7) & 127) > +#define WC_H2(n) ((n) >> 14) > + > +#define WC_L4(n) ((n) & 15) > +#define WC_H4(n) (((n) >> 4) & 15) > + > +/* Model string and config string */ > +#define WC_MODEL_STRING_LENGTH 18 > +uint8_t WC_MODEL_STRING[WC_MODEL_STRING_LENGTH + 1] = > "~#CT-0045R,V1.3-5,"; > + > +#define WC_CONFIG_STRING_LENGTH 8 > +uint8_t WC_CONFIG_STRING[WC_CONFIG_STRING_LENGTH + 1] = "96,N,8,0"; > + > +#define WC_FULL_CONFIG_STRING_LENGTH 61 > +uint8_t WC_FULL_CONFIG_STRING[WC_FULL_CONFIG_STRING_LENGTH + 1] = { > + 0x5c, 0x39, 0x36, 0x2c, 0x4e, 0x2c, 0x38, 0x2c, > + 0x31, 0x28, 0x01, 0x24, 0x57, 0x41, 0x43, 0x30, > + 0x30, 0x34, 0x35, 0x5c, 0x5c, 0x50, 0x45, 0x4e, 0x5c, > + 0x57, 0x41, 0x43, 0x30, 0x30, 0x30, 0x30, 0x5c, > + 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x0d, 0x0a, > + 0x43, 0x54, 0x2d, 0x30, 0x30, 0x34, 0x35, 0x52, > + 0x2c, 0x56, 0x31, 0x2e, 0x33, 0x2d, 0x35, 0x0d, > + 0x0a, 0x45, 0x37, 0x29 > +}; > + > +/* This structure is used to save private info for Wacom Tablet. */ > +typedef struct { > + Chardev parent; > + QemuInputHandlerState *hs; > + > + /* Query string from serial */ > + uint8_t query[100]; > + int query_index; > + > + /* Command to be sent to serial port */ > + uint8_t outbuf[WC_OUTPUT_BUF_MAX_LEN]; > + int outlen; > + > + int line_speed; > + bool send_events; > + int axis[INPUT_AXIS__MAX]; > + bool btns[INPUT_BUTTON__MAX]; > + > +} TabletChardev; > + > +#define TYPE_CHARDEV_WCTABLET "chardev-wctablet" > +#define WCTABLET_CHARDEV(obj) \ > + OBJECT_CHECK(TabletChardev, (obj), TYPE_CHARDEV_WCTABLET) > + > + > +static void wctablet_chr_accept_input(Chardev *chr); > + > +static void wctablet_shift_input(TabletChardev *tablet, int count) > +{ > + tablet->query_index -= count; > + memmove(tablet->query, tablet->query + count, tablet->query_index); > + tablet->query[tablet->query_index] = 0; > +} > + > +static void wctablet_queue_output(TabletChardev *tablet, uint8_t *buf, > int count) > +{ > + if (tablet->outlen + count > sizeof(tablet->outbuf)) { > + return; > + } > + > + memcpy(tablet->outbuf + tablet->outlen, buf, count); > + tablet->outlen += count; > + wctablet_chr_accept_input(CHARDEV(tablet)); > +} > + > +static void wctablet_reset(TabletChardev *tablet) > +{ > + /* clear buffers */ > + tablet->query_index = 0; > + tablet->outlen = 0; > + /* reset state */ > + tablet->send_events = false; > +} > + > +static void wctablet_queue_event(TabletChardev *tablet) > +{ > + uint8_t codes[8] = { 0xe0, 0, 0, 0, 0, 0, 0 }; > + > + if (tablet->line_speed != 9600) { > + return; > + } > + > + int newX = tablet->axis[INPUT_AXIS_X] * 0.1537; > + int nexY = tablet->axis[INPUT_AXIS_Y] * 0.1152; > + > + codes[0] = codes[0] | WC_H2(newX); > + codes[1] = codes[1] | WC_M7(newX); > + codes[2] = codes[2] | WC_L7(newX); > + > + codes[3] = codes[3] | WC_H2(nexY); > + codes[4] = codes[4] | WC_M7(nexY); > + codes[5] = codes[5] | WC_L7(nexY); > + > + if (tablet->btns[INPUT_BUTTON_LEFT]) { > + codes[0] = 0xa0; > + } > + > + wctablet_queue_output(tablet, codes, 7); > +} > + > +static void wctablet_input_event(DeviceState *dev, QemuConsole *src, > + InputEvent *evt) > +{ > + TabletChardev *tablet = (TabletChardev *)dev; > + InputMoveEvent *move; > + InputBtnEvent *btn; > + > + switch (evt->type) { > + case INPUT_EVENT_KIND_ABS: > + move = evt->u.abs.data; > + tablet->axis[move->axis] = move->value; > + break; > + > + case INPUT_EVENT_KIND_BTN: > + btn = evt->u.btn.data; > + tablet->btns[btn->button] = btn->down; > + break; > + > + default: > + /* keep gcc happy */ > + break; > + } > +} > + > +static void wctablet_input_sync(DeviceState *dev) > +{ > + TabletChardev *tablet = (TabletChardev *)dev; > + > + if (tablet->send_events) { > + wctablet_queue_event(tablet); > + } > +} > + > +static QemuInputHandler wctablet_handler = { > + .name = "QEMU Wacome Pen Tablet", > + .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, > + .event = wctablet_input_event, > + .sync = wctablet_input_sync, > +}; > + > +static void wctablet_chr_accept_input(Chardev *chr) > +{ > + TabletChardev *tablet = WCTABLET_CHARDEV(chr); > + int len, canWrite; > + > + canWrite = qemu_chr_be_can_write(chr); > + len = canWrite; > + if (len > tablet->outlen) { > + len = tablet->outlen; > + } > + > + if (len) { > + qemu_chr_be_write(chr, tablet->outbuf, len); > + tablet->outlen -= len; > + if (tablet->outlen) { > + memmove(tablet->outbuf, tablet->outbuf + len, tablet->outlen); > + } > + } > +} > + > +static int wctablet_chr_write(struct Chardev *chr, > + const uint8_t *buf, int len) > +{ > + TabletChardev *tablet = WCTABLET_CHARDEV(chr); > + unsigned int i, clen; > + char *pos; > + > + if (tablet->line_speed != 9600) { > + return len; > + } > + for (i = 0; i < len && tablet->query_index < sizeof(tablet->query) - > 1; i++) { > + tablet->query[tablet->query_index++] = buf[i]; > + } > + tablet->query[tablet->query_index] = 0; > + > + while (tablet->query_index > 0 && (tablet->query[0] == '@' || > + tablet->query[0] == '\r' || > + tablet->query[0] == '\n')) { > + wctablet_shift_input(tablet, 1); > + } > + if (!tablet->query_index) { > + return len; > + } > + > + if (strncmp((char *)tablet->query, "~#", 2) == 0) { > + /* init / detect sequence */ > + trace_wct_init(); > + wctablet_shift_input(tablet, 2); > + wctablet_queue_output(tablet, WC_MODEL_STRING, > + WC_MODEL_STRING_LENGTH); > + return len; > + } > + > + /* detect line */ > + pos = strchr((char *)tablet->query, '\r'); > + if (!pos) { > + pos = strchr((char *)tablet->query, '\n'); > + } > + if (!pos) { > + return len; > + } > + clen = pos - (char *)tablet->query; > + > + /* process commands */ > + if (strncmp((char *)tablet->query, "RE", 2) == 0 && > + clen == 2) { > + trace_wct_cmd_re(); > + wctablet_shift_input(tablet, 3); > + wctablet_queue_output(tablet, WC_CONFIG_STRING, > + WC_CONFIG_STRING_LENGTH); > + > + } else if (strncmp((char *)tablet->query, "ST", 2) == 0 && > + clen == 2) { > + trace_wct_cmd_st(); > + wctablet_shift_input(tablet, 3); > + tablet->send_events = true; > + wctablet_queue_event(tablet); > + > + } else if (strncmp((char *)tablet->query, "SP", 2) == 0 && > + clen == 2) { > + trace_wct_cmd_sp(); > + wctablet_shift_input(tablet, 3); > + tablet->send_events = false; > + > + } else if (strncmp((char *)tablet->query, "TS", 2) == 0 && > + clen == 3) { > + unsigned int input = tablet->query[2]; > + uint8_t codes[7] = { > + 0xa3, > + ((input & 0x80) == 0) ? 0x7e : 0x7f, > + (((WC_H4(input) & 0x7) ^ 0x5) << 4) | (WC_L4(input) ^ 0x7), > + 0x03, > + 0x7f, > + 0x7f, > + 0x00, > + }; > + trace_wct_cmd_ts(input); > + wctablet_shift_input(tablet, 4); > + wctablet_queue_output(tablet, codes, 7); > + > + } else { > + tablet->query[clen] = 0; /* terminate line for printing */ > + trace_wct_cmd_other((char *)tablet->query); > + wctablet_shift_input(tablet, clen + 1); > + > + } > + > + return len; > +} > + > +static int wctablet_chr_ioctl(Chardev *chr, int cmd, void *arg) > +{ > + TabletChardev *tablet = WCTABLET_CHARDEV(chr); > + QEMUSerialSetParams *ssp; > + > + switch (cmd) { > + case CHR_IOCTL_SERIAL_SET_PARAMS: > + ssp = arg; > + if (tablet->line_speed != ssp->speed) { > + trace_wct_speed(ssp->speed); > + wctablet_reset(tablet); > + tablet->line_speed = ssp->speed; > + } > + break; > + default: > + return -ENOTSUP; > + } > + return 0; > +} > + > +static void wctablet_chr_free(struct Chardev *chr) > +{ > + TabletChardev *tablet = WCTABLET_CHARDEV(chr); > + > + qemu_input_handler_unregister(tablet->hs); > + g_free(tablet); > +} > + > +static void wctablet_chr_open(Chardev *chr, > + ChardevBackend *backend, > + bool *be_opened, > + Error **errp) > +{ > + TabletChardev *tablet = WCTABLET_CHARDEV(chr); > + > + *be_opened = true; > + > + /* init state machine */ > + memcpy(tablet->outbuf, WC_FULL_CONFIG_STRING, > WC_FULL_CONFIG_STRING_LENGTH); > + tablet->outlen = WC_FULL_CONFIG_STRING_LENGTH; > + tablet->query_index = 0; > + > + tablet->hs = qemu_input_handler_register((DeviceState *)tablet, > + &wctablet_handler); > +} > + > +static void wctablet_chr_class_init(ObjectClass *oc, void *data) > +{ > + ChardevClass *cc = CHARDEV_CLASS(oc); > + > + cc->open = wctablet_chr_open; > + cc->chr_write = wctablet_chr_write; > + cc->chr_ioctl = wctablet_chr_ioctl; > + cc->chr_free = wctablet_chr_free; > chr_free is going away in the last pull request, you can replace it with qom finalize. > + cc->chr_accept_input = wctablet_chr_accept_input; > +} > + > +static const TypeInfo wctablet_type_info = { > + .name = TYPE_CHARDEV_WCTABLET, > + .parent = TYPE_CHARDEV, > + .instance_size = sizeof(TabletChardev), > + .class_init = wctablet_chr_class_init, > +}; > + > +static const CharDriver wctablet_char_driver = { > + .kind = CHARDEV_BACKEND_KIND_WCTABLET, > +}; > + > +static void register_types(void) > +{ > + register_char_driver(&wctablet_char_driver); > + type_register_static(&wctablet_type_info); > +} > + > +type_init(register_types); > diff --git a/docs/qdev-device-use.txt b/docs/qdev-device-use.txt > index 136d271..b059405 100644 > --- a/docs/qdev-device-use.txt > +++ b/docs/qdev-device-use.txt > @@ -200,7 +200,7 @@ LEGACY-CHARDEV translates to -chardev HOST-OPTS... as > follows: > > * null becomes -chardev null > > -* pty, msmouse, braille, stdio likewise > +* pty, msmouse, wctablet, braille, stdio likewise > > * vc:WIDTHxHEIGHT becomes -chardev vc,width=WIDTH,height=HEIGHT > > diff --git a/qapi-schema.json b/qapi-schema.json > index 82fabc6..d2257ee 100644 > --- a/qapi-schema.json > +++ b/qapi-schema.json > @@ -4865,7 +4865,7 @@ > # > # Configuration info for the new chardev backend. > # > -# Since: 1.4 (testdev since 2.2) > +# Since: 1.4 (testdev since 2.2, wctablet since 2.9) > ## > { 'union': 'ChardevBackend', 'data': { 'file' : 'ChardevFile', > 'serial' : 'ChardevHostdev', > @@ -4877,6 +4877,7 @@ > 'null' : 'ChardevCommon', > 'mux' : 'ChardevMux', > 'msmouse': 'ChardevCommon', > + 'wctablet' : 'ChardevCommon', > 'braille': 'ChardevCommon', > 'testdev': 'ChardevCommon', > 'stdio' : 'ChardevStdio', > diff --git a/qemu-char.c b/qemu-char.c > index 6b4a299..dfb11c4 100644 > --- a/qemu-char.c > +++ b/qemu-char.c > @@ -3698,6 +3698,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, "wctablet") == 0 || > strcmp(filename, "braille") == 0 || > strcmp(filename, "testdev") == 0 || > strcmp(filename, "stdio") == 0) { > -- > 1.8.3.1 > > > -- Marc-André Lureau
Hi, > + cc->chr_free = wctablet_chr_free; > > > chr_free is going away in the last pull request, you can replace it > with qom finalize. Yep, saw the patches already, rebased now, v4 is on the way. cheers, Gerd
© 2016 - 2024 Red Hat, Inc.