Hello, Peter.
I have another version of the patch which makes GPM usable on scrolled
consoles. I'd like to think that it's now final.
On Sat, Jan 28, 2023 at 14:41:53 +0000, Peter Humphrey wrote:
> On Friday, 27 January 2023 22:31:17 GMT Alan Mackenzie wrote:
> > On Fri, Jan 27, 2023 at 12:24:41 +0000, Peter Humphrey wrote:
> --->8
> > > I've attached the reject files.
> >
> > Thanks for these! It looks like it'll probably be straightforward to
> > amend the patch for 6.1.8. Are you currently running 6.1.8 as your main
> > kernel?
> Yes Alan, I'm running ~amd64, so 6.1.8 is the current kernel. I have 5.15.88
> as a backup.
OK, I'm enclosing a patch for each version, 5.15.x and 6.1.y. I'm not
aware of any outstanding bugs in these patches. In particular, the
following have been fixed since the previous version:
(i) The synchronisation between the "mouse" character buffer and the
contents of the screen is now 100%. In particular, when changing
fonts (e.g. at bootup), and when the Tux logos get removed from the
screen, things continue working.
(ii) CONFIG_LOGO (i.e. the Tuxes on bootup) can now be freely configured
without losing functionality.
Just a quick reminder of how to use these files for anybody else who
might be interested:
(i) cd /usr/src/linux-5.15.88-gentoo, or similar. (Or
...-6.1.x-gentoo).
(ii) patch -p1 < 5.15.80-GPM.20230203.diff (or the other one).
(iii) Configure the kernel as normal. Additionally, under Device
drivers/Graphic Support/Console display driver support, enable
CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK, set the buffer size to
taste (it's default is 128 kB) and accept the default enablement of
CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM.
(iv) Build the kernel and install it into your boot manager.
(v) Reboot and enjoy! You can now use GPM in scrolled consoles.
> --
> Regards,
> Peter.
--
Alan Mackenzie (Nuremberg, Germany).
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index b8f5bc19416d..5d5508209164 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
@@ -286,8 +291,31 @@ static inline bool con_should_update(const struct vc_data
*vc)
static inline unsigned short *screenpos(const struct vc_data *vc, int offset,
bool viewed)
{
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ unsigned long softback_pos, scrolled_expanse;
+
+ if (vc->vc_softback_curr == vc->vc_origin) /* Should never happen! */
+ return (unsigned short *)(vc->vc_origin + offset);
+ else {
+ if (vc->vc_softback_in >= vc->vc_softback_curr)
+ scrolled_expanse = vc->vc_softback_in -
vc->vc_softback_curr;
+ else
+ scrolled_expanse = vc->vc_softback_in -
vc->vc_softback_curr
+ + vc->vc_softback_end - vc->vc_softback_buf;
+ if (offset >= scrolled_expanse)
+ return (unsigned short *)(vc->vc_origin
+ + (offset -
scrolled_expanse));
+ else {
+ softback_pos = vc->vc_softback_curr + offset;
+ if (softback_pos >= vc->vc_softback_end)
+ softback_pos -= vc->vc_softback_end
+ - vc->vc_softback_buf;
+ }
+ }
+ return (unsigned short *)softback_pos;
+#else
unsigned short *p;
-
+
if (!viewed)
p = (unsigned short *)(vc->vc_origin + offset);
else if (!vc->vc_sw->con_screen_pos)
@@ -295,6 +323,7 @@ static inline unsigned short *screenpos(const struct
vc_data *vc, int offset,
else
p = vc->vc_sw->con_screen_pos(vc, offset);
return p;
+#endif
}
/* Called from the keyboard irq path.. */
@@ -316,101 +345,96 @@ void schedule_console_callback(void)
* Code to manage unicode-based screen buffers
*/
-#ifdef NO_VC_UNI_SCREEN
-/* this disables and optimizes related code away at compile time */
-#define get_vc_uniscr(vc) NULL
-#else
-#define get_vc_uniscr(vc) vc->vc_uni_screen
-#endif
-
#define VC_UNI_SCREEN_DEBUG 0
typedef uint32_t char32_t;
-/*
- * Our screen buffer is preceded by an array of line pointers so that
- * scrolling only implies some pointer shuffling.
- */
-struct uni_screen {
- char32_t *lines[0];
-};
+#define vc_uniscr_buf_end(vc) (vc->vc_uniscr_buf + vc->vc_uniscr_char_size)
-static struct uni_screen *vc_uniscr_alloc(unsigned int cols, unsigned int rows)
+static int vc_uniscr_alloc(struct vc_data *vc, unsigned int cols, unsigned int
rows)
{
- struct uni_screen *uniscr;
- void *p;
- unsigned int memsize, i;
+ uint32_t *p;
+ unsigned int new_size; /* In 32-bit characters */
- /* allocate everything in one go */
- memsize = cols * rows * sizeof(char32_t);
- memsize += rows * sizeof(char32_t *);
- p = vzalloc(memsize);
- if (!p)
- return NULL;
-
- /* initial line pointers */
- uniscr = p;
- p = uniscr->lines + rows;
- for (i = 0; i < rows; i++) {
- uniscr->lines[i] = p;
- p += cols * sizeof(char32_t);
- }
- return uniscr;
-}
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ unsigned int num_scrollback_rows;
-static void vc_uniscr_free(struct uni_screen *uniscr)
-{
- vfree(uniscr);
+ num_scrollback_rows = (console_soft_scrollback_size / 2) / cols;
+ new_size = cols * (num_scrollback_rows + rows + 1);
+#else
+ new_size = cols * (rows + 1); /* 1 row for the circular buffer admin */
+#endif
+ p = (uint32_t *)vzalloc (sizeof (uint32_t) * new_size);
+ if (!p)
+ return -ENOMEM;
+ vc->vc_uniscr_buf = p;
+ vc->vc_uniscr_curr = p;
+ vc->vc_uniscr_char_size = new_size;
+ memset32(p, ' ', new_size); /* Probably redundant. */
+ return 0;
}
-static void vc_uniscr_set(struct vc_data *vc, struct uni_screen *new_uniscr)
+static void vc_uniscr_free(struct vc_data *vc)
{
- vc_uniscr_free(vc->vc_uni_screen);
- vc->vc_uni_screen = new_uniscr;
+ kvfree(vc->vc_uniscr_buf);
+ vc->vc_uniscr_buf = NULL;
}
static void vc_uniscr_putc(struct vc_data *vc, char32_t uc)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
+ uint32_t *pos;
- if (uniscr)
- uniscr->lines[vc->state.y][vc->state.x] = uc;
+ if (vc->vc_uniscr_buf) {
+ pos = vc->vc_uniscr_curr;
+ UNISCR_PLUS(pos, vc->state.y * vc->vc_cols + vc->state.x);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(pos, vc->vc_cols * (vc->vc_softback_lines +
vc->vc_top));
+#endif
+ *pos = uc;
+ }
}
static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
-
- if (uniscr) {
- char32_t *ln = uniscr->lines[vc->state.y];
- unsigned int x = vc->state.x, cols = vc->vc_cols;
+ unsigned int x = vc->state.x, y = vc->state.y, cols = vc->vc_cols;
+ uint32_t *ln = vc->vc_uniscr_curr;
- memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(*ln));
+ if (vc->vc_uniscr_buf) {
+ UNISCR_PLUS(ln, y * cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(ln, vc->vc_cols * (vc->vc_softback_lines +
vc->vc_top));
+#endif
+ memmove(&ln[x + nr], &ln[x], (cols - x - nr) *
sizeof(uint32_t));
memset32(&ln[x], ' ', nr);
}
}
static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
-
- if (uniscr) {
- char32_t *ln = uniscr->lines[vc->state.y];
- unsigned int x = vc->state.x, cols = vc->vc_cols;
+ unsigned int x = vc->state.x, y = vc->state.y, cols = vc->vc_cols;
+ uint32_t *ln = vc->vc_uniscr_curr;
- memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(*ln));
+ if (vc->vc_uniscr_buf) {
+ UNISCR_PLUS(ln, y * cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(ln, vc->vc_cols * (vc->vc_softback_lines +
vc->vc_top));
+#endif
+ memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(uint32_t));
memset32(&ln[cols - nr], ' ', nr);
}
}
+/* FIXME!!! We need to check that NR never goes beyond the current line end.
!!! */
static void vc_uniscr_clear_line(struct vc_data *vc, unsigned int x,
unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
-
- if (uniscr) {
- char32_t *ln = uniscr->lines[vc->state.y];
+ if (vc->vc_uniscr_buf) {
+ uint32_t *ln = vc->vc_uniscr_curr;
+ UNISCR_PLUS(ln, vc->state.y * vc->vc_cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(ln, vc->vc_cols * (vc->vc_softback_lines +
vc->vc_top));
+#endif
memset32(&ln[x], ' ', nr);
}
}
@@ -418,77 +442,70 @@ static void vc_uniscr_clear_line(struct vc_data *vc,
unsigned int x,
static void vc_uniscr_clear_lines(struct vc_data *vc, unsigned int y,
unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
-
- if (uniscr) {
+ if (vc->vc_uniscr_buf) {
unsigned int cols = vc->vc_cols;
+ uint32_t *ln = vc->vc_uniscr_curr;
- while (nr--)
- memset32(uniscr->lines[y++], ' ', cols);
+ UNISCR_PLUS(ln, y * cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(ln, vc->vc_cols * (vc->vc_softback_lines +
vc->vc_top));
+#endif
+ while (nr--) {
+ memset32(ln, ' ', cols);
+ UNISCR_PLUS(ln, cols);
+ }
}
}
static void vc_uniscr_scroll(struct vc_data *vc, unsigned int t, unsigned int
b,
enum con_scroll dir, unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
-
- if (uniscr) {
- unsigned int i, j, k, sz, d, clear;
-
- sz = b - t;
- clear = b - nr;
- d = nr;
- if (dir == SM_DOWN) {
+ if (vc->vc_uniscr_buf) {
+ unsigned int cols = vc->vc_cols;
+ unsigned int sz, /* number of rows being scrolled */
+ d, /* number of rows needing blanking */
+ clear; /* The number of the topmost row
needing blanking. */
+ uint32_t *dest, *src;
+ unsigned int i;
+
+ if (dir == SM_UP /* && t == 0 */&& b == vc->vc_rows) {
+ UNISCR_PLUS(vc->vc_uniscr_curr, nr * cols);
+ d = nr;
+ clear = vc->vc_rows - nr;
+ } else if (dir == SM_DOWN && t == 0 && b == vc->vc_rows - nr) {
+ UNISCR_MINUS(vc->vc_uniscr_curr, nr * cols);
+ d = nr;
clear = t;
- d = sz - nr;
- }
- for (i = 0; i < gcd(d, sz); i++) {
- char32_t *tmp = uniscr->lines[t + i];
- j = i;
- while (1) {
- k = j + d;
- if (k >= sz)
- k -= sz;
- if (k == i)
- break;
- uniscr->lines[t + j] = uniscr->lines[t + k];
- j = k;
+ } else if (dir == SM_UP) {
+ sz = b - t;
+ src = vc->vc_uniscr_curr;
+ UNISCR_PLUS(src, t * cols);
+ dest = src;
+ UNISCR_MINUS(dest, nr * cols);
+ i = b - t;
+ while (i--) {
+ memcpy(dest, src, cols * sizeof(uint32_t));
+ UNISCR_PLUS(src, cols);
+ UNISCR_PLUS(dest, cols);
+ }
+ d = nr;
+ clear = b - nr;
+ } else {
+ src = vc->vc_uniscr_curr;
+ UNISCR_PLUS(src, b * cols);
+ dest = src;
+ UNISCR_PLUS(dest, nr * cols);
+ i = b - t;
+ while (i--) {
+ UNISCR_MINUS(src, cols);
+ UNISCR_MINUS(dest, cols);
+ memcpy(dest, src, cols * sizeof(uint32_t));
}
- uniscr->lines[t + j] = tmp;
+ d = nr;
+ clear = t;
}
- vc_uniscr_clear_lines(vc, clear, nr);
- }
-}
-
-static void vc_uniscr_copy_area(struct uni_screen *dst,
- unsigned int dst_cols,
- unsigned int dst_rows,
- struct uni_screen *src,
- unsigned int src_cols,
- unsigned int src_top_row,
- unsigned int src_bot_row)
-{
- unsigned int dst_row = 0;
-
- if (!dst)
- return;
-
- while (src_top_row < src_bot_row) {
- char32_t *src_line = src->lines[src_top_row];
- char32_t *dst_line = dst->lines[dst_row];
-
- memcpy(dst_line, src_line, src_cols * sizeof(char32_t));
- if (dst_cols - src_cols)
- memset32(dst_line + src_cols, ' ', dst_cols - src_cols);
- src_top_row++;
- dst_row++;
- }
- while (dst_row < dst_rows) {
- char32_t *dst_line = dst->lines[dst_row];
-
- memset32(dst_line, ' ', dst_cols);
- dst_row++;
+ if (d)
+ vc_uniscr_clear_lines(vc, clear, nr);
}
}
@@ -500,7 +517,6 @@ static void vc_uniscr_copy_area(struct uni_screen *dst,
*/
int vc_uniscr_check(struct vc_data *vc)
{
- struct uni_screen *uniscr;
unsigned short *p;
int x, y, mask;
@@ -512,11 +528,10 @@ int vc_uniscr_check(struct vc_data *vc)
if (!vc->vc_utf)
return -ENODATA;
- if (vc->vc_uni_screen)
+ if (vc->vc_uniscr_buf)
return 0;
- uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows);
- if (!uniscr)
+ if (vc_uniscr_alloc (vc, vc->vc_cols, vc->vc_rows))
return -ENOMEM;
/*
@@ -528,14 +543,14 @@ int vc_uniscr_check(struct vc_data *vc)
p = (unsigned short *)vc->vc_origin;
mask = vc->vc_hi_font_mask | 0xff;
for (y = 0; y < vc->vc_rows; y++) {
- char32_t *line = uniscr->lines[y];
+ uint32_t *line = vc->vc_uniscr_curr;
+ UNISCR_PLUS(line, y * vc->vc_cols);
for (x = 0; x < vc->vc_cols; x++) {
u16 glyph = scr_readw(p++) & mask;
line[x] = inverse_translate(vc, glyph, true);
}
}
- vc->vc_uni_screen = uniscr;
return 0;
}
@@ -547,12 +562,23 @@ int vc_uniscr_check(struct vc_data *vc)
void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed,
unsigned int row, unsigned int col, unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ uint32_t *pos; /* Position in the unicode buffer of col/row */
+#else
int offset = row * vc->vc_size_row + col * 2;
- unsigned long pos;
+ unsigned long pos; /* Position in the main screen buffer of col/row */
+#endif
- BUG_ON(!uniscr);
+ BUG_ON(!vc->vc_uniscr_buf);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ pos = vc->vc_uniscr_curr;
+ UNISCR_PLUS(pos, row * vc->vc_cols + col);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(pos, vc->vc_softback_lines * vc->vc_cols);
+#endif
+ memcpy(dest, pos, nr * sizeof(uint32_t));
+#else
pos = (unsigned long)screenpos(vc, offset, viewed);
if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
/*
@@ -562,60 +588,271 @@ void vc_uniscr_copy_line(const struct vc_data *vc, void
*dest, bool viewed,
*/
row = (pos - vc->vc_origin) / vc->vc_size_row;
col = ((pos - vc->vc_origin) % vc->vc_size_row) / 2;
- memcpy(dest, &uniscr->lines[row][col], nr * sizeof(char32_t));
+ memcpy(dest,
+ (void *)(vc->vc_uniscr_curr + row * vc->vc_cols + col),
+ nr);
} else {
/*
- * Scrollback is active. For now let's simply backtranslate
- * the screen glyphs until the unicode screen buffer does
- * synchronize with console display drivers for a scrollback
- * buffer of its own.
+ * Scrollback is active. So hoik the unicode characters out
+ * of the unicode circular buffer.
*/
- u16 *p = (u16 *)pos;
- int mask = vc->vc_hi_font_mask | 0xff;
- char32_t *uni_buf = dest;
- while (nr--) {
- u16 glyph = scr_readw(p++) & mask;
- *uni_buf++ = inverse_translate(vc, glyph, true);
- }
+ /* CAN'T HAPPEN!!! (Hah hah!) */
}
+#endif
}
/* this is for validation and debugging only */
static void vc_uniscr_debug_check(struct vc_data *vc)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
- unsigned short *p;
- int x, y, mask;
+#ifndef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ /* struct uni_screen *uniscr = get_vc_uniscr(vc); */
+ /* unsigned short *p; */
+ /* int x, y, mask; */
+#endif
- if (!VC_UNI_SCREEN_DEBUG || !uniscr)
+ if (!VC_UNI_SCREEN_DEBUG || !vc->vc_uniscr_buf)
return;
WARN_CONSOLE_UNLOCKED();
+#ifndef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
/*
* Make sure our unicode screen translates into the same glyphs
* as the actual screen. This is brutal indeed.
*/
- p = (unsigned short *)vc->vc_origin;
- mask = vc->vc_hi_font_mask | 0xff;
- for (y = 0; y < vc->vc_rows; y++) {
- char32_t *line = uniscr->lines[y];
- for (x = 0; x < vc->vc_cols; x++) {
- u16 glyph = scr_readw(p++) & mask;
- char32_t uc = line[x];
- int tc = conv_uni_to_pc(vc, uc);
- if (tc == -4)
- tc = conv_uni_to_pc(vc, 0xfffd);
- if (tc == -4)
- tc = conv_uni_to_pc(vc, '?');
- if (tc != glyph)
- pr_err_ratelimited(
- "%s: mismatch at %d,%d: glyph=%#x
tc=%#x\n",
- __func__, x, y, glyph, tc);
+ /* p = (unsigned short *)vc->vc_origin; */
+ /* mask = vc->vc_hi_font_mask | 0xff; */
+ /* for (y = 0; y < vc->vc_rows; y++) { */
+ /* char32_t *line = uniscr->lines[y]; */
+ /* for (x = 0; x < vc->vc_cols; x++) { */
+ /* u16 glyph = scr_readw(p++) & mask; */
+ /* char32_t uc = line[x]; */
+ /* int tc = conv_uni_to_pc(vc, uc); */
+ /* if (tc == -4) */
+ /* tc = conv_uni_to_pc(vc, 0xfffd); */
+ /* if (tc == -4) */
+ /* tc = conv_uni_to_pc(vc, '?'); */
+ /* if (tc != glyph) */
+ /* pr_err_ratelimited( */
+ /* "%s: mismatch at %d,%d: glyph=%#x
tc=%#x\n", */
+ /* __func__, x, y, glyph, tc); */
+ /* } */
+ /* } */
+#endif
+}
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+static void con_update_softback(struct vc_data *vc)
+{
+ int l = console_soft_scrollback_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 +863,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 +882,53 @@ 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 - vc->vc_softback_lines * vc->vc_size_row;
+ 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++;
+ }
+}
+#else
static void do_update_region(struct vc_data *vc, unsigned long start, int
count)
{
unsigned int xx, yy, offset;
@@ -684,6 +972,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 +981,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);
}
}
@@ -753,51 +1045,68 @@ static void update_attr(struct vc_data *vc)
}
/* Note: inverting the screen twice should revert to the original state */
+/* OFFSET is the offset in bytes (not 16-bit characters), COUNT is a byte
+ * count (not a character count). */
void invert_screen(struct vc_data *vc, int offset, int count, bool viewed)
{
unsigned short *p;
+ int row_offset, bytes_left_in_row;
+ int row_count;
WARN_CONSOLE_UNLOCKED();
count /= 2;
- p = screenpos(vc, offset, viewed);
- if (vc->vc_sw->con_invert_region) {
- vc->vc_sw->con_invert_region(vc, p, count);
- } else {
- u16 *q = p;
- int cnt = count;
- u16 a;
-
- if (!vc->vc_can_do_color) {
- while (cnt--) {
- a = scr_readw(q);
- a ^= 0x0800;
- scr_writew(a, q);
- q++;
- }
- } else if (vc->vc_hi_font_mask == 0x100) {
- while (cnt--) {
- a = scr_readw(q);
- a = (a & 0x11ff) |
- ((a & 0xe000) >> 4) |
- ((a & 0x0e00) << 4);
- scr_writew(a, q);
- q++;
- }
+ row_offset = offset;
+ bytes_left_in_row = vc->vc_size_row - (row_offset % vc->vc_size_row);
+ row_count = (count < bytes_left_in_row / 2)
+ ? count
+ : bytes_left_in_row / 2;
+
+ while (count) {
+ p = screenpos(vc, row_offset, viewed);
+ if (vc->vc_sw->con_invert_region) {
+ vc->vc_sw->con_invert_region(vc, p, row_count);
} else {
- while (cnt--) {
- a = scr_readw(q);
- a = (a & 0x88ff) |
- ((a & 0x7000) >> 4) |
- ((a & 0x0700) << 4);
- scr_writew(a, q);
- q++;
+ u16 *q = p;
+ int cnt = row_count;
+ u16 a;
+
+ if (!vc->vc_can_do_color) {
+ while (cnt--) {
+ a = scr_readw(q);
+ a ^= 0x0800;
+ scr_writew(a, q);
+ q++;
+ }
+ } else if (vc->vc_hi_font_mask == 0x100) {
+ while (cnt--) {
+ a = scr_readw(q);
+ a = (a & 0x11ff) |
+ ((a & 0xe000) >> 4) |
+ ((a & 0x0e00) << 4);
+ scr_writew(a, q);
+ q++;
+ }
+ } else {
+ while (cnt--) {
+ a = scr_readw(q);
+ a = (a & 0x88ff) |
+ ((a & 0x7000) >> 4) |
+ ((a & 0x0700) << 4);
+ scr_writew(a, q);
+ q++;
+ }
}
}
- }
- if (con_should_update(vc))
- do_update_region(vc, (unsigned long) p, count);
+ if (con_should_update(vc))
+ do_update_region(vc, (unsigned long) p, row_count);
+ row_offset += 2 * row_count;
+ count -= row_count;
+ row_count = (count >= vc->vc_cols)
+ ? vc->vc_cols
+ : count;
+ }
notify_update(vc);
}
@@ -927,8 +1236,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;
@@ -989,7 +1307,6 @@ void redraw_screen(struct vc_data *vc, int is_switch)
if (!vc) {
/* strange ... */
- /* printk("redraw_screen: tty %d not allocated ??\n",
new_console+1); */
return;
}
@@ -1004,7 +1321,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 +1333,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 +1347,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,7 +1437,6 @@ int vc_allocate(unsigned int currcons) /* return 0 on
success */
int err;
WARN_CONSOLE_UNLOCKED();
-
if (currcons >= MAX_NR_CONSOLES)
return -ENXIO;
@@ -1147,16 +1471,30 @@ int vc_allocate(unsigned int currcons) /* return 0 on
success */
vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_KERNEL);
if (!vc->vc_screenbuf)
goto err_free;
-
/* If no drivers have overridden us and the user didn't pass a
boot option, default to displaying the cursor */
if (global_cursor_default == -1)
global_cursor_default = 1;
vc_init(vc, vc->vc_rows, vc->vc_cols, 1);
+ if (vc_uniscr_alloc (vc, vc->vc_cols, vc->vc_rows) != 0)
+ goto err_free;
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)vzalloc(console_soft_scrollback_size);
+ 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);
+ vc_uniscr_alloc(vc, vc->vc_cols, vc->vc_rows);
+#endif
return 0;
err_free:
visual_deinit(vc);
@@ -1177,6 +1515,75 @@ static inline int resize_screen(struct vc_data *vc, int
width, int height,
return err;
}
+static int vc_copy_uniscr_to_new_area (struct vc_data *vc,
+ unsigned int new_cols,
+ unsigned int new_rows)
+{
+ unsigned int old_rows = vc->vc_rows, old_cols = vc->vc_cols;
+ uint32_t *old_uniscr_curr = vc->vc_uniscr_curr,
+ *old_uniscr_buf = vc->vc_uniscr_buf;
+ unsigned int old_uniscr_char_size = vc->vc_uniscr_char_size;
+ unsigned int new_lines;
+ unsigned int copy_cols;
+ uint32_t *dest, *src;
+ int res;
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ unsigned int new_uniscr_rows;
+ unsigned int old_lines;
+ unsigned long tmp;
+
+ if (vc->vc_softback_in >= vc->vc_softback_top)
+ tmp = vc->vc_softback_in - vc->vc_softback_top;
+ else
+ tmp = vc->vc_softback_in - vc->vc_softback_top
+ + vc->vc_softback_end - vc->vc_softback_buf;
+ old_lines = tmp / vc->vc_size_row + old_rows;
+
+ if ((res = vc_uniscr_alloc(vc, new_cols, new_rows)) != 0)
+ return res;
+
+ new_uniscr_rows = vc->vc_uniscr_char_size / new_cols + new_rows;
+ new_lines = min(old_lines, new_uniscr_rows);
+ copy_cols = min(old_cols, new_cols);
+
+ dest = vc->vc_uniscr_curr;
+ if (new_lines > old_rows) {
+ dest -= (new_lines - old_rows) * new_cols;
+ while (dest < vc->vc_uniscr_buf) /* Could happen twice. */
+ dest += vc->vc_uniscr_char_size;
+ }
+ src = old_uniscr_curr;
+ if (new_lines > old_rows) {
+ src -= (new_lines - old_rows) * old_cols;
+ while (src < old_uniscr_buf)
+ src += old_uniscr_char_size;
+ }
+#else
+ if ((res = vc_uniscr_alloc(vc, new_cols, new_rows)) != 0)
+ return res;
+
+ new_lines = min(old_rows, new_rows);
+ copy_cols = min(old_cols, new_cols);
+ dest = vc->vc_uniscr_curr;
+ src = old_uniscr_curr;
+#endif
+ if (old_uniscr_buf) {
+ while (new_lines--) {
+ memcpy(dest, src, copy_cols * sizeof(uint32_t));
+ if (new_cols > old_cols)
+ memset32(dest + old_cols, ' ',
+ new_cols - old_cols);
+ UNISCR_PLUS(dest, new_cols);
+ src += old_cols;
+ if (src >= old_uniscr_buf + old_uniscr_char_size)
+ src -= old_uniscr_char_size;
+ }
+ kvfree(old_uniscr_buf);
+ }
+ return 0;
+}
+
/**
* vc_do_resize - resizing method for the tty
* @tty: tty being resized
@@ -1197,12 +1604,19 @@ static int vc_do_resize(struct tty_struct *tty, struct
vc_data *vc,
{
unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0;
unsigned long end;
- unsigned int old_rows, old_row_size, first_copied_row;
- unsigned int new_cols, new_rows, new_row_size, new_screen_size;
+ unsigned int old_rows, old_size_row, first_copied_row;
+ unsigned int new_cols, new_rows, new_size_row, new_screen_size;
unsigned int user;
unsigned short *oldscreen, *newscreen;
- struct uni_screen *new_uniscr = NULL;
-
+ uint32_t *old_uniscr = vc->vc_uniscr_buf;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ unsigned short *d;
+ unsigned long old_softback_buf, new_softback_buf, tmp;
+ unsigned long old_softback_end, new_softback_end;
+ unsigned int old_scrolled_rows, new_scrolled_rows;
+ unsigned int count, copied_scrolled_rows;
+ void *temp_new_softback_buf;
+#endif
WARN_CONSOLE_UNLOCKED();
if (!vc)
@@ -1216,8 +1630,8 @@ static int vc_do_resize(struct tty_struct *tty, struct
vc_data *vc,
new_cols = (cols ? cols : vc->vc_cols);
new_rows = (lines ? lines : vc->vc_rows);
- new_row_size = new_cols << 1;
- new_screen_size = new_row_size * new_rows;
+ new_size_row = new_cols << 1;
+ new_screen_size = new_size_row * new_rows;
if (new_cols == vc->vc_cols && new_rows == vc->vc_rows) {
/*
@@ -1245,61 +1659,91 @@ static int vc_do_resize(struct tty_struct *tty, struct
vc_data *vc,
if (!newscreen)
return -ENOMEM;
- if (get_vc_uniscr(vc)) {
- new_uniscr = vc_uniscr_alloc(new_cols, new_rows);
- if (!new_uniscr) {
- kfree(newscreen);
- return -ENOMEM;
- }
- }
-
if (vc_is_sel(vc))
clear_selection();
old_rows = vc->vc_rows;
- old_row_size = vc->vc_size_row;
+ old_size_row = vc->vc_size_row;
err = resize_screen(vc, new_cols, new_rows, user);
if (err) {
kfree(newscreen);
- vc_uniscr_free(new_uniscr);
+ vc_uniscr_free(vc);
+ vc->vc_uniscr_buf = old_uniscr;
return err;
}
- vc->vc_rows = new_rows;
- vc->vc_cols = new_cols;
- vc->vc_size_row = new_row_size;
- vc->vc_screenbuf_size = new_screen_size;
-
- rlth = min(old_row_size, new_row_size);
- rrem = new_row_size - rlth;
+ rlth = min(old_size_row, new_size_row);
+ rrem = new_size_row - rlth;
old_origin = vc->vc_origin;
new_origin = (long) newscreen;
new_scr_end = new_origin + new_screen_size;
if (vc->state.y > new_rows) {
- if (old_rows - vc->state.y < new_rows) {
+ if (old_rows - new_rows < vc->vc_top + vc->state.y) {
+ if (old_rows - new_rows > vc->vc_top) {
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ con_softback_note(vc, vc->vc_top,
+ old_rows - new_rows -
vc->vc_top);
+#endif
+ vc_uniscr_scroll(vc, 0, old_rows, SM_UP,
+ old_rows - new_rows -
vc->vc_top);
+ }
/*
* Cursor near the bottom, copy contents from the
* bottom of buffer
*/
first_copied_row = (old_rows - new_rows);
} else {
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ con_softback_note(vc, vc->vc_top,
+ vc->state.y - new_rows/2);
+#endif
+ vc_uniscr_scroll(vc, 0, old_rows, SM_UP,
+ vc->state.y - new_rows/2);
/*
* Cursor is in no man's land, copy 1/2 screenful
* from the top and bottom of cursor position
*/
first_copied_row = (vc->state.y - new_rows/2);
}
- old_origin += first_copied_row * old_row_size;
+ old_origin += first_copied_row * old_size_row;
} else
first_copied_row = 0;
- end = old_origin + old_row_size * min(old_rows, new_rows);
+ end = old_origin + old_size_row * min(old_rows, new_rows);
+
+ if ((err = vc_copy_uniscr_to_new_area(vc, new_cols, new_rows)) != 0)
+ return err;
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ concon_set_origin(vc);
+ old_softback_buf = vc->vc_softback_buf;
+ old_softback_end = vc->vc_softback_end;
+ vc->vc_softback_size = console_soft_scrollback_size;
+ temp_new_softback_buf = vzalloc(console_soft_scrollback_size);
+ new_softback_buf = (unsigned long)temp_new_softback_buf;
+ if (!new_softback_buf)
+ return -ENOMEM;
+ new_softback_end = new_softback_buf + console_soft_scrollback_size;
+ d = (unsigned short *)new_softback_buf;
+ while (d != (u16 *)new_softback_end) {
+ scr_writew (0x0020, d);
+ d++;
+ }
+ if (vc->vc_softback_top <= vc->vc_softback_in)
+ tmp = vc->vc_softback_in - vc->vc_softback_top;
+ else
+ tmp = vc->vc_softback_in - vc->vc_softback_top
+ + vc->vc_softback_end - vc->vc_softback_buf;
+ old_scrolled_rows = tmp / vc->vc_size_row;
+ new_scrolled_rows = console_soft_scrollback_size / new_size_row;
+ copied_scrolled_rows = min(old_scrolled_rows, new_scrolled_rows);
+#endif
- vc_uniscr_copy_area(new_uniscr, new_cols, new_rows,
- get_vc_uniscr(vc), rlth/2, first_copied_row,
- min(old_rows, new_rows));
- vc_uniscr_set(vc, new_uniscr);
+ vc->vc_cols = new_cols;
+ vc->vc_rows = new_rows;
+ vc->vc_size_row = new_size_row;
+ vc->vc_screenbuf_size = new_screen_size;
update_attr(vc);
@@ -1309,17 +1753,60 @@ static int vc_do_resize(struct tty_struct *tty, struct
vc_data *vc,
if (rrem)
scr_memsetw((void *)(new_origin + rlth),
vc->vc_video_erase_char, rrem);
- old_origin += old_row_size;
- new_origin += new_row_size;
+ old_origin += old_size_row;
+ new_origin += new_size_row;
}
if (new_scr_end > new_origin)
scr_memsetw((void *)new_origin, vc->vc_video_erase_char,
new_scr_end - new_origin);
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ new_origin = new_softback_buf;
+ if (copied_scrolled_rows) {
+ old_origin = vc->vc_softback_in
+ - copied_scrolled_rows * old_size_row;
+ if (old_origin < vc->vc_softback_buf)
+ old_origin += vc->vc_softback_end
+ - vc->vc_softback_buf;
+ count = copied_scrolled_rows;
+
+ while (count--) {
+ scr_memcpyw((unsigned short *) new_origin,
+ (unsigned short *) old_origin, rlth);
+ if (rrem)
+ scr_memsetw((void *)(new_origin + rlth),
+ vc->vc_video_erase_char, rrem);
+ old_origin += old_size_row;
+ if (old_origin >= old_softback_end)
+ old_origin -= old_softback_end
+ - old_softback_buf;
+ new_origin += new_size_row;
+ }
+ }
+#endif
oldscreen = vc->vc_screenbuf;
vc->vc_screenbuf = newscreen;
vc->vc_screenbuf_size = new_screen_size;
set_origin(vc);
kfree(oldscreen);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ vc->vc_softback_buf = new_softback_buf;
+ vc->vc_softback_end = new_softback_buf
+ + new_scrolled_rows * new_size_row;
+ if (copied_scrolled_rows) {
+ if (new_origin >= vc->vc_softback_end)
+ new_origin -= vc->vc_softback_end - vc->vc_softback_buf;
+ vc->vc_softback_in = new_origin;
+ } else
+ vc->vc_softback_in = new_softback_buf;
+ vc->vc_softback_top = new_softback_buf;
+ if (copied_scrolled_rows
+ && (new_origin == new_softback_buf))
+ vc->vc_softback_top += new_size_row;
+ vc->vc_softback_curr = vc->vc_softback_in;
+ vc->vc_softback_lines = 0; /* Probably redundant. */
+ kvfree((void *)old_softback_buf);
+#endif
/* do part of a reset_terminal() */
vc->vc_top = 0;
@@ -1400,8 +1887,8 @@ struct vc_data *vc_deallocate(unsigned int currcons)
visual_deinit(vc);
con_free_unimap(vc);
put_pid(vc->vt_pid);
- vc_uniscr_set(vc, NULL);
kfree(vc->vc_screenbuf);
+ vc->vc_uniscr_buf = NULL;
vc_cons[currcons].d = NULL;
}
return vc;
@@ -1646,7 +2133,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;
@@ -1658,7 +2145,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;
}
@@ -2928,6 +3415,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++;
@@ -3094,11 +3587,8 @@ static void vt_console_print(struct console *co, const
char *b, unsigned count)
if (kmsg_console && vc_cons_allocated(kmsg_console - 1))
vc = vc_cons[kmsg_console - 1].d;
- if (!vc_cons_allocated(fg_console)) {
- /* impossible */
- /* printk("vt_console_print: tty %d not allocated ??\n",
currcons+1); */
+ if (!vc_cons_allocated(fg_console))
goto quit;
- }
if (vc->vc_mode != KD_TEXT)
goto quit;
@@ -3143,7 +3633,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:
@@ -3364,7 +3858,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();
}
@@ -3385,6 +3883,14 @@ 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);
+ if (!vc->vc_uniscr_buf)
+ ret = vc_uniscr_alloc(vc, vc->vc_cols, vc->vc_rows);
+ if (ret)
+ goto unlock;
+#endif
+
/* Still being freed */
if (vc->port.tty) {
ret = -ERESTARTSYS;
@@ -3440,7 +3946,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);
@@ -3526,6 +4032,7 @@ static int __init con_init(void)
vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
vc_init(vc, vc->vc_rows, vc->vc_cols,
currcons || !vc->vc_sw->con_save_screen);
+ vc_uniscr_alloc(vc, vc->vc_cols, vc->vc_rows);
}
currcons = fg_console = 0;
master_display_fg = vc = vc_cons[currcons].d;
@@ -4140,7 +4647,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;
@@ -4441,7 +4948,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);
@@ -4741,10 +5251,16 @@ EXPORT_SYMBOL_GPL(screen_glyph);
u32 screen_glyph_unicode(const struct vc_data *vc, int n)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
+ int y = n / vc->vc_cols, x = n % vc->vc_cols;
+ uint32_t *ln = vc->vc_uniscr_curr;
- if (uniscr)
- return uniscr->lines[n / vc->vc_cols][n % vc->vc_cols];
+ if (vc->vc_uniscr_curr) {
+ UNISCR_PLUS(ln, y * vc->vc_cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(ln, vc->vc_softback_lines * vc->vc_cols);
+#endif
+ return ln[x];
+ }
return inverse_translate(vc, screen_glyph(vc, n * 2), 1);
}
EXPORT_SYMBOL_GPL(screen_glyph_unicode);
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index fcc46380e7c9..1abba103d7da 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -98,6 +98,44 @@ config FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION
If unsure, select n.
+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_SOFT_SCROLLBACK_GPM
+ bool "Enable a working GPM for scrolled back scrollback buffer in
System RAM"
+ depends on FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ default y
+ help
+ This option buffers up Unicode characters corresponding to the glyphs
+ displayed by the scrollback buffer. This enables the GPM mouse driver
+ (or similar) to copy characters from a scrolled back buffer.
+
+ A buffer is created for each framebuffer console, this buffer being
+ approximately twice as big as the buffer size specified in
+ FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE.
+
+ If unsure, say 'Y'.
+
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 e035a63bbe5b..824225747aef 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -633,6 +633,28 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct
fb_info *info,
erase,
vc->vc_size_row * logo_lines);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ {
+ uint32_t *s = vc->vc_uniscr_curr, *d = vc->vc_uniscr_curr;
+ unsigned int i = vc->vc_rows - logo_lines;
+
+ UNISCR_PLUS(d, vc->vc_rows * vc->vc_cols);
+ UNISCR_PLUS(s, (vc->vc_rows - logo_lines) * vc->vc_cols);
+ while (i--) {
+ UNISCR_MINUS(d, vc->vc_cols);
+ UNISCR_MINUS(s, vc->vc_cols);
+ memcpy(d, s, vc->vc_cols * sizeof (uint32_t));
+ }
+ i = logo_lines;
+ d = vc->vc_uniscr_curr;
+ while (i--) {
+ memset32(d, ' ', vc->vc_cols);
+ UNISCR_PLUS(d, vc->vc_cols);
+ }
+ vc->vc_uniscr_curr = d;
+ }
+#endif
+
if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
fbcon_clear_margins(vc, 0);
update_screen(vc);
@@ -1273,6 +1295,25 @@ static void fbcon_clear(struct vc_data *vc, int sy, int
sx, int height,
* bitmap stretched into the margin area.
*/
fbcon_clear_margins(vc, 0);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ {
+ uint32_t *s = vc->vc_uniscr_curr, *d =
vc->vc_uniscr_curr;
+ unsigned int i = vc->vc_rows - logo_lines;
+
+ UNISCR_PLUS(d, (vc->vc_rows - logo_lines) *
vc->vc_cols);
+ UNISCR_PLUS(s, (vc->vc_rows - 2 * logo_lines) *
vc->vc_cols);
+ while (i--) {
+ UNISCR_MINUS(d, vc->vc_cols);
+ UNISCR_MINUS(s, vc->vc_cols);
+ memcpy (d, s, vc->vc_cols * sizeof (uint32_t));
+ }
+ i = logo_lines;
+ while (i--) {
+ UNISCR_MINUS(d, vc->vc_cols);
+ memset32(d, ' ', vc->vc_cols);
+ }
+ }
+#endif
}
/* Split blits that cross physical y_wrap boundary */
@@ -2080,6 +2121,7 @@ static int fbcon_switch(struct vc_data *vc)
struct fbcon_ops *ops;
struct fbcon_display *p = &fb_display[vc->vc_num];
struct fb_var_screeninfo var;
+ unsigned short *d, *s;
int i, ret, prev_console;
info = registered_fb[con2fb_map[vc->vc_num]];
@@ -2089,8 +2131,21 @@ static int fbcon_switch(struct vc_data *vc)
struct vc_data *conp2 = vc_cons[logo_shown].d;
if (conp2->vc_top == logo_lines
- && conp2->vc_bottom == conp2->vc_rows)
+ && conp2->vc_bottom == conp2->vc_rows) {
+ /* Scroll the bottom part of the screen up to fill the
+ * logo lines. */
+ i = conp2->vc_bottom - conp2->vc_top;
+ d = (unsigned short *)conp2->vc_origin;
+ s = (unsigned short *)(conp2->vc_origin + logo_lines *
conp2->vc_size_row);
+ while (i--) {
+ scr_memcpyw(d, s, conp2->vc_size_row);
+ d += conp2->vc_cols;
+ s += conp2->vc_cols;
+ }
+ scr_memsetw(d, conp2->vc_video_erase_char,
+ conp2->vc_size_row * logo_lines);
conp2->vc_top = 0;
+ }
logo_shown = FBCON_LOGO_CANSHOW;
}
@@ -3152,6 +3207,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..d8d159e75f39 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 */
@@ -159,7 +170,9 @@ struct vc_data {
struct vc_data **vc_display_fg; /* [!] Ptr to var holding fg
console for this display */
struct uni_pagedir *vc_uni_pagedir;
struct uni_pagedir **vc_uni_pagedir_loc; /* [!] Location of uni_pagedir
variable for this console */
- struct uni_screen *vc_uni_screen; /* unicode screen content */
+ uint32_t *vc_uniscr_buf; /* Address of unicode screen content */
+ unsigned int vc_uniscr_char_size; /* Size of *vc-uniscr_buf in 32-bit
chars */
+ uint32_t *vc_uniscr_curr; /* Pos of first char of (unscrolled)
screen */
/* additional information is in vt_kern.h */
};
@@ -194,4 +207,22 @@ extern void vc_SAK(struct work_struct *work);
bool con_is_visible(const struct vc_data *vc);
+/* Macros for wraparound in the uniscr buffer. POS must be a uint32_t pointer
+ * variable which is expected to be within the confines of vc->vc_uniscr_buf
+ * and vc->vc_uniscr_buf + vc->vc_uniscr_char_size. OFFSET must be a positive
+ * distance measured in uint32_t's, which when added to or subtracted from POS
+ * won't be too far away from the buffer. */
+#define UNISCR_PLUS(pos,offset)
\
+ do { \
+ pos += offset; \
+ if (pos >= vc->vc_uniscr_buf + vc->vc_uniscr_char_size) \
+ pos -= vc->vc_uniscr_char_size; \
+ } while (0)
+#define UNISCR_MINUS(pos,offset) \
+ do { \
+ pos -= offset; \
+ if (pos < vc->vc_uniscr_buf) \
+ pos += vc->vc_uniscr_char_size; \
+ } while (0)
+
#endif /* _LINUX_CONSOLE_STRUCT_H */
diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h
index b5ab452fca5b..1a11c9868f8e 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,
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 981d2bfcf9a5..cb9342fb0a3a 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
@@ -286,8 +291,31 @@ static inline bool con_should_update(const struct vc_data
*vc)
static inline unsigned short *screenpos(const struct vc_data *vc, int offset,
bool viewed)
{
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ unsigned long softback_pos, scrolled_expanse;
+
+ if (vc->vc_softback_curr == vc->vc_origin) /* Should never happen! */
+ return (unsigned short *)(vc->vc_origin + offset);
+ else {
+ if (vc->vc_softback_in >= vc->vc_softback_curr)
+ scrolled_expanse = vc->vc_softback_in -
vc->vc_softback_curr;
+ else
+ scrolled_expanse = vc->vc_softback_in -
vc->vc_softback_curr
+ + vc->vc_softback_end - vc->vc_softback_buf;
+ if (offset >= scrolled_expanse)
+ return (unsigned short *)(vc->vc_origin
+ + (offset -
scrolled_expanse));
+ else {
+ softback_pos = vc->vc_softback_curr + offset;
+ if (softback_pos >= vc->vc_softback_end)
+ softback_pos -= vc->vc_softback_end
+ - vc->vc_softback_buf;
+ }
+ }
+ return (unsigned short *)softback_pos;
+#else
unsigned short *p;
-
+
if (!viewed)
p = (unsigned short *)(vc->vc_origin + offset);
else if (!vc->vc_sw->con_screen_pos)
@@ -295,6 +323,7 @@ static inline unsigned short *screenpos(const struct
vc_data *vc, int offset,
else
p = vc->vc_sw->con_screen_pos(vc, offset);
return p;
+#endif
}
/* Called from the keyboard irq path.. */
@@ -316,101 +345,96 @@ void schedule_console_callback(void)
* Code to manage unicode-based screen buffers
*/
-#ifdef NO_VC_UNI_SCREEN
-/* this disables and optimizes related code away at compile time */
-#define get_vc_uniscr(vc) NULL
-#else
-#define get_vc_uniscr(vc) vc->vc_uni_screen
-#endif
-
#define VC_UNI_SCREEN_DEBUG 0
typedef uint32_t char32_t;
-/*
- * Our screen buffer is preceded by an array of line pointers so that
- * scrolling only implies some pointer shuffling.
- */
-struct uni_screen {
- char32_t *lines[0];
-};
+#define vc_uniscr_buf_end(vc) (vc->vc_uniscr_buf + vc->vc_uniscr_char_size)
-static struct uni_screen *vc_uniscr_alloc(unsigned int cols, unsigned int rows)
+static int vc_uniscr_alloc(struct vc_data *vc, unsigned int cols, unsigned int
rows)
{
- struct uni_screen *uniscr;
- void *p;
- unsigned int memsize, i;
+ uint32_t *p;
+ unsigned int new_size; /* In 32-bit characters */
- /* allocate everything in one go */
- memsize = cols * rows * sizeof(char32_t);
- memsize += rows * sizeof(char32_t *);
- p = vzalloc(memsize);
- if (!p)
- return NULL;
-
- /* initial line pointers */
- uniscr = p;
- p = uniscr->lines + rows;
- for (i = 0; i < rows; i++) {
- uniscr->lines[i] = p;
- p += cols * sizeof(char32_t);
- }
- return uniscr;
-}
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ unsigned int num_scrollback_rows;
-static void vc_uniscr_free(struct uni_screen *uniscr)
-{
- vfree(uniscr);
+ num_scrollback_rows = (console_soft_scrollback_size / 2) / cols;
+ new_size = cols * (num_scrollback_rows + rows + 1);
+#else
+ new_size = cols * (rows + 1); /* 1 row for the circular buffer admin */
+#endif
+ p = (uint32_t *)vzalloc (sizeof (uint32_t) * new_size);
+ if (!p)
+ return -ENOMEM;
+ vc->vc_uniscr_buf = p;
+ vc->vc_uniscr_curr = p;
+ vc->vc_uniscr_char_size = new_size;
+ memset32(p, ' ', new_size); /* Probably redundant. */
+ return 0;
}
-static void vc_uniscr_set(struct vc_data *vc, struct uni_screen *new_uniscr)
+static void vc_uniscr_free(struct vc_data *vc)
{
- vc_uniscr_free(vc->vc_uni_screen);
- vc->vc_uni_screen = new_uniscr;
+ kvfree(vc->vc_uniscr_buf);
+ vc->vc_uniscr_buf = NULL;
}
static void vc_uniscr_putc(struct vc_data *vc, char32_t uc)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
+ uint32_t *pos;
- if (uniscr)
- uniscr->lines[vc->state.y][vc->state.x] = uc;
+ if (vc->vc_uniscr_buf) {
+ pos = vc->vc_uniscr_curr;
+ UNISCR_PLUS(pos, vc->state.y * vc->vc_cols + vc->state.x);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(pos, vc->vc_cols * (vc->vc_softback_lines +
vc->vc_top));
+#endif
+ *pos = uc;
+ }
}
static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
-
- if (uniscr) {
- char32_t *ln = uniscr->lines[vc->state.y];
- unsigned int x = vc->state.x, cols = vc->vc_cols;
+ unsigned int x = vc->state.x, y = vc->state.y, cols = vc->vc_cols;
+ uint32_t *ln = vc->vc_uniscr_curr;
- memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(*ln));
+ if (vc->vc_uniscr_buf) {
+ UNISCR_PLUS(ln, y * cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(ln, vc->vc_cols * (vc->vc_softback_lines +
vc->vc_top));
+#endif
+ memmove(&ln[x + nr], &ln[x], (cols - x - nr) *
sizeof(uint32_t));
memset32(&ln[x], ' ', nr);
}
}
static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
-
- if (uniscr) {
- char32_t *ln = uniscr->lines[vc->state.y];
- unsigned int x = vc->state.x, cols = vc->vc_cols;
+ unsigned int x = vc->state.x, y = vc->state.y, cols = vc->vc_cols;
+ uint32_t *ln = vc->vc_uniscr_curr;
- memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(*ln));
+ if (vc->vc_uniscr_buf) {
+ UNISCR_PLUS(ln, y * cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(ln, vc->vc_cols * (vc->vc_softback_lines +
vc->vc_top));
+#endif
+ memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(uint32_t));
memset32(&ln[cols - nr], ' ', nr);
}
}
+/* FIXME!!! We need to check that NR never goes beyond the current line end.
!!! */
static void vc_uniscr_clear_line(struct vc_data *vc, unsigned int x,
unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
-
- if (uniscr) {
- char32_t *ln = uniscr->lines[vc->state.y];
+ if (vc->vc_uniscr_buf) {
+ uint32_t *ln = vc->vc_uniscr_curr;
+ UNISCR_PLUS(ln, vc->state.y * vc->vc_cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(ln, vc->vc_cols * (vc->vc_softback_lines +
vc->vc_top));
+#endif
memset32(&ln[x], ' ', nr);
}
}
@@ -418,77 +442,70 @@ static void vc_uniscr_clear_line(struct vc_data *vc,
unsigned int x,
static void vc_uniscr_clear_lines(struct vc_data *vc, unsigned int y,
unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
-
- if (uniscr) {
+ if (vc->vc_uniscr_buf) {
unsigned int cols = vc->vc_cols;
+ uint32_t *ln = vc->vc_uniscr_curr;
- while (nr--)
- memset32(uniscr->lines[y++], ' ', cols);
+ UNISCR_PLUS(ln, y * cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(ln, vc->vc_cols * (vc->vc_softback_lines +
vc->vc_top));
+#endif
+ while (nr--) {
+ memset32(ln, ' ', cols);
+ UNISCR_PLUS(ln, cols);
+ }
}
}
static void vc_uniscr_scroll(struct vc_data *vc, unsigned int t, unsigned int
b,
enum con_scroll dir, unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
-
- if (uniscr) {
- unsigned int i, j, k, sz, d, clear;
-
- sz = b - t;
- clear = b - nr;
- d = nr;
- if (dir == SM_DOWN) {
+ if (vc->vc_uniscr_buf) {
+ unsigned int cols = vc->vc_cols;
+ unsigned int sz, /* number of rows being scrolled */
+ d, /* number of rows needing blanking */
+ clear; /* The number of the topmost row
needing blanking. */
+ uint32_t *dest, *src;
+ unsigned int i;
+
+ if (dir == SM_UP /* && t == 0 */&& b == vc->vc_rows) {
+ UNISCR_PLUS(vc->vc_uniscr_curr, nr * cols);
+ d = nr;
+ clear = vc->vc_rows - nr;
+ } else if (dir == SM_DOWN && t == 0 && b == vc->vc_rows - nr) {
+ UNISCR_MINUS(vc->vc_uniscr_curr, nr * cols);
+ d = nr;
clear = t;
- d = sz - nr;
- }
- for (i = 0; i < gcd(d, sz); i++) {
- char32_t *tmp = uniscr->lines[t + i];
- j = i;
- while (1) {
- k = j + d;
- if (k >= sz)
- k -= sz;
- if (k == i)
- break;
- uniscr->lines[t + j] = uniscr->lines[t + k];
- j = k;
+ } else if (dir == SM_UP) {
+ sz = b - t;
+ src = vc->vc_uniscr_curr;
+ UNISCR_PLUS(src, t * cols);
+ dest = src;
+ UNISCR_MINUS(dest, nr * cols);
+ i = b - t;
+ while (i--) {
+ memcpy(dest, src, cols * sizeof(uint32_t));
+ UNISCR_PLUS(src, cols);
+ UNISCR_PLUS(dest, cols);
+ }
+ d = nr;
+ clear = b - nr;
+ } else {
+ src = vc->vc_uniscr_curr;
+ UNISCR_PLUS(src, b * cols);
+ dest = src;
+ UNISCR_PLUS(dest, nr * cols);
+ i = b - t;
+ while (i--) {
+ UNISCR_MINUS(src, cols);
+ UNISCR_MINUS(dest, cols);
+ memcpy(dest, src, cols * sizeof(uint32_t));
}
- uniscr->lines[t + j] = tmp;
+ d = nr;
+ clear = t;
}
- vc_uniscr_clear_lines(vc, clear, nr);
- }
-}
-
-static void vc_uniscr_copy_area(struct uni_screen *dst,
- unsigned int dst_cols,
- unsigned int dst_rows,
- struct uni_screen *src,
- unsigned int src_cols,
- unsigned int src_top_row,
- unsigned int src_bot_row)
-{
- unsigned int dst_row = 0;
-
- if (!dst)
- return;
-
- while (src_top_row < src_bot_row) {
- char32_t *src_line = src->lines[src_top_row];
- char32_t *dst_line = dst->lines[dst_row];
-
- memcpy(dst_line, src_line, src_cols * sizeof(char32_t));
- if (dst_cols - src_cols)
- memset32(dst_line + src_cols, ' ', dst_cols - src_cols);
- src_top_row++;
- dst_row++;
- }
- while (dst_row < dst_rows) {
- char32_t *dst_line = dst->lines[dst_row];
-
- memset32(dst_line, ' ', dst_cols);
- dst_row++;
+ if (d)
+ vc_uniscr_clear_lines(vc, clear, nr);
}
}
@@ -500,7 +517,6 @@ static void vc_uniscr_copy_area(struct uni_screen *dst,
*/
int vc_uniscr_check(struct vc_data *vc)
{
- struct uni_screen *uniscr;
unsigned short *p;
int x, y, mask;
@@ -512,11 +528,10 @@ int vc_uniscr_check(struct vc_data *vc)
if (!vc->vc_utf)
return -ENODATA;
- if (vc->vc_uni_screen)
+ if (vc->vc_uniscr_buf)
return 0;
- uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows);
- if (!uniscr)
+ if (vc_uniscr_alloc (vc, vc->vc_cols, vc->vc_rows))
return -ENOMEM;
/*
@@ -528,14 +543,14 @@ int vc_uniscr_check(struct vc_data *vc)
p = (unsigned short *)vc->vc_origin;
mask = vc->vc_hi_font_mask | 0xff;
for (y = 0; y < vc->vc_rows; y++) {
- char32_t *line = uniscr->lines[y];
+ uint32_t *line = vc->vc_uniscr_curr;
+ UNISCR_PLUS(line, y * vc->vc_cols);
for (x = 0; x < vc->vc_cols; x++) {
u16 glyph = scr_readw(p++) & mask;
line[x] = inverse_translate(vc, glyph, true);
}
}
- vc->vc_uni_screen = uniscr;
return 0;
}
@@ -547,12 +562,23 @@ int vc_uniscr_check(struct vc_data *vc)
void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed,
unsigned int row, unsigned int col, unsigned int nr)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ uint32_t *pos; /* Position in the unicode buffer of col/row */
+#else
int offset = row * vc->vc_size_row + col * 2;
- unsigned long pos;
+ unsigned long pos; /* Position in the main screen buffer of col/row */
+#endif
- BUG_ON(!uniscr);
+ BUG_ON(!vc->vc_uniscr_buf);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ pos = vc->vc_uniscr_curr;
+ UNISCR_PLUS(pos, row * vc->vc_cols + col);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(pos, vc->vc_softback_lines * vc->vc_cols);
+#endif
+ memcpy(dest, pos, nr * sizeof(uint32_t));
+#else
pos = (unsigned long)screenpos(vc, offset, viewed);
if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
/*
@@ -562,60 +588,271 @@ void vc_uniscr_copy_line(const struct vc_data *vc, void
*dest, bool viewed,
*/
row = (pos - vc->vc_origin) / vc->vc_size_row;
col = ((pos - vc->vc_origin) % vc->vc_size_row) / 2;
- memcpy(dest, &uniscr->lines[row][col], nr * sizeof(char32_t));
+ memcpy(dest,
+ (void *)(vc->vc_uniscr_curr + row * vc->vc_cols + col),
+ nr);
} else {
/*
- * Scrollback is active. For now let's simply backtranslate
- * the screen glyphs until the unicode screen buffer does
- * synchronize with console display drivers for a scrollback
- * buffer of its own.
+ * Scrollback is active. So hoik the unicode characters out
+ * of the unicode circular buffer.
*/
- u16 *p = (u16 *)pos;
- int mask = vc->vc_hi_font_mask | 0xff;
- char32_t *uni_buf = dest;
- while (nr--) {
- u16 glyph = scr_readw(p++) & mask;
- *uni_buf++ = inverse_translate(vc, glyph, true);
- }
+ /* CAN'T HAPPEN!!! (Hah hah!) */
}
+#endif
}
/* this is for validation and debugging only */
static void vc_uniscr_debug_check(struct vc_data *vc)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
- unsigned short *p;
- int x, y, mask;
+#ifndef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ /* struct uni_screen *uniscr = get_vc_uniscr(vc); */
+ /* unsigned short *p; */
+ /* int x, y, mask; */
+#endif
- if (!VC_UNI_SCREEN_DEBUG || !uniscr)
+ if (!VC_UNI_SCREEN_DEBUG || !vc->vc_uniscr_buf)
return;
WARN_CONSOLE_UNLOCKED();
+#ifndef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
/*
* Make sure our unicode screen translates into the same glyphs
* as the actual screen. This is brutal indeed.
*/
- p = (unsigned short *)vc->vc_origin;
- mask = vc->vc_hi_font_mask | 0xff;
- for (y = 0; y < vc->vc_rows; y++) {
- char32_t *line = uniscr->lines[y];
- for (x = 0; x < vc->vc_cols; x++) {
- u16 glyph = scr_readw(p++) & mask;
- char32_t uc = line[x];
- int tc = conv_uni_to_pc(vc, uc);
- if (tc == -4)
- tc = conv_uni_to_pc(vc, 0xfffd);
- if (tc == -4)
- tc = conv_uni_to_pc(vc, '?');
- if (tc != glyph)
- pr_err_ratelimited(
- "%s: mismatch at %d,%d: glyph=%#x
tc=%#x\n",
- __func__, x, y, glyph, tc);
+ /* p = (unsigned short *)vc->vc_origin; */
+ /* mask = vc->vc_hi_font_mask | 0xff; */
+ /* for (y = 0; y < vc->vc_rows; y++) { */
+ /* char32_t *line = uniscr->lines[y]; */
+ /* for (x = 0; x < vc->vc_cols; x++) { */
+ /* u16 glyph = scr_readw(p++) & mask; */
+ /* char32_t uc = line[x]; */
+ /* int tc = conv_uni_to_pc(vc, uc); */
+ /* if (tc == -4) */
+ /* tc = conv_uni_to_pc(vc, 0xfffd); */
+ /* if (tc == -4) */
+ /* tc = conv_uni_to_pc(vc, '?'); */
+ /* if (tc != glyph) */
+ /* pr_err_ratelimited( */
+ /* "%s: mismatch at %d,%d: glyph=%#x
tc=%#x\n", */
+ /* __func__, x, y, glyph, tc); */
+ /* } */
+ /* } */
+#endif
+}
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+static void con_update_softback(struct vc_data *vc)
+{
+ int l = console_soft_scrollback_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 +863,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 +882,53 @@ 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 - vc->vc_softback_lines * vc->vc_size_row;
+ 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++;
+ }
+}
+#else
static void do_update_region(struct vc_data *vc, unsigned long start, int
count)
{
unsigned int xx, yy, offset;
@@ -684,6 +972,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 +981,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);
}
}
@@ -753,51 +1045,68 @@ static void update_attr(struct vc_data *vc)
}
/* Note: inverting the screen twice should revert to the original state */
+/* OFFSET is the offset in bytes (not 16-bit characters), COUNT is a byte
+ * count (not a character count). */
void invert_screen(struct vc_data *vc, int offset, int count, bool viewed)
{
unsigned short *p;
+ int row_offset, bytes_left_in_row;
+ int row_count;
WARN_CONSOLE_UNLOCKED();
count /= 2;
- p = screenpos(vc, offset, viewed);
- if (vc->vc_sw->con_invert_region) {
- vc->vc_sw->con_invert_region(vc, p, count);
- } else {
- u16 *q = p;
- int cnt = count;
- u16 a;
-
- if (!vc->vc_can_do_color) {
- while (cnt--) {
- a = scr_readw(q);
- a ^= 0x0800;
- scr_writew(a, q);
- q++;
- }
- } else if (vc->vc_hi_font_mask == 0x100) {
- while (cnt--) {
- a = scr_readw(q);
- a = (a & 0x11ff) |
- ((a & 0xe000) >> 4) |
- ((a & 0x0e00) << 4);
- scr_writew(a, q);
- q++;
- }
+ row_offset = offset;
+ bytes_left_in_row = vc->vc_size_row - (row_offset % vc->vc_size_row);
+ row_count = (count < bytes_left_in_row / 2)
+ ? count
+ : bytes_left_in_row / 2;
+
+ while (count) {
+ p = screenpos(vc, row_offset, viewed);
+ if (vc->vc_sw->con_invert_region) {
+ vc->vc_sw->con_invert_region(vc, p, row_count);
} else {
- while (cnt--) {
- a = scr_readw(q);
- a = (a & 0x88ff) |
- ((a & 0x7000) >> 4) |
- ((a & 0x0700) << 4);
- scr_writew(a, q);
- q++;
+ u16 *q = p;
+ int cnt = row_count;
+ u16 a;
+
+ if (!vc->vc_can_do_color) {
+ while (cnt--) {
+ a = scr_readw(q);
+ a ^= 0x0800;
+ scr_writew(a, q);
+ q++;
+ }
+ } else if (vc->vc_hi_font_mask == 0x100) {
+ while (cnt--) {
+ a = scr_readw(q);
+ a = (a & 0x11ff) |
+ ((a & 0xe000) >> 4) |
+ ((a & 0x0e00) << 4);
+ scr_writew(a, q);
+ q++;
+ }
+ } else {
+ while (cnt--) {
+ a = scr_readw(q);
+ a = (a & 0x88ff) |
+ ((a & 0x7000) >> 4) |
+ ((a & 0x0700) << 4);
+ scr_writew(a, q);
+ q++;
+ }
}
}
- }
- if (con_should_update(vc))
- do_update_region(vc, (unsigned long) p, count);
+ if (con_should_update(vc))
+ do_update_region(vc, (unsigned long) p, row_count);
+ row_offset += 2 * row_count;
+ count -= row_count;
+ row_count = (count >= vc->vc_cols)
+ ? vc->vc_cols
+ : count;
+ }
notify_update(vc);
}
@@ -927,8 +1236,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;
@@ -989,7 +1307,6 @@ void redraw_screen(struct vc_data *vc, int is_switch)
if (!vc) {
/* strange ... */
- /* printk("redraw_screen: tty %d not allocated ??\n",
new_console+1); */
return;
}
@@ -1004,7 +1321,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 +1333,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 +1347,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,7 +1437,6 @@ int vc_allocate(unsigned int currcons) /* return 0 on
success */
int err;
WARN_CONSOLE_UNLOCKED();
-
if (currcons >= MAX_NR_CONSOLES)
return -ENXIO;
@@ -1147,16 +1471,30 @@ int vc_allocate(unsigned int currcons) /* return 0 on
success */
vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_KERNEL);
if (!vc->vc_screenbuf)
goto err_free;
-
/* If no drivers have overridden us and the user didn't pass a
boot option, default to displaying the cursor */
if (global_cursor_default == -1)
global_cursor_default = 1;
vc_init(vc, vc->vc_rows, vc->vc_cols, 1);
+ if (vc_uniscr_alloc (vc, vc->vc_cols, vc->vc_rows) != 0)
+ goto err_free;
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)vzalloc(console_soft_scrollback_size);
+ 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);
+ vc_uniscr_alloc(vc, vc->vc_cols, vc->vc_rows);
+#endif
return 0;
err_free:
visual_deinit(vc);
@@ -1177,6 +1515,75 @@ static inline int resize_screen(struct vc_data *vc, int
width, int height,
return err;
}
+static int vc_copy_uniscr_to_new_area (struct vc_data *vc,
+ unsigned int new_cols,
+ unsigned int new_rows)
+{
+ unsigned int old_rows = vc->vc_rows, old_cols = vc->vc_cols;
+ uint32_t *old_uniscr_curr = vc->vc_uniscr_curr,
+ *old_uniscr_buf = vc->vc_uniscr_buf;
+ unsigned int old_uniscr_char_size = vc->vc_uniscr_char_size;
+ unsigned int new_lines;
+ unsigned int copy_cols;
+ uint32_t *dest, *src;
+ int res;
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ unsigned int new_uniscr_rows;
+ unsigned int old_lines;
+ unsigned long tmp;
+
+ if (vc->vc_softback_in >= vc->vc_softback_top)
+ tmp = vc->vc_softback_in - vc->vc_softback_top;
+ else
+ tmp = vc->vc_softback_in - vc->vc_softback_top
+ + vc->vc_softback_end - vc->vc_softback_buf;
+ old_lines = tmp / vc->vc_size_row + old_rows;
+
+ if ((res = vc_uniscr_alloc(vc, new_cols, new_rows)) != 0)
+ return res;
+
+ new_uniscr_rows = vc->vc_uniscr_char_size / new_cols + new_rows;
+ new_lines = min(old_lines, new_uniscr_rows);
+ copy_cols = min(old_cols, new_cols);
+
+ dest = vc->vc_uniscr_curr;
+ if (new_lines > old_rows) {
+ dest -= (new_lines - old_rows) * new_cols;
+ while (dest < vc->vc_uniscr_buf) /* Could happen twice. */
+ dest += vc->vc_uniscr_char_size;
+ }
+ src = old_uniscr_curr;
+ if (new_lines > old_rows) {
+ src -= (new_lines - old_rows) * old_cols;
+ while (src < old_uniscr_buf)
+ src += old_uniscr_char_size;
+ }
+#else
+ if ((res = vc_uniscr_alloc(vc, new_cols, new_rows)) != 0)
+ return res;
+
+ new_lines = min(old_rows, new_rows);
+ copy_cols = min(old_cols, new_cols);
+ dest = vc->vc_uniscr_curr;
+ src = old_uniscr_curr;
+#endif
+ if (old_uniscr_buf) {
+ while (new_lines--) {
+ memcpy(dest, src, copy_cols * sizeof(uint32_t));
+ if (new_cols > old_cols)
+ memset32(dest + old_cols, ' ',
+ new_cols - old_cols);
+ UNISCR_PLUS(dest, new_cols);
+ src += old_cols;
+ if (src >= old_uniscr_buf + old_uniscr_char_size)
+ src -= old_uniscr_char_size;
+ }
+ kvfree(old_uniscr_buf);
+ }
+ return 0;
+}
+
/**
* vc_do_resize - resizing method for the tty
* @tty: tty being resized
@@ -1197,12 +1604,19 @@ static int vc_do_resize(struct tty_struct *tty, struct
vc_data *vc,
{
unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0;
unsigned long end;
- unsigned int old_rows, old_row_size, first_copied_row;
- unsigned int new_cols, new_rows, new_row_size, new_screen_size;
+ unsigned int old_rows, old_size_row, first_copied_row;
+ unsigned int new_cols, new_rows, new_size_row, new_screen_size;
unsigned int user;
unsigned short *oldscreen, *newscreen;
- struct uni_screen *new_uniscr = NULL;
-
+ uint32_t *old_uniscr = vc->vc_uniscr_buf;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ unsigned short *d;
+ unsigned long old_softback_buf, new_softback_buf, tmp;
+ unsigned long old_softback_end, new_softback_end;
+ unsigned int old_scrolled_rows, new_scrolled_rows;
+ unsigned int count, copied_scrolled_rows;
+ void *temp_new_softback_buf;
+#endif
WARN_CONSOLE_UNLOCKED();
if (!vc)
@@ -1216,8 +1630,8 @@ static int vc_do_resize(struct tty_struct *tty, struct
vc_data *vc,
new_cols = (cols ? cols : vc->vc_cols);
new_rows = (lines ? lines : vc->vc_rows);
- new_row_size = new_cols << 1;
- new_screen_size = new_row_size * new_rows;
+ new_size_row = new_cols << 1;
+ new_screen_size = new_size_row * new_rows;
if (new_cols == vc->vc_cols && new_rows == vc->vc_rows) {
/*
@@ -1245,61 +1659,91 @@ static int vc_do_resize(struct tty_struct *tty, struct
vc_data *vc,
if (!newscreen)
return -ENOMEM;
- if (get_vc_uniscr(vc)) {
- new_uniscr = vc_uniscr_alloc(new_cols, new_rows);
- if (!new_uniscr) {
- kfree(newscreen);
- return -ENOMEM;
- }
- }
-
if (vc_is_sel(vc))
clear_selection();
old_rows = vc->vc_rows;
- old_row_size = vc->vc_size_row;
+ old_size_row = vc->vc_size_row;
err = resize_screen(vc, new_cols, new_rows, user);
if (err) {
kfree(newscreen);
- vc_uniscr_free(new_uniscr);
+ vc_uniscr_free(vc);
+ vc->vc_uniscr_buf = old_uniscr;
return err;
}
- vc->vc_rows = new_rows;
- vc->vc_cols = new_cols;
- vc->vc_size_row = new_row_size;
- vc->vc_screenbuf_size = new_screen_size;
-
- rlth = min(old_row_size, new_row_size);
- rrem = new_row_size - rlth;
+ rlth = min(old_size_row, new_size_row);
+ rrem = new_size_row - rlth;
old_origin = vc->vc_origin;
new_origin = (long) newscreen;
new_scr_end = new_origin + new_screen_size;
if (vc->state.y > new_rows) {
- if (old_rows - vc->state.y < new_rows) {
+ if (old_rows - new_rows < vc->vc_top + vc->state.y) {
+ if (old_rows - new_rows > vc->vc_top) {
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ con_softback_note(vc, vc->vc_top,
+ old_rows - new_rows -
vc->vc_top);
+#endif
+ vc_uniscr_scroll(vc, 0, old_rows, SM_UP,
+ old_rows - new_rows -
vc->vc_top);
+ }
/*
* Cursor near the bottom, copy contents from the
* bottom of buffer
*/
first_copied_row = (old_rows - new_rows);
} else {
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ con_softback_note(vc, vc->vc_top,
+ vc->state.y - new_rows/2);
+#endif
+ vc_uniscr_scroll(vc, 0, old_rows, SM_UP,
+ vc->state.y - new_rows/2);
/*
* Cursor is in no man's land, copy 1/2 screenful
* from the top and bottom of cursor position
*/
first_copied_row = (vc->state.y - new_rows/2);
}
- old_origin += first_copied_row * old_row_size;
+ old_origin += first_copied_row * old_size_row;
} else
first_copied_row = 0;
- end = old_origin + old_row_size * min(old_rows, new_rows);
+ end = old_origin + old_size_row * min(old_rows, new_rows);
+
+ if ((err = vc_copy_uniscr_to_new_area(vc, new_cols, new_rows)) != 0)
+ return err;
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ concon_set_origin(vc);
+ old_softback_buf = vc->vc_softback_buf;
+ old_softback_end = vc->vc_softback_end;
+ vc->vc_softback_size = console_soft_scrollback_size;
+ temp_new_softback_buf = vzalloc(console_soft_scrollback_size);
+ new_softback_buf = (unsigned long)temp_new_softback_buf;
+ if (!new_softback_buf)
+ return -ENOMEM;
+ new_softback_end = new_softback_buf + console_soft_scrollback_size;
+ d = (unsigned short *)new_softback_buf;
+ while (d != (u16 *)new_softback_end) {
+ scr_writew (0x0020, d);
+ d++;
+ }
+ if (vc->vc_softback_top <= vc->vc_softback_in)
+ tmp = vc->vc_softback_in - vc->vc_softback_top;
+ else
+ tmp = vc->vc_softback_in - vc->vc_softback_top
+ + vc->vc_softback_end - vc->vc_softback_buf;
+ old_scrolled_rows = tmp / vc->vc_size_row;
+ new_scrolled_rows = console_soft_scrollback_size / new_size_row;
+ copied_scrolled_rows = min(old_scrolled_rows, new_scrolled_rows);
+#endif
- vc_uniscr_copy_area(new_uniscr, new_cols, new_rows,
- get_vc_uniscr(vc), rlth/2, first_copied_row,
- min(old_rows, new_rows));
- vc_uniscr_set(vc, new_uniscr);
+ vc->vc_cols = new_cols;
+ vc->vc_rows = new_rows;
+ vc->vc_size_row = new_size_row;
+ vc->vc_screenbuf_size = new_screen_size;
update_attr(vc);
@@ -1309,17 +1753,60 @@ static int vc_do_resize(struct tty_struct *tty, struct
vc_data *vc,
if (rrem)
scr_memsetw((void *)(new_origin + rlth),
vc->vc_video_erase_char, rrem);
- old_origin += old_row_size;
- new_origin += new_row_size;
+ old_origin += old_size_row;
+ new_origin += new_size_row;
}
if (new_scr_end > new_origin)
scr_memsetw((void *)new_origin, vc->vc_video_erase_char,
new_scr_end - new_origin);
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ new_origin = new_softback_buf;
+ if (copied_scrolled_rows) {
+ old_origin = vc->vc_softback_in
+ - copied_scrolled_rows * old_size_row;
+ if (old_origin < vc->vc_softback_buf)
+ old_origin += vc->vc_softback_end
+ - vc->vc_softback_buf;
+ count = copied_scrolled_rows;
+
+ while (count--) {
+ scr_memcpyw((unsigned short *) new_origin,
+ (unsigned short *) old_origin, rlth);
+ if (rrem)
+ scr_memsetw((void *)(new_origin + rlth),
+ vc->vc_video_erase_char, rrem);
+ old_origin += old_size_row;
+ if (old_origin >= old_softback_end)
+ old_origin -= old_softback_end
+ - old_softback_buf;
+ new_origin += new_size_row;
+ }
+ }
+#endif
oldscreen = vc->vc_screenbuf;
vc->vc_screenbuf = newscreen;
vc->vc_screenbuf_size = new_screen_size;
set_origin(vc);
kfree(oldscreen);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ vc->vc_softback_buf = new_softback_buf;
+ vc->vc_softback_end = new_softback_buf
+ + new_scrolled_rows * new_size_row;
+ if (copied_scrolled_rows) {
+ if (new_origin >= vc->vc_softback_end)
+ new_origin -= vc->vc_softback_end - vc->vc_softback_buf;
+ vc->vc_softback_in = new_origin;
+ } else
+ vc->vc_softback_in = new_softback_buf;
+ vc->vc_softback_top = new_softback_buf;
+ if (copied_scrolled_rows
+ && (new_origin == new_softback_buf))
+ vc->vc_softback_top += new_size_row;
+ vc->vc_softback_curr = vc->vc_softback_in;
+ vc->vc_softback_lines = 0; /* Probably redundant. */
+ kvfree((void *)old_softback_buf);
+#endif
/* do part of a reset_terminal() */
vc->vc_top = 0;
@@ -1400,8 +1887,8 @@ struct vc_data *vc_deallocate(unsigned int currcons)
visual_deinit(vc);
con_free_unimap(vc);
put_pid(vc->vt_pid);
- vc_uniscr_set(vc, NULL);
kfree(vc->vc_screenbuf);
+ vc->vc_uniscr_buf = NULL;
vc_cons[currcons].d = NULL;
}
return vc;
@@ -1646,7 +2133,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;
@@ -1658,7 +2145,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;
}
@@ -2928,6 +3415,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++;
@@ -3094,11 +3587,8 @@ static void vt_console_print(struct console *co, const
char *b, unsigned count)
if (kmsg_console && vc_cons_allocated(kmsg_console - 1))
vc = vc_cons[kmsg_console - 1].d;
- if (!vc_cons_allocated(fg_console)) {
- /* impossible */
- /* printk("vt_console_print: tty %d not allocated ??\n",
currcons+1); */
+ if (!vc_cons_allocated(fg_console))
goto quit;
- }
if (vc->vc_mode != KD_TEXT)
goto quit;
@@ -3143,7 +3633,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:
@@ -3364,7 +3858,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();
}
@@ -3385,6 +3883,14 @@ 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);
+ if (!vc->vc_uniscr_buf)
+ ret = vc_uniscr_alloc(vc, vc->vc_cols, vc->vc_rows);
+ if (ret)
+ goto unlock;
+#endif
+
/* Still being freed */
if (vc->port.tty) {
ret = -ERESTARTSYS;
@@ -3440,7 +3946,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);
@@ -3526,6 +4032,7 @@ static int __init con_init(void)
vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT);
vc_init(vc, vc->vc_rows, vc->vc_cols,
currcons || !vc->vc_sw->con_save_screen);
+ vc_uniscr_alloc(vc, vc->vc_cols, vc->vc_rows);
}
currcons = fg_console = 0;
master_display_fg = vc = vc_cons[currcons].d;
@@ -4139,7 +4646,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;
@@ -4440,7 +4947,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);
@@ -4740,11 +5250,17 @@ EXPORT_SYMBOL_GPL(screen_glyph);
u32 screen_glyph_unicode(const struct vc_data *vc, int n)
{
- struct uni_screen *uniscr = get_vc_uniscr(vc);
+ int y = n / vc->vc_cols, x = n % vc->vc_cols;
+ uint32_t *ln = vc->vc_uniscr_curr;
- if (uniscr)
- return uniscr->lines[n / vc->vc_cols][n % vc->vc_cols];
- return inverse_translate(vc, screen_glyph(vc, n * 2), true);
+ if (vc->vc_uniscr_curr) {
+ UNISCR_PLUS(ln, y * vc->vc_cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ UNISCR_MINUS(ln, vc->vc_softback_lines * vc->vc_cols);
+#endif
+ return ln[x];
+ }
+ return inverse_translate(vc, screen_glyph(vc, n * 2), 1);
}
EXPORT_SYMBOL_GPL(screen_glyph_unicode);
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index 22cea5082ac4..7d56ea0018fc 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -99,6 +99,44 @@ config FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION
If unsure, select n.
+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_SOFT_SCROLLBACK_GPM
+ bool "Enable a working GPM for scrolled back scrollback buffer in
System RAM"
+ depends on FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+ default y
+ help
+ This option buffers up Unicode characters corresponding to the glyphs
+ displayed by the scrollback buffer. This enables the GPM mouse driver
+ (or similar) to copy characters from a scrolled back buffer.
+
+ A buffer is created for each framebuffer console, this buffer being
+ approximately twice as big as the buffer size specified in
+ FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE.
+
+ If unsure, say 'Y'.
+
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 14a7d404062c..7b523d586e22 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -609,6 +609,28 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct
fb_info *info,
erase,
vc->vc_size_row * logo_lines);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ {
+ uint32_t *s = vc->vc_uniscr_curr, *d = vc->vc_uniscr_curr;
+ unsigned int i = vc->vc_rows - logo_lines;
+
+ UNISCR_PLUS(d, vc->vc_rows * vc->vc_cols);
+ UNISCR_PLUS(s, (vc->vc_rows - logo_lines) * vc->vc_cols);
+ while (i--) {
+ UNISCR_MINUS(d, vc->vc_cols);
+ UNISCR_MINUS(s, vc->vc_cols);
+ memcpy(d, s, vc->vc_cols * sizeof (uint32_t));
+ }
+ i = logo_lines;
+ d = vc->vc_uniscr_curr;
+ while (i--) {
+ memset32(d, ' ', vc->vc_cols);
+ UNISCR_PLUS(d, vc->vc_cols);
+ }
+ vc->vc_uniscr_curr = d;
+ }
+#endif
+
if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) {
fbcon_clear_margins(vc, 0);
update_screen(vc);
@@ -1262,6 +1284,25 @@ static void fbcon_clear(struct vc_data *vc, int sy, int
sx, int height,
* bitmap stretched into the margin area.
*/
fbcon_clear_margins(vc, 0);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+ {
+ uint32_t *s = vc->vc_uniscr_curr, *d =
vc->vc_uniscr_curr;
+ unsigned int i = vc->vc_rows - logo_lines;
+
+ UNISCR_PLUS(d, (vc->vc_rows - logo_lines) *
vc->vc_cols);
+ UNISCR_PLUS(s, (vc->vc_rows - 2 * logo_lines) *
vc->vc_cols);
+ while (i--) {
+ UNISCR_MINUS(d, vc->vc_cols);
+ UNISCR_MINUS(s, vc->vc_cols);
+ memcpy (d, s, vc->vc_cols * sizeof (uint32_t));
+ }
+ i = logo_lines;
+ while (i--) {
+ UNISCR_MINUS(d, vc->vc_cols);
+ memset32(d, ' ', vc->vc_cols);
+ }
+ }
+#endif
}
/* Split blits that cross physical y_wrap boundary */
@@ -2069,6 +2110,7 @@ static int fbcon_switch(struct vc_data *vc)
struct fbcon_ops *ops;
struct fbcon_display *p = &fb_display[vc->vc_num];
struct fb_var_screeninfo var;
+ unsigned short *d, *s;
int i, ret, prev_console;
info = fbcon_info_from_console(vc->vc_num);
@@ -2078,8 +2120,21 @@ static int fbcon_switch(struct vc_data *vc)
struct vc_data *conp2 = vc_cons[logo_shown].d;
if (conp2->vc_top == logo_lines
- && conp2->vc_bottom == conp2->vc_rows)
+ && conp2->vc_bottom == conp2->vc_rows) {
+ /* Scroll the bottom part of the screen up to fill the
+ * logo lines. */
+ i = conp2->vc_bottom - conp2->vc_top;
+ d = (unsigned short *)conp2->vc_origin;
+ s = (unsigned short *)(conp2->vc_origin + logo_lines *
conp2->vc_size_row);
+ while (i--) {
+ scr_memcpyw(d, s, conp2->vc_size_row);
+ d += conp2->vc_cols;
+ s += conp2->vc_cols;
+ }
+ scr_memsetw(d, conp2->vc_video_erase_char,
+ conp2->vc_size_row * logo_lines);
conp2->vc_top = 0;
+ }
logo_shown = FBCON_LOGO_CANSHOW;
}
@@ -3171,6 +3226,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 1518568aaf0f..f928509e3773 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 */
@@ -159,7 +170,9 @@ struct vc_data {
struct vc_data **vc_display_fg; /* [!] Ptr to var holding fg
console for this display */
struct uni_pagedict *uni_pagedict;
struct uni_pagedict **uni_pagedict_loc; /* [!] Location of uni_pagedict
variable for this console */
- struct uni_screen *vc_uni_screen; /* unicode screen content */
+ uint32_t *vc_uniscr_buf; /* Address of unicode screen content */
+ unsigned int vc_uniscr_char_size; /* Size of *vc-uniscr_buf in 32-bit
chars */
+ uint32_t *vc_uniscr_curr; /* Pos of first char of (unscrolled)
screen */
/* additional information is in vt_kern.h */
};
@@ -194,4 +207,22 @@ extern void vc_SAK(struct work_struct *work);
bool con_is_visible(const struct vc_data *vc);
+/* Macros for wraparound in the uniscr buffer. POS must be a uint32_t pointer
+ * variable which is expected to be within the confines of vc->vc_uniscr_buf
+ * and vc->vc_uniscr_buf + vc->vc_uniscr_char_size. OFFSET must be a positive
+ * distance measured in uint32_t's, which when added to or subtracted from POS
+ * won't be too far away from the buffer. */
+#define UNISCR_PLUS(pos,offset)
\
+ do { \
+ pos += offset; \
+ if (pos >= vc->vc_uniscr_buf + vc->vc_uniscr_char_size) \
+ pos -= vc->vc_uniscr_char_size; \
+ } while (0)
+#define UNISCR_MINUS(pos,offset) \
+ do { \
+ pos -= offset; \
+ if (pos < vc->vc_uniscr_buf) \
+ pos += vc->vc_uniscr_char_size; \
+ } while (0)
+
#endif /* _LINUX_CONSOLE_STRUCT_H */
diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h
index c1f5aebef170..97ecca06eceb 100644
--- a/include/linux/vt_kern.h
+++ b/include/linux/vt_kern.h
@@ -114,6 +114,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,