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