xref: /netbsd-src/sys/dev/wscons/wsdisplay_vcons.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: wsdisplay_vcons.c,v 1.17 2010/08/18 16:46:51 macallan 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.17 2010/08/18 16:46:51 macallan 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 
46 #include <dev/wscons/wsdisplayvar.h>
47 #include <dev/wscons/wsconsio.h>
48 #include <dev/wsfont/wsfont.h>
49 #include <dev/rasops/rasops.h>
50 
51 #include <dev/wscons/wsdisplay_vconsvar.h>
52 
53 #include "opt_wsemul.h"
54 #include "opt_wsdisplay_compat.h"
55 #include "opt_vcons.h"
56 
57 static void vcons_dummy_init_screen(void *, struct vcons_screen *, int,
58 	    long *);
59 
60 static int  vcons_ioctl(void *, void *, u_long, void *, int, struct lwp *);
61 static int  vcons_alloc_screen(void *, const struct wsscreen_descr *, void **,
62 	    int *, int *, long *);
63 static void vcons_free_screen(void *, void *);
64 static int  vcons_show_screen(void *, void *, int, void (*)(void *, int, int),
65 	    void *);
66 
67 #ifdef WSDISPLAY_SCROLLSUPPORT
68 static void vcons_scroll(void *, void *, int);
69 static void vcons_do_scroll(struct vcons_screen *);
70 #endif
71 
72 static void vcons_do_switch(void *);
73 
74 /* methods that work only on text buffers */
75 static void vcons_copycols_buffer(void *, int, int, int, int);
76 static void vcons_erasecols_buffer(void *, int, int, int, long);
77 static void vcons_copyrows_buffer(void *, int, int, int);
78 static void vcons_eraserows_buffer(void *, int, int, long);
79 static void vcons_putchar_buffer(void *, int, int, u_int, long);
80 
81 /*
82  * actual wrapper methods which call both the _buffer ones above and the
83  * driver supplied ones to do the drawing
84  */
85 static void vcons_copycols(void *, int, int, int, int);
86 static void vcons_erasecols(void *, int, int, int, long);
87 static void vcons_copyrows(void *, int, int, int);
88 static void vcons_eraserows(void *, int, int, long);
89 static void vcons_putchar(void *, int, int, u_int, long);
90 static void vcons_cursor(void *, int, int, int);
91 
92 /*
93  * methods that avoid framebuffer reads
94  */
95 static void vcons_copycols_noread(void *, int, int, int, int);
96 static void vcons_copyrows_noread(void *, int, int, int);
97 
98 
99 /* support for reading/writing text buffers. For wsmoused */
100 static int  vcons_putwschar(struct vcons_screen *, struct wsdisplay_char *);
101 static int  vcons_getwschar(struct vcons_screen *, struct wsdisplay_char *);
102 
103 static void vcons_lock(struct vcons_screen *);
104 static void vcons_unlock(struct vcons_screen *);
105 
106 #ifdef VCONS_SWITCH_ASYNC
107 static void vcons_kthread(void *);
108 #endif
109 
110 int
111 vcons_init(struct vcons_data *vd, void *cookie, struct wsscreen_descr *def,
112     struct wsdisplay_accessops *ao)
113 {
114 
115 	/* zero out everything so we can rely on untouched fields being 0 */
116 	memset(vd, 0, sizeof(struct vcons_data));
117 
118 	vd->cookie = cookie;
119 
120 	vd->init_screen = vcons_dummy_init_screen;
121 	vd->show_screen_cb = NULL;
122 
123 	/* keep a copy of the accessops that we replace below with our
124 	 * own wrappers */
125 	vd->ioctl = ao->ioctl;
126 
127 	/* configure the accessops */
128 	ao->ioctl = vcons_ioctl;
129 	ao->alloc_screen = vcons_alloc_screen;
130 	ao->free_screen = vcons_free_screen;
131 	ao->show_screen = vcons_show_screen;
132 #ifdef WSDISPLAY_SCROLLSUPPORT
133 	ao->scroll = vcons_scroll;
134 #endif
135 
136 	LIST_INIT(&vd->screens);
137 	vd->active = NULL;
138 	vd->wanted = NULL;
139 	vd->currenttype = def;
140 	callout_init(&vd->switch_callout, 0);
141 	callout_setfunc(&vd->switch_callout, vcons_do_switch, vd);
142 
143 	/*
144 	 * a lock to serialize access to the framebuffer.
145 	 * when switching screens we need to make sure there's no rasops
146 	 * operation in progress
147 	 */
148 #ifdef DIAGNOSTIC
149 	vd->switch_poll_count = 0;
150 #endif
151 #ifdef VCONS_SWITCH_ASYNC
152 	kthread_create(PRI_NONE, 0, NULL, vcons_kthread, vd,
153 	    &vd->redraw_thread, "vcons_draw");
154 #endif
155 	return 0;
156 }
157 
158 static void
159 vcons_lock(struct vcons_screen *scr)
160 {
161 #ifdef VCONS_PARANOIA
162 	int s;
163 
164 	s = splhigh();
165 #endif
166 	SCREEN_BUSY(scr);
167 #ifdef VCONS_PARANOIA
168 	splx(s);
169 #endif
170 }
171 
172 static void
173 vcons_unlock(struct vcons_screen *scr)
174 {
175 #ifdef VCONS_PARANOIA
176 	int s;
177 
178 	s = splhigh();
179 #endif
180 	SCREEN_IDLE(scr);
181 #ifdef VCONS_PARANOIA
182 	splx(s);
183 #endif
184 #ifdef VCONS_SWITCH_ASYNC
185 	wakeup(&scr->scr_vd->done_drawing);
186 #endif
187 }
188 
189 static void
190 vcons_dummy_init_screen(void *cookie,
191     struct vcons_screen *scr, int exists,
192     long *defattr)
193 {
194 
195 	/*
196 	 * default init_screen() method.
197 	 * Needs to be overwritten so we bitch and whine in case anyone ends
198 	 * up in here.
199 	 */
200 	printf("vcons_init_screen: dummy function called. Your driver is "
201 	       "supposed to supply a replacement for proper operation\n");
202 }
203 
204 int
205 vcons_init_screen(struct vcons_data *vd, struct vcons_screen *scr,
206     int existing, long *defattr)
207 {
208 	struct rasops_info *ri = &scr->scr_ri;
209 	int cnt, i;
210 
211 	scr->scr_cookie = vd->cookie;
212 	scr->scr_vd = scr->scr_origvd = vd;
213 	scr->scr_busy = 0;
214 
215 	/*
216 	 * call the driver-supplied init_screen function which is expected
217 	 * to set up rasops_info, override cursor() and probably others
218 	 */
219 	vd->init_screen(vd->cookie, scr, existing, defattr);
220 
221 	/*
222 	 * save the non virtual console aware rasops and replace them with
223 	 * our wrappers
224 	 */
225 	vd->eraserows = ri->ri_ops.eraserows;
226 	vd->copyrows  = ri->ri_ops.copyrows;
227 	vd->erasecols = ri->ri_ops.erasecols;
228 	vd->copycols  = ri->ri_ops.copycols;
229 	vd->putchar   = ri->ri_ops.putchar;
230 	vd->cursor    = ri->ri_ops.cursor;
231 
232 	ri->ri_ops.eraserows = vcons_eraserows;
233 	ri->ri_ops.erasecols = vcons_erasecols;
234 	ri->ri_ops.putchar   = vcons_putchar;
235 	ri->ri_ops.cursor    = vcons_cursor;
236 
237 	if (scr->scr_flags & VCONS_DONT_READ) {
238 		ri->ri_ops.copyrows  = vcons_copyrows_noread;
239 		ri->ri_ops.copycols  = vcons_copycols_noread;
240 	} else {
241 		ri->ri_ops.copyrows  = vcons_copyrows;
242 		ri->ri_ops.copycols  = vcons_copycols;
243 	}
244 
245 	ri->ri_hw = scr;
246 
247 	/*
248 	 * we allocate both chars and attributes in one chunk, attributes first
249 	 * because they have the (potentially) bigger alignment
250 	 */
251 #ifdef WSDISPLAY_SCROLLSUPPORT
252 	cnt = (ri->ri_rows + WSDISPLAY_SCROLLBACK_LINES) * ri->ri_cols;
253 	scr->scr_lines_in_buffer = WSDISPLAY_SCROLLBACK_LINES;
254 	scr->scr_current_line = 0;
255 	scr->scr_line_wanted = 0;
256 	scr->scr_offset_to_zero = ri->ri_cols * WSDISPLAY_SCROLLBACK_LINES;
257 	scr->scr_current_offset = scr->scr_offset_to_zero;
258 #else
259 	cnt = ri->ri_rows * ri->ri_cols;
260 #endif
261 	scr->scr_attrs = (long *)malloc(cnt * (sizeof(long) +
262 	    sizeof(uint16_t)), M_DEVBUF, M_WAITOK);
263 	if (scr->scr_attrs == NULL)
264 		return ENOMEM;
265 
266 	scr->scr_chars = (uint16_t *)&scr->scr_attrs[cnt];
267 
268 	ri->ri_ops.allocattr(ri, WS_DEFAULT_FG, WS_DEFAULT_BG, 0, defattr);
269 	scr->scr_defattr = *defattr;
270 
271 	/*
272 	 * fill the attribute buffer with *defattr, chars with 0x20
273 	 * since we don't know if the driver tries to mimic firmware output or
274 	 * reset everything we do nothing to VRAM here, any driver that feels
275 	 * the need to clear screen or something will have to do it on its own
276 	 * Additional screens will start out in the background anyway so
277 	 * cleaning or not only really affects the initial console screen
278 	 */
279 	for (i = 0; i < cnt; i++) {
280 		scr->scr_attrs[i] = *defattr;
281 		scr->scr_chars[i] = 0x20;
282 	}
283 
284 	if(vd->active == NULL) {
285 		vd->active = scr;
286 		SCREEN_VISIBLE(scr);
287 	}
288 
289 	if (existing) {
290 		SCREEN_VISIBLE(scr);
291 		vd->active = scr;
292 	} else {
293 		SCREEN_INVISIBLE(scr);
294 	}
295 
296 	LIST_INSERT_HEAD(&vd->screens, scr, next);
297 	return 0;
298 }
299 
300 static void
301 vcons_do_switch(void *arg)
302 {
303 	struct vcons_data *vd = arg;
304 	struct vcons_screen *scr, *oldscr;
305 
306 	scr = vd->wanted;
307 	if (!scr) {
308 		printf("vcons_switch_screen: disappeared\n");
309 		vd->switch_cb(vd->switch_cb_arg, EIO, 0);
310 		return;
311 	}
312 	oldscr = vd->active; /* can be NULL! */
313 
314 	/*
315 	 * if there's an old, visible screen we mark it invisible and wait
316 	 * until it's not busy so we can safely switch
317 	 */
318 	if (oldscr != NULL) {
319 		SCREEN_INVISIBLE(oldscr);
320 		if (SCREEN_IS_BUSY(oldscr)) {
321 			callout_schedule(&vd->switch_callout, 1);
322 #ifdef DIAGNOSTIC
323 			/* bitch if we wait too long */
324 			vd->switch_poll_count++;
325 			if (vd->switch_poll_count > 100) {
326 				panic("vcons: screen still busy");
327 			}
328 #endif
329 			return;
330 		}
331 		/* invisible screen -> no visible cursor image */
332 		oldscr->scr_ri.ri_flg &= ~RI_CURSOR;
333 #ifdef DIAGNOSTIC
334 		vd->switch_poll_count = 0;
335 #endif
336 	}
337 
338 	if (scr == oldscr)
339 		return;
340 
341 #ifdef DIAGNOSTIC
342 	if (SCREEN_IS_VISIBLE(scr))
343 		printf("vcons_switch_screen: already active");
344 #endif
345 
346 #ifdef notyet
347 	if (vd->currenttype != type) {
348 		vcons_set_screentype(vd, type);
349 		vd->currenttype = type;
350 	}
351 #endif
352 
353 	SCREEN_VISIBLE(scr);
354 	vd->active = scr;
355 	vd->wanted = NULL;
356 
357 	if (vd->show_screen_cb != NULL)
358 		vd->show_screen_cb(scr);
359 
360 	if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
361 		vcons_redraw_screen(scr);
362 
363 	if (vd->switch_cb)
364 		vd->switch_cb(vd->switch_cb_arg, 0, 0);
365 }
366 
367 void
368 vcons_redraw_screen(struct vcons_screen *scr)
369 {
370 	uint16_t *charptr = scr->scr_chars;
371 	long *attrptr = scr->scr_attrs;
372 	struct rasops_info *ri = &scr->scr_ri;
373 	int i, j, offset;
374 
375 	vcons_lock(scr);
376 	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
377 
378 		/*
379 		 * only clear the screen when RI_FULLCLEAR is set since we're
380 		 * going to overwrite every single character cell anyway
381 		 */
382 		if (ri->ri_flg & RI_FULLCLEAR) {
383 			scr->scr_vd->eraserows(ri, 0, ri->ri_rows,
384 			    scr->scr_defattr);
385 		}
386 
387 		/* redraw the screen */
388 #ifdef WSDISPLAY_SCROLLSUPPORT
389 		offset = scr->scr_current_offset;
390 #else
391 		offset = 0;
392 #endif
393 		for (i = 0; i < ri->ri_rows; i++) {
394 			for (j = 0; j < ri->ri_cols; j++) {
395 				/*
396 				 * no need to use the wrapper function - we
397 				 * don't change any characters or attributes
398 				 * and we already made sure the screen we're
399 				 * working on is visible
400 				 */
401 				scr->scr_vd->putchar(ri, i, j,
402 				    charptr[offset], attrptr[offset]);
403 				offset++;
404 			}
405 		}
406 		ri->ri_flg &= ~RI_CURSOR;
407 		scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol);
408 	}
409 	vcons_unlock(scr);
410 }
411 
412 static int
413 vcons_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
414 	struct lwp *l)
415 {
416 	struct vcons_data *vd = v;
417 	int error;
418 
419 	switch (cmd) {
420 	case WSDISPLAYIO_GETWSCHAR:
421 		error = vcons_getwschar((struct vcons_screen *)vs,
422 			(struct wsdisplay_char *)data);
423 		break;
424 
425 	case WSDISPLAYIO_PUTWSCHAR:
426 		error = vcons_putwschar((struct vcons_screen *)vs,
427 			(struct wsdisplay_char *)data);
428 		break;
429 
430 	default:
431 		if (vd->ioctl != NULL)
432 			error = (*vd->ioctl)(v, vs, cmd, data, flag, l);
433 		else
434 			error = EPASSTHROUGH;
435 	}
436 
437 	return error;
438 }
439 
440 static int
441 vcons_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
442     int *curxp, int *curyp, long *defattrp)
443 {
444 	struct vcons_data *vd = v;
445 	struct vcons_screen *scr;
446 	int ret;
447 
448 	scr = malloc(sizeof(struct vcons_screen), M_DEVBUF, M_WAITOK | M_ZERO);
449 	if (scr == NULL)
450 		return ENOMEM;
451 
452 	scr->scr_flags = 0;
453 	scr->scr_status = 0;
454 	scr->scr_busy = 0;
455 	scr->scr_type = type;
456 
457 	ret = vcons_init_screen(vd, scr, 0, defattrp);
458 	if (ret != 0) {
459 		free(scr, M_DEVBUF);
460 		return ret;
461 	}
462 
463 	if (vd->active == NULL) {
464 		SCREEN_VISIBLE(scr);
465 		vd->active = scr;
466 		vd->currenttype = type;
467 	}
468 
469 	*cookiep = scr;
470 	*curxp = scr->scr_ri.ri_ccol;
471 	*curyp = scr->scr_ri.ri_crow;
472 	return 0;
473 }
474 
475 static void
476 vcons_free_screen(void *v, void *cookie)
477 {
478 	struct vcons_data *vd = v;
479 	struct vcons_screen *scr = cookie;
480 
481 	vcons_lock(scr);
482 	/* there should be no rasops activity here */
483 
484 	LIST_REMOVE(scr, next);
485 
486 	if ((scr->scr_flags & VCONS_SCREEN_IS_STATIC) == 0) {
487 		free(scr->scr_attrs, M_DEVBUF);
488 		free(scr, M_DEVBUF);
489 	} else {
490 		/*
491 		 * maybe we should just restore the old rasops_info methods
492 		 * and free the character/attribute buffer here?
493 		 */
494 #ifdef VCONS_DEBUG
495 		panic("vcons_free_screen: console");
496 #else
497 		printf("vcons_free_screen: console\n");
498 #endif
499 	}
500 
501 	if (vd->active == scr)
502 		vd->active = NULL;
503 }
504 
505 static int
506 vcons_show_screen(void *v, void *cookie, int waitok,
507     void (*cb)(void *, int, int), void *cb_arg)
508 {
509 	struct vcons_data *vd = v;
510 	struct vcons_screen *scr;
511 
512 	scr = cookie;
513 	if (scr == vd->active)
514 		return 0;
515 
516 	vd->wanted = scr;
517 	vd->switch_cb = cb;
518 	vd->switch_cb_arg = cb_arg;
519 #ifdef VCONS_SWITCH_ASYNC
520 	wakeup(&vd->start_drawing);
521 	return EAGAIN;
522 #else
523 	if (cb) {
524 		callout_schedule(&vd->switch_callout, 0);
525 		return EAGAIN;
526 	}
527 
528 	vcons_do_switch(vd);
529 	return 0;
530 #endif
531 }
532 
533 /* wrappers for rasops_info methods */
534 
535 static void
536 vcons_copycols_buffer(void *cookie, int row, int srccol, int dstcol, int ncols)
537 {
538 	struct rasops_info *ri = cookie;
539 	struct vcons_screen *scr = ri->ri_hw;
540 	int from = srccol + row * ri->ri_cols;
541 	int to = dstcol + row * ri->ri_cols;
542 
543 #ifdef WSDISPLAY_SCROLLSUPPORT
544 	int offset;
545 	offset = scr->scr_offset_to_zero;
546 
547 	memmove(&scr->scr_attrs[offset + to], &scr->scr_attrs[offset + from],
548 	    ncols * sizeof(long));
549 	memmove(&scr->scr_chars[offset + to], &scr->scr_chars[offset + from],
550 	    ncols * sizeof(uint16_t));
551 #else
552 	memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
553 	    ncols * sizeof(long));
554 	memmove(&scr->scr_chars[to], &scr->scr_chars[from],
555 	    ncols * sizeof(uint16_t));
556 #endif
557 }
558 
559 static void
560 vcons_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
561 {
562 	struct rasops_info *ri = cookie;
563 	struct vcons_screen *scr = ri->ri_hw;
564 
565 	vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols);
566 
567 	vcons_lock(scr);
568 	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
569 		scr->scr_vd->copycols(cookie, row, srccol, dstcol, ncols);
570 	}
571 	vcons_unlock(scr);
572 }
573 
574 static void
575 vcons_copycols_noread(void *cookie, int row, int srccol, int dstcol, int ncols)
576 {
577 	struct rasops_info *ri = cookie;
578 	struct vcons_screen *scr = ri->ri_hw;
579 
580 	vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols);
581 
582 	vcons_lock(scr);
583 	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
584 		int pos, c, offset;
585 
586 #ifdef WSDISPLAY_SCROLLSUPPORT
587 		offset = scr->scr_current_offset;
588 #else
589 		offset = 0;
590 #endif
591 		pos = ri->ri_cols * row + dstcol + offset;
592 		for (c = dstcol; c < (dstcol + ncols); c++) {
593 			scr->scr_vd->putchar(cookie, row, c,
594 			   scr->scr_chars[pos], scr->scr_attrs[pos]);
595 			pos++;
596 		}
597 	}
598 	vcons_unlock(scr);
599 }
600 
601 static void
602 vcons_erasecols_buffer(void *cookie, int row, int startcol, int ncols, long fillattr)
603 {
604 	struct rasops_info *ri = cookie;
605 	struct vcons_screen *scr = ri->ri_hw;
606 	int start = startcol + row * ri->ri_cols;
607 	int end = start + ncols, i;
608 
609 #ifdef WSDISPLAY_SCROLLSUPPORT
610 	int offset;
611 	offset = scr->scr_offset_to_zero;
612 
613 	for (i = start; i < end; i++) {
614 		scr->scr_attrs[offset + i] = fillattr;
615 		scr->scr_chars[offset + i] = 0x20;
616 	}
617 #else
618 	for (i = start; i < end; i++) {
619 		scr->scr_attrs[i] = fillattr;
620 		scr->scr_chars[i] = 0x20;
621 	}
622 #endif
623 }
624 
625 static void
626 vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
627 {
628 	struct rasops_info *ri = cookie;
629 	struct vcons_screen *scr = ri->ri_hw;
630 
631 	vcons_erasecols_buffer(cookie, row, startcol, ncols, fillattr);
632 
633 	vcons_lock(scr);
634 	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
635 		scr->scr_vd->erasecols(cookie, row, startcol, ncols,
636 		    fillattr);
637 	}
638 	vcons_unlock(scr);
639 }
640 
641 static void
642 vcons_copyrows_buffer(void *cookie, int srcrow, int dstrow, int nrows)
643 {
644 	struct rasops_info *ri = cookie;
645 	struct vcons_screen *scr = ri->ri_hw;
646 	int from, to, len;
647 
648 #ifdef WSDISPLAY_SCROLLSUPPORT
649 	int offset;
650 	offset = scr->scr_offset_to_zero;
651 
652 	/* do we need to scroll the back buffer? */
653 	if (dstrow == 0) {
654 		from = ri->ri_cols * srcrow;
655 		to = ri->ri_cols * dstrow;
656 
657 		memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
658 		    scr->scr_offset_to_zero * sizeof(long));
659 		memmove(&scr->scr_chars[to], &scr->scr_chars[from],
660 		    scr->scr_offset_to_zero * sizeof(uint16_t));
661 	}
662 	from = ri->ri_cols * srcrow + offset;
663 	to = ri->ri_cols * dstrow + offset;
664 	len = ri->ri_cols * nrows;
665 
666 #else
667 	from = ri->ri_cols * srcrow;
668 	to = ri->ri_cols * dstrow;
669 	len = ri->ri_cols * nrows;
670 #endif
671 	memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
672 	    len * sizeof(long));
673 	memmove(&scr->scr_chars[to], &scr->scr_chars[from],
674 	    len * sizeof(uint16_t));
675 }
676 
677 static void
678 vcons_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
679 {
680 	struct rasops_info *ri = cookie;
681 	struct vcons_screen *scr = ri->ri_hw;
682 
683 	vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows);
684 
685 	vcons_lock(scr);
686 	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
687 		scr->scr_vd->copyrows(cookie, srcrow, dstrow, nrows);
688 	}
689 	vcons_unlock(scr);
690 }
691 
692 static void
693 vcons_copyrows_noread(void *cookie, int srcrow, int dstrow, int nrows)
694 {
695 	struct rasops_info *ri = cookie;
696 	struct vcons_screen *scr = ri->ri_hw;
697 
698 	vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows);
699 
700 	vcons_lock(scr);
701 	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
702 		int pos, l, c, offset;
703 
704 #ifdef WSDISPLAY_SCROLLSUPPORT
705 		offset = scr->scr_current_offset;
706 #else
707 		offset = 0;
708 #endif
709 		pos = ri->ri_cols * dstrow + offset;
710 		for (l = dstrow; l < (dstrow + nrows); l++) {
711 			for (c = 0; c < ri->ri_cols; c++) {
712 				scr->scr_vd->putchar(cookie, l, c,
713 				   scr->scr_chars[pos], scr->scr_attrs[pos]);
714 				pos++;
715 			}
716 		}
717 	}
718 	vcons_unlock(scr);
719 }
720 
721 static void
722 vcons_eraserows_buffer(void *cookie, int row, int nrows, long fillattr)
723 {
724 	struct rasops_info *ri = cookie;
725 	struct vcons_screen *scr = ri->ri_hw;
726 	int start, end, i;
727 
728 #ifdef WSDISPLAY_SCROLLSUPPORT
729 	int offset;
730 	offset = scr->scr_offset_to_zero;
731 
732 	start = ri->ri_cols * row + offset;
733 	end = ri->ri_cols * (row + nrows) + offset;
734 #else
735 	start = ri->ri_cols * row;
736 	end = ri->ri_cols * (row + nrows);
737 #endif
738 
739 	for (i = start; i < end; i++) {
740 		scr->scr_attrs[i] = fillattr;
741 		scr->scr_chars[i] = 0x20;
742 	}
743 }
744 
745 static void
746 vcons_eraserows(void *cookie, int row, int nrows, long fillattr)
747 {
748 	struct rasops_info *ri = cookie;
749 	struct vcons_screen *scr = ri->ri_hw;
750 
751 	vcons_eraserows_buffer(cookie, row, nrows, fillattr);
752 
753 	vcons_lock(scr);
754 	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
755 		scr->scr_vd->eraserows(cookie, row, nrows, fillattr);
756 	}
757 	vcons_unlock(scr);
758 }
759 
760 static void
761 vcons_putchar_buffer(void *cookie, int row, int col, u_int c, long attr)
762 {
763 	struct rasops_info *ri = cookie;
764 	struct vcons_screen *scr = ri->ri_hw;
765 	int pos;
766 
767 #ifdef WSDISPLAY_SCROLLSUPPORT
768 	int offset;
769 	offset = scr->scr_offset_to_zero;
770 
771 	if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) &&
772 	     (col < ri->ri_cols)) {
773 		pos = col + row * ri->ri_cols;
774 		scr->scr_attrs[pos + offset] = attr;
775 		scr->scr_chars[pos + offset] = c;
776 	}
777 #else
778 	if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) &&
779 	     (col < ri->ri_cols)) {
780 		pos = col + row * ri->ri_cols;
781 		scr->scr_attrs[pos] = attr;
782 		scr->scr_chars[pos] = c;
783 	}
784 #endif
785 }
786 
787 static void
788 vcons_putchar(void *cookie, int row, int col, u_int c, long attr)
789 {
790 	struct rasops_info *ri = cookie;
791 	struct vcons_screen *scr = ri->ri_hw;
792 
793 	vcons_putchar_buffer(cookie, row, col, c, attr);
794 
795 	vcons_lock(scr);
796 	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
797 		scr->scr_vd->putchar(cookie, row, col, c, attr);
798 	}
799 	vcons_unlock(scr);
800 }
801 
802 static void
803 vcons_cursor(void *cookie, int on, int row, int col)
804 {
805 	struct rasops_info *ri = cookie;
806 	struct vcons_screen *scr = ri->ri_hw;
807 
808 	vcons_lock(scr);
809 	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
810 		scr->scr_vd->cursor(cookie, on, row, col);
811 	} else {
812 		scr->scr_ri.ri_crow = row;
813 		scr->scr_ri.ri_ccol = col;
814 	}
815 	vcons_unlock(scr);
816 }
817 
818 /* methods to read/write characters via ioctl() */
819 
820 static int
821 vcons_putwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc)
822 {
823 	long attr;
824 	struct rasops_info *ri;
825 
826 	KASSERT(scr != NULL && wsc != NULL);
827 
828 	ri = &scr->scr_ri;
829 
830 	if (__predict_false((unsigned int)wsc->col > ri->ri_cols ||
831 	    (unsigned int)wsc->row > ri->ri_rows))
832 			return (EINVAL);
833 
834 	if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) &&
835 	     (wsc->col < ri->ri_cols)) {
836 
837 		ri->ri_ops.allocattr(ri, wsc->foreground, wsc->background,
838 		    wsc->flags, &attr);
839 		vcons_putchar(ri, wsc->row, wsc->col, wsc->letter, attr);
840 #ifdef VCONS_DEBUG
841 		printf("vcons_putwschar(%d, %d, %x, %lx\n", wsc->row, wsc->col,
842 		    wsc->letter, attr);
843 #endif
844 		return 0;
845 	} else
846 		return EINVAL;
847 }
848 
849 static int
850 vcons_getwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc)
851 {
852 	int offset;
853 	long attr;
854 	struct rasops_info *ri;
855 
856 	KASSERT(scr != NULL && wsc != NULL);
857 
858 	ri = &scr->scr_ri;
859 
860 	if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) &&
861 	     (wsc->col < ri->ri_cols)) {
862 
863 		offset = ri->ri_cols * wsc->row + wsc->col;
864 #ifdef WSDISPLAY_SCROLLSUPPORT
865 		offset += scr->scr_offset_to_zero;
866 #endif
867 		wsc->letter = scr->scr_chars[offset];
868 		attr = scr->scr_attrs[offset];
869 
870 		/*
871 		 * this is ugly. We need to break up an attribute into colours and
872 		 * flags but there's no rasops method to do that so we must rely on
873 		 * the 'canonical' encoding.
874 		 */
875 #ifdef VCONS_DEBUG
876 		printf("vcons_getwschar: %d, %d, %x, %lx\n", wsc->row,
877 		    wsc->col, wsc->letter, attr);
878 #endif
879 		wsc->foreground = (attr >> 24) & 0xff;
880 		wsc->background = (attr >> 16) & 0xff;
881 		wsc->flags      = attr & 0xff;
882 		return 0;
883 	} else
884 		return EINVAL;
885 }
886 
887 #ifdef WSDISPLAY_SCROLLSUPPORT
888 
889 static void
890 vcons_scroll(void *cookie, void *vs, int where)
891 {
892 	struct vcons_screen *scr = vs;
893 
894 	if (where == 0) {
895 		scr->scr_line_wanted = 0;
896 	} else {
897 		scr->scr_line_wanted = scr->scr_line_wanted - where;
898 		if (scr->scr_line_wanted < 0)
899 			scr->scr_line_wanted = 0;
900 		if (scr->scr_line_wanted > scr->scr_lines_in_buffer)
901 			scr->scr_line_wanted = scr->scr_lines_in_buffer;
902 	}
903 
904 	if (scr->scr_line_wanted != scr->scr_current_line) {
905 
906 		vcons_do_scroll(scr);
907 	}
908 }
909 
910 static void
911 vcons_do_scroll(struct vcons_screen *scr)
912 {
913 	int dist, from, to, num;
914 	int r_offset, r_start;
915 	int i, j;
916 
917 	if (scr->scr_line_wanted == scr->scr_current_line)
918 		return;
919 	dist = scr->scr_line_wanted - scr->scr_current_line;
920 	scr->scr_current_line = scr->scr_line_wanted;
921 	scr->scr_current_offset = scr->scr_ri.ri_cols *
922 	    (scr->scr_lines_in_buffer - scr->scr_current_line);
923 	if (abs(dist) >= scr->scr_ri.ri_rows) {
924 		vcons_redraw_screen(scr);
925 		return;
926 	}
927 	/* scroll and redraw only what we really have to */
928 	if (dist > 0) {
929 		/* we scroll down */
930 		from = 0;
931 		to = dist;
932 		num = scr->scr_ri.ri_rows - dist;
933 		/* now the redraw parameters */
934 		r_offset = scr->scr_current_offset;
935 		r_start = 0;
936 	} else {
937 		/* scrolling up */
938 		to = 0;
939 		from = -dist;
940 		num = scr->scr_ri.ri_rows + dist;
941 		r_offset = scr->scr_current_offset + num * scr->scr_ri.ri_cols;
942 		r_start = num;
943 	}
944 	scr->scr_vd->copyrows(scr, from, to, num);
945 	for (i = 0; i < abs(dist); i++) {
946 		for (j = 0; j < scr->scr_ri.ri_cols; j++) {
947 			scr->scr_vd->putchar(scr, i + r_start, j,
948 			    scr->scr_chars[r_offset],
949 			    scr->scr_attrs[r_offset]);
950 			r_offset++;
951 		}
952 	}
953 
954 	if (scr->scr_line_wanted == 0) {
955 		/* this was a reset - need to draw the cursor */
956 		scr->scr_ri.ri_flg &= ~RI_CURSOR;
957 		scr->scr_vd->cursor(scr, 1, scr->scr_ri.ri_crow,
958 		    scr->scr_ri.ri_ccol);
959 	}
960 }
961 
962 #endif /* WSDISPLAY_SCROLLSUPPORT */
963 
964 /* async drawing using a kernel thread */
965 
966 #ifdef VCONS_SWITCH_ASYNC
967 static void
968 vcons_kthread(void *cookie)
969 {
970 	struct vcons_data *vd = cookie;
971 	struct vcons_screen *scr;
972 	int sec = hz;
973 
974 	while (1) {
975 
976 		tsleep(&vd->start_drawing, 0, "vc_idle", sec);
977 		if ((vd->wanted != vd->active) && (vd->wanted != NULL)) {
978 			/*
979 			 * we need to switch screens
980 			 * so first we mark the active screen as invisible
981 			 * and wait until it's idle
982 			 */
983 			scr = vd->wanted;
984 			SCREEN_INVISIBLE(vd->active);
985 			while (SCREEN_IS_BUSY(vd->active)) {
986 
987 				tsleep(&vd->done_drawing, 0, "vc_wait", sec);
988 			}
989 			/*
990 			 * now we mark the wanted screen busy so nobody
991 			 * messes around while we redraw it
992 			 */
993 			vd->active = scr;
994 			vd->wanted = NULL;
995 			SCREEN_VISIBLE(scr);
996 
997 			if (vd->show_screen_cb != NULL)
998 				vd->show_screen_cb(scr);
999 
1000 			if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
1001 				vcons_redraw_screen(scr);
1002 
1003 			if (vd->switch_cb)
1004 				vd->switch_cb(vd->switch_cb_arg, 0, 0);
1005 		}
1006 	}
1007 }
1008 #endif /* VCONS_SWITCH_ASYNC */
1009