1 /*- 2 * Copyright (c) 1998 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp> 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The DragonFly Project 6 * by Sascha Wildner <saw@online.de> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer as 13 * the first lines of this file unmodified. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * $FreeBSD: src/sys/dev/syscons/scvidctl.c,v 1.19.2.2 2000/05/05 09:16:08 nyan Exp $ 30 * $DragonFly: src/sys/dev/misc/syscons/scvidctl.c,v 1.16 2007/08/19 11:39:11 swildner Exp $ 31 */ 32 33 #include "opt_syscons.h" 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/conf.h> 38 #include <sys/signalvar.h> 39 #include <sys/tty.h> 40 #include <sys/kernel.h> 41 #include <sys/thread2.h> 42 43 #include <machine/console.h> 44 45 #include <dev/video/fb/fbreg.h> 46 #include "syscons.h" 47 48 SET_DECLARE(scrndr_set, const sc_renderer_t); 49 50 int 51 sc_set_text_mode(scr_stat *scp, struct tty *tp, int mode, int xsize, int ysize, 52 int fontsize) 53 { 54 video_info_t info; 55 u_char *font; 56 int prev_ysize; 57 int new_ysize; 58 int error; 59 60 if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, mode, &info)) 61 return ENODEV; 62 63 /* adjust argument values */ 64 if (fontsize <= 0) 65 fontsize = info.vi_cheight; 66 if (fontsize < 14) { 67 fontsize = 8; 68 #ifndef SC_NO_FONT_LOADING 69 if (!(scp->sc->fonts_loaded & FONT_8)) 70 return EINVAL; 71 font = scp->sc->font_8; 72 #else 73 font = NULL; 74 #endif 75 } else if (fontsize >= 16) { 76 fontsize = 16; 77 #ifndef SC_NO_FONT_LOADING 78 if (!(scp->sc->fonts_loaded & FONT_16)) 79 return EINVAL; 80 font = scp->sc->font_16; 81 #else 82 font = NULL; 83 #endif 84 } else { 85 fontsize = 14; 86 #ifndef SC_NO_FONT_LOADING 87 if (!(scp->sc->fonts_loaded & FONT_14)) 88 return EINVAL; 89 font = scp->sc->font_14; 90 #else 91 font = NULL; 92 #endif 93 } 94 if ((xsize <= 0) || (xsize > info.vi_width)) 95 xsize = info.vi_width; 96 if ((ysize <= 0) || (ysize > info.vi_height)) 97 ysize = info.vi_height; 98 99 /* stop screen saver, etc */ 100 crit_enter(); 101 if ((error = sc_clean_up(scp))) { 102 crit_exit(); 103 return error; 104 } 105 106 if (sc_render_match(scp, scp->sc->adp->va_name, 0) == NULL) { 107 crit_exit(); 108 return ENODEV; 109 } 110 111 /* set up scp */ 112 new_ysize = 0; 113 #ifndef SC_NO_HISTORY 114 if (scp->history != NULL) { 115 sc_hist_save(scp); 116 new_ysize = sc_vtb_rows(scp->history); 117 } 118 #endif 119 prev_ysize = scp->ysize; 120 /* 121 * This is a kludge to fend off scrn_update() while we 122 * muck around with scp. XXX 123 */ 124 scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN; 125 scp->status &= ~(GRAPHICS_MODE | PIXEL_MODE | MOUSE_VISIBLE); 126 scp->mode = mode; 127 scp->xsize = xsize; 128 scp->ysize = ysize; 129 scp->xoff = 0; 130 scp->yoff = 0; 131 scp->xpixel = scp->xsize*8; 132 scp->ypixel = scp->ysize*fontsize; 133 scp->font = font; 134 scp->font_size = fontsize; 135 136 /* allocate buffers */ 137 sc_alloc_scr_buffer(scp, TRUE, TRUE); 138 sc_init_emulator(scp, NULL); 139 #ifndef SC_NO_CUTPASTE 140 sc_alloc_cut_buffer(scp, FALSE); 141 #endif 142 #ifndef SC_NO_HISTORY 143 sc_alloc_history_buffer(scp, new_ysize, prev_ysize, FALSE); 144 #endif 145 crit_exit(); 146 147 if (scp == scp->sc->cur_scp) 148 set_mode(scp); 149 scp->status &= ~UNKNOWN_MODE; 150 151 if (tp == NULL) 152 return 0; 153 DPRINTF(5, ("ws_*size (%d,%d), size (%d,%d)\n", 154 tp->t_winsize.ws_col, tp->t_winsize.ws_row, scp->xsize, scp->ysize)); 155 if (tp->t_winsize.ws_col != scp->xsize 156 || tp->t_winsize.ws_row != scp->ysize) { 157 tp->t_winsize.ws_col = scp->xsize; 158 tp->t_winsize.ws_row = scp->ysize; 159 pgsignal(tp->t_pgrp, SIGWINCH, 1); 160 } 161 162 return 0; 163 } 164 165 int 166 sc_set_graphics_mode(scr_stat *scp, struct tty *tp, int mode) 167 { 168 #ifdef SC_NO_MODE_CHANGE 169 return ENODEV; 170 #else 171 video_info_t info; 172 int error; 173 174 if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, mode, &info)) 175 return ENODEV; 176 177 /* stop screen saver, etc */ 178 crit_enter(); 179 if ((error = sc_clean_up(scp))) { 180 crit_exit(); 181 return error; 182 } 183 184 if (sc_render_match(scp, scp->sc->adp->va_name, GRAPHICS_MODE) == NULL) { 185 crit_exit(); 186 return ENODEV; 187 } 188 189 /* set up scp */ 190 scp->status |= (UNKNOWN_MODE | GRAPHICS_MODE | MOUSE_HIDDEN); 191 scp->status &= ~(PIXEL_MODE | MOUSE_VISIBLE); 192 scp->mode = mode; 193 /* 194 * Don't change xsize and ysize; preserve the previous vty 195 * and history buffers. 196 */ 197 scp->xoff = 0; 198 scp->yoff = 0; 199 scp->xpixel = info.vi_width; 200 scp->ypixel = info.vi_height; 201 scp->font = NULL; 202 scp->font_size = 0; 203 #ifndef SC_NO_SYSMOUSE 204 /* move the mouse cursor at the center of the screen */ 205 sc_mouse_move(scp, scp->xpixel / 2, scp->ypixel / 2); 206 #endif 207 sc_init_emulator(scp, NULL); 208 crit_exit(); 209 210 if (scp == scp->sc->cur_scp) 211 set_mode(scp); 212 /* clear_graphics();*/ 213 refresh_ega_palette(scp); 214 scp->status &= ~UNKNOWN_MODE; 215 216 if (tp == NULL) 217 return 0; 218 if (tp->t_winsize.ws_xpixel != scp->xpixel 219 || tp->t_winsize.ws_ypixel != scp->ypixel) { 220 tp->t_winsize.ws_xpixel = scp->xpixel; 221 tp->t_winsize.ws_ypixel = scp->ypixel; 222 pgsignal(tp->t_pgrp, SIGWINCH, 1); 223 } 224 225 return 0; 226 #endif /* SC_NO_MODE_CHANGE */ 227 } 228 229 int 230 sc_set_pixel_mode(scr_stat *scp, struct tty *tp, int xsize, int ysize, 231 int fontsize) 232 { 233 #ifndef SC_PIXEL_MODE 234 return ENODEV; 235 #else 236 video_info_t info; 237 u_char *font; 238 int prev_ysize; 239 int new_ysize; 240 int error; 241 242 if ((*vidsw[scp->sc->adapter]->get_info)(scp->sc->adp, scp->mode, &info)) 243 return ENODEV; /* this shouldn't happen */ 244 245 /* adjust argument values */ 246 if (fontsize <= 0) 247 fontsize = info.vi_cheight; 248 if (fontsize < 14) { 249 fontsize = 8; 250 #ifndef SC_NO_FONT_LOADING 251 if (!(scp->sc->fonts_loaded & FONT_8)) 252 return EINVAL; 253 font = scp->sc->font_8; 254 #else 255 font = NULL; 256 #endif 257 } else if (fontsize >= 16) { 258 fontsize = 16; 259 #ifndef SC_NO_FONT_LOADING 260 if (!(scp->sc->fonts_loaded & FONT_16)) 261 return EINVAL; 262 font = scp->sc->font_16; 263 #else 264 font = NULL; 265 #endif 266 } else { 267 fontsize = 14; 268 #ifndef SC_NO_FONT_LOADING 269 if (!(scp->sc->fonts_loaded & FONT_14)) 270 return EINVAL; 271 font = scp->sc->font_14; 272 #else 273 font = NULL; 274 #endif 275 } 276 if (xsize <= 0) 277 xsize = info.vi_width/8; 278 if (ysize <= 0) 279 ysize = info.vi_height/fontsize; 280 281 if ((info.vi_width < xsize*8) || (info.vi_height < ysize*fontsize)) 282 return EINVAL; 283 284 /* 285 * We currently support the following graphic modes: 286 * 287 * - 4 bpp planar modes whose memory size does not exceed 64K 288 * - 15, 16, 24 and 32 bpp direct modes with linear frame buffer 289 */ 290 291 if (info.vi_mem_model == V_INFO_MM_PLANAR) { 292 if (info.vi_planes != 4) 293 return ENODEV; 294 295 /* 296 * A memory size >64K requires bank switching to access the entire 297 * screen. XXX 298 */ 299 300 if (info.vi_width * info.vi_height / 8 > info.vi_window_size) 301 return ENODEV; 302 } else if (info.vi_mem_model == V_INFO_MM_DIRECT) { 303 if (!(info.vi_flags & V_INFO_LINEAR) && 304 (info.vi_depth != 15) && (info.vi_depth != 16) && 305 (info.vi_depth != 24) && (info.vi_depth != 32)) 306 return ENODEV; 307 } else 308 return ENODEV; 309 310 /* stop screen saver, etc */ 311 crit_enter(); 312 if ((error = sc_clean_up(scp))) { 313 crit_exit(); 314 return error; 315 } 316 317 if (sc_render_match(scp, scp->sc->adp->va_name, PIXEL_MODE) == NULL) { 318 crit_exit(); 319 return ENODEV; 320 } 321 322 #if 0 323 if (scp->tsw) 324 (*scp->tsw->te_term)(scp, scp->ts); 325 scp->tsw = NULL; 326 scp->ts = NULL; 327 #endif 328 329 /* set up scp */ 330 new_ysize = 0; 331 #ifndef SC_NO_HISTORY 332 if (scp->history != NULL) { 333 sc_hist_save(scp); 334 new_ysize = sc_vtb_rows(scp->history); 335 } 336 #endif 337 prev_ysize = scp->ysize; 338 scp->status |= (UNKNOWN_MODE | PIXEL_MODE | MOUSE_HIDDEN); 339 scp->status &= ~(GRAPHICS_MODE | MOUSE_VISIBLE); 340 scp->xsize = xsize; 341 scp->ysize = ysize; 342 scp->xoff = (scp->xpixel/8 - xsize)/2; 343 scp->yoff = (scp->ypixel/fontsize - ysize)/2; 344 scp->font = font; 345 scp->font_size = fontsize; 346 347 /* allocate buffers */ 348 sc_alloc_scr_buffer(scp, TRUE, TRUE); 349 sc_init_emulator(scp, NULL); 350 #ifndef SC_NO_CUTPASTE 351 sc_alloc_cut_buffer(scp, FALSE); 352 #endif 353 #ifndef SC_NO_HISTORY 354 sc_alloc_history_buffer(scp, new_ysize, prev_ysize, FALSE); 355 #endif 356 crit_exit(); 357 358 if (scp == scp->sc->cur_scp) { 359 sc_set_border(scp, scp->border); 360 sc_set_cursor_image(scp); 361 } 362 363 scp->status &= ~UNKNOWN_MODE; 364 365 if (tp == NULL) 366 return 0; 367 if (tp->t_winsize.ws_col != scp->xsize 368 || tp->t_winsize.ws_row != scp->ysize) { 369 tp->t_winsize.ws_col = scp->xsize; 370 tp->t_winsize.ws_row = scp->ysize; 371 pgsignal(tp->t_pgrp, SIGWINCH, 1); 372 } 373 374 return 0; 375 #endif /* SC_PIXEL_MODE */ 376 } 377 378 #define fb_ioctl(a, c, d) \ 379 (((a) == NULL) ? ENODEV : \ 380 (*vidsw[(a)->va_index]->ioctl)((a), (c), (caddr_t)(d))) 381 382 int 383 sc_vid_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag) 384 { 385 scr_stat *scp; 386 video_adapter_t *adp; 387 video_info_t info; 388 int error; 389 390 scp = SC_STAT(tp->t_dev); 391 if (scp == NULL) /* tp == SC_MOUSE */ 392 return ENOIOCTL; 393 adp = scp->sc->adp; 394 if (adp == NULL) /* shouldn't happen??? */ 395 return ENODEV; 396 397 switch (cmd) { 398 399 case CONS_CURRENTADP: /* get current adapter index */ 400 case FBIO_ADAPTER: 401 return fb_ioctl(adp, FBIO_ADAPTER, data); 402 403 case CONS_CURRENT: /* get current adapter type */ 404 case FBIO_ADPTYPE: 405 return fb_ioctl(adp, FBIO_ADPTYPE, data); 406 407 case CONS_ADPINFO: /* adapter information */ 408 case FBIO_ADPINFO: 409 if (((video_adapter_info_t *)data)->va_index >= 0) { 410 adp = vid_get_adapter(((video_adapter_info_t *)data)->va_index); 411 if (adp == NULL) 412 return ENODEV; 413 } 414 return fb_ioctl(adp, FBIO_ADPINFO, data); 415 416 case CONS_GET: /* get current video mode */ 417 case FBIO_GETMODE: 418 *(int *)data = scp->mode; 419 return 0; 420 421 #ifndef SC_NO_MODE_CHANGE 422 case CONS_SET: 423 case FBIO_SETMODE: /* set video mode */ 424 if (!(adp->va_flags & V_ADP_MODECHANGE)) 425 return ENODEV; 426 info.vi_mode = *(int *)data; 427 error = fb_ioctl(adp, FBIO_MODEINFO, &info); 428 if (error) 429 return error; 430 if (info.vi_flags & V_INFO_GRAPHICS) 431 return sc_set_graphics_mode(scp, tp, *(int *)data); 432 else 433 return sc_set_text_mode(scp, tp, *(int *)data, 0, 0, 0); 434 #endif /* SC_NO_MODE_CHANGE */ 435 436 case CONS_MODEINFO: /* get mode information */ 437 case FBIO_MODEINFO: 438 return fb_ioctl(adp, FBIO_MODEINFO, data); 439 440 case CONS_FINDMODE: /* find a matching video mode */ 441 case FBIO_FINDMODE: 442 return fb_ioctl(adp, FBIO_FINDMODE, data); 443 444 case CONS_SETWINORG: /* set frame buffer window origin */ 445 case FBIO_SETWINORG: 446 if (scp != scp->sc->cur_scp) 447 return ENODEV; /* XXX */ 448 return fb_ioctl(adp, FBIO_SETWINORG, data); 449 450 case FBIO_GETWINORG: /* get frame buffer window origin */ 451 if (scp != scp->sc->cur_scp) 452 return ENODEV; /* XXX */ 453 return fb_ioctl(adp, FBIO_GETWINORG, data); 454 455 case FBIO_GETDISPSTART: 456 case FBIO_SETDISPSTART: 457 case FBIO_GETLINEWIDTH: 458 case FBIO_SETLINEWIDTH: 459 if (scp != scp->sc->cur_scp) 460 return ENODEV; /* XXX */ 461 return fb_ioctl(adp, cmd, data); 462 463 case FBIO_GETPALETTE: 464 case FBIO_SETPALETTE: 465 case FBIOPUTCMAP: 466 case FBIOGETCMAP: 467 case FBIOGTYPE: 468 case FBIOGATTR: 469 case FBIOSVIDEO: 470 case FBIOGVIDEO: 471 case FBIOSCURSOR: 472 case FBIOGCURSOR: 473 case FBIOSCURPOS: 474 case FBIOGCURPOS: 475 case FBIOGCURMAX: 476 if (scp != scp->sc->cur_scp) 477 return ENODEV; /* XXX */ 478 return fb_ioctl(adp, cmd, data); 479 480 case KDSETMODE: /* set current mode of this (virtual) console */ 481 switch (*(int *)data) { 482 case KD_TEXT: /* switch to TEXT (known) mode */ 483 /* 484 * If scp->mode is of graphics modes, we don't know which 485 * text mode to switch back to... 486 */ 487 if (scp->status & GRAPHICS_MODE) 488 return EINVAL; 489 /* restore fonts & palette ! */ 490 #if 0 491 #ifndef SC_NO_FONT_LOADING 492 if (ISFONTAVAIL(adp->va_flags) 493 && !(scp->status & (GRAPHICS_MODE | PIXEL_MODE))) 494 /* 495 * FONT KLUDGE 496 * Don't load fonts for now... XXX 497 */ 498 if (scp->sc->fonts_loaded & FONT_8) 499 sc_load_font(scp, 0, 8, scp->sc->font_8, 0, 256); 500 if (scp->sc->fonts_loaded & FONT_14) 501 sc_load_font(scp, 0, 14, scp->sc->font_14, 0, 256); 502 if (scp->sc->fonts_loaded & FONT_16) 503 sc_load_font(scp, 0, 16, scp->sc->font_16, 0, 256); 504 } 505 #endif /* SC_NO_FONT_LOADING */ 506 #endif 507 508 #ifndef SC_NO_PALETTE_LOADING 509 load_palette(adp, scp->sc->palette); 510 #endif 511 512 /* move hardware cursor out of the way */ 513 (*vidsw[adp->va_index]->set_hw_cursor)(adp, -1, -1); 514 /* FALL THROUGH */ 515 516 case KD_TEXT1: /* switch to TEXT (known) mode */ 517 /* 518 * If scp->mode is of graphics modes, we don't know which 519 * text/pixel mode to switch back to... 520 */ 521 if (scp->status & GRAPHICS_MODE) 522 return EINVAL; 523 crit_enter(); 524 if ((error = sc_clean_up(scp))) { 525 crit_exit(); 526 return error; 527 } 528 scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN; 529 crit_exit(); 530 /* no restore fonts & palette */ 531 if (scp == scp->sc->cur_scp) 532 set_mode(scp); 533 sc_clear_screen(scp); 534 scp->status &= ~UNKNOWN_MODE; 535 return 0; 536 537 #ifdef SC_PIXEL_MODE 538 case KD_PIXEL: /* pixel (raster) display */ 539 if (!(scp->status & (GRAPHICS_MODE | PIXEL_MODE))) 540 return EINVAL; 541 if (scp->status & GRAPHICS_MODE) 542 return sc_set_pixel_mode(scp, tp, scp->xsize, scp->ysize, 543 scp->font_size); 544 crit_enter(); 545 if ((error = sc_clean_up(scp))) { 546 crit_exit(); 547 return error; 548 } 549 scp->status |= (UNKNOWN_MODE | PIXEL_MODE | MOUSE_HIDDEN); 550 crit_exit(); 551 if (scp == scp->sc->cur_scp) { 552 set_mode(scp); 553 #ifndef SC_NO_PALETTE_LOADING 554 load_palette(adp, scp->sc->palette); 555 #endif 556 } 557 sc_clear_screen(scp); 558 scp->status &= ~UNKNOWN_MODE; 559 return 0; 560 #endif /* SC_PIXEL_MODE */ 561 562 case KD_GRAPHICS: /* switch to GRAPHICS (unknown) mode */ 563 crit_enter(); 564 if ((error = sc_clean_up(scp))) { 565 crit_exit(); 566 return error; 567 } 568 scp->status |= UNKNOWN_MODE | MOUSE_HIDDEN; 569 crit_exit(); 570 return 0; 571 572 default: 573 return EINVAL; 574 } 575 /* NOT REACHED */ 576 577 #ifdef SC_PIXEL_MODE 578 case KDRASTER: /* set pixel (raster) display mode */ 579 if (ISUNKNOWNSC(scp) || ISTEXTSC(scp)) 580 return ENODEV; 581 return sc_set_pixel_mode(scp, tp, ((int *)data)[0], ((int *)data)[1], 582 ((int *)data)[2]); 583 #endif /* SC_PIXEL_MODE */ 584 585 case KDGETMODE: /* get current mode of this (virtual) console */ 586 /* 587 * From the user program's point of view, KD_PIXEL is the same 588 * as KD_TEXT... 589 */ 590 *data = ISGRAPHSC(scp) ? KD_GRAPHICS : KD_TEXT; 591 return 0; 592 593 case KDSBORDER: /* set border color of this (virtual) console */ 594 scp->border = *data; 595 if (scp == scp->sc->cur_scp) 596 sc_set_border(scp, scp->border); 597 return 0; 598 } 599 600 return ENOIOCTL; 601 } 602 603 static LIST_HEAD(, sc_renderer) sc_rndr_list = 604 LIST_HEAD_INITIALIZER(sc_rndr_list); 605 606 int 607 sc_render_add(sc_renderer_t *rndr) 608 { 609 LIST_INSERT_HEAD(&sc_rndr_list, rndr, link); 610 return 0; 611 } 612 613 int 614 sc_render_remove(sc_renderer_t *rndr) 615 { 616 /* 617 LIST_REMOVE(rndr, link); 618 */ 619 return EBUSY; /* XXX */ 620 } 621 622 sc_rndr_sw_t * 623 sc_render_match(scr_stat *scp, char *name, int mode) 624 { 625 const sc_renderer_t **list; 626 const sc_renderer_t *p; 627 628 if (!LIST_EMPTY(&sc_rndr_list)) { 629 LIST_FOREACH(p, &sc_rndr_list, link) { 630 if ((strcmp(p->name, name) == 0) 631 && (mode == p->mode)) { 632 scp->status &= 633 ~(VR_CURSOR_ON | VR_CURSOR_BLINK); 634 return p->rndrsw; 635 } 636 } 637 } else { 638 SET_FOREACH(list, scrndr_set) { 639 p = *list; 640 if ((strcmp(p->name, name) == 0) 641 && (mode == p->mode)) { 642 scp->status &= 643 ~(VR_CURSOR_ON | VR_CURSOR_BLINK); 644 return p->rndrsw; 645 } 646 } 647 } 648 649 return NULL; 650 } 651