From 4808b8cb1c76a79439b24baf47bd60779b434ecf Mon Sep 17 00:00:00 2001 From: Dietmar Hahn Date: Wed, 24 Mar 2010 11:14:51 +0100 Subject: [PATCH] Simple emulation of host keyboard and mouse for gfx_passthru. Currently the input to HVM guest using gfx_passthru with primary gfx-card is only possible via usb kbd/mouse. This patch adds a simple emulation of the host PS/2 kbd/mouse. To activate set gfx_passthru=1001 in config file. Signed-off-by: Dietmar Hahn --- i386-dm/gfx_host_kbdmouse.c | 331 +++++++++++++++++++++++++++++++++++++++++++ i386-dm/helper2.c | 7 + vl.c | 5 +- 3 files changed, 342 insertions(+), 1 deletions(-) create mode 100644 i386-dm/gfx_host_kbdmouse.c diff --git a/i386-dm/gfx_host_kbdmouse.c b/i386-dm/gfx_host_kbdmouse.c new file mode 100644 index 0000000..5e5fd1a --- /dev/null +++ b/i386-dm/gfx_host_kbdmouse.c @@ -0,0 +1,331 @@ +/* + * Emulation of PS/2 - keyboard and mouse for gfx_passthru. + * Keyboard: + * The emulation opens the next free VT device and set it to active. + * This device is used for keyboard input. The keyboard events are + * sent to the i8042 emulation in the qemu. + * Mouse: + * In this simple version /dev/mice is used as input for mouse events + * sent to the i8042 emulation too. + * + * Some code is taken from xorg-server-1.7.1 and xorg-x11-driver-input-7.4. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "console.h" + +/* In vl.c together with gfx_passthru. */ +extern int gfx_host_kbdmouse; + +static fd_set gfx_host_fdset, gfx_host_rset; +static int maxfd=0; + +static int sleep_cnt; + +static int gfx_host_kbd_fd = -1; +static int gfx_host_kbd_mode_save; +static struct termios gfx_host_kbd_termios_save; +int gfx_host_kbd_vtno, gfx_host_kbd_old_vtno; + +static int gfx_host_mouse_fd = -1; + +static int +gfx_host_file_set_nonblock(int fd) +{ + int flags; + flags = fcntl (fd, F_GETFL); + flags |= O_ASYNC | O_NONBLOCK; + if (fcntl(fd, F_SETFL, flags) == -1) { + fprintf(logfile, "fcntl(F_SETFL) failed: %d\n", errno); + return 0; + } + return 1; +} + +static void gfx_host_file_reset_nonblock(int fd) +{ + int flags; + flags = fcntl(fd, F_GETFL); + flags &= ~(O_ASYNC | O_NONBLOCK); + fcntl(fd, F_SETFL, flags); +} + +static void gfx_host_fd_add(int fd) +{ + FD_SET(fd, &gfx_host_fdset); + if (fd > maxfd) + maxfd = fd; /* max fd for select() */ +} + +static void gfx_host_fd_remove(int fd) +{ + FD_CLR(fd, &gfx_host_fdset); +} + +/* Keyboard/mouse signal handler */ +static void gfx_host_kbd_mouse_read(int sig) +{ + unsigned char buf[256]; + int n, i; + sleep_cnt = 5; + errno = 0; + + gfx_host_rset = gfx_host_fdset; /* gfx_host_rset gets modified each time around */ + if ( (n = select(maxfd + 1, &gfx_host_rset, NULL, NULL, NULL)) < 0) { + fprintf(logfile, "%s: select() failed: errno: %d\n", __func__, errno); + return; + } + if (FD_ISSET(gfx_host_kbd_fd, &gfx_host_rset)) { /* read kbd buffer */ + if ((n = read (gfx_host_kbd_fd, buf, sizeof (buf))) == -1) { + if (errno != EAGAIN) + fprintf(logfile, "%s-kbd: read failed: %d\n", __func__, errno); + } + if (n != 0) { + for (i=0; i 0) + ; + gfx_host_fd_add(gfx_host_kbd_fd); + return 1; +} + +static int gfx_host_mouse_enable(void) +{ + unsigned char buf[256]; + + if (fcntl (gfx_host_mouse_fd, F_SETOWN, getpid()) == -1) { + fprintf(logfile, "%s: fcntl(F_SETOWN) failed: %d\n", __func__, errno); + return 0; + } + if (!gfx_host_file_set_nonblock(gfx_host_mouse_fd)) + return 0; + + while (read(gfx_host_mouse_fd, buf, sizeof (buf)) > 0) + ; + gfx_host_fd_add(gfx_host_mouse_fd); + return 1; +} + +static void gfx_host_mouse_disable(void) +{ + gfx_host_file_reset_nonblock(gfx_host_mouse_fd); + gfx_host_fd_remove(gfx_host_mouse_fd); +} + +static void gfx_host_kbd_disable(void) +{ + struct sigaction act; + + gfx_host_file_reset_nonblock(gfx_host_kbd_fd); + memset (&act, '\0', sizeof act); + act.sa_handler = SIG_IGN; + sigemptyset (&act.sa_mask); + sigaction (SIGIO, &act, 0); + gfx_host_fd_remove(gfx_host_kbd_fd); + tcsetattr(gfx_host_kbd_fd, TCSANOW, &gfx_host_kbd_termios_save); + ioctl(gfx_host_kbd_fd, KDSKBMODE, gfx_host_kbd_mode_save); +} + +static void gfx_host_kbd_alloc(void) +{ + struct vt_mode VT; + struct vt_stat vts; + const char* chktty = "/dev/tty0"; + const char* mytty = "/dev/tty%d"; + static char vtname[11]; + + if ((gfx_host_kbd_fd = open(chktty, O_RDWR|O_NDELAY, 0)) == -1) { + fprintf(logfile, "open(%s) failed: %d\n", chktty, errno); + exit(2); + } + if (ioctl(gfx_host_kbd_fd, VT_GETSTATE, &vts) < 0) { + fprintf(logfile, "ioctl daneben"); + goto failed; + } + gfx_host_kbd_old_vtno = vts.v_active; + + if ((ioctl(gfx_host_kbd_fd, VT_OPENQRY, &gfx_host_kbd_vtno) < 0) || + (gfx_host_kbd_vtno == -1)) { + fprintf(logfile, "No free VT"); + goto failed; + } + + close(gfx_host_kbd_fd); + sprintf(vtname, mytty, gfx_host_kbd_vtno); + + if ((gfx_host_kbd_fd = open(vtname, O_RDWR|O_NDELAY, 0)) == -1) { + fprintf(logfile, "open(%s) failed: %d\n", vtname, errno); + exit(2); + } + if (ioctl(gfx_host_kbd_fd, VT_ACTIVATE, gfx_host_kbd_vtno) < 0) { + fprintf(logfile, "ioctl(VT_ACTIVATE) failed: %d\n", errno); + goto failed; + } + if (ioctl(gfx_host_kbd_fd, VT_WAITACTIVE, gfx_host_kbd_vtno) < 0) { + printf("ioctl(VT_WAITACTIVE) failed: %d\n", errno); + fprintf(logfile, "ioctl(VT_WAITACTIVE) failed: %d\n", errno); + goto failed; + } + fprintf(logfile, "gfx_host_kbdmous runs on tty%d\n", gfx_host_kbd_vtno); + return; +failed: + close(gfx_host_kbd_fd); + exit(2); +} + +static void gfx_host_kbd_release(void) +{ + struct vt_stat vts; + /* Restore old VT */ + if (ioctl(gfx_host_kbd_fd, VT_ACTIVATE, gfx_host_kbd_old_vtno) < 0) { + fprintf(logfile, "%s: ioctl(VT_ACTIVATE) failed: %d\n", + __func__, errno); + close(gfx_host_kbd_fd); + exit(2); + } + if (ioctl(gfx_host_kbd_fd, VT_WAITACTIVE, gfx_host_kbd_old_vtno) < 0) { + fprintf(logfile, "%s: ioctl(VT_WAITACTIVE) failed: %d\n", + __func__, errno); + close(gfx_host_kbd_fd); + exit(2); + } + close(gfx_host_kbd_fd); + gfx_host_kbd_fd = open ("/dev/tty0", O_RDWR|O_NDELAY, 0); + if (gfx_host_kbd_fd < 0) + fprintf(logfile, "%s: open(dev/tty0) failed: %d\n", __func__, errno); + else { + if (gfx_host_kbd_fd >= 0) { + if (ioctl(gfx_host_kbd_fd, VT_DISALLOCATE, gfx_host_kbd_vtno) < 0) + fprintf(logfile, "%s: ioctl(VT_DISALLOCATE) failed: %d\n", + __func__, errno); + close (gfx_host_kbd_fd); + } + } + fprintf(logfile, "gfx_host_kbdmouse: restored orig active tty%d\n", + gfx_host_kbd_old_vtno); +} + +static int +gfx_host_mouse_alloc(void) +{ + const char *name = "/dev/input/mice"; + int i; + + if ((gfx_host_mouse_fd = open(name, O_RDWR|O_NDELAY, 0)) == -1) { + fprintf(logfile, "open(%s) failed: %d\n", name, errno); + return 0; + } + return 1; +} + +static void +gfx_host_mouse_release(void) +{ + close(gfx_host_mouse_fd); +} + +static void +gfx_host_kbdmouse_init(void) +{ + FD_ZERO(&gfx_host_fdset); + gfx_host_kbd_alloc(); + if (!gfx_host_mouse_alloc()) { + gfx_host_kbd_release(); + return; + } + if (! gfx_host_kbd_enable()) { + gfx_host_kbd_release(); + gfx_host_mouse_release(); + return; + } + gfx_host_mouse_enable(); /* Ignore failing mouse. */ +} + +static void gfx_host_kbdmouse_close(void) +{ + gfx_host_mouse_disable(); + gfx_host_kbd_disable(); + gfx_host_mouse_release(); + gfx_host_kbd_release(); +} + diff --git a/i386-dm/helper2.c b/i386-dm/helper2.c index 986df3c..9ce1da3 100644 --- a/i386-dm/helper2.c +++ b/i386-dm/helper2.c @@ -61,6 +61,8 @@ #include "sysemu.h" #include "qemu-xen.h" +#include "gfx_host_kbdmouse.c" + //#define DEBUG_MMU #ifdef USE_CODE_COPY @@ -536,6 +538,8 @@ static void cpu_handle_ioreq(void *opaque) if (qemu_shutdown_requested()) { fprintf(logfile, "shutdown requested in cpu_handle_ioreq\n"); destroy_hvm_domain(); + if (gfx_host_kbdmouse) + gfx_host_kbdmouse_close(); } if (qemu_reset_requested()) { fprintf(logfile, "reset requested in cpu_handle_ioreq.\n"); @@ -566,6 +570,9 @@ int main_loop(void) if (evtchn_fd != -1) qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, env); + if (gfx_host_kbdmouse) + gfx_host_kbdmouse_init(); + xenstore_record_dm_state("running"); qemu_set_fd_handler(xenstore_fd(), xenstore_process_event, NULL, NULL); diff --git a/vl.c b/vl.c index 16de102..510a4f0 100644 --- a/vl.c +++ b/vl.c @@ -216,6 +216,7 @@ int cirrus_vga_enabled = 1; int std_vga_enabled = 0; int vmsvga_enabled = 0; int gfx_passthru = 0; +int gfx_host_kbdmouse = 0; #ifdef TARGET_SPARC int graphic_width = 1024; int graphic_height = 768; @@ -4452,7 +4453,7 @@ static const QEMUOption qemu_options[] = { #endif { "acpi", 0, QEMU_OPTION_acpi }, /* deprecated, for xend compatibility */ { "direct_pci", HAS_ARG, QEMU_OPTION_direct_pci }, - { "gfx_passthru", 0, QEMU_OPTION_gfx_passthru}, + { "gfx_passthru", HAS_ARG, QEMU_OPTION_gfx_passthru}, { "pciemulation", HAS_ARG, QEMU_OPTION_pci_emulation }, { "vncunused", 0, QEMU_OPTION_vncunused }, { "vcpus", HAS_ARG, QEMU_OPTION_vcpus }, @@ -5548,6 +5549,8 @@ int main(int argc, char **argv, char **envp) break; case QEMU_OPTION_gfx_passthru: select_vgahw("passthrough"); + if (atoi(optarg) >= 1000) + gfx_host_kbdmouse = 1; break; } } -- 1.6.4.2