summaryrefslogtreecommitdiff
path: root/fblog
diff options
context:
space:
mode:
Diffstat (limited to 'fblog')
-rw-r--r--fblog/0001-fblog-new-framebuffer-kernel-log-dummy-driver.patch227
-rw-r--r--fblog/0002-fblog-implement-buffer-management.patch159
-rw-r--r--fblog/0003-fblog-register-framebuffer-objects.patch152
-rw-r--r--fblog/0004-fblog-implement-fblog_redraw.patch163
-rw-r--r--fblog/0005-fblog-add-framebuffer-helpers.patch60
-rw-r--r--fblog/0006-fblog-allow-enabling-disabling-fblog-on-runtime.patch117
-rw-r--r--fblog/0007-fblog-forward-kernel-log-messages-to-all-framebuffer.patch51
-rw-r--r--fblog/0008-fblog-react-on-framebuffer-events.patch144
-rw-r--r--fblog/0009-fblog-register-all-handlers-on-module-init.patch102
-rw-r--r--fblog/0010-fblog-add-activate-module-parameter.patch55
10 files changed, 1230 insertions, 0 deletions
diff --git a/fblog/0001-fblog-new-framebuffer-kernel-log-dummy-driver.patch b/fblog/0001-fblog-new-framebuffer-kernel-log-dummy-driver.patch
new file mode 100644
index 0000000..d68a1bb
--- /dev/null
+++ b/fblog/0001-fblog-new-framebuffer-kernel-log-dummy-driver.patch
@@ -0,0 +1,227 @@
+From 19c80ae416f42991c764501ff2283b0294fd6b5b Mon Sep 17 00:00:00 2001
+From: David Herrmann <dh.herrmann@googlemail.com>
+Date: Sat, 16 Jun 2012 22:44:09 +0200
+Subject: [PATCH 01/10] fblog: new framebuffer kernel log dummy driver
+
+Fblog displays all kernel log messages on all connected framebuffers. It
+replaces fbcon when CONFIG_VT=n is selected. Its main purpose is to debug
+boot problems by displaying the whole boot log on the screen. This patch
+provides the first dummy module-init/deinit functions.
+
+As is uses all the font and fb functions I placed it in
+drivers/video/console. However, this means that we need to move the check
+for CONFIG_VT in Makefile/Kconfig from drivers/video into
+drivers/video/console as fblog does not depend on CONFIG_VT.
+
+Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
+---
+ drivers/video/Kconfig | 5 +---
+ drivers/video/Makefile | 2 +-
+ drivers/video/console/Kconfig | 37 +++++++++++++++++++++------
+ drivers/video/console/Makefile | 1 +
+ drivers/video/console/fblog.c | 55 ++++++++++++++++++++++++++++++++++++++++
+ 5 files changed, 87 insertions(+), 13 deletions(-)
+ create mode 100644 drivers/video/console/fblog.c
+
+diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
+index 0217f74..e8fd53d 100644
+--- a/drivers/video/Kconfig
++++ b/drivers/video/Kconfig
+@@ -2448,10 +2448,7 @@ source "drivers/video/omap/Kconfig"
+ source "drivers/video/omap2/Kconfig"
+ source "drivers/video/exynos/Kconfig"
+ source "drivers/video/backlight/Kconfig"
+-
+-if VT
+- source "drivers/video/console/Kconfig"
+-endif
++source "drivers/video/console/Kconfig"
+
+ if FB || SGI_NEWPORT_CONSOLE
+ source "drivers/video/logo/Kconfig"
+diff --git a/drivers/video/Makefile b/drivers/video/Makefile
+index ee8dafb..9f8a7f0 100644
+--- a/drivers/video/Makefile
++++ b/drivers/video/Makefile
+@@ -11,7 +11,7 @@ fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \
+ modedb.o fbcvt.o
+ fb-objs := $(fb-y)
+
+-obj-$(CONFIG_VT) += console/
++obj-y += console/
+ obj-$(CONFIG_LOGO) += logo/
+ obj-y += backlight/
+
+diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
+index c2d11fe..cfee482 100644
+--- a/drivers/video/console/Kconfig
++++ b/drivers/video/console/Kconfig
+@@ -6,7 +6,7 @@ menu "Console display driver support"
+
+ config VGA_CONSOLE
+ bool "VGA text console" if EXPERT || !X86
+- depends on !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !SUPERH && !BLACKFIN && !AVR32 && !MN10300 && (!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER)
++ depends on VT && !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !SUPERH && !BLACKFIN && !AVR32 && !MN10300 && (!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER)
+ default y
+ help
+ Saying Y here will allow you to use Linux in text mode through a
+@@ -45,7 +45,7 @@ config VGACON_SOFT_SCROLLBACK_SIZE
+ screenfuls of scrollback buffer
+
+ config MDA_CONSOLE
+- depends on !M68K && !PARISC && ISA
++ depends on VT && !M68K && !PARISC && ISA
+ tristate "MDA text console (dual-headed) (EXPERIMENTAL)"
+ ---help---
+ Say Y here if you have an old MDA or monochrome Hercules graphics
+@@ -61,14 +61,14 @@ config MDA_CONSOLE
+
+ config SGI_NEWPORT_CONSOLE
+ tristate "SGI Newport Console support"
+- depends on SGI_IP22
++ depends on VT && SGI_IP22
+ help
+ Say Y here if you want the console on the Newport aka XL graphics
+ card of your Indy. Most people say Y here.
+
+ config DUMMY_CONSOLE
+ bool
+- depends on VGA_CONSOLE!=y || SGI_NEWPORT_CONSOLE!=y
++ depends on VT && (VGA_CONSOLE!=y || SGI_NEWPORT_CONSOLE!=y)
+ default y
+
+ config DUMMY_CONSOLE_COLUMNS
+@@ -89,7 +89,7 @@ config DUMMY_CONSOLE_ROWS
+
+ config FRAMEBUFFER_CONSOLE
+ tristate "Framebuffer Console support"
+- depends on FB
++ depends on VT && FB
+ select CRC32
+ help
+ Low-level framebuffer-based console driver.
+@@ -122,16 +122,37 @@ config FRAMEBUFFER_CONSOLE_ROTATION
+
+ config STI_CONSOLE
+ bool "STI text console"
+- depends on PARISC
++ depends on VT && PARISC
+ default y
+ help
+ The STI console is the builtin display/keyboard on HP-PARISC
+ machines. Say Y here to build support for it into your kernel.
+ The alternative is to use your primary serial port as a console.
+
++config FBLOG
++ tristate "Framebuffer Kernel Log Driver"
++ depends on !VT && FB
++ default n
++ help
++ This driver displays all kernel log messages on all connected
++ framebuffers. It is mutually exclusive with CONFIG_FRAMEBUFFER_CONSOLE
++ and CONFIG_VT. It was mainly created for debugging purposes when
++ CONFIG_VT is not selected but you still want kernel boot messages on
++ the screen.
++
++ This driver overwrites all other graphics output on the framebuffer as
++ long as it is active so the kernel log will always be visible. You
++ need to disable this driver via sysfs to be able to start another
++ graphics application.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the module will
++ be called fblog.
++
+ config FONTS
+ bool "Select compiled-in fonts"
+- depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
++ depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE || FBLOG
+ help
+ Say Y here if you would like to use fonts other than the default
+ your frame buffer console usually use.
+@@ -158,7 +179,7 @@ config FONT_8x8
+
+ config FONT_8x16
+ bool "VGA 8x16 font" if FONTS
+- depends on FRAMEBUFFER_CONSOLE || SGI_NEWPORT_CONSOLE || STI_CONSOLE || USB_SISUSBVGA_CON
++ depends on FRAMEBUFFER_CONSOLE || SGI_NEWPORT_CONSOLE || STI_CONSOLE || USB_SISUSBVGA_CON || FBLOG
+ default y if !SPARC && !FONTS
+ help
+ This is the "high resolution" font for the VGA frame buffer (the one
+diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile
+index a862e91..f608c97 100644
+--- a/drivers/video/console/Makefile
++++ b/drivers/video/console/Makefile
+@@ -20,6 +20,7 @@ font-objs += $(font-objs-y)
+
+ # Each configuration option enables a list of files.
+
++obj-$(CONFIG_FBLOG) += fblog.o font.o
+ obj-$(CONFIG_DUMMY_CONSOLE) += dummycon.o
+ obj-$(CONFIG_SGI_NEWPORT_CONSOLE) += newport_con.o font.o
+ obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o font.o
+diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
+new file mode 100644
+index 0000000..ea83643
+--- /dev/null
++++ b/drivers/video/console/fblog.c
+@@ -0,0 +1,55 @@
++/*
++ * Framebuffer Kernel Log Driver
++ * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
++ */
++
++/*
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the Free
++ * Software Foundation; either version 2 of the License, or (at your option)
++ * any later version.
++ */
++
++/*
++ * Framebuffer Kernel Log
++ * This driver prints the kernel log to all connected display devices. It
++ * replaces CONFIG_VT and cannot run simultaneously with it. It does not provide
++ * any virtual-terminal, though. It should only be used to get kernel boot
++ * messages to debug kernel errors.
++ * Hence, this driver is neither optimized for speed, nor does it provide any
++ * fancy features like colored text output. After booting is done, the init
++ * process should set /sys/class/graphics/fblog/active to 0 which disables this
++ * driver and you can start using the graphics devices. During shutdown, you can
++ * set this to 1 to get log messages again.
++ * This driver forcibly writes to the framebuffer while active, therefore, you
++ * cannot run other graphics applications simultaneously.
++ *
++ * fblog_redraw_line() is heavily based on the fbcon driver. See bitblit.c for
++ * the original implementation copyrighted by:
++ * Copyright (C) 2004 Antonino Daplas <adaplas@pol.net>
++ *
++ * Please note that nearly all functions here must be called with console_lock
++ * held. This way, we have no locking problems and do not need special
++ * synchronization.
++ */
++
++#include <linux/atomic.h>
++#include <linux/console.h>
++#include <linux/fb.h>
++#include <linux/font.h>
++#include <linux/module.h>
++
++static int __init fblog_init(void)
++{
++ return 0;
++}
++
++static void __exit fblog_exit(void)
++{
++}
++
++module_init(fblog_init);
++module_exit(fblog_exit);
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("David Herrmann <dh.herrmann@googlemail.com>");
++MODULE_DESCRIPTION("Framebuffer Kernel Log Driver");
+--
+1.7.10.4
+
diff --git a/fblog/0002-fblog-implement-buffer-management.patch b/fblog/0002-fblog-implement-buffer-management.patch
new file mode 100644
index 0000000..a79a49b
--- /dev/null
+++ b/fblog/0002-fblog-implement-buffer-management.patch
@@ -0,0 +1,159 @@
+From 71d0022376609edba28571ee1ecab3c4ccfcc009 Mon Sep 17 00:00:00 2001
+From: David Herrmann <dh.herrmann@googlemail.com>
+Date: Sat, 16 Jun 2012 22:49:35 +0200
+Subject: [PATCH 02/10] fblog: implement buffer management
+
+Each available framebuffer can have a different font and buffer size
+inside of fblog. Therefore, we need to remember all the log messages that
+are currently printed on screen. We save them as an array of lines which
+can be rotated and traversed very fast.
+
+This also implements a very trivial way of resizing the buffer but still
+keeping the content. As there is no need to improve this for speed, we can
+keep it this way.
+
+Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
+---
+ drivers/video/console/fblog.c | 126 +++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 126 insertions(+)
+
+diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
+index ea83643..1504ba9 100644
+--- a/drivers/video/console/fblog.c
++++ b/drivers/video/console/fblog.c
+@@ -39,6 +39,132 @@
+ #include <linux/font.h>
+ #include <linux/module.h>
+
++/**
++ * struct fblog_buf: Console text buffer
++ *
++ * Each framebuffer has its own text buffer which contains all characters that
++ * are currently printed on screen. The buffers might have different sizes and
++ * can be resized during runtime. When the buffer content changes, we redraw the
++ * screen.
++ *
++ * width: Width of buffer in characters
++ * height: Height of buffer in characters
++ * lines: Array of lines
++ * pos_x: Cursor x-position
++ * pos_y: Cursor y-position
++ */
++struct fblog_buf {
++ size_t width;
++ size_t height;
++ char **lines;
++ size_t pos_x;
++ size_t pos_y;
++};
++
++static void fblog_buf_resize(struct fblog_buf *buf, size_t width,
++ size_t height)
++{
++ char **lines = NULL;
++ size_t i, j, minw, minh;
++
++ if (buf->height == height && buf->width == width)
++ return;
++
++ if (width && height) {
++ lines = kzalloc(height * sizeof(char*), GFP_KERNEL);
++ if (!lines)
++ return;
++
++ for (i = 0; i < height; ++i) {
++ lines[i] = kzalloc(width * sizeof(char), GFP_KERNEL);
++ if (!lines[i]) {
++ while (i--)
++ kfree(lines[i]);
++ return;
++ }
++ }
++
++ /* copy old lines */
++ minw = min(width, buf->width);
++ minh = min(height, buf->height);
++ if (height >= buf->height)
++ i = 0;
++ else
++ i = buf->height - height;
++
++ for (j = 0; j < minh; ++i, ++j)
++ memcpy(lines[j], buf->lines[i], minw * sizeof(char));
++ } else {
++ width = 0;
++ height = 0;
++ }
++
++ for (i = 0; i < buf->height; ++i)
++ kfree(buf->lines[i]);
++ kfree(buf->lines);
++
++ buf->lines = lines;
++ buf->width = width;
++ buf->height = height;
++}
++
++static void fblog_buf_init(struct fblog_buf *buf)
++{
++ fblog_buf_resize(buf, 80, 24);
++}
++
++static void fblog_buf_deinit(struct fblog_buf *buf)
++{
++ fblog_buf_resize(buf, 0, 0);
++}
++
++static void fblog_buf_rotate(struct fblog_buf *buf)
++{
++ char *line;
++
++ if (!buf->height)
++ return;
++
++ line = buf->lines[0];
++ memset(line, 0, sizeof(char) * buf->width);
++
++ memmove(buf->lines, &buf->lines[1], sizeof(char*) * (buf->height - 1));
++ buf->lines[buf->height - 1] = line;
++}
++
++static void fblog_buf_write(struct fblog_buf *buf, const char *str, size_t len)
++{
++ char c;
++
++ if (!buf->height)
++ return;
++
++ while (len--) {
++ c = *str++;
++
++ if (c == '\n') {
++ buf->pos_x = 0;
++ if (++buf->pos_y >= buf->height) {
++ buf->pos_y = buf->height - 1;
++ fblog_buf_rotate(buf);
++ }
++ } else if (c == 0) {
++ /* ignore */
++ } else {
++ if (buf->pos_x >= buf->width) {
++ buf->pos_x = 0;
++ ++buf->pos_y;
++ }
++ if (buf->pos_y >= buf->height) {
++ buf->pos_y = buf->height - 1;
++ fblog_buf_rotate(buf);
++ }
++
++ buf->lines[buf->pos_y][buf->pos_x++] = c;
++ }
++ }
++}
++
+ static int __init fblog_init(void)
+ {
+ return 0;
+--
+1.7.10.4
+
diff --git a/fblog/0003-fblog-register-framebuffer-objects.patch b/fblog/0003-fblog-register-framebuffer-objects.patch
new file mode 100644
index 0000000..fab782a
--- /dev/null
+++ b/fblog/0003-fblog-register-framebuffer-objects.patch
@@ -0,0 +1,152 @@
+From cbb97ae4af35277a5478383045a86438f2e97c71 Mon Sep 17 00:00:00 2001
+From: David Herrmann <dh.herrmann@googlemail.com>
+Date: Sat, 16 Jun 2012 22:56:55 +0200
+Subject: [PATCH 03/10] fblog: register framebuffer objects
+
+We register each available framebuffer in the system with the fblog driver
+so we always know all active devices. We directly open the fb-driver,
+initialize the buffer and load a font so we are ready for drawing
+operations. If a device cannot be opened, we mark it as dead and ignore it
+in all other functions.
+
+Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
+---
+ drivers/video/console/fblog.c | 108 +++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 108 insertions(+)
+
+diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
+index 1504ba9..8038dcc 100644
+--- a/drivers/video/console/fblog.c
++++ b/drivers/video/console/fblog.c
+@@ -39,6 +39,14 @@
+ #include <linux/font.h>
+ #include <linux/module.h>
+
++#define FBLOG_STR(x) x, sizeof(x) - 1
++
++enum fblog_flags {
++ FBLOG_KILLED,
++ FBLOG_SUSPENDED,
++ FBLOG_BLANKED,
++};
++
+ /**
+ * struct fblog_buf: Console text buffer
+ *
+@@ -61,6 +69,30 @@ struct fblog_buf {
+ size_t pos_y;
+ };
+
++/**
++ * struct fblog_fb: Framebuffer object
++ *
++ * For each framebuffer we register this object. It contains all data we need to
++ * display the console log on it. The index of a framebuffer in registered_fb[]
++ * is the same as in fblog_fbs[]. So the following must always be true if the
++ * pointers are non-NULL:
++ * registered_fb[idx] == fblog_fbs[idx]->info
++ * fblog_fbs[idx]->info->node == idx
++ *
++ * flags: Framebuffer flags (see fblog_flags)
++ * info: Pointer to the associated framebuffer device
++ * font: Currently used font
++ * buf: Console text buffer
++ */
++struct fblog_fb {
++ unsigned long flags;
++ struct fb_info *info;
++ const struct font_desc *font;
++ struct fblog_buf buf;
++};
++
++static struct fblog_fb *fblog_fbs[FB_MAX];
++
+ static void fblog_buf_resize(struct fblog_buf *buf, size_t width,
+ size_t height)
+ {
+@@ -165,6 +197,82 @@ static void fblog_buf_write(struct fblog_buf *buf, const char *str, size_t len)
+ }
+ }
+
++static struct fblog_fb *fblog_info2fb(struct fb_info *info)
++{
++ if (!info || info->node < 0 || info->node >= FB_MAX ||
++ !registered_fb[info->node])
++ return NULL;
++
++ return fblog_fbs[info->node];
++}
++
++static void fblog_register(struct fb_info *info)
++{
++ struct fblog_fb *fb;
++ struct fb_var_screeninfo var;
++ const struct fb_videomode *mode;
++ unsigned int width, height;
++
++ if (!info || info->node < 0 || info->node >= FB_MAX)
++ return;
++ if (!registered_fb[info->node] || fblog_fbs[info->node])
++ return;
++
++ fb = kzalloc(sizeof(*fb), GFP_KERNEL);
++ if (!fb)
++ return;
++
++ fblog_fbs[info->node] = fb;
++ fb->info = info;
++ fblog_buf_init(&fb->buf);
++ fblog_buf_write(&fb->buf, FBLOG_STR("Framebuffer log initialized\n"));
++
++ if (!try_module_get(info->fbops->owner))
++ goto out_killed;
++ if (info->fbops->fb_open && info->fbops->fb_open(info, 0))
++ goto out_unref;
++
++ var = info->var;
++ mode = fb_find_best_mode(&var, &info->modelist);
++ var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
++ fb_set_var(info, &var);
++
++ fb->font = get_default_font(info->var.xres, info->var.yres,
++ info->pixmap.blit_x,
++ info->pixmap.blit_y);
++ if (fb->font) {
++ width = info->var.xres / fb->font->width;
++ height = info->var.yres / fb->font->height;
++ fblog_buf_resize(&fb->buf, width, height);
++ }
++
++ return;
++
++out_unref:
++ module_put(info->fbops->owner);
++out_killed:
++ set_bit(FBLOG_KILLED, &fb->flags);
++}
++
++static void fblog_unregister(struct fblog_fb *fb)
++{
++ struct fb_info *info;
++
++ if (!fb)
++ return;
++
++ info = fb->info;
++ if (!test_bit(FBLOG_KILLED, &fb->flags)) {
++ if (info->fbops->fb_release)
++ info->fbops->fb_release(info, 0);
++ module_put(info->fbops->owner);
++ }
++
++ fblog_buf_deinit(&fb->buf);
++ fblog_fbs[info->node] = NULL;
++ kfree(fb);
++}
++
+ static int __init fblog_init(void)
+ {
+ return 0;
+--
+1.7.10.4
+
diff --git a/fblog/0004-fblog-implement-fblog_redraw.patch b/fblog/0004-fblog-implement-fblog_redraw.patch
new file mode 100644
index 0000000..9f48163
--- /dev/null
+++ b/fblog/0004-fblog-implement-fblog_redraw.patch
@@ -0,0 +1,163 @@
+From 086525bfbf807d8c2792b18d35127c791289d0b8 Mon Sep 17 00:00:00 2001
+From: David Herrmann <dh.herrmann@googlemail.com>
+Date: Sat, 16 Jun 2012 23:01:41 +0200
+Subject: [PATCH 04/10] fblog: implement fblog_redraw()
+
+This mostly copies the functionality from drivers/video/console/bitblit.c
+so we can draw the console content on all available framebuffers.
+
+All speed optimizations have been removed for simplicity. The original
+code depends heavily on CONFIG_VT so we cannot share the codebase here.
+
+Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
+---
+ drivers/video/console/fblog.c | 126 +++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 126 insertions(+)
+
+diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
+index 8038dcc..e790971 100644
+--- a/drivers/video/console/fblog.c
++++ b/drivers/video/console/fblog.c
+@@ -197,6 +197,131 @@ static void fblog_buf_write(struct fblog_buf *buf, const char *str, size_t len)
+ }
+ }
+
++static void fblog_redraw_aligned(struct fblog_fb *fb, const char *s, u32 cnt,
++ u32 d_pitch, u32 s_pitch, u32 cellsize,
++ struct fb_image *image, u8 *dst)
++{
++ struct fb_info *info = fb->info;
++ const struct font_desc *font = fb->font;
++ u32 idx = font->width >> 3;
++ u8 *src;
++
++ while (cnt--) {
++ src = (void*)(font->data + (*s++ & 0xff) * cellsize);
++ fb_pad_aligned_buffer(dst, d_pitch, src, idx, image->height);
++ dst += s_pitch;
++ }
++
++ info->fbops->fb_imageblit(info, image);
++}
++
++static void fblog_redraw_unaligned(struct fblog_fb *fb, const char *s, u32 cnt,
++ u32 d_pitch, u32 s_pitch, u32 cellsize,
++ struct fb_image *image, u8 *dst)
++{
++ struct fb_info *info = fb->info;
++ const struct font_desc *font = fb->font;
++ u32 shift_low = 0, mod = font->width % 8;
++ u32 shift_high = 8;
++ u32 idx = font->width >> 3;
++ u8 *src;
++
++ while (cnt--) {
++ src = (void*)(font->data + (*s++ & 0xff) * cellsize);
++ fb_pad_unaligned_buffer(dst, d_pitch, src, idx,
++ image->height, shift_high,
++ shift_low, mod);
++ shift_low += mod;
++ dst += (shift_low >= 8) ? s_pitch : s_pitch - 1;
++ shift_low &= 7;
++ shift_high = 8 - shift_low;
++ }
++
++ info->fbops->fb_imageblit(info, image);
++}
++
++static void fblog_redraw_line(struct fblog_fb *fb, size_t line,
++ const char *str, size_t len)
++{
++ struct fb_info *info = fb->info;
++ const struct font_desc *font = fb->font;
++ struct fb_image image;
++ u32 width = DIV_ROUND_UP(font->width, 8);
++ u32 cellsize = width * font->height;
++ u32 maxcnt = info->pixmap.size / cellsize;
++ u32 scan_align = info->pixmap.scan_align - 1;
++ u32 buf_align = info->pixmap.buf_align - 1;
++ u32 mod = font->width % 8;
++ u32 cnt, pitch, size;
++ u8 *dst;
++
++ image.fg_color = 7;
++ image.bg_color = 0;
++ image.dx = 0;
++ image.dy = line * font->height;
++ image.height = font->height;
++ image.depth = 1;
++
++ while (len) {
++ if (len > maxcnt)
++ cnt = maxcnt;
++ else
++ cnt = len;
++
++ image.width = font->width * cnt;
++ pitch = DIV_ROUND_UP(image.width, 8) + scan_align;
++ pitch &= ~scan_align;
++ size = pitch * image.height + buf_align;
++ size &= ~buf_align;
++ dst = fb_get_buffer_offset(info, &info->pixmap, size);
++ image.data = dst;
++
++ if (!mod)
++ fblog_redraw_aligned(fb, str, cnt, pitch, width,
++ cellsize, &image, dst);
++ else
++ fblog_redraw_unaligned(fb, str, cnt, pitch, width,
++ cellsize, &image, dst);
++
++ image.dx += cnt * font->width;
++ len -= cnt;
++ str += cnt;
++ }
++}
++
++static void fblog_redraw_clear(struct fblog_fb *fb)
++{
++ struct fb_fillrect region;
++ struct fb_info *info = fb->info;
++
++ region.color = 0;
++ region.dx = 0;
++ region.dy = 0;
++ region.width = info->var.xres;
++ region.height = info->var.yres;
++ region.rop = ROP_COPY;
++
++ info->fbops->fb_fillrect(info, &region);
++}
++
++static void fblog_redraw(struct fblog_fb *fb)
++{
++ size_t i, len;
++
++ if (!fb || !fb->font || test_bit(FBLOG_KILLED, &fb->flags) ||
++ test_bit(FBLOG_SUSPENDED, &fb->flags) ||
++ test_bit(FBLOG_BLANKED, &fb->flags))
++ return;
++
++ fblog_redraw_clear(fb);
++
++ for (i = 0; i < fb->buf.height; ++i) {
++ len = strnlen(fb->buf.lines[i], fb->buf.width);
++ if (len)
++ fblog_redraw_line(fb, i, fb->buf.lines[i], len);
++ }
++}
++
+ static struct fblog_fb *fblog_info2fb(struct fb_info *info)
+ {
+ if (!info || info->node < 0 || info->node >= FB_MAX ||
+@@ -244,6 +369,7 @@ static void fblog_register(struct fb_info *info)
+ width = info->var.xres / fb->font->width;
+ height = info->var.yres / fb->font->height;
+ fblog_buf_resize(&fb->buf, width, height);
++ fblog_redraw(fb);
+ }
+
+ return;
+--
+1.7.10.4
+
diff --git a/fblog/0005-fblog-add-framebuffer-helpers.patch b/fblog/0005-fblog-add-framebuffer-helpers.patch
new file mode 100644
index 0000000..69aa09e
--- /dev/null
+++ b/fblog/0005-fblog-add-framebuffer-helpers.patch
@@ -0,0 +1,60 @@
+From c0dbeaf1fa932dbae6027a49ddbff973b2689eea Mon Sep 17 00:00:00 2001
+From: David Herrmann <dh.herrmann@googlemail.com>
+Date: Sat, 16 Jun 2012 23:04:48 +0200
+Subject: [PATCH 05/10] fblog: add framebuffer helpers
+
+These helpers scan the system for all available framebuffers and register
+or unregister them. This is needed during startup and stopping fblog so we
+are aware of all connected displays.
+
+The third helper handles mode changes by rescanning the mode and adjusting
+the buffer size.
+
+Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
+---
+ drivers/video/console/fblog.c | 29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
+index e790971..7d4032e 100644
+--- a/drivers/video/console/fblog.c
++++ b/drivers/video/console/fblog.c
+@@ -399,6 +399,35 @@ static void fblog_unregister(struct fblog_fb *fb)
+ kfree(fb);
+ }
+
++static void fblog_register_all(void)
++{
++ int i;
++
++ for (i = 0; i < FB_MAX; ++i)
++ fblog_register(registered_fb[i]);
++}
++
++static void fblog_unregister_all(void)
++{
++ int i;
++
++ for (i = 0; i < FB_MAX; ++i)
++ fblog_unregister(fblog_info2fb(registered_fb[i]));
++}
++
++static void fblog_refresh(struct fblog_fb *fb)
++{
++ unsigned int width, height;
++
++ if (!fb || !fb->font)
++ return;
++
++ width = fb->info->var.xres / fb->font->width;
++ height = fb->info->var.yres / fb->font->height;
++ fblog_buf_resize(&fb->buf, width, height);
++ fblog_redraw(fb);
++}
++
+ static int __init fblog_init(void)
+ {
+ return 0;
+--
+1.7.10.4
+
diff --git a/fblog/0006-fblog-allow-enabling-disabling-fblog-on-runtime.patch b/fblog/0006-fblog-allow-enabling-disabling-fblog-on-runtime.patch
new file mode 100644
index 0000000..7204c64
--- /dev/null
+++ b/fblog/0006-fblog-allow-enabling-disabling-fblog-on-runtime.patch
@@ -0,0 +1,117 @@
+From cab17dccb6026f9f33717db72f528f3b84d7393f Mon Sep 17 00:00:00 2001
+From: David Herrmann <dh.herrmann@googlemail.com>
+Date: Sat, 16 Jun 2012 23:07:50 +0200
+Subject: [PATCH 06/10] fblog: allow enabling/disabling fblog on runtime
+
+A sysfs file called "active" can be used to enable and disable fblog on
+runtime. For example, the init-process can run "echo '0' >.../active"
+after booting the system. This will allow other applications like X11 to
+use the graphics subsystem. During shutdown, we can write '1' to get
+system messages again.
+
+When disabling fblog, we remove all framebuffers and will prevent new
+hotplugged framebuffers from being added. When enabling fblog again, we
+rescan the system for all framebuffers and resume operating.
+
+The sysfs file is not registered, yet, as we do not have a "struct device"
+yet. This will follow shortly, though.
+
+Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
+---
+ Documentation/ABI/testing/sysfs-fblog | 9 ++++++
+ drivers/video/console/fblog.c | 49 +++++++++++++++++++++++++++++++++
+ 2 files changed, 58 insertions(+)
+ create mode 100644 Documentation/ABI/testing/sysfs-fblog
+
+diff --git a/Documentation/ABI/testing/sysfs-fblog b/Documentation/ABI/testing/sysfs-fblog
+new file mode 100644
+index 0000000..596393c
+--- /dev/null
++++ b/Documentation/ABI/testing/sysfs-fblog
+@@ -0,0 +1,9 @@
++What: /sys/class/graphics/fblog/active
++Date: June 2012
++KernelVersion: 3.6
++Contact: David Herrmann <dh.herrmann@googlemail.com>
++Description: Enable/Disable fblog. When setting this to 0, fblog will stop
++ writing to framebuffers and other applications can use the
++ graphics subsystem. When setting this to 1, fblog will rescan
++ the system for all framebuffers and resume drawing the kernel
++ log onto all framebuffers.
+diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
+index 7d4032e..9b05c56 100644
+--- a/drivers/video/console/fblog.c
++++ b/drivers/video/console/fblog.c
+@@ -92,6 +92,7 @@ struct fblog_fb {
+ };
+
+ static struct fblog_fb *fblog_fbs[FB_MAX];
++static atomic_t fblog_active;
+
+ static void fblog_buf_resize(struct fblog_buf *buf, size_t width,
+ size_t height)
+@@ -338,6 +339,8 @@ static void fblog_register(struct fb_info *info)
+ const struct fb_videomode *mode;
+ unsigned int width, height;
+
++ if (!atomic_read(&fblog_active))
++ return;
+ if (!info || info->node < 0 || info->node >= FB_MAX)
+ return;
+ if (!registered_fb[info->node] || fblog_fbs[info->node])
+@@ -428,6 +431,52 @@ static void fblog_refresh(struct fblog_fb *fb)
+ fblog_redraw(fb);
+ }
+
++static void fblog_activate(void)
++{
++ if (atomic_read(&fblog_active))
++ return;
++
++ atomic_set(&fblog_active, 1);
++ fblog_register_all();
++}
++
++static void fblog_deactivate(void)
++{
++ if (!atomic_read(&fblog_active))
++ return;
++
++ atomic_set(&fblog_active, 0);
++ fblog_unregister_all();
++}
++
++static ssize_t fblog_dev_active_show(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&fblog_active));
++}
++
++static ssize_t fblog_dev_active_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf,
++ size_t count)
++{
++ unsigned long num;
++
++ num = simple_strtoul(buf, NULL, 10);
++ console_lock();
++ if (num)
++ fblog_activate();
++ else
++ fblog_deactivate();
++ console_unlock();
++
++ return count;
++}
++
++static DEVICE_ATTR(active, S_IRUGO | S_IWUSR | S_IWGRP, fblog_dev_active_show,
++ fblog_dev_active_store);
++
+ static int __init fblog_init(void)
+ {
+ return 0;
+--
+1.7.10.4
+
diff --git a/fblog/0007-fblog-forward-kernel-log-messages-to-all-framebuffer.patch b/fblog/0007-fblog-forward-kernel-log-messages-to-all-framebuffer.patch
new file mode 100644
index 0000000..75955f1
--- /dev/null
+++ b/fblog/0007-fblog-forward-kernel-log-messages-to-all-framebuffer.patch
@@ -0,0 +1,51 @@
+From 942d4ff8f16fcf9d9fdf50d7241a3a8fb4fec39a Mon Sep 17 00:00:00 2001
+From: David Herrmann <dh.herrmann@googlemail.com>
+Date: Sat, 16 Jun 2012 23:20:52 +0200
+Subject: [PATCH 07/10] fblog: forward kernel log messages to all framebuffers
+
+This provides a console-driver that forwards all log messages to all
+framebuffers and redraws them.
+
+To avoid redrawing multiple times in short intervals, we could use a
+work-queue here by simply pushing the task onto the system work-queue.
+However, fblog is not performance critical and only used for debugging so
+we avoid the complexity for now. This may change in the future, though.
+
+Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
+---
+ drivers/video/console/fblog.c | 19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+
+diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
+index 9b05c56..5297eca 100644
+--- a/drivers/video/console/fblog.c
++++ b/drivers/video/console/fblog.c
+@@ -449,6 +449,25 @@ static void fblog_deactivate(void)
+ fblog_unregister_all();
+ }
+
++static void fblog_con_write(struct console *con, const char *buf,
++ unsigned int len)
++{
++ int i;
++
++ for (i = 0; i < FB_MAX; ++i) {
++ if (fblog_fbs[i]) {
++ fblog_buf_write(&fblog_fbs[i]->buf, buf, len);
++ fblog_redraw(fblog_fbs[i]);
++ }
++ }
++}
++
++static struct console fblog_con_driver = {
++ .name = "fblog",
++ .write = fblog_con_write,
++ .flags = CON_PRINTBUFFER | CON_ENABLED,
++};
++
+ static ssize_t fblog_dev_active_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+--
+1.7.10.4
+
diff --git a/fblog/0008-fblog-react-on-framebuffer-events.patch b/fblog/0008-fblog-react-on-framebuffer-events.patch
new file mode 100644
index 0000000..c826640
--- /dev/null
+++ b/fblog/0008-fblog-react-on-framebuffer-events.patch
@@ -0,0 +1,144 @@
+From b74b9f619f1c43d75ed7f6637ef2e9d39799e3a1 Mon Sep 17 00:00:00 2001
+From: David Herrmann <dh.herrmann@googlemail.com>
+Date: Sat, 16 Jun 2012 23:24:00 +0200
+Subject: [PATCH 08/10] fblog: react on framebuffer events
+
+This provides an fb-notifier object that can be registered with the
+framebuffer subsystem. We are then notified about events on all
+framebuffers.
+Most of the events are only of interest for fbcon so we can safely ignore
+them. However, we need to handle REGISTERED/UNBIND to add new framebbufers
+and we need to react on mode-changes (probably triggered by user-space).
+
+Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
+---
+ drivers/video/console/fblog.c | 113 +++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 113 insertions(+)
+
+diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
+index 5297eca..79bfbcc 100644
+--- a/drivers/video/console/fblog.c
++++ b/drivers/video/console/fblog.c
+@@ -449,6 +449,119 @@ static void fblog_deactivate(void)
+ fblog_unregister_all();
+ }
+
++static int fblog_event(struct notifier_block *self, unsigned long action,
++ void *data)
++{
++ struct fb_event *event = data;
++ struct fb_info *info = event->info;
++ struct fblog_fb *fb = fblog_info2fb(info);
++ int *blank;
++
++ if (action == FB_EVENT_FB_REGISTERED) {
++ /* This is called when a low-level system driver registers a new
++ * framebuffer. The registration lock is held but the console
++ * lock might not be held when this is called (really?). */
++ fblog_register(info);
++ return 0;
++ }
++
++ if (!fb)
++ return 0;
++
++ switch(action) {
++ case FB_EVENT_FB_UNREGISTERED:
++ /* This is called when a low-level system driver unregisters a
++ * framebuffer. The registration lock is held but the console
++ * lock might not be held (really?). */
++ /* ignore; see UNBIND */
++ break;
++ case FB_EVENT_FB_UNBIND:
++ /* Called directly before unregistering an FB. The FB is still
++ * valid here and the registration lock is held but the console
++ * lock might not be held (really?). */
++ fblog_unregister(fb);
++ break;
++ case FB_EVENT_SUSPEND:
++ /* This is called when the low-level display driver suspends the
++ * video system. We should not access the video system while it
++ * is suspended. This is called with the console lock held. */
++ set_bit(FBLOG_SUSPENDED, &fb->flags);
++ break;
++ case FB_EVENT_RESUME:
++ /* This is called when the low-level display driver resumes
++ * operating. It is called with the console lock held. */
++ clear_bit(FBLOG_SUSPENDED, &fb->flags);
++ break;
++ case FB_EVENT_MODE_DELETE:
++ /* This is sent when a video mode is removed. The current video
++ * mode is never removed! The console lock is held while this is
++ * called. */
++ /* fallthrough */
++ case FB_EVENT_NEW_MODELIST:
++ /* This is sent when the modelist got changed. The console-lock
++ * is held and we should reset the mode. */
++ /* fallthrough */
++ case FB_EVENT_MODE_CHANGE_ALL:
++ /* This is the same as below but notifies us that the user used
++ * the FB_ACTIVATE_ALL flag when setting the video mode. */
++ /* fallthrough */
++ case FB_EVENT_MODE_CHANGE:
++ /* This is called when the _user_ changes the video mode via
++ * ioctls. It is not sent, when the kernel changes the mode
++ * internally. This callback is called inside fb_set_var() so
++ * the console lock is held. */
++ fblog_refresh(fb);
++ break;
++ case FB_EVENT_BLANK:
++ /* This gets called _after_ the framebuffer was successfully
++ * blanked. The console-lock is always held while fb_blank is
++ * called and during this callback. */
++ blank = (int*)event->data;
++ if (*blank == FB_BLANK_UNBLANK)
++ clear_bit(FBLOG_BLANKED, &fb->flags);
++ else
++ set_bit(FBLOG_BLANKED, &fb->flags);
++ break;
++ case FB_EVENT_GET_REQ:
++ /* When fb_set_var() is called, this callback is called to get
++ * our display requirements. They are then compared with the
++ * display properties and only if they fulfill the requirements,
++ * the new mode is activated. The console-lock should be held
++ * while calling fb_set_var() so we can assume it is locked
++ * here. */
++ /* ignore */
++ break;
++ case FB_EVENT_CONBLANK:
++ /* This is sent by fbcon when doing a fake blank. That
++ * is, blanking the screen when the fb driver failed to perform
++ * an fb_blank(). It simply writes empty lines to the screen.
++ * We are not interested in this signal. We should also never
++ * run together with fbcon so this should never be catched. */
++ /* ignore */
++ break;
++ case FB_EVENT_GET_CONSOLE_MAP:
++ /* fallthrough */
++ case FB_EVENT_SET_CONSOLE_MAP:
++ /* Is there any reason why we should support this? We
++ * ignore it as we consider ourself not to be the classic linux
++ * console. Hence, this request is not targeted at us. */
++ /* ignore */
++ break;
++ case FB_EVENT_REMAP_ALL_CONSOLE:
++ /* What are we supposed to do here? Do we have to remap
++ * the primary device to the framebuffer given by \info? Like
++ * above we currently ignore it for the same reasons. */
++ /* ignore */
++ break;
++ }
++
++ return 0;
++}
++
++static struct notifier_block fblog_notifier = {
++ .notifier_call = fblog_event,
++};
++
+ static void fblog_con_write(struct console *con, const char *buf,
+ unsigned int len)
+ {
+--
+1.7.10.4
+
diff --git a/fblog/0009-fblog-register-all-handlers-on-module-init.patch b/fblog/0009-fblog-register-all-handlers-on-module-init.patch
new file mode 100644
index 0000000..10ac641
--- /dev/null
+++ b/fblog/0009-fblog-register-all-handlers-on-module-init.patch
@@ -0,0 +1,102 @@
+From 1ca9b0e3d8f11b5fd0ce99511b44b8be2c89d3ba Mon Sep 17 00:00:00 2001
+From: David Herrmann <dh.herrmann@googlemail.com>
+Date: Sat, 16 Jun 2012 23:28:18 +0200
+Subject: [PATCH 09/10] fblog: register all handlers on module-init
+
+We now create a new "fblog" device when initializing the fblog module. We
+register the "active" sysfs-file with it so user-space can now access
+fblog. We also register the framebuffer-notifier and console-handler so
+fblog is ready to go.
+
+Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
+---
+ drivers/video/console/fblog.c | 59 +++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 59 insertions(+)
+
+diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
+index 79bfbcc..9d3b072 100644
+--- a/drivers/video/console/fblog.c
++++ b/drivers/video/console/fblog.c
+@@ -92,6 +92,7 @@ struct fblog_fb {
+ };
+
+ static struct fblog_fb *fblog_fbs[FB_MAX];
++static struct device *fblog_device;
+ static atomic_t fblog_active;
+
+ static void fblog_buf_resize(struct fblog_buf *buf, size_t width,
+@@ -609,13 +610,71 @@ static ssize_t fblog_dev_active_store(struct device *dev,
+ static DEVICE_ATTR(active, S_IRUGO | S_IWUSR | S_IWGRP, fblog_dev_active_show,
+ fblog_dev_active_store);
+
++static void fblog_dev_release(struct device *dev)
++{
++ kfree(dev);
++ module_put(THIS_MODULE);
++}
++
+ static int __init fblog_init(void)
+ {
++ int ret;
++
++ fblog_device = kzalloc(sizeof(*fblog_device), GFP_KERNEL);
++ if (!fblog_device) {
++ pr_err("fblog: cannot allocate device\n");
++ ret = -ENOMEM;
++ goto err_out;
++ }
++
++ __module_get(THIS_MODULE);
++ device_initialize(fblog_device);
++ fblog_device->class = fb_class;
++ fblog_device->release = fblog_dev_release;
++ dev_set_name(fblog_device, "fblog");
++
++ ret = device_add(fblog_device);
++ if (ret) {
++ pr_err("fblog: cannot add device\n");
++ goto err_dev;
++ }
++
++ ret = fb_register_client(&fblog_notifier);
++ if (ret) {
++ pr_err("fblog: cannot register framebuffer notifier\n");
++ goto err_dev_rm;
++ }
++
++ ret = device_create_file(fblog_device, &dev_attr_active);
++ if (ret) {
++ pr_err("fblog: cannot create sysfs entry\n");
++ goto err_fb;
++ }
++
++ register_console(&fblog_con_driver);
++
+ return 0;
++
++err_fb:
++ fb_unregister_client(&fblog_notifier);
++err_dev_rm:
++ device_del(fblog_device);
++err_dev:
++ put_device(fblog_device);
++err_out:
++ return ret;
+ }
+
+ static void __exit fblog_exit(void)
+ {
++ unregister_console(&fblog_con_driver);
++ device_remove_file(fblog_device, &dev_attr_active);
++ device_del(fblog_device);
++ fb_unregister_client(&fblog_notifier);
++ console_lock();
++ fblog_deactivate();
++ console_unlock();
++ put_device(fblog_device);
+ }
+
+ module_init(fblog_init);
+--
+1.7.10.4
+
diff --git a/fblog/0010-fblog-add-activate-module-parameter.patch b/fblog/0010-fblog-add-activate-module-parameter.patch
new file mode 100644
index 0000000..fcdc0c7
--- /dev/null
+++ b/fblog/0010-fblog-add-activate-module-parameter.patch
@@ -0,0 +1,55 @@
+From 308643219b3ec4cb8c772459af6330d06d8bda82 Mon Sep 17 00:00:00 2001
+From: David Herrmann <dh.herrmann@googlemail.com>
+Date: Sat, 16 Jun 2012 23:31:18 +0200
+Subject: [PATCH 10/10] fblog: add "activate" module parameter
+
+This new parameter controls whether fblog is automatically activated when
+it is loaded. This defaults to "true".
+
+We can now compile with CONFIG_VT=n and CONFIG_FBLOG=y and control fblog
+with fblog.activate=0/1 on the kernel command line to enable/disable
+debugging.
+
+Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
+---
+ drivers/video/console/fblog.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
+index 9d3b072..cabc550 100644
+--- a/drivers/video/console/fblog.c
++++ b/drivers/video/console/fblog.c
+@@ -94,6 +94,7 @@ struct fblog_fb {
+ static struct fblog_fb *fblog_fbs[FB_MAX];
+ static struct device *fblog_device;
+ static atomic_t fblog_active;
++static bool activate = 1;
+
+ static void fblog_buf_resize(struct fblog_buf *buf, size_t width,
+ size_t height)
+@@ -653,6 +654,12 @@ static int __init fblog_init(void)
+
+ register_console(&fblog_con_driver);
+
++ if (activate) {
++ console_lock();
++ fblog_activate();
++ console_unlock();
++ }
++
+ return 0;
+
+ err_fb:
+@@ -677,6 +684,9 @@ static void __exit fblog_exit(void)
+ put_device(fblog_device);
+ }
+
++module_param(activate, bool, S_IRUGO);
++MODULE_PARM_DESC(activate, "Activate fblog by default");
++
+ module_init(fblog_init);
+ module_exit(fblog_exit);
+ MODULE_LICENSE("GPL");
+--
+1.7.10.4
+