wsdisplay_vcons.c revision 1.38.10.1 1 /* $NetBSD: wsdisplay_vcons.c,v 1.38.10.1 2019/06/10 22:07:36 christos Exp $ */
2
3 /*-
4 * Copyright (c) 2005, 2006 Michael Lorenz
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: wsdisplay_vcons.c,v 1.38.10.1 2019/06/10 22:07:36 christos Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/buf.h>
36 #include <sys/device.h>
37 #include <sys/ioctl.h>
38 #include <sys/malloc.h>
39 #include <sys/mman.h>
40 #include <sys/tty.h>
41 #include <sys/conf.h>
42 #include <sys/proc.h>
43 #include <sys/kthread.h>
44 #include <sys/tprintf.h>
45 #include <sys/atomic.h>
46
47 #include <dev/wscons/wsdisplayvar.h>
48 #include <dev/wscons/wsconsio.h>
49 #include <dev/wsfont/wsfont.h>
50 #include <dev/rasops/rasops.h>
51
52 #include <dev/wscons/wsdisplay_vconsvar.h>
53
54 #ifdef _KERNEL_OPT
55 #include "opt_wsemul.h"
56 #include "opt_wsdisplay_compat.h"
57 #include "opt_vcons.h"
58 #endif
59
60 #ifdef VCONS_DEBUG
61 #define DPRINTF printf
62 #else
63 #define DPRINTF if (0) printf
64 #endif
65
66 static void vcons_dummy_init_screen(void *, struct vcons_screen *, int,
67 long *);
68
69 static int vcons_ioctl(void *, void *, u_long, void *, int, struct lwp *);
70 static int vcons_alloc_screen(void *, const struct wsscreen_descr *, void **,
71 int *, int *, long *);
72 static void vcons_free_screen(void *, void *);
73 static int vcons_show_screen(void *, void *, int, void (*)(void *, int, int),
74 void *);
75 static int vcons_load_font(void *, void *, struct wsdisplay_font *);
76
77 #ifdef WSDISPLAY_SCROLLSUPPORT
78 static void vcons_scroll(void *, void *, int);
79 static void vcons_do_scroll(struct vcons_screen *);
80 #endif
81
82 static void vcons_do_switch(void *);
83
84 /* methods that work only on text buffers */
85 static void vcons_copycols_buffer(void *, int, int, int, int);
86 static void vcons_erasecols_buffer(void *, int, int, int, long);
87 static void vcons_copyrows_buffer(void *, int, int, int);
88 static void vcons_eraserows_buffer(void *, int, int, long);
89 static void vcons_putchar_buffer(void *, int, int, u_int, long);
90
91 /*
92 * actual wrapper methods which call both the _buffer ones above and the
93 * driver supplied ones to do the drawing
94 */
95 static void vcons_copycols(void *, int, int, int, int);
96 static void vcons_erasecols(void *, int, int, int, long);
97 static void vcons_copyrows(void *, int, int, int);
98 static void vcons_eraserows(void *, int, int, long);
99 static void vcons_putchar(void *, int, int, u_int, long);
100 #ifdef VCONS_DRAW_INTR
101 static void vcons_erasecols_cached(void *, int, int, int, long);
102 static void vcons_eraserows_cached(void *, int, int, long);
103 static void vcons_putchar_cached(void *, int, int, u_int, long);
104 #endif
105 static void vcons_cursor(void *, int, int, int);
106
107 /*
108 * methods that avoid framebuffer reads
109 */
110 static void vcons_copycols_noread(void *, int, int, int, int);
111 static void vcons_copyrows_noread(void *, int, int, int);
112
113
114 /* support for reading/writing text buffers. For wsmoused */
115 static int vcons_putwschar(struct vcons_screen *, struct wsdisplay_char *);
116 static int vcons_getwschar(struct vcons_screen *, struct wsdisplay_char *);
117
118 static void vcons_lock(struct vcons_screen *);
119 static void vcons_unlock(struct vcons_screen *);
120
121 #ifdef VCONS_DRAW_INTR
122 static void vcons_intr(void *);
123 static void vcons_softintr(void *);
124 static void vcons_init_thread(void *);
125 #endif
126
127 int
128 vcons_init(struct vcons_data *vd, void *cookie, struct wsscreen_descr *def,
129 struct wsdisplay_accessops *ao)
130 {
131
132 /* zero out everything so we can rely on untouched fields being 0 */
133 memset(vd, 0, sizeof(struct vcons_data));
134
135 vd->cookie = cookie;
136
137 vd->init_screen = vcons_dummy_init_screen;
138 vd->show_screen_cb = NULL;
139
140 /* keep a copy of the accessops that we replace below with our
141 * own wrappers */
142 vd->ioctl = ao->ioctl;
143
144 /* configure the accessops */
145 ao->ioctl = vcons_ioctl;
146 ao->alloc_screen = vcons_alloc_screen;
147 ao->free_screen = vcons_free_screen;
148 ao->show_screen = vcons_show_screen;
149 ao->load_font = vcons_load_font;
150 #ifdef WSDISPLAY_SCROLLSUPPORT
151 ao->scroll = vcons_scroll;
152 #endif
153
154 LIST_INIT(&vd->screens);
155 vd->active = NULL;
156 vd->wanted = NULL;
157 vd->currenttype = def;
158 vd->defaulttype = def;
159 callout_init(&vd->switch_callout, 0);
160 callout_setfunc(&vd->switch_callout, vcons_do_switch, vd);
161 #ifdef VCONS_DRAW_INTR
162 vd->cells = 0;
163 vd->attrs = NULL;
164 vd->chars = NULL;
165 vd->cursor_offset = -1;
166 #endif
167
168 /*
169 * a lock to serialize access to the framebuffer.
170 * when switching screens we need to make sure there's no rasops
171 * operation in progress
172 */
173 #ifdef DIAGNOSTIC
174 vd->switch_poll_count = 0;
175 #endif
176 #ifdef VCONS_DRAW_INTR
177 vd->intr_softint = softint_establish(SOFTINT_SERIAL,
178 vcons_softintr, vd);
179 callout_init(&vd->intr, 0);
180 callout_setfunc(&vd->intr, vcons_intr, vd);
181 vd->intr_valid = 1;
182
183 if (kthread_create(PRI_NONE, 0, NULL, vcons_init_thread, vd, NULL,
184 "vcons_init") != 0) {
185 printf("%s: unable to create thread.\n", __func__);
186 return -1;
187 }
188 #endif
189 return 0;
190 }
191
192 static void
193 vcons_lock(struct vcons_screen *scr)
194 {
195 #ifdef VCONS_PARANOIA
196 int s;
197
198 s = splhigh();
199 #endif
200 SCREEN_BUSY(scr);
201 #ifdef VCONS_PARANOIA
202 splx(s);
203 #endif
204 }
205
206 static void
207 vcons_unlock(struct vcons_screen *scr)
208 {
209 #ifdef VCONS_PARANOIA
210 int s;
211
212 s = splhigh();
213 #endif
214 SCREEN_IDLE(scr);
215 #ifdef VCONS_PARANOIA
216 splx(s);
217 #endif
218 }
219
220 static void
221 vcons_dummy_init_screen(void *cookie,
222 struct vcons_screen *scr, int exists,
223 long *defattr)
224 {
225
226 /*
227 * default init_screen() method.
228 * Needs to be overwritten so we bitch and whine in case anyone ends
229 * up in here.
230 */
231 printf("vcons_init_screen: dummy function called. Your driver is "
232 "supposed to supply a replacement for proper operation\n");
233 }
234
235 static int
236 vcons_alloc_buffers(struct vcons_data *vd, struct vcons_screen *scr)
237 {
238 struct rasops_info *ri = &scr->scr_ri;
239 int cnt, i;
240 #ifdef VCONS_DRAW_INTR
241 int size;
242 #endif
243
244 /*
245 * we allocate both chars and attributes in one chunk, attributes first
246 * because they have the (potentially) bigger alignment
247 */
248 #ifdef WSDISPLAY_SCROLLSUPPORT
249 cnt = (ri->ri_rows + WSDISPLAY_SCROLLBACK_LINES) * ri->ri_cols;
250 scr->scr_lines_in_buffer = WSDISPLAY_SCROLLBACK_LINES;
251 scr->scr_current_line = 0;
252 scr->scr_line_wanted = 0;
253 scr->scr_offset_to_zero = ri->ri_cols * WSDISPLAY_SCROLLBACK_LINES;
254 scr->scr_current_offset = scr->scr_offset_to_zero;
255 #else
256 cnt = ri->ri_rows * ri->ri_cols;
257 #endif
258 scr->scr_attrs = malloc(cnt * (sizeof(long) +
259 sizeof(uint32_t)), M_DEVBUF, M_WAITOK);
260 if (scr->scr_attrs == NULL)
261 return ENOMEM;
262
263 scr->scr_chars = (uint32_t *)&scr->scr_attrs[cnt];
264
265 /*
266 * fill the attribute buffer with *defattr, chars with 0x20
267 * since we don't know if the driver tries to mimic firmware output or
268 * reset everything we do nothing to VRAM here, any driver that feels
269 * the need to clear screen or something will have to do it on its own
270 * Additional screens will start out in the background anyway so
271 * cleaning or not only really affects the initial console screen
272 */
273 for (i = 0; i < cnt; i++) {
274 scr->scr_attrs[i] = scr->scr_defattr;
275 scr->scr_chars[i] = 0x20;
276 }
277
278 #ifdef VCONS_DRAW_INTR
279 size = ri->ri_cols * ri->ri_rows;
280 if (size > vd->cells) {
281 if (vd->chars != NULL) free(vd->chars, M_DEVBUF);
282 if (vd->attrs != NULL) free(vd->attrs, M_DEVBUF);
283 vd->cells = size;
284 vd->chars = malloc(size * sizeof(uint32_t), M_DEVBUF,
285 M_WAITOK|M_ZERO);
286 vd->attrs = malloc(size * sizeof(long), M_DEVBUF,
287 M_WAITOK|M_ZERO);
288 vcons_invalidate_cache(vd);
289 } else if (SCREEN_IS_VISIBLE(scr))
290 vcons_invalidate_cache(vd);
291 #endif
292 return 0;
293 }
294
295 int
296 vcons_init_screen(struct vcons_data *vd, struct vcons_screen *scr,
297 int existing, long *defattr)
298 {
299 struct rasops_info *ri = &scr->scr_ri;
300 int i;
301
302 scr->scr_cookie = vd->cookie;
303 scr->scr_vd = scr->scr_origvd = vd;
304 scr->scr_busy = 0;
305 if (scr->scr_type == NULL)
306 scr->scr_type = vd->defaulttype;
307
308 /*
309 * call the driver-supplied init_screen function which is expected
310 * to set up rasops_info, override cursor() and probably others
311 */
312 vd->init_screen(vd->cookie, scr, existing, defattr);
313
314 /*
315 * save the non virtual console aware rasops and replace them with
316 * our wrappers
317 */
318 vd->eraserows = ri->ri_ops.eraserows;
319 vd->erasecols = ri->ri_ops.erasecols;
320 scr->putchar = ri->ri_ops.putchar;
321 vd->cursor = ri->ri_ops.cursor;
322
323 if (scr->scr_flags & VCONS_NO_COPYCOLS) {
324 vd->copycols = vcons_copycols_noread;
325 } else {
326 vd->copycols = ri->ri_ops.copycols;
327 }
328
329 if (scr->scr_flags & VCONS_NO_COPYROWS) {
330 vd->copyrows = vcons_copyrows_noread;
331 } else {
332 vd->copyrows = ri->ri_ops.copyrows;
333 }
334
335 ri->ri_ops.eraserows = vcons_eraserows;
336 ri->ri_ops.erasecols = vcons_erasecols;
337 ri->ri_ops.putchar = vcons_putchar;
338 ri->ri_ops.cursor = vcons_cursor;
339 ri->ri_ops.copycols = vcons_copycols;
340 ri->ri_ops.copyrows = vcons_copyrows;
341
342
343 ri->ri_hw = scr;
344
345 i = ri->ri_ops.allocattr(ri, WS_DEFAULT_FG, WS_DEFAULT_BG, 0, defattr);
346 if (i != 0) {
347 #ifdef DIAGNOSTIC
348 printf("vcons: error allocating attribute %d\n", i);
349 #endif
350 scr->scr_defattr = 0;
351 } else
352 scr->scr_defattr = *defattr;
353
354 vcons_alloc_buffers(vd, scr);
355
356 if(vd->active == NULL) {
357 vd->active = scr;
358 SCREEN_VISIBLE(scr);
359 }
360
361 if (existing) {
362 SCREEN_VISIBLE(scr);
363 vd->active = scr;
364 } else {
365 SCREEN_INVISIBLE(scr);
366 }
367
368 LIST_INSERT_HEAD(&vd->screens, scr, next);
369 return 0;
370 }
371
372 static int
373 vcons_load_font(void *v, void *cookie, struct wsdisplay_font *f)
374 {
375 struct vcons_data *vd = v;
376 struct vcons_screen *scr = cookie;
377 struct rasops_info *ri;
378 struct wsdisplay_font *font;
379 int flags = WSFONT_FIND_BITMAP, fcookie;
380
381 /* see if we're asked to add a font or use it */
382 if (scr == NULL)
383 return 0;
384
385 ri = &scr->scr_ri;
386
387 /* see if the driver knows how to handle multiple fonts */
388 if ((scr->scr_flags & VCONS_LOADFONT) == 0) {
389 return EOPNOTSUPP;
390 }
391
392 /* now see what fonts we can use */
393 if (ri->ri_flg & RI_ENABLE_ALPHA) {
394 flags |= WSFONT_FIND_ALPHA;
395 }
396
397 fcookie = wsfont_find(f->name, 0, 0, 0, 0, 0, flags);
398 if (fcookie == -1)
399 return EINVAL;
400
401 wsfont_lock(fcookie, &font);
402 if (font == NULL)
403 return EINVAL;
404
405 /* ok, we got a font. Now clear the screen with the old parameters */
406 if (SCREEN_IS_VISIBLE(scr))
407 vd->eraserows(ri, 0, ri->ri_rows, scr->scr_defattr);
408
409 vcons_lock(vd->active);
410 #ifdef VCONS_DRAW_INTR
411 callout_halt(&vd->intr, NULL);
412 #endif
413 /* set the new font and re-initialize things */
414 ri->ri_font = font;
415 wsfont_unlock(ri->ri_wsfcookie);
416 ri->ri_wsfcookie = fcookie;
417
418 vd->init_screen(vd->cookie, scr, 1, &scr->scr_defattr);
419 DPRINTF("caps %x %x\n", scr->scr_type->capabilities, ri->ri_caps);
420 if (scr->scr_type->capabilities & WSSCREEN_RESIZE) {
421 scr->scr_type->nrows = ri->ri_rows;
422 scr->scr_type->ncols = ri->ri_cols;
423 DPRINTF("new size %d %d\n", ri->ri_rows, ri->ri_cols);
424 }
425
426
427 /* now, throw the old buffers away */
428 if (scr->scr_attrs)
429 free(scr->scr_attrs, M_DEVBUF);
430 /* allocate new buffers */
431 vcons_alloc_buffers(vd, scr);
432
433 /* save the potentially changed putchar */
434 scr->putchar = ri->ri_ops.putchar;
435
436 /* and put our wrappers back */
437 ri->ri_ops.eraserows = vcons_eraserows;
438 ri->ri_ops.erasecols = vcons_erasecols;
439 ri->ri_ops.putchar = vcons_putchar;
440 ri->ri_ops.cursor = vcons_cursor;
441 ri->ri_ops.copycols = vcons_copycols;
442 ri->ri_ops.copyrows = vcons_copyrows;
443 vcons_unlock(vd->active);
444
445 /* notify things that we're about to redraw */
446 if (vd->show_screen_cb != NULL)
447 vd->show_screen_cb(scr, vd->show_screen_cookie);
448
449 #ifdef VCONS_DRAW_INTR
450 /*
451 * XXX
452 * Something(tm) craps all over VRAM somewhere up there if we're
453 * using VCONS_DRAW_INTR. Until I figure out what causes it, just
454 * redraw the screen for now.
455 */
456 vcons_redraw_screen(vd->active);
457 callout_schedule(&vd->intr, mstohz(33));
458 #endif
459 /* no need to draw anything, wsdisplay should reset the terminal */
460
461 return 0;
462 }
463
464 static void
465 vcons_do_switch(void *arg)
466 {
467 struct vcons_data *vd = arg;
468 struct vcons_screen *scr, *oldscr;
469
470 scr = vd->wanted;
471 if (!scr) {
472 printf("vcons_switch_screen: disappeared\n");
473 vd->switch_cb(vd->switch_cb_arg, EIO, 0);
474 return;
475 }
476 oldscr = vd->active; /* can be NULL! */
477
478 /*
479 * if there's an old, visible screen we mark it invisible and wait
480 * until it's not busy so we can safely switch
481 */
482 if (oldscr != NULL) {
483 SCREEN_INVISIBLE(oldscr);
484 if (SCREEN_IS_BUSY(oldscr)) {
485 callout_schedule(&vd->switch_callout, 1);
486 #ifdef DIAGNOSTIC
487 /* bitch if we wait too long */
488 vd->switch_poll_count++;
489 if (vd->switch_poll_count > 100) {
490 panic("vcons: screen still busy");
491 }
492 #endif
493 return;
494 }
495 /* invisible screen -> no visible cursor image */
496 oldscr->scr_ri.ri_flg &= ~RI_CURSOR;
497 #ifdef DIAGNOSTIC
498 vd->switch_poll_count = 0;
499 #endif
500 }
501
502 if (scr == oldscr)
503 return;
504
505 #ifdef DIAGNOSTIC
506 if (SCREEN_IS_VISIBLE(scr))
507 printf("vcons_switch_screen: already active");
508 #endif
509
510 #ifdef notyet
511 if (vd->currenttype != type) {
512 vcons_set_screentype(vd, type);
513 vd->currenttype = type;
514 }
515 #endif
516
517 SCREEN_VISIBLE(scr);
518 vd->active = scr;
519 vd->wanted = NULL;
520
521 #ifdef VCONS_DRAW_INTR
522 vcons_invalidate_cache(vd);
523 #endif
524
525 if (vd->show_screen_cb != NULL)
526 vd->show_screen_cb(scr, vd->show_screen_cookie);
527
528 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
529 vcons_redraw_screen(scr);
530
531 if (vd->switch_cb)
532 vd->switch_cb(vd->switch_cb_arg, 0, 0);
533 }
534
535 void
536 vcons_redraw_screen(struct vcons_screen *scr)
537 {
538 uint32_t *charptr = scr->scr_chars, c;
539 long *attrptr = scr->scr_attrs, a, last_a = 0, mask, cmp, acmp;
540 struct rasops_info *ri = &scr->scr_ri;
541 struct vcons_data *vd = scr->scr_vd;
542 int i, j, offset, boffset = 0, start = -1;
543
544 mask = 0x00ff00ff; /* background and flags */
545 cmp = 0xffffffff; /* never match anything */
546 vcons_lock(scr);
547 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
548
549 /*
550 * only clear the screen when RI_FULLCLEAR is set since we're
551 * going to overwrite every single character cell anyway
552 */
553 if (ri->ri_flg & RI_FULLCLEAR) {
554 vd->eraserows(ri, 0, ri->ri_rows,
555 scr->scr_defattr);
556 cmp = scr->scr_defattr & mask;
557 }
558
559 /* redraw the screen */
560 #ifdef WSDISPLAY_SCROLLSUPPORT
561 offset = scr->scr_current_offset;
562 #else
563 offset = 0;
564 #endif
565 for (i = 0; i < ri->ri_rows; i++) {
566 start = -1;
567 for (j = 0; j < ri->ri_cols; j++) {
568 /*
569 * no need to use the wrapper function - we
570 * don't change any characters or attributes
571 * and we already made sure the screen we're
572 * working on is visible
573 */
574 c = charptr[offset];
575 a = attrptr[offset];
576 acmp = a & mask;
577 if (c == ' ') {
578 /*
579 * if we already erased the background
580 * and this blank uses the same colour
581 * and flags we don't need to do
582 * anything here
583 */
584 if (acmp == cmp)
585 goto next;
586 /*
587 * see if we can optimize things a
588 * little bit by drawing stretches of
589 * blanks using erasecols
590 */
591
592 if (start == -1) {
593 start = j;
594 last_a = acmp;
595 } else if (acmp != last_a) {
596 /*
597 * different attr, need to
598 * flush & restart
599 */
600 vd->erasecols(ri, i, start,
601 j - start, last_a);
602 start = j;
603 last_a = acmp;
604 }
605 } else {
606 if (start != -1) {
607 vd->erasecols(ri, i, start,
608 j - start, last_a);
609 start = -1;
610 }
611
612 scr->putchar(ri, i, j, c, a);
613 }
614 next:
615 #ifdef VCONS_DRAW_INTR
616 vd->chars[boffset] = charptr[offset];
617 vd->attrs[boffset] = attrptr[offset];
618 #endif
619 offset++;
620 boffset++;
621 }
622 /* end of the line - draw all defered blanks, if any */
623 if (start != -1) {
624 vd->erasecols(ri, i, start, j - start, last_a);
625 }
626 }
627 ri->ri_flg &= ~RI_CURSOR;
628 scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol);
629 #ifdef VCONS_DRAW_INTR
630 vd->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol;
631 #endif
632 }
633 vcons_unlock(scr);
634 }
635
636 #ifdef VCONS_DRAW_INTR
637 void
638 vcons_update_screen(struct vcons_screen *scr)
639 {
640 uint32_t *charptr = scr->scr_chars;
641 long *attrptr = scr->scr_attrs;
642 struct rasops_info *ri = &scr->scr_ri;
643 struct vcons_data *vd = scr->scr_vd;
644 int i, j, offset, boffset = 0;
645
646 vcons_lock(scr);
647 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
648
649 /* redraw the screen */
650 #ifdef WSDISPLAY_SCROLLSUPPORT
651 offset = scr->scr_current_offset;
652 #else
653 offset = 0;
654 #endif
655 /*
656 * we mark the character cell occupied by the cursor as dirty
657 * so we don't have to deal with it
658 * notice that this isn't necessarily the position where rasops
659 * thinks it is, just where we drew it the last time
660 */
661 if (vd->cursor_offset >= 0)
662 vd->attrs[vd->cursor_offset] = 0xffffffff;
663
664 for (i = 0; i < ri->ri_rows; i++) {
665 for (j = 0; j < ri->ri_cols; j++) {
666 /*
667 * no need to use the wrapper function - we
668 * don't change any characters or attributes
669 * and we already made sure the screen we're
670 * working on is visible
671 */
672 if ((vd->chars[boffset] != charptr[offset]) ||
673 (vd->attrs[boffset] != attrptr[offset])) {
674 scr->putchar(ri, i, j,
675 charptr[offset], attrptr[offset]);
676 vd->chars[boffset] = charptr[offset];
677 vd->attrs[boffset] = attrptr[offset];
678 }
679 offset++;
680 boffset++;
681 }
682 }
683 ri->ri_flg &= ~RI_CURSOR;
684 scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol);
685 vd->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol;
686 }
687 vcons_unlock(scr);
688 }
689 #endif
690
691 static int
692 vcons_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
693 struct lwp *l)
694 {
695 struct vcons_data *vd = v;
696 int error = 0;
697
698
699 switch (cmd) {
700 case WSDISPLAYIO_GETWSCHAR:
701 error = vcons_getwschar((struct vcons_screen *)vs,
702 (struct wsdisplay_char *)data);
703 break;
704
705 case WSDISPLAYIO_PUTWSCHAR:
706 error = vcons_putwschar((struct vcons_screen *)vs,
707 (struct wsdisplay_char *)data);
708 break;
709
710 case WSDISPLAYIO_SET_POLLING: {
711 int poll = *(int *)data;
712
713 /* first call the driver's ioctl handler */
714 if (vd->ioctl != NULL)
715 error = (*vd->ioctl)(v, vs, cmd, data, flag, l);
716 if (poll) {
717 vcons_enable_polling(vd);
718 vcons_hard_switch(LIST_FIRST(&vd->screens));
719 } else
720 vcons_disable_polling(vd);
721 }
722 break;
723
724 default:
725 if (vd->ioctl != NULL)
726 error = (*vd->ioctl)(v, vs, cmd, data, flag, l);
727 else
728 error = EPASSTHROUGH;
729 }
730
731 return error;
732 }
733
734 static int
735 vcons_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
736 int *curxp, int *curyp, long *defattrp)
737 {
738 struct vcons_data *vd = v;
739 struct vcons_screen *scr;
740 struct wsscreen_descr *t = __UNCONST(type);
741 int ret;
742
743 scr = malloc(sizeof(struct vcons_screen), M_DEVBUF, M_WAITOK | M_ZERO);
744 if (scr == NULL)
745 return ENOMEM;
746
747 scr->scr_flags = 0;
748 scr->scr_status = 0;
749 scr->scr_busy = 0;
750 scr->scr_type = __UNCONST(type);
751
752 ret = vcons_init_screen(vd, scr, 0, defattrp);
753 if (ret != 0) {
754 free(scr, M_DEVBUF);
755 return ret;
756 }
757 if (t->capabilities & WSSCREEN_RESIZE) {
758 t->nrows = scr->scr_ri.ri_rows;
759 t->ncols = scr->scr_ri.ri_cols;
760 }
761
762 if (vd->active == NULL) {
763 SCREEN_VISIBLE(scr);
764 vd->active = scr;
765 vd->currenttype = type;
766 }
767
768 *cookiep = scr;
769 *curxp = scr->scr_ri.ri_ccol;
770 *curyp = scr->scr_ri.ri_crow;
771 return 0;
772 }
773
774 static void
775 vcons_free_screen(void *v, void *cookie)
776 {
777 struct vcons_data *vd = v;
778 struct vcons_screen *scr = cookie;
779
780 vcons_lock(scr);
781 /* there should be no rasops activity here */
782
783 LIST_REMOVE(scr, next);
784
785 if ((scr->scr_flags & VCONS_SCREEN_IS_STATIC) == 0) {
786 free(scr->scr_attrs, M_DEVBUF);
787 free(scr, M_DEVBUF);
788 } else {
789 /*
790 * maybe we should just restore the old rasops_info methods
791 * and free the character/attribute buffer here?
792 */
793 #ifdef VCONS_DEBUG
794 panic("vcons_free_screen: console");
795 #else
796 printf("vcons_free_screen: console\n");
797 #endif
798 }
799
800 if (vd->active == scr)
801 vd->active = NULL;
802 }
803
804 static int
805 vcons_show_screen(void *v, void *cookie, int waitok,
806 void (*cb)(void *, int, int), void *cb_arg)
807 {
808 struct vcons_data *vd = v;
809 struct vcons_screen *scr;
810
811 scr = cookie;
812 if (scr == vd->active)
813 return 0;
814
815 vd->wanted = scr;
816 vd->switch_cb = cb;
817 vd->switch_cb_arg = cb_arg;
818 if (cb) {
819 callout_schedule(&vd->switch_callout, 0);
820 return EAGAIN;
821 }
822
823 vcons_do_switch(vd);
824 return 0;
825 }
826
827 /* wrappers for rasops_info methods */
828
829 static void
830 vcons_copycols_buffer(void *cookie, int row, int srccol, int dstcol, int ncols)
831 {
832 struct rasops_info *ri = cookie;
833 struct vcons_screen *scr = ri->ri_hw;
834 int from = srccol + row * ri->ri_cols;
835 int to = dstcol + row * ri->ri_cols;
836
837 #ifdef WSDISPLAY_SCROLLSUPPORT
838 int offset;
839 offset = scr->scr_offset_to_zero;
840
841 memmove(&scr->scr_attrs[offset + to], &scr->scr_attrs[offset + from],
842 ncols * sizeof(long));
843 memmove(&scr->scr_chars[offset + to], &scr->scr_chars[offset + from],
844 ncols * sizeof(uint32_t));
845 #else
846 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
847 ncols * sizeof(long));
848 memmove(&scr->scr_chars[to], &scr->scr_chars[from],
849 ncols * sizeof(uint32_t));
850 #endif
851
852 #ifdef VCONS_DRAW_INTR
853 atomic_inc_uint(&scr->scr_dirty);
854 #endif
855 }
856
857 static void
858 vcons_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
859 {
860 struct rasops_info *ri = cookie;
861 struct vcons_screen *scr = ri->ri_hw;
862
863 vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols);
864
865 #if defined(VCONS_DRAW_INTR)
866 if (scr->scr_vd->use_intr)
867 return;
868 #endif
869
870 vcons_lock(scr);
871 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
872 #if defined(VCONS_DRAW_INTR)
873 vcons_update_screen(scr);
874 #else
875 scr->scr_vd->copycols(cookie, row, srccol, dstcol, ncols);
876 #endif
877 }
878 vcons_unlock(scr);
879 }
880
881 static void
882 vcons_copycols_noread(void *cookie, int row, int srccol, int dstcol, int ncols)
883 {
884 struct rasops_info *ri = cookie;
885 struct vcons_screen *scr = ri->ri_hw;
886 #ifdef VCONS_DRAW_INTR
887 struct vcons_data *vd = scr->scr_vd;
888 #endif
889
890 vcons_lock(scr);
891 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
892 int pos, c, offset, ppos;
893
894 #ifdef WSDISPLAY_SCROLLSUPPORT
895 offset = scr->scr_current_offset;
896 #else
897 offset = 0;
898 #endif
899 ppos = ri->ri_cols * row + dstcol;
900 pos = ppos + offset;
901 for (c = dstcol; c < (dstcol + ncols); c++) {
902 #ifdef VCONS_DRAW_INTR
903 if ((scr->scr_chars[pos] != vd->chars[ppos]) ||
904 (scr->scr_attrs[pos] != vd->attrs[ppos])) {
905 scr->putchar(cookie, row, c,
906 scr->scr_chars[pos], scr->scr_attrs[pos]);
907 vd->chars[ppos] = scr->scr_chars[pos];
908 vd->attrs[ppos] = scr->scr_attrs[pos];
909 }
910 #else
911 scr->putchar(cookie, row, c, scr->scr_chars[pos],
912 scr->scr_attrs[pos]);
913 #endif
914 pos++;
915 ppos++;
916 }
917 }
918 vcons_unlock(scr);
919 }
920
921 static void
922 vcons_erasecols_buffer(void *cookie, int row, int startcol, int ncols, long fillattr)
923 {
924 struct rasops_info *ri = cookie;
925 struct vcons_screen *scr = ri->ri_hw;
926 int start = startcol + row * ri->ri_cols;
927 int end = start + ncols, i;
928
929 #ifdef WSDISPLAY_SCROLLSUPPORT
930 int offset;
931 offset = scr->scr_offset_to_zero;
932
933 for (i = start; i < end; i++) {
934 scr->scr_attrs[offset + i] = fillattr;
935 scr->scr_chars[offset + i] = 0x20;
936 }
937 #else
938 for (i = start; i < end; i++) {
939 scr->scr_attrs[i] = fillattr;
940 scr->scr_chars[i] = 0x20;
941 }
942 #endif
943
944 #ifdef VCONS_DRAW_INTR
945 atomic_inc_uint(&scr->scr_dirty);
946 #endif
947 }
948
949 #ifdef VCONS_DRAW_INTR
950 static void
951 vcons_erasecols_cached(void *cookie, int row, int startcol, int ncols, long fillattr)
952 {
953 struct rasops_info *ri = cookie;
954 struct vcons_screen *scr = ri->ri_hw;
955 struct vcons_data *vd = scr->scr_vd;
956 int i, pos = row * ri->ri_cols + startcol;
957
958 for (i = pos; i < ncols; i++) {
959 vd->chars[i] = 0x20;
960 vd->attrs[i] = fillattr;
961 }
962 vd->erasecols(cookie, row, startcol, ncols, fillattr);
963 }
964 #endif
965
966 static void
967 vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
968 {
969 struct rasops_info *ri = cookie;
970 struct vcons_screen *scr = ri->ri_hw;
971
972 vcons_erasecols_buffer(cookie, row, startcol, ncols, fillattr);
973
974 #if defined(VCONS_DRAW_INTR)
975 if (scr->scr_vd->use_intr)
976 return;
977 #endif
978
979 vcons_lock(scr);
980 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
981 #ifdef VCONS_DRAW_INTR
982 vcons_erasecols_cached(cookie, row, startcol, ncols,
983 fillattr);
984 #else
985 scr->scr_vd->erasecols(cookie, row, startcol, ncols, fillattr);
986 #endif
987 }
988 vcons_unlock(scr);
989 }
990
991 static void
992 vcons_copyrows_buffer(void *cookie, int srcrow, int dstrow, int nrows)
993 {
994 struct rasops_info *ri = cookie;
995 struct vcons_screen *scr = ri->ri_hw;
996 int from, to, len;
997
998 #ifdef WSDISPLAY_SCROLLSUPPORT
999 int offset;
1000 offset = scr->scr_offset_to_zero;
1001
1002 /* do we need to scroll the back buffer? */
1003 if (dstrow == 0) {
1004 from = ri->ri_cols * srcrow;
1005 to = ri->ri_cols * dstrow;
1006
1007 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
1008 scr->scr_offset_to_zero * sizeof(long));
1009 memmove(&scr->scr_chars[to], &scr->scr_chars[from],
1010 scr->scr_offset_to_zero * sizeof(uint32_t));
1011 }
1012 from = ri->ri_cols * srcrow + offset;
1013 to = ri->ri_cols * dstrow + offset;
1014 len = ri->ri_cols * nrows;
1015
1016 #else
1017 from = ri->ri_cols * srcrow;
1018 to = ri->ri_cols * dstrow;
1019 len = ri->ri_cols * nrows;
1020 #endif
1021 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
1022 len * sizeof(long));
1023 memmove(&scr->scr_chars[to], &scr->scr_chars[from],
1024 len * sizeof(uint32_t));
1025
1026 #ifdef VCONS_DRAW_INTR
1027 atomic_inc_uint(&scr->scr_dirty);
1028 #endif
1029 }
1030
1031 static void
1032 vcons_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
1033 {
1034 struct rasops_info *ri = cookie;
1035 struct vcons_screen *scr = ri->ri_hw;
1036
1037 vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows);
1038
1039 #if defined(VCONS_DRAW_INTR)
1040 if (scr->scr_vd->use_intr)
1041 return;
1042 #endif
1043
1044 vcons_lock(scr);
1045 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1046 #if defined(VCONS_DRAW_INTR)
1047 vcons_update_screen(scr);
1048 #else
1049 scr->scr_vd->copyrows(cookie, srcrow, dstrow, nrows);
1050 #endif
1051 }
1052 vcons_unlock(scr);
1053 }
1054
1055 static void
1056 vcons_copyrows_noread(void *cookie, int srcrow, int dstrow, int nrows)
1057 {
1058 struct rasops_info *ri = cookie;
1059 struct vcons_screen *scr = ri->ri_hw;
1060 #ifdef VCONS_DRAW_INTR
1061 struct vcons_data *vd = scr->scr_vd;
1062 #endif
1063 vcons_lock(scr);
1064 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1065 int pos, l, c, offset, ppos;
1066
1067 #ifdef WSDISPLAY_SCROLLSUPPORT
1068 offset = scr->scr_current_offset;
1069 #else
1070 offset = 0;
1071 #endif
1072 ppos = ri->ri_cols * dstrow;
1073 pos = ppos + offset;
1074 for (l = dstrow; l < (dstrow + nrows); l++) {
1075 for (c = 0; c < ri->ri_cols; c++) {
1076 #ifdef VCONS_DRAW_INTR
1077 if ((scr->scr_chars[pos] != vd->chars[ppos]) ||
1078 (scr->scr_attrs[pos] != vd->attrs[ppos])) {
1079 scr->putchar(cookie, l, c,
1080 scr->scr_chars[pos], scr->scr_attrs[pos]);
1081 vd->chars[ppos] = scr->scr_chars[pos];
1082 vd->attrs[ppos] = scr->scr_attrs[pos];
1083 }
1084 #else
1085 scr->putchar(cookie, l, c, scr->scr_chars[pos],
1086 scr->scr_attrs[pos]);
1087 #endif
1088 pos++;
1089 ppos++;
1090 }
1091 }
1092 }
1093 vcons_unlock(scr);
1094 }
1095
1096 static void
1097 vcons_eraserows_buffer(void *cookie, int row, int nrows, long fillattr)
1098 {
1099 struct rasops_info *ri = cookie;
1100 struct vcons_screen *scr = ri->ri_hw;
1101 int start, end, i;
1102
1103 #ifdef WSDISPLAY_SCROLLSUPPORT
1104 int offset;
1105 offset = scr->scr_offset_to_zero;
1106
1107 start = ri->ri_cols * row + offset;
1108 end = ri->ri_cols * (row + nrows) + offset;
1109 #else
1110 start = ri->ri_cols * row;
1111 end = ri->ri_cols * (row + nrows);
1112 #endif
1113
1114 for (i = start; i < end; i++) {
1115 scr->scr_attrs[i] = fillattr;
1116 scr->scr_chars[i] = 0x20;
1117 }
1118
1119 #ifdef VCONS_DRAW_INTR
1120 atomic_inc_uint(&scr->scr_dirty);
1121 #endif
1122 }
1123
1124 #ifdef VCONS_DRAW_INTR
1125 static void
1126 vcons_eraserows_cached(void *cookie, int row, int nrows, long fillattr)
1127 {
1128 struct rasops_info *ri = cookie;
1129 struct vcons_screen *scr = ri->ri_hw;
1130 struct vcons_data *vd = scr->scr_vd;
1131 int i, pos = row * ri->ri_cols, end = (row+nrows) * ri->ri_cols;
1132
1133 for (i = pos; i < end; i++) {
1134 vd->chars[i] = 0x20;
1135 vd->attrs[i] = fillattr;
1136 }
1137 vd->eraserows(cookie, row, nrows, fillattr);
1138 }
1139 #endif
1140
1141 static void
1142 vcons_eraserows(void *cookie, int row, int nrows, long fillattr)
1143 {
1144 struct rasops_info *ri = cookie;
1145 struct vcons_screen *scr = ri->ri_hw;
1146
1147 vcons_eraserows_buffer(cookie, row, nrows, fillattr);
1148
1149 #if defined(VCONS_DRAW_INTR)
1150 if (scr->scr_vd->use_intr)
1151 return;
1152 #endif
1153
1154 vcons_lock(scr);
1155 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1156 #ifdef VCONS_DRAW_INTR
1157 vcons_eraserows_cached(cookie, row, nrows, fillattr);
1158 #else
1159 scr->scr_vd->eraserows(cookie, row, nrows, fillattr);
1160 #endif
1161 }
1162 vcons_unlock(scr);
1163 }
1164
1165 static void
1166 vcons_putchar_buffer(void *cookie, int row, int col, u_int c, long attr)
1167 {
1168 struct rasops_info *ri = cookie;
1169 struct vcons_screen *scr = ri->ri_hw;
1170 int pos;
1171
1172 #ifdef WSDISPLAY_SCROLLSUPPORT
1173 int offset;
1174 offset = scr->scr_offset_to_zero;
1175
1176 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) &&
1177 (col < ri->ri_cols)) {
1178 pos = col + row * ri->ri_cols;
1179 scr->scr_attrs[pos + offset] = attr;
1180 scr->scr_chars[pos + offset] = c;
1181 }
1182 #else
1183 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) &&
1184 (col < ri->ri_cols)) {
1185 pos = col + row * ri->ri_cols;
1186 scr->scr_attrs[pos] = attr;
1187 scr->scr_chars[pos] = c;
1188 }
1189 #endif
1190
1191 #ifdef VCONS_DRAW_INTR
1192 atomic_inc_uint(&scr->scr_dirty);
1193 #endif
1194 }
1195
1196 #ifdef VCONS_DRAW_INTR
1197 static void
1198 vcons_putchar_cached(void *cookie, int row, int col, u_int c, long attr)
1199 {
1200 struct rasops_info *ri = cookie;
1201 struct vcons_screen *scr = ri->ri_hw;
1202 struct vcons_data *vd = scr->scr_vd;
1203 int pos = row * ri->ri_cols + col;
1204
1205 if ((vd->chars == NULL) || (vd->attrs == NULL)) {
1206 scr->putchar(cookie, row, col, c, attr);
1207 return;
1208 }
1209 if ((vd->chars[pos] != c) || (vd->attrs[pos] != attr)) {
1210 vd->attrs[pos] = attr;
1211 vd->chars[pos] = c;
1212 scr->putchar(cookie, row, col, c, attr);
1213 }
1214 }
1215 #endif
1216
1217 static void
1218 vcons_putchar(void *cookie, int row, int col, u_int c, long attr)
1219 {
1220 struct rasops_info *ri = cookie;
1221 struct vcons_screen *scr = ri->ri_hw;
1222
1223 vcons_putchar_buffer(cookie, row, col, c, attr);
1224
1225 #if defined(VCONS_DRAW_INTR)
1226 if (scr->scr_vd->use_intr)
1227 return;
1228 #endif
1229
1230 vcons_lock(scr);
1231 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1232 #ifdef VCONS_DRAW_INTR
1233 vcons_putchar_cached(cookie, row, col, c, attr);
1234 #else
1235 scr->putchar(cookie, row, col, c, attr);
1236 #endif
1237 }
1238 vcons_unlock(scr);
1239 }
1240
1241 static void
1242 vcons_cursor(void *cookie, int on, int row, int col)
1243 {
1244 struct rasops_info *ri = cookie;
1245 struct vcons_screen *scr = ri->ri_hw;
1246
1247
1248 #if defined(VCONS_DRAW_INTR)
1249 if (scr->scr_vd->use_intr) {
1250 vcons_lock(scr);
1251 if (scr->scr_ri.ri_crow != row || scr->scr_ri.ri_ccol != col) {
1252 scr->scr_ri.ri_crow = row;
1253 scr->scr_ri.ri_ccol = col;
1254 atomic_inc_uint(&scr->scr_dirty);
1255 }
1256 vcons_unlock(scr);
1257 return;
1258 }
1259 #endif
1260
1261 vcons_lock(scr);
1262
1263 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1264 scr->scr_vd->cursor(cookie, on, row, col);
1265 } else {
1266 scr->scr_ri.ri_crow = row;
1267 scr->scr_ri.ri_ccol = col;
1268 }
1269 vcons_unlock(scr);
1270 }
1271
1272 /* methods to read/write characters via ioctl() */
1273
1274 static int
1275 vcons_putwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc)
1276 {
1277 long attr;
1278 struct rasops_info *ri;
1279 int error;
1280
1281 KASSERT(scr != NULL && wsc != NULL);
1282
1283 ri = &scr->scr_ri;
1284
1285 if (__predict_false((unsigned int)wsc->col > ri->ri_cols ||
1286 (unsigned int)wsc->row > ri->ri_rows))
1287 return (EINVAL);
1288
1289 if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) &&
1290 (wsc->col < ri->ri_cols)) {
1291
1292 error = ri->ri_ops.allocattr(ri, wsc->foreground,
1293 wsc->background, wsc->flags, &attr);
1294 if (error)
1295 return error;
1296 vcons_putchar(ri, wsc->row, wsc->col, wsc->letter, attr);
1297 #ifdef VCONS_DEBUG
1298 printf("vcons_putwschar(%d, %d, %x, %lx\n", wsc->row, wsc->col,
1299 wsc->letter, attr);
1300 #endif
1301 return 0;
1302 } else
1303 return EINVAL;
1304 }
1305
1306 static int
1307 vcons_getwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc)
1308 {
1309 int offset;
1310 long attr;
1311 struct rasops_info *ri;
1312
1313 KASSERT(scr != NULL && wsc != NULL);
1314
1315 ri = &scr->scr_ri;
1316
1317 if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) &&
1318 (wsc->col < ri->ri_cols)) {
1319
1320 offset = ri->ri_cols * wsc->row + wsc->col;
1321 #ifdef WSDISPLAY_SCROLLSUPPORT
1322 offset += scr->scr_offset_to_zero;
1323 #endif
1324 wsc->letter = scr->scr_chars[offset];
1325 attr = scr->scr_attrs[offset];
1326
1327 /*
1328 * this is ugly. We need to break up an attribute into colours and
1329 * flags but there's no rasops method to do that so we must rely on
1330 * the 'canonical' encoding.
1331 */
1332 #ifdef VCONS_DEBUG
1333 printf("vcons_getwschar: %d, %d, %x, %lx\n", wsc->row,
1334 wsc->col, wsc->letter, attr);
1335 #endif
1336 wsc->foreground = (attr >> 24) & 0xff;
1337 wsc->background = (attr >> 16) & 0xff;
1338 wsc->flags = attr & 0xff;
1339 return 0;
1340 } else
1341 return EINVAL;
1342 }
1343
1344 #ifdef WSDISPLAY_SCROLLSUPPORT
1345
1346 static void
1347 vcons_scroll(void *cookie, void *vs, int where)
1348 {
1349 struct vcons_screen *scr = vs;
1350
1351 if (where == 0) {
1352 scr->scr_line_wanted = 0;
1353 } else {
1354 scr->scr_line_wanted = scr->scr_line_wanted - where;
1355 if (scr->scr_line_wanted < 0)
1356 scr->scr_line_wanted = 0;
1357 if (scr->scr_line_wanted > scr->scr_lines_in_buffer)
1358 scr->scr_line_wanted = scr->scr_lines_in_buffer;
1359 }
1360
1361 if (scr->scr_line_wanted != scr->scr_current_line) {
1362
1363 vcons_do_scroll(scr);
1364 }
1365 }
1366
1367 static void
1368 vcons_do_scroll(struct vcons_screen *scr)
1369 {
1370 int dist, from, to, num;
1371 int r_offset, r_start;
1372 int i, j;
1373
1374 if (scr->scr_line_wanted == scr->scr_current_line)
1375 return;
1376 dist = scr->scr_line_wanted - scr->scr_current_line;
1377 scr->scr_current_line = scr->scr_line_wanted;
1378 scr->scr_current_offset = scr->scr_ri.ri_cols *
1379 (scr->scr_lines_in_buffer - scr->scr_current_line);
1380 if (abs(dist) >= scr->scr_ri.ri_rows) {
1381 vcons_redraw_screen(scr);
1382 return;
1383 }
1384 /* scroll and redraw only what we really have to */
1385 if (dist > 0) {
1386 /* we scroll down */
1387 from = 0;
1388 to = dist;
1389 num = scr->scr_ri.ri_rows - dist;
1390 /* now the redraw parameters */
1391 r_offset = scr->scr_current_offset;
1392 r_start = 0;
1393 } else {
1394 /* scrolling up */
1395 to = 0;
1396 from = -dist;
1397 num = scr->scr_ri.ri_rows + dist;
1398 r_offset = scr->scr_current_offset + num * scr->scr_ri.ri_cols;
1399 r_start = num;
1400 }
1401 scr->scr_vd->copyrows(scr, from, to, num);
1402 for (i = 0; i < abs(dist); i++) {
1403 for (j = 0; j < scr->scr_ri.ri_cols; j++) {
1404 #ifdef VCONS_DRAW_INTR
1405 vcons_putchar_cached(scr, i + r_start, j,
1406 scr->scr_chars[r_offset],
1407 scr->scr_attrs[r_offset]);
1408 #else
1409 scr->putchar(scr, i + r_start, j,
1410 scr->scr_chars[r_offset],
1411 scr->scr_attrs[r_offset]);
1412 #endif
1413 r_offset++;
1414 }
1415 }
1416
1417 if (scr->scr_line_wanted == 0) {
1418 /* this was a reset - need to draw the cursor */
1419 scr->scr_ri.ri_flg &= ~RI_CURSOR;
1420 scr->scr_vd->cursor(scr, 1, scr->scr_ri.ri_crow,
1421 scr->scr_ri.ri_ccol);
1422 }
1423 }
1424
1425 #endif /* WSDISPLAY_SCROLLSUPPORT */
1426
1427 #ifdef VCONS_DRAW_INTR
1428 static void
1429 vcons_intr(void *cookie)
1430 {
1431 struct vcons_data *vd = cookie;
1432
1433 softint_schedule(vd->intr_softint);
1434 }
1435
1436 static void
1437 vcons_softintr(void *cookie)
1438 {
1439 struct vcons_data *vd = cookie;
1440 struct vcons_screen *scr = vd->active;
1441 unsigned int dirty;
1442
1443 if (scr && vd->use_intr) {
1444 if (!SCREEN_IS_BUSY(scr)) {
1445 dirty = atomic_swap_uint(&scr->scr_dirty, 0);
1446 if (vd->use_intr == 2) {
1447 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) {
1448 vd->use_intr = 1;
1449 vcons_redraw_screen(scr);
1450 }
1451 } else if (dirty > 0) {
1452 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
1453 vcons_update_screen(scr);
1454 }
1455 }
1456 }
1457
1458 callout_schedule(&vd->intr, mstohz(33));
1459 }
1460
1461 static void
1462 vcons_init_thread(void *cookie)
1463 {
1464 struct vcons_data *vd = (struct vcons_data *)cookie;
1465
1466 vd->use_intr = 2;
1467 callout_schedule(&vd->intr, mstohz(33));
1468 kthread_exit(0);
1469 }
1470 #endif /* VCONS_DRAW_INTR */
1471
1472 void
1473 vcons_enable_polling(struct vcons_data *vd)
1474 {
1475 struct vcons_screen *scr = vd->active;
1476
1477 #ifdef VCONS_DRAW_INTR
1478 vd->use_intr = 0;
1479 #endif
1480
1481 if (scr && !SCREEN_IS_BUSY(scr)) {
1482 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
1483 vcons_redraw_screen(scr);
1484 }
1485 }
1486
1487 void
1488 vcons_disable_polling(struct vcons_data *vd)
1489 {
1490 #ifdef VCONS_DRAW_INTR
1491 struct vcons_screen *scr = vd->active;
1492
1493 if (!vd->intr_valid)
1494 return;
1495
1496 vd->use_intr = 2;
1497 if (scr)
1498 atomic_inc_uint(&scr->scr_dirty);
1499 #endif
1500 }
1501
1502 void
1503 vcons_hard_switch(struct vcons_screen *scr)
1504 {
1505 struct vcons_data *vd = scr->scr_vd;
1506 struct vcons_screen *oldscr = vd->active;
1507
1508 if (oldscr) {
1509 SCREEN_INVISIBLE(oldscr);
1510 oldscr->scr_ri.ri_flg &= ~RI_CURSOR;
1511 }
1512 SCREEN_VISIBLE(scr);
1513 vd->active = scr;
1514 vd->wanted = NULL;
1515
1516 if (vd->show_screen_cb != NULL)
1517 vd->show_screen_cb(scr, vd->show_screen_cookie);
1518 }
1519
1520 #ifdef VCONS_DRAW_INTR
1521 void
1522 vcons_invalidate_cache(struct vcons_data *vd)
1523 {
1524 int i;
1525
1526 if (vd->cells == 0)
1527 return;
1528
1529 for (i = 0; i > vd->cells; i++) {
1530 vd->chars[i] = -1;
1531 vd->attrs[i] = -1;
1532 }
1533 }
1534 #endif
1535