1 /* $NetBSD: wsdisplay_vcons.c,v 1.15 2007/10/17 22:13:56 joerg Exp $ */ 2 3 /*- 4 * Copyright (c) 2005, 2006 Michael Lorenz 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 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the NetBSD 18 * Foundation, Inc. and its contributors. 19 * 4. Neither the name of The NetBSD Foundation nor the names of its 20 * contributors may be used to endorse or promote products derived 21 * from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 27 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 * POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: wsdisplay_vcons.c,v 1.15 2007/10/17 22:13:56 joerg Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/kernel.h> 42 #include <sys/buf.h> 43 #include <sys/device.h> 44 #include <sys/ioctl.h> 45 #include <sys/malloc.h> 46 #include <sys/mman.h> 47 #include <sys/tty.h> 48 #include <sys/conf.h> 49 #include <sys/proc.h> 50 #include <sys/kthread.h> 51 #include <sys/tprintf.h> 52 53 #include <dev/wscons/wsdisplayvar.h> 54 #include <dev/wscons/wsconsio.h> 55 #include <dev/wsfont/wsfont.h> 56 #include <dev/rasops/rasops.h> 57 58 #include <dev/wscons/wsdisplay_vconsvar.h> 59 60 #include "opt_wsemul.h" 61 #include "opt_wsdisplay_compat.h" 62 #include "opt_vcons.h" 63 64 static void vcons_dummy_init_screen(void *, struct vcons_screen *, int, 65 long *); 66 67 static int vcons_ioctl(void *, void *, u_long, void *, int, struct lwp *); 68 static int vcons_alloc_screen(void *, const struct wsscreen_descr *, void **, 69 int *, int *, long *); 70 static void vcons_free_screen(void *, void *); 71 static int vcons_show_screen(void *, void *, int, void (*)(void *, int, int), 72 void *); 73 74 #ifdef WSDISPLAY_SCROLLSUPPORT 75 static void vcons_scroll(void *, void *, int); 76 static void vcons_do_scroll(struct vcons_screen *); 77 #endif 78 79 static void vcons_do_switch(void *); 80 81 /* methods that work only on text buffers */ 82 static void vcons_copycols_buffer(void *, int, int, int, int); 83 static void vcons_erasecols_buffer(void *, int, int, int, long); 84 static void vcons_copyrows_buffer(void *, int, int, int); 85 static void vcons_eraserows_buffer(void *, int, int, long); 86 static void vcons_putchar_buffer(void *, int, int, u_int, long); 87 88 /* 89 * actual wrapper methods which call both the _buffer ones above and the 90 * driver supplied ones to do the drawing 91 */ 92 static void vcons_copycols(void *, int, int, int, int); 93 static void vcons_erasecols(void *, int, int, int, long); 94 static void vcons_copyrows(void *, int, int, int); 95 static void vcons_eraserows(void *, int, int, long); 96 static void vcons_putchar(void *, int, int, u_int, long); 97 static void vcons_cursor(void *, int, int, int); 98 99 /* support for reading/writing text buffers. For wsmoused */ 100 static int vcons_putwschar(struct vcons_screen *, struct wsdisplay_char *); 101 static int vcons_getwschar(struct vcons_screen *, struct wsdisplay_char *); 102 103 static void vcons_lock(struct vcons_screen *); 104 static void vcons_unlock(struct vcons_screen *); 105 106 #ifdef VCONS_SWITCH_ASYNC 107 static void vcons_kthread(void *); 108 #endif 109 110 int 111 vcons_init(struct vcons_data *vd, void *cookie, struct wsscreen_descr *def, 112 struct wsdisplay_accessops *ao) 113 { 114 115 /* zero out everything so we can rely on untouched fields being 0 */ 116 memset(vd, 0, sizeof(struct vcons_data)); 117 118 vd->cookie = cookie; 119 120 vd->init_screen = vcons_dummy_init_screen; 121 vd->show_screen_cb = NULL; 122 123 /* keep a copy of the accessops that we replace below with our 124 * own wrappers */ 125 vd->ioctl = ao->ioctl; 126 127 /* configure the accessops */ 128 ao->ioctl = vcons_ioctl; 129 ao->alloc_screen = vcons_alloc_screen; 130 ao->free_screen = vcons_free_screen; 131 ao->show_screen = vcons_show_screen; 132 #ifdef WSDISPLAY_SCROLLSUPPORT 133 ao->scroll = vcons_scroll; 134 #endif 135 136 LIST_INIT(&vd->screens); 137 vd->active = NULL; 138 vd->wanted = NULL; 139 vd->currenttype = def; 140 callout_init(&vd->switch_callout, 0); 141 callout_setfunc(&vd->switch_callout, vcons_do_switch, vd); 142 143 /* 144 * a lock to serialize access to the framebuffer. 145 * when switching screens we need to make sure there's no rasops 146 * operation in progress 147 */ 148 #ifdef DIAGNOSTIC 149 vd->switch_poll_count = 0; 150 #endif 151 #ifdef VCONS_SWITCH_ASYNC 152 kthread_create(PRI_NONE, 0, NULL, vcons_kthread, vd, 153 &vd->redraw_thread, "vcons_draw"); 154 #endif 155 return 0; 156 } 157 158 static void 159 vcons_lock(struct vcons_screen *scr) 160 { 161 #ifdef VCONS_PARANOIA 162 int s; 163 164 s = splhigh(); 165 #endif 166 SCREEN_BUSY(scr); 167 #ifdef VCONS_PARANOIA 168 splx(s); 169 #endif 170 } 171 172 static void 173 vcons_unlock(struct vcons_screen *scr) 174 { 175 #ifdef VCONS_PARANOIA 176 int s; 177 178 s = splhigh(); 179 #endif 180 SCREEN_IDLE(scr); 181 #ifdef VCONS_PARANOIA 182 splx(s); 183 #endif 184 #ifdef VCONS_SWITCH_ASYNC 185 wakeup(&scr->scr_vd->done_drawing); 186 #endif 187 } 188 189 static void 190 vcons_dummy_init_screen(void *cookie, 191 struct vcons_screen *scr, int exists, 192 long *defattr) 193 { 194 195 /* 196 * default init_screen() method. 197 * Needs to be overwritten so we bitch and whine in case anyone ends 198 * up in here. 199 */ 200 printf("vcons_init_screen: dummy function called. Your driver is " 201 "supposed to supply a replacement for proper operation\n"); 202 } 203 204 int 205 vcons_init_screen(struct vcons_data *vd, struct vcons_screen *scr, 206 int existing, long *defattr) 207 { 208 struct rasops_info *ri = &scr->scr_ri; 209 int cnt, i; 210 211 scr->scr_cookie = vd->cookie; 212 scr->scr_vd = scr->scr_origvd = vd; 213 scr->scr_busy = 0; 214 215 /* 216 * call the driver-supplied init_screen function which is expected 217 * to set up rasops_info, override cursor() and probably others 218 */ 219 vd->init_screen(vd->cookie, scr, existing, defattr); 220 221 /* 222 * save the non virtual console aware rasops and replace them with 223 * our wrappers 224 */ 225 vd->eraserows = ri->ri_ops.eraserows; 226 vd->copyrows = ri->ri_ops.copyrows; 227 vd->erasecols = ri->ri_ops.erasecols; 228 vd->copycols = ri->ri_ops.copycols; 229 vd->putchar = ri->ri_ops.putchar; 230 vd->cursor = ri->ri_ops.cursor; 231 232 ri->ri_ops.eraserows = vcons_eraserows; 233 ri->ri_ops.copyrows = vcons_copyrows; 234 ri->ri_ops.erasecols = vcons_erasecols; 235 ri->ri_ops.copycols = vcons_copycols; 236 ri->ri_ops.putchar = vcons_putchar; 237 ri->ri_ops.cursor = vcons_cursor; 238 ri->ri_hw = scr; 239 240 /* 241 * we allocate both chars and attributes in one chunk, attributes first 242 * because they have the (potentially) bigger alignment 243 */ 244 #ifdef WSDISPLAY_SCROLLSUPPORT 245 cnt = (ri->ri_rows + WSDISPLAY_SCROLLBACK_LINES) * ri->ri_cols; 246 scr->scr_lines_in_buffer = WSDISPLAY_SCROLLBACK_LINES; 247 scr->scr_current_line = 0; 248 scr->scr_line_wanted = 0; 249 scr->scr_offset_to_zero = ri->ri_cols * WSDISPLAY_SCROLLBACK_LINES; 250 scr->scr_current_offset = scr->scr_offset_to_zero; 251 #else 252 cnt = ri->ri_rows * ri->ri_cols; 253 #endif 254 scr->scr_attrs = (long *)malloc(cnt * (sizeof(long) + 255 sizeof(uint16_t)), M_DEVBUF, M_WAITOK); 256 if (scr->scr_attrs == NULL) 257 return ENOMEM; 258 259 scr->scr_chars = (uint16_t *)&scr->scr_attrs[cnt]; 260 261 ri->ri_ops.allocattr(ri, WS_DEFAULT_FG, WS_DEFAULT_BG, 0, defattr); 262 scr->scr_defattr = *defattr; 263 264 /* 265 * fill the attribute buffer with *defattr, chars with 0x20 266 * since we don't know if the driver tries to mimic firmware output or 267 * reset everything we do nothing to VRAM here, any driver that feels 268 * the need to clear screen or something will have to do it on its own 269 * Additional screens will start out in the background anyway so 270 * cleaning or not only really affects the initial console screen 271 */ 272 for (i = 0; i < cnt; i++) { 273 scr->scr_attrs[i] = *defattr; 274 scr->scr_chars[i] = 0x20; 275 } 276 277 if(vd->active == NULL) { 278 vd->active = scr; 279 SCREEN_VISIBLE(scr); 280 } 281 282 if (existing) { 283 SCREEN_VISIBLE(scr); 284 vd->active = scr; 285 } else { 286 SCREEN_INVISIBLE(scr); 287 } 288 289 LIST_INSERT_HEAD(&vd->screens, scr, next); 290 return 0; 291 } 292 293 static void 294 vcons_do_switch(void *arg) 295 { 296 struct vcons_data *vd = arg; 297 struct vcons_screen *scr, *oldscr; 298 299 scr = vd->wanted; 300 if (!scr) { 301 printf("vcons_switch_screen: disappeared\n"); 302 vd->switch_cb(vd->switch_cb_arg, EIO, 0); 303 return; 304 } 305 oldscr = vd->active; /* can be NULL! */ 306 307 /* 308 * if there's an old, visible screen we mark it invisible and wait 309 * until it's not busy so we can safely switch 310 */ 311 if (oldscr != NULL) { 312 SCREEN_INVISIBLE(oldscr); 313 if (SCREEN_IS_BUSY(oldscr)) { 314 callout_schedule(&vd->switch_callout, 1); 315 #ifdef DIAGNOSTIC 316 /* bitch if we wait too long */ 317 vd->switch_poll_count++; 318 if (vd->switch_poll_count > 100) { 319 panic("vcons: screen still busy"); 320 } 321 #endif 322 return; 323 } 324 /* invisible screen -> no visible cursor image */ 325 oldscr->scr_ri.ri_flg &= ~RI_CURSOR; 326 #ifdef DIAGNOSTIC 327 vd->switch_poll_count = 0; 328 #endif 329 } 330 331 if (scr == oldscr) 332 return; 333 334 #ifdef DIAGNOSTIC 335 if (SCREEN_IS_VISIBLE(scr)) 336 printf("vcons_switch_screen: already active"); 337 #endif 338 339 #ifdef notyet 340 if (vd->currenttype != type) { 341 vcons_set_screentype(vd, type); 342 vd->currenttype = type; 343 } 344 #endif 345 346 SCREEN_VISIBLE(scr); 347 vd->active = scr; 348 vd->wanted = NULL; 349 350 if (vd->show_screen_cb != NULL) 351 vd->show_screen_cb(scr); 352 353 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 354 vcons_redraw_screen(scr); 355 356 if (vd->switch_cb) 357 vd->switch_cb(vd->switch_cb_arg, 0, 0); 358 } 359 360 void 361 vcons_redraw_screen(struct vcons_screen *scr) 362 { 363 uint16_t *charptr = scr->scr_chars; 364 long *attrptr = scr->scr_attrs; 365 struct rasops_info *ri = &scr->scr_ri; 366 int i, j, offset; 367 368 vcons_lock(scr); 369 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 370 371 /* 372 * only clear the screen when RI_FULLCLEAR is set since we're 373 * going to overwrite every single character cell anyway 374 */ 375 if (ri->ri_flg & RI_FULLCLEAR) { 376 scr->scr_vd->eraserows(ri, 0, ri->ri_rows, 377 scr->scr_defattr); 378 } 379 380 /* redraw the screen */ 381 #ifdef WSDISPLAY_SCROLLSUPPORT 382 offset = scr->scr_current_offset; 383 #else 384 offset = 0; 385 #endif 386 for (i = 0; i < ri->ri_rows; i++) { 387 for (j = 0; j < ri->ri_cols; j++) { 388 /* 389 * no need to use the wrapper function - we 390 * don't change any characters or attributes 391 * and we already made sure the screen we're 392 * working on is visible 393 */ 394 scr->scr_vd->putchar(ri, i, j, 395 charptr[offset], attrptr[offset]); 396 offset++; 397 } 398 } 399 ri->ri_flg &= ~RI_CURSOR; 400 scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol); 401 } 402 vcons_unlock(scr); 403 } 404 405 static int 406 vcons_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 407 struct lwp *l) 408 { 409 struct vcons_data *vd = v; 410 int error; 411 412 switch (cmd) { 413 case WSDISPLAYIO_GETWSCHAR: 414 error = vcons_getwschar((struct vcons_screen *)vs, 415 (struct wsdisplay_char *)data); 416 break; 417 418 case WSDISPLAYIO_PUTWSCHAR: 419 error = vcons_putwschar((struct vcons_screen *)vs, 420 (struct wsdisplay_char *)data); 421 break; 422 423 default: 424 if (vd->ioctl != NULL) 425 error = (*vd->ioctl)(v, vs, cmd, data, flag, l); 426 else 427 error = EPASSTHROUGH; 428 } 429 430 return error; 431 } 432 433 static int 434 vcons_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep, 435 int *curxp, int *curyp, long *defattrp) 436 { 437 struct vcons_data *vd = v; 438 struct vcons_screen *scr; 439 int ret; 440 441 scr = malloc(sizeof(struct vcons_screen), M_DEVBUF, M_WAITOK | M_ZERO); 442 if (scr == NULL) 443 return ENOMEM; 444 445 scr->scr_flags = 0; 446 scr->scr_status = 0; 447 scr->scr_busy = 0; 448 scr->scr_type = type; 449 450 ret = vcons_init_screen(vd, scr, 0, defattrp); 451 if (ret != 0) { 452 free(scr, M_DEVBUF); 453 return ret; 454 } 455 456 if (vd->active == NULL) { 457 SCREEN_VISIBLE(scr); 458 vd->active = scr; 459 vd->currenttype = type; 460 } 461 462 *cookiep = scr; 463 *curxp = scr->scr_ri.ri_ccol; 464 *curyp = scr->scr_ri.ri_crow; 465 return 0; 466 } 467 468 static void 469 vcons_free_screen(void *v, void *cookie) 470 { 471 struct vcons_data *vd = v; 472 struct vcons_screen *scr = cookie; 473 474 vcons_lock(scr); 475 /* there should be no rasops activity here */ 476 477 LIST_REMOVE(scr, next); 478 479 if ((scr->scr_flags & VCONS_SCREEN_IS_STATIC) == 0) { 480 free(scr->scr_attrs, M_DEVBUF); 481 free(scr, M_DEVBUF); 482 } else { 483 /* 484 * maybe we should just restore the old rasops_info methods 485 * and free the character/attribute buffer here? 486 */ 487 #ifdef VCONS_DEBUG 488 panic("vcons_free_screen: console"); 489 #else 490 printf("vcons_free_screen: console\n"); 491 #endif 492 } 493 494 if (vd->active == scr) 495 vd->active = NULL; 496 } 497 498 static int 499 vcons_show_screen(void *v, void *cookie, int waitok, 500 void (*cb)(void *, int, int), void *cb_arg) 501 { 502 struct vcons_data *vd = v; 503 struct vcons_screen *scr; 504 505 scr = cookie; 506 if (scr == vd->active) 507 return 0; 508 509 vd->wanted = scr; 510 vd->switch_cb = cb; 511 vd->switch_cb_arg = cb_arg; 512 #ifdef VCONS_SWITCH_ASYNC 513 wakeup(&vd->start_drawing); 514 return EAGAIN; 515 #else 516 if (cb) { 517 callout_schedule(&vd->switch_callout, 0); 518 return EAGAIN; 519 } 520 521 vcons_do_switch(vd); 522 return 0; 523 #endif 524 } 525 526 /* wrappers for rasops_info methods */ 527 528 static void 529 vcons_copycols_buffer(void *cookie, int row, int srccol, int dstcol, int ncols) 530 { 531 struct rasops_info *ri = cookie; 532 struct vcons_screen *scr = ri->ri_hw; 533 int from = srccol + row * ri->ri_cols; 534 int to = dstcol + row * ri->ri_cols; 535 536 #ifdef WSDISPLAY_SCROLLSUPPORT 537 int offset; 538 offset = scr->scr_offset_to_zero; 539 540 memmove(&scr->scr_attrs[offset + to], &scr->scr_attrs[offset + from], 541 ncols * sizeof(long)); 542 memmove(&scr->scr_chars[offset + to], &scr->scr_chars[offset + from], 543 ncols * sizeof(uint16_t)); 544 #else 545 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 546 ncols * sizeof(long)); 547 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 548 ncols * sizeof(uint16_t)); 549 #endif 550 } 551 552 static void 553 vcons_copycols(void *cookie, int row, int srccol, int dstcol, int ncols) 554 { 555 struct rasops_info *ri = cookie; 556 struct vcons_screen *scr = ri->ri_hw; 557 558 vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols); 559 560 vcons_lock(scr); 561 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 562 scr->scr_vd->copycols(cookie, row, srccol, dstcol, ncols); 563 } 564 vcons_unlock(scr); 565 } 566 567 static void 568 vcons_erasecols_buffer(void *cookie, int row, int startcol, int ncols, long fillattr) 569 { 570 struct rasops_info *ri = cookie; 571 struct vcons_screen *scr = ri->ri_hw; 572 int start = startcol + row * ri->ri_cols; 573 int end = start + ncols, i; 574 575 #ifdef WSDISPLAY_SCROLLSUPPORT 576 int offset; 577 offset = scr->scr_offset_to_zero; 578 579 for (i = start; i < end; i++) { 580 scr->scr_attrs[offset + i] = fillattr; 581 scr->scr_chars[offset + i] = 0x20; 582 } 583 #else 584 for (i = start; i < end; i++) { 585 scr->scr_attrs[i] = fillattr; 586 scr->scr_chars[i] = 0x20; 587 } 588 #endif 589 } 590 591 static void 592 vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr) 593 { 594 struct rasops_info *ri = cookie; 595 struct vcons_screen *scr = ri->ri_hw; 596 597 vcons_erasecols_buffer(cookie, row, startcol, ncols, fillattr); 598 599 vcons_lock(scr); 600 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 601 scr->scr_vd->erasecols(cookie, row, startcol, ncols, 602 fillattr); 603 } 604 vcons_unlock(scr); 605 } 606 607 static void 608 vcons_copyrows_buffer(void *cookie, int srcrow, int dstrow, int nrows) 609 { 610 struct rasops_info *ri = cookie; 611 struct vcons_screen *scr = ri->ri_hw; 612 int from, to, len; 613 614 #ifdef WSDISPLAY_SCROLLSUPPORT 615 int offset; 616 offset = scr->scr_offset_to_zero; 617 618 /* do we need to scroll the back buffer? */ 619 if (dstrow == 0) { 620 from = ri->ri_cols * srcrow; 621 to = ri->ri_cols * dstrow; 622 623 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 624 scr->scr_offset_to_zero * sizeof(long)); 625 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 626 scr->scr_offset_to_zero * sizeof(uint16_t)); 627 } 628 from = ri->ri_cols * srcrow + offset; 629 to = ri->ri_cols * dstrow + offset; 630 len = ri->ri_cols * nrows; 631 632 #else 633 from = ri->ri_cols * srcrow; 634 to = ri->ri_cols * dstrow; 635 len = ri->ri_cols * nrows; 636 #endif 637 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 638 len * sizeof(long)); 639 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 640 len * sizeof(uint16_t)); 641 } 642 643 static void 644 vcons_copyrows(void *cookie, int srcrow, int dstrow, int nrows) 645 { 646 struct rasops_info *ri = cookie; 647 struct vcons_screen *scr = ri->ri_hw; 648 649 vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows); 650 651 vcons_lock(scr); 652 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 653 scr->scr_vd->copyrows(cookie, srcrow, dstrow, nrows); 654 } 655 vcons_unlock(scr); 656 } 657 658 static void 659 vcons_eraserows_buffer(void *cookie, int row, int nrows, long fillattr) 660 { 661 struct rasops_info *ri = cookie; 662 struct vcons_screen *scr = ri->ri_hw; 663 int start, end, i; 664 665 #ifdef WSDISPLAY_SCROLLSUPPORT 666 int offset; 667 offset = scr->scr_offset_to_zero; 668 669 start = ri->ri_cols * row + offset; 670 end = ri->ri_cols * (row + nrows) + offset; 671 #else 672 start = ri->ri_cols * row; 673 end = ri->ri_cols * (row + nrows); 674 #endif 675 676 for (i = start; i < end; i++) { 677 scr->scr_attrs[i] = fillattr; 678 scr->scr_chars[i] = 0x20; 679 } 680 } 681 682 static void 683 vcons_eraserows(void *cookie, int row, int nrows, long fillattr) 684 { 685 struct rasops_info *ri = cookie; 686 struct vcons_screen *scr = ri->ri_hw; 687 688 vcons_eraserows_buffer(cookie, row, nrows, fillattr); 689 690 vcons_lock(scr); 691 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 692 scr->scr_vd->eraserows(cookie, row, nrows, fillattr); 693 } 694 vcons_unlock(scr); 695 } 696 697 static void 698 vcons_putchar_buffer(void *cookie, int row, int col, u_int c, long attr) 699 { 700 struct rasops_info *ri = cookie; 701 struct vcons_screen *scr = ri->ri_hw; 702 int pos; 703 704 #ifdef WSDISPLAY_SCROLLSUPPORT 705 int offset; 706 offset = scr->scr_offset_to_zero; 707 708 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) && 709 (col < ri->ri_cols)) { 710 pos = col + row * ri->ri_cols; 711 scr->scr_attrs[pos + offset] = attr; 712 scr->scr_chars[pos + offset] = c; 713 } 714 #else 715 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) && 716 (col < ri->ri_cols)) { 717 pos = col + row * ri->ri_cols; 718 scr->scr_attrs[pos] = attr; 719 scr->scr_chars[pos] = c; 720 } 721 #endif 722 } 723 724 static void 725 vcons_putchar(void *cookie, int row, int col, u_int c, long attr) 726 { 727 struct rasops_info *ri = cookie; 728 struct vcons_screen *scr = ri->ri_hw; 729 730 vcons_putchar_buffer(cookie, row, col, c, attr); 731 732 vcons_lock(scr); 733 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 734 scr->scr_vd->putchar(cookie, row, col, c, attr); 735 } 736 vcons_unlock(scr); 737 } 738 739 static void 740 vcons_cursor(void *cookie, int on, int row, int col) 741 { 742 struct rasops_info *ri = cookie; 743 struct vcons_screen *scr = ri->ri_hw; 744 745 vcons_lock(scr); 746 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 747 scr->scr_vd->cursor(cookie, on, row, col); 748 } else { 749 scr->scr_ri.ri_crow = row; 750 scr->scr_ri.ri_ccol = col; 751 } 752 vcons_unlock(scr); 753 } 754 755 /* methods to read/write characters via ioctl() */ 756 757 static int 758 vcons_putwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 759 { 760 long attr; 761 struct rasops_info *ri; 762 763 KASSERT(scr != NULL && wsc != NULL); 764 765 ri = &scr->scr_ri; 766 767 if (__predict_false((unsigned int)wsc->col > ri->ri_cols || 768 (unsigned int)wsc->row > ri->ri_rows)) 769 return (EINVAL); 770 771 if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) && 772 (wsc->col < ri->ri_cols)) { 773 774 ri->ri_ops.allocattr(ri, wsc->foreground, wsc->background, 775 wsc->flags, &attr); 776 vcons_putchar(ri, wsc->row, wsc->col, wsc->letter, attr); 777 #ifdef VCONS_DEBUG 778 printf("vcons_putwschar(%d, %d, %x, %lx\n", wsc->row, wsc->col, 779 wsc->letter, attr); 780 #endif 781 return 0; 782 } else 783 return EINVAL; 784 } 785 786 static int 787 vcons_getwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 788 { 789 int offset; 790 long attr; 791 struct rasops_info *ri; 792 793 KASSERT(scr != NULL && wsc != NULL); 794 795 ri = &scr->scr_ri; 796 797 if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) && 798 (wsc->col < ri->ri_cols)) { 799 800 offset = ri->ri_cols * wsc->row + wsc->col; 801 #ifdef WSDISPLAY_SCROLLSUPPORT 802 offset += scr->scr_offset_to_zero; 803 #endif 804 wsc->letter = scr->scr_chars[offset]; 805 attr = scr->scr_attrs[offset]; 806 807 /* 808 * this is ugly. We need to break up an attribute into colours and 809 * flags but there's no rasops method to do that so we must rely on 810 * the 'canonical' encoding. 811 */ 812 #ifdef VCONS_DEBUG 813 printf("vcons_getwschar: %d, %d, %x, %lx\n", wsc->row, 814 wsc->col, wsc->letter, attr); 815 #endif 816 wsc->foreground = (attr >> 24) & 0xff; 817 wsc->background = (attr >> 16) & 0xff; 818 wsc->flags = attr & 0xff; 819 return 0; 820 } else 821 return EINVAL; 822 } 823 824 #ifdef WSDISPLAY_SCROLLSUPPORT 825 826 static void 827 vcons_scroll(void *cookie, void *vs, int where) 828 { 829 struct vcons_screen *scr = vs; 830 831 if (where == 0) { 832 scr->scr_line_wanted = 0; 833 } else { 834 scr->scr_line_wanted = scr->scr_line_wanted - where; 835 if (scr->scr_line_wanted < 0) 836 scr->scr_line_wanted = 0; 837 if (scr->scr_line_wanted > scr->scr_lines_in_buffer) 838 scr->scr_line_wanted = scr->scr_lines_in_buffer; 839 } 840 841 if (scr->scr_line_wanted != scr->scr_current_line) { 842 843 vcons_do_scroll(scr); 844 } 845 } 846 847 static void 848 vcons_do_scroll(struct vcons_screen *scr) 849 { 850 int dist, from, to, num; 851 int r_offset, r_start; 852 int i, j; 853 854 if (scr->scr_line_wanted == scr->scr_current_line) 855 return; 856 dist = scr->scr_line_wanted - scr->scr_current_line; 857 scr->scr_current_line = scr->scr_line_wanted; 858 scr->scr_current_offset = scr->scr_ri.ri_cols * 859 (scr->scr_lines_in_buffer - scr->scr_current_line); 860 if (abs(dist) >= scr->scr_ri.ri_rows) { 861 vcons_redraw_screen(scr); 862 return; 863 } 864 /* scroll and redraw only what we really have to */ 865 if (dist > 0) { 866 /* we scroll down */ 867 from = 0; 868 to = dist; 869 num = scr->scr_ri.ri_rows - dist; 870 /* now the redraw parameters */ 871 r_offset = scr->scr_current_offset; 872 r_start = 0; 873 } else { 874 /* scrolling up */ 875 to = 0; 876 from = -dist; 877 num = scr->scr_ri.ri_rows + dist; 878 r_offset = scr->scr_current_offset + num * scr->scr_ri.ri_cols; 879 r_start = num; 880 } 881 scr->scr_vd->copyrows(scr, from, to, num); 882 for (i = 0; i < abs(dist); i++) { 883 for (j = 0; j < scr->scr_ri.ri_cols; j++) { 884 scr->scr_vd->putchar(scr, i + r_start, j, 885 scr->scr_chars[r_offset], 886 scr->scr_attrs[r_offset]); 887 r_offset++; 888 } 889 } 890 891 if (scr->scr_line_wanted == 0) { 892 /* this was a reset - need to draw the cursor */ 893 scr->scr_ri.ri_flg &= ~RI_CURSOR; 894 scr->scr_vd->cursor(scr, 1, scr->scr_ri.ri_crow, 895 scr->scr_ri.ri_ccol); 896 } 897 } 898 899 #endif /* WSDISPLAY_SCROLLSUPPORT */ 900 901 /* async drawing using a kernel thread */ 902 903 #ifdef VCONS_SWITCH_ASYNC 904 static void 905 vcons_kthread(void *cookie) 906 { 907 struct vcons_data *vd = cookie; 908 struct vcons_screen *scr; 909 int sec = hz; 910 911 while (1) { 912 913 tsleep(&vd->start_drawing, 0, "vc_idle", sec); 914 if ((vd->wanted != vd->active) && (vd->wanted != NULL)) { 915 /* 916 * we need to switch screens 917 * so first we mark the active screen as invisible 918 * and wait until it's idle 919 */ 920 scr = vd->wanted; 921 SCREEN_INVISIBLE(vd->active); 922 while (SCREEN_IS_BUSY(vd->active)) { 923 924 tsleep(&vd->done_drawing, 0, "vc_wait", sec); 925 } 926 /* 927 * now we mark the wanted screen busy so nobody 928 * messes around while we redraw it 929 */ 930 vd->active = scr; 931 vd->wanted = NULL; 932 SCREEN_VISIBLE(scr); 933 934 if (vd->show_screen_cb != NULL) 935 vd->show_screen_cb(scr); 936 937 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 938 vcons_redraw_screen(scr); 939 940 if (vd->switch_cb) 941 vd->switch_cb(vd->switch_cb_arg, 0, 0); 942 } 943 } 944 } 945 #endif /* VCONS_SWITCH_ASYNC */ 946