--- /dev/null
+/*
+ * QEMU dom0 /dev/input driver
+ *
+ * Copyright (c) 2008 Citrix Systems
+ *
+ * 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 "qemu-common.h"
+#include "console.h"
+#include "qemu-timer.h"
+
+#include <sys/types.h>
+#include <linux/input.h>
+#include <linux/kd.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+
+#define DEBUG_DOM0_DRIVER 1
+#define EVENT_PATH "/dev/input/event"
+#define XS_REFRESH_INTERVAL 100
+
+#if DEBUG_DOM0_DRIVER
+# define DEBUG(_format_, args...) \
+ fprintf(stderr, "dom0_driver(%d):%d: " _format_, domid, __LINE__, ## args)
+#else
+# define DEBUG(_format_, args...) (void)0
+#endif
+
+#define INTEL_FB_PAGES ((16 * 1024 * 1024) / (4 * 1024))
+#define INTEL_FB_BASE 0xd8000
+
+static void dom0_read(void *opaque);
+
+enum dom0Direction
+{
+ DOM0_LEFT,
+ DOM0_TOP,
+ DOM0_CENTER,
+ DOM0_BOTTOM,
+ DOM0_RIGHT,
+ DOM0_OUT
+};
+
+static const char *str2pos[] =
+{
+ "left", "top", "center", "bottom", "right"
+};
+
+struct dom0Driver
+{
+ int *event_fds;
+ int event_nb;
+ int mouse_button_state;
+ QEMUTimer *xs_timer;
+ uint8_t scroll_lock_count;
+};
+
+static struct dom0Driver driver;
+
+static void dom0_update(DisplayState *ds, int x, int y, int w, int h)
+{
+}
+
+static void dom0_refresh(DisplayState *ds)
+{
+}
+
+static int dom0_safe_switch(int keycode)
+{
+ if (driver.scroll_lock_count == 3)
+ {
+ switch (keycode)
+ {
+ case KEY_LEFT:
+ xenstore_write_dom0_driver("leaving-dir", DOM0_LEFT);
+ break;
+ case KEY_RIGHT:
+ xenstore_write_dom0_driver("leaving-dir", DOM0_RIGHT);
+ break;
+ case KEY_DOWN:
+ xenstore_write_dom0_driver("leaving-dir", DOM0_BOTTOM);
+ break;
+ case KEY_UP:
+ xenstore_write_dom0_driver("leaving-dir", DOM0_TOP);
+ break;
+ default:
+ return 0;
+ }
+ xenstore_write_dom0_driver("state", 0);
+ driver.scroll_lock_count = 0;
+ return 1;
+ }
+
+ if (keycode == KEY_SCROLLLOCK)
+ {
+ driver.scroll_lock_count++;
+ DEBUG("scroll count %d\n", driver.scroll_lock_count);
+ }
+ else
+ driver.scroll_lock_count = 0;
+ return 0;
+}
+
+static void dom0_key_event(int down, uint32_t keycode)
+{
+ int shift_keys = 0;
+ uint8_t esc = 0;
+
+ if (down && dom0_safe_switch(keycode))
+ return;
+
+ switch (keycode)
+ {
+ case KEY_F11: keycode = 0X57; break; /* F11 */
+ case KEY_F12: keycode = 0X58; break; /* F12 */
+ case KEY_INSERT: keycode = 0X52; break;
+ case KEY_HOME: keycode = 0X47; break;
+ case KEY_PAGEUP: keycode = 0X49; break;
+ case KEY_DELETE: keycode = 0X53; break;
+ case KEY_END: keycode = 0X4F; break;
+ case KEY_PAGEDOWN: keycode = 0x51; break;
+ case KEY_UP: keycode = 0X48; break;
+ case KEY_LEFT: keycode = 0X4B; break;
+ case KEY_DOWN: keycode = 0X50; break;
+ case KEY_RIGHT: keycode = 0X4D; break;
+ case KEY_RIGHTALT: keycode = 0x38 | 0x80; break;
+ case KEY_LEFTMETA: keycode = 0x5B | 0x80; break;
+ case KEY_RIGHTMETA: keycode = 0x5C | 0x80; break;
+ }
+
+ if (keycode & 0x80)
+ kbd_put_keycode(0xe0);
+
+ if (down)
+ kbd_put_keycode(keycode | 0x80);
+ else
+ kbd_put_keycode(keycode & 0x7f);
+}
+
+static void dom0_get_positions(int *positions)
+{
+ int *domids = NULL;
+ int num;
+ char *tmp;
+ unsigned int len;
+ int pos = 0;
+
+ for (int i = 0; i < DOM0_OUT; i++)
+ positions[i] = 0;
+
+ /* Get all the positions */
+ domids = xenstore_get_domids(&num);
+ for (int i = 0; i < num; i++)
+ if (domids[i])
+ {
+ if (!(tmp = xenstore_dom_read(domids[i], "dom0_input", &len)))
+ continue;
+ pos = strtol(tmp, NULL, 10);
+ free(tmp);
+ assert(pos >= 0 && pos <= 5);
+
+ positions[pos] = domids[i];
+ }
+ free(domids);
+ for (pos = 0; pos < DOM0_OUT; pos++)
+ DEBUG("pos %d -> %d\n", pos, positions[pos]);
+}
+
+static int dom0_get_next_domid(int dir, int id)
+{
+ int pos = 0, next_pos = 0;
+ int positions[6];
+
+ dom0_get_positions(positions);
+ /* Find current domain pos */
+ for (pos = 0; pos < DOM0_OUT; pos++)
+ if (positions[pos] && positions[pos] == id)
+ break;
+ assert(pos != DOM0_OUT);
+
+ if (pos == DOM0_LEFT && dir == DOM0_RIGHT)
+ next_pos = DOM0_CENTER;
+ else if (pos == DOM0_RIGHT && dir == DOM0_LEFT)
+ next_pos = DOM0_CENTER;
+ else if (pos == DOM0_TOP && dir == DOM0_BOTTOM)
+ next_pos = DOM0_CENTER;
+ else if (pos == DOM0_BOTTOM && dir == DOM0_TOP)
+ next_pos = DOM0_CENTER;
+ else if (pos == DOM0_CENTER && dir != DOM0_CENTER)
+ next_pos = dir;
+
+ if (next_pos == 0)
+ next_pos = pos;
+
+ DEBUG("positions[next_pos] %d, id %d\n", positions[next_pos], id);
+ if (!positions[next_pos])
+ return domid;
+ else
+ return positions[next_pos];
+}
+
+static void dom0_read(void *opaque)
+{
+ struct input_event event[5];
+ int i = 0;
+ int read_sz = 0;
+ int fd = *(int *)opaque;
+
+ read_sz = read(fd, event, sizeof (event));
+ for (i = 0; i < read_sz / (sizeof (struct input_event)); i++)
+ {
+ if (event[i].type == EV_KEY)
+ {
+ if (event[i].code >= BTN_MOUSE)
+ {
+ /* Mouse Key */
+ int type = 0;
+
+ switch(event[i].code)
+ {
+ case BTN_LEFT: type = MOUSE_EVENT_LBUTTON; break;
+ case BTN_RIGHT: type = MOUSE_EVENT_RBUTTON; break;
+ case BTN_MIDDLE: type = MOUSE_EVENT_MBUTTON; break;
+ }
+
+ if (event[i].value)
+ driver.mouse_button_state |= type;
+ else
+ driver.mouse_button_state &= ~type;
+ kbd_mouse_event(0, 0, 0, driver.mouse_button_state);
+ }
+ else
+ dom0_key_event(event[i].value == 0, event[i].code);
+ }
+
+ if (event[i].type == EV_REL)
+ {
+ /* Mouse motion */
+ int x = 0, y = 0, z = 0;
+
+ switch (event[i].code)
+ {
+ case REL_X : x = event[i].value; break;
+ case REL_Y : y = event[i].value; break;
+ case REL_WHEEL : z = -event[i].value; break;
+ }
+
+ kbd_mouse_event(x, y, z, driver.mouse_button_state);
+ }
+ }
+}
+
+static void dom0_gr_devices(int grab)
+{
+ char *tmp;
+ int id;
+
+ for (int i = 0; i < driver.event_nb; i++)
+ {
+ if (ioctl(driver.event_fds[i], EVIOCGRAB, grab) == -1)
+ {
+ DEBUG("IOCTL failed !\n");
+ return;
+ }
+ if (grab)
+ qemu_set_fd_handler(driver.event_fds[i], dom0_read, NULL,
+ &driver.event_fds[i]);
+ else
+ qemu_set_fd_handler(driver.event_fds[i], NULL, NULL,
+ &driver.event_fds[i]);
+ }
+ xenstore_write_dom0_driver("state", grab);
+ xenstore_write_dom0_driver("domid", domid);
+}
+
+static int dom0_dom_alive(int id)
+{
+ int *domids;
+ int len, i;
+
+ domids = xenstore_get_domids(&len);
+ if (domids == NULL)
+ return 0;
+ for (i = 0; i < len; i++)
+ if (domids[i] == id)
+ break;
+ free(domids);
+ return (i < len);
+}
+
+static void dom0_probe_xenstore(void *opaque)
+{
+ char *tmp = NULL;
+ char *dir = NULL;
+ int state = 0;
+ int cur_domid, next_domid;
+
+ if (!(tmp = xenstore_read_dom0_driver("state")))
+ {
+ DEBUG("No state grab the device\n");
+ dom0_gr_devices(1);
+ goto out;
+ }
+ state = strtol(tmp, NULL, 10);
+ free(tmp);
+
+ if (!(tmp = xenstore_read_dom0_driver("domid")))
+ {
+ dom0_gr_devices(1);
+ goto out;
+ }
+ cur_domid = strtol(tmp, NULL, 10);
+ free(tmp);
+
+ /* The domain which has the focus crash */
+ if (!dom0_dom_alive(cur_domid))
+ {
+ DEBUG("steal the focus from %d\n", cur_domid);
+ dom0_gr_devices(1);
+ goto out;
+ }
+
+ if (state == 0) /* loosing the focus */
+ {
+ if (!(dir = xenstore_read_dom0_driver("leaving-dir")))
+ goto out;
+ next_domid = dom0_get_next_domid(strtol(dir, NULL, 10), cur_domid);
+ DEBUG("leaving-dir %ld\n", strtol(dir, NULL, 10));
+ DEBUG("next_domid is %d\n", next_domid);
+ DEBUG("cur_domid is %d\n", cur_domid);
+
+ if (cur_domid != next_domid)
+ {
+ if (cur_domid == domid)
+ {
+ DEBUG("release devices\n");
+ dom0_gr_devices(0);
+ }
+ if (next_domid == domid)
+ {
+ DEBUG("grab devices\n");
+ dom0_gr_devices(1);
+ }
+ }
+ else
+ xenstore_write_dom0_driver("state", 1);
+ }
+
+out:
+ qemu_mod_timer(driver.xs_timer, qemu_get_clock(rt_clock) + XS_REFRESH_INTERVAL);
+}
+
+static void dom0_driver_event_init(const char *position)
+{
+ char dev_name[strlen(EVENT_PATH) + 3];
+ int fd = -1;
+ int pos = 0, i = 0;
+ int positions[6];
+ char str[10];
+
+ do
+ {
+ snprintf(dev_name, sizeof (dev_name), "%s%d", EVENT_PATH, i++);
+ if ((fd = open(dev_name, O_RDONLY)) == -1)
+ break;
+
+ if (!(driver.event_fds = realloc(driver.event_fds,
+ (driver.event_nb + 1) * sizeof (int))))
+ {
+ DEBUG("memory allocation failed\n");
+ exit(1);
+ }
+
+ driver.event_fds[driver.event_nb] = fd;
+ driver.event_nb++;
+
+ DEBUG("XXX Using %s\n", dev_name);
+ }
+ while (1);
+
+ if (driver.event_nb == 0)
+ return;
+
+ /* Register position */
+ DEBUG("Register the position inside xenstore\n");
+ for (pos = 0; pos < DOM0_OUT; pos++)
+ if (strcmp(str2pos[pos], position) == 0)
+ break;
+ DEBUG("Get the position %s\n", str2pos[pos]);
+ if (pos == DOM0_OUT)
+ {
+ DEBUG("position must be left, right, center, top or bottom\n");
+ exit(1);
+ }
+ dom0_get_positions(positions);
+ if (positions[pos])
+ {
+ DEBUG("There is already a vm at this position\n");
+ exit(1);
+ }
+ else
+ {
+ snprintf(str, 9, "%d", pos);
+ DEBUG("register dom0_input %s\n", str);
+ xenstore_dom_write(domid, "dom0_input", str);
+ }
+
+ /* Register the xenstore probing */
+ driver.xs_timer = qemu_new_timer(rt_clock, dom0_probe_xenstore, NULL);
+ qemu_mod_timer(driver.xs_timer, qemu_get_clock(rt_clock) + XS_REFRESH_INTERVAL);
+}
+
+static void dom0_driver_cleanup(void)
+{
+ char *tmp;
+ int cur_domid, state;
+
+ free(driver.event_fds);
+
+ if (!(tmp = xenstore_read_dom0_driver("domid")))
+ return;
+ cur_domid = strtol(tmp, NULL, 10);
+ free(tmp);
+
+ if (!(tmp = xenstore_read_dom0_driver("state")))
+ return;
+ state = strtol(tmp, NULL, 10);
+ free(tmp);
+
+ if (cur_domid == domid && state == 1)
+ xenstore_write_dom0_driver("state", 0);
+
+}
+
+void dom0_driver_init(DisplayState *ds, const char *position)
+{
+ memset(&driver, 0, sizeof (driver));
+ dom0_driver_event_init(position);
+ ds->data = NULL;
+ ds->linesize = 0;
+ ds->depth = 0;
+ ds->dpy_update = dom0_update;
+ ds->dpy_refresh = dom0_refresh;
+
+ atexit(dom0_driver_cleanup);
+}