Hello, Jorge.
On Fri, Sep 24, 2021 at 09:22:45 +0100, Jorge Almeida wrote:
> On Thu, Sep 23, 2021 at 6:03 PM Alan Mackenzie <[email protected]> wrote:
[ .... ]
> It still fails:
> $ patch -p0 <../patch_for_5.14.diff
> patching file ./drivers/tty/vt/vt.c
> Hunk #1 FAILED at 3208.
> 1 out of 1 hunk FAILED -- saving rejects to file ./drivers/tty/vt/vt.c.rej
Apologies once more. Late last night I managed to get a Linux kernel
git repository set up. :-) So, at least if there are any more
failures, they'll be systematic failures rather than erratic failures.
;-)
>From this I've constructed a complete clean 5.14.5 patch, which I've
attached. Please start again from a gentoo-sources without any previous
traces of the scrollback patches, and apply that patch. _Surely_ it
should work this time.
To apply the patch (you surely know this already), cd to the top of the
kernel tree, and use
$ patch -p1 < 5.14.5-scroll-20210924.diff
.. Alternatively, if you've got git, you could use
$ git apply 5.14.5-scroll-20210924.diff
.. Please let me know again how it works out. Thanks!
[ .... ]
> Thanks,
> Jorge Almeida
--
Alan Mackenzie (Nuremberg, Germany).
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index ef981d3b7bb4..17b51bdc9f6e 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -134,6 +134,11 @@ const struct consw *conswitchp;
#define DEFAULT_BELL_DURATION (HZ/8)
#define DEFAULT_CURSOR_BLINK_MS 200
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+static unsigned int console_soft_scrollback_size =
+ 1024 * CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE;
+#endif
+
struct vc vc_cons [MAX_NR_CONSOLES];
#ifndef VT_SINGLE_DRIVER
@@ -287,7 +292,7 @@ static inline unsigned short *screenpos(const struct
vc_data *vc, int offset,
bool viewed)
{
unsigned short *p;
-
+
if (!viewed)
p = (unsigned short *)(vc->vc_origin + offset);
else if (!vc->vc_sw->con_screen_pos)
@@ -616,6 +621,218 @@ static void vc_uniscr_debug_check(struct vc_data *vc)
}
}
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+static void con_update_softback(struct vc_data *vc)
+{
+ int l = vc->vc_softback_size / vc->vc_size_row;
+ if (l > 5)
+ {
+ vc->vc_softback_end = vc->vc_softback_buf + l * vc->vc_size_row;
+ vc->vc_softback_top = vc->vc_softback_buf;
+ }
+ else
+ /* Smaller scrollback makes no sense, and 0 would screw
+ the operation totally */
+ vc->vc_softback_top = 0;
+}
+
+static int concon_set_origin(struct vc_data *vc)
+{
+ if (vc->vc_softback_lines)
+ concon_scrolldelta(vc, vc->vc_softback_lines);
+ return 0;
+}
+
+#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta)
* vc->vc_size_row)
+
+static void con_redraw_softback(struct vc_data *vc, /* struct display *p, */
+ long delta)
+{
+ int count = vc->vc_rows;
+ unsigned short *d, *s;
+ unsigned long n;
+ int line = 0;
+
+ if (!vc->vc_softback_lines)
+ vc->vc_char_at_pos = scr_readw((u16 *)vc->vc_pos);
+
+ d = (u16 *) vc->vc_softback_curr;
+ if (d == (u16 *) vc->vc_softback_in)
+ d = (u16 *) vc->vc_origin;
+ n = vc->vc_softback_curr + delta * vc->vc_size_row;
+ vc->vc_softback_lines -= delta;
+ if (delta < 0) {
+ if (vc->vc_softback_curr < vc->vc_softback_top
+ && n < vc->vc_softback_buf) {
+ n += vc->vc_softback_end - vc->vc_softback_buf;
+ if (n < vc->vc_softback_top) {
+ vc->vc_softback_lines -=
+ (vc->vc_softback_top - n) / vc->vc_size_row;
+ n = vc->vc_softback_top;
+ }
+ } else if (vc->vc_softback_curr >= vc->vc_softback_top
+ && n < vc->vc_softback_top) {
+ vc->vc_softback_lines -=
+ (vc->vc_softback_top - n) / vc->vc_size_row;
+ n = vc->vc_softback_top;
+ }
+ } else {
+ if (vc->vc_softback_curr > vc->vc_softback_in
+ && n >= vc->vc_softback_end) {
+ n += vc->vc_softback_buf - vc->vc_softback_end;
+ if (n > vc->vc_softback_in) {
+ n = vc->vc_softback_in;
+ vc->vc_softback_lines = 0;
+ }
+ } else if (vc->vc_softback_curr <= vc->vc_softback_in
+ && n > vc->vc_softback_in) {
+ n = vc->vc_softback_in;
+ vc->vc_softback_lines = 0;
+ }
+ }
+ if (n == vc->vc_softback_curr)
+ return;
+ vc->vc_softback_curr = n;
+ /* If we're not scrolled any more, restore the character to the cursor
+ * position */
+ if (!vc->vc_softback_lines)
+ scr_writew(vc->vc_char_at_pos, (u16 *)vc->vc_pos);
+ s = (u16 *) vc->vc_softback_curr;
+ if (s == (u16 *) vc->vc_softback_in)
+ s = (u16 *) vc->vc_origin;
+ while (count--) {
+ unsigned short *start;
+ unsigned short *le;
+ unsigned short c;
+ int x = 0;
+ unsigned short attr = 1;
+
+ start = s;
+ le = advance_row(s, 1);
+ /* Temporarily overwrite the character at the cursor position
+ * with the one we actually want to see on the screen. */
+ if (count == vc->vc_rows - vc->state.y - 1)
+ {
+ c = scr_readw((u16 *)(s + vc->state.x));
+ scr_writew(c, (u16 *)vc->vc_pos);
+ vc->vc_sw->con_putcs
+ (vc, (u16 *)vc->vc_pos, 1, line, vc->state.x);
+ }
+ do {
+ c = scr_readw(s);
+ if (attr != (c & 0xff00)) {
+ attr = c & 0xff00;
+ if (s > start) {
+ vc->vc_sw->con_putcs(
+ vc, start, s - start,
+ line, x);
+ x += s - start;
+ start = s;
+ }
+ }
+ if (c == scr_readw(d)) {
+ if (s > start) {
+ vc->vc_sw->con_putcs(
+ vc, start, s - start,
+ line, x);
+ x += s - start + 1;
+ start = s + 1;
+ } else {
+ x++;
+ start++;
+ }
+ }
+ s++;
+ d++;
+ } while (s < le);
+ if (s > start)
+ vc->vc_sw->con_putcs(vc, start, s - start, line, x);
+ line++;
+ if (d == (u16 *) vc->vc_softback_end)
+ d = (u16 *) vc->vc_softback_buf;
+ if (d == (u16 *) vc->vc_softback_in)
+ d = (u16 *) vc->vc_origin;
+ if (s == (u16 *) vc->vc_softback_end)
+ s = (u16 *) vc->vc_softback_buf;
+ if (s == (u16 *) vc->vc_softback_in)
+ s = (u16 *) vc->vc_origin;
+ }
+}
+
+static inline void con_softback_note(struct vc_data *vc, int t,
+ int count)
+{
+ unsigned short *p;
+
+ if (vc->vc_num != fg_console)
+ return;
+ p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row);
+
+ while (count) {
+ scr_memcpyw((u16 *) vc->vc_softback_in, p, vc->vc_size_row);
+ count--;
+ p = advance_row(p, 1);
+ vc->vc_softback_in += vc->vc_size_row;
+ if (vc->vc_softback_in == vc->vc_softback_end)
+ vc->vc_softback_in = vc->vc_softback_buf;
+ if (vc->vc_softback_in == vc->vc_softback_top) {
+ vc->vc_softback_top += vc->vc_size_row;
+ if (vc->vc_softback_top == vc->vc_softback_end)
+ vc->vc_softback_top = vc->vc_softback_buf;
+ }
+ }
+ vc->vc_softback_curr = vc->vc_softback_in;
+}
+
+void concon_scrolldelta(struct vc_data *vc, int lines)
+{
+ /* struct display *disp = &fb_display[fg_console]; */
+ /* int offset, limit, scrollback_old; */
+
+ if (vc->vc_softback_top) {
+ if (vc->vc_num != fg_console)
+ return;
+ if (vc->vc_mode != KD_TEXT || !lines)
+ return;
+#if 0
+ if (logo_shown >= 0) {
+ struct vc_data *conp2 = vc_cons[logo_shown].d;
+
+ if (conp2->vc_top == logo_lines
+ && conp2->vc_bottom == conp2->vc_rows)
+ conp2->vc_top = 0;
+ if (logo_shown == vc->vc_num) {
+ unsigned long p, q;
+ int i;
+
+ p = vc->vc_softback_in;
+ q = vc->vc_origin +
+ logo_lines * vc->vc_size_row;
+ for (i = 0; i < logo_lines; i++) {
+ if (p == vc->vc_softback_top)
+ break;
+ if (p == vc->vc_softback_buf)
+ p = vc->vc_softback_end;
+ p -= vc->vc_size_row;
+ q -= vc->vc_size_row;
+ scr_memcpyw((u16 *) q, (u16 *) p,
+ vc->vc_size_row);
+ }
+ vc->vc_softback_in = vc->vc_softback_curr = p;
+ update_region(vc, vc->vc_origin,
+ logo_lines * vc->vc_cols);
+ }
+ logo_shown = FBCON_LOGO_CANSHOW;
+ }
+#endif
+ vc->vc_sw->con_cursor(vc, CM_ERASE /* | CM_SOFTBACK */);
+ con_redraw_softback(vc, /* disp, */ lines);
+ if (!vc->vc_softback_lines)
+ vc->vc_sw->con_cursor(vc, CM_DRAW /* | CM_SOFTBACK */);
+ }
+}
+
+#endif /* CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK */
static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
enum con_scroll dir, unsigned int nr)
@@ -626,6 +843,10 @@ static void con_scroll(struct vc_data *vc, unsigned int t,
unsigned int b,
nr = b - t - 1;
if (b > vc->vc_rows || t >= b || nr < 1)
return;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ if (dir == SM_UP && vc->vc_softback_top)
+ con_softback_note (vc, t, nr);
+#endif
vc_uniscr_scroll(vc, t, b, dir, nr);
if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, dir, nr))
return;
@@ -641,6 +862,56 @@ static void con_scroll(struct vc_data *vc, unsigned int t,
unsigned int b,
scr_memsetw(clear, vc->vc_video_erase_char, vc->vc_size_row * nr);
}
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+static void do_update_region(struct vc_data *vc, unsigned long start, int
count)
+{
+ unsigned int xx, yy, offset;
+ u16 *p;
+
+ unsigned long origin =
+ (start >= vc->vc_softback_buf && start < vc->vc_softback_end)
+ ? start >= vc->vc_softback_curr
+ ? vc->vc_softback_curr
+ : vc->vc_softback_curr
+ - (vc->vc_softback_end - vc->vc_softback_buf)
+ : vc->vc_origin;
+ p = (u16 *) start;
+ offset = (start - origin) / 2;
+ xx = offset % vc->vc_cols;
+ yy = offset / vc->vc_cols;
+ for(;;) {
+ u16 attrib = scr_readw(p) & 0xff00;
+ int startx = xx;
+ u16 *q = p;
+ while (xx < vc->vc_cols && count) {
+ if (attrib != (scr_readw(p) & 0xff00)) {
+ if (p > q)
+ vc->vc_sw->con_putcs(vc, q, p-q, yy,
startx);
+ startx = xx;
+ q = p;
+ attrib = scr_readw(p) & 0xff00;
+ }
+ p++;
+ xx++;
+ count--;
+ }
+ if (p > q)
+ vc->vc_sw->con_putcs(vc, q, p-q, yy, startx);
+ if (p == (u16 *) vc->vc_softback_end)
+ p = (u16 *)vc->vc_softback_buf;
+ if (p == (u16 *) vc->vc_softback_in)
+ p = (u16 *)vc->vc_origin;
+ if (!count)
+ break;
+ xx = 0;
+ yy++;
+ /* if (vc->vc_sw->con_getxy) { */
+ /* p = (u16 *)start; */
+ /* start = vc->vc_sw->con_getxy(vc, start, NULL, NULL); */
+ /* } */
+ }
+}
+#else
static void do_update_region(struct vc_data *vc, unsigned long start, int
count)
{
unsigned int xx, yy, offset;
@@ -684,6 +955,7 @@ static void do_update_region(struct vc_data *vc, unsigned
long start, int count)
}
}
}
+#endif
void update_region(struct vc_data *vc, unsigned long start, int count)
{
@@ -692,7 +964,10 @@ void update_region(struct vc_data *vc, unsigned long
start, int count)
if (con_should_update(vc)) {
hide_cursor(vc);
do_update_region(vc, start, count);
- set_cursor(vc);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ if (!vc->vc_softback_lines)
+#endif
+ set_cursor(vc);
}
}
@@ -927,8 +1202,17 @@ static void set_origin(struct vc_data *vc)
WARN_CONSOLE_UNLOCKED();
if (!con_is_visible(vc) ||
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ (
+ !concon_set_origin (vc) &&
+ (
+#endif
!vc->vc_sw->con_set_origin ||
- !vc->vc_sw->con_set_origin(vc))
+ !vc->vc_sw->con_set_origin(vc)
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ ))
+#endif
+ )
vc->vc_origin = (unsigned long)vc->vc_screenbuf;
vc->vc_visible_origin = vc->vc_origin;
vc->vc_scr_end = vc->vc_origin + vc->vc_screenbuf_size;
@@ -1004,7 +1288,6 @@ void redraw_screen(struct vc_data *vc, int is_switch)
hide_cursor(old_vc);
if (!con_is_visible(old_vc)) {
save_screen(old_vc);
- set_origin(old_vc);
}
if (tty0dev)
sysfs_notify(&tty0dev->kobj, NULL, "active");
@@ -1017,7 +1300,6 @@ void redraw_screen(struct vc_data *vc, int is_switch)
int update;
int old_was_color = vc->vc_can_do_color;
- set_origin(vc);
update = vc->vc_sw->con_switch(vc);
set_palette(vc);
/*
@@ -1032,9 +1314,19 @@ void redraw_screen(struct vc_data *vc, int is_switch)
}
if (update && vc->vc_mode != KD_GRAPHICS)
- do_update_region(vc, vc->vc_origin,
vc->vc_screenbuf_size / 2);
+ do_update_region(vc,
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ vc->vc_softback_lines
+ ? vc->vc_softback_curr
+ :
+#endif
+ vc->vc_origin,
+ vc->vc_screenbuf_size / 2);
}
- set_cursor(vc);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ if (!vc->vc_softback_lines)
+#endif
+ set_cursor(vc);
if (is_switch) {
vt_set_leds_compute_shiftstate();
notify_update(vc);
@@ -1112,12 +1404,28 @@ int vc_allocate(unsigned int currcons) /* return 0 on
success */
int err;
WARN_CONSOLE_UNLOCKED();
-
if (currcons >= MAX_NR_CONSOLES)
return -ENXIO;
if (vc_cons[currcons].d)
+ {
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ vc = vc_cons[currcons].d;
+ if (!vc->vc_softback_size) {
+ /* vc was partially initialized by __init. */
+ vc->vc_softback_size = console_soft_scrollback_size;
+ vc->vc_softback_buf =
+ (unsigned long)kzalloc(vc->vc_softback_size,
GFP_KERNEL);
+ if (vc->vc_softback_buf) {
+ vc->vc_softback_in = vc->vc_softback_top =
+ vc->vc_softback_curr =
vc->vc_softback_buf;
+ vc->vc_softback_lines = 0;
+ con_update_softback(vc);
+ }
+ }
+#endif
return 0;
+ }
/* due to the granularity of kmalloc, we waste some memory here */
/* the alloc is done in two steps, to optimize the common situation
@@ -1157,6 +1465,18 @@ int vc_allocate(unsigned int currcons) /* return 0 on
success */
vcs_make_sysfs(currcons);
atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, ¶m);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ vc->vc_softback_size = console_soft_scrollback_size;
+ err = -ENOMEM;
+ vc->vc_softback_buf =
+ (unsigned long)kzalloc(vc->vc_softback_size, GFP_KERNEL);
+ if (!vc->vc_softback_buf)
+ goto err_free;
+ vc->vc_softback_in = vc->vc_softback_top = vc->vc_softback_curr =
+ vc->vc_softback_buf;
+ vc->vc_softback_lines = 0;
+ con_update_softback(vc);
+#endif
return 0;
err_free:
visual_deinit(vc);
@@ -1629,7 +1949,7 @@ struct rgb { u8 r; u8 g; u8 b; };
static void rgb_from_256(int i, struct rgb *c)
{
- if (i < 8) { /* Standard colours. */
+ if (i < 8) { /* Standard colours. */
c->r = i&1 ? 0xaa : 0x00;
c->g = i&2 ? 0xaa : 0x00;
c->b = i&4 ? 0xaa : 0x00;
@@ -1641,7 +1961,7 @@ static void rgb_from_256(int i, struct rgb *c)
c->r = (i - 16) / 36 * 85 / 2;
c->g = (i - 16) / 6 % 6 * 85 / 2;
c->b = (i - 16) % 6 * 85 / 2;
- } else /* Grayscale ramp. */
+ } else /* Grayscale ramp. */
c->r = c->g = c->b = i * 10 - 2312;
}
@@ -2888,6 +3208,12 @@ static int do_con_write(struct tty_struct *tty, const
unsigned char *buf, int co
param.vc = vc;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ /* Undo any soft scrolling - <Alt><Fn> and <Shift><PgUp/Down> do
+ not pass through this function. */
+ concon_set_origin (vc);
+#endif
+
while (!tty->flow.stopped && count) {
int orig = *buf;
buf++;
@@ -3103,7 +3429,11 @@ static void vt_console_print(struct console *co, const
char *b, unsigned count)
}
if (cnt && con_is_visible(vc))
vc->vc_sw->con_putcs(vc, start, cnt, vc->state.y, start_x);
- set_cursor(vc);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ if (!vc->vc_softback_lines)
+#endif
+ set_cursor(vc);
+
notify_update(vc);
quit:
@@ -3324,7 +3654,11 @@ static void con_flush_chars(struct tty_struct *tty)
/* if we race with con_close(), vt may be null */
console_lock();
vc = tty->driver_data;
- if (vc)
+ if (vc
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ && !vc->vc_softback_lines
+#endif
+ )
set_cursor(vc);
console_unlock();
}
@@ -3345,6 +3679,10 @@ static int con_install(struct tty_driver *driver, struct
tty_struct *tty)
vc = vc_cons[currcons].d;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ con_update_softback(vc);
+#endif
+
/* Still being freed */
if (vc->port.tty) {
ret = -ERESTARTSYS;
@@ -3400,7 +3738,7 @@ static void con_cleanup(struct tty_struct *tty)
tty_port_put(&vc->port);
}
-static int default_color = 7; /* white */
+static int default_color = 7; /* white */
static int default_italic_color = 2; // green (ASCII)
static int default_underline_color = 3; // cyan (ASCII)
module_param_named(color, default_color, int, S_IRUGO | S_IWUSR);
@@ -4100,7 +4438,7 @@ static int do_register_con_driver(const struct consw
*csw, int first, int last)
con_driver->desc = desc;
con_driver->node = i;
con_driver->flag = CON_DRIVER_FLAG_MODULE |
- CON_DRIVER_FLAG_INIT;
+ CON_DRIVER_FLAG_INIT;
con_driver->first = first;
con_driver->last = last;
retval = 0;
@@ -4401,7 +4739,10 @@ void do_unblank_screen(int leaving_gfx)
if (console_blank_hook)
console_blank_hook(0);
set_palette(vc);
- set_cursor(vc);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ if (!vc->vc_softback_lines)
+#endif
+ set_cursor(vc);
vt_event_post(VT_EVENT_UNBLANK, vc->vc_num, vc->vc_num);
}
EXPORT_SYMBOL(do_unblank_screen);
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index 840d9813b0bc..d126d6a4f2b2 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -78,6 +78,55 @@ config FRAMEBUFFER_CONSOLE
help
Low-level framebuffer-based console driver.
+config FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ bool "Enable Scrollback Buffer in System RAM"
+ depends on FB=y && FRAMEBUFFER_CONSOLE
+ default y
+ select FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_PERSISTENT_ENABLE_BY_DEFAULT
+ help
+ This option creates scrollback buffers for each framebuffer console,
+ or one buffer for them all. These buffers are allocated dynamically
+ during initialisation.
+
+ If you want this feature, say 'Y' here and enter the amount of
+ RAM to allocate for this buffer. If unsure, say 'N'.
+
+config FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE
+ int "Scrollback Buffer Size (in KB)"
+ depends on FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ range 1 1024
+ default "128"
+ help
+ Enter the amount of System RAM to allocate for each scrollback
+ buffer of framebuffer consoles in kilobytes. Each character
+ position on the video takes 2 bytes of storage. 128k will give you
+ approximately 4 240x67 screenfuls of scrollback buffer.
+
+config FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_PERSISTENT_ENABLE_BY_DEFAULT
+ bool "Persistent Scrollback History for each framebuffer console by
default"
+ depends on FB=y && FRAMEBUFFER_CONSOLE &&
FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ default y
+ help
+
+ Note: this option's value N has not (?yet) been implemented (2021-04).
+
+ Say Y here if the scrollback history should persist by default when
+ switching between consoles. Otherwise, the scrollback history will
+ be flushed the first time a scroll-up operation occurs on the new
+ console after the console is switched. STOUGH!!! FIXME!!! This
+ feature can also be enabled using the boot command line parameter
+ 'vgacon.scrollback_persistent=1'.
+
+ This feature might break your tool of choice to flush the scrollback
+ buffer, e.g. clear(1) will work fine but Debian's clear_console(1)
+ will be broken, which might cause security issues.
+ You can use the escape sequence \e[3J instead if this feature is
+ activated.
+
+ Note that a buffer of VGACON_SOFT_SCROLLBACK_SIZE is taken for each
+ created tty device.
+ So if you use a RAM-constrained system, say N here.
+
config FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
bool "Map the console to the primary display device"
depends on FRAMEBUFFER_CONSOLE
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index 22bb3892f6bd..c89cc8c605fc 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -3053,6 +3053,9 @@ static const struct consw fb_con = {
.con_font_get = fbcon_get_font,
.con_font_default = fbcon_set_def_font,
.con_set_palette = fbcon_set_palette,
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ .con_scrolldelta = concon_scrolldelta,
+#endif
.con_invert_region = fbcon_invert_region,
.con_screen_pos = fbcon_screen_pos,
.con_getxy = fbcon_getxy,
diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h
index d5b9c8d40c18..acc277e73e32 100644
--- a/include/linux/console_struct.h
+++ b/include/linux/console_struct.h
@@ -110,6 +110,17 @@ struct vc_data {
unsigned short *vc_screenbuf; /* In-memory
character/attribute buffer */
unsigned int vc_screenbuf_size;
unsigned char vc_mode; /* KD_TEXT, ... */
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ unsigned int vc_softback_size; /* Size in bytes of scrollback
buffer. */
+ unsigned long vc_softback_buf; /* Address of scrollback
buffer. */
+ unsigned long vc_softback_end; /* (Just past) end of buffer. */
+ unsigned long vc_softback_in; /* Head pointer into circular
buffer. */
+ unsigned long vc_softback_top; /* Tail pointer into circular
buffer. */
+ unsigned long vc_softback_curr; /* Pos in vc_screenbuf or
vc_softback_buf
+ corresponding to visible
screen. */
+ int vc_softback_lines; /* Number of lines currently
scrolled. */
+ unsigned short vc_char_at_pos; /* Char at vc_pos when no soft
scroll */
+#endif
/* attributes for all characters on screen */
unsigned char vc_attr; /* Current attributes */
unsigned char vc_def_color; /* Default colors */
diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h
index 0da94a6dee15..63b83ffbef95 100644
--- a/include/linux/vt_kern.h
+++ b/include/linux/vt_kern.h
@@ -115,6 +115,9 @@ int con_copy_unimap(struct vc_data *dst_vc, struct vc_data
*src_vc)
/* vt.c */
void vt_event_post(unsigned int event, unsigned int old, unsigned int new);
int vt_waitactive(int n);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+void concon_scrolldelta(struct vc_data *vc, int lines);
+#endif
void change_console(struct vc_data *new_vc);
void reset_vc(struct vc_data *vc);
int do_unbind_con_driver(const struct consw *csw, int first, int last,