schistory.c revision 215034
148104Syokota/*-
248104Syokota * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
348104Syokota * Copyright (c) 1992-1998 S�ren Schmidt
448104Syokota * All rights reserved.
548104Syokota *
648104Syokota * Redistribution and use in source and binary forms, with or without
748104Syokota * modification, are permitted provided that the following conditions
848104Syokota * are met:
948104Syokota * 1. Redistributions of source code must retain the above copyright
1048104Syokota *    notice, this list of conditions and the following disclaimer,
1148104Syokota *    without modification, immediately at the beginning of the file.
1248104Syokota * 2. Redistributions in binary form must reproduce the above copyright
1348104Syokota *    notice, this list of conditions and the following disclaimer in the
1448104Syokota *    documentation and/or other materials provided with the distribution.
1548104Syokota * 3. The name of the author may not be used to endorse or promote products
1648104Syokota *    derived from this software without specific prior written permission.
1748104Syokota *
1848104Syokota * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
1948104Syokota * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2048104Syokota * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2148104Syokota * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2248104Syokota * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2348104Syokota * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2448104Syokota * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2548104Syokota * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2648104Syokota * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2748104Syokota * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2848104Syokota */
2948104Syokota
30119420Sobrien#include <sys/cdefs.h>
31119420Sobrien__FBSDID("$FreeBSD: head/sys/dev/syscons/schistory.c 215034 2010-11-09 10:59:09Z brucec $");
32119420Sobrien
3348104Syokota#include "opt_syscons.h"
3448104Syokota
3548104Syokota#ifndef SC_NO_HISTORY
3648104Syokota
3748104Syokota#include <sys/param.h>
3848104Syokota#include <sys/systm.h>
3951404Syokota#include <sys/conf.h>
4066834Sphk#include <sys/consio.h>
4148104Syokota#include <sys/tty.h>
4248104Syokota#include <sys/kernel.h>
4348104Syokota#include <sys/malloc.h>
4448104Syokota
45153072Sru#if defined(__sparc64__) || defined(__powerpc__)
46119379Sjake#include <machine/sc_machdep.h>
47119379Sjake#else
4856043Syokota#include <machine/pc/display.h>
49119379Sjake#endif
5048104Syokota
5148104Syokota#include <dev/syscons/syscons.h>
5248104Syokota
53102148Speter/*
54102148Speter * XXX Placeholder.
55215034Sbrucec * This calculations should be dynamically scaled by number of separate sc
56102148Speter * devices.  A base value of 'extra_history_size' should be defined for
57102148Speter * each syscons unit, and added and subtracted from the dynamic
58102148Speter * 'extra_history_size' as units are added and removed.  This way, each time
59102148Speter * a new syscons unit goes online, extra_history_size is automatically bumped.
60102148Speter */
61102148Speter#define	MAXSC	1
62102148Speter
6348104Syokota#if !defined(SC_MAX_HISTORY_SIZE)
64102148Speter#define SC_MAX_HISTORY_SIZE	(1000 * MAXCONS * MAXSC)
6548104Syokota#endif
6648104Syokota
6748104Syokota#if !defined(SC_HISTORY_SIZE)
6848104Syokota#define SC_HISTORY_SIZE		(ROW * 4)
6948104Syokota#endif
7048104Syokota
71102148Speter#if (SC_HISTORY_SIZE * MAXCONS * MAXSC) > SC_MAX_HISTORY_SIZE
7248104Syokota#undef SC_MAX_HISTORY_SIZE
73102148Speter#define SC_MAX_HISTORY_SIZE	(SC_HISTORY_SIZE * MAXCONS * MAXSC)
7448104Syokota#endif
7548104Syokota
7648104Syokota/* local variables */
7748104Syokotastatic int		extra_history_size
7848104Syokota				= SC_MAX_HISTORY_SIZE - SC_HISTORY_SIZE*MAXCONS;
7948104Syokota
8048104Syokota/* local functions */
8151394Syokotastatic void copy_history(sc_vtb_t *from, sc_vtb_t *to);
8248104Syokotastatic void history_to_screen(scr_stat *scp);
8348104Syokota
8448104Syokota/* allocate a history buffer */
8548104Syokotaint
8648667Syokotasc_alloc_history_buffer(scr_stat *scp, int lines, int prev_ysize, int wait)
8748104Syokota{
8848104Syokota	/*
89215034Sbrucec	 * syscons unconditionally allocates buffers up to
9048104Syokota	 * SC_HISTORY_SIZE lines or scp->ysize lines, whichever
9148104Syokota	 * is larger. A value greater than that is allowed,
9248104Syokota	 * subject to extra_history_size.
9348104Syokota	 */
9448104Syokota	sc_vtb_t *history;
9548667Syokota	sc_vtb_t *prev_history;
9648104Syokota	int cur_lines;				/* current buffer size */
9748104Syokota	int min_lines;				/* guaranteed buffer size */
9848667Syokota	int delta;				/* lines to put back */
9948104Syokota
10048104Syokota	if (lines <= 0)
10148104Syokota		lines = SC_HISTORY_SIZE;	/* use the default value */
10248667Syokota
10348667Syokota	/* make it at least as large as the screen size */
10448104Syokota	lines = imax(lines, scp->ysize);
10548104Syokota
10648667Syokota	/* remove the history buffer while we update it */
10748667Syokota	history = prev_history = scp->history;
10848104Syokota	scp->history = NULL;
10948667Syokota
11048667Syokota	/* calculate the amount of lines to put back to extra_history_size */
11148667Syokota	delta = 0;
11248667Syokota	if (prev_history) {
11348104Syokota		cur_lines = sc_vtb_rows(history);
11448667Syokota		min_lines = imax(SC_HISTORY_SIZE, prev_ysize);
11548104Syokota		if (cur_lines > min_lines)
11648667Syokota			delta = cur_lines - min_lines;
11748104Syokota	}
11848104Syokota
119215034Sbrucec	/* lines up to min_lines are always allowed. */
12048667Syokota	min_lines = imax(SC_HISTORY_SIZE, scp->ysize);
12148667Syokota	if (lines > min_lines) {
12248667Syokota		if (lines - min_lines > extra_history_size + delta) {
12348667Syokota			/* too many lines are requested */
12448667Syokota			scp->history = prev_history;
12548667Syokota			return EINVAL;
12648667Syokota		}
12748667Syokota	}
12848667Syokota
12951394Syokota	/* allocate a new buffer */
13051394Syokota	history = (sc_vtb_t *)malloc(sizeof(*history),
13151394Syokota				     M_DEVBUF,
132111119Simp				     (wait) ? M_WAITOK : M_NOWAIT);
13348667Syokota	if (history != NULL) {
13448667Syokota		if (lines > min_lines)
13548667Syokota			extra_history_size -= lines - min_lines;
13656043Syokota		/* XXX error check? */
13748104Syokota		sc_vtb_init(history, VTB_RINGBUFFER, scp->xsize, lines,
13848104Syokota			    NULL, wait);
13956043Syokota		/* FIXME: XXX no good? */
14051394Syokota		sc_vtb_clear(history, scp->sc->scr_map[0x20],
14156043Syokota			     SC_NORM_ATTR << 8);
14251394Syokota		if (prev_history != NULL)
14351394Syokota			copy_history(prev_history, history);
14451394Syokota		scp->history_pos = sc_vtb_tail(history);
14551394Syokota	} else {
14651394Syokota		scp->history_pos = 0;
14748667Syokota	}
14848667Syokota
14951394Syokota	/* destroy the previous buffer */
15051394Syokota	if (prev_history != NULL) {
15151394Syokota		extra_history_size += delta;
15251394Syokota		sc_vtb_destroy(prev_history);
15354387Syokota		free(prev_history, M_DEVBUF);
15451394Syokota	}
15551394Syokota
15648104Syokota	scp->history = history;
15748104Syokota
15848104Syokota	return 0;
15948104Syokota}
16048104Syokota
16151394Syokotastatic void
16251394Syokotacopy_history(sc_vtb_t *from, sc_vtb_t *to)
16351394Syokota{
16451394Syokota	int lines;
16551394Syokota	int cols;
16651394Syokota	int cols1;
16751394Syokota	int cols2;
16851394Syokota	int pos;
16951394Syokota	int i;
17051394Syokota
17151394Syokota	lines = sc_vtb_rows(from);
17251394Syokota	cols1 = sc_vtb_cols(from);
17351394Syokota	cols2 = sc_vtb_cols(to);
17451394Syokota	cols = imin(cols1, cols2);
17551394Syokota	pos = sc_vtb_tail(from);
17651394Syokota	for (i = 0; i < lines; ++i) {
17751394Syokota		sc_vtb_append(from, pos, to, cols);
17851394Syokota		if (cols < cols2)
17951394Syokota			sc_vtb_seek(to, sc_vtb_pos(to,
18051394Syokota						   sc_vtb_tail(to),
18151394Syokota						   cols2 - cols));
18251394Syokota		pos = sc_vtb_pos(from, pos, cols1);
18351394Syokota	}
18451394Syokota}
18551394Syokota
18648667Syokotavoid
18748667Syokotasc_free_history_buffer(scr_stat *scp, int prev_ysize)
18848667Syokota{
18948667Syokota	sc_vtb_t *history;
19048667Syokota	int cur_lines;				/* current buffer size */
19148667Syokota	int min_lines;				/* guaranteed buffer size */
19248667Syokota
19348667Syokota	history = scp->history;
19448667Syokota	scp->history = NULL;
19548667Syokota	if (history == NULL)
19648667Syokota		return;
19748667Syokota
19848667Syokota	cur_lines = sc_vtb_rows(history);
19948667Syokota	min_lines = imax(SC_HISTORY_SIZE, prev_ysize);
20056043Syokota	extra_history_size += (cur_lines > min_lines) ?
20156043Syokota				  cur_lines - min_lines : 0;
20248667Syokota
20348667Syokota	sc_vtb_destroy(history);
20448667Syokota	free(history, M_DEVBUF);
20548667Syokota}
20648667Syokota
20748104Syokota/* copy entire screen into the top of the history buffer */
20848104Syokotavoid
20948104Syokotasc_hist_save(scr_stat *scp)
21048104Syokota{
21148104Syokota	sc_vtb_append(&scp->vtb, 0, scp->history, scp->xsize*scp->ysize);
21248104Syokota	scp->history_pos = sc_vtb_tail(scp->history);
21348104Syokota}
21448104Syokota
21548104Syokota/* restore the screen by copying from the history buffer */
21648104Syokotaint
21748104Syokotasc_hist_restore(scr_stat *scp)
21848104Syokota{
21948104Syokota	int ret;
22048104Syokota
22148104Syokota	if (scp->history_pos != sc_vtb_tail(scp->history)) {
22248104Syokota		scp->history_pos = sc_vtb_tail(scp->history);
22348104Syokota		history_to_screen(scp);
22448104Syokota		ret =  0;
22548104Syokota	} else {
22648104Syokota		ret = 1;
22748104Syokota	}
22848104Syokota	sc_vtb_seek(scp->history, sc_vtb_pos(scp->history,
22948104Syokota					     sc_vtb_tail(scp->history),
23048104Syokota					     -scp->xsize*scp->ysize));
23148104Syokota	return ret;
23248104Syokota}
23348104Syokota
23448104Syokota/* copy screen-full of saved lines */
23548104Syokotastatic void
23648104Syokotahistory_to_screen(scr_stat *scp)
23748104Syokota{
23848104Syokota	int pos;
23948104Syokota	int i;
24048104Syokota
24148104Syokota	pos = scp->history_pos;
24248104Syokota	for (i = 1; i <= scp->ysize; ++i) {
24348104Syokota		pos = sc_vtb_pos(scp->history, pos, -scp->xsize);
24448104Syokota		sc_vtb_copy(scp->history, pos,
24548104Syokota			    &scp->vtb, scp->xsize*(scp->ysize - i),
24648104Syokota			    scp->xsize);
24748104Syokota	}
24848104Syokota	mark_all(scp);
24948104Syokota}
25048104Syokota
25148104Syokota/* go to the tail of the history buffer */
25248104Syokotavoid
25348104Syokotasc_hist_home(scr_stat *scp)
25448104Syokota{
25548104Syokota	scp->history_pos = sc_vtb_tail(scp->history);
25648104Syokota	history_to_screen(scp);
25748104Syokota}
25848104Syokota
25948104Syokota/* go to the top of the history buffer */
26048104Syokotavoid
26148104Syokotasc_hist_end(scr_stat *scp)
26248104Syokota{
26348104Syokota	scp->history_pos = sc_vtb_pos(scp->history, sc_vtb_tail(scp->history),
26448104Syokota				      scp->xsize*scp->ysize);
26548104Syokota	history_to_screen(scp);
26648104Syokota}
26748104Syokota
26848104Syokota/* move one line up */
26948104Syokotaint
27048104Syokotasc_hist_up_line(scr_stat *scp)
27148104Syokota{
27248104Syokota	if (sc_vtb_pos(scp->history, scp->history_pos, -(scp->xsize*scp->ysize))
27348104Syokota	    == sc_vtb_tail(scp->history))
27448104Syokota		return -1;
27548104Syokota	scp->history_pos = sc_vtb_pos(scp->history, scp->history_pos,
27648104Syokota				      -scp->xsize);
27748104Syokota	history_to_screen(scp);
27848104Syokota	return 0;
27948104Syokota}
28048104Syokota
28148104Syokota/* move one line down */
28248104Syokotaint
28348104Syokotasc_hist_down_line(scr_stat *scp)
28448104Syokota{
28548104Syokota	if (scp->history_pos == sc_vtb_tail(scp->history))
28648104Syokota		return -1;
28748104Syokota	scp->history_pos = sc_vtb_pos(scp->history, scp->history_pos,
28848104Syokota				      scp->xsize);
28948104Syokota	history_to_screen(scp);
29048104Syokota	return 0;
29148104Syokota}
29248104Syokota
29348104Syokotaint
294181905Sedsc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
29548104Syokota{
29648104Syokota	scr_stat *scp;
29748667Syokota	int error;
29848104Syokota
29948104Syokota	switch (cmd) {
30048104Syokota
30148104Syokota	case CONS_HISTORY:  	/* set history size */
302181905Sed		scp = SC_STAT(tp);
30348104Syokota		if (*(int *)data <= 0)
30448104Syokota			return EINVAL;
30548104Syokota		if (scp->status & BUFFER_SAVED)
30648104Syokota			return EBUSY;
30748667Syokota		DPRINTF(5, ("lines:%d, ysize:%d, pool:%d\n",
30848667Syokota			    *(int *)data, scp->ysize, extra_history_size));
30948667Syokota		error = sc_alloc_history_buffer(scp,
31048104Syokota					       imax(*(int *)data, scp->ysize),
31148667Syokota					       scp->ysize, TRUE);
31248667Syokota		DPRINTF(5, ("error:%d, rows:%d, pool:%d\n", error,
31348667Syokota			    sc_vtb_rows(scp->history), extra_history_size));
31448667Syokota		return error;
31577251Sdd
31677251Sdd	case CONS_CLRHIST:
317181905Sed		scp = SC_STAT(tp);
31877251Sdd		sc_vtb_clear(scp->history, scp->sc->scr_map[0x20],
31977251Sdd		    SC_NORM_ATTR << 8);
32077251Sdd		return 0;
32148104Syokota	}
32248104Syokota
32348104Syokota	return ENOIOCTL;
32448104Syokota}
32548104Syokota
32648104Syokota#endif /* SC_NO_HISTORY */
327