diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index b8f5bc19416d..58332d1dd923 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,38 @@ 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 { + /* OLD STOUGH, 2023-01-26 */ + /* scrolled_expanse = vc->vc_softback_in - vc->vc_softback_curr; */ + /* if (scrolled_expanse < 0) */ + /* scrolled_expanse += vc->vc_softback_end */ + /* - vc->vc_softback_buf; */ + /* NEW STOUGH, 2023-01-26 */ + 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; + /* END OF NEW STOUGH */ + 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 +330,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 +352,110 @@ 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); - - if (uniscr) - uniscr->lines[vc->state.y][vc->state.x] = uc; + uint32_t *pos; + + if (vc->vc_uniscr_buf) { + pos = vc->vc_uniscr_curr + + vc->state.y * vc->vc_cols + + vc->state.x; + if (pos >= vc_uniscr_buf_end(vc)) + pos -= vc->vc_uniscr_char_size; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + pos -= vc->vc_softback_lines * vc->vc_cols; + if (pos < vc->vc_uniscr_buf) + pos += vc->vc_uniscr_char_size; +#endif + *pos = uc; + } } static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr) { - struct uni_screen *uniscr = get_vc_uniscr(vc); + unsigned int x = vc->state.x, y = vc->state.y, cols = vc->vc_cols; + uint32_t *ln = vc->vc_uniscr_curr + y * cols; - if (uniscr) { - char32_t *ln = uniscr->lines[vc->state.y]; - unsigned int x = vc->state.x, cols = vc->vc_cols; - - memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(*ln)); + if (vc->vc_uniscr_buf) { + if (ln >= vc_uniscr_buf_end(vc)) + ln -= vc->vc_uniscr_char_size; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + ln -= vc->vc_softback_lines * vc->vc_cols; + if (ln < vc->vc_uniscr_buf) + ln += vc->vc_uniscr_char_size; +#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 + y * cols; - memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(*ln)); + if (vc->vc_uniscr_buf) { + if (ln >= vc_uniscr_buf_end(vc)) + ln -= vc->vc_uniscr_char_size; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + ln -= vc->vc_softback_lines * vc->vc_cols; + if (ln < vc->vc_uniscr_buf) + ln += vc->vc_uniscr_char_size; +#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 + vc->state.y * vc->vc_cols; + if (ln >= vc_uniscr_buf_end(vc)) + ln -= vc->vc_uniscr_char_size; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + ln -= vc->vc_softback_lines * vc->vc_cols; + if (ln < vc->vc_uniscr_buf) + ln += vc->vc_uniscr_char_size; +#endif memset32(&ln[x], ' ', nr); } } @@ -418,77 +463,88 @@ 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 + y * cols; - while (nr--) - memset32(uniscr->lines[y++], ' ', cols); +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + ln -= vc->vc_softback_lines * cols; + if (ln < vc->vc_uniscr_buf) + ln += vc->vc_uniscr_char_size; +#endif + while (nr--) { + if (ln >= vc_uniscr_buf_end(vc)) + ln -= vc->vc_uniscr_char_size; + memset32(ln, ' ', cols); + 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) { - 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; + 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) { + vc->vc_uniscr_curr += nr * cols; + if (vc->vc_uniscr_curr >= vc_uniscr_buf_end(vc)) + vc->vc_uniscr_curr -= vc->vc_uniscr_char_size; + d = nr; + clear = vc->vc_rows - nr; + } else if (dir == SM_DOWN && t == 0 && b == vc->vc_rows - nr) { + vc->vc_uniscr_curr -= nr * cols; + if (vc->vc_uniscr_curr < vc->vc_uniscr_buf) + vc->vc_uniscr_curr += vc->vc_uniscr_char_size; + d = 0; + } else if (dir == SM_UP) { + sz = b - t; + src = vc->vc_uniscr_curr + t * cols; + if (src >= vc_uniscr_buf_end(vc)) + src -= vc->vc_uniscr_char_size; + dest = src - nr * cols; + if (dest < vc->vc_uniscr_buf) + dest += vc->vc_uniscr_char_size; + i = b - t; + while (i--) { + memcpy(dest, src, cols * sizeof(uint32_t)); + src += cols; + if (src >= vc_uniscr_buf_end(vc)) + src -= vc->vc_uniscr_char_size; + dest += cols; + if (dest >= vc_uniscr_buf_end(vc)) + dest -= vc->vc_uniscr_char_size; } - uniscr->lines[t + j] = tmp; + d = nr; + clear = b - nr; + } else { + src = vc->vc_uniscr_curr + b * cols; + if (src >= vc_uniscr_buf_end(vc)) + src -= vc->vc_uniscr_char_size; + dest = src + nr * cols; + if (dest >= vc_uniscr_buf_end(vc)) + dest -= vc->vc_uniscr_char_size; + i = b - t; + while (i--) { + src -= cols; + if (src < vc->vc_uniscr_buf) + src += vc->vc_uniscr_char_size; + dest -= cols; + if (dest < vc->vc_uniscr_buf) + dest += vc->vc_uniscr_char_size; + memcpy(dest, src, cols * sizeof(uint32_t)); + } + 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 +556,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 +567,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 +582,15 @@ 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 + y * vc->vc_cols; + if (line >= vc_uniscr_buf_end(vc)) + line -= vc->vc_uniscr_char_size; 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 +602,26 @@ 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 + row * vc->vc_cols + col); + if (pos >= vc_uniscr_buf_end(vc)) + pos -= vc->vc_uniscr_char_size; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + pos -= vc->vc_softback_lines * vc->vc_cols; + if (pos < vc->vc_uniscr_buf) + pos += vc->vc_uniscr_char_size; +#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 +631,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 = vc->vc_softback_size / vc->vc_size_row; + if (l > 5) + { + vc->vc_softback_end = vc->vc_softback_buf + l * vc->vc_size_row; + vc->vc_softback_top = vc->vc_softback_buf; + } + else + /* Smaller scrollback makes no sense, and 0 would screw + the operation totally */ + vc->vc_softback_top = 0; +} + +static int concon_set_origin(struct vc_data *vc) +{ + if (vc->vc_softback_lines) + concon_scrolldelta(vc, vc->vc_softback_lines); + return 0; +} + +#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row) + +static void con_redraw_softback(struct vc_data *vc, /* struct display *p, */ + long delta) +{ + int count = vc->vc_rows; + unsigned short *d, *s; + unsigned long n; + int line = 0; + + if (!vc->vc_softback_lines) + vc->vc_char_at_pos = scr_readw((u16 *)vc->vc_pos); + + d = (u16 *) vc->vc_softback_curr; + if (d == (u16 *) vc->vc_softback_in) + d = (u16 *) vc->vc_origin; + n = vc->vc_softback_curr + delta * vc->vc_size_row; + vc->vc_softback_lines -= delta; + if (delta < 0) { + if (vc->vc_softback_curr < vc->vc_softback_top + && n < vc->vc_softback_buf) { + n += vc->vc_softback_end - vc->vc_softback_buf; + if (n < vc->vc_softback_top) { + vc->vc_softback_lines -= + (vc->vc_softback_top - n) / vc->vc_size_row; + n = vc->vc_softback_top; + } + } else if (vc->vc_softback_curr >= vc->vc_softback_top + && n < vc->vc_softback_top) { + vc->vc_softback_lines -= + (vc->vc_softback_top - n) / vc->vc_size_row; + n = vc->vc_softback_top; + } + } else { + if (vc->vc_softback_curr > vc->vc_softback_in + && n >= vc->vc_softback_end) { + n += vc->vc_softback_buf - vc->vc_softback_end; + if (n > vc->vc_softback_in) { + n = vc->vc_softback_in; + vc->vc_softback_lines = 0; + } + } else if (vc->vc_softback_curr <= vc->vc_softback_in + && n > vc->vc_softback_in) { + n = vc->vc_softback_in; + vc->vc_softback_lines = 0; } } + if (n == vc->vc_softback_curr) + return; + vc->vc_softback_curr = n; + /* If we're not scrolled any more, restore the character to the cursor + * position */ + if (!vc->vc_softback_lines) + scr_writew(vc->vc_char_at_pos, (u16 *)vc->vc_pos); + s = (u16 *) vc->vc_softback_curr; + if (s == (u16 *) vc->vc_softback_in) + s = (u16 *) vc->vc_origin; + while (count--) { + unsigned short *start; + unsigned short *le; + unsigned short c; + int x = 0; + unsigned short attr = 1; + + start = s; + le = advance_row(s, 1); + /* Temporarily overwrite the character at the cursor position + * with the one we actually want to see on the screen. */ + if (count == vc->vc_rows - vc->state.y - 1) + { + c = scr_readw((u16 *)(s + vc->state.x)); + scr_writew(c, (u16 *)vc->vc_pos); + vc->vc_sw->con_putcs + (vc, (u16 *)vc->vc_pos, 1, line, vc->state.x); + } + do { + c = scr_readw(s); + if (attr != (c & 0xff00)) { + attr = c & 0xff00; + if (s > start) { + vc->vc_sw->con_putcs( + vc, start, s - start, + line, x); + x += s - start; + start = s; + } + } + if (c == scr_readw(d)) { + if (s > start) { + vc->vc_sw->con_putcs( + vc, start, s - start, + line, x); + x += s - start + 1; + start = s + 1; + } else { + x++; + start++; + } + } + s++; + d++; + } while (s < le); + if (s > start) + vc->vc_sw->con_putcs(vc, start, s - start, line, x); + line++; + if (d == (u16 *) vc->vc_softback_end) + d = (u16 *) vc->vc_softback_buf; + if (d == (u16 *) vc->vc_softback_in) + d = (u16 *) vc->vc_origin; + if (s == (u16 *) vc->vc_softback_end) + s = (u16 *) vc->vc_softback_buf; + if (s == (u16 *) vc->vc_softback_in) + s = (u16 *) vc->vc_origin; + } +} + +static inline void con_softback_note(struct vc_data *vc, int t, + int count) +{ + unsigned short *p; + + if (vc->vc_num != fg_console) + return; + p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row); + + while (count) { + scr_memcpyw((u16 *) vc->vc_softback_in, p, vc->vc_size_row); + count--; + p = advance_row(p, 1); + vc->vc_softback_in += vc->vc_size_row; + if (vc->vc_softback_in == vc->vc_softback_end) + vc->vc_softback_in = vc->vc_softback_buf; + if (vc->vc_softback_in == vc->vc_softback_top) { + vc->vc_softback_top += vc->vc_size_row; + if (vc->vc_softback_top == vc->vc_softback_end) + vc->vc_softback_top = vc->vc_softback_buf; + } + } + vc->vc_softback_curr = vc->vc_softback_in; } +void concon_scrolldelta(struct vc_data *vc, int lines) +{ + /* struct display *disp = &fb_display[fg_console]; */ + /* int offset, limit, scrollback_old; */ + + if (vc->vc_softback_top) { + if (vc->vc_num != fg_console) + return; + if (vc->vc_mode != KD_TEXT || !lines) + return; +#if 0 + if (logo_shown >= 0) { + struct vc_data *conp2 = vc_cons[logo_shown].d; + + if (conp2->vc_top == logo_lines + && conp2->vc_bottom == conp2->vc_rows) + conp2->vc_top = 0; + if (logo_shown == vc->vc_num) { + unsigned long p, q; + int i; + + p = vc->vc_softback_in; + q = vc->vc_origin + + logo_lines * vc->vc_size_row; + for (i = 0; i < logo_lines; i++) { + if (p == vc->vc_softback_top) + break; + if (p == vc->vc_softback_buf) + p = vc->vc_softback_end; + p -= vc->vc_size_row; + q -= vc->vc_size_row; + scr_memcpyw((u16 *) q, (u16 *) p, + vc->vc_size_row); + } + vc->vc_softback_in = vc->vc_softback_curr = p; + update_region(vc, vc->vc_origin, + logo_lines * vc->vc_cols); + } + logo_shown = FBCON_LOGO_CANSHOW; + } +#endif + vc->vc_sw->con_cursor(vc, CM_ERASE /* | CM_SOFTBACK */); + con_redraw_softback(vc, /* disp, */ lines); + if (!vc->vc_softback_lines) + vc->vc_sw->con_cursor(vc, CM_DRAW /* | CM_SOFTBACK */); + } +} + +#endif /* CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK */ static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b, enum con_scroll dir, unsigned int nr) @@ -626,6 +906,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 +925,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 +1015,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 +1024,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 +1088,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 +1279,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 +1350,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 +1364,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 +1376,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 +1390,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); @@ -1110,14 +1478,70 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ struct vt_notifier_param param; struct vc_data *vc; int err; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK + unsigned long new_end; + unsigned long new_in, new_top; + unsigned long in_residue; + unsigned short *d; +#endif WARN_CONSOLE_UNLOCKED(); - if (currcons >= MAX_NR_CONSOLES) return -ENXIO; if (vc_cons[currcons].d) + { +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK + vc = vc_cons[currcons].d; + if (!vc->vc_softback_size) { + /* vc was partially initialized by __init. */ + vc->vc_softback_size = console_soft_scrollback_size; + vc->vc_softback_buf = + (unsigned long)/*k*/vzalloc(vc->vc_softback_size/*, GFP_KERNEL*/); + if (vc->vc_softback_buf) { + vc->vc_softback_in = vc->vc_softback_top = + vc->vc_softback_curr = vc->vc_softback_buf; + vc->vc_softback_lines = 0; + con_update_softback(vc); + vc_uniscr_alloc(vc, vc->vc_cols, vc->vc_rows); + } + } + if (vc->vc_softback_buf + && (vc->vc_softback_end - vc->vc_softback_buf) % vc->vc_size_row) { + new_end = vc->vc_softback_buf + vc->vc_softback_size + - (vc->vc_softback_size % vc->vc_size_row); + new_top = vc->vc_softback_top + - (vc->vc_softback_top - vc->vc_softback_buf) + % vc->vc_size_row; + in_residue = (vc->vc_softback_in - vc->vc_softback_buf) + % vc->vc_size_row; + new_in = vc->vc_softback_in + + (in_residue ? vc->vc_size_row - in_residue : 0); + if (new_in == new_end) + new_in = vc->vc_softback_buf; + if (new_top == new_in) + new_top += vc->vc_size_row; + else if ((new_in > new_top + && (new_in - new_top <= vc->vc_size_row))) + new_top += 2 * vc->vc_size_row; + if (new_top >= new_end) + new_top -= (new_end - vc->vc_softback_buf); + vc->vc_softback_lines = 0; + vc->vc_softback_curr = new_in; + + d = (u16 *)vc->vc_softback_in; + vc->vc_softback_end = new_end; + vc->vc_softback_in = new_in; + vc->vc_softback_top = new_top; + while (d != (u16 *)new_in) { + scr_writew (0x0020, d); /* How about non-VGA? */ + if (++d == (u16 *)new_end) + d = (u16 *)vc->vc_softback_buf; + } + } +#endif return 0; + } /* due to the granularity of kmalloc, we waste some memory here */ /* the alloc is done in two steps, to optimize the common situation @@ -1157,6 +1581,19 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ vcs_make_sysfs(currcons); atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, ¶m); +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK + vc->vc_softback_size = console_soft_scrollback_size; + err = -ENOMEM; + vc->vc_softback_buf = + (unsigned long)/*k*/vzalloc(vc->vc_softback_size/*, GFP_KERNEL*/); + if (!vc->vc_softback_buf) + goto err_free; + vc->vc_softback_in = vc->vc_softback_top = vc->vc_softback_curr = + vc->vc_softback_buf; + vc->vc_softback_lines = 0; + con_update_softback(vc); + vc_uniscr_alloc(vc, vc->vc_cols, vc->vc_rows); +#endif return 0; err_free: visual_deinit(vc); @@ -1177,6 +1614,78 @@ 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 > new_rows) { + dest -= (new_lines - new_rows) * new_cols; + if (dest < vc->vc_uniscr_buf) + dest += vc->vc_uniscr_char_size; + } + src = old_uniscr_curr; + if (new_lines > new_rows) { + src -= (new_lines - new_rows) * old_cols; + if (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); + dest += new_cols; + if (dest >= vc->vc_uniscr_buf + + vc->vc_uniscr_char_size) + dest -= vc->vc_uniscr_char_size; + 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 +1706,18 @@ 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; - + unsigned short *oldscreen, *newscreen, *d; + uint32_t *old_uniscr = vc->vc_uniscr_buf; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK + 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 +1731,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 +1760,93 @@ 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; - } +#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(vc->vc_softback_size); + new_softback_buf = (unsigned long)temp_new_softback_buf; + if (!new_softback_buf) + return -ENOMEM; + new_softback_end = new_softback_buf + vc->vc_softback_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 = vc->vc_softback_size / new_size_row; + copied_scrolled_rows = min(old_scrolled_rows, new_scrolled_rows); +#endif 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) { +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK + con_softback_note(vc, 0, + old_rows - new_rows); +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + vc_uniscr_scroll(vc, 0, old_rows, SM_UP, + old_rows - new_rows); +#endif +#endif /* * 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, 0, + vc->state.y - new_rows/2); +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + vc_uniscr_scroll(vc, 0, old_rows, SM_UP, + vc->state.y - new_rows/2); +#endif +#endif /* * 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); - 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); + if ((err = vc_copy_uniscr_to_new_area(vc, new_cols, new_rows)) != 0) + return err; + + 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 +1856,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 /* (unsigned long)newscreen STOUGH, 2023-01-26 */; + 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 +1990,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 +2236,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 +2248,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 +3518,12 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co param.vc = vc; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK + /* Undo any soft scrolling - and do + not pass through this function. */ + concon_set_origin (vc); +#endif + while (!tty->flow.stopped && count) { int orig = *buf; buf++; @@ -3094,11 +3690,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 +3736,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 +3961,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 +3986,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 +4049,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); @@ -4140,7 +4749,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 +5050,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 +5353,19 @@ 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 + y * vc->vc_cols; - if (uniscr) - return uniscr->lines[n / vc->vc_cols][n % vc->vc_cols]; + if (vc->vc_uniscr_curr) { + if (ln >= vc_uniscr_buf_end(vc)) + ln -= vc->vc_uniscr_char_size; +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + ln -= vc->vc_softback_lines * vc->vc_cols; + if (ln < vc->vc_uniscr_buf) + ln += vc->vc_uniscr_char_size; +#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..a4fab71c5853 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -3152,6 +3152,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..281da9b3797f 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,11 @@ 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 */ +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM + 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 */ +#endif /* additional information is in vt_kern.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,