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 __FBSDID("$FreeBSD$"); 33 34 #include <signal.h> 35 #include <stdio.h> 36 #include <sys/types.h> 37 #include <sys/file.h> 38 #include <sys/ioctl.h> 39 #include <sys/mman.h> 40 #include <sys/fbio.h> 41 #include <sys/kbio.h> 42 #include <sys/consio.h> 43 #include "vgl.h" 44 45 /* XXX Direct Color 24bits modes unsupported */ 46 47 #define min(x, y) (((x) < (y)) ? (x) : (y)) 48 #define max(x, y) (((x) > (y)) ? (x) : (y)) 49 50 VGLBitmap *VGLDisplay; 51 VGLBitmap VGLVDisplay; 52 video_info_t VGLModeInfo; 53 video_adapter_info_t VGLAdpInfo; 54 byte *VGLBuf; 55 56 static int VGLMode; 57 static int VGLOldMode; 58 static size_t VGLBufSize; 59 static byte *VGLMem = MAP_FAILED; 60 static int VGLSwitchPending; 61 static int VGLAbortPending; 62 static int VGLOnDisplay; 63 static unsigned int VGLCurWindow; 64 static int VGLInitDone = 0; 65 static video_info_t VGLOldModeInfo; 66 static vid_info_t VGLOldVInfo; 67 68 void 69 VGLEnd() 70 { 71 struct vt_mode smode; 72 int size[3]; 73 74 if (!VGLInitDone) 75 return; 76 signal(SIGUSR1, SIG_IGN); 77 signal(SIGUSR2, SIG_IGN); 78 VGLSwitchPending = 0; 79 VGLAbortPending = 0; 80 VGLMousePointerHide(); 81 82 if (VGLMem != MAP_FAILED) { 83 VGLClear(VGLDisplay, 0); 84 munmap(VGLMem, VGLAdpInfo.va_window_size); 85 } 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 110 static void 111 VGLAbort(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 129 static void 130 VGLSwitch(int arg __unused) 131 { 132 if (!VGLOnDisplay) 133 VGLOnDisplay = 1; 134 else 135 VGLOnDisplay = 0; 136 VGLSwitchPending = 1; 137 signal(SIGUSR1, VGLSwitch); 138 } 139 140 int 141 VGLInit(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 #if notyet 223 case 3: 224 VGLDisplay->Type = VIDBUF24; 225 break; 226 #endif 227 case 4: 228 VGLDisplay->Type = VIDBUF32; 229 break; 230 default: 231 VGLEnd(); 232 return -4; 233 } 234 break; 235 default: 236 VGLEnd(); 237 return -4; 238 } 239 240 ioctl(0, VT_WAITACTIVE, 0); 241 ioctl(0, KDSETMODE, KD_GRAPHICS); 242 if (ioctl(0, mode, 0)) { 243 VGLEnd(); 244 return -5; 245 } 246 if (ioctl(0, CONS_ADPINFO, &VGLAdpInfo)) { /* FBIO_ADPINFO */ 247 VGLEnd(); 248 return -6; 249 } 250 251 /* 252 * Calculate the shadow screen buffer size. In -CURRENT, va_buffer_size 253 * always holds the entire frame buffer size, wheather it's in the linear 254 * mode or windowed mode. 255 * VGLBufSize = VGLAdpInfo.va_buffer_size; 256 * In -STABLE, va_buffer_size holds the frame buffer size, only if 257 * the linear frame buffer mode is supported. Otherwise the field is zero. 258 * We shall calculate the minimal size in this case: 259 * VGLAdpInfo.va_line_width*VGLModeInfo.vi_height*VGLModeInfo.vi_planes 260 * or 261 * VGLAdpInfo.va_window_size*VGLModeInfo.vi_planes; 262 * Use whichever is larger. 263 */ 264 if (VGLAdpInfo.va_buffer_size != 0) 265 VGLBufSize = VGLAdpInfo.va_buffer_size; 266 else 267 VGLBufSize = max(VGLAdpInfo.va_line_width*VGLModeInfo.vi_height, 268 VGLAdpInfo.va_window_size)*VGLModeInfo.vi_planes; 269 /* 270 * The above is for old -CURRENT. Current -CURRENT since r203535 and/or 271 * r248799 restricts va_buffer_size to the displayed size in VESA modes to 272 * avoid wasting kva for mapping unused parts of the frame buffer. But all 273 * parts were usable here. Applying the same restriction to user mappings 274 * makes our virtualization useless and breaks our panning, but large frame 275 * buffers are also difficult for us to manage (clearing and switching may 276 * be too slow, and malloc() may fail). Restrict ourselves similarly to 277 * get the same efficiency and bugs for all kernels. 278 */ 279 if (VGLModeInfo.vi_mode >= M_VESA_BASE) 280 VGLBufSize = VGLAdpInfo.va_line_width*VGLModeInfo.vi_height* 281 VGLModeInfo.vi_planes; 282 VGLBuf = malloc(VGLBufSize); 283 if (VGLBuf == NULL) { 284 VGLEnd(); 285 return -7; 286 } 287 288 #ifdef LIBVGL_DEBUG 289 fprintf(stderr, "VGLBufSize:0x%x\n", VGLBufSize); 290 #endif 291 292 /* see if we are in the windowed buffer mode or in the linear buffer mode */ 293 if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) { 294 switch (VGLDisplay->Type) { 295 case VIDBUF4: 296 VGLDisplay->Type = VIDBUF4S; 297 break; 298 case VIDBUF8: 299 VGLDisplay->Type = VIDBUF8S; 300 break; 301 case VIDBUF16: 302 VGLDisplay->Type = VIDBUF16S; 303 break; 304 case VIDBUF24: 305 VGLDisplay->Type = VIDBUF24S; 306 break; 307 case VIDBUF32: 308 VGLDisplay->Type = VIDBUF32S; 309 break; 310 default: 311 VGLEnd(); 312 return -8; 313 } 314 } 315 316 VGLMode = mode; 317 VGLCurWindow = 0; 318 319 VGLDisplay->Xsize = VGLModeInfo.vi_width; 320 VGLDisplay->Ysize = VGLModeInfo.vi_height; 321 depth = VGLModeInfo.vi_depth; 322 if (depth == 15) 323 depth = 16; 324 VGLDisplay->VXsize = VGLAdpInfo.va_line_width 325 *8/(depth/VGLModeInfo.vi_planes); 326 VGLDisplay->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width; 327 VGLDisplay->Xorigin = 0; 328 VGLDisplay->Yorigin = 0; 329 330 VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE, 331 MAP_FILE | MAP_SHARED, 0, 0); 332 if (VGLMem == MAP_FAILED) { 333 VGLEnd(); 334 return -7; 335 } 336 VGLDisplay->Bitmap = VGLMem; 337 338 VGLVDisplay = *VGLDisplay; 339 VGLVDisplay.Type = MEMBUF; 340 if (VGLModeInfo.vi_depth < 8) 341 VGLVDisplay.Bitmap = malloc(2 * VGLBufSize); 342 else 343 VGLVDisplay.Bitmap = VGLBuf; 344 345 VGLSavePalette(); 346 347 #ifdef LIBVGL_DEBUG 348 fprintf(stderr, "va_line_width:%d\n", VGLAdpInfo.va_line_width); 349 fprintf(stderr, "VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n", 350 VGLDisplay->Xsize, VGLDisplay->Ysize, 351 VGLDisplay->VXsize, VGLDisplay->VYsize); 352 #endif 353 354 smode.mode = VT_PROCESS; 355 smode.waitv = 0; 356 smode.relsig = SIGUSR1; 357 smode.acqsig = SIGUSR1; 358 smode.frsig = SIGINT; 359 if (ioctl(0, VT_SETMODE, &smode)) { 360 VGLEnd(); 361 return -9; 362 } 363 VGLTextSetFontFile((byte*)0); 364 VGLClear(VGLDisplay, 0); 365 return 0; 366 } 367 368 void 369 VGLCheckSwitch() 370 { 371 if (VGLAbortPending) { 372 VGLEnd(); 373 exit(0); 374 } 375 while (VGLSwitchPending) { 376 VGLSwitchPending = 0; 377 if (VGLOnDisplay) { 378 if (VGLModeInfo.vi_mem_model != V_INFO_MM_DIRECT) 379 ioctl(0, KDENABIO, 0); 380 ioctl(0, KDSETMODE, KD_GRAPHICS); 381 ioctl(0, VGLMode, 0); 382 VGLCurWindow = 0; 383 VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE, 384 MAP_FILE | MAP_SHARED, 0, 0); 385 386 /* XXX: what if mmap() has failed! */ 387 VGLDisplay->Type = VIDBUF8; /* XXX */ 388 switch (VGLModeInfo.vi_mem_model) { 389 case V_INFO_MM_PLANAR: 390 if (VGLModeInfo.vi_depth == 4 && VGLModeInfo.vi_planes == 4) { 391 if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) 392 VGLDisplay->Type = VIDBUF4S; 393 else 394 VGLDisplay->Type = VIDBUF4; 395 } else { 396 /* shouldn't be happening */ 397 } 398 break; 399 case V_INFO_MM_PACKED: 400 if (VGLModeInfo.vi_depth == 8) { 401 if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) 402 VGLDisplay->Type = VIDBUF8S; 403 else 404 VGLDisplay->Type = VIDBUF8; 405 } 406 break; 407 case V_INFO_MM_VGAX: 408 VGLDisplay->Type = VIDBUF8X; 409 break; 410 case V_INFO_MM_DIRECT: 411 switch (VGLModeInfo.vi_pixel_size) { 412 case 2: 413 if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) 414 VGLDisplay->Type = VIDBUF16S; 415 else 416 VGLDisplay->Type = VIDBUF16; 417 break; 418 case 3: 419 if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) 420 VGLDisplay->Type = VIDBUF24S; 421 else 422 VGLDisplay->Type = VIDBUF24; 423 break; 424 case 4: 425 if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) 426 VGLDisplay->Type = VIDBUF32S; 427 else 428 VGLDisplay->Type = VIDBUF32; 429 break; 430 default: 431 /* shouldn't be happening */ 432 break; 433 } 434 default: 435 /* shouldn't be happening */ 436 break; 437 } 438 439 VGLDisplay->Bitmap = VGLMem; 440 VGLDisplay->Xsize = VGLModeInfo.vi_width; 441 VGLDisplay->Ysize = VGLModeInfo.vi_height; 442 VGLSetVScreenSize(VGLDisplay, VGLDisplay->VXsize, VGLDisplay->VYsize); 443 VGLRestoreBlank(); 444 VGLRestoreBorder(); 445 VGLMouseRestore(); 446 VGLPanScreen(VGLDisplay, VGLDisplay->Xorigin, VGLDisplay->Yorigin); 447 VGLBitmapCopy(&VGLVDisplay, 0, 0, VGLDisplay, 0, 0, 448 VGLDisplay->VXsize, VGLDisplay->VYsize); 449 VGLRestorePalette(); 450 ioctl(0, VT_RELDISP, VT_ACKACQ); 451 } 452 else { 453 VGLMem = MAP_FAILED; 454 munmap(VGLDisplay->Bitmap, VGLAdpInfo.va_window_size); 455 ioctl(0, VGLOldMode, 0); 456 ioctl(0, KDSETMODE, KD_TEXT); 457 if (VGLModeInfo.vi_mem_model != V_INFO_MM_DIRECT) 458 ioctl(0, KDDISABIO, 0); 459 ioctl(0, VT_RELDISP, VT_TRUE); 460 VGLDisplay->Bitmap = VGLBuf; 461 VGLDisplay->Type = MEMBUF; 462 VGLDisplay->Xsize = VGLDisplay->VXsize; 463 VGLDisplay->Ysize = VGLDisplay->VYsize; 464 while (!VGLOnDisplay) pause(); 465 } 466 } 467 } 468 469 int 470 VGLSetSegment(unsigned int offset) 471 { 472 if (offset/VGLAdpInfo.va_window_size != VGLCurWindow) { 473 ioctl(0, CONS_SETWINORG, offset); /* FBIO_SETWINORG */ 474 VGLCurWindow = offset/VGLAdpInfo.va_window_size; 475 } 476 return (offset%VGLAdpInfo.va_window_size); 477 } 478 479 int 480 VGLSetVScreenSize(VGLBitmap *object, int VXsize, int VYsize) 481 { 482 int depth; 483 484 if (VXsize < object->Xsize || VYsize < object->Ysize) 485 return -1; 486 if (object->Type == MEMBUF) 487 return -1; 488 if (ioctl(0, FBIO_SETLINEWIDTH, &VXsize)) 489 return -1; 490 ioctl(0, CONS_ADPINFO, &VGLAdpInfo); /* FBIO_ADPINFO */ 491 depth = VGLModeInfo.vi_depth; 492 if (depth == 15) 493 depth = 16; 494 object->VXsize = VGLAdpInfo.va_line_width 495 *8/(depth/VGLModeInfo.vi_planes); 496 object->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width; 497 if (VYsize < object->VYsize) 498 object->VYsize = VYsize; 499 500 #ifdef LIBVGL_DEBUG 501 fprintf(stderr, "new size: VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n", 502 object->Xsize, object->Ysize, object->VXsize, object->VYsize); 503 #endif 504 505 return 0; 506 } 507 508 int 509 VGLPanScreen(VGLBitmap *object, int x, int y) 510 { 511 video_display_start_t origin; 512 513 if (x < 0 || x + object->Xsize > object->VXsize 514 || y < 0 || y + object->Ysize > object->VYsize) 515 return -1; 516 if (object->Type == MEMBUF) 517 return 0; 518 origin.x = x; 519 origin.y = y; 520 if (ioctl(0, FBIO_SETDISPSTART, &origin)) 521 return -1; 522 object->Xorigin = x; 523 object->Yorigin = y; 524 525 #ifdef LIBVGL_DEBUG 526 fprintf(stderr, "new origin: (%d, %d)\n", x, y); 527 #endif 528 529 return 0; 530 } 531