diff -u -r --exclude-from=linux-2.6.12/Documentation/dontdiff linux-2.6.12/drivers/char/selection.c linux-2.6.13-rc1-mm1-devel/drivers/char/selection.c --- linux-2.6.12/drivers/char/selection.c 2005-06-17 13:48:29.000000000 -0600 +++ linux-2.6.13-rc1-mm1-devel/drivers/char/selection.c 2005-07-12 01:22:01.000000000 -0600 @@ -118,6 +118,9 @@ poke_blanked_console(); + if (vc->vc_cumdelta) + vc->vc_sw->con_scrolldelta(vc, 0); + { unsigned short xs, ys, xe, ye; if (!access_ok(VERIFY_READ, sel, sizeof(*sel))) diff -u -r --exclude-from=linux-2.6.12/Documentation/dontdiff linux-2.6.12/drivers/char/vt.c linux-2.6.13-rc1-mm1-devel/drivers/char/vt.c --- linux-2.6.12/drivers/char/vt.c 2005-07-12 01:45:46.000000000 -0600 +++ linux-2.6.13-rc1-mm1-devel/drivers/char/vt.c 2005-07-12 02:03:27.000000000 -0600 @@ -93,6 +93,7 @@ #include #include #include +#include #include #include @@ -145,6 +146,7 @@ static void console_callback(void *ignored); static void blank_screen_t(unsigned long dummy); static void set_palette(struct vc_data *vc); +static void evict_scrollback(struct vc_data *vc, unsigned int count); static int printable; /* Is console ready for printing? */ @@ -194,6 +196,11 @@ */ static int scrollback_delta; +int vt_max_scrollback_dfl = 32000; +char vt_scrollback_ready = 0; /* global scrollback enable */ +static unsigned int scrback_gfpmask = GFP_KERNEL | __GFP_NORETRY + | __GFP_NOWARN; + /* * Hook so that the power management routines can (un)blank * the console on our behalf. @@ -245,6 +252,190 @@ schedule_work(&console_work); } +int vt_set_scrollback(struct vt_scrollparms *sc) +{ + struct vc_data *vc; + + if (sc->vp_con > MAX_NR_CONSOLES || !vc_cons[sc->vp_con].d) + return -EINVAL; + + vc = vc_cons[sc->vp_con].d; + if (!vc->vc_support_scrollback) + return -ENOSYS; + + if (sc->vp_size == vc->vc_maxscrollback) + return 0; + vc->vc_sw->con_scrolldelta(vc, 0); + /* shrink? */ + if ((sc->vp_size < vc->vc_maxscrollback) && + (vc->vc_scrollbacksz > sc->vp_size)) + evict_scrollback(vc, vc->vc_scrollbacksz - sc->vp_size); + vc->vc_maxscrollback = sc->vp_size; + + return(0); +} +EXPORT_SYMBOL(vt_set_scrollback); + +unsigned int vt_get_scrollback(unsigned char **rb, + struct vc_data *vc, unsigned int needlines) +{ + static char *retbuf = NULL; + static int retbufsz; + char *retbufptr; + struct vc_scrback *sc; + unsigned int bytes_needed, orig_bytes_needed, skip; + + orig_bytes_needed = bytes_needed = needlines * vc->vc_size_row; + + if (!retbuf || retbufsz != bytes_needed) { + if (retbuf) + kfree(retbuf); + retbufsz = bytes_needed; + retbuf = kmalloc(bytes_needed, scrback_gfpmask); + } + + *rb = retbufptr = retbuf; + if (vc->vc_scrollbacksz > vc->vc_cumdelta * vc->vc_size_row) + skip = vc->vc_scrollbacksz - + (vc->vc_cumdelta * vc->vc_size_row); + else + skip = 0; + + list_for_each_entry(sc, &vc->vc_scrollback, vsc_list) { + unsigned int buf_inuse = sc->vsc_bufsize - sc->vsc_bufleft; + unsigned int copycount; + + if (!skip) { + copycount = min(buf_inuse, bytes_needed); + scr_memcpyw(retbufptr, sc->vsc_buf, copycount); + } else if (skip < buf_inuse) { + copycount = min(buf_inuse - skip, bytes_needed); + scr_memcpyw(retbufptr, sc->vsc_buf + skip, copycount); + skip = 0; + } else { + skip -= buf_inuse; + continue; + } + + bytes_needed -= copycount; + retbufptr += copycount; + + if (!bytes_needed) + return(needlines); + } + return((orig_bytes_needed - bytes_needed) / vc->vc_size_row); +} +EXPORT_SYMBOL(vt_get_scrollback); + +static void evict_scrollback(struct vc_data *vc, unsigned int count) +{ + struct vc_scrback *sc, *tmpsc; + unsigned int buf_used; + + list_for_each_entry_safe(sc, tmpsc, &vc->vc_scrollback, vsc_list) { + buf_used = sc->vsc_bufsize - sc->vsc_bufleft; + + vc->vc_scrollbacksz -= buf_used; + kfree(sc->vsc_buf); + list_del(&sc->vsc_list); + kfree(sc); + if (buf_used >= count) + break; + count -= buf_used; + } +} + +void vt_clear_scrollback(struct vc_data *vc) +{ + if (vc->vc_cumdelta) + vc->vc_sw->con_scrolldelta(vc, 0); + if(vc->vc_scrollbacksz) + evict_scrollback(vc, vc->vc_scrollbacksz); +} +EXPORT_SYMBOL(vt_clear_scrollback); + +int vt_update_cumdelta(struct vc_data *c, int delta) +{ + int old_cumdelta = c->vc_cumdelta; + + if (!delta || delta > c->vc_cumdelta) { + c->vc_cumdelta = 0; + } else { + c->vc_cumdelta -= delta; + c->vc_cumdelta = min(c->vc_cumdelta, + c->vc_scrollbacksz / c->vc_size_row); + } + + if (c->vc_cumdelta == old_cumdelta) + return 0; + return 1; +} +EXPORT_SYMBOL(vt_update_cumdelta); + +static void save_scrollback(struct vc_data *vc, unsigned short *p, + unsigned int count) +{ + int copycount; + struct vc_scrback *sc; + + if (!count || !vc->vc_maxscrollback) + return; + if (count > vc->vc_maxscrollback) { + p += (count - vc->vc_maxscrollback); + count = vc->vc_maxscrollback; + } + if ((vc->vc_scrollbacksz + count) > vc->vc_maxscrollback) + evict_scrollback(vc, count - + (vc->vc_maxscrollback - vc->vc_scrollbacksz)); + if (!vc->vc_scrollbacksz) { + /* no scrollback at all, start everything */ + sc = kmalloc(sizeof(struct vc_scrback), scrback_gfpmask); + if (!sc) + return; /* out of memory - oh well */ + sc->vsc_bufleft = sc->vsc_bufsize = + min(vc->vc_screenbuf_size, vc->vc_maxscrollback); + sc->vsc_buf = sc->vsc_bufptr = + kmalloc(sc->vsc_bufleft, scrback_gfpmask); + if (!sc->vsc_buf) { + kfree(sc); + return; + } + list_add(&sc->vsc_list, &vc->vc_scrollback); + } else { + /* if list is empty this just initializes sc, if list is not + * empty it points sc to the last entry */ + sc = list_entry(vc->vc_scrollback.prev, struct vc_scrback, + vsc_list); + } + + while (count) { + /* out of bufspace, alloc a new one */ + if (!sc->vsc_bufleft) { + sc = kmalloc(sizeof(struct vc_scrback), + scrback_gfpmask); + if (!sc) + return; /* drop scrollback */ + sc->vsc_bufleft = sc->vsc_bufsize = + min(vc->vc_screenbuf_size, + vc->vc_maxscrollback - vc->vc_scrollbacksz); + sc->vsc_buf = sc->vsc_bufptr = + kmalloc(sc->vsc_bufleft, scrback_gfpmask); + if (!sc->vsc_buf) { + kfree(sc); + return; + } + list_add_tail(&sc->vsc_list, &vc->vc_scrollback); + } + copycount = min(count, sc->vsc_bufleft); + scr_memcpyw(sc->vsc_bufptr, p, copycount); + vc->vc_scrollbacksz += copycount; + sc->vsc_bufleft -= copycount; + sc->vsc_bufptr += copycount; + p += copycount; + count -= copycount; + } +} + static void scrup(struct vc_data *vc, unsigned int t, unsigned int b, int nr) { unsigned short *d, *s; @@ -253,6 +444,10 @@ nr = b - t - 1; if (b > vc->vc_rows || t >= b || nr < 1) return; + if (vt_scrollback_ready && vc->vc_support_scrollback) + if(!t) + save_scrollback(vc, (unsigned short *)vc->vc_origin, + nr * vc->vc_size_row); if (CON_IS_VISIBLE(vc) && vc->vc_sw->con_scroll(vc, t, b, SM_UP, nr)) return; d = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t); @@ -677,12 +872,17 @@ vc->vc_hi_font_mask = 0; vc->vc_complement_mask = 0; vc->vc_can_do_color = 0; + vc->vc_maxscrollback = vt_max_scrollback_dfl; + vc->vc_support_scrollback = 0; vc->vc_sw->con_init(vc, init); if (!vc->vc_complement_mask) vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; vc->vc_s_complement_mask = vc->vc_complement_mask; vc->vc_size_row = vc->vc_cols << 1; vc->vc_screenbuf_size = vc->vc_rows * vc->vc_size_row; + vc->vc_scrollbacksz = 0; + vc->vc_scrollback_needsave = 1; + INIT_LIST_HEAD(&vc->vc_scrollback); } int vc_allocate(unsigned int currcons) /* return 0 on success */ @@ -724,7 +924,8 @@ return 0; } -static inline int resize_screen(struct vc_data *vc, int width, int height) +static inline int resize_screen(struct vc_data *vc, unsigned int width, + unsigned int height) { /* Resizes the resolution of the display adapater */ int err = 0; @@ -1947,8 +2148,11 @@ charmask = himask ? 0x1ff : 0xff; /* undraw cursor first */ - if (IS_FG(vc)) + if (IS_FG(vc)) { + if (vc->vc_cumdelta) + vc->vc_sw->con_scrolldelta(vc, 0); hide_cursor(vc); + } while (!tty->stopped && count) { int orig = *buf; @@ -2102,7 +2306,7 @@ if (scrollback_delta) { struct vc_data *vc = vc_cons[fg_console].d; clear_selection(); - if (vc->vc_mode == KD_TEXT) + if (vc->vc_mode == KD_TEXT && vc->vc_sw->con_scrolldelta) vc->vc_sw->con_scrolldelta(vc, scrollback_delta); scrollback_delta = 0; } @@ -2160,8 +2364,11 @@ goto quit; /* undraw cursor first */ - if (IS_FG(vc)) + if (IS_FG(vc)) { + if (vc->vc_cumdelta) + vc->vc_sw->con_scrolldelta(vc, 0); hide_cursor(vc); + } start = (ushort *)vc->vc_pos; @@ -2877,6 +3084,8 @@ void poke_blanked_console(void) { + struct vc_data *vc = vc_cons[fg_console].d; + WARN_CONSOLE_UNLOCKED(); /* Add this so we quickly catch whoever might call us in a non @@ -2893,7 +3102,7 @@ del_timer(&console_timer); blank_timer_expired = 0; - if (ignore_poke || !vc_cons[fg_console].d || vc_cons[fg_console].d->vc_mode == KD_GRAPHICS) + if (ignore_poke || !vc || vc->vc_mode == KD_GRAPHICS) return; if (console_blanked) unblank_screen(); diff -u -r --exclude-from=linux-2.6.12/Documentation/dontdiff linux-2.6.12/drivers/char/vt_ioctl.c linux-2.6.13-rc1-mm1-devel/drivers/char/vt_ioctl.c --- linux-2.6.12/drivers/char/vt_ioctl.c 2005-07-12 01:45:46.000000000 -0600 +++ linux-2.6.13-rc1-mm1-devel/drivers/char/vt_ioctl.c 2005-07-12 02:04:33.000000000 -0600 @@ -37,6 +37,7 @@ static char vt_dont_switch; extern struct tty_driver *console_driver; +extern char vt_scrollback_ready; #define VT_IS_IN_USE(i) (console_driver->ttys[i] && console_driver->ttys[i]->count) #define VT_BUSY(i) (VT_IS_IN_USE(i) || i == fg_console || vc_cons[i].d == sel_cons) @@ -650,6 +651,55 @@ return 0; } + case VT_ENABLESCROLLBACK: + { + if (!perm) + return -EPERM; + vt_scrollback_ready = 1; + return 0; + } + + case VT_SETSCROLLBACK: + { + struct vt_scrollparms sp; + int ret; + + if (!perm) + return -EPERM; + if (!vt_scrollback_ready) + return -ENOSYS; + if (copy_from_user(&sp, up, sizeof(struct vt_scrollparms))) + return -EFAULT; + acquire_console_sem(); + ret = vt_set_scrollback(&sp); + release_console_sem(); + return(ret); + } + + case VT_GETSCROLLBACK: + { + struct vt_scrollparms sp; + + if (!vt_scrollback_ready) + return -ENOSYS; + + if (copy_from_user(&sp, up, sizeof(struct vt_scrollparms))) + return -EFAULT; + acquire_console_sem(); + if (sp.vp_con > MAX_NR_CONSOLES || !vc_cons[sp.vp_con].d || + !vc_cons[sp.vp_con].d->vc_support_scrollback) { + release_console_sem(); + return -EINVAL; + } + + sp.vp_size = vc_cons[sp.vp_con].d->vc_maxscrollback; + release_console_sem(); + + if (copy_to_user(up, &sp, sizeof(struct vt_scrollparms))) + return -EFAULT; + return 0; + } + case VT_SETMODE: { struct vt_mode tmp; diff -u -r --exclude-from=linux-2.6.12/Documentation/dontdiff linux-2.6.12/drivers/video/console/vgacon.c linux-2.6.13-rc1-mm1-devel/drivers/video/console/vgacon.c --- linux-2.6.12/drivers/video/console/vgacon.c 2005-07-12 01:45:48.000000000 -0600 +++ linux-2.6.13-rc1-mm1-devel/drivers/video/console/vgacon.c 2005-07-12 02:07:23.000000000 -0600 @@ -57,6 +57,7 @@ static int cursor_size_lastfrom; static int cursor_size_lastto; static struct vgastate state; +extern int vt_max_scrollback_dfl; #define BLANK 0x0020 @@ -339,6 +340,10 @@ c->vc_scan_lines = vga_scan_lines; c->vc_font.height = vga_video_font_height; c->vc_complement_mask = 0x7700; + + /* we are willing to use vt-provided scrollback */ + c->vc_support_scrollback = 1; + if (vga_512_chars) c->vc_hi_font_mask = 0x0800; p = *c->vc_uni_pagedir_loc; @@ -365,6 +370,8 @@ con_free_unimap(c); } c->vc_uni_pagedir_loc = &c->vc_uni_pagedir; + if (c->vc_scrollbacksz) + vt_clear_scrollback(c); con_set_default_unimap(c); } @@ -962,34 +969,44 @@ #endif -static int vgacon_scrolldelta(struct vc_data *c, int lines) +static int vgacon_scrolldelta(struct vc_data *c, int delta) { - if (!lines) /* Turn scrollback off */ - c->vc_visible_origin = c->vc_origin; - else { - int margin = c->vc_size_row * 4; - int ul, we, p, st; - - if (vga_rolled_over > - (c->vc_scr_end - vga_vram_base) + margin) { - ul = c->vc_scr_end - vga_vram_base; - we = vga_rolled_over + c->vc_size_row; - } else { - ul = 0; - we = vga_vram_size; + unsigned char *sp; + int gotback; + + if (!c->vc_scrollbacksz) + return(1); + + if (!vt_update_cumdelta(c, delta)) + return 1; + + if (!c->vc_cumdelta) { /* Turn scrollback off */ + scr_memcpyw((void *) c->vc_origin, (void *) c->vc_screenbuf, + c->vc_screenbuf_size); + c->vc_scrollback_needsave = 1; + c->vc_cumdelta = 0; + vgacon_cursor(c, CM_DRAW); + } else { + vgacon_cursor(c, CM_ERASE); + gotback = vt_get_scrollback(&sp, c, c->vc_rows); + + if (!gotback) + return 1; + + if (c->vc_scrollback_needsave) { + scr_memcpyw((void *)c->vc_screenbuf, + (void *)c->vc_origin, c->vc_screenbuf_size); + c->vc_scrollback_needsave = 0; } - p = (c->vc_visible_origin - vga_vram_base - ul + we) % we + - lines * c->vc_size_row; - st = (c->vc_origin - vga_vram_base - ul + we) % we; - if (st < 2 * margin) - margin = 0; - if (p < margin) - p = 0; - if (p > st - margin) - p = st; - c->vc_visible_origin = vga_vram_base + (p + ul) % we; + + if (gotback < c->vc_rows) + scr_memcpyw((void *)(sp + c->vc_size_row * gotback), + (void *) c->vc_screenbuf, + (c->vc_rows - gotback) * c->vc_size_row); + + scr_memcpyw((void *)c->vc_origin, + (void *)sp, c->vc_screenbuf_size); } - vga_set_mem_top(c); return 1; } @@ -1021,9 +1038,12 @@ /* We can't copy in more then the size of the video buffer, * or we'll be copying in VGA BIOS */ - if (!vga_is_gfx) + if (!vga_is_gfx) { + if(c->vc_cumdelta) + vgacon_scrolldelta(c, 0); scr_memcpyw((u16 *) c->vc_screenbuf, (u16 *) c->vc_origin, c->vc_screenbuf_size > vga_vram_size ? vga_vram_size : c->vc_screenbuf_size); + } } static int vgacon_scroll(struct vc_data *c, int t, int b, int dir, @@ -1035,7 +1055,7 @@ if (t || b != c->vc_rows || vga_is_gfx) return 0; - if (c->vc_origin != c->vc_visible_origin) + if (c->vc_cumdelta) vgacon_scrolldelta(c, 0); if (!vga_hardscroll_enabled || lines >= c->vc_rows / 2) @@ -1054,7 +1074,7 @@ c->vc_origin += delta; scr_memsetw((u16 *) (c->vc_origin + c->vc_screenbuf_size - delta), c->vc_video_erase_char, - delta); + delta); } else { if (oldo - delta < vga_vram_base) { scr_memmovew((u16 *) (vga_vram_end - @@ -1076,6 +1096,17 @@ return 1; } +/* + * we don't really do anything here except erase all the old scrollback + * that's stored at the wrong resolution + */ +static int vgacon_resize(struct vc_data *c, unsigned int width, + unsigned int height) +{ + if (c->vc_scrollbacksz) + vt_clear_scrollback(c); + return 0; +} /* * The console `switch' structure for the VGA based console @@ -1083,6 +1114,8 @@ static int vgacon_dummy(struct vc_data *c) { + if (c->vc_cumdelta) + vgacon_scrolldelta(c, 0); return 0; } @@ -1100,6 +1133,7 @@ .con_scroll = vgacon_scroll, .con_bmove = DUMMY, .con_switch = vgacon_switch, + .con_resize = vgacon_resize, .con_blank = vgacon_blank, .con_font_set = vgacon_font_set, .con_font_get = vgacon_font_get, diff -u -r --exclude-from=linux-2.6.12/Documentation/dontdiff linux-2.6.12/include/linux/console_struct.h linux-2.6.13-rc1-mm1-devel/include/linux/console_struct.h --- linux-2.6.12/include/linux/console_struct.h 2005-06-17 13:48:29.000000000 -0600 +++ linux-2.6.13-rc1-mm1-devel/include/linux/console_struct.h 2005-07-12 02:08:06.000000000 -0600 @@ -13,6 +13,14 @@ #define NPAR 16 +struct vc_scrback { + struct list_head vsc_list; + unsigned char *vsc_buf; + unsigned char *vsc_bufptr; + unsigned int vsc_bufleft; + unsigned int vsc_bufsize; +}; + struct vc_data { unsigned short vc_num; /* Console number */ unsigned int vc_cols; /* [#] Console size */ @@ -26,6 +34,13 @@ const struct consw *vc_sw; unsigned short *vc_screenbuf; /* In-memory character/attribute buffer */ unsigned int vc_screenbuf_size; + /* scrollback */ + struct list_head vc_scrollback; + unsigned int vc_scrollbacksz; /* no. bytes in scrollback */ + unsigned int vc_maxscrollback; /* max scrollback size */ + int vc_cumdelta; /* cumulative delta */ + short vc_scrollback_needsave; + int vc_support_scrollback : 1; /* driver support? */ unsigned char vc_mode; /* KD_TEXT, ... */ /* attributes for all characters on screen */ unsigned char vc_attr; /* Current attributes */ diff -u -r --exclude-from=linux-2.6.12/Documentation/dontdiff linux-2.6.12/include/linux/vt.h linux-2.6.13-rc1-mm1-devel/include/linux/vt.h --- linux-2.6.12/include/linux/vt.h 2005-06-17 13:48:29.000000000 -0600 +++ linux-2.6.13-rc1-mm1-devel/include/linux/vt.h 2005-07-10 23:12:13.000000000 -0600 @@ -51,4 +51,12 @@ #define VT_LOCKSWITCH 0x560B /* disallow vt switching */ #define VT_UNLOCKSWITCH 0x560C /* allow vt switching */ +#define VT_ENABLESCROLLBACK 0x560D /* turn on global scrollback */ +#define VT_SETSCROLLBACK 0x560E /* change scrollback properties */ +#define VT_GETSCROLLBACK 0x560F /* get scrollback properties */ + +struct vt_scrollparms { + unsigned int vp_con; + unsigned int vp_size; +}; #endif /* _LINUX_VT_H */ diff -u -r --exclude-from=linux-2.6.12/Documentation/dontdiff linux-2.6.12/include/linux/vt_kern.h linux-2.6.13-rc1-mm1-devel/include/linux/vt_kern.h --- linux-2.6.12/include/linux/vt_kern.h 2005-06-17 13:48:29.000000000 -0600 +++ linux-2.6.13-rc1-mm1-devel/include/linux/vt_kern.h 2005-07-12 02:08:23.000000000 -0600 @@ -46,6 +46,12 @@ void scrollfront(struct vc_data *vc, int lines); void update_region(struct vc_data *vc, unsigned long start, int count); void redraw_screen(struct vc_data *vc, int is_switch); +int vt_set_scrollback(struct vt_scrollparms *sc); +void vt_clear_scrollback(struct vc_data *vc); +unsigned int vt_get_scrollback(unsigned char **rb, struct vc_data *vc, + unsigned int needlines); +int vt_update_cumdelta(struct vc_data *c, int delta); + #define update_screen(x) redraw_screen(x, 0) #define switch_screen(x) redraw_screen(x, 1)