scvidctl.c revision 42504
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 * $Id: $
27 */
28
29#include "sc.h"
30#include "opt_syscons.h"
31
32#if NSC > 0
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/signalvar.h>
37#include <sys/tty.h>
38#include <sys/kernel.h>
39
40#include <machine/apm_bios.h>
41#include <machine/console.h>
42
43#include <dev/fb/fbreg.h>
44#include <dev/syscons/syscons.h>
45
46/* for compatibility with previous versions */
47typedef struct old_video_adapter {
48    int			va_index;
49    int			va_type;
50    int			va_flags;
51#define V_ADP_COLOR	(1<<0)
52#define V_ADP_MODECHANGE (1<<1)
53#define V_ADP_STATESAVE	(1<<2)
54#define V_ADP_STATELOAD	(1<<3)
55#define V_ADP_FONT	(1<<4)
56#define V_ADP_PALETTE	(1<<5)
57#define V_ADP_BORDER	(1<<6)
58#define V_ADP_VESA	(1<<7)
59    int			va_crtc_addr;
60    u_int		va_window;	/* virtual address */
61    size_t		va_window_size;
62    size_t		va_window_gran;
63    u_int		va_buffer;	/* virtual address */
64    size_t		va_buffer_size;
65    int			va_initial_mode;
66    int			va_initial_bios_mode;
67    int			va_mode;
68} old_video_adapter_t;
69
70#define OLD_CONS_ADPINFO _IOWR('c', 101, old_video_adapter_t)
71
72/* variables */
73extern scr_stat *cur_console;
74extern int fonts_loaded;
75extern int sc_history_size;
76extern u_char palette[];
77
78int
79sc_set_text_mode(scr_stat *scp, struct tty *tp, int mode, int xsize, int ysize,
80		 int fontsize)
81{
82    video_info_t info;
83    int error;
84    int s;
85    int i;
86
87    if ((*vidsw[scp->ad]->get_info)(scp->adp, mode, &info))
88	return ENODEV;
89
90    /* adjust argument values */
91    if (fontsize <= 0)
92	fontsize = info.vi_cheight;
93    if (fontsize < 14) {
94	fontsize = 8;
95	if (!(fonts_loaded & FONT_8))
96	    return EINVAL;
97    } else if (fontsize >= 16) {
98	fontsize = 16;
99	if (!(fonts_loaded & FONT_16))
100	    return EINVAL;
101    } else {
102	fontsize = 14;
103	if (!(fonts_loaded & FONT_14))
104	    return EINVAL;
105    }
106    if ((xsize <= 0) || (xsize > info.vi_width))
107	xsize = info.vi_width;
108    if ((ysize <= 0) || (ysize > info.vi_height))
109	ysize = info.vi_height;
110
111    /* stop screen saver, etc */
112    s = spltty();
113    if ((error = sc_clean_up(scp))) {
114	splx(s);
115	return error;
116    }
117
118    /* set up scp */
119    if (scp->history != NULL)
120	i = imax(scp->history_size / scp->xsize
121		 - imax(sc_history_size, scp->ysize), 0);
122    else
123	i = 0;
124    /*
125     * This is a kludge to fend off scrn_update() while we
126     * muck around with scp. XXX
127     */
128    scp->status |= UNKNOWN_MODE;
129    scp->status &= ~(GRAPHICS_MODE | PIXEL_MODE);
130    scp->mode = mode;
131    scp->font_size = fontsize;
132    scp->xsize = xsize;
133    scp->ysize = ysize;
134    scp->xoff = 0;
135    scp->yoff = 0;
136    scp->xpixel = scp->xsize*8;
137    scp->ypixel = scp->ysize*fontsize;
138
139    /* allocate buffers */
140    sc_alloc_scr_buffer(scp, TRUE, TRUE);
141    if (ISMOUSEAVAIL(scp->adp->va_flags))
142	sc_alloc_cut_buffer(scp, FALSE);
143    sc_alloc_history_buffer(scp, sc_history_size, i, FALSE);
144    splx(s);
145
146    if (scp == cur_console)
147	set_mode(scp);
148    scp->status &= ~UNKNOWN_MODE;
149
150    if (tp == NULL)
151	return 0;
152    if (tp->t_winsize.ws_col != scp->xsize
153	|| tp->t_winsize.ws_row != scp->ysize) {
154	tp->t_winsize.ws_col = scp->xsize;
155	tp->t_winsize.ws_row = scp->ysize;
156	pgsignal(tp->t_pgrp, SIGWINCH, 1);
157    }
158
159    return 0;
160}
161
162int
163sc_set_graphics_mode(scr_stat *scp, struct tty *tp, int mode)
164{
165    video_info_t info;
166    int error;
167    int s;
168
169    if ((*vidsw[scp->ad]->get_info)(scp->adp, mode, &info))
170	return ENODEV;
171
172    /* stop screen saver, etc */
173    s = spltty();
174    if ((error = sc_clean_up(scp))) {
175	splx(s);
176	return error;
177    }
178
179    /* set up scp */
180    scp->status |= (UNKNOWN_MODE | GRAPHICS_MODE);
181    scp->status &= ~PIXEL_MODE;
182    scp->mode = mode;
183    scp->xoff = 0;
184    scp->yoff = 0;
185    scp->xpixel = info.vi_width;
186    scp->ypixel = info.vi_height;
187    scp->xsize = info.vi_width/8;
188    scp->ysize = info.vi_height/info.vi_cheight;
189    scp->font_size = FONT_NONE;
190    /* move the mouse cursor at the center of the screen */
191    sc_move_mouse(scp, scp->xpixel / 2, scp->ypixel / 2);
192    splx(s);
193
194    if (scp == cur_console)
195	set_mode(scp);
196    /* clear_graphics();*/
197    scp->status &= ~UNKNOWN_MODE;
198
199    if (tp == NULL)
200	return 0;
201    if (tp->t_winsize.ws_xpixel != scp->xpixel
202	|| tp->t_winsize.ws_ypixel != scp->ypixel) {
203	tp->t_winsize.ws_xpixel = scp->xpixel;
204	tp->t_winsize.ws_ypixel = scp->ypixel;
205	pgsignal(tp->t_pgrp, SIGWINCH, 1);
206    }
207
208    return 0;
209}
210
211int
212sc_set_pixel_mode(scr_stat *scp, struct tty *tp, int xsize, int ysize,
213		  int fontsize)
214{
215    video_info_t info;
216    int error;
217    int s;
218    int i;
219
220    if ((*vidsw[scp->ad]->get_info)(scp->adp, scp->mode, &info))
221	return ENODEV;		/* this shouldn't happen */
222
223#ifdef SC_VIDEO_DEBUG
224    if (scp->scr_buf != NULL) {
225	printf("set_pixel_mode(): mode:%x, col:%d, row:%d, font:%d\n",
226	       scp->mode, xsize, ysize, fontsize);
227    }
228#endif
229
230    /* adjust argument values */
231    if ((fontsize <= 0) || (fontsize == FONT_NONE))
232	fontsize = info.vi_cheight;
233    if (fontsize < 14) {
234	fontsize = 8;
235	if (!(fonts_loaded & FONT_8))
236	    return EINVAL;
237    } else if (fontsize >= 16) {
238	fontsize = 16;
239	if (!(fonts_loaded & FONT_16))
240	    return EINVAL;
241    } else {
242	fontsize = 14;
243	if (!(fonts_loaded & FONT_14))
244	    return EINVAL;
245    }
246    if (xsize <= 0)
247	xsize = info.vi_width/8;
248    if (ysize <= 0)
249	ysize = info.vi_height/fontsize;
250
251#ifdef SC_VIDEO_DEBUG
252    if (scp->scr_buf != NULL) {
253	printf("set_pixel_mode(): mode:%x, col:%d, row:%d, font:%d\n",
254	       scp->mode, xsize, ysize, fontsize);
255	printf("set_pixel_mode(): window:%x, %dx%d, xoff:%d, yoff:%d\n",
256	       scp->adp->va_window, info.vi_width, info.vi_height,
257	       (info.vi_width/8 - xsize)/2,
258	       (info.vi_height/fontsize - ysize)/2);
259    }
260#endif
261
262    if ((info.vi_width < xsize*8) || (info.vi_height < ysize*fontsize))
263	return EINVAL;
264
265    /* only 16 color, 4 plane modes are supported XXX */
266    if ((info.vi_depth != 4) || (info.vi_planes != 4))
267	return ENODEV;
268
269    /*
270     * set_pixel_mode() currently does not support video modes whose
271     * memory size is larger than 64K. Because such modes require
272     * bank switching to access the entire screen. XXX
273     */
274    if (info.vi_width*info.vi_height/8 > info.vi_window_size)
275	return ENODEV;
276
277    /* stop screen saver, etc */
278    s = spltty();
279    if ((error = sc_clean_up(scp))) {
280	splx(s);
281	return error;
282    }
283
284    /* set up scp */
285    if (scp->history != NULL)
286	i = imax(scp->history_size / scp->xsize
287		 - imax(sc_history_size, scp->ysize), 0);
288    else
289	i = 0;
290    scp->status |= (UNKNOWN_MODE | PIXEL_MODE);
291    scp->status &= ~(GRAPHICS_MODE | MOUSE_ENABLED);
292    scp->xsize = xsize;
293    scp->ysize = ysize;
294    scp->font_size = fontsize;
295    scp->xoff = (scp->xpixel/8 - xsize)/2;
296    scp->yoff = (scp->ypixel/fontsize - ysize)/2;
297
298    /* allocate buffers */
299    sc_alloc_scr_buffer(scp, TRUE, TRUE);
300    if (ISMOUSEAVAIL(scp->adp->va_flags))
301	sc_alloc_cut_buffer(scp, FALSE);
302    sc_alloc_history_buffer(scp, sc_history_size, i, FALSE);
303    splx(s);
304
305    if (scp == cur_console)
306	set_border(scp, scp->border);
307
308    scp->status &= ~UNKNOWN_MODE;
309
310#ifdef SC_VIDEO_DEBUG
311    printf("set_pixel_mode(): status:%x\n", scp->status);
312#endif
313
314    if (tp == NULL)
315	return 0;
316    if (tp->t_winsize.ws_col != scp->xsize
317	|| tp->t_winsize.ws_row != scp->ysize) {
318	tp->t_winsize.ws_col = scp->xsize;
319	tp->t_winsize.ws_row = scp->ysize;
320	pgsignal(tp->t_pgrp, SIGWINCH, 1);
321    }
322
323    return 0;
324}
325
326int
327sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *p)
328{
329    scr_stat *scp;
330    int error;
331    int s;
332
333    scp = sc_get_scr_stat(tp->t_dev);
334
335    switch (cmd) {
336
337    case CONS_CURRENT:  	/* get current adapter type */
338	if (scp->adp == NULL)
339	    return ENODEV;
340	*(int *)data = scp->adp->va_type;
341	return 0;
342
343    case CONS_CURRENTADP:	/* get current adapter index */
344	*(int *)data = scp->ad;
345	return 0;
346
347    case OLD_CONS_ADPINFO:	/* adapter information */
348	if (scp->adp == NULL)
349	    return ENODEV;
350	((old_video_adapter_t *)data)->va_index = scp->adp->va_index;
351	((old_video_adapter_t *)data)->va_type = scp->adp->va_type;
352	((old_video_adapter_t *)data)->va_flags = scp->adp->va_flags;
353	((old_video_adapter_t *)data)->va_crtc_addr = scp->adp->va_crtc_addr;
354	((old_video_adapter_t *)data)->va_window = scp->adp->va_window;
355	((old_video_adapter_t *)data)->va_window_size
356	    = scp->adp->va_window_size;
357	((old_video_adapter_t *)data)->va_window_gran
358	    = scp->adp->va_window_gran;
359	((old_video_adapter_t *)data)->va_buffer = scp->adp->va_buffer;
360	((old_video_adapter_t *)data)->va_buffer_size
361	    = scp->adp->va_buffer_size;
362	((old_video_adapter_t *)data)->va_mode = scp->adp->va_mode;
363	((old_video_adapter_t *)data)->va_initial_mode
364	    = scp->adp->va_initial_mode;
365	((old_video_adapter_t *)data)->va_initial_bios_mode
366	    = scp->adp->va_initial_bios_mode;
367	return 0;
368
369    case CONS_ADPINFO:		/* adapter information */
370	if (scp->adp == NULL)
371	    return ENODEV;
372	((video_adapter_info_t *)data)->va_index = scp->adp->va_index;
373	((video_adapter_info_t *)data)->va_type = scp->adp->va_type;
374	bcopy(scp->adp->va_name, ((video_adapter_info_t *)data)->va_name,
375	      imin(strlen(scp->adp->va_name) + 1,
376		   sizeof(((video_adapter_info_t *)data)->va_name)));
377	((video_adapter_info_t *)data)->va_unit = scp->adp->va_unit;
378	((video_adapter_info_t *)data)->va_flags = scp->adp->va_flags;
379	((video_adapter_info_t *)data)->va_io_base = scp->adp->va_io_base;
380	((video_adapter_info_t *)data)->va_io_size = scp->adp->va_io_size;
381	((video_adapter_info_t *)data)->va_crtc_addr = scp->adp->va_crtc_addr;
382	((video_adapter_info_t *)data)->va_mem_base = scp->adp->va_mem_base;
383	((video_adapter_info_t *)data)->va_mem_size = scp->adp->va_mem_size;
384	((video_adapter_info_t *)data)->va_window = scp->adp->va_window;
385	((video_adapter_info_t *)data)->va_window_size
386	    = scp->adp->va_window_size;
387	((video_adapter_info_t *)data)->va_window_gran
388	    = scp->adp->va_window_gran;
389	((video_adapter_info_t *)data)->va_buffer = scp->adp->va_buffer;
390	((video_adapter_info_t *)data)->va_buffer_size
391	    = scp->adp->va_buffer_size;
392	((video_adapter_info_t *)data)->va_mode = scp->adp->va_mode;
393	((video_adapter_info_t *)data)->va_mode_flags = scp->adp->va_mode_flags;
394	((video_adapter_info_t *)data)->va_initial_mode
395	    = scp->adp->va_initial_mode;
396	((video_adapter_info_t *)data)->va_initial_bios_mode
397	    = scp->adp->va_initial_bios_mode;
398	return 0;
399
400    case CONS_GET:      	/* get current video mode */
401	*(int *)data = scp->mode;
402	return 0;
403
404    case CONS_MODEINFO:		/* get mode information */
405	return ((*vidsw[scp->ad]->get_info)(scp->adp,
406		    ((video_info_t *)data)->vi_mode, (video_info_t *)data)
407		? ENODEV : 0);
408
409    case CONS_FINDMODE:		/* find a matching video mode */
410	return ((*vidsw[scp->ad]->query_mode)(scp->adp, (video_info_t *)data)
411		? ENODEV : 0);
412
413    case CONS_SETWINORG:
414	return ((*vidsw[scp->ad]->set_win_org)(scp->adp, *(u_int *)data)
415		   ? ENODEV : 0);
416
417    /* generic text modes */
418    case SW_TEXT_80x25:	case SW_TEXT_80x30:
419    case SW_TEXT_80x43: case SW_TEXT_80x50:
420    case SW_TEXT_80x60:
421	/* FALL THROUGH */
422
423    /* VGA TEXT MODES */
424    case SW_VGA_C40x25:
425    case SW_VGA_C80x25: case SW_VGA_M80x25:
426    case SW_VGA_C80x30: case SW_VGA_M80x30:
427    case SW_VGA_C80x50: case SW_VGA_M80x50:
428    case SW_VGA_C80x60: case SW_VGA_M80x60:
429    case SW_B40x25:     case SW_C40x25:
430    case SW_B80x25:     case SW_C80x25:
431    case SW_ENH_B40x25: case SW_ENH_C40x25:
432    case SW_ENH_B80x25: case SW_ENH_C80x25:
433    case SW_ENH_B80x43: case SW_ENH_C80x43:
434    case SW_EGAMONO80x25:
435
436#ifdef PC98
437    /* PC98 TEXT MODES */
438    case SW_PC98_80x25:
439    case SW_PC98_80x30:
440#endif
441	if (!(scp->adp->va_flags & V_ADP_MODECHANGE))
442 	    return ENODEV;
443	return sc_set_text_mode(scp, tp, cmd & 0xff, 0, 0, 0);
444
445    /* GRAPHICS MODES */
446    case SW_BG320:     case SW_BG640:
447    case SW_CG320:     case SW_CG320_D:   case SW_CG640_E:
448    case SW_CG640x350: case SW_ENH_CG640:
449    case SW_BG640x480: case SW_CG640x480: case SW_VGA_CG320:
450    case SW_VGA_MODEX:
451	if (!(scp->adp->va_flags & V_ADP_MODECHANGE))
452	    return ENODEV;
453	return sc_set_graphics_mode(scp, tp, cmd & 0xff);
454
455    case KDSETMODE:     	/* set current mode of this (virtual) console */
456	switch (*(int *)data) {
457	case KD_TEXT:   	/* switch to TEXT (known) mode */
458	    /*
459	     * If scp->mode is of graphics modes, we don't know which
460	     * text mode to switch back to...
461	     */
462	    if (scp->status & GRAPHICS_MODE)
463		return EINVAL;
464	    /* restore fonts & palette ! */
465#if 0
466	    if (ISFONTAVAIL(scp->adp->va_flags)
467		&& !(scp->status & (GRAPHICS_MODE | PIXEL_MODE)))
468		/*
469		 * FONT KLUDGE
470		 * Don't load fonts for now... XXX
471		 */
472		if (fonts_loaded & FONT_8)
473		    copy_font(scp, LOAD, 8, font_8);
474		if (fonts_loaded & FONT_14)
475		    copy_font(scp, LOAD, 14, font_14);
476		if (fonts_loaded & FONT_16)
477		    copy_font(scp, LOAD, 16, font_16);
478	    }
479#endif
480	    load_palette(scp->adp, palette);
481
482	    /* move hardware cursor out of the way */
483	    (*vidsw[scp->ad]->set_hw_cursor)(scp->adp, -1, -1);
484
485	    /* FALL THROUGH */
486
487	case KD_TEXT1:  	/* switch to TEXT (known) mode */
488	    /*
489	     * If scp->mode is of graphics modes, we don't know which
490	     * text/pixel mode to switch back to...
491	     */
492	    if (scp->status & GRAPHICS_MODE)
493		return EINVAL;
494	    s = spltty();
495	    if ((error = sc_clean_up(scp))) {
496		splx(s);
497		return error;
498	    }
499#ifndef PC98
500	    scp->status |= UNKNOWN_MODE;
501	    splx(s);
502	    /* no restore fonts & palette */
503	    if (scp == cur_console)
504		set_mode(scp);
505	    sc_clear_screen(scp);
506	    scp->status &= ~UNKNOWN_MODE;
507#else /* PC98 */
508	    scp->status &= ~UNKNOWN_MODE;
509	    /* no restore fonts & palette */
510	    if (scp == cur_console)
511		set_mode(scp);
512	    sc_clear_screen(scp);
513	    splx(s);
514#endif /* PC98 */
515	    return 0;
516
517	case KD_PIXEL:		/* pixel (raster) display */
518	    if (!(scp->status & (GRAPHICS_MODE | PIXEL_MODE)))
519		return EINVAL;
520	    if (scp->status & GRAPHICS_MODE)
521		return sc_set_pixel_mode(scp, tp, scp->xsize, scp->ysize,
522					 scp->font_size);
523	    s = spltty();
524	    if ((error = sc_clean_up(scp))) {
525		splx(s);
526		return error;
527	    }
528	    scp->status |= (UNKNOWN_MODE | PIXEL_MODE);
529	    splx(s);
530	    if (scp == cur_console) {
531		set_mode(scp);
532		load_palette(scp->adp, palette);
533	    }
534	    sc_clear_screen(scp);
535	    scp->status &= ~UNKNOWN_MODE;
536	    return 0;
537
538	case KD_GRAPHICS:	/* switch to GRAPHICS (unknown) mode */
539	    s = spltty();
540	    if ((error = sc_clean_up(scp))) {
541		splx(s);
542		return error;
543	    }
544	    scp->status |= UNKNOWN_MODE;
545	    splx(s);
546#ifdef PC98
547	    if (scp == cur_console)
548		set_mode(scp);
549#endif
550	    return 0;
551
552	default:
553	    return EINVAL;
554	}
555	/* NOT REACHED */
556
557    case KDRASTER:		/* set pixel (raster) display mode */
558	if (ISUNKNOWNSC(scp) || ISTEXTSC(scp))
559	    return ENODEV;
560	return sc_set_pixel_mode(scp, tp, ((int *)data)[0], ((int *)data)[1],
561				 ((int *)data)[2]);
562
563    case KDGETMODE:     	/* get current mode of this (virtual) console */
564	/*
565	 * From the user program's point of view, KD_PIXEL is the same
566	 * as KD_TEXT...
567	 */
568	*data = ISGRAPHSC(scp) ? KD_GRAPHICS : KD_TEXT;
569	return 0;
570
571    case KDSBORDER:     	/* set border color of this (virtual) console */
572	scp->border = *data;
573	if (scp == cur_console)
574	    set_border(scp, scp->border);
575	return 0;
576    }
577
578    return ENOIOCTL;
579}
580
581#endif /* NSC > 0 */
582