1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1991-1997 S��ren Schmidt
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer
12 *    in this position and unchanged.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <sys/cdefs.h>
32#include <signal.h>
33#include <stdio.h>
34#include <sys/types.h>
35#include <sys/file.h>
36#include <sys/ioctl.h>
37#include <sys/mman.h>
38#include <sys/fbio.h>
39#include <sys/kbio.h>
40#include <sys/consio.h>
41#include "vgl.h"
42
43#define min(x, y)	(((x) < (y)) ? (x) : (y))
44#define max(x, y)	(((x) > (y)) ? (x) : (y))
45
46VGLBitmap *VGLDisplay;
47VGLBitmap VGLVDisplay;
48video_info_t VGLModeInfo;
49video_adapter_info_t VGLAdpInfo;
50byte *VGLBuf;
51
52static int VGLMode;
53static int VGLOldMode;
54static size_t VGLBufSize;
55static byte *VGLMem = MAP_FAILED;
56static int VGLSwitchPending;
57static int VGLAbortPending;
58static int VGLOnDisplay;
59static unsigned int VGLCurWindow;
60static int VGLInitDone = 0;
61static video_info_t VGLOldModeInfo;
62static vid_info_t VGLOldVInfo;
63static int VGLOldVXsize;
64
65void
66VGLEnd()
67{
68struct vt_mode smode;
69  int size[3];
70
71  if (!VGLInitDone)
72    return;
73  VGLInitDone = 0;
74  signal(SIGUSR1, SIG_IGN);
75  signal(SIGUSR2, SIG_IGN);
76  VGLSwitchPending = 0;
77  VGLAbortPending = 0;
78  VGLMouseMode(VGL_MOUSEHIDE);
79
80  if (VGLMem != MAP_FAILED) {
81    VGLClear(VGLDisplay, 0);
82    munmap(VGLMem, VGLAdpInfo.va_window_size);
83  }
84
85  ioctl(0, FBIO_SETLINEWIDTH, &VGLOldVXsize);
86
87  if (VGLOldMode >= M_VESA_BASE)
88    ioctl(0, _IO('V', VGLOldMode - M_VESA_BASE), 0);
89  else
90    ioctl(0, _IO('S', VGLOldMode), 0);
91  if (VGLOldModeInfo.vi_flags & V_INFO_GRAPHICS) {
92    size[0] = VGLOldVInfo.mv_csz;
93    size[1] = VGLOldVInfo.mv_rsz;
94    size[2] = VGLOldVInfo.font_size;
95    ioctl(0, KDRASTER, size);
96  }
97  if (VGLModeInfo.vi_mem_model != V_INFO_MM_DIRECT)
98    ioctl(0, KDDISABIO, 0);
99  ioctl(0, KDSETMODE, KD_TEXT);
100  smode.mode = VT_AUTO;
101  ioctl(0, VT_SETMODE, &smode);
102  if (VGLBuf)
103    free(VGLBuf);
104  VGLBuf = NULL;
105  free(VGLDisplay);
106  VGLDisplay = NULL;
107  VGLKeyboardEnd();
108}
109
110static void
111VGLAbort(int arg)
112{
113  sigset_t mask;
114
115  VGLAbortPending = 1;
116  signal(SIGINT, SIG_IGN);
117  signal(SIGTERM, SIG_IGN);
118  signal(SIGUSR2, SIG_IGN);
119  if (arg == SIGBUS || arg == SIGSEGV) {
120    signal(arg, SIG_DFL);
121    sigemptyset(&mask);
122    sigaddset(&mask, arg);
123    sigprocmask(SIG_UNBLOCK, &mask, NULL);
124    VGLEnd();
125    kill(getpid(), arg);
126  }
127}
128
129static void
130VGLSwitch(int arg __unused)
131{
132  if (!VGLOnDisplay)
133    VGLOnDisplay = 1;
134  else
135    VGLOnDisplay = 0;
136  VGLSwitchPending = 1;
137  signal(SIGUSR1, VGLSwitch);
138}
139
140int
141VGLInit(int mode)
142{
143  struct vt_mode smode;
144  int adptype, depth;
145
146  if (VGLInitDone)
147    return -1;
148
149  signal(SIGUSR1, VGLSwitch);
150  signal(SIGINT, VGLAbort);
151  signal(SIGTERM, VGLAbort);
152  signal(SIGSEGV, VGLAbort);
153  signal(SIGBUS, VGLAbort);
154  signal(SIGUSR2, SIG_IGN);
155
156  VGLOnDisplay = 1;
157  VGLSwitchPending = 0;
158  VGLAbortPending = 0;
159
160  if (ioctl(0, CONS_GET, &VGLOldMode) || ioctl(0, CONS_CURRENT, &adptype))
161    return -1;
162  if (IOCGROUP(mode) == 'V')	/* XXX: this is ugly */
163    VGLModeInfo.vi_mode = (mode & 0x0ff) + M_VESA_BASE;
164  else
165    VGLModeInfo.vi_mode = mode & 0x0ff;
166  if (ioctl(0, CONS_MODEINFO, &VGLModeInfo))	/* FBIO_MODEINFO */
167    return -1;
168
169  /* Save info for old mode to restore font size if old mode is graphics. */
170  VGLOldModeInfo.vi_mode = VGLOldMode;
171  if (ioctl(0, CONS_MODEINFO, &VGLOldModeInfo))
172    return -1;
173  VGLOldVInfo.size = sizeof(VGLOldVInfo);
174  if (ioctl(0, CONS_GETINFO, &VGLOldVInfo))
175    return -1;
176
177  VGLDisplay = (VGLBitmap *)malloc(sizeof(VGLBitmap));
178  if (VGLDisplay == NULL)
179    return -2;
180
181  if (VGLModeInfo.vi_mem_model != V_INFO_MM_DIRECT && ioctl(0, KDENABIO, 0)) {
182    free(VGLDisplay);
183    return -3;
184  }
185
186  VGLInitDone = 1;
187
188  /*
189   * vi_mem_model specifies the memory model of the current video mode
190   * in -CURRENT.
191   */
192  switch (VGLModeInfo.vi_mem_model) {
193  case V_INFO_MM_PLANAR:
194    /* we can handle EGA/VGA planner modes only */
195    if (VGLModeInfo.vi_depth != 4 || VGLModeInfo.vi_planes != 4
196	|| (adptype != KD_EGA && adptype != KD_VGA)) {
197      VGLEnd();
198      return -4;
199    }
200    VGLDisplay->Type = VIDBUF4;
201    VGLDisplay->PixelBytes = 1;
202    break;
203  case V_INFO_MM_PACKED:
204    /* we can do only 256 color packed modes */
205    if (VGLModeInfo.vi_depth != 8) {
206      VGLEnd();
207      return -4;
208    }
209    VGLDisplay->Type = VIDBUF8;
210    VGLDisplay->PixelBytes = 1;
211    break;
212  case V_INFO_MM_VGAX:
213    VGLDisplay->Type = VIDBUF8X;
214    VGLDisplay->PixelBytes = 1;
215    break;
216  case V_INFO_MM_DIRECT:
217    VGLDisplay->PixelBytes = VGLModeInfo.vi_pixel_size;
218    switch (VGLDisplay->PixelBytes) {
219    case 2:
220      VGLDisplay->Type = VIDBUF16;
221      break;
222    case 3:
223      VGLDisplay->Type = VIDBUF24;
224      break;
225    case 4:
226      VGLDisplay->Type = VIDBUF32;
227      break;
228    default:
229      VGLEnd();
230      return -4;
231    }
232    break;
233  default:
234    VGLEnd();
235    return -4;
236  }
237
238  ioctl(0, VT_WAITACTIVE, 0);
239  ioctl(0, KDSETMODE, KD_GRAPHICS);
240  if (ioctl(0, mode, 0)) {
241    VGLEnd();
242    return -5;
243  }
244  if (ioctl(0, CONS_ADPINFO, &VGLAdpInfo)) {	/* FBIO_ADPINFO */
245    VGLEnd();
246    return -6;
247  }
248
249  /*
250   * Calculate the shadow screen buffer size.  In -CURRENT, va_buffer_size
251   * always holds the entire frame buffer size, wheather it's in the linear
252   * mode or windowed mode.
253   *     VGLBufSize = VGLAdpInfo.va_buffer_size;
254   * In -STABLE, va_buffer_size holds the frame buffer size, only if
255   * the linear frame buffer mode is supported. Otherwise the field is zero.
256   * We shall calculate the minimal size in this case:
257   *     VGLAdpInfo.va_line_width*VGLModeInfo.vi_height*VGLModeInfo.vi_planes
258   * or
259   *     VGLAdpInfo.va_window_size*VGLModeInfo.vi_planes;
260   * Use whichever is larger.
261   */
262  if (VGLAdpInfo.va_buffer_size != 0)
263    VGLBufSize = VGLAdpInfo.va_buffer_size;
264  else
265    VGLBufSize = max(VGLAdpInfo.va_line_width*VGLModeInfo.vi_height,
266		     VGLAdpInfo.va_window_size)*VGLModeInfo.vi_planes;
267  /*
268   * The above is for old -CURRENT.  Current -CURRENT since r203535 and/or
269   * r248799 restricts va_buffer_size to the displayed size in VESA modes to
270   * avoid wasting kva for mapping unused parts of the frame buffer.  But all
271   * parts were usable here.  Applying the same restriction to user mappings
272   * makes our virtualization useless and breaks our panning, but large frame
273   * buffers are also difficult for us to manage (clearing and switching may
274   * be too slow, and malloc() may fail).  Restrict ourselves similarly to
275   * get the same efficiency and bugs for all kernels.
276   */
277  if (VGLModeInfo.vi_mode >= M_VESA_BASE)
278    VGLBufSize = VGLAdpInfo.va_line_width*VGLModeInfo.vi_height*
279                 VGLModeInfo.vi_planes;
280  VGLBuf = malloc(VGLBufSize);
281  if (VGLBuf == NULL) {
282    VGLEnd();
283    return -7;
284  }
285
286#ifdef LIBVGL_DEBUG
287  fprintf(stderr, "VGLBufSize:0x%x\n", VGLBufSize);
288#endif
289
290  /* see if we are in the windowed buffer mode or in the linear buffer mode */
291  if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) {
292    switch (VGLDisplay->Type) {
293    case VIDBUF4:
294      VGLDisplay->Type = VIDBUF4S;
295      break;
296    case VIDBUF8:
297      VGLDisplay->Type = VIDBUF8S;
298      break;
299    case VIDBUF16:
300      VGLDisplay->Type = VIDBUF16S;
301      break;
302    case VIDBUF24:
303      VGLDisplay->Type = VIDBUF24S;
304      break;
305    case VIDBUF32:
306      VGLDisplay->Type = VIDBUF32S;
307      break;
308    default:
309      VGLEnd();
310      return -8;
311    }
312  }
313
314  VGLMode = mode;
315  VGLCurWindow = 0;
316
317  VGLDisplay->Xsize = VGLModeInfo.vi_width;
318  VGLDisplay->Ysize = VGLModeInfo.vi_height;
319  depth = VGLModeInfo.vi_depth;
320  if (depth == 15)
321    depth = 16;
322  VGLOldVXsize =
323  VGLDisplay->VXsize = VGLAdpInfo.va_line_width
324			   *8/(depth/VGLModeInfo.vi_planes);
325  VGLDisplay->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
326  VGLDisplay->Xorigin = 0;
327  VGLDisplay->Yorigin = 0;
328
329  VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
330		       MAP_FILE | MAP_SHARED, 0, 0);
331  if (VGLMem == MAP_FAILED) {
332    VGLEnd();
333    return -7;
334  }
335  VGLDisplay->Bitmap = VGLMem;
336
337  VGLVDisplay = *VGLDisplay;
338  VGLVDisplay.Type = MEMBUF;
339  if (VGLModeInfo.vi_depth < 8)
340    VGLVDisplay.Bitmap = malloc(2 * VGLBufSize);
341  else
342    VGLVDisplay.Bitmap = VGLBuf;
343
344  VGLSavePalette();
345
346#ifdef LIBVGL_DEBUG
347  fprintf(stderr, "va_line_width:%d\n", VGLAdpInfo.va_line_width);
348  fprintf(stderr, "VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
349	  VGLDisplay->Xsize, VGLDisplay->Ysize,
350	  VGLDisplay->VXsize, VGLDisplay->VYsize);
351#endif
352
353  smode.mode = VT_PROCESS;
354  smode.waitv = 0;
355  smode.relsig = SIGUSR1;
356  smode.acqsig = SIGUSR1;
357  smode.frsig  = SIGINT;
358  if (ioctl(0, VT_SETMODE, &smode)) {
359    VGLEnd();
360    return -9;
361  }
362  VGLTextSetFontFile((byte*)0);
363  VGLClear(VGLDisplay, 0);
364  return 0;
365}
366
367void
368VGLCheckSwitch()
369{
370  if (VGLAbortPending) {
371    VGLEnd();
372    exit(0);
373  }
374  while (VGLSwitchPending) {
375    VGLSwitchPending = 0;
376    if (VGLOnDisplay) {
377      if (VGLModeInfo.vi_mem_model != V_INFO_MM_DIRECT)
378        ioctl(0, KDENABIO, 0);
379      ioctl(0, KDSETMODE, KD_GRAPHICS);
380      ioctl(0, VGLMode, 0);
381      VGLCurWindow = 0;
382      VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE,
383			   MAP_FILE | MAP_SHARED, 0, 0);
384
385      /* XXX: what if mmap() has failed! */
386      VGLDisplay->Type = VIDBUF8;	/* XXX */
387      switch (VGLModeInfo.vi_mem_model) {
388      case V_INFO_MM_PLANAR:
389	if (VGLModeInfo.vi_depth == 4 && VGLModeInfo.vi_planes == 4) {
390	  if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
391	    VGLDisplay->Type = VIDBUF4S;
392	  else
393	    VGLDisplay->Type = VIDBUF4;
394	} else {
395	  /* shouldn't be happening */
396	}
397        break;
398      case V_INFO_MM_PACKED:
399	if (VGLModeInfo.vi_depth == 8) {
400	  if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
401	    VGLDisplay->Type = VIDBUF8S;
402	  else
403	    VGLDisplay->Type = VIDBUF8;
404	}
405        break;
406      case V_INFO_MM_VGAX:
407	VGLDisplay->Type = VIDBUF8X;
408	break;
409      case V_INFO_MM_DIRECT:
410	switch (VGLModeInfo.vi_pixel_size) {
411	  case 2:
412	    if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
413	      VGLDisplay->Type = VIDBUF16S;
414	    else
415	      VGLDisplay->Type = VIDBUF16;
416	    break;
417	  case 3:
418	    if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
419	      VGLDisplay->Type = VIDBUF24S;
420	    else
421	      VGLDisplay->Type = VIDBUF24;
422	    break;
423	  case 4:
424	    if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size)
425	      VGLDisplay->Type = VIDBUF32S;
426	    else
427	      VGLDisplay->Type = VIDBUF32;
428	    break;
429	  default:
430	  /* shouldn't be happening */
431          break;
432        }
433      default:
434	/* shouldn't be happening */
435        break;
436      }
437
438      VGLDisplay->Bitmap = VGLMem;
439      VGLDisplay->Xsize = VGLModeInfo.vi_width;
440      VGLDisplay->Ysize = VGLModeInfo.vi_height;
441      VGLSetVScreenSize(VGLDisplay, VGLDisplay->VXsize, VGLDisplay->VYsize);
442      VGLRestoreBlank();
443      VGLRestoreBorder();
444      VGLMouseRestore();
445      VGLPanScreen(VGLDisplay, VGLDisplay->Xorigin, VGLDisplay->Yorigin);
446      VGLBitmapCopy(&VGLVDisplay, 0, 0, VGLDisplay, 0, 0,
447                    VGLDisplay->VXsize, VGLDisplay->VYsize);
448      VGLRestorePalette();
449      ioctl(0, VT_RELDISP, VT_ACKACQ);
450    }
451    else {
452      VGLMem = MAP_FAILED;
453      munmap(VGLDisplay->Bitmap, VGLAdpInfo.va_window_size);
454      ioctl(0, VGLOldMode, 0);
455      ioctl(0, KDSETMODE, KD_TEXT);
456      if (VGLModeInfo.vi_mem_model != V_INFO_MM_DIRECT)
457        ioctl(0, KDDISABIO, 0);
458      ioctl(0, VT_RELDISP, VT_TRUE);
459      VGLDisplay->Bitmap = VGLBuf;
460      VGLDisplay->Type = MEMBUF;
461      VGLDisplay->Xsize = VGLDisplay->VXsize;
462      VGLDisplay->Ysize = VGLDisplay->VYsize;
463      while (!VGLOnDisplay) pause();
464    }
465  }
466}
467
468int
469VGLSetSegment(unsigned int offset)
470{
471  if (offset/VGLAdpInfo.va_window_size != VGLCurWindow) {
472    ioctl(0, CONS_SETWINORG, offset);		/* FBIO_SETWINORG */
473    VGLCurWindow = offset/VGLAdpInfo.va_window_size;
474  }
475  return (offset%VGLAdpInfo.va_window_size);
476}
477
478int
479VGLSetVScreenSize(VGLBitmap *object, int VXsize, int VYsize)
480{
481  int depth;
482
483  if (VXsize < object->Xsize || VYsize < object->Ysize)
484    return -1;
485  if (object->Type == MEMBUF)
486    return -1;
487  if (ioctl(0, FBIO_SETLINEWIDTH, &VXsize))
488    return -1;
489  ioctl(0, CONS_ADPINFO, &VGLAdpInfo);	/* FBIO_ADPINFO */
490  depth = VGLModeInfo.vi_depth;
491  if (depth == 15)
492    depth = 16;
493  object->VXsize = VGLAdpInfo.va_line_width
494			   *8/(depth/VGLModeInfo.vi_planes);
495  object->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width;
496  if (VYsize < object->VYsize)
497    object->VYsize = VYsize;
498
499#ifdef LIBVGL_DEBUG
500  fprintf(stderr, "new size: VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n",
501	  object->Xsize, object->Ysize, object->VXsize, object->VYsize);
502#endif
503
504  return 0;
505}
506
507int
508VGLPanScreen(VGLBitmap *object, int x, int y)
509{
510  video_display_start_t origin;
511
512  if (x < 0 || x + object->Xsize > object->VXsize
513      || y < 0 || y + object->Ysize > object->VYsize)
514    return -1;
515  if (object->Type == MEMBUF)
516    return 0;
517  origin.x = x;
518  origin.y = y;
519  if (ioctl(0, FBIO_SETDISPSTART, &origin))
520    return -1;
521  object->Xorigin = x;
522  object->Yorigin = y;
523
524#ifdef LIBVGL_DEBUG
525  fprintf(stderr, "new origin: (%d, %d)\n", x, y);
526#endif
527
528  return 0;
529}
530