qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver


From: Stefano Stabellini
Subject: Re: [Qemu-devel] [PATCH 4/9] fbdev: add linux framebuffer display driver.
Date: Tue, 18 Sep 2012 16:01:22 +0100
User-agent: Alpine 2.02 (DEB 1266 2009-07-14)

On Tue, 18 Sep 2012, 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 enough to type monitor commands.  Not goot enough to
> work seriously on a serial terminal.  But the qemu terminal emulation
> isn't good enough 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>).
> 
> Signed-off-by: Gerd Hoffmann <address@hidden>

This series is not bisectable: this patch references functions and
variables only defined in later patches (surface,
pixman_from_displaystate). Please make it bisectable.
It also makes it harder to review.
At the very least the Makefile changes should be in the last patch.


>  console.h           |    4 +
>  qemu-options.hx     |    8 +
>  sysemu.h            |    1 +
>  trace-events        |   15 +
>  ui/Makefile.objs    |    1 +
>  ui/fbdev.c          |  974 
> +++++++++++++++++++++++++++++++++++++++++++++++++++
>  ui/linux-keynames.h |  388 ++++++++++++++++++++
>  vl.c                |   12 +
>  8 files changed, 1403 insertions(+), 0 deletions(-)
>  create mode 100644 ui/fbdev.c
>  create mode 100644 ui/linux-keynames.h
> 
> diff --git a/console.h b/console.h
> index bef2d2d..0a3bae2 100644
> --- a/console.h
> +++ b/console.h
> @@ -417,6 +417,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 */
> +int fbdev_display_init(DisplayState *ds, const char *device);
> +void fbdev_display_uninit(DisplayState *ds);
> +
>  /* cocoa.m */
>  void cocoa_display_init(DisplayState *ds, int full_screen);
> 
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 09c86c4..3445655 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -947,6 +947,14 @@ Enable/disable spice seamless migration. Default is off.
>  @end table
>  ETEXI
> 
> +DEF("fbdev", 0, QEMU_OPTION_fbdev,
> +    "-fbdev          enable fbdev\n", QEMU_ARCH_ALL)
> +STEXI
> address@hidden -fbdev
> address@hidden -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 65552ac..34e6bfa 100644
> --- a/sysemu.h
> +++ b/sysemu.h
> @@ -93,6 +93,7 @@ typedef enum DisplayType
>      DT_DEFAULT,
>      DT_CURSES,
>      DT_SDL,
> +    DT_FBDEV,
>      DT_NOGRAPHIC,
>      DT_NONE,
>  } DisplayType;
> diff --git a/trace-events b/trace-events
> index b48fe2d..0d0b7fa 100644
> --- a/trace-events
> +++ b/trace-events
> @@ -994,3 +994,18 @@ spapr_pci_rtas_ibm_change_msi(unsigned func, unsigned 
> req) "func %u, requested %
>  spapr_pci_rtas_ibm_query_interrupt_source_number(unsigned ioa, unsigned 
> intr) "queries for #%u, IRQ%u"
>  spapr_pci_msi_write(uint64_t addr, uint64_t data, uint32_t dt_irq) 
> "@%"PRIx64"<=%"PRIx64" IRQ %u"
>  spapr_pci_lsi_set(const char *busname, int pin, uint32_t irq) "%s PIN%d IRQ 
> %u"
> +
> +# ui/fbdev.c
> +fbdev_enabled(void) ""
> +fbdev_cleanup(void) ""
> +fbdev_vt_activate(int vtno, int wait) "vtno %d, wait %d"
> +fbdev_vt_activated(void) ""
> +fbdev_vt_release_request(void) ""
> +fbdev_vt_released(void) ""
> +fbdev_vt_aquire_request(void) ""
> +fbdev_vt_aquired(void) ""
> +fbdev_kbd_raw(int enable) "enable %d"
> +fbdev_kbd_event(int keycode, const char *kname, int up) "keycode 0x%x [%s], 
> down %d"
> +fbdev_dpy_resize(int w, int h) "%dx%d"
> +fbdev_dpy_redraw(void)
> +
> diff --git a/ui/Makefile.objs b/ui/Makefile.objs
> index adc07be..55ddcf2 100644
> --- a/ui/Makefile.objs
> +++ b/ui/Makefile.objs
> @@ -12,3 +12,4 @@ common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
>  common-obj-$(CONFIG_COCOA) += cocoa.o
>  common-obj-$(CONFIG_CURSES) += curses.o
>  common-obj-$(CONFIG_VNC) += $(vnc-obj-y)
> +common-obj-$(CONFIG_LINUX) += fbdev.o
> diff --git a/ui/fbdev.c b/ui/fbdev.c
> new file mode 100644
> index 0000000..40fc7d4
> --- /dev/null
> +++ b/ui/fbdev.c
> @@ -0,0 +1,974 @@
> +/*
> + * linux fbdev output driver.
> + *
> + * Author: Gerd Hoffmann <address@hidden>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * 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 "qemu-common.h"
> +#include "console.h"
> +#include "keymaps.h"
> +#include "sysemu.h"
> +#include "pflib.h"
> +
> +/*
> + * must be last so we get the linux input layer
> + * KEY_* defines, not the ncurses ones.
> + */
> +#include <linux/input.h>
> +
> +/* -------------------------------------------------------------------- */
> +
> +/* file handles */
> +static int                        tty = -1, fb = -1, mice = -1;
> +
> +/* saved state, for restore on exit */
> +static int                        orig_vtno;
> +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;
> +
> +/* 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;
> +
> +/* 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 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 (mice == -1) {
> +        return -1;
> +    }
> +    qemu_set_fd_handler(mice, read_mouse, NULL, NULL);
> +    return 0;
> +}
> +
> +static void uninit_mouse(void)
> +{
> +    if (mice == -1) {
> +        return;
> +    }
> +    qemu_set_fd_handler(mice, NULL, NULL, NULL);
> +    close(mice);
> +    mice = -1;
> +}
> +
> +/* -------------------------------------------------------------------- */
> +/* keyboard                                                             */
> +
> +static const char *keynames[] = {
> +#include "linux-keynames.h"
> +};
> +
> +static const 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,
> +#if 0
> +    [KEY_PAUSE]         = FIXME,
> +#endif
> +    [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 const 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;
> +    }
> +    trace_fbdev_kbd_raw(1);
> +
> +    /* 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;
> +    }
> +    trace_fbdev_kbd_raw(0);
> +
> +    /* 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",
> +                __func__, 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)
> +{
> +    const 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",
> +                __func__, 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", __func__);
> +        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;
> +            }
> +
> +            trace_fbdev_kbd_event(keycode, keynames[keycode], !up);
> +
> +            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)
> +{
> +    trace_fbdev_vt_activate(vtno, wait);
> +
> +    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;
> +        }
> +        trace_fbdev_vt_activated();
> +    }
> +
> +    return 0;
> +}
> +
> +static void fbdev_cleanup(void)
> +{
> +    trace_fbdev_cleanup();
> +
> +    /* 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");
> +        }
> +        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));
> +            goto err_early;
> +        }
> +        if (ioctl(tty, VT_OPENQRY, &vtno) < 0) {
> +            perror("ioctl VT_OPENQRY");
> +            goto err_early;
> +        }
> +        if (ioctl(tty, VT_GETSTATE, &vts) < 0) {
> +            perror("ioctl VT_GETSTATE\n");
> +            goto err_early;
> +        }
> +        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));
> +            goto err_early;
> +        }
> +        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");
> +        goto err_early;
> +    }
> +    if (ioctl(tty, KDGETMODE, &kd_omode) < 0) {
> +        perror("ioctl KDGETMODE");
> +        goto err_early;
> +    }
> +    if (ioctl(tty, VT_GETMODE, &vt_omode) < 0) {
> +        perror("ioctl VT_GETMODE");
> +        goto err_early;
> +    }
> +
> +    /* 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_switch_state = FB_ACTIVE;
> +    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;

the conversion should probably be in an separate helper function


> +    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_early:
> +    if (tty > 0) {
> +        close(tty);
> +    }
> +    close(fb);
> +    return -1;
> +
> +err:
> +    fbdev_cleanup();
> +    return -1;
> +}
> +
> +static void
> +fbdev_catch_fatal_signal(int signr)
> +{
> +    fprintf(stderr, "%s: %s, restoring linux console state ...\n",
> +            __func__, strsignal(signr));
> +    fbdev_cleanup();
> +    signal(SIGABRT, SIG_DFL);
> +    fprintf(stderr, "%s: ... done, going abort() now.\n", __func__);
> +    abort();
> +}
> +
> +static void fbdev_catch_exit_signals(void)
> +{
> +    static const int signals[] = {
> +        SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGBUS
> +    };
> +    struct sigaction act, old;
> +    int i;
> +
> +    memset(&act, 0, sizeof(act));
> +    act.sa_handler = fbdev_catch_fatal_signal;
> +    act.sa_flags = SA_RESETHAND;
> +    sigemptyset(&act.sa_mask);
> +    for (i = 0; i < ARRAY_SIZE(signals); i++) {
> +        sigaction(signals[i], &act, &old);
> +    }
> +}
> +
> +/* -------------------------------------------------------------------- */
> +/* console switching                                                    */
> +
> +static void fbdev_switch_signal(int signal)
> +{
> +    if (signal == SIG_REL) {
> +        /* release */
> +        trace_fbdev_vt_release_request();
> +        fb_switch_state = FB_REL_REQ;
> +    }
> +    if (signal == SIG_ACQ) {
> +        /* acquisition */
> +        trace_fbdev_vt_aquire_request();
> +        fb_switch_state = FB_ACQ_REQ;
> +    }
> +}
> +
> +static void fbdev_switch_release(void)
> +{
> +    stop_mediumraw(tty);
> +    ioctl(tty, KDSETMODE, kd_omode);
> +    ioctl(tty, VT_RELDISP, 1);
> +    fb_switch_state = FB_INACTIVE;
> +    trace_fbdev_vt_released();
> +}
> +
> +static void fbdev_switch_acquire(void)
> +{
> +    ioctl(tty, VT_RELDISP, VT_ACKACQ);
> +    start_mediumraw(tty);
> +    reset_keys();
> +    ioctl(tty, KDSETMODE, KD_GRAPHICS);
> +    fb_switch_state = FB_ACTIVE;
> +    trace_fbdev_vt_aquired();
> +}
> +
> +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) {
> +        trace_fbdev_dpy_resize(ds_get_width(ds), ds_get_height(ds));
> +        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) {
> +        trace_fbdev_dpy_redraw();
> +        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)
> +{
> +    resize_screen++;
> +    redraw_screen++;
> +}
> +
> +static void fbdev_setdata(DisplayState *ds)
> +{
> +    if (surface) {
            ^ where is this coming from?

> +        pixman_image_unref(surface);
> +    }
> +    surface = pixman_from_displaystate(ds);
> +    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++;
> +    case FB_ACTIVE:
> +        break;
> +    }
> +
> +    vga_hw_update();
> +    if (redraw_screen) {
> +        fbdev_update(ds, 0, 0, 0, 0);
> +    }
> +}
> +
> +static void fbdev_exit_notifier(Notifier *notifier, void *data)
> +{
> +    fbdev_cleanup();
> +}
> +
> +int fbdev_display_init(DisplayState *ds, const char *device)
> +{
> +    if (dcl != NULL) {
> +        return 0;
> +    }

is it actually possible that fbdev_display_init gets called multiple
times?


> +    if (fbdev_init(device) != 0) {
> +        return -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 = g_new0(DisplayChangeListener, 1);
> +    dcl->dpy_update  = fbdev_update;
> +    dcl->dpy_resize  = fbdev_resize;
> +    dcl->dpy_setdata = fbdev_setdata;
> +    dcl->dpy_refresh = fbdev_refresh;
> +    register_displaychangelistener(ds, dcl);
>

The fbdev driver could benefit from registering a DisplayAllocator (see
sdl.c).


> +    trace_fbdev_enabled();
> +    return 0;
> +}
> +
> +void fbdev_display_uninit(DisplayState *ds)
> +{
> +    if (dcl == NULL) {
> +        return;
> +    }
> +
> +    unregister_displaychangelistener(ds, dcl);
> +    g_free(dcl);
> +    dcl = NULL;
> +
> +    fbdev_cleanup();
> +    qemu_remove_exit_notifier(&exit_notifier);
> +    uninit_mouse();
> +}



reply via email to

[Prev in Thread] Current Thread [Next in Thread]