1 /*- 2 * Copyright (c) 1991-1997 S�ren Schmidt 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 10 * in this position and unchanged. 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 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software withough specific prior written permission 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include <stdio.h> 32 #include <sys/types.h> 33 #include <sys/signal.h> 34 #include <sys/file.h> 35 #include <sys/ioctl.h> 36 #include <sys/mman.h> 37 #include <sys/fbio.h> 38 #include <sys/kbio.h> 39 #include <sys/consio.h> 40 #include "vgl.h" 41 42 #define min(x, y) (((x) < (y)) ? (x) : (y)) 43 #define max(x, y) (((x) > (y)) ? (x) : (y)) 44 45 VGLBitmap *VGLDisplay; 46 video_info_t VGLModeInfo; 47 video_adapter_info_t VGLAdpInfo; 48 byte *VGLBuf; 49 50 static int VGLMode; 51 static int VGLOldMode; 52 static size_t VGLBufSize; 53 static byte *VGLMem = MAP_FAILED; 54 static int VGLSwitchPending; 55 static int VGLOnDisplay; 56 static unsigned int VGLCurWindow; 57 static int VGLInitDone = 0; 58 59 void 60 VGLEnd() 61 { 62 struct vt_mode smode; 63 64 if (!VGLInitDone) 65 return; 66 VGLInitDone = 0; 67 68 signal(SIGUSR1, SIG_IGN); 69 70 if (VGLMem != MAP_FAILED) { 71 VGLClear(VGLDisplay, 0); 72 munmap(VGLMem, VGLAdpInfo.va_window_size); 73 } 74 75 if (VGLOldMode >= M_VESA_BASE) { 76 /* ugly, but necessary */ 77 ioctl(0, _IO('V', VGLOldMode - M_VESA_BASE), 0); 78 if (VGLOldMode == M_VESA_800x600) { 79 int size[3]; 80 size[0] = 80; 81 size[1] = 25; 82 size[2] = 16; 83 ioctl(0, KDRASTER, size); 84 } 85 } else { 86 ioctl(0, _IO('S', VGLOldMode), 0); 87 } 88 ioctl(0, KDDISABIO, 0); 89 ioctl(0, KDSETMODE, KD_TEXT); 90 smode.mode = VT_AUTO; 91 ioctl(0, VT_SETMODE, &smode); 92 if (VGLBuf) 93 free(VGLBuf); 94 VGLBuf = NULL; 95 free(VGLDisplay); 96 VGLDisplay = NULL; 97 VGLKeyboardEnd(); 98 } 99 100 static void 101 VGLAbort() 102 { 103 VGLEnd(); 104 exit(0); 105 } 106 107 static void 108 VGLSwitch() 109 { 110 if (!VGLOnDisplay) 111 VGLOnDisplay = 1; 112 else 113 VGLOnDisplay = 0; 114 VGLSwitchPending = 1; 115 signal(SIGUSR1, VGLSwitch); 116 } 117 118 int 119 VGLInit(int mode) 120 { 121 struct vt_mode smode; 122 int adptype; 123 124 if (VGLInitDone) 125 return -1; 126 127 signal(SIGUSR1, VGLSwitch); 128 signal(SIGINT, VGLAbort); 129 signal(SIGTERM, VGLAbort); 130 signal(SIGSEGV, VGLAbort); 131 signal(SIGBUS, VGLAbort); 132 133 VGLOnDisplay = 1; 134 VGLSwitchPending = 0; 135 136 if (ioctl(0, CONS_GET, &VGLOldMode) || ioctl(0, CONS_CURRENT, &adptype)) 137 return -1; 138 if (IOCGROUP(mode) == 'V') /* XXX: this is ugly */ 139 VGLModeInfo.vi_mode = (mode & 0x0ff) + M_VESA_BASE; 140 else 141 VGLModeInfo.vi_mode = mode & 0x0ff; 142 if (ioctl(0, CONS_MODEINFO, &VGLModeInfo)) /* FBIO_MODEINFO */ 143 return -1; 144 145 VGLDisplay = (VGLBitmap *)malloc(sizeof(VGLBitmap)); 146 if (VGLDisplay == NULL) 147 return -2; 148 149 if (ioctl(0, KDENABIO, 0)) { 150 free(VGLDisplay); 151 return -3; 152 } 153 154 VGLInitDone = 1; 155 156 /* 157 * vi_mem_model specifies the memory model of the current video mode 158 * in -CURRENT. 159 */ 160 switch (VGLModeInfo.vi_mem_model) { 161 case V_INFO_MM_PLANAR: 162 /* we can handle EGA/VGA planner modes only */ 163 if (VGLModeInfo.vi_depth != 4 || VGLModeInfo.vi_planes != 4 164 || (adptype != KD_EGA && adptype != KD_VGA)) { 165 VGLEnd(); 166 return -4; 167 } 168 VGLDisplay->Type = VIDBUF4; 169 break; 170 case V_INFO_MM_PACKED: 171 /* we can do only 256 color packed modes */ 172 if (VGLModeInfo.vi_depth != 8) { 173 VGLEnd(); 174 return -4; 175 } 176 VGLDisplay->Type = VIDBUF8; 177 break; 178 case V_INFO_MM_VGAX: 179 VGLDisplay->Type = VIDBUF8X; 180 break; 181 default: 182 VGLEnd(); 183 return -4; 184 } 185 186 ioctl(0, VT_WAITACTIVE, 0); 187 ioctl(0, KDSETMODE, KD_GRAPHICS); 188 if (ioctl(0, mode, 0)) { 189 VGLEnd(); 190 return -5; 191 } 192 if (ioctl(0, CONS_ADPINFO, &VGLAdpInfo)) { /* FBIO_ADPINFO */ 193 VGLEnd(); 194 return -6; 195 } 196 197 /* 198 * Calculate the shadow screen buffer size. In -CURRENT, va_buffer_size 199 * always holds the entire frame buffer size, wheather it's in the linear 200 * mode or windowed mode. 201 * VGLBufSize = VGLAdpInfo.va_buffer_size; 202 * In -STABLE, va_buffer_size holds the frame buffer size, only if 203 * the linear frame buffer mode is supported. Otherwise the field is zero. 204 * We shall calculate the minimal size in this case: 205 * VGLAdpInfo.va_line_width*VGLModeInfo.vi_height*VGLModeInfo.vi_planes 206 * or 207 * VGLAdpInfo.va_window_size*VGLModeInfo.vi_planes; 208 * Use whichever is larger. 209 */ 210 if (VGLAdpInfo.va_buffer_size != 0) 211 VGLBufSize = VGLAdpInfo.va_buffer_size; 212 else 213 VGLBufSize = max(VGLAdpInfo.va_line_width*VGLModeInfo.vi_height, 214 VGLAdpInfo.va_window_size)*VGLModeInfo.vi_planes; 215 VGLBuf = malloc(VGLBufSize); 216 if (VGLBuf == NULL) { 217 VGLEnd(); 218 return -7; 219 } 220 221 #ifdef LIBVGL_DEBUG 222 fprintf(stderr, "VGLBufSize:0x%x\n", VGLBufSize); 223 #endif 224 225 /* see if we are in the windowed buffer mode or in the linear buffer mode */ 226 if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) { 227 if (VGLDisplay->Type == VIDBUF4) 228 VGLDisplay->Type = VIDBUF4S; 229 else if (VGLDisplay->Type == VIDBUF8) 230 VGLDisplay->Type = VIDBUF8S; 231 } 232 233 VGLMode = mode; 234 VGLCurWindow = 0; 235 236 VGLDisplay->Xsize = VGLModeInfo.vi_width; 237 VGLDisplay->Ysize = VGLModeInfo.vi_height; 238 VGLDisplay->VXsize = VGLAdpInfo.va_line_width 239 *8/(VGLModeInfo.vi_depth/VGLModeInfo.vi_planes); 240 VGLDisplay->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width; 241 VGLDisplay->Xorigin = 0; 242 VGLDisplay->Yorigin = 0; 243 244 VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE, 245 MAP_FILE, 0, 0); 246 if (VGLMem == MAP_FAILED) { 247 VGLEnd(); 248 return -7; 249 } 250 VGLDisplay->Bitmap = VGLMem; 251 252 VGLSavePalette(); 253 254 #ifdef LIBVGL_DEBUG 255 fprintf(stderr, "va_line_width:%d\n", VGLAdpInfo.va_line_width); 256 fprintf(stderr, "VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n", 257 VGLDisplay->Xsize, VGLDisplay->Ysize, 258 VGLDisplay->VXsize, VGLDisplay->VYsize); 259 #endif 260 261 smode.mode = VT_PROCESS; 262 smode.waitv = 0; 263 smode.relsig = SIGUSR1; 264 smode.acqsig = SIGUSR1; 265 smode.frsig = SIGINT; 266 if (ioctl(0, VT_SETMODE, &smode)) { 267 VGLEnd(); 268 return -9; 269 } 270 VGLTextSetFontFile((byte*)0); 271 VGLClear(VGLDisplay, 0); 272 return 0; 273 } 274 275 void 276 VGLCheckSwitch() 277 { 278 while (VGLSwitchPending) { 279 unsigned int offset; 280 unsigned int len; 281 int i; 282 283 VGLSwitchPending = 0; 284 if (VGLOnDisplay) { 285 ioctl(0, KDENABIO, 0); 286 ioctl(0, KDSETMODE, KD_GRAPHICS); 287 ioctl(0, VGLMode, 0); 288 VGLCurWindow = 0; 289 VGLMem = (byte*)mmap(0, VGLAdpInfo.va_window_size, PROT_READ|PROT_WRITE, 290 MAP_FILE, 0, 0); 291 292 /* XXX: what if mmap() has failed! */ 293 VGLDisplay->Type = VIDBUF8; /* XXX */ 294 switch (VGLModeInfo.vi_mem_model) { 295 case V_INFO_MM_PLANAR: 296 if (VGLModeInfo.vi_depth == 4 && VGLModeInfo.vi_planes == 4) { 297 if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) 298 VGLDisplay->Type = VIDBUF4S; 299 else 300 VGLDisplay->Type = VIDBUF4; 301 } else { 302 /* shouldn't be happening */ 303 } 304 break; 305 case V_INFO_MM_PACKED: 306 if (VGLModeInfo.vi_depth == 8) { 307 if (VGLBufSize/VGLModeInfo.vi_planes > VGLAdpInfo.va_window_size) 308 VGLDisplay->Type = VIDBUF8S; 309 else 310 VGLDisplay->Type = VIDBUF8; 311 } else { 312 /* shouldn't be happening */ 313 } 314 break; 315 case V_INFO_MM_VGAX: 316 VGLDisplay->Type = VIDBUF8X; 317 break; 318 default: 319 /* shouldn't be happening */ 320 break; 321 } 322 323 VGLDisplay->Bitmap = VGLMem; 324 VGLDisplay->Xsize = VGLModeInfo.vi_width; 325 VGLDisplay->Ysize = VGLModeInfo.vi_height; 326 VGLSetVScreenSize(VGLDisplay, VGLDisplay->VXsize, VGLDisplay->VYsize); 327 VGLPanScreen(VGLDisplay, VGLDisplay->Xorigin, VGLDisplay->Yorigin); 328 switch (VGLDisplay->Type) { 329 case VIDBUF4S: 330 outb(0x3c6, 0xff); 331 outb(0x3ce, 0x01); outb(0x3cf, 0x00); /* set/reset enable */ 332 outb(0x3ce, 0x08); outb(0x3cf, 0xff); /* bit mask */ 333 for (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes; 334 offset += len) { 335 VGLSetSegment(offset); 336 len = min(VGLBufSize/VGLModeInfo.vi_planes - offset, 337 VGLAdpInfo.va_window_size); 338 for (i = 0; i < VGLModeInfo.vi_planes; i++) { 339 outb(0x3c4, 0x02); 340 outb(0x3c5, 0x01<<i); 341 bcopy(&VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset], 342 VGLMem, len); 343 } 344 } 345 break; 346 case VIDBUF4: 347 case VIDBUF8X: 348 outb(0x3c6, 0xff); 349 outb(0x3ce, 0x01); outb(0x3cf, 0x00); /* set/reset enable */ 350 outb(0x3ce, 0x08); outb(0x3cf, 0xff); /* bit mask */ 351 for (i = 0; i < VGLModeInfo.vi_planes; i++) { 352 outb(0x3c4, 0x02); 353 outb(0x3c5, 0x01<<i); 354 bcopy(&VGLBuf[i*VGLAdpInfo.va_window_size], VGLMem, 355 VGLAdpInfo.va_window_size); 356 } 357 break; 358 case VIDBUF8: 359 case VIDBUF8S: 360 for (offset = 0; offset < VGLBufSize; offset += len) { 361 VGLSetSegment(offset); 362 len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size); 363 bcopy(&VGLBuf[offset], VGLMem, len); 364 } 365 break; 366 } 367 VGLRestorePalette(); 368 ioctl(0, VT_RELDISP, VT_ACKACQ); 369 } 370 else { 371 switch (VGLDisplay->Type) { 372 case VIDBUF4S: 373 for (offset = 0; offset < VGLBufSize/VGLModeInfo.vi_planes; 374 offset += len) { 375 VGLSetSegment(offset); 376 len = min(VGLBufSize/VGLModeInfo.vi_planes - offset, 377 VGLAdpInfo.va_window_size); 378 for (i = 0; i < VGLModeInfo.vi_planes; i++) { 379 outb(0x3ce, 0x04); 380 outb(0x3cf, i); 381 bcopy(VGLMem, &VGLBuf[i*VGLBufSize/VGLModeInfo.vi_planes + offset], 382 len); 383 } 384 } 385 break; 386 case VIDBUF4: 387 case VIDBUF8X: 388 /* 389 * NOTE: the saved buffer is NOT in the MEMBUF format which 390 * the ordinary memory bitmap object is stored in. XXX 391 */ 392 for (i = 0; i < VGLModeInfo.vi_planes; i++) { 393 outb(0x3ce, 0x04); 394 outb(0x3cf, i); 395 bcopy(VGLMem, &VGLBuf[i*VGLAdpInfo.va_window_size], 396 VGLAdpInfo.va_window_size); 397 } 398 break; 399 case VIDBUF8: 400 case VIDBUF8S: 401 for (offset = 0; offset < VGLBufSize; offset += len) { 402 VGLSetSegment(offset); 403 len = min(VGLBufSize - offset, VGLAdpInfo.va_window_size); 404 bcopy(VGLMem, &VGLBuf[offset], len); 405 } 406 break; 407 } 408 VGLMem = MAP_FAILED; 409 munmap(VGLDisplay->Bitmap, VGLAdpInfo.va_window_size); 410 ioctl(0, VGLOldMode, 0); 411 ioctl(0, KDSETMODE, KD_TEXT); 412 ioctl(0, KDDISABIO, 0); 413 ioctl(0, VT_RELDISP, VT_TRUE); 414 VGLDisplay->Bitmap = VGLBuf; 415 VGLDisplay->Type = MEMBUF; 416 VGLDisplay->Xsize = VGLDisplay->VXsize; 417 VGLDisplay->Ysize = VGLDisplay->VYsize; 418 while (!VGLOnDisplay) pause(); 419 } 420 } 421 } 422 423 int 424 VGLSetSegment(unsigned int offset) 425 { 426 if (offset/VGLAdpInfo.va_window_size != VGLCurWindow) { 427 ioctl(0, CONS_SETWINORG, offset); /* FBIO_SETWINORG */ 428 VGLCurWindow = offset/VGLAdpInfo.va_window_size; 429 } 430 return (offset%VGLAdpInfo.va_window_size); 431 } 432 433 int 434 VGLSetVScreenSize(VGLBitmap *object, int VXsize, int VYsize) 435 { 436 if (VXsize < object->Xsize || VYsize < object->Ysize) 437 return -1; 438 if (object->Type == MEMBUF) 439 return -1; 440 if (ioctl(0, FBIO_SETLINEWIDTH, &VXsize)) 441 return -1; 442 ioctl(0, CONS_ADPINFO, &VGLAdpInfo); /* FBIO_ADPINFO */ 443 object->VXsize = VGLAdpInfo.va_line_width 444 *8/(VGLModeInfo.vi_depth/VGLModeInfo.vi_planes); 445 object->VYsize = VGLBufSize/VGLModeInfo.vi_planes/VGLAdpInfo.va_line_width; 446 if (VYsize < object->VYsize) 447 object->VYsize = VYsize; 448 449 #ifdef LIBVGL_DEBUG 450 fprintf(stderr, "new size: VGLXsize:%d, Ysize:%d, VXsize:%d, VYsize:%d\n", 451 object->Xsize, object->Ysize, object->VXsize, object->VYsize); 452 #endif 453 454 return 0; 455 } 456 457 int 458 VGLPanScreen(VGLBitmap *object, int x, int y) 459 { 460 video_display_start_t origin; 461 462 if (x < 0 || x + object->Xsize > object->VXsize 463 || y < 0 || y + object->Ysize > object->VYsize) 464 return -1; 465 if (object->Type == MEMBUF) 466 return 0; 467 origin.x = x; 468 origin.y = y; 469 if (ioctl(0, FBIO_SETDISPSTART, &origin)) 470 return -1; 471 object->Xorigin = x; 472 object->Yorigin = y; 473 474 #ifdef LIBVGL_DEBUG 475 fprintf(stderr, "new origin: (%d, %d)\n", x, y); 476 #endif 477 478 return 0; 479 } 480