148104Syokota/*-
248104Syokota * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
3230132Suqs * 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$");
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
45239696Sgonzo#if defined(__arm__) || defined(__mips__) || \
46239696Sgonzo	defined(__powerpc__) || defined(__sparc64__)
47119379Sjake#include <machine/sc_machdep.h>
48119379Sjake#else
4956043Syokota#include <machine/pc/display.h>
50119379Sjake#endif
5148104Syokota
5248104Syokota#include <dev/syscons/syscons.h>
5348104Syokota
54102148Speter/*
55102148Speter * XXX Placeholder.
56215034Sbrucec * This calculations should be dynamically scaled by number of separate sc
57102148Speter * devices.  A base value of 'extra_history_size' should be defined for
58102148Speter * each syscons unit, and added and subtracted from the dynamic
59102148Speter * 'extra_history_size' as units are added and removed.  This way, each time
60102148Speter * a new syscons unit goes online, extra_history_size is automatically bumped.
61102148Speter */
62102148Speter#define	MAXSC	1
63102148Speter
6448104Syokota#if !defined(SC_MAX_HISTORY_SIZE)
65102148Speter#define SC_MAX_HISTORY_SIZE	(1000 * MAXCONS * MAXSC)
6648104Syokota#endif
6748104Syokota
6848104Syokota#if !defined(SC_HISTORY_SIZE)
6948104Syokota#define SC_HISTORY_SIZE		(ROW * 4)
7048104Syokota#endif
7148104Syokota
72102148Speter#if (SC_HISTORY_SIZE * MAXCONS * MAXSC) > SC_MAX_HISTORY_SIZE
7348104Syokota#undef SC_MAX_HISTORY_SIZE
74102148Speter#define SC_MAX_HISTORY_SIZE	(SC_HISTORY_SIZE * MAXCONS * MAXSC)
7548104Syokota#endif
7648104Syokota
7748104Syokota/* local variables */
7848104Syokotastatic int		extra_history_size
7948104Syokota				= SC_MAX_HISTORY_SIZE - SC_HISTORY_SIZE*MAXCONS;
8048104Syokota
8148104Syokota/* local functions */
8251394Syokotastatic void copy_history(sc_vtb_t *from, sc_vtb_t *to);
8348104Syokotastatic void history_to_screen(scr_stat *scp);
8448104Syokota
8548104Syokota/* allocate a history buffer */
8648104Syokotaint
8748667Syokotasc_alloc_history_buffer(scr_stat *scp, int lines, int prev_ysize, int wait)
8848104Syokota{
8948104Syokota	/*
90215034Sbrucec	 * syscons unconditionally allocates buffers up to
9148104Syokota	 * SC_HISTORY_SIZE lines or scp->ysize lines, whichever
9248104Syokota	 * is larger. A value greater than that is allowed,
9348104Syokota	 * subject to extra_history_size.
9448104Syokota	 */
9548104Syokota	sc_vtb_t *history;
9648667Syokota	sc_vtb_t *prev_history;
9748104Syokota	int cur_lines;				/* current buffer size */
9848104Syokota	int min_lines;				/* guaranteed buffer size */
9948667Syokota	int delta;				/* lines to put back */
10048104Syokota
10148104Syokota	if (lines <= 0)
10248104Syokota		lines = SC_HISTORY_SIZE;	/* use the default value */
10348667Syokota
10448667Syokota	/* make it at least as large as the screen size */
10548104Syokota	lines = imax(lines, scp->ysize);
10648104Syokota
10748667Syokota	/* remove the history buffer while we update it */
10848667Syokota	history = prev_history = scp->history;
10948104Syokota	scp->history = NULL;
11048667Syokota
11148667Syokota	/* calculate the amount of lines to put back to extra_history_size */
11248667Syokota	delta = 0;
11348667Syokota	if (prev_history) {
11448104Syokota		cur_lines = sc_vtb_rows(history);
11548667Syokota		min_lines = imax(SC_HISTORY_SIZE, prev_ysize);
11648104Syokota		if (cur_lines > min_lines)
11748667Syokota			delta = cur_lines - min_lines;
11848104Syokota	}
11948104Syokota
120215034Sbrucec	/* lines up to min_lines are always allowed. */
12148667Syokota	min_lines = imax(SC_HISTORY_SIZE, scp->ysize);
12248667Syokota	if (lines > min_lines) {
12348667Syokota		if (lines - min_lines > extra_history_size + delta) {
12448667Syokota			/* too many lines are requested */
12548667Syokota			scp->history = prev_history;
12648667Syokota			return EINVAL;
12748667Syokota		}
12848667Syokota	}
12948667Syokota
13051394Syokota	/* allocate a new buffer */
13151394Syokota	history = (sc_vtb_t *)malloc(sizeof(*history),
13251394Syokota				     M_DEVBUF,
133111119Simp				     (wait) ? M_WAITOK : M_NOWAIT);
13448667Syokota	if (history != NULL) {
13548667Syokota		if (lines > min_lines)
13648667Syokota			extra_history_size -= lines - min_lines;
13756043Syokota		/* XXX error check? */
13848104Syokota		sc_vtb_init(history, VTB_RINGBUFFER, scp->xsize, lines,
13948104Syokota			    NULL, wait);
14056043Syokota		/* FIXME: XXX no good? */
14151394Syokota		sc_vtb_clear(history, scp->sc->scr_map[0x20],
14256043Syokota			     SC_NORM_ATTR << 8);
14351394Syokota		if (prev_history != NULL)
14451394Syokota			copy_history(prev_history, history);
14551394Syokota		scp->history_pos = sc_vtb_tail(history);
14651394Syokota	} else {
14751394Syokota		scp->history_pos = 0;
14848667Syokota	}
14948667Syokota
15051394Syokota	/* destroy the previous buffer */
15151394Syokota	if (prev_history != NULL) {
15251394Syokota		extra_history_size += delta;
15351394Syokota		sc_vtb_destroy(prev_history);
15454387Syokota		free(prev_history, M_DEVBUF);
15551394Syokota	}
15651394Syokota
15748104Syokota	scp->history = history;
15848104Syokota
15948104Syokota	return 0;
16048104Syokota}
16148104Syokota
16251394Syokotastatic void
16351394Syokotacopy_history(sc_vtb_t *from, sc_vtb_t *to)
16451394Syokota{
16551394Syokota	int lines;
16651394Syokota	int cols;
16751394Syokota	int cols1;
16851394Syokota	int cols2;
16951394Syokota	int pos;
17051394Syokota	int i;
17151394Syokota
17251394Syokota	lines = sc_vtb_rows(from);
17351394Syokota	cols1 = sc_vtb_cols(from);
17451394Syokota	cols2 = sc_vtb_cols(to);
17551394Syokota	cols = imin(cols1, cols2);
17651394Syokota	pos = sc_vtb_tail(from);
17751394Syokota	for (i = 0; i < lines; ++i) {
17851394Syokota		sc_vtb_append(from, pos, to, cols);
17951394Syokota		if (cols < cols2)
18051394Syokota			sc_vtb_seek(to, sc_vtb_pos(to,
18151394Syokota						   sc_vtb_tail(to),
18251394Syokota						   cols2 - cols));
18351394Syokota		pos = sc_vtb_pos(from, pos, cols1);
18451394Syokota	}
18551394Syokota}
18651394Syokota
18748667Syokotavoid
18848667Syokotasc_free_history_buffer(scr_stat *scp, int prev_ysize)
18948667Syokota{
19048667Syokota	sc_vtb_t *history;
19148667Syokota	int cur_lines;				/* current buffer size */
19248667Syokota	int min_lines;				/* guaranteed buffer size */
19348667Syokota
19448667Syokota	history = scp->history;
19548667Syokota	scp->history = NULL;
19648667Syokota	if (history == NULL)
19748667Syokota		return;
19848667Syokota
19948667Syokota	cur_lines = sc_vtb_rows(history);
20048667Syokota	min_lines = imax(SC_HISTORY_SIZE, prev_ysize);
20156043Syokota	extra_history_size += (cur_lines > min_lines) ?
20256043Syokota				  cur_lines - min_lines : 0;
20348667Syokota
20448667Syokota	sc_vtb_destroy(history);
20548667Syokota	free(history, M_DEVBUF);
20648667Syokota}
20748667Syokota
20848104Syokota/* copy entire screen into the top of the history buffer */
20948104Syokotavoid
21048104Syokotasc_hist_save(scr_stat *scp)
21148104Syokota{
21248104Syokota	sc_vtb_append(&scp->vtb, 0, scp->history, scp->xsize*scp->ysize);
21348104Syokota	scp->history_pos = sc_vtb_tail(scp->history);
21448104Syokota}
21548104Syokota
21648104Syokota/* restore the screen by copying from the history buffer */
21748104Syokotaint
21848104Syokotasc_hist_restore(scr_stat *scp)
21948104Syokota{
22048104Syokota	int ret;
22148104Syokota
22248104Syokota	if (scp->history_pos != sc_vtb_tail(scp->history)) {
22348104Syokota		scp->history_pos = sc_vtb_tail(scp->history);
22448104Syokota		history_to_screen(scp);
22548104Syokota		ret =  0;
22648104Syokota	} else {
22748104Syokota		ret = 1;
22848104Syokota	}
22948104Syokota	sc_vtb_seek(scp->history, sc_vtb_pos(scp->history,
23048104Syokota					     sc_vtb_tail(scp->history),
23148104Syokota					     -scp->xsize*scp->ysize));
23248104Syokota	return ret;
23348104Syokota}
23448104Syokota
23548104Syokota/* copy screen-full of saved lines */
23648104Syokotastatic void
23748104Syokotahistory_to_screen(scr_stat *scp)
23848104Syokota{
23948104Syokota	int pos;
24048104Syokota	int i;
24148104Syokota
24248104Syokota	pos = scp->history_pos;
24348104Syokota	for (i = 1; i <= scp->ysize; ++i) {
24448104Syokota		pos = sc_vtb_pos(scp->history, pos, -scp->xsize);
24548104Syokota		sc_vtb_copy(scp->history, pos,
24648104Syokota			    &scp->vtb, scp->xsize*(scp->ysize - i),
24748104Syokota			    scp->xsize);
24848104Syokota	}
24948104Syokota	mark_all(scp);
25048104Syokota}
25148104Syokota
25248104Syokota/* go to the tail of the history buffer */
25348104Syokotavoid
25448104Syokotasc_hist_home(scr_stat *scp)
25548104Syokota{
25648104Syokota	scp->history_pos = sc_vtb_tail(scp->history);
25748104Syokota	history_to_screen(scp);
25848104Syokota}
25948104Syokota
26048104Syokota/* go to the top of the history buffer */
26148104Syokotavoid
26248104Syokotasc_hist_end(scr_stat *scp)
26348104Syokota{
26448104Syokota	scp->history_pos = sc_vtb_pos(scp->history, sc_vtb_tail(scp->history),
26548104Syokota				      scp->xsize*scp->ysize);
26648104Syokota	history_to_screen(scp);
26748104Syokota}
26848104Syokota
26948104Syokota/* move one line up */
27048104Syokotaint
27148104Syokotasc_hist_up_line(scr_stat *scp)
27248104Syokota{
27348104Syokota	if (sc_vtb_pos(scp->history, scp->history_pos, -(scp->xsize*scp->ysize))
27448104Syokota	    == sc_vtb_tail(scp->history))
27548104Syokota		return -1;
27648104Syokota	scp->history_pos = sc_vtb_pos(scp->history, scp->history_pos,
27748104Syokota				      -scp->xsize);
27848104Syokota	history_to_screen(scp);
27948104Syokota	return 0;
28048104Syokota}
28148104Syokota
28248104Syokota/* move one line down */
28348104Syokotaint
28448104Syokotasc_hist_down_line(scr_stat *scp)
28548104Syokota{
28648104Syokota	if (scp->history_pos == sc_vtb_tail(scp->history))
28748104Syokota		return -1;
28848104Syokota	scp->history_pos = sc_vtb_pos(scp->history, scp->history_pos,
28948104Syokota				      scp->xsize);
29048104Syokota	history_to_screen(scp);
29148104Syokota	return 0;
29248104Syokota}
29348104Syokota
29448104Syokotaint
295181905Sedsc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
29648104Syokota{
29748104Syokota	scr_stat *scp;
29848667Syokota	int error;
29948104Syokota
30048104Syokota	switch (cmd) {
30148104Syokota
30248104Syokota	case CONS_HISTORY:  	/* set history size */
303181905Sed		scp = SC_STAT(tp);
30448104Syokota		if (*(int *)data <= 0)
30548104Syokota			return EINVAL;
30648104Syokota		if (scp->status & BUFFER_SAVED)
30748104Syokota			return EBUSY;
30848667Syokota		DPRINTF(5, ("lines:%d, ysize:%d, pool:%d\n",
30948667Syokota			    *(int *)data, scp->ysize, extra_history_size));
31048667Syokota		error = sc_alloc_history_buffer(scp,
31148104Syokota					       imax(*(int *)data, scp->ysize),
31248667Syokota					       scp->ysize, TRUE);
31348667Syokota		DPRINTF(5, ("error:%d, rows:%d, pool:%d\n", error,
31448667Syokota			    sc_vtb_rows(scp->history), extra_history_size));
31548667Syokota		return error;
31677251Sdd
31777251Sdd	case CONS_CLRHIST:
318181905Sed		scp = SC_STAT(tp);
31977251Sdd		sc_vtb_clear(scp->history, scp->sc->scr_map[0x20],
32077251Sdd		    SC_NORM_ATTR << 8);
32177251Sdd		return 0;
32248104Syokota	}
32348104Syokota
32448104Syokota	return ENOIOCTL;
32548104Syokota}
32648104Syokota
32748104Syokota#endif /* SC_NO_HISTORY */
328