]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/syscons/schistory.c
cxgbe(4): Revert r367917.
[FreeBSD/FreeBSD.git] / sys / dev / syscons / schistory.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
5  * Copyright (c) 1992-1998 Søren Schmidt
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer,
13  *    without modification, immediately at the beginning of the file.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include "opt_syscons.h"
36
37 #ifndef SC_NO_HISTORY
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/conf.h>
42 #include <sys/consio.h>
43 #include <sys/tty.h>
44 #include <sys/kernel.h>
45 #include <sys/malloc.h>
46
47 #if defined(__arm__) || defined(__mips__) || defined(__powerpc__)
48 #include <machine/sc_machdep.h>
49 #else
50 #include <machine/pc/display.h>
51 #endif
52
53 #include <dev/syscons/syscons.h>
54
55 /*
56  * XXX Placeholder.
57  * This calculations should be dynamically scaled by number of separate sc
58  * devices.  A base value of 'extra_history_size' should be defined for
59  * each syscons unit, and added and subtracted from the dynamic
60  * 'extra_history_size' as units are added and removed.  This way, each time
61  * a new syscons unit goes online, extra_history_size is automatically bumped.
62  */
63 #define MAXSC   1
64
65 #if !defined(SC_MAX_HISTORY_SIZE)
66 #define SC_MAX_HISTORY_SIZE     (1000 * MAXCONS * MAXSC)
67 #endif
68
69 #if !defined(SC_HISTORY_SIZE)
70 #define SC_HISTORY_SIZE         (ROW * 4)
71 #endif
72
73 #if (SC_HISTORY_SIZE * MAXCONS * MAXSC) > SC_MAX_HISTORY_SIZE
74 #undef SC_MAX_HISTORY_SIZE
75 #define SC_MAX_HISTORY_SIZE     (SC_HISTORY_SIZE * MAXCONS * MAXSC)
76 #endif
77
78 /* local variables */
79 static int              extra_history_size
80                                 = SC_MAX_HISTORY_SIZE - SC_HISTORY_SIZE*MAXCONS;
81
82 /* local functions */
83 static void copy_history(sc_vtb_t *from, sc_vtb_t *to);
84 static void history_to_screen(scr_stat *scp);
85
86 /* allocate a history buffer */
87 int
88 sc_alloc_history_buffer(scr_stat *scp, int lines, int prev_ysize, int wait)
89 {
90         /*
91          * syscons unconditionally allocates buffers up to 
92          * SC_HISTORY_SIZE lines or scp->ysize lines, whichever 
93          * is larger. A value greater than that is allowed, 
94          * subject to extra_history_size.
95          */
96         sc_vtb_t *history;
97         sc_vtb_t *prev_history;
98         int cur_lines;                          /* current buffer size */
99         int min_lines;                          /* guaranteed buffer size */
100         int delta;                              /* lines to put back */
101
102         if (lines <= 0)
103                 lines = SC_HISTORY_SIZE;        /* use the default value */
104
105         /* make it at least as large as the screen size */
106         lines = imax(lines, scp->ysize);
107
108         /* remove the history buffer while we update it */
109         history = prev_history = scp->history;
110         scp->history = NULL;
111
112         /* calculate the amount of lines to put back to extra_history_size */
113         delta = 0;
114         if (prev_history) {
115                 cur_lines = sc_vtb_rows(history);
116                 min_lines = imax(SC_HISTORY_SIZE, prev_ysize);
117                 if (cur_lines > min_lines)
118                         delta = cur_lines - min_lines;
119         }
120
121         /* lines up to min_lines are always allowed. */
122         min_lines = imax(SC_HISTORY_SIZE, scp->ysize);
123         if (lines > min_lines) {
124                 if (lines - min_lines > extra_history_size + delta) {
125                         /* too many lines are requested */
126                         scp->history = prev_history;
127                         return EINVAL;
128                 }
129         }
130
131         /* allocate a new buffer */
132         history = (sc_vtb_t *)malloc(sizeof(*history),
133                                      M_DEVBUF,
134                                      (wait) ? M_WAITOK : M_NOWAIT);
135         if (history != NULL) {
136                 if (lines > min_lines)
137                         extra_history_size -= lines - min_lines;
138                 /* XXX error check? */
139                 sc_vtb_init(history, VTB_RINGBUFFER, scp->xsize, lines,
140                             NULL, wait);
141                 /* FIXME: XXX no good? */
142                 sc_vtb_clear(history, scp->sc->scr_map[0x20],
143                              SC_NORM_ATTR << 8);
144                 if (prev_history != NULL)
145                         copy_history(prev_history, history);
146                 scp->history_pos = sc_vtb_tail(history);
147         } else {
148                 scp->history_pos = 0;
149         }
150
151         /* destroy the previous buffer */
152         if (prev_history != NULL) {
153                 extra_history_size += delta;
154                 sc_vtb_destroy(prev_history);
155                 free(prev_history, M_DEVBUF);
156         }
157
158         scp->history = history;
159
160         return 0;
161 }
162
163 static void
164 copy_history(sc_vtb_t *from, sc_vtb_t *to)
165 {
166         int lines;
167         int cols;
168         int cols1;
169         int cols2;
170         int pos;
171         int i;
172
173         lines = sc_vtb_rows(from);
174         cols1 = sc_vtb_cols(from);
175         cols2 = sc_vtb_cols(to);
176         cols = imin(cols1, cols2);
177         pos = sc_vtb_tail(from);
178         for (i = 0; i < lines; ++i) {
179                 sc_vtb_append(from, pos, to, cols);
180                 if (cols < cols2)
181                         sc_vtb_seek(to, sc_vtb_pos(to, 
182                                                    sc_vtb_tail(to), 
183                                                    cols2 - cols));
184                 pos = sc_vtb_pos(from, pos, cols1);
185         }
186 }
187
188 void
189 sc_free_history_buffer(scr_stat *scp, int prev_ysize)
190 {
191         sc_vtb_t *history;
192         int cur_lines;                          /* current buffer size */
193         int min_lines;                          /* guaranteed buffer size */
194
195         history = scp->history;
196         scp->history = NULL;
197         if (history == NULL)
198                 return;
199
200         cur_lines = sc_vtb_rows(history);
201         min_lines = imax(SC_HISTORY_SIZE, prev_ysize);
202         extra_history_size += (cur_lines > min_lines) ? 
203                                   cur_lines - min_lines : 0;
204
205         sc_vtb_destroy(history);
206         free(history, M_DEVBUF);
207 }
208
209 /* copy entire screen into the top of the history buffer */
210 void
211 sc_hist_save(scr_stat *scp)
212 {
213         sc_vtb_append(&scp->vtb, 0, scp->history, scp->xsize*scp->ysize);
214         scp->history_pos = sc_vtb_tail(scp->history);
215 }
216
217 /* restore the screen by copying from the history buffer */
218 int
219 sc_hist_restore(scr_stat *scp)
220 {
221         int ret;
222
223         if (scp->history_pos != sc_vtb_tail(scp->history)) {
224                 scp->history_pos = sc_vtb_tail(scp->history);
225                 history_to_screen(scp);
226                 ret =  0;
227         } else {
228                 ret = 1;
229         }
230         sc_vtb_seek(scp->history, sc_vtb_pos(scp->history, 
231                                              sc_vtb_tail(scp->history),
232                                              -scp->xsize*scp->ysize));
233         return ret;
234 }
235
236 /* copy screen-full of saved lines */
237 static void
238 history_to_screen(scr_stat *scp)
239 {
240         int pos;
241         int i;
242
243         pos = scp->history_pos;
244         for (i = 1; i <= scp->ysize; ++i) {
245                 pos = sc_vtb_pos(scp->history, pos, -scp->xsize);
246                 sc_vtb_copy(scp->history, pos,
247                             &scp->vtb, scp->xsize*(scp->ysize - i),
248                             scp->xsize);
249         }
250         mark_all(scp);
251 }
252
253 /* go to the tail of the history buffer */
254 void
255 sc_hist_home(scr_stat *scp)
256 {
257         scp->history_pos = sc_vtb_tail(scp->history);
258         history_to_screen(scp);
259 }
260
261 /* go to the top of the history buffer */
262 void
263 sc_hist_end(scr_stat *scp)
264 {
265         scp->history_pos = sc_vtb_pos(scp->history, sc_vtb_tail(scp->history),
266                                       scp->xsize*scp->ysize);
267         history_to_screen(scp);
268 }
269
270 /* move one line up */
271 int
272 sc_hist_up_line(scr_stat *scp)
273 {
274         if (sc_vtb_pos(scp->history, scp->history_pos, -(scp->xsize*scp->ysize))
275             == sc_vtb_tail(scp->history))
276                 return -1;
277         scp->history_pos = sc_vtb_pos(scp->history, scp->history_pos,
278                                       -scp->xsize);
279         history_to_screen(scp);
280         return 0;
281 }
282
283 /* move one line down */
284 int
285 sc_hist_down_line(scr_stat *scp)
286 {
287         if (scp->history_pos == sc_vtb_tail(scp->history))
288                 return -1;
289         scp->history_pos = sc_vtb_pos(scp->history, scp->history_pos,
290                                       scp->xsize);
291         history_to_screen(scp);
292         return 0;
293 }
294
295 int
296 sc_hist_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
297 {
298         scr_stat *scp;
299         int error;
300
301         switch (cmd) {
302         case CONS_HISTORY:      /* set history size */
303                 scp = SC_STAT(tp);
304                 if (*(int *)data <= 0)
305                         return EINVAL;
306                 if (scp->status & BUFFER_SAVED)
307                         return EBUSY;
308                 DPRINTF(5, ("lines:%d, ysize:%d, pool:%d\n",
309                             *(int *)data, scp->ysize, extra_history_size));
310                 error = sc_alloc_history_buffer(scp, 
311                                                imax(*(int *)data, scp->ysize),
312                                                scp->ysize, TRUE);
313                 DPRINTF(5, ("error:%d, rows:%d, pool:%d\n", error,
314                             sc_vtb_rows(scp->history), extra_history_size));
315                 return error;
316
317         case CONS_CLRHIST:
318                 scp = SC_STAT(tp);
319                 sc_vtb_clear(scp->history, scp->sc->scr_map[0x20],
320                     SC_NORM_ATTR << 8);
321                 return 0;
322         }
323
324         return ENOIOCTL;
325 }
326
327 #endif /* SC_NO_HISTORY */