xref: /freebsd/sys/dev/syscons/scvidctl.c (revision 2546665afcaf0d53dc2c7058fee96354b3680f5a)
1 /*-
2  * Copyright (c) 1998 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer as
10  *    the first lines of this file unmodified.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include "opt_syscons.h"
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/conf.h>
35 #include <sys/signalvar.h>
36 #include <sys/tty.h>
37 #include <sys/kernel.h>
38 #include <sys/fbio.h>
39 #include <sys/consio.h>
40 #include <sys/filedesc.h>
41 #include <sys/lock.h>
42 #include <sys/sx.h>
43 #include <sys/mutex.h>
44 #include <sys/proc.h>
45 
46 #include <dev/fb/fbreg.h>
47 #include <dev/syscons/syscons.h>
48 
49 SET_DECLARE(scrndr_set, const sc_renderer_t);
50 
51 /* for compatibility with previous versions */
52 /* 3.0-RELEASE used the following structure */
53 typedef struct old_video_adapter {
54     int			va_index;
55     int			va_type;
56     int			va_flags;
57 /* flag bits are the same as the -CURRENT
58 #define V_ADP_COLOR	(1<<0)
59 #define V_ADP_MODECHANGE (1<<1)
60 #define V_ADP_STATESAVE	(1<<2)
61 #define V_ADP_STATELOAD	(1<<3)
62 #define V_ADP_FONT	(1<<4)
63 #define V_ADP_PALETTE	(1<<5)
64 #define V_ADP_BORDER	(1<<6)
65 #define V_ADP_VESA	(1<<7)
66 */
67     int			va_crtc_addr;
68     u_int		va_window;	/* virtual address */
69     size_t		va_window_size;
70     size_t		va_window_gran;
71     u_int		va_buffer;	/* virtual address */
72     size_t		va_buffer_size;
73     int			va_initial_mode;
74     int			va_initial_bios_mode;
75     int			va_mode;
76 } old_video_adapter_t;
77 
78 #define OLD_CONS_ADPINFO _IOWR('c', 101, old_video_adapter_t)
79 
80 /* 3.1-RELEASE used the following structure */
81 typedef struct old_video_adapter_info {
82     int			va_index;
83     int			va_type;
84     char		va_name[16];
85     int			va_unit;
86     int			va_flags;
87     int			va_io_base;
88     int			va_io_size;
89     int			va_crtc_addr;
90     int			va_mem_base;
91     int			va_mem_size;
92     u_int		va_window;	/* virtual address */
93     size_t		va_window_size;
94     size_t		va_window_gran;
95     u_int		va_buffer;
96     size_t		va_buffer_size;
97     int			va_initial_mode;
98     int			va_initial_bios_mode;
99     int			va_mode;
100     int			va_line_width;
101 } old_video_adapter_info_t;
102 
103 #define OLD_CONS_ADPINFO2 _IOWR('c', 101, old_video_adapter_info_t)
104 
105 /* 3.0-RELEASE and 3.1-RELEASE used the following structure */
106 typedef struct old_video_info {
107     int			vi_mode;
108     int			vi_flags;
109 /* flag bits are the same as the -CURRENT
110 #define V_INFO_COLOR	(1<<0)
111 #define V_INFO_GRAPHICS	(1<<1)
112 #define V_INFO_LINEAR	(1<<2)
113 #define V_INFO_VESA	(1<<3)
114 */
115     int			vi_width;
116     int			vi_height;
117     int			vi_cwidth;
118     int			vi_cheight;
119     int			vi_depth;
120     int			vi_planes;
121     u_int		vi_window;	/* physical address */
122     size_t		vi_window_size;
123     size_t		vi_window_gran;
124     u_int		vi_buffer;	/* physical address */
125     size_t		vi_buffer_size;
126 } old_video_info_t;
127 
128 #define OLD_CONS_MODEINFO _IOWR('c', 102, old_video_info_t)
129 #define OLD_CONS_FINDMODE _IOWR('c', 103, old_video_info_t)
130 
131 int
132 sc_set_text_mode(scr_stat *scp, struct tty *tp, int mode, int xsize, int ysize,
133 		 int fontsize)
134 {
135     video_info_t info;
136     u_char *font;
137     int prev_ysize;
138     int error;
139     int s;
140 
141     if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, mode, &info))
142 	return ENODEV;
143 
144     /* adjust argument values */
145     if (fontsize <= 0)
146 	fontsize = info.vi_cheight;
147     if (fontsize < 14) {
148 	fontsize = 8;
149 #ifndef SC_NO_FONT_LOADING
150 	if (!(scp->sc->fonts_loaded & FONT_8))
151 	    return EINVAL;
152 	font = scp->sc->font_8;
153 #else
154 	font = NULL;
155 #endif
156     } else if (fontsize >= 16) {
157 	fontsize = 16;
158 #ifndef SC_NO_FONT_LOADING
159 	if (!(scp->sc->fonts_loaded & FONT_16))
160 	    return EINVAL;
161 	font = scp->sc->font_16;
162 #else
163 	font = NULL;
164 #endif
165     } else {
166 	fontsize = 14;
167 #ifndef SC_NO_FONT_LOADING
168 	if (!(scp->sc->fonts_loaded & FONT_14))
169 	    return EINVAL;
170 	font = scp->sc->font_14;
171 #else
172 	font = NULL;
173 #endif
174     }
175     if ((xsize <= 0) || (xsize > info.vi_width))
176 	xsize = info.vi_width;
177     if ((ysize <= 0) || (ysize > info.vi_height))
178 	ysize = info.vi_height;
179 
180     /* stop screen saver, etc */
181     s = spltty();
182     if ((error = sc_clean_up(scp))) {
183 	splx(s);
184 	return error;
185     }
186 
187     if (sc_render_match(scp, scp->sc->adp->va_name, 0) == NULL) {
188 	splx(s);
189 	return ENODEV;
190     }
191 
192     /* set up scp */
193 #ifndef SC_NO_HISTORY
194     if (scp->history != NULL)
195 	sc_hist_save(scp);
196 #endif
197     prev_ysize = scp->ysize;
198     /*
199      * This is a kludge to fend off scrn_update() while we
200      * muck around with scp. XXX
201      */
202     scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN;
203     scp->status &= ~(GRAPHICS_MODE | PIXEL_MODE | MOUSE_VISIBLE);
204     scp->mode = mode;
205     scp->xsize = xsize;
206     scp->ysize = ysize;
207     scp->xoff = 0;
208     scp->yoff = 0;
209     scp->xpixel = scp->xsize*8;
210     scp->ypixel = scp->ysize*fontsize;
211     scp->font = font;
212     scp->font_size = fontsize;
213 
214     /* allocate buffers */
215     sc_alloc_scr_buffer(scp, TRUE, TRUE);
216     sc_init_emulator(scp, NULL);
217 #ifndef SC_NO_CUTPASTE
218     sc_alloc_cut_buffer(scp, FALSE);
219 #endif
220 #ifndef SC_NO_HISTORY
221     sc_alloc_history_buffer(scp, 0, prev_ysize, FALSE);
222 #endif
223     splx(s);
224 
225     if (scp == scp->sc->cur_scp)
226 	set_mode(scp);
227     scp->status &= ~UNKNOWN_MODE;
228 
229     if (tp == NULL)
230 	return 0;
231     DPRINTF(5, ("ws_*size (%d,%d), size (%d,%d)\n",
232 	tp->t_winsize.ws_col, tp->t_winsize.ws_row, scp->xsize, scp->ysize));
233     if (tp->t_winsize.ws_col != scp->xsize
234 	|| tp->t_winsize.ws_row != scp->ysize) {
235 	tp->t_winsize.ws_col = scp->xsize;
236 	tp->t_winsize.ws_row = scp->ysize;
237 	if (tp->t_pgrp != NULL) {
238 	    PGRP_LOCK(tp->t_pgrp);
239 	    pgsignal(tp->t_pgrp, SIGWINCH, 1);
240 	    PGRP_UNLOCK(tp->t_pgrp);
241 	}
242     }
243 
244     return 0;
245 }
246 
247 int
248 sc_set_graphics_mode(scr_stat *scp, struct tty *tp, int mode)
249 {
250 #ifdef SC_NO_MODE_CHANGE
251     return ENODEV;
252 #else
253     video_info_t info;
254     int error;
255     int s;
256 
257     if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, mode, &info))
258 	return ENODEV;
259 
260     /* stop screen saver, etc */
261     s = spltty();
262     if ((error = sc_clean_up(scp))) {
263 	splx(s);
264 	return error;
265     }
266 
267     if (sc_render_match(scp, scp->sc->adp->va_name, GRAPHICS_MODE) == NULL) {
268 	splx(s);
269 	return ENODEV;
270     }
271 
272     /* set up scp */
273     scp->status |= (UNKNOWN_MODE | GRAPHICS_MODE | MOUSE_HIDDEN);
274     scp->status &= ~(PIXEL_MODE | MOUSE_VISIBLE);
275     scp->mode = mode;
276     /*
277      * Don't change xsize and ysize; preserve the previous vty
278      * and history buffers.
279      */
280     scp->xoff = 0;
281     scp->yoff = 0;
282     scp->xpixel = info.vi_width;
283     scp->ypixel = info.vi_height;
284     scp->font = NULL;
285     scp->font_size = 0;
286 #ifndef SC_NO_SYSMOUSE
287     /* move the mouse cursor at the center of the screen */
288     sc_mouse_move(scp, scp->xpixel / 2, scp->ypixel / 2);
289 #endif
290     sc_init_emulator(scp, NULL);
291     splx(s);
292 
293     if (scp == scp->sc->cur_scp)
294 	set_mode(scp);
295     /* clear_graphics();*/
296     scp->status &= ~UNKNOWN_MODE;
297 
298     if (tp == NULL)
299 	return 0;
300     if (tp->t_winsize.ws_xpixel != scp->xpixel
301 	|| tp->t_winsize.ws_ypixel != scp->ypixel) {
302 	tp->t_winsize.ws_xpixel = scp->xpixel;
303 	tp->t_winsize.ws_ypixel = scp->ypixel;
304 	if (tp->t_pgrp != NULL) {
305 	    PGRP_LOCK(tp->t_pgrp);
306 	    pgsignal(tp->t_pgrp, SIGWINCH, 1);
307 	    PGRP_UNLOCK(tp->t_pgrp);
308 	}
309     }
310 
311     return 0;
312 #endif /* SC_NO_MODE_CHANGE */
313 }
314 
315 int
316 sc_set_pixel_mode(scr_stat *scp, struct tty *tp, int xsize, int ysize,
317 		  int fontsize)
318 {
319 #ifndef SC_PIXEL_MODE
320     return ENODEV;
321 #else
322     video_info_t info;
323     u_char *font;
324     int prev_ysize;
325     int error;
326     int s;
327 
328     if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, scp->mode, &info))
329 	return ENODEV;		/* this shouldn't happen */
330 
331     /* adjust argument values */
332     if (fontsize <= 0)
333 	fontsize = info.vi_cheight;
334     if (fontsize < 14) {
335 	fontsize = 8;
336 #ifndef SC_NO_FONT_LOADING
337 	if (!(scp->sc->fonts_loaded & FONT_8))
338 	    return EINVAL;
339 	font = scp->sc->font_8;
340 #else
341 	font = NULL;
342 #endif
343     } else if (fontsize >= 16) {
344 	fontsize = 16;
345 #ifndef SC_NO_FONT_LOADING
346 	if (!(scp->sc->fonts_loaded & FONT_16))
347 	    return EINVAL;
348 	font = scp->sc->font_16;
349 #else
350 	font = NULL;
351 #endif
352     } else {
353 	fontsize = 14;
354 #ifndef SC_NO_FONT_LOADING
355 	if (!(scp->sc->fonts_loaded & FONT_14))
356 	    return EINVAL;
357 	font = scp->sc->font_14;
358 #else
359 	font = NULL;
360 #endif
361     }
362     if (xsize <= 0)
363 	xsize = info.vi_width/8;
364     if (ysize <= 0)
365 	ysize = info.vi_height/fontsize;
366 
367     if ((info.vi_width < xsize*8) || (info.vi_height < ysize*fontsize))
368 	return EINVAL;
369 
370     /* only 16 color, 4 plane modes are supported XXX */
371     if ((info.vi_depth != 4) || (info.vi_planes != 4))
372 	return ENODEV;
373 
374     /*
375      * set_pixel_mode() currently does not support video modes whose
376      * memory size is larger than 64K. Because such modes require
377      * bank switching to access the entire screen. XXX
378      */
379     if (info.vi_width*info.vi_height/8 > info.vi_window_size)
380 	return ENODEV;
381 
382     /* stop screen saver, etc */
383     s = spltty();
384     if ((error = sc_clean_up(scp))) {
385 	splx(s);
386 	return error;
387     }
388 
389     if (sc_render_match(scp, scp->sc->adp->va_name, PIXEL_MODE) == NULL) {
390 	splx(s);
391 	return ENODEV;
392     }
393 
394 #if 0
395     if (scp->tsw)
396 	(*scp->tsw->te_term)(scp, scp->ts);
397     scp->tsw = NULL;
398     scp->ts = NULL;
399 #endif
400 
401     /* set up scp */
402 #ifndef SC_NO_HISTORY
403     if (scp->history != NULL)
404 	sc_hist_save(scp);
405 #endif
406     prev_ysize = scp->ysize;
407     scp->status |= (UNKNOWN_MODE | PIXEL_MODE | MOUSE_HIDDEN);
408     scp->status &= ~(GRAPHICS_MODE | MOUSE_VISIBLE);
409     scp->xsize = xsize;
410     scp->ysize = ysize;
411     scp->xoff = (scp->xpixel/8 - xsize)/2;
412     scp->yoff = (scp->ypixel/fontsize - ysize)/2;
413     scp->font = font;
414     scp->font_size = fontsize;
415 
416     /* allocate buffers */
417     sc_alloc_scr_buffer(scp, TRUE, TRUE);
418     sc_init_emulator(scp, NULL);
419 #ifndef SC_NO_CUTPASTE
420     sc_alloc_cut_buffer(scp, FALSE);
421 #endif
422 #ifndef SC_NO_HISTORY
423     sc_alloc_history_buffer(scp, 0, prev_ysize, FALSE);
424 #endif
425     splx(s);
426 
427     if (scp == scp->sc->cur_scp) {
428 	sc_set_border(scp, scp->border);
429 	sc_set_cursor_image(scp);
430     }
431 
432     scp->status &= ~UNKNOWN_MODE;
433 
434     if (tp == NULL)
435 	return 0;
436     if (tp->t_winsize.ws_col != scp->xsize
437 	|| tp->t_winsize.ws_row != scp->ysize) {
438 	tp->t_winsize.ws_col = scp->xsize;
439 	tp->t_winsize.ws_row = scp->ysize;
440 	if (tp->t_pgrp != NULL) {
441 	    PGRP_LOCK(tp->t_pgrp);
442 	    pgsignal(tp->t_pgrp, SIGWINCH, 1);
443 	    PGRP_UNLOCK(tp->t_pgrp);
444 	}
445     }
446 
447     return 0;
448 #endif /* SC_PIXEL_MODE */
449 }
450 
451 #define fb_ioctl(a, c, d)		\
452 	(((a) == NULL) ? ENODEV : 	\
453 			 (*vidsw[(a)->va_index]->ioctl)((a), (c), (caddr_t)(d)))
454 
455 int
456 sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread *td)
457 {
458     scr_stat *scp;
459     video_adapter_t *adp;
460     video_info_t info;
461     video_adapter_info_t adp_info;
462     int error;
463     int s;
464 
465     scp = SC_STAT(tp->t_dev);
466     if (scp == NULL)		/* tp == SC_MOUSE */
467 	return ENOIOCTL;
468     adp = scp->sc->adp;
469     if (adp == NULL)		/* shouldn't happen??? */
470 	return ENODEV;
471 
472     switch (cmd) {
473 
474     case CONS_CURRENTADP:	/* get current adapter index */
475     case FBIO_ADAPTER:
476 	return fb_ioctl(adp, FBIO_ADAPTER, data);
477 
478     case CONS_CURRENT:  	/* get current adapter type */
479     case FBIO_ADPTYPE:
480 	return fb_ioctl(adp, FBIO_ADPTYPE, data);
481 
482     case OLD_CONS_ADPINFO:	/* adapter information (old interface) */
483 	if (((old_video_adapter_t *)data)->va_index >= 0) {
484 	    adp = vid_get_adapter(((old_video_adapter_t *)data)->va_index);
485 	    if (adp == NULL)
486 		return ENODEV;
487 	}
488 	((old_video_adapter_t *)data)->va_index = adp->va_index;
489 	((old_video_adapter_t *)data)->va_type = adp->va_type;
490 	((old_video_adapter_t *)data)->va_flags = adp->va_flags;
491 	((old_video_adapter_t *)data)->va_crtc_addr = adp->va_crtc_addr;
492 	((old_video_adapter_t *)data)->va_window = adp->va_window;
493 	((old_video_adapter_t *)data)->va_window_size = adp->va_window_size;
494 	((old_video_adapter_t *)data)->va_window_gran = adp->va_window_gran;
495 	((old_video_adapter_t *)data)->va_buffer = adp->va_buffer;
496 	((old_video_adapter_t *)data)->va_buffer_size = adp->va_buffer_size;
497 	((old_video_adapter_t *)data)->va_mode = adp->va_mode;
498 	((old_video_adapter_t *)data)->va_initial_mode = adp->va_initial_mode;
499 	((old_video_adapter_t *)data)->va_initial_bios_mode
500 	    = adp->va_initial_bios_mode;
501 	return 0;
502 
503     case OLD_CONS_ADPINFO2:	/* adapter information (yet another old I/F) */
504 	adp_info.va_index = ((old_video_adapter_info_t *)data)->va_index;
505 	if (adp_info.va_index >= 0) {
506 	    adp = vid_get_adapter(adp_info.va_index);
507 	    if (adp == NULL)
508 		return ENODEV;
509 	}
510 	error = fb_ioctl(adp, FBIO_ADPINFO, &adp_info);
511 	if (error == 0)
512 	    bcopy(&adp_info, data, sizeof(old_video_adapter_info_t));
513 	return error;
514 
515     case CONS_ADPINFO:		/* adapter information */
516     case FBIO_ADPINFO:
517 	if (((video_adapter_info_t *)data)->va_index >= 0) {
518 	    adp = vid_get_adapter(((video_adapter_info_t *)data)->va_index);
519 	    if (adp == NULL)
520 		return ENODEV;
521 	}
522 	return fb_ioctl(adp, FBIO_ADPINFO, data);
523 
524     case CONS_GET:      	/* get current video mode */
525     case FBIO_GETMODE:
526 	*(int *)data = scp->mode;
527 	return 0;
528 
529 #ifndef SC_NO_MODE_CHANGE
530     case FBIO_SETMODE:		/* set video mode */
531 	if (!(adp->va_flags & V_ADP_MODECHANGE))
532  	    return ENODEV;
533 	info.vi_mode = *(int *)data;
534 	error = fb_ioctl(adp, FBIO_MODEINFO, &info);
535 	if (error)
536 	    return error;
537 	if (info.vi_flags & V_INFO_GRAPHICS)
538 	    return sc_set_graphics_mode(scp, tp, *(int *)data);
539 	else
540 	    return sc_set_text_mode(scp, tp, *(int *)data, 0, 0, 0);
541 #endif /* SC_NO_MODE_CHANGE */
542 
543     case OLD_CONS_MODEINFO:	/* get mode information (old infterface) */
544 	info.vi_mode = ((old_video_info_t *)data)->vi_mode;
545 	error = fb_ioctl(adp, FBIO_MODEINFO, &info);
546 	if (error == 0)
547 	    bcopy(&info, (old_video_info_t *)data, sizeof(old_video_info_t));
548 	return error;
549 
550     case CONS_MODEINFO:		/* get mode information */
551     case FBIO_MODEINFO:
552 	return fb_ioctl(adp, FBIO_MODEINFO, data);
553 
554     case OLD_CONS_FINDMODE:	/* find a matching video mode (old interface) */
555 	bzero(&info, sizeof(info));
556 	bcopy((old_video_info_t *)data, &info, sizeof(old_video_info_t));
557 	error = fb_ioctl(adp, FBIO_FINDMODE, &info);
558 	if (error == 0)
559 	    bcopy(&info, (old_video_info_t *)data, sizeof(old_video_info_t));
560 	return error;
561 
562     case CONS_FINDMODE:		/* find a matching video mode */
563     case FBIO_FINDMODE:
564 	return fb_ioctl(adp, FBIO_FINDMODE, data);
565 
566     case CONS_SETWINORG:	/* set frame buffer window origin */
567     case FBIO_SETWINORG:
568 	if (scp != scp->sc->cur_scp)
569 	    return ENODEV;	/* XXX */
570 	return fb_ioctl(adp, FBIO_SETWINORG, data);
571 
572     case FBIO_GETWINORG:	/* get frame buffer window origin */
573 	if (scp != scp->sc->cur_scp)
574 	    return ENODEV;	/* XXX */
575 	return fb_ioctl(adp, FBIO_GETWINORG, data);
576 
577     case FBIO_GETDISPSTART:
578     case FBIO_SETDISPSTART:
579     case FBIO_GETLINEWIDTH:
580     case FBIO_SETLINEWIDTH:
581 	if (scp != scp->sc->cur_scp)
582 	    return ENODEV;	/* XXX */
583 	return fb_ioctl(adp, cmd, data);
584 
585     case FBIO_GETPALETTE:
586     case FBIO_SETPALETTE:
587     case FBIOPUTCMAP:
588     case FBIOGETCMAP:
589     case FBIOGTYPE:
590     case FBIOGATTR:
591     case FBIOSVIDEO:
592     case FBIOGVIDEO:
593     case FBIOSCURSOR:
594     case FBIOGCURSOR:
595     case FBIOSCURPOS:
596     case FBIOGCURPOS:
597     case FBIOGCURMAX:
598 	if (scp != scp->sc->cur_scp)
599 	    return ENODEV;	/* XXX */
600 	return fb_ioctl(adp, cmd, data);
601 
602     case FBIO_BLANK:
603 	if (scp != scp->sc->cur_scp)
604 	    return ENODEV;	/* XXX */
605 	return fb_ioctl(adp, cmd, data);
606 
607 #ifndef SC_NO_MODE_CHANGE
608     /* generic text modes */
609     case SW_TEXT_80x25:	case SW_TEXT_80x30:
610     case SW_TEXT_80x43: case SW_TEXT_80x50:
611     case SW_TEXT_80x60:
612 	/* FALLTHROUGH */
613 
614     /* VGA TEXT MODES */
615     case SW_VGA_C40x25:
616     case SW_VGA_C80x25: case SW_VGA_M80x25:
617     case SW_VGA_C80x30: case SW_VGA_M80x30:
618     case SW_VGA_C80x50: case SW_VGA_M80x50:
619     case SW_VGA_C80x60: case SW_VGA_M80x60:
620     case SW_VGA_C90x25: case SW_VGA_M90x25:
621     case SW_VGA_C90x30: case SW_VGA_M90x30:
622     case SW_VGA_C90x43: case SW_VGA_M90x43:
623     case SW_VGA_C90x50: case SW_VGA_M90x50:
624     case SW_VGA_C90x60: case SW_VGA_M90x60:
625     case SW_B40x25:     case SW_C40x25:
626     case SW_B80x25:     case SW_C80x25:
627     case SW_ENH_B40x25: case SW_ENH_C40x25:
628     case SW_ENH_B80x25: case SW_ENH_C80x25:
629     case SW_ENH_B80x43: case SW_ENH_C80x43:
630     case SW_EGAMONO80x25:
631 
632 #ifdef PC98
633     /* PC98 TEXT MODES */
634     case SW_PC98_80x25:
635     case SW_PC98_80x30:
636 #endif
637 	if (!(adp->va_flags & V_ADP_MODECHANGE))
638  	    return ENODEV;
639 	return sc_set_text_mode(scp, tp, cmd & 0xff, 0, 0, 0);
640 
641     /* GRAPHICS MODES */
642     case SW_BG320:     case SW_BG640:
643     case SW_CG320:     case SW_CG320_D:   case SW_CG640_E:
644     case SW_CG640x350: case SW_ENH_CG640:
645     case SW_BG640x480: case SW_CG640x480: case SW_VGA_CG320:
646     case SW_VGA_MODEX:
647 #ifdef PC98
648     /* PC98 GRAPHICS MODES */
649     case SW_PC98_EGC640x400:	case SW_PC98_PEGC640x400:
650     case SW_PC98_PEGC640x480:
651 #endif
652 	if (!(adp->va_flags & V_ADP_MODECHANGE))
653 	    return ENODEV;
654 	return sc_set_graphics_mode(scp, tp, cmd & 0xff);
655 #endif /* SC_NO_MODE_CHANGE */
656 
657     case KDSETMODE:     	/* set current mode of this (virtual) console */
658 	switch (*(intptr_t *)data) {
659 	case KD_TEXT:   	/* switch to TEXT (known) mode */
660 	    /*
661 	     * If scp->mode is of graphics modes, we don't know which
662 	     * text mode to switch back to...
663 	     */
664 	    if (scp->status & GRAPHICS_MODE)
665 		return EINVAL;
666 	    /* restore fonts & palette ! */
667 #if 0
668 #ifndef SC_NO_FONT_LOADING
669 	    if (ISFONTAVAIL(adp->va_flags)
670 		&& !(scp->status & (GRAPHICS_MODE | PIXEL_MODE)))
671 		/*
672 		 * FONT KLUDGE
673 		 * Don't load fonts for now... XXX
674 		 */
675 		if (scp->sc->fonts_loaded & FONT_8)
676 		    sc_load_font(scp, 0, 8, scp->sc->font_8, 0, 256);
677 		if (scp->sc->fonts_loaded & FONT_14)
678 		    sc_load_font(scp, 0, 14, scp->sc->font_14, 0, 256);
679 		if (scp->sc->fonts_loaded & FONT_16)
680 		    sc_load_font(scp, 0, 16, scp->sc->font_16, 0, 256);
681 	    }
682 #endif /* SC_NO_FONT_LOADING */
683 #endif
684 
685 #ifndef SC_NO_PALETTE_LOADING
686 	    load_palette(adp, scp->sc->palette);
687 #endif
688 
689 #ifndef PC98
690 	    /* move hardware cursor out of the way */
691 	    (*vidsw[adp->va_index]->set_hw_cursor)(adp, -1, -1);
692 #endif
693 
694 	    /* FALLTHROUGH */
695 
696 	case KD_TEXT1:  	/* switch to TEXT (known) mode */
697 	    /*
698 	     * If scp->mode is of graphics modes, we don't know which
699 	     * text/pixel mode to switch back to...
700 	     */
701 	    if (scp->status & GRAPHICS_MODE)
702 		return EINVAL;
703 	    s = spltty();
704 	    if ((error = sc_clean_up(scp))) {
705 		splx(s);
706 		return error;
707 	    }
708 #ifndef PC98
709 	    scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN;
710 	    splx(s);
711 	    /* no restore fonts & palette */
712 	    if (scp == scp->sc->cur_scp)
713 		set_mode(scp);
714 	    sc_clear_screen(scp);
715 	    scp->status &= ~UNKNOWN_MODE;
716 #else /* PC98 */
717 	    scp->status &= ~UNKNOWN_MODE;
718 	    /* no restore fonts & palette */
719 	    if (scp == scp->sc->cur_scp)
720 		set_mode(scp);
721 	    sc_clear_screen(scp);
722 	    splx(s);
723 #endif /* PC98 */
724 	    return 0;
725 
726 #ifdef SC_PIXEL_MODE
727 	case KD_PIXEL:		/* pixel (raster) display */
728 	    if (!(scp->status & (GRAPHICS_MODE | PIXEL_MODE)))
729 		return EINVAL;
730 	    if (scp->status & GRAPHICS_MODE)
731 		return sc_set_pixel_mode(scp, tp, scp->xsize, scp->ysize,
732 					 scp->font_size);
733 	    s = spltty();
734 	    if ((error = sc_clean_up(scp))) {
735 		splx(s);
736 		return error;
737 	    }
738 	    scp->status |= (UNKNOWN_MODE | PIXEL_MODE | MOUSE_HIDDEN);
739 	    splx(s);
740 	    if (scp == scp->sc->cur_scp) {
741 		set_mode(scp);
742 #ifndef SC_NO_PALETTE_LOADING
743 		load_palette(adp, scp->sc->palette);
744 #endif
745 	    }
746 	    sc_clear_screen(scp);
747 	    scp->status &= ~UNKNOWN_MODE;
748 	    return 0;
749 #endif /* SC_PIXEL_MODE */
750 
751 	case KD_GRAPHICS:	/* switch to GRAPHICS (unknown) mode */
752 	    s = spltty();
753 	    if ((error = sc_clean_up(scp))) {
754 		splx(s);
755 		return error;
756 	    }
757 	    scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN;
758 	    splx(s);
759 #ifdef PC98
760 	    if (scp == scp->sc->cur_scp)
761 		set_mode(scp);
762 #endif
763 	    return 0;
764 
765 	default:
766 	    return EINVAL;
767 	}
768 	/* NOT REACHED */
769 
770 #ifdef SC_PIXEL_MODE
771     case KDRASTER:		/* set pixel (raster) display mode */
772 	if (ISUNKNOWNSC(scp) || ISTEXTSC(scp))
773 	    return ENODEV;
774 	return sc_set_pixel_mode(scp, tp, ((int *)data)[0], ((int *)data)[1],
775 				 ((int *)data)[2]);
776 #endif /* SC_PIXEL_MODE */
777 
778     case KDGETMODE:     	/* get current mode of this (virtual) console */
779 	/*
780 	 * From the user program's point of view, KD_PIXEL is the same
781 	 * as KD_TEXT...
782 	 */
783 	*data = ISGRAPHSC(scp) ? KD_GRAPHICS : KD_TEXT;
784 	return 0;
785 
786     case KDSBORDER:     	/* set border color of this (virtual) console */
787 	scp->border = *(intptr_t *)data;
788 	if (scp == scp->sc->cur_scp)
789 	    sc_set_border(scp, scp->border);
790 	return 0;
791     }
792 
793     return ENOIOCTL;
794 }
795 
796 static LIST_HEAD(, sc_renderer) sc_rndr_list =
797 	LIST_HEAD_INITIALIZER(sc_rndr_list);
798 
799 int
800 sc_render_add(sc_renderer_t *rndr)
801 {
802 	LIST_INSERT_HEAD(&sc_rndr_list, rndr, link);
803 	return 0;
804 }
805 
806 int
807 sc_render_remove(sc_renderer_t *rndr)
808 {
809 	/*
810 	LIST_REMOVE(rndr, link);
811 	*/
812 	return EBUSY;	/* XXX */
813 }
814 
815 sc_rndr_sw_t
816 *sc_render_match(scr_stat *scp, char *name, int mode)
817 {
818 	const sc_renderer_t **list;
819 	const sc_renderer_t *p;
820 
821 	if (!LIST_EMPTY(&sc_rndr_list)) {
822 		LIST_FOREACH(p, &sc_rndr_list, link) {
823 			if ((strcmp(p->name, name) == 0)
824 				&& (mode == p->mode)) {
825 				scp->status &=
826 				    ~(VR_CURSOR_ON | VR_CURSOR_BLINK);
827 				return p->rndrsw;
828 			}
829 		}
830 	} else {
831 		SET_FOREACH(list, scrndr_set) {
832 			p = *list;
833 			if ((strcmp(p->name, name) == 0)
834 				&& (mode == p->mode)) {
835 				scp->status &=
836 				    ~(VR_CURSOR_ON | VR_CURSOR_BLINK);
837 				return p->rndrsw;
838 			}
839 		}
840 	}
841 
842 	return NULL;
843 }
844