1/*	$OpenBSD: rasops.c,v 1.71 2023/04/13 02:19:05 jsg Exp $	*/
2/*	$NetBSD: rasops.c,v 1.35 2001/02/02 06:01:01 marcus Exp $	*/
3
4/*-
5 * Copyright (c) 1999 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Andrew Doran.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/param.h>
34#include <sys/malloc.h>
35#include <sys/systm.h>
36#include <sys/time.h>
37#include <sys/task.h>
38
39#include <dev/wscons/wsdisplayvar.h>
40#include <dev/wscons/wsconsio.h>
41#include <dev/wsfont/wsfont.h>
42#include <dev/rasops/rasops.h>
43
44#ifndef _KERNEL
45#include <errno.h>
46#endif
47
48/* ANSI colormap (R,G,B) */
49
50#define	NORMAL_BLACK	0x000000
51#define	NORMAL_RED	0x7f0000
52#define	NORMAL_GREEN	0x007f00
53#define	NORMAL_BROWN	0x7f7f00
54#define	NORMAL_BLUE	0x00007f
55#define	NORMAL_MAGENTA	0x7f007f
56#define	NORMAL_CYAN	0x007f7f
57#define	NORMAL_WHITE	0xc7c7c7	/* XXX too dim? */
58
59#define	HILITE_BLACK	0x7f7f7f
60#define	HILITE_RED	0xff0000
61#define	HILITE_GREEN	0x00ff00
62#define	HILITE_BROWN	0xffff00
63#define	HILITE_BLUE	0x0000ff
64#define	HILITE_MAGENTA	0xff00ff
65#define	HILITE_CYAN	0x00ffff
66#define	HILITE_WHITE	0xffffff
67
68const u_char rasops_cmap[256 * 3] = {
69#define	_C(x)	((x) & 0xff0000) >> 16, ((x) & 0x00ff00) >> 8, ((x) & 0x0000ff)
70
71	_C(NORMAL_BLACK),
72	_C(NORMAL_RED),
73	_C(NORMAL_GREEN),
74	_C(NORMAL_BROWN),
75	_C(NORMAL_BLUE),
76	_C(NORMAL_MAGENTA),
77	_C(NORMAL_CYAN),
78	_C(NORMAL_WHITE),
79
80	_C(HILITE_BLACK),
81	_C(HILITE_RED),
82	_C(HILITE_GREEN),
83	_C(HILITE_BROWN),
84	_C(HILITE_BLUE),
85	_C(HILITE_MAGENTA),
86	_C(HILITE_CYAN),
87	_C(HILITE_WHITE),
88
89	/*
90	 * For the cursor, we need the last 16 colors to be the
91	 * opposite of the first 16. Fill the intermediate space with
92	 * white completely for simplicity.
93	 */
94#define _CMWHITE16 \
95	_C(HILITE_WHITE), _C(HILITE_WHITE), _C(HILITE_WHITE), _C(HILITE_WHITE), \
96	_C(HILITE_WHITE), _C(HILITE_WHITE), _C(HILITE_WHITE), _C(HILITE_WHITE), \
97	_C(HILITE_WHITE), _C(HILITE_WHITE), _C(HILITE_WHITE), _C(HILITE_WHITE), \
98	_C(HILITE_WHITE), _C(HILITE_WHITE), _C(HILITE_WHITE), _C(HILITE_WHITE),
99	_CMWHITE16 _CMWHITE16 _CMWHITE16 _CMWHITE16 _CMWHITE16
100	_CMWHITE16 _CMWHITE16 _CMWHITE16 _CMWHITE16 _CMWHITE16
101	_CMWHITE16 _CMWHITE16 _CMWHITE16 _CMWHITE16
102#undef _CMWHITE16
103
104	_C(~HILITE_WHITE),
105	_C(~HILITE_CYAN),
106	_C(~HILITE_MAGENTA),
107	_C(~HILITE_BLUE),
108	_C(~HILITE_BROWN),
109	_C(~HILITE_GREEN),
110	_C(~HILITE_RED),
111	_C(~HILITE_BLACK),
112
113	_C(~NORMAL_WHITE),
114	_C(~NORMAL_CYAN),
115	_C(~NORMAL_MAGENTA),
116	_C(~NORMAL_BLUE),
117	_C(~NORMAL_BROWN),
118	_C(~NORMAL_GREEN),
119	_C(~NORMAL_RED),
120	_C(~NORMAL_BLACK),
121
122#undef	_C
123};
124
125struct rasops_screen {
126	LIST_ENTRY(rasops_screen) rs_next;
127	struct rasops_info *rs_ri;
128
129	struct wsdisplay_charcell *rs_bs;
130	int rs_visible;
131	int rs_crow;
132	int rs_ccol;
133	uint32_t rs_defattr;
134
135	int rs_sbscreens;
136#define RS_SCROLLBACK_SCREENS 5
137	int rs_dispoffset;	/* rs_bs index, start of our actual screen */
138	int rs_visibleoffset;	/* rs_bs index, current scrollback screen */
139};
140
141/* Generic functions */
142int	rasops_copycols(void *, int, int, int, int);
143int	rasops_copyrows(void *, int, int, int);
144int	rasops_mapchar(void *, int, u_int *);
145int	rasops_cursor(void *, int, int, int);
146int	rasops_pack_cattr(void *, int, int, int, uint32_t *);
147int	rasops_pack_mattr(void *, int, int, int, uint32_t *);
148int	rasops_do_cursor(struct rasops_info *);
149void	rasops_init_devcmap(struct rasops_info *);
150void	rasops_unpack_attr(void *, uint32_t, int *, int *, int *);
151#if NRASOPS_BSWAP > 0
152static void slow_bcopy(void *, void *, size_t);
153#endif
154#if NRASOPS_ROTATION > 0
155void	rasops_copychar(void *, int, int, int, int);
156int	rasops_copycols_rotated(void *, int, int, int, int);
157int	rasops_copyrows_rotated(void *, int, int, int);
158int	rasops_erasecols_rotated(void *, int, int, int, uint32_t);
159int	rasops_eraserows_rotated(void *, int, int, uint32_t);
160int	rasops_putchar_rotated(void *, int, int, u_int, uint32_t);
161void	rasops_rotate_font(int *, int);
162
163/*
164 * List of all rotated fonts
165 */
166SLIST_HEAD(, rotatedfont) rotatedfonts = SLIST_HEAD_INITIALIZER(rotatedfonts);
167struct	rotatedfont {
168	SLIST_ENTRY(rotatedfont) rf_next;
169	int rf_cookie;
170	int rf_rotated;
171};
172#endif
173
174void	rasops_doswitch(void *);
175int	rasops_vcons_cursor(void *, int, int, int);
176int	rasops_vcons_mapchar(void *, int, u_int *);
177int	rasops_vcons_putchar(void *, int, int, u_int, uint32_t);
178int	rasops_vcons_copycols(void *, int, int, int, int);
179int	rasops_vcons_erasecols(void *, int, int, int, uint32_t);
180int	rasops_vcons_copyrows(void *, int, int, int);
181int	rasops_vcons_eraserows(void *, int, int, uint32_t);
182int	rasops_vcons_pack_attr(void *, int, int, int, uint32_t *);
183void	rasops_vcons_unpack_attr(void *, uint32_t, int *, int *, int *);
184
185int	rasops_wronly_putchar(void *, int, int, u_int, uint32_t);
186int	rasops_wronly_copycols(void *, int, int, int, int);
187int	rasops_wronly_erasecols(void *, int, int, int, uint32_t);
188int	rasops_wronly_copyrows(void *, int, int, int);
189int	rasops_wronly_eraserows(void *, int, int, uint32_t);
190int	rasops_wronly_do_cursor(struct rasops_info *);
191
192int	rasops_add_font(struct rasops_info *, struct wsdisplay_font *);
193int	rasops_use_font(struct rasops_info *, struct wsdisplay_font *);
194int	rasops_list_font_cb(void *, struct wsdisplay_font *);
195
196/*
197 * Initialize a 'rasops_info' descriptor.
198 */
199int
200rasops_init(struct rasops_info *ri, int wantrows, int wantcols)
201{
202
203#ifdef _KERNEL
204	/* Select a font if the caller doesn't care */
205	if (ri->ri_font == NULL) {
206		int cookie = -1;
207
208		wsfont_init();
209
210		if (ri->ri_width >= 120 * 32)
211			/* Screen width of at least 3840px, 32px wide font */
212			cookie = wsfont_find(NULL, 32, 0, 0);
213
214		if (cookie <= 0 && ri->ri_width >= 120 * 16)
215			/* Screen width of at least 1920px, 16px wide font */
216			cookie = wsfont_find(NULL, 16, 0, 0);
217
218		if (cookie <= 0 && ri->ri_width > 80 * 12)
219			/* Screen width larger than 960px, 12px wide font */
220			cookie = wsfont_find(NULL, 12, 0, 0);
221
222		if (cookie <= 0)
223			/* Lower resolution, choose a 8px wide font */
224			cookie = wsfont_find(NULL, 8, 0, 0);
225
226		if (cookie <= 0)
227			cookie = wsfont_find(NULL, 0, 0, 0);
228
229		if (cookie <= 0) {
230			printf("rasops_init: font table is empty\n");
231			return (-1);
232		}
233
234#if NRASOPS_ROTATION > 0
235		/*
236		 * Pick the rotated version of this font. This will create it
237		 * if necessary.
238		 */
239		if (ri->ri_flg & (RI_ROTATE_CW | RI_ROTATE_CCW))
240			rasops_rotate_font(&cookie,
241			    ISSET(ri->ri_flg, RI_ROTATE_CCW));
242#endif
243
244		if (wsfont_lock(cookie, &ri->ri_font,
245		    WSDISPLAY_FONTORDER_L2R, WSDISPLAY_FONTORDER_L2R) <= 0) {
246			printf("rasops_init: couldn't lock font\n");
247			return (-1);
248		}
249
250		ri->ri_wsfcookie = cookie;
251	}
252#endif
253
254	/* This should never happen in reality... */
255#ifdef DEBUG
256	if ((long)ri->ri_bits & 3) {
257		printf("rasops_init: bits not aligned on 32-bit boundary\n");
258		return (-1);
259	}
260
261	if ((int)ri->ri_stride & 3) {
262		printf("rasops_init: stride not aligned on 32-bit boundary\n");
263		return (-1);
264	}
265#endif
266
267	if (rasops_reconfig(ri, wantrows, wantcols))
268		return (-1);
269
270	LIST_INIT(&ri->ri_screens);
271	ri->ri_nscreens = 0;
272
273	ri->ri_putchar = ri->ri_ops.putchar;
274	ri->ri_copycols = ri->ri_ops.copycols;
275	ri->ri_erasecols = ri->ri_ops.erasecols;
276	ri->ri_copyrows = ri->ri_ops.copyrows;
277	ri->ri_eraserows = ri->ri_ops.eraserows;
278	ri->ri_pack_attr = ri->ri_ops.pack_attr;
279
280	if (ri->ri_flg & RI_VCONS) {
281		void *cookie;
282		int curx, cury;
283		uint32_t attr;
284
285		if (rasops_alloc_screen(ri, &cookie, &curx, &cury, &attr))
286			return (-1);
287
288		ri->ri_active = cookie;
289		ri->ri_bs =
290		    &ri->ri_active->rs_bs[ri->ri_active->rs_dispoffset];
291
292		ri->ri_ops.cursor = rasops_vcons_cursor;
293		ri->ri_ops.mapchar = rasops_vcons_mapchar;
294		ri->ri_ops.putchar = rasops_vcons_putchar;
295		ri->ri_ops.copycols = rasops_vcons_copycols;
296		ri->ri_ops.erasecols = rasops_vcons_erasecols;
297		ri->ri_ops.copyrows = rasops_vcons_copyrows;
298		ri->ri_ops.eraserows = rasops_vcons_eraserows;
299		ri->ri_ops.pack_attr = rasops_vcons_pack_attr;
300		ri->ri_ops.unpack_attr = rasops_vcons_unpack_attr;
301		ri->ri_do_cursor = rasops_wronly_do_cursor;
302	} else if ((ri->ri_flg & RI_WRONLY) && ri->ri_bs != NULL) {
303		uint32_t attr;
304		int i;
305
306		ri->ri_ops.putchar = rasops_wronly_putchar;
307		ri->ri_ops.copycols = rasops_wronly_copycols;
308		ri->ri_ops.erasecols = rasops_wronly_erasecols;
309		ri->ri_ops.copyrows = rasops_wronly_copyrows;
310		ri->ri_ops.eraserows = rasops_wronly_eraserows;
311		ri->ri_do_cursor = rasops_wronly_do_cursor;
312
313		if (ri->ri_flg & RI_CLEAR) {
314			ri->ri_pack_attr(ri, 0, 0, 0, &attr);
315			for (i = 0; i < ri->ri_rows * ri->ri_cols; i++) {
316				ri->ri_bs[i].uc = ' ';
317				ri->ri_bs[i].attr = attr;
318			}
319		}
320	}
321
322	task_set(&ri->ri_switchtask, rasops_doswitch, ri);
323
324	rasops_init_devcmap(ri);
325	return (0);
326}
327
328/*
329 * Reconfigure (because parameters have changed in some way).
330 */
331int
332rasops_reconfig(struct rasops_info *ri, int wantrows, int wantcols)
333{
334	int l, bpp, s;
335
336	s = splhigh();
337
338	if (ri->ri_font->fontwidth > 32 || ri->ri_font->fontwidth < 4)
339		panic("rasops_init: fontwidth assumptions botched!");
340
341	/* Need this to frob the setup below */
342	bpp = (ri->ri_depth == 15 ? 16 : ri->ri_depth);
343
344	if ((ri->ri_flg & RI_CFGDONE) != 0)
345		ri->ri_bits = ri->ri_origbits;
346
347	/* Don't care if the caller wants a hideously small console */
348	if (wantrows < 10)
349		wantrows = 10;
350
351	if (wantcols < 20)
352		wantcols = 20;
353
354	/* Now constrain what they get */
355	ri->ri_emuwidth = ri->ri_font->fontwidth * wantcols;
356	ri->ri_emuheight = ri->ri_font->fontheight * wantrows;
357
358	if (ri->ri_emuwidth > ri->ri_width)
359		ri->ri_emuwidth = ri->ri_width;
360
361	if (ri->ri_emuheight > ri->ri_height)
362		ri->ri_emuheight = ri->ri_height;
363
364	/* Reduce width until aligned on a 32-bit boundary */
365	while ((ri->ri_emuwidth * bpp & 31) != 0)
366		ri->ri_emuwidth--;
367
368#if NRASOPS_ROTATION > 0
369	if (ri->ri_flg & (RI_ROTATE_CW | RI_ROTATE_CCW)) {
370		ri->ri_rows = ri->ri_emuwidth / ri->ri_font->fontwidth;
371		ri->ri_cols = ri->ri_emuheight / ri->ri_font->fontheight;
372	} else
373#endif
374	{
375		ri->ri_cols = ri->ri_emuwidth / ri->ri_font->fontwidth;
376		ri->ri_rows = ri->ri_emuheight / ri->ri_font->fontheight;
377	}
378	ri->ri_emustride = ri->ri_emuwidth * bpp >> 3;
379	ri->ri_delta = ri->ri_stride - ri->ri_emustride;
380	ri->ri_ccol = 0;
381	ri->ri_crow = 0;
382	ri->ri_pelbytes = bpp >> 3;
383
384	ri->ri_xscale = (ri->ri_font->fontwidth * bpp) >> 3;
385	ri->ri_yscale = ri->ri_font->fontheight * ri->ri_stride;
386	ri->ri_fontscale = ri->ri_font->fontheight * ri->ri_font->stride;
387
388#ifdef DEBUG
389	if ((ri->ri_delta & 3) != 0)
390		panic("rasops_init: ri_delta not aligned on 32-bit boundary");
391#endif
392	/* Clear the entire display */
393	if ((ri->ri_flg & RI_CLEAR) != 0) {
394		memset(ri->ri_bits, 0, ri->ri_stride * ri->ri_height);
395		ri->ri_flg &= ~RI_CLEARMARGINS;
396	}
397
398	/* Now centre our window if needs be */
399	ri->ri_origbits = ri->ri_bits;
400
401	if ((ri->ri_flg & RI_CENTER) != 0) {
402		ri->ri_bits += (((ri->ri_width * bpp >> 3) -
403		    ri->ri_emustride) >> 1) & ~3;
404		ri->ri_bits += ((ri->ri_height - ri->ri_emuheight) >> 1) *
405		    ri->ri_stride;
406
407		ri->ri_yorigin = (int)(ri->ri_bits - ri->ri_origbits)
408		   / ri->ri_stride;
409		ri->ri_xorigin = (((int)(ri->ri_bits - ri->ri_origbits)
410		   % ri->ri_stride) * 8 / bpp);
411	} else
412		ri->ri_xorigin = ri->ri_yorigin = 0;
413
414	/* Clear the margins */
415	if ((ri->ri_flg & RI_CLEARMARGINS) != 0) {
416		memset(ri->ri_origbits, 0, ri->ri_bits - ri->ri_origbits);
417		for (l = 0; l < ri->ri_emuheight; l++)
418			memset(ri->ri_bits + ri->ri_emustride +
419			    l * ri->ri_stride, 0,
420			    ri->ri_stride - ri->ri_emustride);
421		memset(ri->ri_bits + ri->ri_emuheight * ri->ri_stride, 0,
422		    (ri->ri_origbits + ri->ri_height * ri->ri_stride) -
423		    (ri->ri_bits + ri->ri_emuheight * ri->ri_stride));
424	}
425
426	/*
427	 * Fill in defaults for operations set.  XXX this nukes private
428	 * routines used by accelerated fb drivers.
429	 */
430	ri->ri_ops.mapchar = rasops_mapchar;
431	ri->ri_ops.copyrows = rasops_copyrows;
432	ri->ri_ops.copycols = rasops_copycols;
433	ri->ri_ops.erasecols = rasops_erasecols;
434	ri->ri_ops.eraserows = rasops_eraserows;
435	ri->ri_ops.cursor = rasops_cursor;
436	ri->ri_ops.unpack_attr = rasops_unpack_attr;
437	ri->ri_do_cursor = rasops_do_cursor;
438	ri->ri_updatecursor = NULL;
439
440	if (ri->ri_depth < 8 || (ri->ri_flg & RI_FORCEMONO) != 0) {
441		ri->ri_ops.pack_attr = rasops_pack_mattr;
442		ri->ri_caps = WSSCREEN_UNDERLINE | WSSCREEN_REVERSE;
443	} else {
444		ri->ri_ops.pack_attr = rasops_pack_cattr;
445		ri->ri_caps = WSSCREEN_UNDERLINE | WSSCREEN_HILIT |
446		    WSSCREEN_WSCOLORS | WSSCREEN_REVERSE;
447	}
448
449	switch (ri->ri_depth) {
450#if NRASOPS1 > 0
451	case 1:
452		rasops1_init(ri);
453		break;
454#endif
455#if NRASOPS4 > 0
456	case 4:
457		rasops4_init(ri);
458		break;
459#endif
460#if NRASOPS8 > 0
461	case 8:
462		rasops8_init(ri);
463		break;
464#endif
465#if NRASOPS15 > 0 || NRASOPS16 > 0
466	case 15:
467	case 16:
468		rasops15_init(ri);
469		break;
470#endif
471#if NRASOPS24 > 0
472	case 24:
473		rasops24_init(ri);
474		break;
475#endif
476#if NRASOPS32 > 0
477	case 32:
478		rasops32_init(ri);
479		break;
480#endif
481	default:
482		ri->ri_flg &= ~RI_CFGDONE;
483		splx(s);
484		return (-1);
485	}
486
487#if NRASOPS_ROTATION > 0
488	if (ri->ri_flg & (RI_ROTATE_CW | RI_ROTATE_CCW)) {
489		ri->ri_real_ops = ri->ri_ops;
490		ri->ri_ops.copycols = rasops_copycols_rotated;
491		ri->ri_ops.copyrows = rasops_copyrows_rotated;
492		ri->ri_ops.erasecols = rasops_erasecols_rotated;
493		ri->ri_ops.eraserows = rasops_eraserows_rotated;
494		ri->ri_ops.putchar = rasops_putchar_rotated;
495	}
496#endif
497
498	ri->ri_flg |= RI_CFGDONE;
499	splx(s);
500	return (0);
501}
502
503/*
504 * Map a character.
505 */
506int
507rasops_mapchar(void *cookie, int c, u_int *cp)
508{
509	struct rasops_info *ri;
510
511	ri = (struct rasops_info *)cookie;
512
513#ifdef DIAGNOSTIC
514	if (ri->ri_font == NULL)
515		panic("rasops_mapchar: no font selected");
516#endif
517	if (ri->ri_font->encoding != WSDISPLAY_FONTENC_ISO) {
518		if ((c = wsfont_map_unichar(ri->ri_font, c)) < 0) {
519			*cp = '?';
520			return (0);
521		}
522	}
523
524	if (c < ri->ri_font->firstchar ||
525	    c - ri->ri_font->firstchar >= ri->ri_font->numchars) {
526		*cp = '?';
527		return (0);
528	}
529
530	*cp = c;
531	return (5);
532}
533
534/*
535 * Pack a color attribute.
536 */
537int
538rasops_pack_cattr(void *cookie, int fg, int bg, int flg, uint32_t *attr)
539{
540	int swap;
541
542	if ((flg & WSATTR_BLINK) != 0)
543		return (EINVAL);
544
545	if ((flg & WSATTR_WSCOLORS) == 0) {
546		fg = WS_DEFAULT_FG;
547		bg = WS_DEFAULT_BG;
548	}
549
550	if ((flg & WSATTR_REVERSE) != 0) {
551		swap = fg;
552		fg = bg;
553		bg = swap;
554	}
555
556	/* Bold is only supported for ANSI colors 0 - 7. */
557	if ((flg & WSATTR_HILIT) != 0 && fg < 8)
558		fg += 8;
559
560	*attr = (bg << 16) | (fg << 24) | (flg & WSATTR_UNDERLINE);
561	return (0);
562}
563
564/*
565 * Pack a mono attribute.
566 */
567int
568rasops_pack_mattr(void *cookie, int fg, int bg, int flg, uint32_t *attr)
569{
570	int swap;
571
572	if ((flg & (WSATTR_BLINK | WSATTR_HILIT | WSATTR_WSCOLORS)) != 0)
573		return (EINVAL);
574
575	fg = 1;
576	bg = 0;
577
578	if ((flg & WSATTR_REVERSE) != 0) {
579		swap = fg;
580		fg = bg;
581		bg = swap;
582	}
583
584	*attr = (bg << 16) | (fg << 24) | (flg & WSATTR_UNDERLINE);
585	return (0);
586}
587
588/*
589 * Copy rows.
590 */
591int
592rasops_copyrows(void *cookie, int src, int dst, int num)
593{
594	int32_t *sp, *dp, *srp, *drp;
595	struct rasops_info *ri;
596	int n8, n1, cnt, delta;
597
598	ri = (struct rasops_info *)cookie;
599
600#ifdef RASOPS_CLIPPING
601	if (dst == src)
602		return 0;
603
604	if (src < 0) {
605		num += src;
606		src = 0;
607	}
608
609	if ((src + num) > ri->ri_rows)
610		num = ri->ri_rows - src;
611
612	if (dst < 0) {
613		num += dst;
614		dst = 0;
615	}
616
617	if ((dst + num) > ri->ri_rows)
618		num = ri->ri_rows - dst;
619
620	if (num <= 0)
621		return 0;
622#endif
623
624	num *= ri->ri_font->fontheight;
625	n8 = ri->ri_emustride >> 5;
626	n1 = (ri->ri_emustride >> 2) & 7;
627
628	if (dst < src) {
629		srp = (int32_t *)(ri->ri_bits + src * ri->ri_yscale);
630		drp = (int32_t *)(ri->ri_bits + dst * ri->ri_yscale);
631		delta = ri->ri_stride;
632	} else {
633		src = ri->ri_font->fontheight * src + num - 1;
634		dst = ri->ri_font->fontheight * dst + num - 1;
635		srp = (int32_t *)(ri->ri_bits + src * ri->ri_stride);
636		drp = (int32_t *)(ri->ri_bits + dst * ri->ri_stride);
637		delta = -ri->ri_stride;
638	}
639
640	while (num--) {
641		dp = drp;
642		sp = srp;
643		DELTA(drp, delta, int32_t *);
644		DELTA(srp, delta, int32_t *);
645
646		for (cnt = n8; cnt; cnt--) {
647			dp[0] = sp[0];
648			dp[1] = sp[1];
649			dp[2] = sp[2];
650			dp[3] = sp[3];
651			dp[4] = sp[4];
652			dp[5] = sp[5];
653			dp[6] = sp[6];
654			dp[7] = sp[7];
655			dp += 8;
656			sp += 8;
657		}
658
659		for (cnt = n1; cnt; cnt--)
660			*dp++ = *sp++;
661	}
662
663	return 0;
664}
665
666/*
667 * Copy columns. This is slow, and hard to optimize due to alignment,
668 * and the fact that we have to copy both left->right and right->left.
669 * We simply cop-out here and use either memmove() or slow_bcopy(),
670 * since they handle all of these cases anyway.
671 */
672int
673rasops_copycols(void *cookie, int row, int src, int dst, int num)
674{
675	struct rasops_info *ri;
676	u_char *sp, *dp;
677	int height;
678
679	ri = (struct rasops_info *)cookie;
680
681#ifdef RASOPS_CLIPPING
682	if (dst == src)
683		return 0;
684
685	/* Catches < 0 case too */
686	if ((unsigned)row >= (unsigned)ri->ri_rows)
687		return 0;
688
689	if (src < 0) {
690		num += src;
691		src = 0;
692	}
693
694	if ((src + num) > ri->ri_cols)
695		num = ri->ri_cols - src;
696
697	if (dst < 0) {
698		num += dst;
699		dst = 0;
700	}
701
702	if ((dst + num) > ri->ri_cols)
703		num = ri->ri_cols - dst;
704
705	if (num <= 0)
706		return 0;
707#endif
708
709	num *= ri->ri_xscale;
710	row *= ri->ri_yscale;
711	height = ri->ri_font->fontheight;
712
713	sp = ri->ri_bits + row + src * ri->ri_xscale;
714	dp = ri->ri_bits + row + dst * ri->ri_xscale;
715
716#if NRASOPS_BSWAP > 0
717	if (ri->ri_flg & RI_BSWAP) {
718		while (height--) {
719			slow_bcopy(sp, dp, num);
720			dp += ri->ri_stride;
721			sp += ri->ri_stride;
722		}
723	} else
724#endif
725	{
726		while (height--) {
727			memmove(dp, sp, num);
728			dp += ri->ri_stride;
729			sp += ri->ri_stride;
730		}
731	}
732
733	return 0;
734}
735
736/*
737 * Turn cursor off/on.
738 */
739int
740rasops_cursor(void *cookie, int on, int row, int col)
741{
742	struct rasops_info *ri;
743	int rc;
744
745	ri = (struct rasops_info *)cookie;
746
747	/* Turn old cursor off */
748	if ((ri->ri_flg & RI_CURSOR) != 0) {
749#ifdef RASOPS_CLIPPING
750		if ((ri->ri_flg & RI_CURSORCLIP) == 0)
751#endif
752			if ((rc = ri->ri_do_cursor(ri)) != 0)
753				return rc;
754		ri->ri_flg &= ~RI_CURSOR;
755	}
756
757	/* Select new cursor */
758#ifdef RASOPS_CLIPPING
759	ri->ri_flg &= ~RI_CURSORCLIP;
760
761	if (row < 0 || row >= ri->ri_rows)
762		ri->ri_flg |= RI_CURSORCLIP;
763	else if (col < 0 || col >= ri->ri_cols)
764		ri->ri_flg |= RI_CURSORCLIP;
765#endif
766	ri->ri_crow = row;
767	ri->ri_ccol = col;
768
769	if (ri->ri_updatecursor != NULL)
770		ri->ri_updatecursor(ri);
771
772	if (on) {
773#ifdef RASOPS_CLIPPING
774		if ((ri->ri_flg & RI_CURSORCLIP) == 0)
775#endif
776			if ((rc = ri->ri_do_cursor(ri)) != 0)
777				return rc;
778		ri->ri_flg |= RI_CURSOR;
779	}
780
781	return 0;
782}
783
784/*
785 * Make the device colormap
786 */
787void
788rasops_init_devcmap(struct rasops_info *ri)
789{
790	int i;
791#if NRASOPS15 > 0 || NRASOPS16 > 0 || NRASOPS24 > 0 || NRASOPS32 > 0
792	const u_char *p;
793#endif
794#if NRASOPS4 > 0 || NRASOPS15 > 0 || NRASOPS16 > 0 || NRASOPS24 > 0 || NRASOPS32 > 0
795	int c;
796#endif
797
798	if (ri->ri_depth == 1 || (ri->ri_flg & RI_FORCEMONO) != 0) {
799		ri->ri_devcmap[0] = 0;
800		for (i = 1; i < 16; i++)
801			ri->ri_devcmap[i] = 0xffffffff;
802		return;
803	}
804
805	switch (ri->ri_depth) {
806#if NRASOPS4 > 0
807	case 4:
808		for (i = 0; i < 16; i++) {
809			c = i | (i << 4);
810			ri->ri_devcmap[i] = c | (c<<8) | (c<<16) | (c<<24);
811		}
812		return;
813#endif
814#if NRASOPS8 > 0
815	case 8:
816		for (i = 0; i < 16; i++)
817			ri->ri_devcmap[i] = i | (i<<8) | (i<<16) | (i<<24);
818		return;
819#endif
820	default:
821		break;
822	}
823
824#if NRASOPS15 > 0 || NRASOPS16 > 0 || NRASOPS24 > 0 || NRASOPS32 > 0
825	p = rasops_cmap;
826
827	for (i = 0; i < 16; i++) {
828		if (ri->ri_rnum <= 8)
829			c = (*p >> (8 - ri->ri_rnum)) << ri->ri_rpos;
830		else
831			c = (*p << (ri->ri_rnum - 8)) << ri->ri_rpos;
832		p++;
833
834		if (ri->ri_gnum <= 8)
835			c |= (*p >> (8 - ri->ri_gnum)) << ri->ri_gpos;
836		else
837			c |= (*p << (ri->ri_gnum - 8)) << ri->ri_gpos;
838		p++;
839
840		if (ri->ri_bnum <= 8)
841			c |= (*p >> (8 - ri->ri_bnum)) << ri->ri_bpos;
842		else
843			c |= (*p << (ri->ri_bnum - 8)) << ri->ri_bpos;
844		p++;
845
846		/* Fill the word for generic routines, which want this */
847		if (ri->ri_depth == 24)
848			c = c | ((c & 0xff) << 24);
849		else if (ri->ri_depth <= 16)
850			c = c | (c << 16);
851
852		/* 24bpp does bswap on the fly. {32,16,15}bpp do it here. */
853#if NRASOPS_BSWAP > 0
854		if ((ri->ri_flg & RI_BSWAP) == 0)
855			ri->ri_devcmap[i] = c;
856		else if (ri->ri_depth == 32)
857			ri->ri_devcmap[i] = swap32(c);
858		else if (ri->ri_depth == 16 || ri->ri_depth == 15)
859			ri->ri_devcmap[i] = swap16(c);
860		else
861			ri->ri_devcmap[i] = c;
862#else
863		ri->ri_devcmap[i] = c;
864#endif
865	}
866#endif
867}
868
869/*
870 * Unpack a rasops attribute
871 */
872void
873rasops_unpack_attr(void *cookie, uint32_t attr, int *fg, int *bg, int *underline)
874{
875	*fg = ((u_int)attr >> 24) & 0xf;
876	*bg = ((u_int)attr >> 16) & 0xf;
877	if (underline != NULL)
878		*underline = (u_int)attr & WSATTR_UNDERLINE;
879}
880
881/*
882 * Erase rows
883 */
884int
885rasops_eraserows(void *cookie, int row, int num, uint32_t attr)
886{
887	struct rasops_info *ri;
888	int np, nw, cnt, delta;
889	int32_t *dp, clr;
890
891	ri = (struct rasops_info *)cookie;
892
893#ifdef RASOPS_CLIPPING
894	if (row < 0) {
895		num += row;
896		row = 0;
897	}
898
899	if ((row + num) > ri->ri_rows)
900		num = ri->ri_rows - row;
901
902	if (num <= 0)
903		return 0;
904#endif
905
906	clr = ri->ri_devcmap[(attr >> 16) & 0xf];
907
908	/*
909	 * XXX The wsdisplay_emulops interface seems a little deficient in
910	 * that there is no way to clear the *entire* screen. We provide a
911	 * workaround here: if the entire console area is being cleared, and
912	 * the RI_FULLCLEAR flag is set, clear the entire display.
913	 */
914	if (num == ri->ri_rows && (ri->ri_flg & RI_FULLCLEAR) != 0) {
915		np = ri->ri_stride >> 5;
916		nw = (ri->ri_stride >> 2) & 7;
917		num = ri->ri_height;
918		dp = (int32_t *)ri->ri_origbits;
919		delta = 0;
920	} else {
921		np = ri->ri_emustride >> 5;
922		nw = (ri->ri_emustride >> 2) & 7;
923		num *= ri->ri_font->fontheight;
924		dp = (int32_t *)(ri->ri_bits + row * ri->ri_yscale);
925		delta = ri->ri_delta;
926	}
927
928	while (num--) {
929		for (cnt = np; cnt; cnt--) {
930			dp[0] = clr;
931			dp[1] = clr;
932			dp[2] = clr;
933			dp[3] = clr;
934			dp[4] = clr;
935			dp[5] = clr;
936			dp[6] = clr;
937			dp[7] = clr;
938			dp += 8;
939		}
940
941		for (cnt = nw; cnt; cnt--) {
942			*(int32_t *)dp = clr;
943			DELTA(dp, 4, int32_t *);
944		}
945
946		DELTA(dp, delta, int32_t *);
947	}
948
949	return 0;
950}
951
952/*
953 * Actually turn the cursor on or off. This does the dirty work for
954 * rasops_cursor().
955 */
956int
957rasops_do_cursor(struct rasops_info *ri)
958{
959	int full1, height, cnt, slop1, slop2, row, col;
960	u_char *dp, *rp;
961
962#if NRASOPS_ROTATION > 0
963	if (ri->ri_flg & RI_ROTATE_CW) {
964		/* Rotate rows/columns */
965		row = ri->ri_ccol;
966		col = ri->ri_rows - ri->ri_crow - 1;
967	} else if (ri->ri_flg & RI_ROTATE_CCW) {
968		/* Rotate rows/columns */
969		row = ri->ri_cols - ri->ri_ccol - 1;
970		col = ri->ri_crow;
971	} else
972#endif
973	{
974		row = ri->ri_crow;
975		col = ri->ri_ccol;
976	}
977
978	rp = ri->ri_bits + row * ri->ri_yscale + col * ri->ri_xscale;
979	height = ri->ri_font->fontheight;
980	slop1 = (4 - ((long)rp & 3)) & 3;
981
982	if (slop1 > ri->ri_xscale)
983		slop1 = ri->ri_xscale;
984
985	slop2 = (ri->ri_xscale - slop1) & 3;
986	full1 = (ri->ri_xscale - slop1 - slop2) >> 2;
987
988	if ((slop1 | slop2) == 0) {
989		/* A common case */
990		while (height--) {
991			dp = rp;
992			rp += ri->ri_stride;
993
994			for (cnt = full1; cnt; cnt--) {
995				*(int32_t *)dp ^= ~0;
996				dp += 4;
997			}
998		}
999	} else {
1000		/* XXX this is stupid.. use masks instead */
1001		while (height--) {
1002			dp = rp;
1003			rp += ri->ri_stride;
1004
1005			if (slop1 & 1)
1006				*dp++ ^= ~0;
1007
1008			if (slop1 & 2) {
1009				*(int16_t *)dp ^= ~0;
1010				dp += 2;
1011			}
1012
1013			for (cnt = full1; cnt; cnt--) {
1014				*(int32_t *)dp ^= ~0;
1015				dp += 4;
1016			}
1017
1018			if (slop2 & 1)
1019				*dp++ ^= ~0;
1020
1021			if (slop2 & 2)
1022				*(int16_t *)dp ^= ~0;
1023		}
1024	}
1025
1026	return 0;
1027}
1028
1029/*
1030 * Erase columns.
1031 */
1032int
1033rasops_erasecols(void *cookie, int row, int col, int num, uint32_t attr)
1034{
1035	int n8, height, cnt, slop1, slop2, clr;
1036	struct rasops_info *ri;
1037	int32_t *rp, *dp;
1038
1039	ri = (struct rasops_info *)cookie;
1040
1041#ifdef RASOPS_CLIPPING
1042	if ((unsigned)row >= (unsigned)ri->ri_rows)
1043		return 0;
1044
1045	if (col < 0) {
1046		num += col;
1047		col = 0;
1048	}
1049
1050	if ((col + num) > ri->ri_cols)
1051		num = ri->ri_cols - col;
1052
1053	if (num <= 0)
1054		return 0;
1055#endif
1056
1057	num = num * ri->ri_xscale;
1058	rp = (int32_t *)(ri->ri_bits + row*ri->ri_yscale + col*ri->ri_xscale);
1059	height = ri->ri_font->fontheight;
1060	clr = ri->ri_devcmap[(attr >> 16) & 0xf];
1061
1062	/* Don't bother using the full loop for <= 32 pels */
1063	if (num <= 32) {
1064		if (((num | ri->ri_xscale) & 3) == 0) {
1065			/* Word aligned blt */
1066			num >>= 2;
1067
1068			while (height--) {
1069				dp = rp;
1070				DELTA(rp, ri->ri_stride, int32_t *);
1071
1072				for (cnt = num; cnt; cnt--)
1073					*dp++ = clr;
1074			}
1075		} else if (((num | ri->ri_xscale) & 1) == 0) {
1076			/*
1077			 * Halfword aligned blt. This is needed so the
1078			 * 15/16 bit ops can use this function.
1079			 */
1080			num >>= 1;
1081
1082			while (height--) {
1083				dp = rp;
1084				DELTA(rp, ri->ri_stride, int32_t *);
1085
1086				for (cnt = num; cnt; cnt--) {
1087					*(int16_t *)dp = clr;
1088					DELTA(dp, 2, int32_t *);
1089				}
1090			}
1091		} else {
1092			while (height--) {
1093				dp = rp;
1094				DELTA(rp, ri->ri_stride, int32_t *);
1095
1096				for (cnt = num; cnt; cnt--) {
1097					*(u_char *)dp = clr;
1098					DELTA(dp, 1, int32_t *);
1099				}
1100			}
1101		}
1102
1103		return 0;
1104	}
1105
1106	slop1 = (4 - ((long)rp & 3)) & 3;
1107	slop2 = (num - slop1) & 3;
1108	num -= slop1 + slop2;
1109	n8 = num >> 5;
1110	num = (num >> 2) & 7;
1111
1112	while (height--) {
1113		dp = rp;
1114		DELTA(rp, ri->ri_stride, int32_t *);
1115
1116		/* Align span to 4 bytes */
1117		if (slop1 & 1) {
1118			*(u_char *)dp = clr;
1119			DELTA(dp, 1, int32_t *);
1120		}
1121
1122		if (slop1 & 2) {
1123			*(int16_t *)dp = clr;
1124			DELTA(dp, 2, int32_t *);
1125		}
1126
1127		/* Write 32 bytes per loop */
1128		for (cnt = n8; cnt; cnt--) {
1129			dp[0] = clr;
1130			dp[1] = clr;
1131			dp[2] = clr;
1132			dp[3] = clr;
1133			dp[4] = clr;
1134			dp[5] = clr;
1135			dp[6] = clr;
1136			dp[7] = clr;
1137			dp += 8;
1138		}
1139
1140		/* Write 4 bytes per loop */
1141		for (cnt = num; cnt; cnt--)
1142			*dp++ = clr;
1143
1144		/* Write unaligned trailing slop */
1145		if (slop2 & 1) {
1146			*(u_char *)dp = clr;
1147			DELTA(dp, 1, int32_t *);
1148		}
1149
1150		if (slop2 & 2)
1151			*(int16_t *)dp = clr;
1152	}
1153
1154	return 0;
1155}
1156
1157#if NRASOPS_ROTATION > 0
1158/*
1159 * Quarter clockwise rotation routines (originally intended for the
1160 * built-in Zaurus C3x00 display in 16bpp).
1161 */
1162
1163void
1164rasops_rotate_font(int *cookie, int ccw)
1165{
1166	struct rotatedfont *f;
1167	int ncookie;
1168
1169	SLIST_FOREACH(f, &rotatedfonts, rf_next) {
1170		if (f->rf_cookie == *cookie) {
1171			*cookie = f->rf_rotated;
1172			return;
1173		}
1174	}
1175
1176	/*
1177	 * We did not find a rotated version of this font. Ask the wsfont
1178	 * code to compute one for us.
1179	 */
1180	if ((ncookie = wsfont_rotate(*cookie, ccw)) == -1)
1181		return;
1182
1183	f = malloc(sizeof(struct rotatedfont), M_DEVBUF, M_WAITOK);
1184	f->rf_cookie = *cookie;
1185	f->rf_rotated = ncookie;
1186	SLIST_INSERT_HEAD(&rotatedfonts, f, rf_next);
1187
1188	*cookie = ncookie;
1189}
1190
1191void
1192rasops_copychar(void *cookie, int srcrow, int dstrow, int srccol, int dstcol)
1193{
1194	struct rasops_info *ri;
1195	u_char *sp, *dp;
1196	int height;
1197	int r_srcrow, r_dstrow, r_srccol, r_dstcol;
1198
1199	ri = (struct rasops_info *)cookie;
1200
1201	r_srcrow = srccol;
1202	r_dstrow = dstcol;
1203	r_srccol = ri->ri_rows - srcrow - 1;
1204	r_dstcol = ri->ri_rows - dstrow - 1;
1205
1206	r_srcrow *= ri->ri_yscale;
1207	r_dstrow *= ri->ri_yscale;
1208	height = ri->ri_font->fontheight;
1209
1210	sp = ri->ri_bits + r_srcrow + r_srccol * ri->ri_xscale;
1211	dp = ri->ri_bits + r_dstrow + r_dstcol * ri->ri_xscale;
1212
1213#if NRASOPS_BSWAP > 0
1214	if (ri->ri_flg & RI_BSWAP) {
1215		while (height--) {
1216			slow_bcopy(sp, dp, ri->ri_xscale);
1217			dp += ri->ri_stride;
1218			sp += ri->ri_stride;
1219		}
1220	} else
1221#endif
1222	{
1223		while (height--) {
1224			memmove(dp, sp, ri->ri_xscale);
1225			dp += ri->ri_stride;
1226			sp += ri->ri_stride;
1227		}
1228	}
1229}
1230
1231int
1232rasops_putchar_rotated(void *cookie, int row, int col, u_int uc, uint32_t attr)
1233{
1234	struct rasops_info *ri;
1235	u_char *rp;
1236	int height;
1237	int rc;
1238
1239	ri = (struct rasops_info *)cookie;
1240
1241	if (ri->ri_flg & RI_ROTATE_CW)
1242		row = ri->ri_rows - row - 1;
1243	else
1244		col = ri->ri_cols - col - 1;
1245
1246	/* Do rotated char sans (side)underline */
1247	rc = ri->ri_real_ops.putchar(cookie, col, row, uc,
1248	    attr & ~WSATTR_UNDERLINE);
1249	if (rc != 0)
1250		return rc;
1251
1252	/* Do rotated underline */
1253	rp = ri->ri_bits + col * ri->ri_yscale + row * ri->ri_xscale;
1254	if (ri->ri_flg & RI_ROTATE_CCW)
1255		rp += (ri->ri_font->fontwidth - 1) * ri->ri_pelbytes;
1256	height = ri->ri_font->fontheight;
1257
1258	/* XXX this assumes 16-bit color depth */
1259	if ((attr & WSATTR_UNDERLINE) != 0) {
1260		int16_t c = (int16_t)ri->ri_devcmap[((u_int)attr >> 24) & 0xf];
1261
1262		while (height--) {
1263			*(int16_t *)rp = c;
1264			rp += ri->ri_stride;
1265		}
1266	}
1267
1268	return 0;
1269}
1270
1271int
1272rasops_erasecols_rotated(void *cookie, int row, int col, int num, uint32_t attr)
1273{
1274	int i;
1275	int rc;
1276
1277	for (i = col; i < col + num; i++) {
1278		rc = rasops_putchar_rotated(cookie, row, i, ' ', attr);
1279		if (rc != 0)
1280			return rc;
1281	}
1282
1283	return 0;
1284}
1285
1286/* XXX: these could likely be optimised somewhat. */
1287int
1288rasops_copyrows_rotated(void *cookie, int src, int dst, int num)
1289{
1290	struct rasops_info *ri = (struct rasops_info *)cookie;
1291	int col, roff;
1292
1293	if (src > dst) {
1294		for (roff = 0; roff < num; roff++)
1295			for (col = 0; col < ri->ri_cols; col++)
1296				rasops_copychar(cookie, src + roff, dst + roff,
1297				    col, col);
1298	} else {
1299		for (roff = num - 1; roff >= 0; roff--)
1300			for (col = 0; col < ri->ri_cols; col++)
1301				rasops_copychar(cookie, src + roff, dst + roff,
1302				    col, col);
1303	}
1304
1305	return 0;
1306}
1307
1308int
1309rasops_copycols_rotated(void *cookie, int row, int src, int dst, int num)
1310{
1311	int coff;
1312
1313	if (src > dst) {
1314		for (coff = 0; coff < num; coff++)
1315			rasops_copychar(cookie, row, row, src + coff,
1316			    dst + coff);
1317	} else {
1318		for (coff = num - 1; coff >= 0; coff--)
1319			rasops_copychar(cookie, row, row, src + coff,
1320			    dst + coff);
1321	}
1322
1323	return 0;
1324}
1325
1326int
1327rasops_eraserows_rotated(void *cookie, int row, int num, uint32_t attr)
1328{
1329	struct rasops_info *ri;
1330	int col, rn;
1331	int rc;
1332
1333	ri = (struct rasops_info *)cookie;
1334
1335	for (rn = row; rn < row + num; rn++)
1336		for (col = 0; col < ri->ri_cols; col++) {
1337			rc = rasops_putchar_rotated(cookie, rn, col, ' ', attr);
1338			if (rc != 0)
1339				return rc;
1340		}
1341
1342	return 0;
1343}
1344#endif	/* NRASOPS_ROTATION */
1345
1346#if NRASOPS_BSWAP > 0
1347/*
1348 * Strictly byte-only bcopy() version, to be used with RI_BSWAP, as the
1349 * regular bcopy() may want to optimize things by doing larger-than-byte
1350 * reads or write. This may confuse things if src and dst have different
1351 * alignments.
1352 */
1353void
1354slow_bcopy(void *s, void *d, size_t len)
1355{
1356	u_int8_t *src = s;
1357	u_int8_t *dst = d;
1358
1359	if ((vaddr_t)dst <= (vaddr_t)src) {
1360		while (len-- != 0)
1361			*dst++ = *src++;
1362	} else {
1363		src += len;
1364		dst += len;
1365		if (len != 0)
1366			while (--len != 0)
1367				*--dst = *--src;
1368	}
1369}
1370#endif	/* NRASOPS_BSWAP */
1371
1372int
1373rasops_alloc_screen(void *v, void **cookiep,
1374    int *curxp, int *curyp, uint32_t *attrp)
1375{
1376	struct rasops_info *ri = v;
1377	struct rasops_screen *scr;
1378	int i;
1379
1380	scr = malloc(sizeof(*scr), M_DEVBUF, M_NOWAIT);
1381	if (scr == NULL)
1382		return (ENOMEM);
1383
1384	scr->rs_sbscreens = RS_SCROLLBACK_SCREENS;
1385	scr->rs_bs = mallocarray(ri->ri_rows * (scr->rs_sbscreens + 1),
1386	    ri->ri_cols * sizeof(struct wsdisplay_charcell), M_DEVBUF,
1387	    M_NOWAIT);
1388	if (scr->rs_bs == NULL) {
1389		free(scr, M_DEVBUF, sizeof(*scr));
1390		return (ENOMEM);
1391	}
1392	scr->rs_visibleoffset = scr->rs_dispoffset = ri->ri_rows *
1393	    scr->rs_sbscreens * ri->ri_cols;
1394
1395	*cookiep = scr;
1396	*curxp = 0;
1397	*curyp = 0;
1398	ri->ri_pack_attr(ri, 0, 0, 0, attrp);
1399
1400	scr->rs_ri = ri;
1401	scr->rs_visible = (ri->ri_nscreens == 0);
1402	scr->rs_crow = -1;
1403	scr->rs_ccol = -1;
1404	scr->rs_defattr = *attrp;
1405
1406	for (i = 0; i < scr->rs_dispoffset; i++) {
1407		scr->rs_bs[i].uc = ' ';
1408		scr->rs_bs[i].attr = scr->rs_defattr;
1409	}
1410
1411	if (ri->ri_bs && scr->rs_visible) {
1412		memcpy(scr->rs_bs + scr->rs_dispoffset, ri->ri_bs,
1413		    ri->ri_rows * ri->ri_cols *
1414		    sizeof(struct wsdisplay_charcell));
1415	} else {
1416		for (i = 0; i < ri->ri_rows * ri->ri_cols; i++) {
1417			scr->rs_bs[scr->rs_dispoffset + i].uc = ' ';
1418			scr->rs_bs[scr->rs_dispoffset + i].attr =
1419			    scr->rs_defattr;
1420		}
1421	}
1422
1423	LIST_INSERT_HEAD(&ri->ri_screens, scr, rs_next);
1424	ri->ri_nscreens++;
1425
1426	return (0);
1427}
1428
1429void
1430rasops_free_screen(void *v, void *cookie)
1431{
1432	struct rasops_info *ri = v;
1433	struct rasops_screen *scr = cookie;
1434
1435	LIST_REMOVE(scr, rs_next);
1436	ri->ri_nscreens--;
1437
1438	free(scr->rs_bs, M_DEVBUF,
1439	    ri->ri_rows * (scr->rs_sbscreens + 1) * ri->ri_cols *
1440	    sizeof(struct wsdisplay_charcell));
1441	free(scr, M_DEVBUF, sizeof(*scr));
1442}
1443
1444int
1445rasops_show_screen(void *v, void *cookie, int waitok,
1446    void (*cb)(void *, int, int), void *cbarg)
1447{
1448	struct rasops_info *ri = v;
1449
1450	ri->ri_switchcookie = cookie;
1451	if (cb) {
1452		ri->ri_switchcb = cb;
1453		ri->ri_switchcbarg = cbarg;
1454		task_add(systq, &ri->ri_switchtask);
1455		return (EAGAIN);
1456	}
1457
1458	rasops_doswitch(ri);
1459	return (0);
1460}
1461
1462void
1463rasops_doswitch(void *v)
1464{
1465	struct rasops_info *ri = v;
1466	struct rasops_screen *scr = ri->ri_switchcookie;
1467	int row, col;
1468
1469	rasops_cursor(ri, 0, 0, 0);
1470	ri->ri_active->rs_visible = 0;
1471	ri->ri_eraserows(ri, 0, ri->ri_rows, scr->rs_defattr);
1472	ri->ri_active = scr;
1473	ri->ri_bs = &ri->ri_active->rs_bs[ri->ri_active->rs_dispoffset];
1474	ri->ri_active->rs_visible = 1;
1475	ri->ri_active->rs_visibleoffset = ri->ri_active->rs_dispoffset;
1476	for (row = 0; row < ri->ri_rows; row++) {
1477		for (col = 0; col < ri->ri_cols; col++) {
1478			int off = row * scr->rs_ri->ri_cols + col +
1479			    scr->rs_visibleoffset;
1480
1481			ri->ri_putchar(ri, row, col, scr->rs_bs[off].uc,
1482			    scr->rs_bs[off].attr);
1483		}
1484	}
1485	if (scr->rs_crow != -1)
1486		rasops_cursor(ri, 1, scr->rs_crow, scr->rs_ccol);
1487
1488	if (ri->ri_switchcb)
1489		(*ri->ri_switchcb)(ri->ri_switchcbarg, 0, 0);
1490}
1491
1492int
1493rasops_getchar(void *v, int row, int col, struct wsdisplay_charcell *cell)
1494{
1495	struct rasops_info *ri = v;
1496	struct rasops_screen *scr = ri->ri_active;
1497
1498	if (scr == NULL || scr->rs_bs == NULL)
1499		return (1);
1500
1501	*cell = scr->rs_bs[row * ri->ri_cols + col + scr->rs_dispoffset];
1502	return (0);
1503}
1504
1505int
1506rasops_vcons_cursor(void *cookie, int on, int row, int col)
1507{
1508	struct rasops_screen *scr = cookie;
1509
1510	scr->rs_crow = on ? row : -1;
1511	scr->rs_ccol = on ? col : -1;
1512
1513	if (!scr->rs_visible)
1514		return 0;
1515
1516	return rasops_cursor(scr->rs_ri, on, row, col);
1517}
1518
1519int
1520rasops_vcons_mapchar(void *cookie, int c, u_int *cp)
1521{
1522	struct rasops_screen *scr = cookie;
1523
1524	return rasops_mapchar(scr->rs_ri, c, cp);
1525}
1526
1527int
1528rasops_vcons_putchar(void *cookie, int row, int col, u_int uc, uint32_t attr)
1529{
1530	struct rasops_screen *scr = cookie;
1531	int off = row * scr->rs_ri->ri_cols + col + scr->rs_dispoffset;
1532
1533	if (scr->rs_visible && scr->rs_visibleoffset != scr->rs_dispoffset)
1534		rasops_scrollback(scr->rs_ri, scr, 0);
1535
1536	scr->rs_bs[off].uc = uc;
1537	scr->rs_bs[off].attr = attr;
1538
1539	if (!scr->rs_visible)
1540		return 0;
1541
1542	return scr->rs_ri->ri_putchar(scr->rs_ri, row, col, uc, attr);
1543}
1544
1545int
1546rasops_vcons_copycols(void *cookie, int row, int src, int dst, int num)
1547{
1548	struct rasops_screen *scr = cookie;
1549	struct rasops_info *ri = scr->rs_ri;
1550	int cols = scr->rs_ri->ri_cols;
1551	int col, rc;
1552
1553	memmove(&scr->rs_bs[row * cols + dst + scr->rs_dispoffset],
1554	    &scr->rs_bs[row * cols + src + scr->rs_dispoffset],
1555	    num * sizeof(struct wsdisplay_charcell));
1556
1557	if (!scr->rs_visible)
1558		return 0;
1559
1560	if ((ri->ri_flg & RI_WRONLY) == 0)
1561		return ri->ri_copycols(ri, row, src, dst, num);
1562
1563	for (col = dst; col < dst + num; col++) {
1564		int off = row * cols + col + scr->rs_dispoffset;
1565
1566		rc = ri->ri_putchar(ri, row, col,
1567		    scr->rs_bs[off].uc, scr->rs_bs[off].attr);
1568		if (rc != 0)
1569			return rc;
1570	}
1571
1572	return 0;
1573}
1574
1575int
1576rasops_vcons_erasecols(void *cookie, int row, int col, int num, uint32_t attr)
1577{
1578	struct rasops_screen *scr = cookie;
1579	int cols = scr->rs_ri->ri_cols;
1580	int i;
1581
1582	for (i = 0; i < num; i++) {
1583		int off = row * cols + col + i + scr->rs_dispoffset;
1584
1585		scr->rs_bs[off].uc = ' ';
1586		scr->rs_bs[off].attr = attr;
1587	}
1588
1589	if (!scr->rs_visible)
1590		return 0;
1591
1592	return scr->rs_ri->ri_erasecols(scr->rs_ri, row, col, num, attr);
1593}
1594
1595int
1596rasops_vcons_copyrows(void *cookie, int src, int dst, int num)
1597{
1598	struct rasops_screen *scr = cookie;
1599	struct rasops_info *ri = scr->rs_ri;
1600	int cols = ri->ri_cols;
1601	int row, col, rc;
1602	int srcofs;
1603	int move;
1604
1605	/* update the scrollback buffer if the entire screen is moving */
1606	if (dst == 0 && (src + num == ri->ri_rows) && scr->rs_sbscreens > 0)
1607		memmove(&scr->rs_bs[dst], &scr->rs_bs[src * cols],
1608		    ri->ri_rows * scr->rs_sbscreens * cols
1609		    * sizeof(struct wsdisplay_charcell));
1610
1611	/* copy everything */
1612	if ((ri->ri_flg & RI_WRONLY) == 0 || !scr->rs_visible) {
1613		memmove(&scr->rs_bs[dst * cols + scr->rs_dispoffset],
1614		    &scr->rs_bs[src * cols + scr->rs_dispoffset],
1615		    num * cols * sizeof(struct wsdisplay_charcell));
1616
1617		if (!scr->rs_visible)
1618			return 0;
1619
1620		return ri->ri_copyrows(ri, src, dst, num);
1621	}
1622
1623	/* smart update, only redraw characters that are different */
1624	srcofs = (src - dst) * cols;
1625
1626	for (move = 0; move < num; move++) {
1627		row = srcofs > 0 ? dst + move : dst + num - 1 - move;
1628		for (col = 0; col < cols; col++) {
1629			int off = row * cols + col + scr->rs_dispoffset;
1630			int newc = scr->rs_bs[off+srcofs].uc;
1631			int newa = scr->rs_bs[off+srcofs].attr;
1632
1633			if (scr->rs_bs[off].uc == newc &&
1634			    scr->rs_bs[off].attr == newa)
1635				continue;
1636			scr->rs_bs[off].uc = newc;
1637			scr->rs_bs[off].attr = newa;
1638			rc = ri->ri_putchar(ri, row, col, newc, newa);
1639			if (rc != 0)
1640				return rc;
1641		}
1642	}
1643
1644	return 0;
1645}
1646
1647int
1648rasops_vcons_eraserows(void *cookie, int row, int num, uint32_t attr)
1649{
1650	struct rasops_screen *scr = cookie;
1651	int cols = scr->rs_ri->ri_cols;
1652	int i;
1653
1654	for (i = 0; i < num * cols; i++) {
1655		int off = row * cols + i + scr->rs_dispoffset;
1656
1657		scr->rs_bs[off].uc = ' ';
1658		scr->rs_bs[off].attr = attr;
1659	}
1660
1661	if (!scr->rs_visible)
1662		return 0;
1663
1664	return scr->rs_ri->ri_eraserows(scr->rs_ri, row, num, attr);
1665}
1666
1667int
1668rasops_vcons_pack_attr(void *cookie, int fg, int bg, int flg, uint32_t *attr)
1669{
1670	struct rasops_screen *scr = cookie;
1671
1672	return scr->rs_ri->ri_pack_attr(scr->rs_ri, fg, bg, flg, attr);
1673}
1674
1675void
1676rasops_vcons_unpack_attr(void *cookie, uint32_t attr, int *fg, int *bg,
1677    int *underline)
1678{
1679	struct rasops_screen *scr = cookie;
1680
1681	rasops_unpack_attr(scr->rs_ri, attr, fg, bg, underline);
1682}
1683
1684int
1685rasops_wronly_putchar(void *cookie, int row, int col, u_int uc, uint32_t attr)
1686{
1687	struct rasops_info *ri = cookie;
1688	int off = row * ri->ri_cols + col;
1689
1690	ri->ri_bs[off].uc = uc;
1691	ri->ri_bs[off].attr = attr;
1692
1693	return ri->ri_putchar(ri, row, col, uc, attr);
1694}
1695
1696int
1697rasops_wronly_copycols(void *cookie, int row, int src, int dst, int num)
1698{
1699	struct rasops_info *ri = cookie;
1700	int cols = ri->ri_cols;
1701	int col, rc;
1702
1703	memmove(&ri->ri_bs[row * cols + dst], &ri->ri_bs[row * cols + src],
1704	    num * sizeof(struct wsdisplay_charcell));
1705
1706	for (col = dst; col < dst + num; col++) {
1707		int off = row * cols + col;
1708
1709		rc = ri->ri_putchar(ri, row, col,
1710		    ri->ri_bs[off].uc, ri->ri_bs[off].attr);
1711		if (rc != 0)
1712			return rc;
1713	}
1714
1715	return 0;
1716}
1717
1718int
1719rasops_wronly_erasecols(void *cookie, int row, int col, int num, uint32_t attr)
1720{
1721	struct rasops_info *ri = cookie;
1722	int cols = ri->ri_cols;
1723	int i;
1724
1725	for (i = 0; i < num; i++) {
1726		int off = row * cols + col + i;
1727
1728		ri->ri_bs[off].uc = ' ';
1729		ri->ri_bs[off].attr = attr;
1730	}
1731
1732	return ri->ri_erasecols(ri, row, col, num, attr);
1733}
1734
1735int
1736rasops_wronly_copyrows(void *cookie, int src, int dst, int num)
1737{
1738	struct rasops_info *ri = cookie;
1739	int cols = ri->ri_cols;
1740	int row, col, rc;
1741
1742	memmove(&ri->ri_bs[dst * cols], &ri->ri_bs[src * cols],
1743	    num * cols * sizeof(struct wsdisplay_charcell));
1744
1745	for (row = dst; row < dst + num; row++) {
1746		for (col = 0; col < cols; col++) {
1747			int off = row * cols + col;
1748
1749			rc = ri->ri_putchar(ri, row, col,
1750			    ri->ri_bs[off].uc, ri->ri_bs[off].attr);
1751			if (rc != 0)
1752				return rc;
1753		}
1754	}
1755
1756	return 0;
1757}
1758
1759int
1760rasops_wronly_eraserows(void *cookie, int row, int num, uint32_t attr)
1761{
1762	struct rasops_info *ri = cookie;
1763	int cols = ri->ri_cols;
1764	int i;
1765
1766	for (i = 0; i < num * cols; i++) {
1767		int off = row * cols + i;
1768
1769		ri->ri_bs[off].uc = ' ';
1770		ri->ri_bs[off].attr = attr;
1771	}
1772
1773	return ri->ri_eraserows(ri, row, num, attr);
1774}
1775
1776int
1777rasops_wronly_do_cursor(struct rasops_info *ri)
1778{
1779	int off = ri->ri_crow * ri->ri_cols + ri->ri_ccol;
1780	u_int uc;
1781	uint32_t attr;
1782	int fg, bg;
1783
1784	uc = ri->ri_bs[off].uc;
1785	attr = ri->ri_bs[off].attr;
1786
1787	if ((ri->ri_flg & RI_CURSOR) == 0) {
1788		fg = ((u_int)attr >> 24) & 0xf;
1789		bg = ((u_int)attr >> 16) & 0xf;
1790		attr &= ~0x0ffff0000;
1791		attr |= (fg << 16) | (bg << 24);
1792	}
1793
1794	return ri->ri_putchar(ri, ri->ri_crow, ri->ri_ccol, uc, attr);
1795}
1796
1797/*
1798 * Font management.
1799 *
1800 * Fonts usable on raster frame buffers are managed by wsfont, and are not
1801 * tied to any particular display.
1802 */
1803
1804int
1805rasops_add_font(struct rasops_info *ri, struct wsdisplay_font *font)
1806{
1807	/* only accept matching metrics */
1808	if (font->fontwidth != ri->ri_font->fontwidth ||
1809	    font->fontheight != ri->ri_font->fontheight)
1810		return EINVAL;
1811
1812	/* for raster consoles, only accept ISO Latin-1 or Unicode encoding */
1813	if (font->encoding != WSDISPLAY_FONTENC_ISO)
1814		return EINVAL;
1815
1816	if (wsfont_add(font, 1) != 0)
1817		return EEXIST;	/* name collision */
1818
1819	font->index = -1;	/* do not store in wsdisplay_softc */
1820
1821	return 0;
1822}
1823
1824int
1825rasops_use_font(struct rasops_info *ri, struct wsdisplay_font *font)
1826{
1827	int wsfcookie;
1828	struct wsdisplay_font *wsf;
1829	const char *name;
1830
1831	/* allow an empty font name to revert to the initial font choice */
1832	name = font->name;
1833	if (*name == '\0')
1834		name = NULL;
1835
1836	wsfcookie = wsfont_find(name, ri->ri_font->fontwidth,
1837	    ri->ri_font->fontheight, 0);
1838	if (wsfcookie < 0) {
1839		wsfcookie = wsfont_find(name, 0, 0, 0);
1840		if (wsfcookie < 0)
1841			return ENOENT;	/* font exist, but different metrics */
1842		else
1843			return EINVAL;
1844	}
1845	if (wsfont_lock(wsfcookie, &wsf, WSDISPLAY_FONTORDER_KNOWN,
1846	    WSDISPLAY_FONTORDER_KNOWN) < 0)
1847		return EINVAL;
1848
1849	wsfont_unlock(ri->ri_wsfcookie);
1850	ri->ri_wsfcookie = wsfcookie;
1851	ri->ri_font = wsf;
1852	ri->ri_fontscale = ri->ri_font->fontheight * ri->ri_font->stride;
1853
1854	return 0;
1855}
1856
1857int
1858rasops_load_font(void *v, void *cookie, struct wsdisplay_font *font)
1859{
1860	struct rasops_info *ri = v;
1861
1862	/*
1863	 * For now, we want to only allow loading fonts of the same
1864	 * metrics as the currently in-use font. This requires the
1865	 * rasops struct to have been correctly configured, and a
1866	 * font to have been selected.
1867	 */
1868	if ((ri->ri_flg & RI_CFGDONE) == 0 || ri->ri_font == NULL)
1869		return EINVAL;
1870
1871	if (font->data != NULL)
1872		return rasops_add_font(ri, font);
1873	else
1874		return rasops_use_font(ri, font);
1875}
1876
1877struct rasops_list_font_ctx {
1878	struct rasops_info *ri;
1879	int cnt;
1880	struct wsdisplay_font *font;
1881};
1882
1883int
1884rasops_list_font_cb(void *cbarg, struct wsdisplay_font *font)
1885{
1886	struct rasops_list_font_ctx *ctx = cbarg;
1887
1888	if (ctx->cnt-- == 0) {
1889		ctx->font = font;
1890		return 1;
1891	}
1892
1893	return 0;
1894}
1895
1896int
1897rasops_list_font(void *v, struct wsdisplay_font *font)
1898{
1899	struct rasops_info *ri = v;
1900	struct rasops_list_font_ctx ctx;
1901	int idx;
1902
1903	if ((ri->ri_flg & RI_CFGDONE) == 0 || ri->ri_font == NULL)
1904		return EINVAL;
1905
1906	if (font->index < 0)
1907		return EINVAL;
1908
1909	ctx.ri = ri;
1910	ctx.cnt = font->index;
1911	ctx.font = NULL;
1912	wsfont_enum(rasops_list_font_cb, &ctx);
1913
1914	if (ctx.font == NULL)
1915		return EINVAL;
1916
1917	idx = font->index;
1918	*font = *ctx.font;	/* struct copy */
1919	font->index = idx;
1920	font->cookie = font->data = NULL;	/* don't leak kernel pointers */
1921	return 0;
1922}
1923
1924void
1925rasops_scrollback(void *v, void *cookie, int lines)
1926{
1927	struct rasops_info *ri = v;
1928	struct rasops_screen *scr = cookie;
1929	int row, col, oldvoff;
1930
1931	oldvoff = scr->rs_visibleoffset;
1932
1933	if (lines == 0)
1934		scr->rs_visibleoffset = scr->rs_dispoffset;
1935	else {
1936		int off = scr->rs_visibleoffset + (lines * ri->ri_cols);
1937
1938		if (off < 0)
1939			off = 0;
1940		else if (off > scr->rs_dispoffset)
1941			off = scr->rs_dispoffset;
1942
1943		scr->rs_visibleoffset = off;
1944	}
1945
1946	if (scr->rs_visibleoffset == oldvoff)
1947		return;
1948
1949	rasops_cursor(ri, 0, 0, 0);
1950	ri->ri_eraserows(ri, 0, ri->ri_rows, scr->rs_defattr);
1951	for (row = 0; row < ri->ri_rows; row++) {
1952		for (col = 0; col < ri->ri_cols; col++) {
1953			int off = row * scr->rs_ri->ri_cols + col +
1954			    scr->rs_visibleoffset;
1955
1956			ri->ri_putchar(ri, row, col, scr->rs_bs[off].uc,
1957			    scr->rs_bs[off].attr);
1958		}
1959	}
1960
1961	if (scr->rs_crow != -1 && scr->rs_visibleoffset == scr->rs_dispoffset)
1962		rasops_cursor(ri, 1, scr->rs_crow, scr->rs_ccol);
1963}
1964
1965struct rasops_framebuffer {
1966	SLIST_ENTRY(rasops_framebuffer)	rf_list;
1967	paddr_t		rf_base;
1968	psize_t		rf_size;
1969	struct device	*rf_dev;
1970};
1971
1972SLIST_HEAD(, rasops_framebuffer) rasops_framebuffers =
1973    SLIST_HEAD_INITIALIZER(&rasops_framebuffers);
1974
1975void
1976rasops_claim_framebuffer(paddr_t base, psize_t size, struct device *dev)
1977{
1978	struct rasops_framebuffer *rf;
1979
1980	rf = malloc(sizeof(*rf), M_DEVBUF, M_WAITOK);
1981	rf->rf_base = base;
1982	rf->rf_size = size;
1983	rf->rf_dev = dev;
1984
1985	SLIST_INSERT_HEAD(&rasops_framebuffers, rf, rf_list);
1986}
1987
1988int
1989rasops_check_framebuffer(paddr_t base)
1990{
1991	struct rasops_framebuffer *rf;
1992
1993	SLIST_FOREACH(rf, &rasops_framebuffers, rf_list) {
1994		if (base >= rf->rf_base && base < rf->rf_base + rf->rf_size)
1995			return 1;
1996	}
1997
1998	return 0;
1999}
2000