1/*	$NetBSD: wsdisplay_vcons.c,v 1.26 2011/05/25 06:13:29 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.26 2011/05/25 06:13:29 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#include <sys/atomic.h>
46
47#include <dev/wscons/wsdisplayvar.h>
48#include <dev/wscons/wsconsio.h>
49#include <dev/wsfont/wsfont.h>
50#include <dev/rasops/rasops.h>
51
52#include <dev/wscons/wsdisplay_vconsvar.h>
53
54#include "opt_wsemul.h"
55#include "opt_wsdisplay_compat.h"
56#include "opt_vcons.h"
57
58static void vcons_dummy_init_screen(void *, struct vcons_screen *, int,
59	    long *);
60
61static int  vcons_ioctl(void *, void *, u_long, void *, int, struct lwp *);
62static int  vcons_alloc_screen(void *, const struct wsscreen_descr *, void **,
63	    int *, int *, long *);
64static void vcons_free_screen(void *, void *);
65static int  vcons_show_screen(void *, void *, int, void (*)(void *, int, int),
66	    void *);
67
68#ifdef WSDISPLAY_SCROLLSUPPORT
69static void vcons_scroll(void *, void *, int);
70static void vcons_do_scroll(struct vcons_screen *);
71#endif
72
73static void vcons_do_switch(void *);
74
75/* methods that work only on text buffers */
76static void vcons_copycols_buffer(void *, int, int, int, int);
77static void vcons_erasecols_buffer(void *, int, int, int, long);
78static void vcons_copyrows_buffer(void *, int, int, int);
79static void vcons_eraserows_buffer(void *, int, int, long);
80static void vcons_putchar_buffer(void *, int, int, u_int, long);
81
82/*
83 * actual wrapper methods which call both the _buffer ones above and the
84 * driver supplied ones to do the drawing
85 */
86static void vcons_copycols(void *, int, int, int, int);
87static void vcons_erasecols(void *, int, int, int, long);
88static void vcons_copyrows(void *, int, int, int);
89static void vcons_eraserows(void *, int, int, long);
90static void vcons_putchar(void *, int, int, u_int, long);
91#ifdef VCONS_DRAW_INTR
92static void vcons_putchar_cached(void *, int, int, u_int, long);
93#endif
94static void vcons_cursor(void *, int, int, int);
95
96/*
97 * methods that avoid framebuffer reads
98 */
99static void vcons_copycols_noread(void *, int, int, int, int);
100static void vcons_copyrows_noread(void *, int, int, int);
101
102
103/* support for reading/writing text buffers. For wsmoused */
104static int  vcons_putwschar(struct vcons_screen *, struct wsdisplay_char *);
105static int  vcons_getwschar(struct vcons_screen *, struct wsdisplay_char *);
106
107static void vcons_lock(struct vcons_screen *);
108static void vcons_unlock(struct vcons_screen *);
109
110#ifdef VCONS_DRAW_INTR
111static void vcons_intr(void *);
112static void vcons_softintr(void *);
113static void vcons_intr_enable(device_t);
114#endif
115
116int
117vcons_init(struct vcons_data *vd, void *cookie, struct wsscreen_descr *def,
118    struct wsdisplay_accessops *ao)
119{
120
121	/* zero out everything so we can rely on untouched fields being 0 */
122	memset(vd, 0, sizeof(struct vcons_data));
123
124	vd->cookie = cookie;
125
126	vd->init_screen = vcons_dummy_init_screen;
127	vd->show_screen_cb = NULL;
128
129	/* keep a copy of the accessops that we replace below with our
130	 * own wrappers */
131	vd->ioctl = ao->ioctl;
132
133	/* configure the accessops */
134	ao->ioctl = vcons_ioctl;
135	ao->alloc_screen = vcons_alloc_screen;
136	ao->free_screen = vcons_free_screen;
137	ao->show_screen = vcons_show_screen;
138#ifdef WSDISPLAY_SCROLLSUPPORT
139	ao->scroll = vcons_scroll;
140#endif
141
142	LIST_INIT(&vd->screens);
143	vd->active = NULL;
144	vd->wanted = NULL;
145	vd->currenttype = def;
146	callout_init(&vd->switch_callout, 0);
147	callout_setfunc(&vd->switch_callout, vcons_do_switch, vd);
148#ifdef VCONS_DRAW_INTR
149	vd->cells = 0;
150	vd->attrs = NULL;
151	vd->chars = NULL;
152	vd->cursor_offset = -1;
153#endif
154
155	/*
156	 * a lock to serialize access to the framebuffer.
157	 * when switching screens we need to make sure there's no rasops
158	 * operation in progress
159	 */
160#ifdef DIAGNOSTIC
161	vd->switch_poll_count = 0;
162#endif
163#ifdef VCONS_DRAW_INTR
164	vd->intr_softint = softint_establish(SOFTINT_SERIAL,
165	    vcons_softintr, vd);
166	callout_init(&vd->intr, 0);
167	callout_setfunc(&vd->intr, vcons_intr, vd);
168	vd->intr_valid = 1;
169
170	/* XXX assume that the 'dev' arg is never dereferenced */
171	config_interrupts((device_t)vd, vcons_intr_enable);
172#endif
173	return 0;
174}
175
176static void
177vcons_lock(struct vcons_screen *scr)
178{
179#ifdef VCONS_PARANOIA
180	int s;
181
182	s = splhigh();
183#endif
184	SCREEN_BUSY(scr);
185#ifdef VCONS_PARANOIA
186	splx(s);
187#endif
188}
189
190static void
191vcons_unlock(struct vcons_screen *scr)
192{
193#ifdef VCONS_PARANOIA
194	int s;
195
196	s = splhigh();
197#endif
198	SCREEN_IDLE(scr);
199#ifdef VCONS_PARANOIA
200	splx(s);
201#endif
202}
203
204static void
205vcons_dummy_init_screen(void *cookie,
206    struct vcons_screen *scr, int exists,
207    long *defattr)
208{
209
210	/*
211	 * default init_screen() method.
212	 * Needs to be overwritten so we bitch and whine in case anyone ends
213	 * up in here.
214	 */
215	printf("vcons_init_screen: dummy function called. Your driver is "
216	       "supposed to supply a replacement for proper operation\n");
217}
218
219int
220vcons_init_screen(struct vcons_data *vd, struct vcons_screen *scr,
221    int existing, long *defattr)
222{
223	struct rasops_info *ri = &scr->scr_ri;
224	int cnt, i;
225#ifdef VCONS_DRAW_INTR
226	int size;
227#endif
228
229	scr->scr_cookie = vd->cookie;
230	scr->scr_vd = scr->scr_origvd = vd;
231	scr->scr_busy = 0;
232
233	/*
234	 * call the driver-supplied init_screen function which is expected
235	 * to set up rasops_info, override cursor() and probably others
236	 */
237	vd->init_screen(vd->cookie, scr, existing, defattr);
238
239	/*
240	 * save the non virtual console aware rasops and replace them with
241	 * our wrappers
242	 */
243	vd->eraserows = ri->ri_ops.eraserows;
244	vd->erasecols = ri->ri_ops.erasecols;
245	vd->putchar   = ri->ri_ops.putchar;
246	vd->cursor    = ri->ri_ops.cursor;
247
248	if (scr->scr_flags & VCONS_NO_COPYCOLS) {
249		vd->copycols  = vcons_copycols_noread;
250	} else {
251		vd->copycols = ri->ri_ops.copycols;
252	}
253
254	if (scr->scr_flags & VCONS_NO_COPYROWS) {
255		vd->copyrows  = vcons_copyrows_noread;
256	} else {
257		vd->copyrows = ri->ri_ops.copyrows;
258	}
259
260	ri->ri_ops.eraserows = vcons_eraserows;
261	ri->ri_ops.erasecols = vcons_erasecols;
262	ri->ri_ops.putchar   = vcons_putchar;
263	ri->ri_ops.cursor    = vcons_cursor;
264	ri->ri_ops.copycols  = vcons_copycols;
265	ri->ri_ops.copyrows  = vcons_copyrows;
266
267
268	ri->ri_hw = scr;
269
270	/*
271	 * we allocate both chars and attributes in one chunk, attributes first
272	 * because they have the (potentially) bigger alignment
273	 */
274#ifdef WSDISPLAY_SCROLLSUPPORT
275	cnt = (ri->ri_rows + WSDISPLAY_SCROLLBACK_LINES) * ri->ri_cols;
276	scr->scr_lines_in_buffer = WSDISPLAY_SCROLLBACK_LINES;
277	scr->scr_current_line = 0;
278	scr->scr_line_wanted = 0;
279	scr->scr_offset_to_zero = ri->ri_cols * WSDISPLAY_SCROLLBACK_LINES;
280	scr->scr_current_offset = scr->scr_offset_to_zero;
281#else
282	cnt = ri->ri_rows * ri->ri_cols;
283#endif
284	scr->scr_attrs = (long *)malloc(cnt * (sizeof(long) +
285	    sizeof(uint32_t)), M_DEVBUF, M_WAITOK);
286	if (scr->scr_attrs == NULL)
287		return ENOMEM;
288
289	scr->scr_chars = (uint32_t *)&scr->scr_attrs[cnt];
290
291	ri->ri_ops.allocattr(ri, WS_DEFAULT_FG, WS_DEFAULT_BG, 0, defattr);
292	scr->scr_defattr = *defattr;
293
294	/*
295	 * fill the attribute buffer with *defattr, chars with 0x20
296	 * since we don't know if the driver tries to mimic firmware output or
297	 * reset everything we do nothing to VRAM here, any driver that feels
298	 * the need to clear screen or something will have to do it on its own
299	 * Additional screens will start out in the background anyway so
300	 * cleaning or not only really affects the initial console screen
301	 */
302	for (i = 0; i < cnt; i++) {
303		scr->scr_attrs[i] = *defattr;
304		scr->scr_chars[i] = 0x20;
305	}
306
307#ifdef VCONS_DRAW_INTR
308	size = ri->ri_cols * ri->ri_rows;
309	if (size > vd->cells) {
310		if (vd->chars != NULL) free(vd->chars, M_DEVBUF);
311		if (vd->attrs != NULL) free(vd->attrs, M_DEVBUF);
312		vd->cells = size;
313		vd->chars = malloc(size * sizeof(uint32_t), M_DEVBUF, M_WAITOK);
314		vd->attrs = malloc(size * sizeof(long), M_DEVBUF, M_WAITOK);
315		vcons_invalidate_cache(vd);
316	}
317#endif
318
319	if(vd->active == NULL) {
320		vd->active = scr;
321		SCREEN_VISIBLE(scr);
322	}
323
324	if (existing) {
325		SCREEN_VISIBLE(scr);
326		vd->active = scr;
327	} else {
328		SCREEN_INVISIBLE(scr);
329	}
330
331	LIST_INSERT_HEAD(&vd->screens, scr, next);
332	return 0;
333}
334
335static void
336vcons_do_switch(void *arg)
337{
338	struct vcons_data *vd = arg;
339	struct vcons_screen *scr, *oldscr;
340
341	scr = vd->wanted;
342	if (!scr) {
343		printf("vcons_switch_screen: disappeared\n");
344		vd->switch_cb(vd->switch_cb_arg, EIO, 0);
345		return;
346	}
347	oldscr = vd->active; /* can be NULL! */
348
349	/*
350	 * if there's an old, visible screen we mark it invisible and wait
351	 * until it's not busy so we can safely switch
352	 */
353	if (oldscr != NULL) {
354		SCREEN_INVISIBLE(oldscr);
355		if (SCREEN_IS_BUSY(oldscr)) {
356			callout_schedule(&vd->switch_callout, 1);
357#ifdef DIAGNOSTIC
358			/* bitch if we wait too long */
359			vd->switch_poll_count++;
360			if (vd->switch_poll_count > 100) {
361				panic("vcons: screen still busy");
362			}
363#endif
364			return;
365		}
366		/* invisible screen -> no visible cursor image */
367		oldscr->scr_ri.ri_flg &= ~RI_CURSOR;
368#ifdef DIAGNOSTIC
369		vd->switch_poll_count = 0;
370#endif
371	}
372
373	if (scr == oldscr)
374		return;
375
376#ifdef DIAGNOSTIC
377	if (SCREEN_IS_VISIBLE(scr))
378		printf("vcons_switch_screen: already active");
379#endif
380
381#ifdef notyet
382	if (vd->currenttype != type) {
383		vcons_set_screentype(vd, type);
384		vd->currenttype = type;
385	}
386#endif
387
388	SCREEN_VISIBLE(scr);
389	vd->active = scr;
390	vd->wanted = NULL;
391
392	if (vd->show_screen_cb != NULL)
393		vd->show_screen_cb(scr);
394
395	if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
396		vcons_redraw_screen(scr);
397
398	if (vd->switch_cb)
399		vd->switch_cb(vd->switch_cb_arg, 0, 0);
400}
401
402void
403vcons_redraw_screen(struct vcons_screen *scr)
404{
405	uint32_t *charptr = scr->scr_chars;
406	long *attrptr = scr->scr_attrs;
407	struct rasops_info *ri = &scr->scr_ri;
408	struct vcons_data *vd = scr->scr_vd;
409	int i, j, offset, boffset = 0;
410
411	vcons_lock(scr);
412	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
413
414		/*
415		 * only clear the screen when RI_FULLCLEAR is set since we're
416		 * going to overwrite every single character cell anyway
417		 */
418		if (ri->ri_flg & RI_FULLCLEAR) {
419			vd->eraserows(ri, 0, ri->ri_rows,
420			    scr->scr_defattr);
421		}
422
423		/* redraw the screen */
424#ifdef WSDISPLAY_SCROLLSUPPORT
425		offset = scr->scr_current_offset;
426#else
427		offset = 0;
428#endif
429		for (i = 0; i < ri->ri_rows; i++) {
430			for (j = 0; j < ri->ri_cols; j++) {
431				/*
432				 * no need to use the wrapper function - we
433				 * don't change any characters or attributes
434				 * and we already made sure the screen we're
435				 * working on is visible
436				 */
437				vd->putchar(ri, i, j,
438				    charptr[offset], attrptr[offset]);
439#ifdef VCONS_DRAW_INTR
440				vd->chars[boffset] = charptr[offset];
441				vd->attrs[boffset] = attrptr[offset];
442#endif
443				offset++;
444				boffset++;
445			}
446		}
447		ri->ri_flg &= ~RI_CURSOR;
448		scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol);
449#ifdef VCONS_DRAW_INTR
450		vd->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol;
451#endif
452	}
453	vcons_unlock(scr);
454}
455
456#ifdef VCONS_DRAW_INTR
457void
458vcons_update_screen(struct vcons_screen *scr)
459{
460	uint32_t *charptr = scr->scr_chars;
461	long *attrptr = scr->scr_attrs;
462	struct rasops_info *ri = &scr->scr_ri;
463	struct vcons_data *vd = scr->scr_vd;
464	int i, j, offset, boffset = 0;
465
466	vcons_lock(scr);
467	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
468
469		/* redraw the screen */
470#ifdef WSDISPLAY_SCROLLSUPPORT
471		offset = scr->scr_current_offset;
472#else
473		offset = 0;
474#endif
475		/*
476		 * we mark the character cell occupied by the cursor as dirty
477		 * so we don't have to deal with it
478		 * notice that this isn't necessarily the position where rasops
479		 * thinks it is, just where we drew it the last time
480		 */
481		if (vd->cursor_offset >= 0)
482			vd->attrs[vd->cursor_offset] = 0xffffffff;
483
484		for (i = 0; i < ri->ri_rows; i++) {
485			for (j = 0; j < ri->ri_cols; j++) {
486				/*
487				 * no need to use the wrapper function - we
488				 * don't change any characters or attributes
489				 * and we already made sure the screen we're
490				 * working on is visible
491				 */
492				if ((vd->chars[boffset] != charptr[offset]) ||
493				    (vd->attrs[boffset] != attrptr[offset])) {
494					vd->putchar(ri, i, j,
495				 	   charptr[offset], attrptr[offset]);
496					vd->chars[boffset] = charptr[offset];
497					vd->attrs[boffset] = attrptr[offset];
498				}
499				offset++;
500				boffset++;
501			}
502		}
503		ri->ri_flg &= ~RI_CURSOR;
504		scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol);
505		vd->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol;
506	}
507	vcons_unlock(scr);
508}
509#endif
510
511static int
512vcons_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
513	struct lwp *l)
514{
515	struct vcons_data *vd = v;
516	int error = 0;
517
518
519	switch (cmd) {
520	case WSDISPLAYIO_GETWSCHAR:
521		error = vcons_getwschar((struct vcons_screen *)vs,
522			(struct wsdisplay_char *)data);
523		break;
524
525	case WSDISPLAYIO_PUTWSCHAR:
526		error = vcons_putwschar((struct vcons_screen *)vs,
527			(struct wsdisplay_char *)data);
528		break;
529
530	case WSDISPLAYIO_SET_POLLING: {
531		int poll = *(int *)data;
532
533		/* first call the driver's ioctl handler */
534		if (vd->ioctl != NULL)
535			error = (*vd->ioctl)(v, vs, cmd, data, flag, l);
536		if (poll) {
537			vcons_enable_polling(vd);
538			vcons_hard_switch(LIST_FIRST(&vd->screens));
539		} else
540			vcons_disable_polling(vd);
541		}
542		break;
543
544	default:
545		if (vd->ioctl != NULL)
546			error = (*vd->ioctl)(v, vs, cmd, data, flag, l);
547		else
548			error = EPASSTHROUGH;
549	}
550
551	return error;
552}
553
554static int
555vcons_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
556    int *curxp, int *curyp, long *defattrp)
557{
558	struct vcons_data *vd = v;
559	struct vcons_screen *scr;
560	int ret;
561
562	scr = malloc(sizeof(struct vcons_screen), M_DEVBUF, M_WAITOK | M_ZERO);
563	if (scr == NULL)
564		return ENOMEM;
565
566	scr->scr_flags = 0;
567	scr->scr_status = 0;
568	scr->scr_busy = 0;
569	scr->scr_type = type;
570
571	ret = vcons_init_screen(vd, scr, 0, defattrp);
572	if (ret != 0) {
573		free(scr, M_DEVBUF);
574		return ret;
575	}
576
577	if (vd->active == NULL) {
578		SCREEN_VISIBLE(scr);
579		vd->active = scr;
580		vd->currenttype = type;
581	}
582
583	*cookiep = scr;
584	*curxp = scr->scr_ri.ri_ccol;
585	*curyp = scr->scr_ri.ri_crow;
586	return 0;
587}
588
589static void
590vcons_free_screen(void *v, void *cookie)
591{
592	struct vcons_data *vd = v;
593	struct vcons_screen *scr = cookie;
594
595	vcons_lock(scr);
596	/* there should be no rasops activity here */
597
598	LIST_REMOVE(scr, next);
599
600	if ((scr->scr_flags & VCONS_SCREEN_IS_STATIC) == 0) {
601		free(scr->scr_attrs, M_DEVBUF);
602		free(scr, M_DEVBUF);
603	} else {
604		/*
605		 * maybe we should just restore the old rasops_info methods
606		 * and free the character/attribute buffer here?
607		 */
608#ifdef VCONS_DEBUG
609		panic("vcons_free_screen: console");
610#else
611		printf("vcons_free_screen: console\n");
612#endif
613	}
614
615	if (vd->active == scr)
616		vd->active = NULL;
617}
618
619static int
620vcons_show_screen(void *v, void *cookie, int waitok,
621    void (*cb)(void *, int, int), void *cb_arg)
622{
623	struct vcons_data *vd = v;
624	struct vcons_screen *scr;
625
626	scr = cookie;
627	if (scr == vd->active)
628		return 0;
629
630	vd->wanted = scr;
631	vd->switch_cb = cb;
632	vd->switch_cb_arg = cb_arg;
633	if (cb) {
634		callout_schedule(&vd->switch_callout, 0);
635		return EAGAIN;
636	}
637
638	vcons_do_switch(vd);
639	return 0;
640}
641
642/* wrappers for rasops_info methods */
643
644static void
645vcons_copycols_buffer(void *cookie, int row, int srccol, int dstcol, int ncols)
646{
647	struct rasops_info *ri = cookie;
648	struct vcons_screen *scr = ri->ri_hw;
649	int from = srccol + row * ri->ri_cols;
650	int to = dstcol + row * ri->ri_cols;
651
652#ifdef WSDISPLAY_SCROLLSUPPORT
653	int offset;
654	offset = scr->scr_offset_to_zero;
655
656	memmove(&scr->scr_attrs[offset + to], &scr->scr_attrs[offset + from],
657	    ncols * sizeof(long));
658	memmove(&scr->scr_chars[offset + to], &scr->scr_chars[offset + from],
659	    ncols * sizeof(uint32_t));
660#else
661	memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
662	    ncols * sizeof(long));
663	memmove(&scr->scr_chars[to], &scr->scr_chars[from],
664	    ncols * sizeof(uint32_t));
665#endif
666
667#ifdef VCONS_DRAW_INTR
668	atomic_inc_uint(&scr->scr_dirty);
669#endif
670}
671
672static void
673vcons_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
674{
675	struct rasops_info *ri = cookie;
676	struct vcons_screen *scr = ri->ri_hw;
677
678	vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols);
679
680#if defined(VCONS_DRAW_INTR)
681	if (scr->scr_vd->use_intr)
682		return;
683#endif
684
685	vcons_lock(scr);
686	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
687		scr->scr_vd->copycols(cookie, row, srccol, dstcol, ncols);
688	}
689	vcons_unlock(scr);
690}
691
692static void
693vcons_copycols_noread(void *cookie, int row, int srccol, int dstcol, int ncols)
694{
695	struct rasops_info *ri = cookie;
696	struct vcons_screen *scr = ri->ri_hw;
697	struct vcons_data *vd = scr->scr_vd;
698
699	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
700		int pos, c, offset, ppos;
701
702#ifdef WSDISPLAY_SCROLLSUPPORT
703		offset = scr->scr_current_offset;
704#else
705		offset = 0;
706#endif
707		ppos = ri->ri_cols * row + dstcol;
708		pos = ppos + offset;
709		for (c = dstcol; c < (dstcol + ncols); c++) {
710#ifdef VCONS_DRAW_INTR
711			if ((scr->scr_chars[pos] != vd->chars[ppos]) ||
712			    (scr->scr_attrs[pos] != vd->attrs[ppos])) {
713				vd->putchar(cookie, row, c,
714				   scr->scr_chars[pos], scr->scr_attrs[pos]);
715				vd->chars[ppos] = scr->scr_chars[pos];
716				vd->attrs[ppos] = scr->scr_attrs[pos];
717			}
718#else
719			vd->putchar(cookie, row, c, scr->scr_chars[pos],
720			    scr->scr_attrs[pos]);
721#endif
722			pos++;
723			ppos++;
724		}
725	}
726}
727
728static void
729vcons_erasecols_buffer(void *cookie, int row, int startcol, int ncols, long fillattr)
730{
731	struct rasops_info *ri = cookie;
732	struct vcons_screen *scr = ri->ri_hw;
733	int start = startcol + row * ri->ri_cols;
734	int end = start + ncols, i;
735
736#ifdef WSDISPLAY_SCROLLSUPPORT
737	int offset;
738	offset = scr->scr_offset_to_zero;
739
740	for (i = start; i < end; i++) {
741		scr->scr_attrs[offset + i] = fillattr;
742		scr->scr_chars[offset + i] = 0x20;
743	}
744#else
745	for (i = start; i < end; i++) {
746		scr->scr_attrs[i] = fillattr;
747		scr->scr_chars[i] = 0x20;
748	}
749#endif
750
751#ifdef VCONS_DRAW_INTR
752	atomic_inc_uint(&scr->scr_dirty);
753#endif
754}
755
756#ifdef VCONS_DRAW_INTR
757static void
758vcons_erasecols_cached(void *cookie, int row, int startcol, int ncols, long fillattr)
759{
760	struct rasops_info *ri = cookie;
761	struct vcons_screen *scr = ri->ri_hw;
762	struct vcons_data *vd = scr->scr_vd;
763	int i, pos = row * ri->ri_cols + startcol;
764
765	for (i = pos; i < ncols; i++) {
766		vd->chars[i] = 0x20;
767		vd->attrs[i] = fillattr;
768	}
769	vd->erasecols(cookie, row, startcol, ncols, fillattr);
770}
771#endif
772
773static void
774vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
775{
776	struct rasops_info *ri = cookie;
777	struct vcons_screen *scr = ri->ri_hw;
778
779	vcons_erasecols_buffer(cookie, row, startcol, ncols, fillattr);
780
781#if defined(VCONS_DRAW_INTR)
782	if (scr->scr_vd->use_intr)
783		return;
784#endif
785
786	vcons_lock(scr);
787	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
788#ifdef VCONS_DRAW_INTR
789			vcons_erasecols_cached(cookie, row, startcol, ncols,
790			    fillattr);
791#else
792		scr->scr_vd->erasecols(cookie, row, startcol, ncols, fillattr);
793#endif
794	}
795	vcons_unlock(scr);
796}
797
798static void
799vcons_copyrows_buffer(void *cookie, int srcrow, int dstrow, int nrows)
800{
801	struct rasops_info *ri = cookie;
802	struct vcons_screen *scr = ri->ri_hw;
803	int from, to, len;
804
805#ifdef WSDISPLAY_SCROLLSUPPORT
806	int offset;
807	offset = scr->scr_offset_to_zero;
808
809	/* do we need to scroll the back buffer? */
810	if (dstrow == 0) {
811		from = ri->ri_cols * srcrow;
812		to = ri->ri_cols * dstrow;
813
814		memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
815		    scr->scr_offset_to_zero * sizeof(long));
816		memmove(&scr->scr_chars[to], &scr->scr_chars[from],
817		    scr->scr_offset_to_zero * sizeof(uint32_t));
818	}
819	from = ri->ri_cols * srcrow + offset;
820	to = ri->ri_cols * dstrow + offset;
821	len = ri->ri_cols * nrows;
822
823#else
824	from = ri->ri_cols * srcrow;
825	to = ri->ri_cols * dstrow;
826	len = ri->ri_cols * nrows;
827#endif
828	memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
829	    len * sizeof(long));
830	memmove(&scr->scr_chars[to], &scr->scr_chars[from],
831	    len * sizeof(uint32_t));
832
833#ifdef VCONS_DRAW_INTR
834	atomic_inc_uint(&scr->scr_dirty);
835#endif
836}
837
838static void
839vcons_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
840{
841	struct rasops_info *ri = cookie;
842	struct vcons_screen *scr = ri->ri_hw;
843
844	vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows);
845
846#if defined(VCONS_DRAW_INTR)
847	if (scr->scr_vd->use_intr)
848		return;
849#endif
850
851	vcons_lock(scr);
852	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
853		scr->scr_vd->copyrows(cookie, srcrow, dstrow, nrows);
854	}
855	vcons_unlock(scr);
856}
857
858static void
859vcons_copyrows_noread(void *cookie, int srcrow, int dstrow, int nrows)
860{
861	struct rasops_info *ri = cookie;
862	struct vcons_screen *scr = ri->ri_hw;
863	struct vcons_data *vd = scr->scr_vd;
864	int dist;
865
866	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
867		int pos, l, c, offset, ppos;
868
869#ifdef WSDISPLAY_SCROLLSUPPORT
870		offset = scr->scr_current_offset;
871#else
872		offset = 0;
873#endif
874		dist = (dstrow - srcrow) * ri->ri_cols;
875		ppos = ri->ri_cols * dstrow;
876		pos = ppos + offset;
877		for (l = dstrow; l < (dstrow + nrows); l++) {
878			for (c = 0; c < ri->ri_cols; c++) {
879#ifdef VCONS_DRAW_INTR
880				if ((scr->scr_chars[pos] != vd->chars[ppos]) ||
881				    (scr->scr_attrs[pos] != vd->attrs[ppos])) {
882					vd->putchar(cookie, l, c,
883					   scr->scr_chars[pos], scr->scr_attrs[pos]);
884					vd->chars[ppos] = scr->scr_chars[pos];
885					vd->attrs[ppos] = scr->scr_attrs[pos];
886				}
887#else
888				vd->putchar(cookie, l, c, scr->scr_chars[pos],
889				    scr->scr_attrs[pos]);
890#endif
891				pos++;
892				ppos++;
893			}
894		}
895	}
896}
897
898static void
899vcons_eraserows_buffer(void *cookie, int row, int nrows, long fillattr)
900{
901	struct rasops_info *ri = cookie;
902	struct vcons_screen *scr = ri->ri_hw;
903	int start, end, i;
904
905#ifdef WSDISPLAY_SCROLLSUPPORT
906	int offset;
907	offset = scr->scr_offset_to_zero;
908
909	start = ri->ri_cols * row + offset;
910	end = ri->ri_cols * (row + nrows) + offset;
911#else
912	start = ri->ri_cols * row;
913	end = ri->ri_cols * (row + nrows);
914#endif
915
916	for (i = start; i < end; i++) {
917		scr->scr_attrs[i] = fillattr;
918		scr->scr_chars[i] = 0x20;
919	}
920
921#ifdef VCONS_DRAW_INTR
922	atomic_inc_uint(&scr->scr_dirty);
923#endif
924}
925
926static void
927vcons_eraserows(void *cookie, int row, int nrows, long fillattr)
928{
929	struct rasops_info *ri = cookie;
930	struct vcons_screen *scr = ri->ri_hw;
931
932	vcons_eraserows_buffer(cookie, row, nrows, fillattr);
933
934#if defined(VCONS_DRAW_INTR)
935	if (scr->scr_vd->use_intr)
936		return;
937#endif
938
939	vcons_lock(scr);
940	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
941		scr->scr_vd->eraserows(cookie, row, nrows, fillattr);
942	}
943	vcons_unlock(scr);
944}
945
946static void
947vcons_putchar_buffer(void *cookie, int row, int col, u_int c, long attr)
948{
949	struct rasops_info *ri = cookie;
950	struct vcons_screen *scr = ri->ri_hw;
951	int pos;
952
953#ifdef WSDISPLAY_SCROLLSUPPORT
954	int offset;
955	offset = scr->scr_offset_to_zero;
956
957	if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) &&
958	     (col < ri->ri_cols)) {
959		pos = col + row * ri->ri_cols;
960		scr->scr_attrs[pos + offset] = attr;
961		scr->scr_chars[pos + offset] = c;
962	}
963#else
964	if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) &&
965	     (col < ri->ri_cols)) {
966		pos = col + row * ri->ri_cols;
967		scr->scr_attrs[pos] = attr;
968		scr->scr_chars[pos] = c;
969	}
970#endif
971
972#ifdef VCONS_DRAW_INTR
973	atomic_inc_uint(&scr->scr_dirty);
974#endif
975}
976
977#ifdef VCONS_DRAW_INTR
978static void
979vcons_putchar_cached(void *cookie, int row, int col, u_int c, long attr)
980{
981	struct rasops_info *ri = cookie;
982	struct vcons_screen *scr = ri->ri_hw;
983	struct vcons_data *vd = scr->scr_vd;
984	int pos = row * ri->ri_cols + col;
985
986	if ((vd->chars == NULL) || (vd->attrs == NULL)) {
987		vd->putchar(cookie, row, col, c, attr);
988		return;
989	}
990	if ((vd->chars[pos] != c) || (vd->attrs[pos] != attr)) {
991		vd->attrs[pos] = attr;
992		vd->chars[pos] = c;
993		vd->putchar(cookie, row, col, c, attr);
994	}
995}
996#endif
997
998static void
999vcons_putchar(void *cookie, int row, int col, u_int c, long attr)
1000{
1001	struct rasops_info *ri = cookie;
1002	struct vcons_screen *scr = ri->ri_hw;
1003
1004	vcons_putchar_buffer(cookie, row, col, c, attr);
1005
1006#if defined(VCONS_DRAW_INTR)
1007	if (scr->scr_vd->use_intr)
1008		return;
1009#endif
1010
1011	vcons_lock(scr);
1012	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1013#ifdef VCONS_DRAW_INTR
1014		vcons_putchar_cached(cookie, row, col, c, attr);
1015#else
1016		scr->scr_vd->putchar(cookie, row, col, c, attr);
1017#endif
1018	}
1019	vcons_unlock(scr);
1020}
1021
1022static void
1023vcons_cursor(void *cookie, int on, int row, int col)
1024{
1025	struct rasops_info *ri = cookie;
1026	struct vcons_screen *scr = ri->ri_hw;
1027
1028
1029#if defined(VCONS_DRAW_INTR)
1030	if (scr->scr_vd->use_intr) {
1031		vcons_lock(scr);
1032		if (scr->scr_ri.ri_crow != row || scr->scr_ri.ri_ccol != col) {
1033			scr->scr_ri.ri_crow = row;
1034			scr->scr_ri.ri_ccol = col;
1035			atomic_inc_uint(&scr->scr_dirty);
1036		}
1037		vcons_unlock(scr);
1038		return;
1039	}
1040#endif
1041
1042	vcons_lock(scr);
1043
1044	if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1045		scr->scr_vd->cursor(cookie, on, row, col);
1046	} else {
1047		scr->scr_ri.ri_crow = row;
1048		scr->scr_ri.ri_ccol = col;
1049	}
1050	vcons_unlock(scr);
1051}
1052
1053/* methods to read/write characters via ioctl() */
1054
1055static int
1056vcons_putwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc)
1057{
1058	long attr;
1059	struct rasops_info *ri;
1060
1061	KASSERT(scr != NULL && wsc != NULL);
1062
1063	ri = &scr->scr_ri;
1064
1065	if (__predict_false((unsigned int)wsc->col > ri->ri_cols ||
1066	    (unsigned int)wsc->row > ri->ri_rows))
1067			return (EINVAL);
1068
1069	if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) &&
1070	     (wsc->col < ri->ri_cols)) {
1071
1072		ri->ri_ops.allocattr(ri, wsc->foreground, wsc->background,
1073		    wsc->flags, &attr);
1074		vcons_putchar(ri, wsc->row, wsc->col, wsc->letter, attr);
1075#ifdef VCONS_DEBUG
1076		printf("vcons_putwschar(%d, %d, %x, %lx\n", wsc->row, wsc->col,
1077		    wsc->letter, attr);
1078#endif
1079		return 0;
1080	} else
1081		return EINVAL;
1082}
1083
1084static int
1085vcons_getwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc)
1086{
1087	int offset;
1088	long attr;
1089	struct rasops_info *ri;
1090
1091	KASSERT(scr != NULL && wsc != NULL);
1092
1093	ri = &scr->scr_ri;
1094
1095	if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) &&
1096	     (wsc->col < ri->ri_cols)) {
1097
1098		offset = ri->ri_cols * wsc->row + wsc->col;
1099#ifdef WSDISPLAY_SCROLLSUPPORT
1100		offset += scr->scr_offset_to_zero;
1101#endif
1102		wsc->letter = scr->scr_chars[offset];
1103		attr = scr->scr_attrs[offset];
1104
1105		/*
1106		 * this is ugly. We need to break up an attribute into colours and
1107		 * flags but there's no rasops method to do that so we must rely on
1108		 * the 'canonical' encoding.
1109		 */
1110#ifdef VCONS_DEBUG
1111		printf("vcons_getwschar: %d, %d, %x, %lx\n", wsc->row,
1112		    wsc->col, wsc->letter, attr);
1113#endif
1114		wsc->foreground = (attr >> 24) & 0xff;
1115		wsc->background = (attr >> 16) & 0xff;
1116		wsc->flags      = attr & 0xff;
1117		return 0;
1118	} else
1119		return EINVAL;
1120}
1121
1122#ifdef WSDISPLAY_SCROLLSUPPORT
1123
1124static void
1125vcons_scroll(void *cookie, void *vs, int where)
1126{
1127	struct vcons_screen *scr = vs;
1128
1129	if (where == 0) {
1130		scr->scr_line_wanted = 0;
1131	} else {
1132		scr->scr_line_wanted = scr->scr_line_wanted - where;
1133		if (scr->scr_line_wanted < 0)
1134			scr->scr_line_wanted = 0;
1135		if (scr->scr_line_wanted > scr->scr_lines_in_buffer)
1136			scr->scr_line_wanted = scr->scr_lines_in_buffer;
1137	}
1138
1139	if (scr->scr_line_wanted != scr->scr_current_line) {
1140
1141		vcons_do_scroll(scr);
1142	}
1143}
1144
1145static void
1146vcons_do_scroll(struct vcons_screen *scr)
1147{
1148	int dist, from, to, num;
1149	int r_offset, r_start;
1150	int i, j;
1151
1152	if (scr->scr_line_wanted == scr->scr_current_line)
1153		return;
1154	dist = scr->scr_line_wanted - scr->scr_current_line;
1155	scr->scr_current_line = scr->scr_line_wanted;
1156	scr->scr_current_offset = scr->scr_ri.ri_cols *
1157	    (scr->scr_lines_in_buffer - scr->scr_current_line);
1158	if (abs(dist) >= scr->scr_ri.ri_rows) {
1159		vcons_redraw_screen(scr);
1160		return;
1161	}
1162	/* scroll and redraw only what we really have to */
1163	if (dist > 0) {
1164		/* we scroll down */
1165		from = 0;
1166		to = dist;
1167		num = scr->scr_ri.ri_rows - dist;
1168		/* now the redraw parameters */
1169		r_offset = scr->scr_current_offset;
1170		r_start = 0;
1171	} else {
1172		/* scrolling up */
1173		to = 0;
1174		from = -dist;
1175		num = scr->scr_ri.ri_rows + dist;
1176		r_offset = scr->scr_current_offset + num * scr->scr_ri.ri_cols;
1177		r_start = num;
1178	}
1179	scr->scr_vd->copyrows(scr, from, to, num);
1180	for (i = 0; i < abs(dist); i++) {
1181		for (j = 0; j < scr->scr_ri.ri_cols; j++) {
1182#ifdef VCONS_DRAW_INTR
1183			vcons_putchar_cached(scr, i + r_start, j,
1184			    scr->scr_chars[r_offset],
1185			    scr->scr_attrs[r_offset]);
1186#else
1187			scr->scr_vd->putchar(scr, i + r_start, j,
1188			    scr->scr_chars[r_offset],
1189			    scr->scr_attrs[r_offset]);
1190#endif
1191			r_offset++;
1192		}
1193	}
1194
1195	if (scr->scr_line_wanted == 0) {
1196		/* this was a reset - need to draw the cursor */
1197		scr->scr_ri.ri_flg &= ~RI_CURSOR;
1198		scr->scr_vd->cursor(scr, 1, scr->scr_ri.ri_crow,
1199		    scr->scr_ri.ri_ccol);
1200	}
1201}
1202
1203#endif /* WSDISPLAY_SCROLLSUPPORT */
1204
1205#ifdef VCONS_DRAW_INTR
1206static void
1207vcons_intr(void *cookie)
1208{
1209	struct vcons_data *vd = cookie;
1210
1211	softint_schedule(vd->intr_softint);
1212}
1213
1214static void
1215vcons_softintr(void *cookie)
1216{
1217	struct vcons_data *vd = cookie;
1218	struct vcons_screen *scr = vd->active;
1219	unsigned int dirty;
1220
1221	if (scr && vd->use_intr == 1) {
1222		if (!SCREEN_IS_BUSY(scr)) {
1223			dirty = atomic_swap_uint(&scr->scr_dirty, 0);
1224			if (dirty > 0) {
1225				if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
1226					vcons_update_screen(scr);
1227			}
1228		}
1229	}
1230
1231	callout_schedule(&vd->intr, mstohz(33));
1232}
1233
1234static void
1235vcons_intr_enable(device_t dev)
1236{
1237	/* the 'dev' arg we pass to config_interrupts isn't a device_t */
1238	struct vcons_data *vd = (struct vcons_data *)dev;
1239	vd->use_intr = 1;
1240	callout_schedule(&vd->intr, mstohz(33));
1241}
1242#endif /* VCONS_DRAW_INTR */
1243
1244void
1245vcons_enable_polling(struct vcons_data *vd)
1246{
1247	struct vcons_screen *scr = vd->active;
1248
1249#ifdef VCONS_DRAW_INTR
1250	vd->use_intr = 0;
1251#endif
1252
1253	if (scr && !SCREEN_IS_BUSY(scr)) {
1254		if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
1255			vcons_redraw_screen(scr);
1256	}
1257}
1258
1259void
1260vcons_disable_polling(struct vcons_data *vd)
1261{
1262#ifdef VCONS_DRAW_INTR
1263	struct vcons_screen *scr = vd->active;
1264
1265	if (!vd->intr_valid)
1266		return;
1267
1268	vd->use_intr = 1;
1269	if (scr)
1270		atomic_inc_uint(&scr->scr_dirty);
1271#endif
1272}
1273
1274void
1275vcons_hard_switch(struct vcons_screen *scr)
1276{
1277	struct vcons_data *vd = scr->scr_vd;
1278	struct vcons_screen *oldscr = vd->active;
1279
1280	if (oldscr) {
1281		SCREEN_INVISIBLE(oldscr);
1282		oldscr->scr_ri.ri_flg &= ~RI_CURSOR;
1283	}
1284	SCREEN_VISIBLE(scr);
1285	vd->active = scr;
1286	vd->wanted = NULL;
1287
1288	if (vd->show_screen_cb != NULL)
1289		vd->show_screen_cb(scr);
1290}
1291
1292#ifdef VCONS_DRAW_INTR
1293void
1294vcons_invalidate_cache(struct vcons_data *vd)
1295{
1296	int i;
1297
1298	if (vd->cells == 0)
1299		return;
1300
1301	for (i = 0; i > vd->cells; i++) {
1302		vd->chars[i] = -1;
1303		vd->attrs[i] = -1;
1304	}
1305}
1306#endif
1307