All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/5] fbdev display driver + misc bits
@ 2010-06-15 10:05 Gerd Hoffmann
  2010-06-15 10:05 ` [Qemu-devel] [PATCH 1/5] QLIST-ify display change listeners Gerd Hoffmann
                   ` (5 more replies)
  0 siblings, 6 replies; 14+ messages in thread
From: Gerd Hoffmann @ 2010-06-15 10:05 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

  Hi,

This patch series features the linux fbdev display driver and a few
more patches the fbdev driver depends on.  Most of the patches have been
on the list before.  If you wondered what they are good for -- here is
the big picture ;)

As usual the individual patches carry more verbose descriptions.

cheers,
  Gerd

Gerd Hoffmann (5):
  QLIST-ify display change listeners.
  add unregister_displaychangelistener
  Fix and simplify gui timer logic.
  add pflib: PixelFormat conversion library.
  linux fbdev display driver.

 Makefile.objs    |    2 +
 console.h        |   82 +++--
 fbdev.c          |  931 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/xenfb.c       |    2 +-
 linux-keynames.h |  386 ++++++++++++++++++++++
 monitor.c        |   14 +
 pflib.c          |  213 +++++++++++++
 pflib.h          |   20 ++
 qemu-options.hx  |   10 +
 sysemu.h         |    1 +
 vl.c             |   55 ++--
 11 files changed, 1648 insertions(+), 68 deletions(-)
 create mode 100644 fbdev.c
 create mode 100644 linux-keynames.h
 create mode 100644 pflib.c
 create mode 100644 pflib.h

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH 1/5] QLIST-ify display change listeners.
  2010-06-15 10:05 [Qemu-devel] [PATCH 0/5] fbdev display driver + misc bits Gerd Hoffmann
@ 2010-06-15 10:05 ` Gerd Hoffmann
  2010-06-15 10:05 ` [Qemu-devel] [PATCH 2/5] add unregister_displaychangelistener Gerd Hoffmann
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 14+ messages in thread
From: Gerd Hoffmann @ 2010-06-15 10:05 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann


Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 console.h  |   72 +++++++++++++++++++++++++++++++----------------------------
 hw/xenfb.c |    2 +-
 vl.c       |    9 ++-----
 3 files changed, 42 insertions(+), 41 deletions(-)

diff --git a/console.h b/console.h
index aafb031..2263971 100644
--- a/console.h
+++ b/console.h
@@ -162,7 +162,7 @@ struct DisplayChangeListener {
                      int w, int h, uint32_t c);
     void (*dpy_text_cursor)(struct DisplayState *s, int x, int y);
 
-    struct DisplayChangeListener *next;
+    QLIST_ENTRY(DisplayChangeListener) next;
 };
 
 struct DisplayAllocator {
@@ -177,7 +177,7 @@ struct DisplayState {
     struct QEMUTimer *gui_timer;
 
     struct DisplayAllocator* allocator;
-    struct DisplayChangeListener* listeners;
+    QLIST_HEAD(, DisplayChangeListener) listeners;
 
     void (*mouse_set)(int x, int y, int on);
     void (*cursor_define)(QEMUCursor *cursor);
@@ -225,72 +225,76 @@ static inline int is_buffer_shared(DisplaySurface *surface)
 
 static inline void register_displaychangelistener(DisplayState *ds, DisplayChangeListener *dcl)
 {
-    dcl->next = ds->listeners;
-    ds->listeners = dcl;
+    QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
 }
 
 static inline void dpy_update(DisplayState *s, int x, int y, int w, int h)
 {
-    struct DisplayChangeListener *dcl = s->listeners;
-    while (dcl != NULL) {
+    struct DisplayChangeListener *dcl;
+    QLIST_FOREACH(dcl, &s->listeners, next) {
         dcl->dpy_update(s, x, y, w, h);
-        dcl = dcl->next;
     }
 }
 
 static inline void dpy_resize(DisplayState *s)
 {
-    struct DisplayChangeListener *dcl = s->listeners;
-    while (dcl != NULL) {
+    struct DisplayChangeListener *dcl;
+    QLIST_FOREACH(dcl, &s->listeners, next) {
         dcl->dpy_resize(s);
-        dcl = dcl->next;
     }
 }
 
 static inline void dpy_setdata(DisplayState *s)
 {
-    struct DisplayChangeListener *dcl = s->listeners;
-    while (dcl != NULL) {
-        if (dcl->dpy_setdata) dcl->dpy_setdata(s);
-        dcl = dcl->next;
+    struct DisplayChangeListener *dcl;
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (dcl->dpy_setdata) {
+            dcl->dpy_setdata(s);
+        }
     }
 }
 
 static inline void dpy_refresh(DisplayState *s)
 {
-    struct DisplayChangeListener *dcl = s->listeners;
-    while (dcl != NULL) {
-        if (dcl->dpy_refresh) dcl->dpy_refresh(s);
-        dcl = dcl->next;
+    struct DisplayChangeListener *dcl;
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (dcl->dpy_refresh) {
+            dcl->dpy_refresh(s);
+        }
     }
 }
 
 static inline void dpy_copy(struct DisplayState *s, int src_x, int src_y,
-                             int dst_x, int dst_y, int w, int h) {
-    struct DisplayChangeListener *dcl = s->listeners;
-    while (dcl != NULL) {
-        if (dcl->dpy_copy)
+                             int dst_x, int dst_y, int w, int h)
+{
+    struct DisplayChangeListener *dcl;
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (dcl->dpy_copy) {
             dcl->dpy_copy(s, src_x, src_y, dst_x, dst_y, w, h);
-        else /* TODO */
+        } else { /* TODO */
             dcl->dpy_update(s, dst_x, dst_y, w, h);
-        dcl = dcl->next;
+        }
     }
 }
 
 static inline void dpy_fill(struct DisplayState *s, int x, int y,
-                             int w, int h, uint32_t c) {
-    struct DisplayChangeListener *dcl = s->listeners;
-    while (dcl != NULL) {
-        if (dcl->dpy_fill) dcl->dpy_fill(s, x, y, w, h, c);
-        dcl = dcl->next;
+                             int w, int h, uint32_t c)
+{
+    struct DisplayChangeListener *dcl;
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (dcl->dpy_fill) {
+            dcl->dpy_fill(s, x, y, w, h, c);
+        }
     }
 }
 
-static inline void dpy_cursor(struct DisplayState *s, int x, int y) {
-    struct DisplayChangeListener *dcl = s->listeners;
-    while (dcl != NULL) {
-        if (dcl->dpy_text_cursor) dcl->dpy_text_cursor(s, x, y);
-        dcl = dcl->next;
+static inline void dpy_cursor(struct DisplayState *s, int x, int y)
+{
+    struct DisplayChangeListener *dcl;
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (dcl->dpy_text_cursor) {
+            dcl->dpy_text_cursor(s, x, y);
+        }
     }
 }
 
diff --git a/hw/xenfb.c b/hw/xenfb.c
index da5297b..5076beb 100644
--- a/hw/xenfb.c
+++ b/hw/xenfb.c
@@ -706,7 +706,7 @@ static void xenfb_update(void *opaque)
 	if (xenfb_queue_full(xenfb))
 	    return;
 
-        for (l = xenfb->c.ds->listeners; l != NULL; l = l->next) {
+        QLIST_FOREACH(l, &xenfb->c.ds->listeners, next) {
             if (l->idle)
                 continue;
             idle = 0;
diff --git a/vl.c b/vl.c
index e5e43b3..34aa388 100644
--- a/vl.c
+++ b/vl.c
@@ -1045,16 +1045,15 @@ static void gui_update(void *opaque)
 {
     uint64_t interval = GUI_REFRESH_INTERVAL;
     DisplayState *ds = opaque;
-    DisplayChangeListener *dcl = ds->listeners;
+    DisplayChangeListener *dcl;
 
     qemu_flush_coalesced_mmio_buffer();
     dpy_refresh(ds);
 
-    while (dcl != NULL) {
+    QLIST_FOREACH(dcl, &ds->listeners, next) {
         if (dcl->gui_timer_interval &&
             dcl->gui_timer_interval < interval)
             interval = dcl->gui_timer_interval;
-        dcl = dcl->next;
     }
     qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock(rt_clock));
 }
@@ -2925,14 +2924,12 @@ int main(int argc, char **argv, char **envp)
     }
     dpy_resize(ds);
 
-    dcl = ds->listeners;
-    while (dcl != NULL) {
+    QLIST_FOREACH(dcl, &ds->listeners, next) {
         if (dcl->dpy_refresh != NULL) {
             ds->gui_timer = qemu_new_timer(rt_clock, gui_update, ds);
             qemu_mod_timer(ds->gui_timer, qemu_get_clock(rt_clock));
             break;
         }
-        dcl = dcl->next;
     }
 
     if (display_type == DT_NOGRAPHIC || display_type == DT_VNC) {
-- 
1.6.5.2

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH 2/5] add unregister_displaychangelistener
  2010-06-15 10:05 [Qemu-devel] [PATCH 0/5] fbdev display driver + misc bits Gerd Hoffmann
  2010-06-15 10:05 ` [Qemu-devel] [PATCH 1/5] QLIST-ify display change listeners Gerd Hoffmann
@ 2010-06-15 10:05 ` Gerd Hoffmann
  2010-06-15 10:05 ` [Qemu-devel] [PATCH 3/5] Fix and simplify gui timer logic Gerd Hoffmann
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 14+ messages in thread
From: Gerd Hoffmann @ 2010-06-15 10:05 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann


Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 console.h |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/console.h b/console.h
index 2263971..3f77661 100644
--- a/console.h
+++ b/console.h
@@ -228,6 +228,11 @@ static inline void register_displaychangelistener(DisplayState *ds, DisplayChang
     QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
 }
 
+static inline void unregister_displaychangelistener(DisplayChangeListener *dcl)
+{
+    QLIST_REMOVE(dcl, next);
+}
+
 static inline void dpy_update(DisplayState *s, int x, int y, int w, int h)
 {
     struct DisplayChangeListener *dcl;
-- 
1.6.5.2

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH 3/5] Fix and simplify gui timer logic.
  2010-06-15 10:05 [Qemu-devel] [PATCH 0/5] fbdev display driver + misc bits Gerd Hoffmann
  2010-06-15 10:05 ` [Qemu-devel] [PATCH 1/5] QLIST-ify display change listeners Gerd Hoffmann
  2010-06-15 10:05 ` [Qemu-devel] [PATCH 2/5] add unregister_displaychangelistener Gerd Hoffmann
@ 2010-06-15 10:05 ` Gerd Hoffmann
  2010-06-15 10:05 ` [Qemu-devel] [PATCH 4/5] add pflib: PixelFormat conversion library Gerd Hoffmann
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 14+ messages in thread
From: Gerd Hoffmann @ 2010-06-15 10:05 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Kill the nographic and gui timers.  Add a single periodic_timer to
replace them.  We have a timer running anyway, so so the gui/nographic
separation is pretty pointless, we can just drive everything we need
from the single periodic timer.

Don't allocate the timer twice in case we have two display listeners.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 console.h |    1 -
 vl.c      |   40 ++++++++++++----------------------------
 2 files changed, 12 insertions(+), 29 deletions(-)

diff --git a/console.h b/console.h
index 3f77661..f866987 100644
--- a/console.h
+++ b/console.h
@@ -174,7 +174,6 @@ struct DisplayAllocator {
 struct DisplayState {
     struct DisplaySurface *surface;
     void *opaque;
-    struct QEMUTimer *gui_timer;
 
     struct DisplayAllocator* allocator;
     QLIST_HEAD(, DisplayChangeListener) listeners;
diff --git a/vl.c b/vl.c
index 34aa388..8fc63c7 100644
--- a/vl.c
+++ b/vl.c
@@ -227,7 +227,7 @@ int nb_numa_nodes;
 uint64_t node_mem[MAX_NODES];
 uint64_t node_cpumask[MAX_NODES];
 
-static QEMUTimer *nographic_timer;
+static QEMUTimer *periodic_timer;
 
 uint8_t qemu_uuid[16];
 
@@ -1041,29 +1041,24 @@ static QEMUMachine *find_default_machine(void)
 /***********************************************************/
 /* main execution loop */
 
-static void gui_update(void *opaque)
+static void periodic_update(void *opaque)
 {
     uint64_t interval = GUI_REFRESH_INTERVAL;
     DisplayState *ds = opaque;
     DisplayChangeListener *dcl;
 
     qemu_flush_coalesced_mmio_buffer();
-    dpy_refresh(ds);
 
-    QLIST_FOREACH(dcl, &ds->listeners, next) {
-        if (dcl->gui_timer_interval &&
-            dcl->gui_timer_interval < interval)
-            interval = dcl->gui_timer_interval;
+    if (ds != NULL && !QLIST_EMPTY(&ds->listeners)) {
+        dpy_refresh(ds);
+        QLIST_FOREACH(dcl, &ds->listeners, next) {
+            if (dcl->gui_timer_interval &&
+                dcl->gui_timer_interval < interval)
+                interval = dcl->gui_timer_interval;
+        }
     }
-    qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock(rt_clock));
-}
-
-static void nographic_update(void *opaque)
-{
-    uint64_t interval = GUI_REFRESH_INTERVAL;
 
-    qemu_flush_coalesced_mmio_buffer();
-    qemu_mod_timer(nographic_timer, interval + qemu_get_clock(rt_clock));
+    qemu_mod_timer(periodic_timer, interval + qemu_get_clock(rt_clock));
 }
 
 struct vm_change_state_entry {
@@ -1807,7 +1802,6 @@ int main(int argc, char **argv, char **envp)
     const char *kernel_filename, *kernel_cmdline;
     char boot_devices[33] = "cad"; /* default to HD->floppy->CD-ROM */
     DisplayState *ds;
-    DisplayChangeListener *dcl;
     int cyls, heads, secs, translation;
     QemuOpts *hda_opts = NULL, *opts;
     int optind;
@@ -2924,18 +2918,8 @@ int main(int argc, char **argv, char **envp)
     }
     dpy_resize(ds);
 
-    QLIST_FOREACH(dcl, &ds->listeners, next) {
-        if (dcl->dpy_refresh != NULL) {
-            ds->gui_timer = qemu_new_timer(rt_clock, gui_update, ds);
-            qemu_mod_timer(ds->gui_timer, qemu_get_clock(rt_clock));
-            break;
-        }
-    }
-
-    if (display_type == DT_NOGRAPHIC || display_type == DT_VNC) {
-        nographic_timer = qemu_new_timer(rt_clock, nographic_update, NULL);
-        qemu_mod_timer(nographic_timer, qemu_get_clock(rt_clock));
-    }
+    periodic_timer = qemu_new_timer(rt_clock, periodic_update, ds);
+    qemu_mod_timer(periodic_timer, qemu_get_clock(rt_clock));
 
     text_consoles_set_display(ds);
 
-- 
1.6.5.2

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH 4/5] add pflib: PixelFormat conversion library.
  2010-06-15 10:05 [Qemu-devel] [PATCH 0/5] fbdev display driver + misc bits Gerd Hoffmann
                   ` (2 preceding siblings ...)
  2010-06-15 10:05 ` [Qemu-devel] [PATCH 3/5] Fix and simplify gui timer logic Gerd Hoffmann
@ 2010-06-15 10:05 ` Gerd Hoffmann
  2010-06-15 10:05 ` [Qemu-devel] [PATCH 5/5] linux fbdev display driver Gerd Hoffmann
  2010-06-24 18:38 ` [Qemu-devel] [PATCH 0/5] fbdev display driver + misc bits Julian Pidancet
  5 siblings, 0 replies; 14+ messages in thread
From: Gerd Hoffmann @ 2010-06-15 10:05 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann


Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 Makefile.objs |    1 +
 pflib.c       |  213 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 pflib.h       |   20 ++++++
 3 files changed, 234 insertions(+), 0 deletions(-)
 create mode 100644 pflib.c
 create mode 100644 pflib.h

diff --git a/Makefile.objs b/Makefile.objs
index 2bfb6d1..acc6179 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -83,6 +83,7 @@ common-obj-y += qemu-char.o savevm.o #aio.o
 common-obj-y += msmouse.o ps2.o
 common-obj-y += qdev.o qdev-properties.o
 common-obj-y += block-migration.o
+common-obj-y += pflib.o
 
 common-obj-$(CONFIG_BRLAPI) += baum.o
 common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
diff --git a/pflib.c b/pflib.c
new file mode 100644
index 0000000..1154d0c
--- /dev/null
+++ b/pflib.c
@@ -0,0 +1,213 @@
+/*
+ * PixelFormat conversion library.
+ *
+ * Author: Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include "qemu-common.h"
+#include "console.h"
+#include "pflib.h"
+
+typedef struct QemuPixel QemuPixel;
+
+typedef void (*pf_convert)(QemuPfConv *conv,
+                           void *dst, void *src, uint32_t cnt);
+typedef void (*pf_convert_from)(PixelFormat *pf,
+                                QemuPixel *dst, void *src, uint32_t cnt);
+typedef void (*pf_convert_to)(PixelFormat *pf,
+                              void *dst, QemuPixel *src, uint32_t cnt);
+
+struct QemuPfConv {
+    pf_convert        convert;
+    PixelFormat       src;
+    PixelFormat       dst;
+
+    /* for copy_generic() */
+    pf_convert_from   conv_from;
+    pf_convert_to     conv_to;
+    QemuPixel         *conv_buf;
+    uint32_t          conv_cnt;
+};
+
+struct QemuPixel {
+    uint8_t red;
+    uint8_t green;
+    uint8_t blue;
+    uint8_t alpha;
+};
+
+/* ----------------------------------------------------------------------- */
+/* PixelFormat -> QemuPixel conversions                                    */
+
+static void conv_16_to_pixel(PixelFormat *pf,
+                             QemuPixel *dst, void *src, uint32_t cnt)
+{
+    uint16_t *src16 = src;
+
+    while (cnt > 0) {
+        dst->red   = ((*src16 & pf->rmask) >> pf->rshift) << (8 - pf->rbits);
+        dst->green = ((*src16 & pf->gmask) >> pf->gshift) << (8 - pf->gbits);
+        dst->blue  = ((*src16 & pf->bmask) >> pf->bshift) << (8 - pf->bbits);
+        dst->alpha = ((*src16 & pf->amask) >> pf->ashift) << (8 - pf->abits);
+        dst++, src16++, cnt--;
+    }
+}
+
+/* assumes pf->{r,g,b,a}bits == 8 */
+static void conv_32_to_pixel_fast(PixelFormat *pf,
+                                  QemuPixel *dst, void *src, uint32_t cnt)
+{
+    uint32_t *src32 = src;
+
+    while (cnt > 0) {
+        dst->red   = (*src32 & pf->rmask) >> pf->rshift;
+        dst->green = (*src32 & pf->gmask) >> pf->gshift;
+        dst->blue  = (*src32 & pf->bmask) >> pf->bshift;
+        dst->alpha = (*src32 & pf->amask) >> pf->ashift;
+        dst++, src32++, cnt--;
+    }
+}
+
+static void conv_32_to_pixel_generic(PixelFormat *pf,
+                                     QemuPixel *dst, void *src, uint32_t cnt)
+{
+    uint32_t *src32 = src;
+
+    while (cnt > 0) {
+        if (pf->rbits < 8) {
+            dst->red   = ((*src32 & pf->rmask) >> pf->rshift) << (8 - pf->rbits);
+        } else {
+            dst->red   = ((*src32 & pf->rmask) >> pf->rshift) >> (pf->rbits - 8);
+        }
+        if (pf->gbits < 8) {
+            dst->green = ((*src32 & pf->gmask) >> pf->gshift) << (8 - pf->gbits);
+        } else {
+            dst->green = ((*src32 & pf->gmask) >> pf->gshift) >> (pf->gbits - 8);
+        }
+        if (pf->bbits < 8) {
+            dst->blue  = ((*src32 & pf->bmask) >> pf->bshift) << (8 - pf->bbits);
+        } else {
+            dst->blue  = ((*src32 & pf->bmask) >> pf->bshift) >> (pf->bbits - 8);
+        }
+        if (pf->abits < 8) {
+            dst->alpha = ((*src32 & pf->amask) >> pf->ashift) << (8 - pf->abits);
+        } else {
+            dst->alpha = ((*src32 & pf->amask) >> pf->ashift) >> (pf->abits - 8);
+        }
+        dst++, src32++, cnt--;
+    }
+}
+
+/* ----------------------------------------------------------------------- */
+/* QemuPixel -> PixelFormat conversions                                    */
+
+static void conv_pixel_to_16(PixelFormat *pf,
+                             void *dst, QemuPixel *src, uint32_t cnt)
+{
+    uint16_t *dst16 = dst;
+
+    while (cnt > 0) {
+        *dst16  = ((uint16_t)src->red   >> (8 - pf->rbits)) << pf->rshift;
+        *dst16 |= ((uint16_t)src->green >> (8 - pf->gbits)) << pf->gshift;
+        *dst16 |= ((uint16_t)src->blue  >> (8 - pf->bbits)) << pf->bshift;
+        *dst16 |= ((uint16_t)src->alpha >> (8 - pf->abits)) << pf->ashift;
+        dst16++, src++, cnt--;
+    }
+}
+
+static void conv_pixel_to_32(PixelFormat *pf,
+                             void *dst, QemuPixel *src, uint32_t cnt)
+{
+    uint32_t *dst32 = dst;
+
+    while (cnt > 0) {
+        *dst32  = ((uint32_t)src->red   >> (8 - pf->rbits)) << pf->rshift;
+        *dst32 |= ((uint32_t)src->green >> (8 - pf->gbits)) << pf->gshift;
+        *dst32 |= ((uint32_t)src->blue  >> (8 - pf->bbits)) << pf->bshift;
+        *dst32 |= ((uint32_t)src->alpha >> (8 - pf->abits)) << pf->ashift;
+        dst32++, src++, cnt--;
+    }
+}
+
+/* ----------------------------------------------------------------------- */
+/* PixelFormat -> PixelFormat conversions                                  */
+
+static void convert_copy(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
+{
+    uint32_t bytes = cnt * conv->src.bytes_per_pixel;
+    memcpy(dst, src, bytes);
+}
+
+static void convert_generic(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
+{
+    if (conv->conv_cnt < cnt) {
+        conv->conv_cnt = cnt;
+        conv->conv_buf = qemu_realloc(conv->conv_buf, sizeof(QemuPixel) * conv->conv_cnt);
+    }
+    conv->conv_from(&conv->src, conv->conv_buf, src, cnt);
+    conv->conv_to(&conv->dst, dst, conv->conv_buf, cnt);
+}
+
+/* ----------------------------------------------------------------------- */
+/* public interface                                                        */
+
+QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src)
+{
+    QemuPfConv *conv = qemu_mallocz(sizeof(QemuPfConv));
+
+    conv->src = *src;
+    conv->dst = *dst;
+
+    if (memcmp(&conv->src, &conv->dst, sizeof(PixelFormat)) == 0) {
+        /* formats identical, can simply copy */
+        conv->convert = convert_copy;
+    } else {
+        /* generic two-step conversion: src -> QemuPixel -> dst  */
+        switch (conv->src.bytes_per_pixel) {
+        case 2:
+            conv->conv_from = conv_16_to_pixel;
+            break;
+        case 4:
+            if (conv->src.rbits == 8 && conv->src.gbits == 8 && conv->src.bbits == 8) {
+                conv->conv_from = conv_32_to_pixel_fast;
+            } else {
+                conv->conv_from = conv_32_to_pixel_generic;
+            }
+            break;
+        default:
+            goto err;
+        }
+        switch (conv->dst.bytes_per_pixel) {
+        case 2:
+            conv->conv_to = conv_pixel_to_16;
+            break;
+        case 4:
+            conv->conv_to = conv_pixel_to_32;
+            break;
+        default:
+            goto err;
+        }
+        conv->convert = convert_generic;
+    }
+    return conv;
+
+err:
+    qemu_free(conv);
+    return NULL;
+}
+
+void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
+{
+    conv->convert(conv, dst, src, cnt);
+}
+
+void qemu_pf_conv_put(QemuPfConv *conv)
+{
+    if (conv) {
+        qemu_free(conv->conv_buf);
+        qemu_free(conv);
+    }
+}
diff --git a/pflib.h b/pflib.h
new file mode 100644
index 0000000..b70c313
--- /dev/null
+++ b/pflib.h
@@ -0,0 +1,20 @@
+#ifndef __QEMU_PFLIB_H
+#define __QEMU_PFLIB_H
+
+/*
+ * PixelFormat conversion library.
+ *
+ * Author: Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+typedef struct QemuPfConv QemuPfConv;
+
+QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src);
+void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt);
+void qemu_pf_conv_put(QemuPfConv *conv);
+
+#endif
-- 
1.6.5.2

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH 5/5] linux fbdev display driver.
  2010-06-15 10:05 [Qemu-devel] [PATCH 0/5] fbdev display driver + misc bits Gerd Hoffmann
                   ` (3 preceding siblings ...)
  2010-06-15 10:05 ` [Qemu-devel] [PATCH 4/5] add pflib: PixelFormat conversion library Gerd Hoffmann
@ 2010-06-15 10:05 ` Gerd Hoffmann
  2010-06-16 12:44   ` [Qemu-devel] " Stefano Stabellini
  2010-06-24 18:38 ` [Qemu-devel] [PATCH 0/5] fbdev display driver + misc bits Julian Pidancet
  5 siblings, 1 reply; 14+ messages in thread
From: Gerd Hoffmann @ 2010-06-15 10:05 UTC (permalink / raw)
  To: qemu-devel; +Cc: Stefano Stabellini, Gerd Hoffmann, Julian Pidancet

Display works, requires truecolor framebuffer with 16 or 32 bpp on the
host.  32bpp is recommended.  The framebuffer is used as-is, qemu
doesn't try to switch modes.  With LCD displays mode switching is pretty
pointless IMHO, also it wouldn't work anyway with the most common
fbdev drivers (vesafb, KMS).  Guest display is centered on the host
screen.

Mouse works, uses /dev/input/mice.

Keyboard works.  Guest screen has whatever keymap you load inside
the guest.  Text windows (monitor, serial, ...) have a simple en-us
keymap.  Good enougth to type monitor commands.  Not goot enougth to
work seriously on a serial terminal.  But the qemu terminal emulation
isn't good enougth for that anyway ;)

Hot keys:
  Ctrl-Alt-F<nr>  -> host console switching.
  Ctrl-Alt-<nr>   -> qemu console switching.
  Ctrl-Alt-ESC    -> exit qemu.

Special feature:  Sane console switching.  Switching away stops screen
updates.  Switching back redraws the screen.  When started from the
linux console qemu uses the vt you've started it from (requires just
read/write access to /dev/fb0).  When starting from somewhere else qemu
tries to open a unused virtual terminal and switch to it (usually
requires root privileges to open /dev/tty<nr>).

For some strange reason console switching from X11 to qemu doesn't work.
Anything else (including X11 -> text console -> qemu) works fine.  To be
investigated ...

Can be enabled/disabled via monitor, use "change fbdev [ on | off ]"

Cc: Julian Pidancet <julian.pidancet@citrix.com>
Cc: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 Makefile.objs    |    1 +
 console.h        |    4 +
 fbdev.c          |  931 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 linux-keynames.h |  386 ++++++++++++++++++++++
 monitor.c        |   14 +
 qemu-options.hx  |   10 +
 sysemu.h         |    1 +
 vl.c             |   10 +
 8 files changed, 1357 insertions(+), 0 deletions(-)
 create mode 100644 fbdev.c
 create mode 100644 linux-keynames.h

diff --git a/Makefile.objs b/Makefile.objs
index acc6179..be6a03c 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -106,6 +106,7 @@ common-obj-y += $(addprefix audio/, $(audio-obj-y))
 common-obj-y += keymaps.o
 common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
 common-obj-$(CONFIG_CURSES) += curses.o
+common-obj-$(CONFIG_LINUX) += fbdev.o
 common-obj-y += vnc.o acl.o d3des.o
 common-obj-y += vnc-encoding-zlib.o vnc-encoding-hextile.o
 common-obj-y += vnc-encoding-tight.o
diff --git a/console.h b/console.h
index f866987..124a22b 100644
--- a/console.h
+++ b/console.h
@@ -369,6 +369,10 @@ void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
 /* sdl.c */
 void sdl_display_init(DisplayState *ds, int full_screen, int no_frame);
 
+/* fbdev.c */
+void fbdev_display_init(DisplayState *ds, const char *device);
+void fbdev_display_uninit(void);
+
 /* cocoa.m */
 void cocoa_display_init(DisplayState *ds, int full_screen);
 
diff --git a/fbdev.c b/fbdev.c
new file mode 100644
index 0000000..2ac4aba
--- /dev/null
+++ b/fbdev.c
@@ -0,0 +1,931 @@
+/*
+ * linux fbdev output driver.
+ *
+ * Author: Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <termios.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <linux/fb.h>
+#include <linux/input.h>
+
+#include "qemu-common.h"
+#include "console.h"
+#include "keymaps.h"
+#include "sysemu.h"
+#include "pflib.h"
+
+/* -------------------------------------------------------------------- */
+
+/* file handles */
+static int                        tty = -1, fb = -1, mice = -1;
+
+/* saved state, for restore on exit */
+static int                        orig_vtno = 0;
+static int                        kd_omode;
+static struct vt_mode             vt_omode;
+static struct fb_var_screeninfo   fb_ovar;
+
+/* framebuffer */
+static struct fb_fix_screeninfo   fb_fix;
+static struct fb_var_screeninfo   fb_var;
+static uint8_t                   *fb_mem;
+static int      		  fb_mem_offset = 0;
+
+/* linux console */
+static int                        vtno;
+static struct vt_mode             vt_mode;
+static struct termios             tty_attributes;
+static unsigned long              tty_mode;
+static unsigned int               tty_flags;
+static bool                       tty_mediumraw;
+static bool                       key_down[KEY_CNT];
+
+/* console switching */
+#define SIG_ACQ      (SIGRTMIN+6)
+#define SIG_REL      (SIGRTMIN+7)
+#define FB_ACTIVE    0
+#define FB_REL_REQ   1
+#define FB_INACTIVE  2
+#define FB_ACQ_REQ   3
+static int fb_switch_state = FB_ACTIVE;
+
+/* qdev windup */
+static DisplayChangeListener      *dcl;
+static QemuPfConv                 *conv;
+static PixelFormat                fbpf;
+static int                        resize_screen;
+static int                        redraw_screen;
+static int                        cx, cy, cw, ch;
+static int                        debug = 0;
+static Notifier                   exit_notifier;
+
+/* fwd decls */
+static int fbdev_activate_vt(int tty, int vtno, bool wait);
+
+/* -------------------------------------------------------------------- */
+/* mouse                                                                */
+
+static void read_mouse(void *opaque)
+{
+    char buf[3];
+    int rc, x, y, b;
+
+    rc = read(mice, buf, sizeof(buf));
+    if (rc != sizeof(buf))
+        return;
+
+    if (fb_switch_state != FB_ACTIVE)
+        return;
+
+    x = buf[1];
+    y = -buf[2];
+    b = buf[0] & 0x7;
+
+    if (kbd_mouse_is_absolute()) {
+        static int ax, ay;
+        ax += x; ay += y;
+        if (ax < 0)
+            ax = 0;
+        if (ay < 0)
+            ay = 0;
+        if (ax >= cw)
+            ax = cw-1;
+        if (ay >= ch)
+            ay = ch-1;
+        kbd_mouse_event(ax * 0x7FFF / cw, ay * 0x7FFF / ch, 0, b);
+    } else {
+        kbd_mouse_event(x, y, 0, b);
+    }
+}
+
+static int init_mouse(void)
+{
+    mice = open("/dev/input/mice", O_RDONLY);
+    if (-1 == mice)
+        return -1;
+    qemu_set_fd_handler(mice, read_mouse, NULL, NULL);
+    return 0;
+}
+
+static void uninit_mouse(void)
+{
+    if (-1 == mice)
+        return;
+    qemu_set_fd_handler(mice, NULL, NULL, NULL);
+    close(mice);
+    mice = -1;
+}
+
+/* -------------------------------------------------------------------- */
+/* keyboard                                                             */
+
+static const char *keynames[] = {
+#include "linux-keynames.h"
+};
+
+static int scancode_map[KEY_CNT] = {
+    [ KEY_ESC          ] = 0x01,
+    [ KEY_1            ] = 0x02,
+    [ KEY_2            ] = 0x03,
+    [ KEY_3            ] = 0x04,
+    [ KEY_4            ] = 0x05,
+    [ KEY_5            ] = 0x06,
+    [ KEY_6            ] = 0x07,
+    [ KEY_7            ] = 0x08,
+    [ KEY_8            ] = 0x09,
+    [ KEY_9            ] = 0x0a,
+    [ KEY_0            ] = 0x0b,
+    [ KEY_MINUS        ] = 0x0c,
+    [ KEY_EQUAL        ] = 0x0d,
+    [ KEY_BACKSPACE    ] = 0x0e,
+
+    [ KEY_TAB          ] = 0x0f,
+    [ KEY_Q            ] = 0x10,
+    [ KEY_W            ] = 0x11,
+    [ KEY_E            ] = 0x12,
+    [ KEY_R            ] = 0x13,
+    [ KEY_T            ] = 0x14,
+    [ KEY_Y            ] = 0x15,
+    [ KEY_U            ] = 0x16,
+    [ KEY_I            ] = 0x17,
+    [ KEY_O            ] = 0x18,
+    [ KEY_P            ] = 0x19,
+    [ KEY_LEFTBRACE    ] = 0x1a,
+    [ KEY_RIGHTBRACE   ] = 0x1b,
+    [ KEY_ENTER        ] = 0x1c,
+
+    [ KEY_A            ] = 0x1e,
+    [ KEY_S            ] = 0x1f,
+    [ KEY_D            ] = 0x20,
+    [ KEY_F            ] = 0x21,
+    [ KEY_G            ] = 0x22,
+    [ KEY_H            ] = 0x23,
+    [ KEY_J            ] = 0x24,
+    [ KEY_K            ] = 0x25,
+    [ KEY_L            ] = 0x26,
+    [ KEY_SEMICOLON    ] = 0x27,
+    [ KEY_APOSTROPHE   ] = 0x28,
+    [ KEY_GRAVE        ] = 0x29,
+    [ KEY_LEFTSHIFT    ] = 0x2a,
+    [ KEY_BACKSLASH    ] = 0x2b,
+
+    [ KEY_Z            ] = 0x2c,
+    [ KEY_X            ] = 0x2d,
+    [ KEY_C            ] = 0x2e,
+    [ KEY_V            ] = 0x2f,
+    [ KEY_B            ] = 0x30,
+    [ KEY_N            ] = 0x31,
+    [ KEY_M            ] = 0x32,
+    [ KEY_COMMA        ] = 0x33,
+    [ KEY_DOT          ] = 0x34,
+    [ KEY_SLASH        ] = 0x35,
+    [ KEY_RIGHTSHIFT   ] = 0x36,
+    [ KEY_SPACE        ] = 0x39,
+
+    [ KEY_F1           ] = 0x3b,
+    [ KEY_F2           ] = 0x3c,
+    [ KEY_F3           ] = 0x3d,
+    [ KEY_F4           ] = 0x3e,
+    [ KEY_F5           ] = 0x3f,
+    [ KEY_F6           ] = 0x40,
+    [ KEY_F7           ] = 0x41,
+    [ KEY_F8           ] = 0x42,
+    [ KEY_F9           ] = 0x43,
+    [ KEY_F10          ] = 0x44,
+    [ KEY_F11          ] = 0x57,
+    [ KEY_F12          ] = 0x58,
+
+    [ KEY_SYSRQ        ] = 0xb7,
+    [ KEY_SCROLLLOCK   ] = 0x46,
+//    [ KEY_PAUSE        ] = FIXME,
+    [ KEY_CAPSLOCK     ] = 0x3a,
+    [ KEY_102ND        ] = 0x56,
+
+    [ KEY_LEFTCTRL     ] = 0x1d,
+    [ KEY_LEFTMETA     ] = 0xdb,
+    [ KEY_LEFTALT      ] = 0x38,
+    [ KEY_RIGHTALT     ] = 0xb8,
+    [ KEY_RIGHTMETA    ] = 0xdc,
+    [ KEY_RIGHTCTRL    ] = 0x9d,
+    [ KEY_COMPOSE      ] = 0xdd,
+
+    [ KEY_INSERT       ] = 0xd2,
+    [ KEY_DELETE       ] = 0xd3,
+    [ KEY_HOME         ] = 0xc7,
+    [ KEY_END          ] = 0xcf,
+    [ KEY_PAGEUP       ] = 0xc9,
+    [ KEY_PAGEDOWN     ] = 0xd1,
+
+    [ KEY_UP           ] = 0xc8,
+    [ KEY_LEFT         ] = 0xcb,
+    [ KEY_RIGHT        ] = 0xcd,
+    [ KEY_DOWN         ] = 0xd0,
+
+    [ KEY_NUMLOCK      ] = 0x45,
+    [ KEY_KPSLASH      ] = 0xb5,
+    [ KEY_KPASTERISK   ] = 0x37,
+    [ KEY_KP7          ] = 0x47,
+    [ KEY_KP8          ] = 0x48,
+    [ KEY_KP9          ] = 0x49,
+    [ KEY_KPMINUS      ] = 0x4a,
+    [ KEY_KP4          ] = 0x4b,
+    [ KEY_KP5          ] = 0x4c,
+    [ KEY_KP6          ] = 0x4d,
+    [ KEY_KPPLUS       ] = 0x4e,
+    [ KEY_KP1          ] = 0x4f,
+    [ KEY_KP2          ] = 0x50,
+    [ KEY_KP3          ] = 0x51,
+    [ KEY_KP0          ] = 0x52,
+    [ KEY_KPDOT        ] = 0x53,
+    [ KEY_KPENTER      ] = 0x9c,
+};
+
+static struct keysym_map {
+    int  normal, shifted;
+} keysym_map_en_us[KEY_CNT] = {
+    [ KEY_A ] = { .normal = 'a', .shifted = 'A' },
+    [ KEY_B ] = { .normal = 'b', .shifted = 'B' },
+    [ KEY_C ] = { .normal = 'c', .shifted = 'C' },
+    [ KEY_D ] = { .normal = 'd', .shifted = 'D' },
+    [ KEY_E ] = { .normal = 'e', .shifted = 'E' },
+    [ KEY_F ] = { .normal = 'f', .shifted = 'F' },
+    [ KEY_G ] = { .normal = 'g', .shifted = 'G' },
+    [ KEY_H ] = { .normal = 'h', .shifted = 'H' },
+    [ KEY_I ] = { .normal = 'i', .shifted = 'I' },
+    [ KEY_J ] = { .normal = 'j', .shifted = 'J' },
+    [ KEY_K ] = { .normal = 'k', .shifted = 'K' },
+    [ KEY_L ] = { .normal = 'l', .shifted = 'L' },
+    [ KEY_M ] = { .normal = 'm', .shifted = 'M' },
+    [ KEY_N ] = { .normal = 'n', .shifted = 'N' },
+    [ KEY_O ] = { .normal = 'o', .shifted = 'O' },
+    [ KEY_P ] = { .normal = 'p', .shifted = 'P' },
+    [ KEY_Q ] = { .normal = 'q', .shifted = 'Q' },
+    [ KEY_R ] = { .normal = 'r', .shifted = 'R' },
+    [ KEY_S ] = { .normal = 's', .shifted = 'S' },
+    [ KEY_T ] = { .normal = 't', .shifted = 'T' },
+    [ KEY_U ] = { .normal = 'u', .shifted = 'U' },
+    [ KEY_V ] = { .normal = 'v', .shifted = 'V' },
+    [ KEY_W ] = { .normal = 'w', .shifted = 'W' },
+    [ KEY_X ] = { .normal = 'x', .shifted = 'X' },
+    [ KEY_Y ] = { .normal = 'y', .shifted = 'Y' },
+    [ KEY_Z ] = { .normal = 'z', .shifted = 'Z' },
+
+    [ KEY_1 ] = { .normal = '1', .shifted = '!' },
+    [ KEY_2 ] = { .normal = '2', .shifted = '@' },
+    [ KEY_3 ] = { .normal = '3', .shifted = '#' },
+    [ KEY_4 ] = { .normal = '4', .shifted = '$' },
+    [ KEY_5 ] = { .normal = '5', .shifted = '%' },
+    [ KEY_6 ] = { .normal = '6', .shifted = '^' },
+    [ KEY_7 ] = { .normal = '7', .shifted = '&' },
+    [ KEY_8 ] = { .normal = '8', .shifted = '*' },
+    [ KEY_9 ] = { .normal = '9', .shifted = '(' },
+    [ KEY_0 ] = { .normal = '0', .shifted = ')' },
+
+    [ KEY_MINUS       ] = { .normal = '-',  .shifted = '_'  },
+    [ KEY_EQUAL       ] = { .normal = '=',  .shifted = '+'  },
+    [ KEY_TAB         ] = { .normal = '\t'  },
+    [ KEY_LEFTBRACE   ] = { .normal = '[',  .shifted = '{'  },
+    [ KEY_RIGHTBRACE  ] = { .normal = ']',  .shifted = '}'  },
+    [ KEY_ENTER       ] = { .normal = '\n', },
+    [ KEY_SEMICOLON   ] = { .normal = ';',  .shifted = ':'  },
+    [ KEY_APOSTROPHE  ] = { .normal = '"',  .shifted = '\'' },
+    [ KEY_BACKSLASH   ] = { .normal = '\\', .shifted = '|'  },
+    [ KEY_COMMA       ] = { .normal = ',',  .shifted = '<'  },
+    [ KEY_DOT         ] = { .normal = '.',  .shifted = '>'  },
+    [ KEY_SLASH       ] = { .normal = '/',  .shifted = '?'  },
+    [ KEY_SPACE       ] = { .normal = ' '   },
+
+    [ KEY_BACKSPACE  ] = { .normal = QEMU_KEY_BACKSPACE  },
+    [ KEY_UP         ] = { .normal = QEMU_KEY_UP         },
+    [ KEY_DOWN       ] = { .normal = QEMU_KEY_DOWN       },
+    [ KEY_LEFT       ] = { .normal = QEMU_KEY_LEFT       },
+    [ KEY_RIGHT      ] = { .normal = QEMU_KEY_RIGHT      },
+};
+
+static void start_mediumraw(int tty)
+{
+    struct termios tattr;
+
+    if (tty_mediumraw)
+        return;
+    if (debug)
+        fprintf(stderr, "%s\n", __FUNCTION__);
+
+    /* save state */
+    tcgetattr(tty, &tty_attributes);
+    ioctl(tty, KDGKBMODE, &tty_mode);
+    tty_flags = fcntl(tty, F_GETFL, NULL);
+
+    /* setup */
+    tattr = tty_attributes;
+    tattr.c_cflag &= ~(IXON|IXOFF);
+    tattr.c_lflag &= ~(ICANON|ECHO|ISIG);
+    tattr.c_iflag = 0;
+    tattr.c_cc[VMIN] = 1;
+    tattr.c_cc[VTIME] = 0;
+    tcsetattr(tty, TCSAFLUSH, &tattr);
+    ioctl(tty, KDSKBMODE, K_MEDIUMRAW);
+    fcntl(tty, F_SETFL, tty_flags | O_NONBLOCK);
+
+    tty_mediumraw = true;
+}
+
+static void stop_mediumraw(int tty)
+{
+    if (!tty_mediumraw)
+        return;
+    if (debug)
+        fprintf(stderr, "%s\n", __FUNCTION__);
+
+    /* restore state */
+    tcsetattr(tty, TCSANOW, &tty_attributes);
+    ioctl(tty, KDSKBMODE, tty_mode);
+    fcntl(tty, F_SETFL, tty_flags);
+
+    tty_mediumraw = false;
+}
+
+static void send_scancode(int keycode, int up)
+{
+    int scancode = scancode_map[keycode];
+
+    if (!scancode) {
+        fprintf(stderr, "%s: unmapped key: 0x%x %s\n",
+                __FUNCTION__, keycode, keynames[keycode]);
+        return;
+    }
+    if (scancode & SCANCODE_GREY)
+        kbd_put_keycode(SCANCODE_EMUL0);
+    if (up) {
+        kbd_put_keycode(scancode | SCANCODE_UP);
+    } else {
+        kbd_put_keycode(scancode & SCANCODE_KEYCODEMASK);
+    }
+}
+
+static void send_keysym(int keycode, int shift)
+{
+    struct keysym_map *keysym_map = keysym_map_en_us;
+    int keysym;
+
+    if (shift && keysym_map[keycode].shifted) {
+        keysym = keysym_map[keycode].shifted;
+    } else if (keysym_map[keycode].normal) {
+        keysym = keysym_map[keycode].normal;
+    } else {
+        fprintf(stderr, "%s: unmapped key: 0x%x %s\n",
+                __FUNCTION__, keycode, keynames[keycode]);
+        return;
+    }
+    kbd_put_keysym(keysym);
+}
+
+static void reset_keys(void)
+{
+    int keycode;
+
+    for (keycode = 0; keycode < KEY_MAX; keycode++) {
+        if (key_down[keycode]) {
+            if (is_graphic_console())
+                send_scancode(keycode, 1);
+            key_down[keycode] = false;
+        }
+    }
+}
+
+static void read_mediumraw(void *opaque)
+{
+    uint8_t buf[32];
+    int i, rc, up, keycode;
+    bool ctrl, alt, shift;
+
+    rc = read(tty, buf, sizeof(buf));
+    switch (rc) {
+    case -1:
+        perror("read tty");
+        goto err;
+    case 0:
+        fprintf(stderr, "%s: eof\n", __FUNCTION__);
+        goto err;
+    default:
+        for (i = 0; i < rc; i++) {
+            up      = buf[i] & 0x80;
+            keycode = buf[i] & 0x7f;
+            if (keycode == 0) {
+                keycode  = (buf[i+1] & 0x7f) << 7;
+                keycode |= buf[i+2] & 0x7f;
+                i += 2;
+            }
+            if (keycode > KEY_MAX) {
+                continue;
+            }
+
+            if (up) {
+                if (!key_down[keycode]) {
+                    continue;
+                }
+                key_down[keycode] = false;
+            } else {
+                key_down[keycode] = true;
+            }
+
+            if (debug)
+                fprintf(stderr, "%s: 0x%x %s %s\n", __FUNCTION__,
+                        keycode, keynames[keycode], up ? "up" : "down");
+
+            alt   = key_down[KEY_LEFTALT]   || key_down[KEY_RIGHTALT];
+            ctrl  = key_down[KEY_LEFTCTRL]  || key_down[KEY_RIGHTCTRL];
+            shift = key_down[KEY_LEFTSHIFT] || key_down[KEY_RIGHTSHIFT];
+
+            if (ctrl && alt && !up) {
+                if (keycode == KEY_ESC) {
+                    fprintf(stderr, "=== fbdev emergency escape (ctrl-alt-esc) ===\n");
+                    exit(1);
+                }
+                if (keycode >= KEY_F1 && keycode <= KEY_F10) {
+                    fbdev_activate_vt(tty, keycode+1-KEY_F1, false);
+                    key_down[keycode] = false;
+                    continue;
+                }
+                if (keycode >= KEY_1 && keycode <= KEY_9) {
+                    console_select(keycode-KEY_1);
+                    reset_keys();
+                    continue;
+                }
+            }
+
+            if (is_graphic_console()) {
+                send_scancode(keycode, up);
+            } else if (!up) {
+                send_keysym(keycode, shift);
+            }
+        }
+    }
+    return;
+
+err:
+    exit(1);
+}
+
+/* -------------------------------------------------------------------- */
+
+static void fbdev_cls(void)
+{
+    memset(fb_mem+fb_mem_offset, 0, fb_fix.line_length * fb_var.yres);
+}
+
+static int fbdev_activate_vt(int tty, int vtno, bool wait)
+{
+    if (debug) {
+        fprintf(stderr, "%s: %d (%s)\n", __FUNCTION__,
+                vtno, wait ? "wait" : "nowait");
+    }
+
+    if (ioctl(tty,VT_ACTIVATE, vtno) < 0) {
+	perror("ioctl VT_ACTIVATE");
+	return -1;
+    }
+    if (wait) {
+        if (ioctl(tty,VT_WAITACTIVE, vtno) < 0) {
+            perror("ioctl VT_WAITACTIVE");
+            return -1;
+        }
+        if (debug) {
+            fprintf(stderr, "%s: finished\n", __FUNCTION__);
+        }
+    }
+    return 0;
+}
+
+static void fbdev_cleanup(void)
+{
+    if (debug)
+        fprintf(stderr, "%s\n", __FUNCTION__);
+
+    /* restore console */
+    if (fb != -1) {
+        if (ioctl(fb,FBIOPUT_VSCREENINFO, &fb_ovar) < 0)
+            perror("ioctl FBIOPUT_VSCREENINFO");
+        close(fb);
+        fb = -1;
+    }
+
+    if (tty != -1) {
+        stop_mediumraw(tty);
+        if (ioctl(tty,KDSETMODE, kd_omode) < 0)
+            perror("ioctl KDSETMODE");
+        if (ioctl(tty,VT_SETMODE, &vt_omode) < 0)
+            perror("ioctl VT_SETMODE");
+        if (orig_vtno)
+            fbdev_activate_vt(tty, orig_vtno, true);
+        qemu_set_fd_handler(tty, NULL, NULL, NULL);
+        close(tty);
+        tty = -1;
+    }
+}
+
+static int fbdev_init(const char *device)
+{
+    struct vt_stat vts;
+    unsigned long page_mask;
+    char ttyname[32];
+
+    /* open framebuffer */
+    if (device == NULL) {
+	device = getenv("FRAMEBUFFER");
+    }
+    if (device == NULL) {
+	device = "/dev/fb0";
+    }
+    fb = open(device, O_RDWR);
+    if (fb == -1) {
+	fprintf(stderr, "open %s: %s\n", device, strerror(errno));
+        return -1;
+    }
+
+    /* open virtual console */
+    tty = 0;
+    if (ioctl(tty, VT_GETSTATE, &vts) < 0) {
+	fprintf(stderr, "Not started from virtual terminal, trying to open one.\n");
+
+        snprintf(ttyname, sizeof(ttyname), "/dev/tty0");
+        tty = open(ttyname, O_RDWR);
+        if (tty == -1) {
+            fprintf(stderr, "open %s: %s\n", ttyname, strerror(errno));
+            exit(1);
+        }
+        if (ioctl(tty,VT_OPENQRY, &vtno) < 0) {
+	    perror("ioctl VT_OPENQRY");
+	    exit(1);
+	}
+        if (ioctl(tty,VT_GETSTATE, &vts) < 0) {
+            perror("ioctl VT_GETSTATE\n");
+            exit(1);
+        }
+        close(tty);
+
+        snprintf(ttyname, sizeof(ttyname), "/dev/tty%d", vtno);
+        tty = open(ttyname, O_RDWR);
+        if (tty == -1) {
+            fprintf(stderr, "open %s: %s\n", ttyname, strerror(errno));
+            exit(1);
+        }
+        orig_vtno = vts.v_active;
+        fprintf(stderr, "Switching to vt %d (current %d).\n", vtno, orig_vtno);
+    } else {
+        orig_vtno = 0;
+        vtno = vts.v_active;
+        fprintf(stderr, "Started at vt %d, using it.\n", vtno);
+    }
+    fbdev_activate_vt(tty, vtno, true);
+
+    /* get current settings (which we have to restore) */
+    if (ioctl(fb, FBIOGET_VSCREENINFO, &fb_ovar) < 0) {
+	perror("ioctl FBIOGET_VSCREENINFO");
+        return -1;
+    }
+    if (ioctl(tty, KDGETMODE, &kd_omode) < 0) {
+	perror("ioctl KDGETMODE");
+        return -1;
+    }
+    if (ioctl(tty, VT_GETMODE, &vt_omode) < 0) {
+	perror("ioctl VT_GETMODE");
+        return -1;
+    }
+
+    /* checks & initialisation */
+    if (ioctl(fb,FBIOGET_FSCREENINFO,&fb_fix) < 0) {
+	perror("ioctl FBIOGET_FSCREENINFO");
+	goto err;
+    }
+    if (ioctl(fb,FBIOGET_VSCREENINFO,&fb_var) < 0) {
+	perror("ioctl FBIOGET_VSCREENINFO");
+	goto err;
+    }
+    if (fb_fix.type != FB_TYPE_PACKED_PIXELS) {
+	fprintf(stderr, "can handle only packed pixel frame buffers\n");
+	goto err;
+    }
+    switch (fb_var.bits_per_pixel) {
+    case 32:
+        break;
+    default:
+	fprintf(stderr, "can't handle %d bpp frame buffers\n",
+                fb_var.bits_per_pixel);
+	goto err;
+    }
+
+    page_mask = getpagesize()-1;
+    fb_mem_offset = (unsigned long)(fb_fix.smem_start) & page_mask;
+    fb_mem = mmap(NULL,fb_fix.smem_len+fb_mem_offset,
+		  PROT_READ|PROT_WRITE,MAP_SHARED,fb,0);
+    if (fb_mem == MAP_FAILED) {
+	perror("mmap");
+	goto err;
+    }
+    /* move viewport to upper left corner */
+    if (fb_var.xoffset != 0 || fb_var.yoffset != 0) {
+	fb_var.xoffset = 0;
+	fb_var.yoffset = 0;
+	if (ioctl(fb,FBIOPAN_DISPLAY,&fb_var) < 0) {
+	    perror("ioctl FBIOPAN_DISPLAY");
+	    goto err;
+	}
+    }
+    if (ioctl(tty,KDSETMODE, KD_GRAPHICS) < 0) {
+	perror("ioctl KDSETMODE");
+	goto err;
+    }
+    /* some fb drivers need this again after switching to graphics ... */
+    fbdev_activate_vt(tty, vtno, true);
+
+    fbdev_cls();
+
+    start_mediumraw(tty);
+    qemu_set_fd_handler(tty, read_mediumraw, NULL, NULL);
+
+    /* create PixelFormat from fbdev structs */
+    fbpf.bits_per_pixel  = fb_var.bits_per_pixel;
+    fbpf.bytes_per_pixel = (fb_var.bits_per_pixel+7)/8;
+    fbpf.depth           = fb_var.bits_per_pixel == 32 ? 24 : fb_var.bits_per_pixel;
+    fbpf.rshift          = fb_var.red.offset;
+    fbpf.rbits           = fb_var.red.length;
+    fbpf.gshift          = fb_var.green.offset;
+    fbpf.gbits           = fb_var.green.length;
+    fbpf.bshift          = fb_var.blue.offset;
+    fbpf.bbits           = fb_var.blue.length;
+    fbpf.ashift          = fb_var.transp.offset;
+    fbpf.abits           = fb_var.transp.length;
+
+    if (fbpf.rbits) {
+        fbpf.rmax   = (1 << fbpf.rbits) - 1;
+        fbpf.rmask  = fbpf.rmax << fbpf.rshift;
+    }
+    if (fbpf.gbits) {
+        fbpf.gmax   = (1 << fbpf.gbits) - 1;
+        fbpf.gmask  = fbpf.gmax << fbpf.gshift;
+    }
+    if (fbpf.bbits) {
+        fbpf.bmax   = (1 << fbpf.bbits) - 1;
+        fbpf.bmask  = fbpf.bmax << fbpf.bshift;
+    }
+    if (fbpf.abits) {
+        fbpf.amax   = (1 << fbpf.abits) - 1;
+        fbpf.amask  = fbpf.amax << fbpf.ashift;
+    }
+    return 0;
+
+ err:
+    fbdev_cleanup();
+    return -1;
+}
+
+static void
+fbdev_catch_fatal_signal(int signal)
+{
+    fbdev_cleanup();
+    abort();
+}
+
+static void fbdev_catch_exit_signals(void)
+{
+    struct sigaction act,old;
+
+    memset(&act,0,sizeof(act));
+    act.sa_handler = fbdev_catch_fatal_signal;
+    sigemptyset(&act.sa_mask);
+    sigaction(SIGSEGV,&act,&old);
+}
+
+/* -------------------------------------------------------------------- */
+/* console switching                                                    */
+
+static void fbdev_switch_signal(int signal)
+{
+    if (signal == SIG_REL) {
+	/* release */
+        if (debug)
+            fprintf(stderr, "%s: release request\n", __FUNCTION__);
+	fb_switch_state = FB_REL_REQ;
+    }
+    if (signal == SIG_ACQ) {
+	/* acquisition */
+        if (debug)
+            fprintf(stderr, "%s: aquire request\n", __FUNCTION__);
+	fb_switch_state = FB_ACQ_REQ;
+    }
+}
+
+static void fbdev_switch_release(void)
+{
+    if (debug)
+        fprintf(stderr, "%s\n", __FUNCTION__);
+    stop_mediumraw(tty);
+    ioctl(tty, VT_RELDISP, 1);
+    fb_switch_state = FB_INACTIVE;
+}
+
+static void fbdev_switch_acquire(void)
+{
+    if (debug)
+        fprintf(stderr, "%s\n", __FUNCTION__);
+    ioctl(tty, VT_RELDISP, VT_ACKACQ);
+    start_mediumraw(tty);
+    reset_keys();
+    fb_switch_state = FB_ACTIVE;
+}
+
+static int fbdev_switch_init(void)
+{
+    struct sigaction act,old;
+
+    memset(&act,0,sizeof(act));
+    act.sa_handler  = fbdev_switch_signal;
+    sigemptyset(&act.sa_mask);
+    sigaction(SIG_REL,&act,&old);
+    sigaction(SIG_ACQ,&act,&old);
+
+    if (ioctl(tty, VT_GETMODE, &vt_mode) < 0) {
+	perror("ioctl VT_GETMODE");
+	exit(1);
+    }
+    vt_mode.mode   = VT_PROCESS;
+    vt_mode.waitv  = 0;
+    vt_mode.relsig = SIG_REL;
+    vt_mode.acqsig = SIG_ACQ;
+
+    if (ioctl(tty, VT_SETMODE, &vt_mode) < 0) {
+	perror("ioctl VT_SETMODE");
+	exit(1);
+    }
+    return 0;
+}
+
+/* -------------------------------------------------------------------- */
+/* rendering                                                            */
+
+static void fbdev_render(DisplayState *ds, int x, int y, int w, int h)
+{
+    uint8_t *dst;
+    uint8_t *src;
+    int line;
+
+    if (!conv)
+        return;
+
+    src = ds_get_data(ds) + y * ds_get_linesize(ds)
+        + x * ds_get_bytes_per_pixel(ds);
+    dst = fb_mem + y * fb_fix.line_length
+        + x * fbpf.bytes_per_pixel;
+
+    dst += cy * fb_fix.line_length;
+    dst += cx * fbpf.bytes_per_pixel;
+
+    if (h > fb_var.yres - y) {
+        h = fb_var.yres - y;
+    }
+    if (w > fb_var.xres - x) {
+        w = fb_var.xres - x;
+    }
+
+    for (line = y; line < y+h; line++) {
+        qemu_pf_conv_run(conv, dst, src, w);
+        dst += fb_fix.line_length;
+        src += ds_get_linesize(ds);
+    }
+}
+
+/* -------------------------------------------------------------------- */
+/* qemu interfaces                                                      */
+
+static void fbdev_update(DisplayState *ds, int x, int y, int w, int h)
+{
+    if (fb_switch_state != FB_ACTIVE)
+        return;
+
+    if (resize_screen) {
+        if (debug)
+            fprintf(stderr, "%s: handle resize\n", __FUNCTION__);
+        resize_screen = 0;
+        cx = 0; cy = 0;
+        cw = ds_get_width(ds);
+        ch = ds_get_height(ds);
+        if (ds_get_width(ds) < fb_var.xres) {
+            cx = (fb_var.xres - ds_get_width(ds)) / 2;
+        }
+        if (ds_get_height(ds) < fb_var.yres) {
+            cy = (fb_var.yres - ds_get_height(ds)) / 2;
+        }
+
+        if (conv) {
+            qemu_pf_conv_put(conv);
+        }
+        conv = qemu_pf_conv_get(&fbpf, &ds->surface->pf);
+        if (conv == NULL) {
+            fprintf(stderr, "fbdev: unsupported PixelFormat conversion\n");
+        }
+    }
+
+    if (redraw_screen) {
+        if (debug)
+            fprintf(stderr, "%s: handle redraw\n", __FUNCTION__);
+        redraw_screen = 0;
+        fbdev_cls();
+        x = 0; y = 0; w = ds_get_width(ds); h = ds_get_height(ds);
+    }
+
+    fbdev_render(ds, x, y, w, h);
+}
+
+static void fbdev_resize(DisplayState *ds)
+{
+    if (debug)
+        fprintf(stderr, "%s: request resize+redraw\n", __FUNCTION__);
+    resize_screen++;
+    redraw_screen++;
+}
+
+static void fbdev_refresh(DisplayState *ds)
+{
+    switch (fb_switch_state) {
+    case FB_REL_REQ:
+        fbdev_switch_release();
+    case FB_INACTIVE:
+        return;
+    case FB_ACQ_REQ:
+        fbdev_switch_acquire();
+        redraw_screen++;
+        if (debug)
+            fprintf(stderr, "%s: request redraw\n", __FUNCTION__);
+    case FB_ACTIVE:
+        break;
+    }
+
+    vga_hw_update();
+    if (redraw_screen) {
+        fbdev_update(ds, 0, 0, 0, 0);
+    }
+}
+
+static void fbdev_exit_notifier(Notifier *notifier)
+{
+    fbdev_cleanup();
+}
+
+void fbdev_display_init(DisplayState *ds, const char *device)
+{
+    if (dcl != NULL) {
+        if (debug)
+            fprintf(stderr, "%s: already active\n", __FUNCTION__);
+        return;
+    }
+
+    if (fbdev_init(device) != 0) {
+        exit(1);
+    }
+    exit_notifier.notify = fbdev_exit_notifier;
+    qemu_add_exit_notifier(&exit_notifier);
+    fbdev_switch_init();
+    fbdev_catch_exit_signals();
+    init_mouse();
+
+    fbdev_resize(ds);
+    dcl = qemu_mallocz(sizeof(DisplayChangeListener));
+    dcl->dpy_update  = fbdev_update;
+    dcl->dpy_resize  = fbdev_resize;
+    dcl->dpy_refresh = fbdev_refresh;
+    register_displaychangelistener(ds, dcl);
+}
+
+void fbdev_display_uninit(void)
+{
+    if (dcl == NULL) {
+        if (debug)
+            fprintf(stderr, "%s: not active\n", __FUNCTION__);
+        return;
+    }
+
+    unregister_displaychangelistener(dcl);
+    qemu_free(dcl);
+    dcl = NULL;
+
+    fbdev_cleanup();
+    qemu_remove_exit_notifier(&exit_notifier);
+    uninit_mouse();
+}
diff --git a/linux-keynames.h b/linux-keynames.h
new file mode 100644
index 0000000..5f08f02
--- /dev/null
+++ b/linux-keynames.h
@@ -0,0 +1,386 @@
+/*
+ *   awk '/#define KEY_/ { printf("    [ %-16s ] = \"%s\",\n",$2,$2); }' \
+ *       /usr/include/linux/input.h
+ */
+    [ KEY_RESERVED     ] = "KEY_RESERVED",
+    [ KEY_ESC          ] = "KEY_ESC",
+    [ KEY_1            ] = "KEY_1",
+    [ KEY_2            ] = "KEY_2",
+    [ KEY_3            ] = "KEY_3",
+    [ KEY_4            ] = "KEY_4",
+    [ KEY_5            ] = "KEY_5",
+    [ KEY_6            ] = "KEY_6",
+    [ KEY_7            ] = "KEY_7",
+    [ KEY_8            ] = "KEY_8",
+    [ KEY_9            ] = "KEY_9",
+    [ KEY_0            ] = "KEY_0",
+    [ KEY_MINUS        ] = "KEY_MINUS",
+    [ KEY_EQUAL        ] = "KEY_EQUAL",
+    [ KEY_BACKSPACE    ] = "KEY_BACKSPACE",
+    [ KEY_TAB          ] = "KEY_TAB",
+    [ KEY_Q            ] = "KEY_Q",
+    [ KEY_W            ] = "KEY_W",
+    [ KEY_E            ] = "KEY_E",
+    [ KEY_R            ] = "KEY_R",
+    [ KEY_T            ] = "KEY_T",
+    [ KEY_Y            ] = "KEY_Y",
+    [ KEY_U            ] = "KEY_U",
+    [ KEY_I            ] = "KEY_I",
+    [ KEY_O            ] = "KEY_O",
+    [ KEY_P            ] = "KEY_P",
+    [ KEY_LEFTBRACE    ] = "KEY_LEFTBRACE",
+    [ KEY_RIGHTBRACE   ] = "KEY_RIGHTBRACE",
+    [ KEY_ENTER        ] = "KEY_ENTER",
+    [ KEY_LEFTCTRL     ] = "KEY_LEFTCTRL",
+    [ KEY_A            ] = "KEY_A",
+    [ KEY_S            ] = "KEY_S",
+    [ KEY_D            ] = "KEY_D",
+    [ KEY_F            ] = "KEY_F",
+    [ KEY_G            ] = "KEY_G",
+    [ KEY_H            ] = "KEY_H",
+    [ KEY_J            ] = "KEY_J",
+    [ KEY_K            ] = "KEY_K",
+    [ KEY_L            ] = "KEY_L",
+    [ KEY_SEMICOLON    ] = "KEY_SEMICOLON",
+    [ KEY_APOSTROPHE   ] = "KEY_APOSTROPHE",
+    [ KEY_GRAVE        ] = "KEY_GRAVE",
+    [ KEY_LEFTSHIFT    ] = "KEY_LEFTSHIFT",
+    [ KEY_BACKSLASH    ] = "KEY_BACKSLASH",
+    [ KEY_Z            ] = "KEY_Z",
+    [ KEY_X            ] = "KEY_X",
+    [ KEY_C            ] = "KEY_C",
+    [ KEY_V            ] = "KEY_V",
+    [ KEY_B            ] = "KEY_B",
+    [ KEY_N            ] = "KEY_N",
+    [ KEY_M            ] = "KEY_M",
+    [ KEY_COMMA        ] = "KEY_COMMA",
+    [ KEY_DOT          ] = "KEY_DOT",
+    [ KEY_SLASH        ] = "KEY_SLASH",
+    [ KEY_RIGHTSHIFT   ] = "KEY_RIGHTSHIFT",
+    [ KEY_KPASTERISK   ] = "KEY_KPASTERISK",
+    [ KEY_LEFTALT      ] = "KEY_LEFTALT",
+    [ KEY_SPACE        ] = "KEY_SPACE",
+    [ KEY_CAPSLOCK     ] = "KEY_CAPSLOCK",
+    [ KEY_F1           ] = "KEY_F1",
+    [ KEY_F2           ] = "KEY_F2",
+    [ KEY_F3           ] = "KEY_F3",
+    [ KEY_F4           ] = "KEY_F4",
+    [ KEY_F5           ] = "KEY_F5",
+    [ KEY_F6           ] = "KEY_F6",
+    [ KEY_F7           ] = "KEY_F7",
+    [ KEY_F8           ] = "KEY_F8",
+    [ KEY_F9           ] = "KEY_F9",
+    [ KEY_F10          ] = "KEY_F10",
+    [ KEY_NUMLOCK      ] = "KEY_NUMLOCK",
+    [ KEY_SCROLLLOCK   ] = "KEY_SCROLLLOCK",
+    [ KEY_KP7          ] = "KEY_KP7",
+    [ KEY_KP8          ] = "KEY_KP8",
+    [ KEY_KP9          ] = "KEY_KP9",
+    [ KEY_KPMINUS      ] = "KEY_KPMINUS",
+    [ KEY_KP4          ] = "KEY_KP4",
+    [ KEY_KP5          ] = "KEY_KP5",
+    [ KEY_KP6          ] = "KEY_KP6",
+    [ KEY_KPPLUS       ] = "KEY_KPPLUS",
+    [ KEY_KP1          ] = "KEY_KP1",
+    [ KEY_KP2          ] = "KEY_KP2",
+    [ KEY_KP3          ] = "KEY_KP3",
+    [ KEY_KP0          ] = "KEY_KP0",
+    [ KEY_KPDOT        ] = "KEY_KPDOT",
+    [ KEY_ZENKAKUHANKAKU ] = "KEY_ZENKAKUHANKAKU",
+    [ KEY_102ND        ] = "KEY_102ND",
+    [ KEY_F11          ] = "KEY_F11",
+    [ KEY_F12          ] = "KEY_F12",
+    [ KEY_RO           ] = "KEY_RO",
+    [ KEY_KATAKANA     ] = "KEY_KATAKANA",
+    [ KEY_HIRAGANA     ] = "KEY_HIRAGANA",
+    [ KEY_HENKAN       ] = "KEY_HENKAN",
+    [ KEY_KATAKANAHIRAGANA ] = "KEY_KATAKANAHIRAGANA",
+    [ KEY_MUHENKAN     ] = "KEY_MUHENKAN",
+    [ KEY_KPJPCOMMA    ] = "KEY_KPJPCOMMA",
+    [ KEY_KPENTER      ] = "KEY_KPENTER",
+    [ KEY_RIGHTCTRL    ] = "KEY_RIGHTCTRL",
+    [ KEY_KPSLASH      ] = "KEY_KPSLASH",
+    [ KEY_SYSRQ        ] = "KEY_SYSRQ",
+    [ KEY_RIGHTALT     ] = "KEY_RIGHTALT",
+    [ KEY_LINEFEED     ] = "KEY_LINEFEED",
+    [ KEY_HOME         ] = "KEY_HOME",
+    [ KEY_UP           ] = "KEY_UP",
+    [ KEY_PAGEUP       ] = "KEY_PAGEUP",
+    [ KEY_LEFT         ] = "KEY_LEFT",
+    [ KEY_RIGHT        ] = "KEY_RIGHT",
+    [ KEY_END          ] = "KEY_END",
+    [ KEY_DOWN         ] = "KEY_DOWN",
+    [ KEY_PAGEDOWN     ] = "KEY_PAGEDOWN",
+    [ KEY_INSERT       ] = "KEY_INSERT",
+    [ KEY_DELETE       ] = "KEY_DELETE",
+    [ KEY_MACRO        ] = "KEY_MACRO",
+    [ KEY_MUTE         ] = "KEY_MUTE",
+    [ KEY_VOLUMEDOWN   ] = "KEY_VOLUMEDOWN",
+    [ KEY_VOLUMEUP     ] = "KEY_VOLUMEUP",
+    [ KEY_POWER        ] = "KEY_POWER",
+    [ KEY_KPEQUAL      ] = "KEY_KPEQUAL",
+    [ KEY_KPPLUSMINUS  ] = "KEY_KPPLUSMINUS",
+    [ KEY_PAUSE        ] = "KEY_PAUSE",
+    [ KEY_SCALE        ] = "KEY_SCALE",
+    [ KEY_KPCOMMA      ] = "KEY_KPCOMMA",
+    [ KEY_HANGEUL      ] = "KEY_HANGEUL",
+    [ KEY_HANGUEL      ] = "KEY_HANGUEL",
+    [ KEY_HANJA        ] = "KEY_HANJA",
+    [ KEY_YEN          ] = "KEY_YEN",
+    [ KEY_LEFTMETA     ] = "KEY_LEFTMETA",
+    [ KEY_RIGHTMETA    ] = "KEY_RIGHTMETA",
+    [ KEY_COMPOSE      ] = "KEY_COMPOSE",
+    [ KEY_STOP         ] = "KEY_STOP",
+    [ KEY_AGAIN        ] = "KEY_AGAIN",
+    [ KEY_PROPS        ] = "KEY_PROPS",
+    [ KEY_UNDO         ] = "KEY_UNDO",
+    [ KEY_FRONT        ] = "KEY_FRONT",
+    [ KEY_COPY         ] = "KEY_COPY",
+    [ KEY_OPEN         ] = "KEY_OPEN",
+    [ KEY_PASTE        ] = "KEY_PASTE",
+    [ KEY_FIND         ] = "KEY_FIND",
+    [ KEY_CUT          ] = "KEY_CUT",
+    [ KEY_HELP         ] = "KEY_HELP",
+    [ KEY_MENU         ] = "KEY_MENU",
+    [ KEY_CALC         ] = "KEY_CALC",
+    [ KEY_SETUP        ] = "KEY_SETUP",
+    [ KEY_SLEEP        ] = "KEY_SLEEP",
+    [ KEY_WAKEUP       ] = "KEY_WAKEUP",
+    [ KEY_FILE         ] = "KEY_FILE",
+    [ KEY_SENDFILE     ] = "KEY_SENDFILE",
+    [ KEY_DELETEFILE   ] = "KEY_DELETEFILE",
+    [ KEY_XFER         ] = "KEY_XFER",
+    [ KEY_PROG1        ] = "KEY_PROG1",
+    [ KEY_PROG2        ] = "KEY_PROG2",
+    [ KEY_WWW          ] = "KEY_WWW",
+    [ KEY_MSDOS        ] = "KEY_MSDOS",
+    [ KEY_COFFEE       ] = "KEY_COFFEE",
+    [ KEY_SCREENLOCK   ] = "KEY_SCREENLOCK",
+    [ KEY_DIRECTION    ] = "KEY_DIRECTION",
+    [ KEY_CYCLEWINDOWS ] = "KEY_CYCLEWINDOWS",
+    [ KEY_MAIL         ] = "KEY_MAIL",
+    [ KEY_BOOKMARKS    ] = "KEY_BOOKMARKS",
+    [ KEY_COMPUTER     ] = "KEY_COMPUTER",
+    [ KEY_BACK         ] = "KEY_BACK",
+    [ KEY_FORWARD      ] = "KEY_FORWARD",
+    [ KEY_CLOSECD      ] = "KEY_CLOSECD",
+    [ KEY_EJECTCD      ] = "KEY_EJECTCD",
+    [ KEY_EJECTCLOSECD ] = "KEY_EJECTCLOSECD",
+    [ KEY_NEXTSONG     ] = "KEY_NEXTSONG",
+    [ KEY_PLAYPAUSE    ] = "KEY_PLAYPAUSE",
+    [ KEY_PREVIOUSSONG ] = "KEY_PREVIOUSSONG",
+    [ KEY_STOPCD       ] = "KEY_STOPCD",
+    [ KEY_RECORD       ] = "KEY_RECORD",
+    [ KEY_REWIND       ] = "KEY_REWIND",
+    [ KEY_PHONE        ] = "KEY_PHONE",
+    [ KEY_ISO          ] = "KEY_ISO",
+    [ KEY_CONFIG       ] = "KEY_CONFIG",
+    [ KEY_HOMEPAGE     ] = "KEY_HOMEPAGE",
+    [ KEY_REFRESH      ] = "KEY_REFRESH",
+    [ KEY_EXIT         ] = "KEY_EXIT",
+    [ KEY_MOVE         ] = "KEY_MOVE",
+    [ KEY_EDIT         ] = "KEY_EDIT",
+    [ KEY_SCROLLUP     ] = "KEY_SCROLLUP",
+    [ KEY_SCROLLDOWN   ] = "KEY_SCROLLDOWN",
+    [ KEY_KPLEFTPAREN  ] = "KEY_KPLEFTPAREN",
+    [ KEY_KPRIGHTPAREN ] = "KEY_KPRIGHTPAREN",
+    [ KEY_NEW          ] = "KEY_NEW",
+    [ KEY_REDO         ] = "KEY_REDO",
+    [ KEY_F13          ] = "KEY_F13",
+    [ KEY_F14          ] = "KEY_F14",
+    [ KEY_F15          ] = "KEY_F15",
+    [ KEY_F16          ] = "KEY_F16",
+    [ KEY_F17          ] = "KEY_F17",
+    [ KEY_F18          ] = "KEY_F18",
+    [ KEY_F19          ] = "KEY_F19",
+    [ KEY_F20          ] = "KEY_F20",
+    [ KEY_F21          ] = "KEY_F21",
+    [ KEY_F22          ] = "KEY_F22",
+    [ KEY_F23          ] = "KEY_F23",
+    [ KEY_F24          ] = "KEY_F24",
+    [ KEY_PLAYCD       ] = "KEY_PLAYCD",
+    [ KEY_PAUSECD      ] = "KEY_PAUSECD",
+    [ KEY_PROG3        ] = "KEY_PROG3",
+    [ KEY_PROG4        ] = "KEY_PROG4",
+    [ KEY_DASHBOARD    ] = "KEY_DASHBOARD",
+    [ KEY_SUSPEND      ] = "KEY_SUSPEND",
+    [ KEY_CLOSE        ] = "KEY_CLOSE",
+    [ KEY_PLAY         ] = "KEY_PLAY",
+    [ KEY_FASTFORWARD  ] = "KEY_FASTFORWARD",
+    [ KEY_BASSBOOST    ] = "KEY_BASSBOOST",
+    [ KEY_PRINT        ] = "KEY_PRINT",
+    [ KEY_HP           ] = "KEY_HP",
+    [ KEY_CAMERA       ] = "KEY_CAMERA",
+    [ KEY_SOUND        ] = "KEY_SOUND",
+    [ KEY_QUESTION     ] = "KEY_QUESTION",
+    [ KEY_EMAIL        ] = "KEY_EMAIL",
+    [ KEY_CHAT         ] = "KEY_CHAT",
+    [ KEY_SEARCH       ] = "KEY_SEARCH",
+    [ KEY_CONNECT      ] = "KEY_CONNECT",
+    [ KEY_FINANCE      ] = "KEY_FINANCE",
+    [ KEY_SPORT        ] = "KEY_SPORT",
+    [ KEY_SHOP         ] = "KEY_SHOP",
+    [ KEY_ALTERASE     ] = "KEY_ALTERASE",
+    [ KEY_CANCEL       ] = "KEY_CANCEL",
+    [ KEY_BRIGHTNESSDOWN ] = "KEY_BRIGHTNESSDOWN",
+    [ KEY_BRIGHTNESSUP ] = "KEY_BRIGHTNESSUP",
+    [ KEY_MEDIA        ] = "KEY_MEDIA",
+    [ KEY_SWITCHVIDEOMODE ] = "KEY_SWITCHVIDEOMODE",
+    [ KEY_KBDILLUMTOGGLE ] = "KEY_KBDILLUMTOGGLE",
+    [ KEY_KBDILLUMDOWN ] = "KEY_KBDILLUMDOWN",
+    [ KEY_KBDILLUMUP   ] = "KEY_KBDILLUMUP",
+    [ KEY_SEND         ] = "KEY_SEND",
+    [ KEY_REPLY        ] = "KEY_REPLY",
+    [ KEY_FORWARDMAIL  ] = "KEY_FORWARDMAIL",
+    [ KEY_SAVE         ] = "KEY_SAVE",
+    [ KEY_DOCUMENTS    ] = "KEY_DOCUMENTS",
+    [ KEY_BATTERY      ] = "KEY_BATTERY",
+    [ KEY_BLUETOOTH    ] = "KEY_BLUETOOTH",
+    [ KEY_WLAN         ] = "KEY_WLAN",
+    [ KEY_UWB          ] = "KEY_UWB",
+    [ KEY_UNKNOWN      ] = "KEY_UNKNOWN",
+    [ KEY_VIDEO_NEXT   ] = "KEY_VIDEO_NEXT",
+    [ KEY_VIDEO_PREV   ] = "KEY_VIDEO_PREV",
+    [ KEY_BRIGHTNESS_CYCLE ] = "KEY_BRIGHTNESS_CYCLE",
+    [ KEY_BRIGHTNESS_ZERO ] = "KEY_BRIGHTNESS_ZERO",
+    [ KEY_DISPLAY_OFF  ] = "KEY_DISPLAY_OFF",
+    [ KEY_WIMAX        ] = "KEY_WIMAX",
+    [ KEY_OK           ] = "KEY_OK",
+    [ KEY_SELECT       ] = "KEY_SELECT",
+    [ KEY_GOTO         ] = "KEY_GOTO",
+    [ KEY_CLEAR        ] = "KEY_CLEAR",
+    [ KEY_POWER2       ] = "KEY_POWER2",
+    [ KEY_OPTION       ] = "KEY_OPTION",
+    [ KEY_INFO         ] = "KEY_INFO",
+    [ KEY_TIME         ] = "KEY_TIME",
+    [ KEY_VENDOR       ] = "KEY_VENDOR",
+    [ KEY_ARCHIVE      ] = "KEY_ARCHIVE",
+    [ KEY_PROGRAM      ] = "KEY_PROGRAM",
+    [ KEY_CHANNEL      ] = "KEY_CHANNEL",
+    [ KEY_FAVORITES    ] = "KEY_FAVORITES",
+    [ KEY_EPG          ] = "KEY_EPG",
+    [ KEY_PVR          ] = "KEY_PVR",
+    [ KEY_MHP          ] = "KEY_MHP",
+    [ KEY_LANGUAGE     ] = "KEY_LANGUAGE",
+    [ KEY_TITLE        ] = "KEY_TITLE",
+    [ KEY_SUBTITLE     ] = "KEY_SUBTITLE",
+    [ KEY_ANGLE        ] = "KEY_ANGLE",
+    [ KEY_ZOOM         ] = "KEY_ZOOM",
+    [ KEY_MODE         ] = "KEY_MODE",
+    [ KEY_KEYBOARD     ] = "KEY_KEYBOARD",
+    [ KEY_SCREEN       ] = "KEY_SCREEN",
+    [ KEY_PC           ] = "KEY_PC",
+    [ KEY_TV           ] = "KEY_TV",
+    [ KEY_TV2          ] = "KEY_TV2",
+    [ KEY_VCR          ] = "KEY_VCR",
+    [ KEY_VCR2         ] = "KEY_VCR2",
+    [ KEY_SAT          ] = "KEY_SAT",
+    [ KEY_SAT2         ] = "KEY_SAT2",
+    [ KEY_CD           ] = "KEY_CD",
+    [ KEY_TAPE         ] = "KEY_TAPE",
+    [ KEY_RADIO        ] = "KEY_RADIO",
+    [ KEY_TUNER        ] = "KEY_TUNER",
+    [ KEY_PLAYER       ] = "KEY_PLAYER",
+    [ KEY_TEXT         ] = "KEY_TEXT",
+    [ KEY_DVD          ] = "KEY_DVD",
+    [ KEY_AUX          ] = "KEY_AUX",
+    [ KEY_MP3          ] = "KEY_MP3",
+    [ KEY_AUDIO        ] = "KEY_AUDIO",
+    [ KEY_VIDEO        ] = "KEY_VIDEO",
+    [ KEY_DIRECTORY    ] = "KEY_DIRECTORY",
+    [ KEY_LIST         ] = "KEY_LIST",
+    [ KEY_MEMO         ] = "KEY_MEMO",
+    [ KEY_CALENDAR     ] = "KEY_CALENDAR",
+    [ KEY_RED          ] = "KEY_RED",
+    [ KEY_GREEN        ] = "KEY_GREEN",
+    [ KEY_YELLOW       ] = "KEY_YELLOW",
+    [ KEY_BLUE         ] = "KEY_BLUE",
+    [ KEY_CHANNELUP    ] = "KEY_CHANNELUP",
+    [ KEY_CHANNELDOWN  ] = "KEY_CHANNELDOWN",
+    [ KEY_FIRST        ] = "KEY_FIRST",
+    [ KEY_LAST         ] = "KEY_LAST",
+    [ KEY_AB           ] = "KEY_AB",
+    [ KEY_NEXT         ] = "KEY_NEXT",
+    [ KEY_RESTART      ] = "KEY_RESTART",
+    [ KEY_SLOW         ] = "KEY_SLOW",
+    [ KEY_SHUFFLE      ] = "KEY_SHUFFLE",
+    [ KEY_BREAK        ] = "KEY_BREAK",
+    [ KEY_PREVIOUS     ] = "KEY_PREVIOUS",
+    [ KEY_DIGITS       ] = "KEY_DIGITS",
+    [ KEY_TEEN         ] = "KEY_TEEN",
+    [ KEY_TWEN         ] = "KEY_TWEN",
+    [ KEY_VIDEOPHONE   ] = "KEY_VIDEOPHONE",
+    [ KEY_GAMES        ] = "KEY_GAMES",
+    [ KEY_ZOOMIN       ] = "KEY_ZOOMIN",
+    [ KEY_ZOOMOUT      ] = "KEY_ZOOMOUT",
+    [ KEY_ZOOMRESET    ] = "KEY_ZOOMRESET",
+    [ KEY_WORDPROCESSOR ] = "KEY_WORDPROCESSOR",
+    [ KEY_EDITOR       ] = "KEY_EDITOR",
+    [ KEY_SPREADSHEET  ] = "KEY_SPREADSHEET",
+    [ KEY_GRAPHICSEDITOR ] = "KEY_GRAPHICSEDITOR",
+    [ KEY_PRESENTATION ] = "KEY_PRESENTATION",
+    [ KEY_DATABASE     ] = "KEY_DATABASE",
+    [ KEY_NEWS         ] = "KEY_NEWS",
+    [ KEY_VOICEMAIL    ] = "KEY_VOICEMAIL",
+    [ KEY_ADDRESSBOOK  ] = "KEY_ADDRESSBOOK",
+    [ KEY_MESSENGER    ] = "KEY_MESSENGER",
+    [ KEY_DISPLAYTOGGLE ] = "KEY_DISPLAYTOGGLE",
+    [ KEY_SPELLCHECK   ] = "KEY_SPELLCHECK",
+    [ KEY_LOGOFF       ] = "KEY_LOGOFF",
+    [ KEY_DOLLAR       ] = "KEY_DOLLAR",
+    [ KEY_EURO         ] = "KEY_EURO",
+    [ KEY_FRAMEBACK    ] = "KEY_FRAMEBACK",
+    [ KEY_FRAMEFORWARD ] = "KEY_FRAMEFORWARD",
+    [ KEY_CONTEXT_MENU ] = "KEY_CONTEXT_MENU",
+    [ KEY_MEDIA_REPEAT ] = "KEY_MEDIA_REPEAT",
+    [ KEY_DEL_EOL      ] = "KEY_DEL_EOL",
+    [ KEY_DEL_EOS      ] = "KEY_DEL_EOS",
+    [ KEY_INS_LINE     ] = "KEY_INS_LINE",
+    [ KEY_DEL_LINE     ] = "KEY_DEL_LINE",
+    [ KEY_FN           ] = "KEY_FN",
+    [ KEY_FN_ESC       ] = "KEY_FN_ESC",
+    [ KEY_FN_F1        ] = "KEY_FN_F1",
+    [ KEY_FN_F2        ] = "KEY_FN_F2",
+    [ KEY_FN_F3        ] = "KEY_FN_F3",
+    [ KEY_FN_F4        ] = "KEY_FN_F4",
+    [ KEY_FN_F5        ] = "KEY_FN_F5",
+    [ KEY_FN_F6        ] = "KEY_FN_F6",
+    [ KEY_FN_F7        ] = "KEY_FN_F7",
+    [ KEY_FN_F8        ] = "KEY_FN_F8",
+    [ KEY_FN_F9        ] = "KEY_FN_F9",
+    [ KEY_FN_F10       ] = "KEY_FN_F10",
+    [ KEY_FN_F11       ] = "KEY_FN_F11",
+    [ KEY_FN_F12       ] = "KEY_FN_F12",
+    [ KEY_FN_1         ] = "KEY_FN_1",
+    [ KEY_FN_2         ] = "KEY_FN_2",
+    [ KEY_FN_D         ] = "KEY_FN_D",
+    [ KEY_FN_E         ] = "KEY_FN_E",
+    [ KEY_FN_F         ] = "KEY_FN_F",
+    [ KEY_FN_S         ] = "KEY_FN_S",
+    [ KEY_FN_B         ] = "KEY_FN_B",
+    [ KEY_BRL_DOT1     ] = "KEY_BRL_DOT1",
+    [ KEY_BRL_DOT2     ] = "KEY_BRL_DOT2",
+    [ KEY_BRL_DOT3     ] = "KEY_BRL_DOT3",
+    [ KEY_BRL_DOT4     ] = "KEY_BRL_DOT4",
+    [ KEY_BRL_DOT5     ] = "KEY_BRL_DOT5",
+    [ KEY_BRL_DOT6     ] = "KEY_BRL_DOT6",
+    [ KEY_BRL_DOT7     ] = "KEY_BRL_DOT7",
+    [ KEY_BRL_DOT8     ] = "KEY_BRL_DOT8",
+    [ KEY_BRL_DOT9     ] = "KEY_BRL_DOT9",
+    [ KEY_BRL_DOT10    ] = "KEY_BRL_DOT10",
+    [ KEY_NUMERIC_0    ] = "KEY_NUMERIC_0",
+    [ KEY_NUMERIC_1    ] = "KEY_NUMERIC_1",
+    [ KEY_NUMERIC_2    ] = "KEY_NUMERIC_2",
+    [ KEY_NUMERIC_3    ] = "KEY_NUMERIC_3",
+    [ KEY_NUMERIC_4    ] = "KEY_NUMERIC_4",
+    [ KEY_NUMERIC_5    ] = "KEY_NUMERIC_5",
+    [ KEY_NUMERIC_6    ] = "KEY_NUMERIC_6",
+    [ KEY_NUMERIC_7    ] = "KEY_NUMERIC_7",
+    [ KEY_NUMERIC_8    ] = "KEY_NUMERIC_8",
+    [ KEY_NUMERIC_9    ] = "KEY_NUMERIC_9",
+    [ KEY_NUMERIC_STAR ] = "KEY_NUMERIC_STAR",
+    [ KEY_NUMERIC_POUND ] = "KEY_NUMERIC_POUND",
+    [ KEY_RFKILL       ] = "KEY_RFKILL",
+    [ KEY_MIN_INTERESTING ] = "KEY_MIN_INTERESTING",
+    [ KEY_MAX          ] = "KEY_MAX",
+    [ KEY_CNT          ] = "KEY_CNT",
diff --git a/monitor.c b/monitor.c
index 05a7ed1..7442f1a 100644
--- a/monitor.c
+++ b/monitor.c
@@ -959,6 +959,18 @@ static int do_change_vnc(Monitor *mon, const char *target, const char *arg)
     return 0;
 }
 
+static int do_change_fbdev(Monitor *mon, const char *target)
+{
+#ifdef CONFIG_LINUX
+    if (strcmp(target, "on") == 0) {
+        fbdev_display_init(get_displaystate(), NULL);
+    } else {
+        fbdev_display_uninit();
+    }
+    return 0;
+#endif
+}
+
 /**
  * do_change(): Change a removable medium, or VNC configuration
  */
@@ -971,6 +983,8 @@ static int do_change(Monitor *mon, const QDict *qdict, QObject **ret_data)
 
     if (strcmp(device, "vnc") == 0) {
         ret = do_change_vnc(mon, target, arg);
+    } else if (strcmp(device, "fbdev") == 0) {
+        ret = do_change_fbdev(mon, target);
     } else {
         ret = do_change_block(mon, device, target, arg);
     }
diff --git a/qemu-options.hx b/qemu-options.hx
index a6928b7..8224a32 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -667,6 +667,16 @@ STEXI
 Enable SDL.
 ETEXI
 
+#ifdef CONFIG_LINUX
+DEF("fbdev", 0, QEMU_OPTION_fbdev,
+    "-fbdev          enable fbdev\n", QEMU_ARCH_ALL)
+#endif
+STEXI
+@item -fbdev
+@findex -fbdev
+Enable fbdev (linux framebuffer).
+ETEXI
+
 DEF("portrait", 0, QEMU_OPTION_portrait,
     "-portrait       rotate graphical output 90 deg left (only PXA LCD)\n",
     QEMU_ARCH_ALL)
diff --git a/sysemu.h b/sysemu.h
index c758243..e40fefd 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -95,6 +95,7 @@ typedef enum DisplayType
     DT_CURSES,
     DT_SDL,
     DT_VNC,
+    DT_FBDEV,
     DT_NOGRAPHIC,
 } DisplayType;
 
diff --git a/vl.c b/vl.c
index 8fc63c7..39e43fa 100644
--- a/vl.c
+++ b/vl.c
@@ -2400,6 +2400,11 @@ int main(int argc, char **argv, char **envp)
                 display_type = DT_SDL;
                 break;
 #endif
+#ifdef CONFIG_LINUX
+            case QEMU_OPTION_fbdev:
+                display_type = DT_FBDEV;
+                break;
+#endif
             case QEMU_OPTION_pidfile:
                 pid_file = optarg;
                 break;
@@ -2895,6 +2900,11 @@ int main(int argc, char **argv, char **envp)
         curses_display_init(ds, full_screen);
         break;
 #endif
+#if defined(CONFIG_LINUX)
+    case DT_FBDEV:
+        fbdev_display_init(ds, NULL);
+        break;
+#endif
 #if defined(CONFIG_SDL)
     case DT_SDL:
         sdl_display_init(ds, full_screen, no_frame);
-- 
1.6.5.2

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] Re: [PATCH 5/5] linux fbdev display driver.
  2010-06-15 10:05 ` [Qemu-devel] [PATCH 5/5] linux fbdev display driver Gerd Hoffmann
@ 2010-06-16 12:44   ` Stefano Stabellini
  2010-06-16 16:22     ` Julian Pidancet
  0 siblings, 1 reply; 14+ messages in thread
From: Stefano Stabellini @ 2010-06-16 12:44 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Stefano Stabellini, qemu-devel, Julian Pidancet

On Tue, 15 Jun 2010, Gerd Hoffmann wrote:
> Display works, requires truecolor framebuffer with 16 or 32 bpp on the
> host.  32bpp is recommended.  The framebuffer is used as-is, qemu
> doesn't try to switch modes.  With LCD displays mode switching is pretty
> pointless IMHO, also it wouldn't work anyway with the most common
> fbdev drivers (vesafb, KMS).  Guest display is centered on the host
> screen.
> 
> Mouse works, uses /dev/input/mice.
> 
> Keyboard works.  Guest screen has whatever keymap you load inside
> the guest.  Text windows (monitor, serial, ...) have a simple en-us
> keymap.  Good enougth to type monitor commands.  Not goot enougth to
> work seriously on a serial terminal.  But the qemu terminal emulation
> isn't good enougth for that anyway ;)
> 
> Hot keys:
>   Ctrl-Alt-F<nr>  -> host console switching.
>   Ctrl-Alt-<nr>   -> qemu console switching.
>   Ctrl-Alt-ESC    -> exit qemu.
> 
> Special feature:  Sane console switching.  Switching away stops screen
> updates.  Switching back redraws the screen.  When started from the
> linux console qemu uses the vt you've started it from (requires just
> read/write access to /dev/fb0).  When starting from somewhere else qemu
> tries to open a unused virtual terminal and switch to it (usually
> requires root privileges to open /dev/tty<nr>).
> 
> For some strange reason console switching from X11 to qemu doesn't work.
> Anything else (including X11 -> text console -> qemu) works fine.  To be
> investigated ...
> 
> Can be enabled/disabled via monitor, use "change fbdev [ on | off ]"

the patch still doesn't use the display allocator interface, but it
shouldn't be difficult to implement support for it on top of this patch,
so it is fine by me.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Qemu-devel] Re: [PATCH 5/5] linux fbdev display driver.
  2010-06-16 12:44   ` [Qemu-devel] " Stefano Stabellini
@ 2010-06-16 16:22     ` Julian Pidancet
  2010-06-17 10:43       ` Gerd Hoffmann
  0 siblings, 1 reply; 14+ messages in thread
From: Julian Pidancet @ 2010-06-16 16:22 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: Gerd Hoffmann, qemu-devel

On 06/16/2010 01:44 PM, Stefano Stabellini wrote:
> 
> the patch still doesn't use the display allocator interface, but it
> shouldn't be difficult to implement support for it on top of this patch,
> so it is fine by me.
> 

This patch adds display allocator support in the fbdev driver.
This way we avoid memcpying when the guest surface is compatible with the physical surface.

diff --git a/fbdev.c b/fbdev.c
index 54f2381..83104c0 100644
--- a/fbdev.c
+++ b/fbdev.c
@@ -69,11 +69,10 @@ static int fb_switch_state = FB_ACTIVE;
 static DisplayChangeListener      *dcl;
 static QemuPfConv                 *conv;
 static PixelFormat                fbpf;
-static int                        resize_screen;
-static int                        redraw_screen;
 static int                        cx, cy, cw, ch;
 static int                        debug = 0;
 static Notifier                   exit_notifier;
+uint8_t                           *guest_surface;
 
 /* fwd decls */
 static int fbdev_activate_vt(int tty, int vtno, bool wait);
@@ -786,10 +785,10 @@ static void fbdev_render(DisplayState *ds, int x, int y, int w, int h)
     uint8_t *src;
     int line;
 
-    if (!conv)
+    if (!conv || !guest_surface)
         return;
 
-    src = ds_get_data(ds) + y * ds_get_linesize(ds)
+    src = guest_surface + y * ds_get_linesize(ds)
         + x * ds_get_bytes_per_pixel(ds);
     dst = fb_mem + y * fb_fix.line_length
         + x * fbpf.bytes_per_pixel;
@@ -819,46 +818,50 @@ static void fbdev_update(DisplayState *ds, int x, int y, int w, int h)
     if (fb_switch_state != FB_ACTIVE)
         return;
 
-    if (resize_screen) {
-        if (debug)
-            fprintf(stderr, "%s: handle resize\n", __FUNCTION__);
-        resize_screen = 0;
-        cx = 0; cy = 0;
-        cw = ds_get_width(ds);
-        ch = ds_get_height(ds);
-        if (ds_get_width(ds) < fb_var.xres) {
-            cx = (fb_var.xres - ds_get_width(ds)) / 2;
-        }
-        if (ds_get_height(ds) < fb_var.yres) {
-            cy = (fb_var.yres - ds_get_height(ds)) / 2;
-        }
+    if (guest_surface != NULL) {
+        fbdev_render(ds, x, y, w, h);
+    }
+}
 
-        if (conv) {
-            qemu_pf_conv_put(conv);
-        }
-        conv = qemu_pf_conv_get(&fbpf, &ds->surface->pf);
-        if (conv == NULL) {
-            fprintf(stderr, "fbdev: unsupported PixelFormat conversion\n");
-        }
+static void fbdev_setdata(DisplayState *ds)
+{
+    if (conv) {
+        qemu_pf_conv_put(conv);
     }
 
-    if (redraw_screen) {
-        if (debug)
-            fprintf(stderr, "%s: handle redraw\n", __FUNCTION__);
-        redraw_screen = 0;
-        fbdev_cls();
-        x = 0; y = 0; w = ds_get_width(ds); h = ds_get_height(ds);
+    conv = qemu_pf_conv_get(&fbpf, &ds->surface->pf);
+    if (conv == NULL) {
+        fprintf(stderr, "fbdev: unsupported PixelFormat conversion\n");
     }
 
-    fbdev_render(ds, x, y, w, h);
+    guest_surface = ds_get_data(ds);
 }
 
 static void fbdev_resize(DisplayState *ds)
 {
-    if (debug)
-        fprintf(stderr, "%s: request resize+redraw\n", __FUNCTION__);
-    resize_screen++;
-    redraw_screen++;
+    int is_video_ptr;
+
+    if (fb_switch_state == FB_ACTIVE) {
+        fbdev_cls();
+    }
+
+    is_video_ptr = ds_get_data(ds) >= fb_mem + fb_mem_offset &&
+                   ds_get_data(ds) < fb_mem + fb_fix.smem_len + fb_mem_offset;
+
+    if (ds_get_bits_per_pixel(ds) != fbpf.bits_per_pixel ||
+        ds_get_linesize(ds) != fb_fix.line_length ||
+        !is_video_ptr) {
+        cx = 0; cy = 0;
+        if (ds_get_width(ds) < fb_var.xres) {
+            cx = (fb_var.xres - ds_get_width(ds)) / 2;
+        }
+        if (ds_get_height(ds) < fb_var.yres) {
+            cy = (fb_var.yres - ds_get_height(ds)) / 2;
+        }
+        fbdev_setdata(ds);
+    } else {
+        guest_surface = NULL;
+    }
 }
 
 static void fbdev_refresh(DisplayState *ds)
@@ -866,21 +869,17 @@ static void fbdev_refresh(DisplayState *ds)
     switch (fb_switch_state) {
     case FB_REL_REQ:
         fbdev_switch_release();
+        vga_hw_invalidate();
     case FB_INACTIVE:
         return;
     case FB_ACQ_REQ:
         fbdev_switch_acquire();
-        redraw_screen++;
-        if (debug)
-            fprintf(stderr, "%s: request redraw\n", __FUNCTION__);
+        vga_hw_invalidate();
     case FB_ACTIVE:
         break;
     }
 
     vga_hw_update();
-    if (redraw_screen) {
-        fbdev_update(ds, 0, 0, 0, 0);
-    }
 }
 
 static void fbdev_exit_notifier(Notifier *notifier)
@@ -888,8 +887,58 @@ static void fbdev_exit_notifier(Notifier *notifier)
     fbdev_cleanup();
 }
 
+static DisplaySurface *fbdev_create_displaysurface(int width, int height)
+{
+    DisplaySurface *surface = qemu_mallocz(sizeof (DisplaySurface));
+
+    surface->width = width;
+    surface->height = height;
+
+    surface->pf = fbpf;
+    surface->linesize = fb_fix.line_length;
+
+    if (fb_switch_state == FB_INACTIVE) {
+        surface->flags = QEMU_ALLOCATED_FLAG;
+        surface->data = qemu_mallocz(surface->linesize * surface->height);
+    } else {
+        surface->flags = QEMU_REALPIXELS_FLAG;
+        surface->data = fb_mem;
+
+        if (width < fb_var.xres)
+            surface->data += ((fb_var.xres - width) / 2) * fbpf.bytes_per_pixel;
+        if (height < fb_var.yres)
+            surface->data += ((fb_var.yres - height) / 2) * fb_fix.line_length;
+    }
+
+    return surface;
+}
+
+static void fbdev_free_displaysurface(DisplaySurface *surface)
+{
+    if (surface == NULL)
+        return;
+
+    if (surface->flags & QEMU_ALLOCATED_FLAG) {
+        qemu_free(surface->data);
+    }
+
+    surface->data = NULL;
+
+    qemu_free(surface);
+}
+
+static DisplaySurface *fbdev_resize_displaysurface(DisplaySurface *surface,
+                                                   int width,
+                                                   int height)
+{
+    fbdev_free_displaysurface(surface);
+    return fbdev_create_displaysurface(width, height);
+}
+
 void fbdev_display_init(DisplayState *ds, const char *device)
 {
+    DisplayAllocator *da;
+
     if (dcl != NULL) {
         if (debug)
             fprintf(stderr, "%s: already active\n", __FUNCTION__);
@@ -910,7 +959,17 @@ void fbdev_display_init(DisplayState *ds, const char *device)
     dcl->dpy_update  = fbdev_update;
     dcl->dpy_resize  = fbdev_resize;
     dcl->dpy_refresh = fbdev_refresh;
+    dcl->dpy_setdata = fbdev_setdata;
     register_displaychangelistener(ds, dcl);
+
+    da = qemu_mallocz(sizeof (DisplayAllocator));
+    da->create_displaysurface = fbdev_create_displaysurface;
+    da->resize_displaysurface = fbdev_resize_displaysurface;
+    da->free_displaysurface = fbdev_free_displaysurface;
+
+    if (register_displayallocator(ds, da) == da) {
+        dpy_resize(ds);
+    }
 }
 
 void fbdev_display_uninit(void)

-- 
Julian Pidancet <julian.pidancet@citrix.com>

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] Re: [PATCH 5/5] linux fbdev display driver.
  2010-06-16 16:22     ` Julian Pidancet
@ 2010-06-17 10:43       ` Gerd Hoffmann
  2010-06-17 14:29         ` Julian Pidancet
  0 siblings, 1 reply; 14+ messages in thread
From: Gerd Hoffmann @ 2010-06-17 10:43 UTC (permalink / raw)
  To: Julian Pidancet; +Cc: qemu-devel, Stefano Stabellini

[-- Attachment #1: Type: text/plain, Size: 1380 bytes --]

   Hi,

> +static void fbdev_free_displaysurface(DisplaySurface *surface)
> +{
> +    if (surface == NULL)
> +        return;
> +
> +    if (surface->flags&  QEMU_ALLOCATED_FLAG) {
> +        qemu_free(surface->data);
> +    }
> +
> +    surface->data = NULL;

This is pretty pointless ...

> +    qemu_free(surface);

... as you free surface anyway ;)

> @@ -910,7 +959,17 @@ void fbdev_display_init(DisplayState *ds, const char *device)
>       dcl->dpy_update  = fbdev_update;
>       dcl->dpy_resize  = fbdev_resize;
>       dcl->dpy_refresh = fbdev_refresh;
> +    dcl->dpy_setdata = fbdev_setdata;
>       register_displaychangelistener(ds, dcl);
> +
> +    da = qemu_mallocz(sizeof (DisplayAllocator));
> +    da->create_displaysurface = fbdev_create_displaysurface;
> +    da->resize_displaysurface = fbdev_resize_displaysurface;
> +    da->free_displaysurface = fbdev_free_displaysurface;
> +
> +    if (register_displayallocator(ds, da) == da) {
> +        dpy_resize(ds);
> +    }

You register the display allocator, but don't unregister in 
fbdev_display_uninit().

You are just lucky that fbdev_cleanup() forgets to unmap the framebuffer.

Apply the attached fix, start qemu with vnc, then do "change fbdev on" 
and "change fbdev off" in the monitor and watch qemu segfault.

Also after "change fbdev on" the guest screen isn't rendered correctly.

cheers,
   Gerd


[-- Attachment #2: 0001-fbdev-unmap-framebuffer-on-cleanup.patch --]
[-- Type: text/plain, Size: 755 bytes --]

>From 685849ae48eaef7927b90e012fb6afb4494052d0 Mon Sep 17 00:00:00 2001
From: Gerd Hoffmann <kraxel@redhat.com>
Date: Thu, 17 Jun 2010 12:32:53 +0200
Subject: [PATCH] fbdev: unmap framebuffer on cleanup

---
 fbdev.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/fbdev.c b/fbdev.c
index 6623a4f..1a95ede 100644
--- a/fbdev.c
+++ b/fbdev.c
@@ -518,6 +518,10 @@ static void fbdev_cleanup(void)
         fprintf(stderr, "%s\n", __FUNCTION__);
 
     /* restore console */
+    if (fb_mem != NULL) {
+        munmap(fb_mem, fb_fix.smem_len+fb_mem_offset);
+        fb_mem = NULL;
+    }
     if (fb != -1) {
         if (ioctl(fb,FBIOPUT_VSCREENINFO, &fb_ovar) < 0)
             perror("ioctl FBIOPUT_VSCREENINFO");
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] Re: [PATCH 5/5] linux fbdev display driver.
  2010-06-17 10:43       ` Gerd Hoffmann
@ 2010-06-17 14:29         ` Julian Pidancet
  2010-06-17 16:25           ` Julian Pidancet
  0 siblings, 1 reply; 14+ messages in thread
From: Julian Pidancet @ 2010-06-17 14:29 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel, Stefano Stabellini

On 06/17/2010 11:43 AM, Gerd Hoffmann wrote:
>    Hi,
> 
> You register the display allocator, but don't unregister in 
> fbdev_display_uninit().
> 
> You are just lucky that fbdev_cleanup() forgets to unmap the framebuffer.
> 
> Apply the attached fix, start qemu with vnc, then do "change fbdev on" 
> and "change fbdev off" in the monitor and watch qemu segfault.
> 
> Also after "change fbdev on" the guest screen isn't rendered correctly.
> 
> cheers,
>    Gerd
> 

Hi,

Thanks for spotting these errors. Here is a respin of my patch to address you concerns.
(The munmap call is included).

Cheers,

Julian

diff --git a/console.c b/console.c
index 698bc10..12ce215 100644
--- a/console.c
+++ b/console.c
@@ -1376,6 +1376,16 @@ DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *
     return ds->allocator;
 }
 
+void unregister_displayallocator(DisplayState *ds)
+{
+    if (ds->allocator != &default_allocator) {
+        ds->allocator->free_displaysurface(ds->surface);
+        ds->surface = defaultallocator_create_displaysurface(ds_get_width(ds),
+                                                             ds_get_height(ds));
+        ds->allocator = &default_allocator;
+    }
+}
+
 DisplayState *graphic_console_init(vga_hw_update_ptr update,
                                    vga_hw_invalidate_ptr invalidate,
                                    vga_hw_screen_dump_ptr screen_dump,
diff --git a/console.h b/console.h
index 124a22b..40bd927 100644
--- a/console.h
+++ b/console.h
@@ -192,6 +192,7 @@ PixelFormat qemu_different_endianness_pixelformat(int bpp);
 PixelFormat qemu_default_pixelformat(int bpp);
 
 DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *da);
+void unregister_displayallocator(DisplayState *ds);
 
 static inline DisplaySurface* qemu_create_displaysurface(DisplayState *ds, int width, int height)
 {
@@ -371,7 +372,7 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame);
 
 /* fbdev.c */
 void fbdev_display_init(DisplayState *ds, const char *device);
-void fbdev_display_uninit(void);
+void fbdev_display_uninit(DisplayState *ds);
 
 /* cocoa.m */
 void cocoa_display_init(DisplayState *ds, int full_screen);
diff --git a/fbdev.c b/fbdev.c
index 54f2381..8ea1838 100644
--- a/fbdev.c
+++ b/fbdev.c
@@ -67,13 +67,13 @@ static int fb_switch_state = FB_ACTIVE;
 
 /* qdev windup */
 static DisplayChangeListener      *dcl;
+static DisplayAllocator           *da;
 static QemuPfConv                 *conv;
 static PixelFormat                fbpf;
-static int                        resize_screen;
-static int                        redraw_screen;
 static int                        cx, cy, cw, ch;
 static int                        debug = 0;
 static Notifier                   exit_notifier;
+uint8_t                           *guest_surface;
 
 /* fwd decls */
 static int fbdev_activate_vt(int tty, int vtno, bool wait);
@@ -519,6 +519,10 @@ static void fbdev_cleanup(void)
         fprintf(stderr, "%s\n", __FUNCTION__);
 
     /* restore console */
+    if (fb_mem != NULL) {
+        munmap(fb_mem, fb_fix.smem_len + fb_mem_offset);
+        fb_mem = NULL;
+    }
     if (fb != -1) {
         if (ioctl(fb,FBIOPUT_VSCREENINFO, &fb_ovar) < 0)
             perror("ioctl FBIOPUT_VSCREENINFO");
@@ -786,10 +790,10 @@ static void fbdev_render(DisplayState *ds, int x, int y, int w, int h)
     uint8_t *src;
     int line;
 
-    if (!conv)
+    if (!conv || !guest_surface)
         return;
 
-    src = ds_get_data(ds) + y * ds_get_linesize(ds)
+    src = guest_surface + y * ds_get_linesize(ds)
         + x * ds_get_bytes_per_pixel(ds);
     dst = fb_mem + y * fb_fix.line_length
         + x * fbpf.bytes_per_pixel;
@@ -819,46 +823,50 @@ static void fbdev_update(DisplayState *ds, int x, int y, int w, int h)
     if (fb_switch_state != FB_ACTIVE)
         return;
 
-    if (resize_screen) {
-        if (debug)
-            fprintf(stderr, "%s: handle resize\n", __FUNCTION__);
-        resize_screen = 0;
-        cx = 0; cy = 0;
-        cw = ds_get_width(ds);
-        ch = ds_get_height(ds);
-        if (ds_get_width(ds) < fb_var.xres) {
-            cx = (fb_var.xres - ds_get_width(ds)) / 2;
-        }
-        if (ds_get_height(ds) < fb_var.yres) {
-            cy = (fb_var.yres - ds_get_height(ds)) / 2;
-        }
+    if (guest_surface != NULL) {
+        fbdev_render(ds, x, y, w, h);
+    }
+}
 
-        if (conv) {
-            qemu_pf_conv_put(conv);
-        }
-        conv = qemu_pf_conv_get(&fbpf, &ds->surface->pf);
-        if (conv == NULL) {
-            fprintf(stderr, "fbdev: unsupported PixelFormat conversion\n");
-        }
+static void fbdev_setdata(DisplayState *ds)
+{
+    if (conv) {
+        qemu_pf_conv_put(conv);
     }
 
-    if (redraw_screen) {
-        if (debug)
-            fprintf(stderr, "%s: handle redraw\n", __FUNCTION__);
-        redraw_screen = 0;
-        fbdev_cls();
-        x = 0; y = 0; w = ds_get_width(ds); h = ds_get_height(ds);
+    conv = qemu_pf_conv_get(&fbpf, &ds->surface->pf);
+    if (conv == NULL) {
+        fprintf(stderr, "fbdev: unsupported PixelFormat conversion\n");
     }
 
-    fbdev_render(ds, x, y, w, h);
+    guest_surface = ds_get_data(ds);
 }
 
 static void fbdev_resize(DisplayState *ds)
 {
-    if (debug)
-        fprintf(stderr, "%s: request resize+redraw\n", __FUNCTION__);
-    resize_screen++;
-    redraw_screen++;
+    int is_video_ptr;
+
+    if (fb_switch_state == FB_ACTIVE) {
+        fbdev_cls();
+    }
+
+    is_video_ptr = ds_get_data(ds) >= fb_mem + fb_mem_offset &&
+                   ds_get_data(ds) < fb_mem + fb_fix.smem_len + fb_mem_offset;
+
+    if (ds_get_bits_per_pixel(ds) != fbpf.bits_per_pixel ||
+        ds_get_linesize(ds) != fb_fix.line_length ||
+        !is_video_ptr) {
+        cx = 0; cy = 0;
+        if (ds_get_width(ds) < fb_var.xres) {
+            cx = (fb_var.xres - ds_get_width(ds)) / 2;
+        }
+        if (ds_get_height(ds) < fb_var.yres) {
+            cy = (fb_var.yres - ds_get_height(ds)) / 2;
+        }
+        fbdev_setdata(ds);
+    } else {
+        guest_surface = NULL;
+    }
 }
 
 static void fbdev_refresh(DisplayState *ds)
@@ -866,21 +874,17 @@ static void fbdev_refresh(DisplayState *ds)
     switch (fb_switch_state) {
     case FB_REL_REQ:
         fbdev_switch_release();
+        vga_hw_invalidate();
     case FB_INACTIVE:
         return;
     case FB_ACQ_REQ:
         fbdev_switch_acquire();
-        redraw_screen++;
-        if (debug)
-            fprintf(stderr, "%s: request redraw\n", __FUNCTION__);
+        vga_hw_invalidate();
     case FB_ACTIVE:
         break;
     }
 
     vga_hw_update();
-    if (redraw_screen) {
-        fbdev_update(ds, 0, 0, 0, 0);
-    }
 }
 
 static void fbdev_exit_notifier(Notifier *notifier)
@@ -888,6 +892,52 @@ static void fbdev_exit_notifier(Notifier *notifier)
     fbdev_cleanup();
 }
 
+static DisplaySurface *fbdev_create_displaysurface(int width, int height)
+{
+    DisplaySurface *surface = qemu_mallocz(sizeof (DisplaySurface));
+
+    surface->width = width;
+    surface->height = height;
+
+    surface->pf = fbpf;
+    surface->linesize = fb_fix.line_length;
+
+    if (fb_switch_state == FB_INACTIVE) {
+        surface->flags = QEMU_ALLOCATED_FLAG;
+        surface->data = qemu_mallocz(surface->linesize * surface->height);
+    } else {
+        surface->flags = QEMU_REALPIXELS_FLAG;
+        surface->data = fb_mem;
+
+        if (width < fb_var.xres)
+            surface->data += ((fb_var.xres - width) / 2) * fbpf.bytes_per_pixel;
+        if (height < fb_var.yres)
+            surface->data += ((fb_var.yres - height) / 2) * fb_fix.line_length;
+    }
+
+    return surface;
+}
+
+static void fbdev_free_displaysurface(DisplaySurface *surface)
+{
+    if (surface == NULL)
+        return;
+
+    if (surface->flags & QEMU_ALLOCATED_FLAG) {
+        qemu_free(surface->data);
+    }
+
+    qemu_free(surface);
+}
+
+static DisplaySurface *fbdev_resize_displaysurface(DisplaySurface *surface,
+                                                   int width,
+                                                   int height)
+{
+    fbdev_free_displaysurface(surface);
+    return fbdev_create_displaysurface(width, height);
+}
+
 void fbdev_display_init(DisplayState *ds, const char *device)
 {
     if (dcl != NULL) {
@@ -905,15 +955,24 @@ void fbdev_display_init(DisplayState *ds, const char *device)
     fbdev_catch_exit_signals();
     init_mouse();
 
-    fbdev_resize(ds);
     dcl = qemu_mallocz(sizeof(DisplayChangeListener));
     dcl->dpy_update  = fbdev_update;
     dcl->dpy_resize  = fbdev_resize;
     dcl->dpy_refresh = fbdev_refresh;
+    dcl->dpy_setdata = fbdev_setdata;
     register_displaychangelistener(ds, dcl);
+
+    da = qemu_mallocz(sizeof (DisplayAllocator));
+    da->create_displaysurface = fbdev_create_displaysurface;
+    da->resize_displaysurface = fbdev_resize_displaysurface;
+    da->free_displaysurface = fbdev_free_displaysurface;
+
+    if (register_displayallocator(ds, da) == da) {
+        vga_hw_invalidate();
+    }
 }
 
-void fbdev_display_uninit(void)
+void fbdev_display_uninit(DisplayState *ds)
 {
     if (dcl == NULL) {
         if (debug)
@@ -921,6 +980,9 @@ void fbdev_display_uninit(void)
         return;
     }
 
+    unregister_displayallocator(ds);
+    qemu_free(da);
+
     unregister_displaychangelistener(dcl);
     qemu_free(dcl);
     dcl = NULL;
diff --git a/monitor.c b/monitor.c
index 7442f1a..5d6efb4 100644
--- a/monitor.c
+++ b/monitor.c
@@ -965,7 +965,7 @@ static int do_change_fbdev(Monitor *mon, const char *target)
     if (strcmp(target, "on") == 0) {
         fbdev_display_init(get_displaystate(), NULL);
     } else {
-        fbdev_display_uninit();
+        fbdev_display_uninit(get_displaystate());
     }
     return 0;
 #endif

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] Re: [PATCH 5/5] linux fbdev display driver.
  2010-06-17 14:29         ` Julian Pidancet
@ 2010-06-17 16:25           ` Julian Pidancet
  2010-06-18  7:32             ` Gerd Hoffmann
  0 siblings, 1 reply; 14+ messages in thread
From: Julian Pidancet @ 2010-06-17 16:25 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel, Stefano Stabellini

On 06/17/2010 03:29 PM, Julian Pidancet wrote:
> 
> Hi,
> 
> Thanks for spotting these errors. Here is a respin of my patch to address you concerns.
> (The munmap call is included).
> 
> Cheers,
> 
> Julian
> 

Oh, I actually tested the last patch only with the -nographic switch. There's still a segfault when starting qemu with vnc.
You can fix it by adding a call to dpy_resize(ds) after the dcl = NULL; line in fbdev_display_uninit().

For some reason, the display is extremely slow when using vnc and fbdev at the same time.

Julian

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] Re: [PATCH 5/5] linux fbdev display driver.
  2010-06-17 16:25           ` Julian Pidancet
@ 2010-06-18  7:32             ` Gerd Hoffmann
  2010-06-18 12:00               ` Julian Pidancet
  0 siblings, 1 reply; 14+ messages in thread
From: Gerd Hoffmann @ 2010-06-18  7:32 UTC (permalink / raw)
  To: Julian Pidancet; +Cc: qemu-devel, Stefano Stabellini

   Hi,

> For some reason, the display is extremely slow when using vnc and
> fbdev at the same time.

Gotcha.  Didn't notice, but it probably depends on the hardware.  Very 
likely the reason is that graphic cards usually are optimized for write 
access and reads might be slow as hell.   vnc must read though.

Hmm.  No easy way out.

cheers,
   Gerd

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] Re: [PATCH 5/5] linux fbdev display driver.
  2010-06-18  7:32             ` Gerd Hoffmann
@ 2010-06-18 12:00               ` Julian Pidancet
  0 siblings, 0 replies; 14+ messages in thread
From: Julian Pidancet @ 2010-06-18 12:00 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel, Stefano Stabellini

On 06/18/2010 08:32 AM, Gerd Hoffmann wrote:
>    Hi,
> 
>> For some reason, the display is extremely slow when using vnc and
>> fbdev at the same time.
> 
> Gotcha.  Didn't notice, but it probably depends on the hardware.  Very 
> likely the reason is that graphic cards usually are optimized for write 
> access and reads might be slow as hell.   vnc must read though.
> 

Access to the framebuffer are cached Write-Combining by default with fbdev, which is probably causing this latency.

One solution would be to disable the display allocator when vnc is present, and let it read from a software surface instead of reading from the framebuffer (like in your initial patch). It would probably decrease display performance, but not as much as it is now if we let the vnc driver read from the hardware framebuffer.

We can easily implement a surface usage counter in the display allocator code, so the fbdev driver can know whether or not the surface is read by other drivers at the same time.

-- 
Julian Pidancet

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH 0/5] fbdev display driver + misc bits
  2010-06-15 10:05 [Qemu-devel] [PATCH 0/5] fbdev display driver + misc bits Gerd Hoffmann
                   ` (4 preceding siblings ...)
  2010-06-15 10:05 ` [Qemu-devel] [PATCH 5/5] linux fbdev display driver Gerd Hoffmann
@ 2010-06-24 18:38 ` Julian Pidancet
  5 siblings, 0 replies; 14+ messages in thread
From: Julian Pidancet @ 2010-06-24 18:38 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

On 06/15/2010 11:05 AM, Gerd Hoffmann wrote:
>   Hi,
> 
> This patch series features the linux fbdev display driver and a few
> more patches the fbdev driver depends on.  Most of the patches have been
> on the list before.  If you wondered what they are good for -- here is
> the big picture ;)
> 
> As usual the individual patches carry more verbose descriptions.
> 
> cheers,
>   Gerd
> 
> Gerd Hoffmann (5):
>   QLIST-ify display change listeners.
>   add unregister_displaychangelistener
>   Fix and simplify gui timer logic.
>   add pflib: PixelFormat conversion library.
>   linux fbdev display driver.
> 
>  Makefile.objs    |    2 +
>  console.h        |   82 +++--
>  fbdev.c          |  931 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/xenfb.c       |    2 +-
>  linux-keynames.h |  386 ++++++++++++++++++++++
>  monitor.c        |   14 +
>  pflib.c          |  213 +++++++++++++
>  pflib.h          |   20 ++
>  qemu-options.hx  |   10 +
>  sysemu.h         |    1 +
>  vl.c             |   55 ++--
>  11 files changed, 1648 insertions(+), 68 deletions(-)
>  create mode 100644 fbdev.c
>  create mode 100644 linux-keynames.h
>  create mode 100644 pflib.c
>  create mode 100644 pflib.h
> 
> 

Have anyone got the time to review this yet ?

-- 
Julian

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2010-06-24 18:34 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-06-15 10:05 [Qemu-devel] [PATCH 0/5] fbdev display driver + misc bits Gerd Hoffmann
2010-06-15 10:05 ` [Qemu-devel] [PATCH 1/5] QLIST-ify display change listeners Gerd Hoffmann
2010-06-15 10:05 ` [Qemu-devel] [PATCH 2/5] add unregister_displaychangelistener Gerd Hoffmann
2010-06-15 10:05 ` [Qemu-devel] [PATCH 3/5] Fix and simplify gui timer logic Gerd Hoffmann
2010-06-15 10:05 ` [Qemu-devel] [PATCH 4/5] add pflib: PixelFormat conversion library Gerd Hoffmann
2010-06-15 10:05 ` [Qemu-devel] [PATCH 5/5] linux fbdev display driver Gerd Hoffmann
2010-06-16 12:44   ` [Qemu-devel] " Stefano Stabellini
2010-06-16 16:22     ` Julian Pidancet
2010-06-17 10:43       ` Gerd Hoffmann
2010-06-17 14:29         ` Julian Pidancet
2010-06-17 16:25           ` Julian Pidancet
2010-06-18  7:32             ` Gerd Hoffmann
2010-06-18 12:00               ` Julian Pidancet
2010-06-24 18:38 ` [Qemu-devel] [PATCH 0/5] fbdev display driver + misc bits Julian Pidancet

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.