]> xenbits.xensource.com Git - xenclient/ioemu.git/commitdiff
- Add the dom0_input qemu driver
authorJean Guyader <jean.guyader@eu.citrix.com>
Thu, 9 Oct 2008 17:12:30 +0000 (18:12 +0100)
committerVincent Hanquez <vincent@snarc.org>
Fri, 31 Oct 2008 12:10:55 +0000 (12:10 +0000)
Makefile.target
dom0_driver.c [new file with mode: 0644]

index 577703366fce404b22fe1076de83ac729683e625..6e3096d6e9a1a2b7da38987a15c350f46fbd6753 100644 (file)
@@ -652,6 +652,9 @@ ifdef CONFIG_COREAUDIO
 COCOA_LIBS+=-framework CoreAudio
 endif
 endif
+
+OBJS+=dom0_driver.o
+
 ifdef CONFIG_SLIRP
 CPPFLAGS+=-I$(SRC_PATH)/slirp
 endif
diff --git a/dom0_driver.c b/dom0_driver.c
new file mode 100644 (file)
index 0000000..e97b467
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * 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);
+}