/* * QEMU graphical console * * Copyright (c) 2004 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "vl.h" //#define DEBUG_CONSOLE #define DEFAULT_BACKSCROLL 512 #define MAX_CONSOLES 12 #define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) #define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff) typedef struct TextAttributes { uint8_t fgcol:4; uint8_t bgcol:4; uint8_t bold:1; uint8_t uline:1; uint8_t blink:1; uint8_t invers:1; uint8_t unvisible:1; } TextAttributes; typedef struct TextCell { uint8_t ch; TextAttributes t_attrib; } TextCell; #define MAX_ESC_PARAMS 3 enum TTYState { TTY_STATE_NORM, TTY_STATE_ESC, TTY_STATE_CSI, }; typedef struct QEMUFIFO { uint8_t *buf; int buf_size; int count, wptr, rptr; } QEMUFIFO; int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1) { int l, len; l = f->buf_size - f->count; if (len1 > l) len1 = l; len = len1; while (len > 0) { l = f->buf_size - f->wptr; if (l > len) l = len; memcpy(f->buf + f->wptr, buf, l); f->wptr += l; if (f->wptr >= f->buf_size) f->wptr = 0; buf += l; len -= l; } f->count += len1; return len1; } int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1) { int l, len; if (len1 > f->count) len1 = f->count; len = len1; while (len > 0) { l = f->buf_size - f->rptr; if (l > len) l = len; memcpy(buf, f->buf + f->rptr, l); f->rptr += l; if (f->rptr >= f->buf_size) f->rptr = 0; buf += l; len -= l; } f->count -= len1; return len1; } /* ??? This is mis-named. It is used for both text and graphical consoles. */ struct TextConsole { int text_console; /* true if text console */ DisplayState *ds; /* Graphic console state. */ vga_hw_update_ptr hw_update; vga_hw_invalidate_ptr hw_invalidate; vga_hw_screen_dump_ptr hw_screen_dump; void *hw; int g_width, g_height; int width; int height; int total_height; int backscroll_height; int x, y; int y_displayed; int y_base; TextAttributes t_attrib_default; /* default text attributes */ TextAttributes t_attrib; /* currently active text attributes */ TextCell *cells; enum TTYState state; int esc_params[MAX_ESC_PARAMS]; int nb_esc_params; /* kbd read handler */ IOCanRWHandler *fd_can_read; IOReadHandler *fd_read; void *fd_opaque; /* fifo for key pressed */ QEMUFIFO out_fifo; uint8_t out_fifo_buf[16]; QEMUTimer *kbd_timer; }; static TextConsole *active_console; static TextConsole *consoles[MAX_CONSOLES]; static int nb_consoles = 0; void vga_hw_update(void) { if (active_console->hw_update) active_console->hw_update(active_console->hw); } void vga_hw_invalidate(void) { if (active_console->hw_invalidate) active_console->hw_invalidate(active_console->hw); } void vga_hw_screen_dump(const char *filename) { /* There is currently no was of specifying which screen we want to dump, so always dump the dirst one. */ if (consoles[0]->hw_screen_dump) consoles[0]->hw_screen_dump(consoles[0]->hw, filename); } /* convert a RGBA color to a color index usable in graphic primitives */ static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba) { unsigned int r, g, b, color; switch(ds->depth) { #if 0 case 8: r = (rgba >> 16) & 0xff; g = (rgba >> 8) & 0xff; b = (rgba) & 0xff; color = (rgb_to_index[r] * 6 * 6) + (rgb_to_index[g] * 6) + (rgb_to_index[b]); break; #endif case 15: r = (rgba >> 16) & 0xff; g = (rgba >> 8) & 0xff; b = (rgba) & 0xff; color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3); break; case 16: r = (rgba >> 16) & 0xff; g = (rgba >> 8) & 0xff; b = (rgba) & 0xff; color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); break; case 32: default: color = rgba; break; } return color; } static void vga_fill_rect (DisplayState *ds, int posx, int posy, int width, int height, uint32_t color) { uint8_t *d, *d1; int x, y, bpp; bpp = (ds->depth + 7) >> 3; d1 = ds->data + ds->linesize * posy + bpp * posx; for (y = 0; y < height; y++) { d = d1; switch(bpp) { case 1: for (x = 0; x < width; x++) { *((uint8_t *)d) = color; d++; } break; case 2: for (x = 0; x < width; x++) { *((uint16_t *)d) = color; d += 2; } break; case 4: for (x = 0; x < width; x++) { *((uint32_t *)d) = color; d += 4; } break; } d1 += ds->linesize; } } /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */ static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h) { const uint8_t *s; uint8_t *d; int wb, y, bpp; bpp = (ds->depth + 7) >> 3; wb = w * bpp; if (yd <= ys) { s = ds->data + ds->linesize * ys + bpp * xs; d = ds->data + ds->linesize * yd + bpp * xd; for (y = 0; y < h; y++) { memmove(d, s, wb); d += ds->linesize; s += ds->linesize; } } else { s = ds->data + ds->linesize * (ys + h - 1) + bpp * xs; d = ds->data + ds->linesize * (yd + h - 1) + bpp * xd; for (y = 0; y < h; y++) { memmove(d, s, wb); d -= ds->linesize; s -= ds->linesize; } } } /***********************************************************/ /* basic char display */ #define FONT_HEIGHT 16 #define FONT_WIDTH 8 #include "vgafont.h" #define cbswap_32(__x) \ ((uint32_t)( \ (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )) #ifdef WORDS_BIGENDIAN #define PAT(x) x #else #define PAT(x) cbswap_32(x) #endif static const uint32_t dmask16[16] = { PAT(0x00000000), PAT(0x000000ff), PAT(0x0000ff00), PAT(0x0000ffff), PAT(0x00ff0000), PAT(0x00ff00ff), PAT(0x00ffff00), PAT(0x00ffffff), PAT(0xff000000), PAT(0xff0000ff), PAT(0xff00ff00), PAT(0x
#
# Copyright (C) 2006-2007 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(INCLUDE_DIR)/host.mk
include $(INCLUDE_DIR)/prereq.mk

ifneq ($(DUMP),1)
  all: compile
endif

export QUILT=1
STAMP_PREPARED:=$(LINUX_DIR)/.prepared
STAMP_CONFIGURED:=$(LINUX_DIR)/.configured
include $(INCLUDE_DIR)/download.mk
include $(INCLUDE_DIR)/quilt.mk
include $(INCLUDE_DIR)/kernel-defaults.mk

define Kernel/Prepare
	$(call Kernel/Prepare/Default)
endef

define Kernel/Configure
	$(call Kernel/Configure/Default)
endef

define Kernel/CompileModules
	$(call Kernel/CompileModules/Default)
endef

define Kernel/CompileImage
	$(call Kernel/CompileImage/Default)
endef

define Kernel/Clean
	$(call Kernel/Clean/Default)
endef

define Download/kernel
  URL:=$(LINUX_SITE)
  FILE:=$(LINUX_SOURCE)
  MD5SUM:=$(LINUX_KERNEL_MD5SUM)
endef

ifdef CONFIG_COLLECT_KERNEL_DEBUG
  define Kernel/CollectDebug
	rm -rf $(KERNEL_BUILD_DIR)/debug
	mkdir -p $(KERNEL_BUILD_DIR)/debug/modules
	$(CP) $(LINUX_DIR)/vmlinux $(KERNEL_BUILD_DIR)/debug/
	-$(CP) \
		$(STAGING_DIR_ROOT)/lib/modules/$(LINUX_VERSION)/* \
		$(KERNEL_BUILD_DIR)/debug/modules/
	$(FIND) $(KERNEL_BUILD_DIR)/debug -type f | $(XARGS) $(KERNEL_CROSS)strip --only-keep-debug
	$(TAR) c -C $(KERNEL_BUILD_DIR) debug | bzip2 -c -9 > $(BIN_DIR)/kernel-debug.tar.bz2
  endef
endif

define BuildKernel
  $(if $(QUILT),$(Build/Quilt))
  $(if $(LINUX_SITE),$(call Download,kernel))

  $(STAMP_PREPARED): $(if $(LINUX_SITE),$(DL_DIR)/$(LINUX_SOURCE))
	-rm -rf $(KERNEL_BUILD_DIR)
	-mkdir -p $(KERNEL_BUILD_DIR)
	$(Kernel/Prepare)
	touch $$@

  $(KERNEL_BUILD_DIR)/symtab.txt: FORCE
	find $(LINUX_DIR) $(STAGING_DIR_ROOT)/lib/modules -name \*.ko | \
		xargs $(TARGET_CROSS)nm | \
		awk '$$$$1 == "U" { print $$$$2 } ' | \
		sort -u > $$@

  $(KERNEL_BUILD_DIR)/symtab.h: $(KERNEL_BUILD_DIR)/symtab.txt
	( \
		echo '#define SYMTAB_KEEP \'; \
		cat $(KERNEL_BUILD_DIR)/symtab.txt | \
			awk '{print "*(__ksymtab." $$$$1 ") \\" }'; \
		echo; \
		echo '#define SYMTAB_KEEP_GPL \'; \
		cat $(KERNEL_BUILD_DIR)/symtab.txt | \
			awk '{print "*(__ksymtab_gpl." $$$$1 ") \\" }'; \
		echo; \
		echo '#define SYMTAB_KEEP_STR \'; \
		cat $(KERNEL_BUILD_DIR)/symtab.txt | \
			awk '{print "*(__ksymtab_strings." $$$$1 ") \\" }'; \
		echo; \
	) > $$@

  $(STAMP_CONFIGURED): $(STAMP_PREPARED) $(LINUX_KCONFIG_LIST) $(TOPDIR)/.config
	$(Kernel/Configure)
	touch $$@

  $(LINUX_DIR)/.modules: $(STAMP_CONFIGURED) $(LINUX_DIR)/.config FORCE
	$(Kernel/CompileModules)
	touch $$@

  $(LINUX_DIR)/.image: $(STAMP_CONFIGURED) $(if $(CONFIG_STRIP_KERNEL_EXPORTS),$(KERNEL_BUILD_DIR)/symtab.h) FORCE
	$(Kernel/CompileImage)
	$(Kernel/CollectDebug)
	touch $$@
	
  mostlyclean: FORCE
	$(Kernel/Clean)

  define BuildKernel
  endef

  download: $(DL_DIR)/$(LINUX_SOURCE)
  prepare: $(STAMP_CONFIGURED)
  compile: $(LINUX_DIR)/.modules
	$(MAKE) -C image compile TARGET_BUILD=

  oldconfig menuconfig nconfig: $(STAMP_PREPARED) $(STAMP_CHECKED) FORCE
	rm -f $(STAMP_CONFIGURED)
	$(LINUX_RECONF_CMD) > $(LINUX_DIR)/.config
	$(_SINGLE)$(MAKE) -C $(LINUX_DIR) $(KERNEL_MAKEOPTS) $$@
	$(LINUX_RECONF_DIFF) $(LINUX_DIR)/.config > $(LINUX_RECONFIG_TARGET)

  install: $(LINUX_DIR)/.image
	+$(MAKE) -C image compile install TARGET_BUILD=

  clean: FORCE
	rm -rf $(KERNEL_BUILD_DIR)

  image-prereq:
	@+$(NO_TRACE_MAKE) -s -C image prereq TARGET_BUILD=

  prereq: image-prereq

endef
= (s->y_base + s->y) % s->total_height; for(x = s->x; x < s->width; x++) { c = &s->cells[y1 * s->width + x]; c->ch = ' '; c->t_attrib = s->t_attrib_default; c++; update_xy(s, x, s->y); } break; default: break; } console_handle_escape(s); break; } } } void console_select(unsigned int index) { TextConsole *s; if (index >= MAX_CONSOLES) return; s = consoles[index]; if (s) { active_console = s; if (s->text_console) { if (s->g_width != s->ds->width || s->g_height != s->ds->height) { s->g_width = s->ds->width; s->g_height = s->ds->height; text_console_resize(s); } console_refresh(s); } else { vga_hw_invalidate(); } } } static int console_puts(CharDriverState *chr, const uint8_t *buf, int len) { TextConsole *s = chr->opaque; int i; console_show_cursor(s, 0); for(i = 0; i < len; i++) { console_putchar(s, buf[i]); } console_show_cursor(s, 1); return len; } static void console_chr_add_read_handler(CharDriverState *chr, IOCanRWHandler *fd_can_read, IOReadHandler *fd_read, void *opaque) { TextConsole *s = chr->opaque; s->fd_can_read = fd_can_read; s->fd_read = fd_read; s->fd_opaque = opaque; } static void console_send_event(CharDriverState *chr, int event) { TextConsole *s = chr->opaque; int i; if (event == CHR_EVENT_FOCUS) { for(i = 0; i < nb_consoles; i++) { if (consoles[i] == s) { console_select(i); break; } } } } static void kbd_send_chars(void *opaque) { TextConsole *s = opaque; int len; uint8_t buf[16]; len = s->fd_can_read(s->fd_opaque); if (len > s->out_fifo.count) len = s->out_fifo.count; if (len > 0) { if (len > sizeof(buf)) len = sizeof(buf); qemu_fifo_read(&s->out_fifo, buf, len); s->fd_read(s->fd_opaque, buf, len); } /* characters are pending: we send them a bit later (XXX: horrible, should change char device API) */ if (s->out_fifo.count > 0) { qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1); } } /* called when an ascii key is pressed */ void kbd_put_keysym(int keysym) { TextConsole *s; uint8_t buf[16], *q; int c; s = active_console; if (!s || !s->text_console) return; switch(keysym) { case QEMU_KEY_CTRL_UP: console_scroll(-1); break; case QEMU_KEY_CTRL_DOWN: console_scroll(1); break; case QEMU_KEY_CTRL_PAGEUP: console_scroll(-10); break; case QEMU_KEY_CTRL_PAGEDOWN: console_scroll(10); break; default: /* convert the QEMU keysym to VT100 key string */ q = buf; if (keysym >= 0xe100 && keysym <= 0xe11f) { *q++ = '\033'; *q++ = '['; c = keysym - 0xe100; if (c >= 10) *q++ = '0' + (c / 10); *q++ = '0' + (c % 10); *q++ = '~'; } else if (keysym >= 0xe120 && keysym <= 0xe17f) { *q++ = '\033'; *q++ = '['; *q++ = keysym & 0xff; } else { *q++ = keysym; } if (s->fd_read) { qemu_fifo_write(&s->out_fifo, buf, q - buf); kbd_send_chars(s); } break; } } static TextConsole *new_console(DisplayState *ds, int text) { TextConsole *s; int i; if (nb_consoles >= MAX_CONSOLES) return NULL; s = qemu_mallocz(sizeof(TextConsole)); if (!s) { return NULL; } if (!active_console || (active_console->text_console && !text)) active_console = s; s->ds = ds; s->text_console = text; if (text) { consoles[nb_consoles++] = s; } else { /* HACK: Put graphical consoles before text consoles. */ for (i = nb_consoles; i > 0; i--) { if (!consoles[i - 1]->text_console) break; consoles[i] = consoles[i - 1]; } consoles[i] = s; } return s; } TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update, vga_hw_invalidate_ptr invalidate, vga_hw_screen_dump_ptr screen_dump, void *opaque) { TextConsole *s; s = new_console(ds, 0); if (!s) return NULL; s->hw_update = update; s->hw_invalidate = invalidate; s->hw_screen_dump = screen_dump; s->hw = opaque; return s; } int is_graphic_console(void) { return !active_console->text_console; } void set_color_table(DisplayState *ds) { int i, j; for(j = 0; j < 2; j++) { for(i = 0; i < 8; i++) { color_table[j][i] = col_expand(ds, vga_get_color(ds, color_table_rgb[j][i])); } } } CharDriverState *text_console_init(DisplayState *ds) { CharDriverState *chr; TextConsole *s; static int color_inited; chr = qemu_mallocz(sizeof(CharDriverState)); if (!chr) return NULL; s = new_console(ds, 1); if (!s) { free(chr); return NULL; } chr->opaque = s; chr->chr_write = console_puts; chr->chr_add_read_handler = console_chr_add_read_handler; chr->chr_send_event = console_send_event; s->out_fifo.buf = s->out_fifo_buf; s->out_fifo.buf_size = sizeof(s->out_fifo_buf); s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s); if (!color_inited) { color_inited = 1; set_color_table(ds); } s->y_displayed = 0; s->y_base = 0; s->total_height = DEFAULT_BACKSCROLL; s->x = 0; s->y = 0; s->g_width = s->ds->width; s->g_height = s->ds->height; /* Set text attribute defaults */ s->t_attrib_default.bold = 0; s->t_attrib_default.uline = 0; s->t_attrib_default.blink = 0; s->t_attrib_default.invers = 0; s->t_attrib_default.unvisible = 0; s->t_attrib_default.fgcol = COLOR_WHITE; s->t_attrib_default.bgcol = COLOR_BLACK; /* set current text attributes to default */ s->t_attrib = s->t_attrib_default; text_console_resize(s); return chr; }