From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by finch.gentoo.org (Postfix) with ESMTPS id DF22B158086 for ; Thu, 7 Oct 2021 19:46:59 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id D59B3E08A8; Thu, 7 Oct 2021 19:46:54 +0000 (UTC) Received: from mail.muc.de (colin.muc.de [193.149.48.1]) by pigeon.gentoo.org (Postfix) with SMTP id 8E236E0848 for ; Thu, 7 Oct 2021 19:46:53 +0000 (UTC) Received: (qmail 55452 invoked by uid 3782); 7 Oct 2021 19:46:52 -0000 Received: from acm.muc.de (p4fe15b20.dip0.t-ipconnect.de [79.225.91.32]) (using STARTTLS) by colin.muc.de (tmda-ofmipd) with ESMTP; Thu, 07 Oct 2021 21:46:51 +0200 Received: (qmail 9945 invoked by uid 1000); 7 Oct 2021 19:46:51 -0000 Date: Thu, 7 Oct 2021 19:46:51 +0000 To: gentoo-user@lists.gentoo.org Subject: [gentoo-user] Soft scrolling on framebuffer consoles - New versions of the patches. Message-ID: Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-user@lists.gentoo.org Reply-to: gentoo-user@lists.gentoo.org X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="5sQ9P/9gZVi9UyXy" Content-Disposition: inline X-Submission-Agent: TMDA/1.3.x (Ph3nix) From: Alan Mackenzie X-Primary-Address: acm@muc.de X-Archives-Salt: 9079e2ad-5928-4d81-a43f-d09508eba3e3 X-Archives-Hash: bf9960cfb97936cd657004be6768e295 --5sQ9P/9gZVi9UyXy Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Hello, Gentoo. Here are the latest versions of my soft scrolling patch for the kernel lines 5.10.xx and 5.14.xx. They fix bugs where a PC would crash if it was initialised with a 25x80 console and later changed to a full resolution frame buffer screen. It also now works (so I am told) for kernels initialised with parameters like vga=791. To use one of these (the right one for your kernel version) do: (i) cd /usr/src/linux-5.10.61-gentoo, or similar. Extract the right patch file to that directory. (Don't worry about having > 61 for a kernel version. Just use it!) (ii) patch -p1 < 5.10.61-scroll.20211007.diff. (iii) Configure the kernel in your usual way. The extra items added by the patch are CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK and CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE, to be found in make menuconfig under Device Drivers/Graphic Support/Console display driver support. The help items for these should explain them adequately. (iv) Build the kernel. (v) Put the new kernel into your usual boot manager. (vi) Reboot and enjoy! As far as I know this patch should be safe (apart from the mysterious alleged security problem which caused the soft scrolling to be removed from the kernel in the first place). There is certainly nothing malicous in it. But if it does break anything for you, you get to keep the pieces. But if anything does go wrong, please tell me about it (preferably here on the list, but by private email if you'd prefer), so that I can try and fix it. -- Alan Mackenzie (Nuremberg, Germany). --5sQ9P/9gZVi9UyXy Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="5.10.61-scroll.20211007.diff" diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 06757b1d4aec..c2061813c825 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) { set_leds(); compute_shiftstate(); @@ -1110,15 +1402,68 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ { struct vt_notifier_param param; struct vc_data *vc; + unsigned long new_end; + unsigned long new_in, new_top; + unsigned long in_residue; + unsigned short *d; 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); + } + } + if (vc->vc_softback_buf + && (vc->vc_softback_end - vc->vc_softback_buf) % vc->vc_size_row) { + new_end = vc->vc_softback_buf + vc->vc_softback_size + - (vc->vc_softback_size % vc->vc_size_row); + new_top = vc->vc_softback_top + - (vc->vc_softback_top - vc->vc_softback_buf) + % vc->vc_size_row; + in_residue = (vc->vc_softback_in - vc->vc_softback_buf) + % vc->vc_size_row; + new_in = vc->vc_softback_in + + (in_residue ? vc->vc_size_row - in_residue : 0); + if (new_in == new_end) + new_in = vc->vc_softback_buf; + if (new_top == new_in) + new_top += vc->vc_size_row; + else if ((new_in > new_top + && (new_in - new_top <= vc->vc_size_row))) + new_top += 2 * vc->vc_size_row; + if (new_top >= new_end) + new_top -= (new_end - vc->vc_softback_buf); + vc->vc_softback_lines = 0; + vc->vc_softback_curr = new_in; + + d = (u16 *)vc->vc_softback_in; + vc->vc_softback_end = new_end; + vc->vc_softback_in = new_in; + vc->vc_softback_top = new_top; + while (d != (u16 *)new_in) { + scr_writew (0x0020, d); /* How about non-VGA? */ + if (++d == (u16 *)new_end) + d = (u16 *)vc->vc_softback_buf; + } + } +#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 @@ -1158,6 +1503,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); @@ -1630,7 +1987,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; @@ -1642,7 +1999,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; } @@ -2889,6 +3246,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 - and do + not pass through this function. */ + concon_set_origin (vc); +#endif + while (!tty->stopped && count) { int orig = *buf; buf++; @@ -3104,7 +3467,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: @@ -3332,7 +3699,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(); } @@ -3353,6 +3724,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; @@ -3408,7 +3783,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); @@ -4109,7 +4484,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; @@ -4410,7 +4785,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 ee33b8ec62bb..b8aab2c58178 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -78,6 +78,29 @@ 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 + help + This option creates a scrollback buffer for each framebuffer console. + These buffers are allocated dynamically during initialisation. + + If you want this feature, say 'Y' here and enter in + FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE the amount of RAM to + allocate for each 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 in kilobytes to allocate for the + scrollback buffer of each framebuffer console. Each character + position on the video takes 2 bytes of storage. 128kB will give you + approximately four 240x67 screenfuls of scrollback buffer. + 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 42c72d051158..05a8eb440c51 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -3087,6 +3087,9 @@ static const struct consw fb_con = { .con_font_default = fbcon_set_def_font, .con_font_copy = fbcon_copy_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 349e39c3ab60..d35ec391c33c 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -127,6 +127,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, --5sQ9P/9gZVi9UyXy Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="5.14.5-scroll.20211007.diff" diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index ef981d3b7bb4..b772f1a2810f 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); @@ -1109,15 +1401,68 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ { struct vt_notifier_param param; struct vc_data *vc; + unsigned long new_end; + unsigned long new_in, new_top; + unsigned long in_residue; + unsigned short *d; 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); + } + } + if (vc->vc_softback_buf + && (vc->vc_softback_end - vc->vc_softback_buf) % vc->vc_size_row) { + new_end = vc->vc_softback_buf + vc->vc_softback_size + - (vc->vc_softback_size % vc->vc_size_row); + new_top = vc->vc_softback_top + - (vc->vc_softback_top - vc->vc_softback_buf) + % vc->vc_size_row; + in_residue = (vc->vc_softback_in - vc->vc_softback_buf) + % vc->vc_size_row; + new_in = vc->vc_softback_in + + (in_residue ? vc->vc_size_row - in_residue : 0); + if (new_in == new_end) + new_in = vc->vc_softback_buf; + if (new_top == new_in) + new_top += vc->vc_size_row; + else if ((new_in > new_top + && (new_in - new_top <= vc->vc_size_row))) + new_top += 2 * vc->vc_size_row; + if (new_top >= new_end) + new_top -= (new_end - vc->vc_softback_buf); + vc->vc_softback_lines = 0; + vc->vc_softback_curr = new_in; + + d = (u16 *)vc->vc_softback_in; + vc->vc_softback_end = new_end; + vc->vc_softback_in = new_in; + vc->vc_softback_top = new_top; + while (d != (u16 *)new_in) { + scr_writew (0x0020, d); /* How about non-VGA? */ + if (++d == (u16 *)new_end) + d = (u16 *)vc->vc_softback_buf; + } + } +#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 +1502,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 +1986,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 +1998,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 +3245,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 - and do + not pass through this function. */ + concon_set_origin (vc); +#endif + while (!tty->flow.stopped && count) { int orig = *buf; buf++; @@ -3103,7 +3466,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 +3691,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 +3716,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 +3775,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 +4475,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 +4776,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..7e1b4925a91a 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -78,6 +78,29 @@ 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 + help + This option creates a scrollback buffer for each framebuffer console. + These buffers are allocated dynamically during initialisation. + + If you want this feature, say 'Y' here and enter in + FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE the amount of RAM to + allocate for each 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 in kilobytes to allocate for the + scrollback buffer of each framebuffer console. Each character + position on the video takes 2 bytes of storage. 128kB will give you + approximately four 240x67 screenfuls of scrollback buffer. + 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, --5sQ9P/9gZVi9UyXy--