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