1 /* $NetBSD: wsdisplay_vcons.c,v 1.39 2018/12/01 00:28:45 msaitoh 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: wsdisplay_vcons.c,v 1.39 2018/12/01 00:28:45 msaitoh Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/buf.h> 36 #include <sys/device.h> 37 #include <sys/ioctl.h> 38 #include <sys/malloc.h> 39 #include <sys/mman.h> 40 #include <sys/tty.h> 41 #include <sys/conf.h> 42 #include <sys/proc.h> 43 #include <sys/kthread.h> 44 #include <sys/tprintf.h> 45 #include <sys/atomic.h> 46 47 #include <dev/wscons/wsdisplayvar.h> 48 #include <dev/wscons/wsconsio.h> 49 #include <dev/wsfont/wsfont.h> 50 #include <dev/rasops/rasops.h> 51 52 #include <dev/wscons/wsdisplay_vconsvar.h> 53 54 #ifdef _KERNEL_OPT 55 #include "opt_wsemul.h" 56 #include "opt_wsdisplay_compat.h" 57 #include "opt_vcons.h" 58 #endif 59 60 #ifdef VCONS_DEBUG 61 #define DPRINTF printf 62 #else 63 #define DPRINTF if (0) printf 64 #endif 65 66 static void vcons_dummy_init_screen(void *, struct vcons_screen *, int, 67 long *); 68 69 static int vcons_ioctl(void *, void *, u_long, void *, int, struct lwp *); 70 static int vcons_alloc_screen(void *, const struct wsscreen_descr *, void **, 71 int *, int *, long *); 72 static void vcons_free_screen(void *, void *); 73 static int vcons_show_screen(void *, void *, int, void (*)(void *, int, int), 74 void *); 75 static int vcons_load_font(void *, void *, struct wsdisplay_font *); 76 77 #ifdef WSDISPLAY_SCROLLSUPPORT 78 static void vcons_scroll(void *, void *, int); 79 static void vcons_do_scroll(struct vcons_screen *); 80 #endif 81 82 static void vcons_do_switch(void *); 83 84 /* methods that work only on text buffers */ 85 static void vcons_copycols_buffer(void *, int, int, int, int); 86 static void vcons_erasecols_buffer(void *, int, int, int, long); 87 static void vcons_copyrows_buffer(void *, int, int, int); 88 static void vcons_eraserows_buffer(void *, int, int, long); 89 static void vcons_putchar_buffer(void *, int, int, u_int, long); 90 91 /* 92 * actual wrapper methods which call both the _buffer ones above and the 93 * driver supplied ones to do the drawing 94 */ 95 static void vcons_copycols(void *, int, int, int, int); 96 static void vcons_erasecols(void *, int, int, int, long); 97 static void vcons_copyrows(void *, int, int, int); 98 static void vcons_eraserows(void *, int, int, long); 99 static void vcons_putchar(void *, int, int, u_int, long); 100 #ifdef VCONS_DRAW_INTR 101 static void vcons_erasecols_cached(void *, int, int, int, long); 102 static void vcons_eraserows_cached(void *, int, int, long); 103 static void vcons_putchar_cached(void *, int, int, u_int, long); 104 #endif 105 static void vcons_cursor(void *, int, int, int); 106 107 /* 108 * methods that avoid framebuffer reads 109 */ 110 static void vcons_copycols_noread(void *, int, int, int, int); 111 static void vcons_copyrows_noread(void *, int, int, int); 112 113 114 /* support for reading/writing text buffers. For wsmoused */ 115 static int vcons_putwschar(struct vcons_screen *, struct wsdisplay_char *); 116 static int vcons_getwschar(struct vcons_screen *, struct wsdisplay_char *); 117 118 static void vcons_lock(struct vcons_screen *); 119 static void vcons_unlock(struct vcons_screen *); 120 121 #ifdef VCONS_DRAW_INTR 122 static void vcons_intr(void *); 123 static void vcons_softintr(void *); 124 static void vcons_init_thread(void *); 125 #endif 126 127 int 128 vcons_init(struct vcons_data *vd, void *cookie, struct wsscreen_descr *def, 129 struct wsdisplay_accessops *ao) 130 { 131 132 /* zero out everything so we can rely on untouched fields being 0 */ 133 memset(vd, 0, sizeof(struct vcons_data)); 134 135 vd->cookie = cookie; 136 137 vd->init_screen = vcons_dummy_init_screen; 138 vd->show_screen_cb = NULL; 139 140 /* keep a copy of the accessops that we replace below with our 141 * own wrappers */ 142 vd->ioctl = ao->ioctl; 143 144 /* configure the accessops */ 145 ao->ioctl = vcons_ioctl; 146 ao->alloc_screen = vcons_alloc_screen; 147 ao->free_screen = vcons_free_screen; 148 ao->show_screen = vcons_show_screen; 149 ao->load_font = vcons_load_font; 150 #ifdef WSDISPLAY_SCROLLSUPPORT 151 ao->scroll = vcons_scroll; 152 #endif 153 154 LIST_INIT(&vd->screens); 155 vd->active = NULL; 156 vd->wanted = NULL; 157 vd->currenttype = def; 158 vd->defaulttype = def; 159 callout_init(&vd->switch_callout, 0); 160 callout_setfunc(&vd->switch_callout, vcons_do_switch, vd); 161 #ifdef VCONS_DRAW_INTR 162 vd->cells = 0; 163 vd->attrs = NULL; 164 vd->chars = NULL; 165 vd->cursor_offset = -1; 166 #endif 167 168 /* 169 * a lock to serialize access to the framebuffer. 170 * when switching screens we need to make sure there's no rasops 171 * operation in progress 172 */ 173 #ifdef DIAGNOSTIC 174 vd->switch_poll_count = 0; 175 #endif 176 #ifdef VCONS_DRAW_INTR 177 vd->intr_softint = softint_establish(SOFTINT_SERIAL, 178 vcons_softintr, vd); 179 callout_init(&vd->intr, 0); 180 callout_setfunc(&vd->intr, vcons_intr, vd); 181 vd->intr_valid = 1; 182 183 if (kthread_create(PRI_NONE, 0, NULL, vcons_init_thread, vd, NULL, 184 "vcons_init") != 0) { 185 printf("%s: unable to create thread.\n", __func__); 186 return -1; 187 } 188 #endif 189 return 0; 190 } 191 192 static void 193 vcons_lock(struct vcons_screen *scr) 194 { 195 #ifdef VCONS_PARANOIA 196 int s; 197 198 s = splhigh(); 199 #endif 200 SCREEN_BUSY(scr); 201 #ifdef VCONS_PARANOIA 202 splx(s); 203 #endif 204 } 205 206 static void 207 vcons_unlock(struct vcons_screen *scr) 208 { 209 #ifdef VCONS_PARANOIA 210 int s; 211 212 s = splhigh(); 213 #endif 214 SCREEN_IDLE(scr); 215 #ifdef VCONS_PARANOIA 216 splx(s); 217 #endif 218 } 219 220 static void 221 vcons_dummy_init_screen(void *cookie, 222 struct vcons_screen *scr, int exists, 223 long *defattr) 224 { 225 226 /* 227 * default init_screen() method. 228 * Needs to be overwritten so we bitch and whine in case anyone ends 229 * up in here. 230 */ 231 printf("vcons_init_screen: dummy function called. Your driver is " 232 "supposed to supply a replacement for proper operation\n"); 233 } 234 235 static int 236 vcons_alloc_buffers(struct vcons_data *vd, struct vcons_screen *scr) 237 { 238 struct rasops_info *ri = &scr->scr_ri; 239 int cnt, i; 240 #ifdef VCONS_DRAW_INTR 241 int size; 242 #endif 243 244 /* 245 * we allocate both chars and attributes in one chunk, attributes first 246 * because they have the (potentially) bigger alignment 247 */ 248 #ifdef WSDISPLAY_SCROLLSUPPORT 249 cnt = (ri->ri_rows + WSDISPLAY_SCROLLBACK_LINES) * ri->ri_cols; 250 scr->scr_lines_in_buffer = WSDISPLAY_SCROLLBACK_LINES; 251 scr->scr_current_line = 0; 252 scr->scr_line_wanted = 0; 253 scr->scr_offset_to_zero = ri->ri_cols * WSDISPLAY_SCROLLBACK_LINES; 254 scr->scr_current_offset = scr->scr_offset_to_zero; 255 #else 256 cnt = ri->ri_rows * ri->ri_cols; 257 #endif 258 scr->scr_attrs = malloc(cnt * (sizeof(long) + 259 sizeof(uint32_t)), M_DEVBUF, M_WAITOK); 260 if (scr->scr_attrs == NULL) 261 return ENOMEM; 262 263 scr->scr_chars = (uint32_t *)&scr->scr_attrs[cnt]; 264 265 /* 266 * fill the attribute buffer with *defattr, chars with 0x20 267 * since we don't know if the driver tries to mimic firmware output or 268 * reset everything we do nothing to VRAM here, any driver that feels 269 * the need to clear screen or something will have to do it on its own 270 * Additional screens will start out in the background anyway so 271 * cleaning or not only really affects the initial console screen 272 */ 273 for (i = 0; i < cnt; i++) { 274 scr->scr_attrs[i] = scr->scr_defattr; 275 scr->scr_chars[i] = 0x20; 276 } 277 278 #ifdef VCONS_DRAW_INTR 279 size = ri->ri_cols * ri->ri_rows; 280 if (size > vd->cells) { 281 if (vd->chars != NULL) free(vd->chars, M_DEVBUF); 282 if (vd->attrs != NULL) free(vd->attrs, M_DEVBUF); 283 vd->cells = size; 284 vd->chars = malloc(size * sizeof(uint32_t), M_DEVBUF, 285 M_WAITOK|M_ZERO); 286 vd->attrs = malloc(size * sizeof(long), M_DEVBUF, 287 M_WAITOK|M_ZERO); 288 vcons_invalidate_cache(vd); 289 } else if (SCREEN_IS_VISIBLE(scr)) 290 vcons_invalidate_cache(vd); 291 #endif 292 return 0; 293 } 294 295 int 296 vcons_init_screen(struct vcons_data *vd, struct vcons_screen *scr, 297 int existing, long *defattr) 298 { 299 struct rasops_info *ri = &scr->scr_ri; 300 int i; 301 302 scr->scr_cookie = vd->cookie; 303 scr->scr_vd = scr->scr_origvd = vd; 304 scr->scr_busy = 0; 305 if (scr->scr_type == NULL) 306 scr->scr_type = vd->defaulttype; 307 308 /* 309 * call the driver-supplied init_screen function which is expected 310 * to set up rasops_info, override cursor() and probably others 311 */ 312 vd->init_screen(vd->cookie, scr, existing, defattr); 313 314 /* 315 * save the non virtual console aware rasops and replace them with 316 * our wrappers 317 */ 318 vd->eraserows = ri->ri_ops.eraserows; 319 vd->erasecols = ri->ri_ops.erasecols; 320 scr->putchar = ri->ri_ops.putchar; 321 vd->cursor = ri->ri_ops.cursor; 322 323 if (scr->scr_flags & VCONS_NO_COPYCOLS) { 324 vd->copycols = vcons_copycols_noread; 325 } else { 326 vd->copycols = ri->ri_ops.copycols; 327 } 328 329 if (scr->scr_flags & VCONS_NO_COPYROWS) { 330 vd->copyrows = vcons_copyrows_noread; 331 } else { 332 vd->copyrows = ri->ri_ops.copyrows; 333 } 334 335 ri->ri_ops.eraserows = vcons_eraserows; 336 ri->ri_ops.erasecols = vcons_erasecols; 337 ri->ri_ops.putchar = vcons_putchar; 338 ri->ri_ops.cursor = vcons_cursor; 339 ri->ri_ops.copycols = vcons_copycols; 340 ri->ri_ops.copyrows = vcons_copyrows; 341 342 343 ri->ri_hw = scr; 344 345 i = ri->ri_ops.allocattr(ri, WS_DEFAULT_FG, WS_DEFAULT_BG, 0, defattr); 346 if (i != 0) { 347 #ifdef DIAGNOSTIC 348 printf("vcons: error allocating attribute %d\n", i); 349 #endif 350 scr->scr_defattr = 0; 351 } else 352 scr->scr_defattr = *defattr; 353 354 vcons_alloc_buffers(vd, scr); 355 356 if(vd->active == NULL) { 357 vd->active = scr; 358 SCREEN_VISIBLE(scr); 359 } 360 361 if (existing) { 362 SCREEN_VISIBLE(scr); 363 vd->active = scr; 364 } else { 365 SCREEN_INVISIBLE(scr); 366 } 367 368 LIST_INSERT_HEAD(&vd->screens, scr, next); 369 return 0; 370 } 371 372 static int 373 vcons_load_font(void *v, void *cookie, struct wsdisplay_font *f) 374 { 375 struct vcons_data *vd = v; 376 struct vcons_screen *scr = cookie; 377 struct rasops_info *ri; 378 struct wsdisplay_font *font; 379 int flags = WSFONT_FIND_BITMAP, fcookie; 380 381 /* see if we're asked to add a font or use it */ 382 if (scr == NULL) 383 return 0; 384 385 ri = &scr->scr_ri; 386 387 /* see if the driver knows how to handle multiple fonts */ 388 if ((scr->scr_flags & VCONS_LOADFONT) == 0) { 389 return EOPNOTSUPP; 390 } 391 392 /* now see what fonts we can use */ 393 if (ri->ri_flg & RI_ENABLE_ALPHA) { 394 flags |= WSFONT_FIND_ALPHA; 395 } 396 397 fcookie = wsfont_find(f->name, 0, 0, 0, 0, 0, flags); 398 if (fcookie == -1) 399 return EINVAL; 400 401 wsfont_lock(fcookie, &font); 402 if (font == NULL) 403 return EINVAL; 404 405 /* ok, we got a font. Now clear the screen with the old parameters */ 406 if (SCREEN_IS_VISIBLE(scr)) 407 vd->eraserows(ri, 0, ri->ri_rows, scr->scr_defattr); 408 409 vcons_lock(vd->active); 410 #ifdef VCONS_DRAW_INTR 411 callout_halt(&vd->intr, NULL); 412 #endif 413 /* set the new font and re-initialize things */ 414 ri->ri_font = font; 415 wsfont_unlock(ri->ri_wsfcookie); 416 ri->ri_wsfcookie = fcookie; 417 418 vd->init_screen(vd->cookie, scr, 1, &scr->scr_defattr); 419 DPRINTF("caps %x %x\n", scr->scr_type->capabilities, ri->ri_caps); 420 if (scr->scr_type->capabilities & WSSCREEN_RESIZE) { 421 scr->scr_type->nrows = ri->ri_rows; 422 scr->scr_type->ncols = ri->ri_cols; 423 DPRINTF("new size %d %d\n", ri->ri_rows, ri->ri_cols); 424 } 425 426 427 /* now, throw the old buffers away */ 428 if (scr->scr_attrs) 429 free(scr->scr_attrs, M_DEVBUF); 430 /* allocate new buffers */ 431 vcons_alloc_buffers(vd, scr); 432 433 /* save the potentially changed putchar */ 434 scr->putchar = ri->ri_ops.putchar; 435 436 /* and put our wrappers back */ 437 ri->ri_ops.eraserows = vcons_eraserows; 438 ri->ri_ops.erasecols = vcons_erasecols; 439 ri->ri_ops.putchar = vcons_putchar; 440 ri->ri_ops.cursor = vcons_cursor; 441 ri->ri_ops.copycols = vcons_copycols; 442 ri->ri_ops.copyrows = vcons_copyrows; 443 vcons_unlock(vd->active); 444 445 /* notify things that we're about to redraw */ 446 if (vd->show_screen_cb != NULL) 447 vd->show_screen_cb(scr, vd->show_screen_cookie); 448 449 #ifdef VCONS_DRAW_INTR 450 /* 451 * XXX 452 * Something(tm) craps all over VRAM somewhere up there if we're 453 * using VCONS_DRAW_INTR. Until I figure out what causes it, just 454 * redraw the screen for now. 455 */ 456 vcons_redraw_screen(vd->active); 457 callout_schedule(&vd->intr, mstohz(33)); 458 #endif 459 /* no need to draw anything, wsdisplay should reset the terminal */ 460 461 return 0; 462 } 463 464 static void 465 vcons_do_switch(void *arg) 466 { 467 struct vcons_data *vd = arg; 468 struct vcons_screen *scr, *oldscr; 469 470 scr = vd->wanted; 471 if (!scr) { 472 printf("vcons_switch_screen: disappeared\n"); 473 vd->switch_cb(vd->switch_cb_arg, EIO, 0); 474 return; 475 } 476 oldscr = vd->active; /* can be NULL! */ 477 478 /* 479 * if there's an old, visible screen we mark it invisible and wait 480 * until it's not busy so we can safely switch 481 */ 482 if (oldscr != NULL) { 483 SCREEN_INVISIBLE(oldscr); 484 if (SCREEN_IS_BUSY(oldscr)) { 485 callout_schedule(&vd->switch_callout, 1); 486 #ifdef DIAGNOSTIC 487 /* bitch if we wait too long */ 488 vd->switch_poll_count++; 489 if (vd->switch_poll_count > 100) { 490 panic("vcons: screen still busy"); 491 } 492 #endif 493 return; 494 } 495 /* invisible screen -> no visible cursor image */ 496 oldscr->scr_ri.ri_flg &= ~RI_CURSOR; 497 #ifdef DIAGNOSTIC 498 vd->switch_poll_count = 0; 499 #endif 500 } 501 502 if (scr == oldscr) 503 return; 504 505 #ifdef DIAGNOSTIC 506 if (SCREEN_IS_VISIBLE(scr)) 507 printf("vcons_switch_screen: already active"); 508 #endif 509 510 #ifdef notyet 511 if (vd->currenttype != type) { 512 vcons_set_screentype(vd, type); 513 vd->currenttype = type; 514 } 515 #endif 516 517 SCREEN_VISIBLE(scr); 518 vd->active = scr; 519 vd->wanted = NULL; 520 521 #ifdef VCONS_DRAW_INTR 522 vcons_invalidate_cache(vd); 523 #endif 524 525 if (vd->show_screen_cb != NULL) 526 vd->show_screen_cb(scr, vd->show_screen_cookie); 527 528 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 529 vcons_redraw_screen(scr); 530 531 if (vd->switch_cb) 532 vd->switch_cb(vd->switch_cb_arg, 0, 0); 533 } 534 535 void 536 vcons_redraw_screen(struct vcons_screen *scr) 537 { 538 uint32_t *charptr = scr->scr_chars, c; 539 long *attrptr = scr->scr_attrs, a, last_a = 0, mask, cmp, acmp; 540 struct rasops_info *ri = &scr->scr_ri; 541 struct vcons_data *vd = scr->scr_vd; 542 int i, j, offset, boffset = 0, start = -1; 543 544 mask = 0x00ff00ff; /* background and flags */ 545 cmp = 0xffffffff; /* never match anything */ 546 vcons_lock(scr); 547 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 548 549 /* 550 * only clear the screen when RI_FULLCLEAR is set since we're 551 * going to overwrite every single character cell anyway 552 */ 553 if (ri->ri_flg & RI_FULLCLEAR) { 554 vd->eraserows(ri, 0, ri->ri_rows, 555 scr->scr_defattr); 556 cmp = scr->scr_defattr & mask; 557 } 558 559 /* redraw the screen */ 560 #ifdef WSDISPLAY_SCROLLSUPPORT 561 offset = scr->scr_current_offset; 562 #else 563 offset = 0; 564 #endif 565 for (i = 0; i < ri->ri_rows; i++) { 566 start = -1; 567 for (j = 0; j < ri->ri_cols; j++) { 568 /* 569 * no need to use the wrapper function - we 570 * don't change any characters or attributes 571 * and we already made sure the screen we're 572 * working on is visible 573 */ 574 c = charptr[offset]; 575 a = attrptr[offset]; 576 acmp = a & mask; 577 if (c == ' ') { 578 /* 579 * if we already erased the background 580 * and this blank uses the same colour 581 * and flags we don't need to do 582 * anything here 583 */ 584 if (acmp == cmp) 585 goto next; 586 /* 587 * see if we can optimize things a 588 * little bit by drawing stretches of 589 * blanks using erasecols 590 */ 591 592 if (start == -1) { 593 start = j; 594 last_a = acmp; 595 } else if (acmp != last_a) { 596 /* 597 * different attr, need to 598 * flush & restart 599 */ 600 vd->erasecols(ri, i, start, 601 j - start, last_a); 602 start = j; 603 last_a = acmp; 604 } 605 } else { 606 if (start != -1) { 607 vd->erasecols(ri, i, start, 608 j - start, last_a); 609 start = -1; 610 } 611 612 scr->putchar(ri, i, j, c, a); 613 } 614 next: 615 #ifdef VCONS_DRAW_INTR 616 vd->chars[boffset] = charptr[offset]; 617 vd->attrs[boffset] = attrptr[offset]; 618 #endif 619 offset++; 620 boffset++; 621 } 622 /* end of the line - draw all defered blanks, if any */ 623 if (start != -1) { 624 vd->erasecols(ri, i, start, j - start, last_a); 625 } 626 } 627 ri->ri_flg &= ~RI_CURSOR; 628 scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol); 629 #ifdef VCONS_DRAW_INTR 630 vd->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol; 631 #endif 632 } 633 vcons_unlock(scr); 634 } 635 636 #ifdef VCONS_DRAW_INTR 637 void 638 vcons_update_screen(struct vcons_screen *scr) 639 { 640 uint32_t *charptr = scr->scr_chars; 641 long *attrptr = scr->scr_attrs; 642 struct rasops_info *ri = &scr->scr_ri; 643 struct vcons_data *vd = scr->scr_vd; 644 int i, j, offset, boffset = 0; 645 646 vcons_lock(scr); 647 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 648 649 /* redraw the screen */ 650 #ifdef WSDISPLAY_SCROLLSUPPORT 651 offset = scr->scr_current_offset; 652 #else 653 offset = 0; 654 #endif 655 /* 656 * we mark the character cell occupied by the cursor as dirty 657 * so we don't have to deal with it 658 * notice that this isn't necessarily the position where rasops 659 * thinks it is, just where we drew it the last time 660 */ 661 if (vd->cursor_offset >= 0) 662 vd->attrs[vd->cursor_offset] = 0xffffffff; 663 664 for (i = 0; i < ri->ri_rows; i++) { 665 for (j = 0; j < ri->ri_cols; j++) { 666 /* 667 * no need to use the wrapper function - we 668 * don't change any characters or attributes 669 * and we already made sure the screen we're 670 * working on is visible 671 */ 672 if ((vd->chars[boffset] != charptr[offset]) || 673 (vd->attrs[boffset] != attrptr[offset])) { 674 scr->putchar(ri, i, j, 675 charptr[offset], attrptr[offset]); 676 vd->chars[boffset] = charptr[offset]; 677 vd->attrs[boffset] = attrptr[offset]; 678 } 679 offset++; 680 boffset++; 681 } 682 } 683 ri->ri_flg &= ~RI_CURSOR; 684 scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol); 685 vd->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol; 686 } 687 vcons_unlock(scr); 688 } 689 #endif 690 691 static int 692 vcons_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 693 struct lwp *l) 694 { 695 struct vcons_data *vd = v; 696 int error = 0; 697 698 699 switch (cmd) { 700 case WSDISPLAYIO_GETWSCHAR: 701 error = vcons_getwschar((struct vcons_screen *)vs, 702 (struct wsdisplay_char *)data); 703 break; 704 705 case WSDISPLAYIO_PUTWSCHAR: 706 error = vcons_putwschar((struct vcons_screen *)vs, 707 (struct wsdisplay_char *)data); 708 break; 709 710 case WSDISPLAYIO_SET_POLLING: { 711 int poll = *(int *)data; 712 713 /* first call the driver's ioctl handler */ 714 if (vd->ioctl != NULL) 715 error = (*vd->ioctl)(v, vs, cmd, data, flag, l); 716 if (poll) { 717 vcons_enable_polling(vd); 718 vcons_hard_switch(LIST_FIRST(&vd->screens)); 719 } else 720 vcons_disable_polling(vd); 721 } 722 break; 723 724 default: 725 if (vd->ioctl != NULL) 726 error = (*vd->ioctl)(v, vs, cmd, data, flag, l); 727 else 728 error = EPASSTHROUGH; 729 } 730 731 return error; 732 } 733 734 static int 735 vcons_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep, 736 int *curxp, int *curyp, long *defattrp) 737 { 738 struct vcons_data *vd = v; 739 struct vcons_screen *scr; 740 struct wsscreen_descr *t = __UNCONST(type); 741 int ret; 742 743 scr = malloc(sizeof(struct vcons_screen), M_DEVBUF, M_WAITOK | M_ZERO); 744 if (scr == NULL) 745 return ENOMEM; 746 747 scr->scr_flags = 0; 748 scr->scr_status = 0; 749 scr->scr_busy = 0; 750 scr->scr_type = __UNCONST(type); 751 752 ret = vcons_init_screen(vd, scr, 0, defattrp); 753 if (ret != 0) { 754 free(scr, M_DEVBUF); 755 return ret; 756 } 757 if (t->capabilities & WSSCREEN_RESIZE) { 758 t->nrows = scr->scr_ri.ri_rows; 759 t->ncols = scr->scr_ri.ri_cols; 760 } 761 762 if (vd->active == NULL) { 763 SCREEN_VISIBLE(scr); 764 vd->active = scr; 765 vd->currenttype = type; 766 } 767 768 *cookiep = scr; 769 *curxp = scr->scr_ri.ri_ccol; 770 *curyp = scr->scr_ri.ri_crow; 771 return 0; 772 } 773 774 static void 775 vcons_free_screen(void *v, void *cookie) 776 { 777 struct vcons_data *vd = v; 778 struct vcons_screen *scr = cookie; 779 780 vcons_lock(scr); 781 /* there should be no rasops activity here */ 782 783 LIST_REMOVE(scr, next); 784 785 if ((scr->scr_flags & VCONS_SCREEN_IS_STATIC) == 0) { 786 free(scr->scr_attrs, M_DEVBUF); 787 free(scr, M_DEVBUF); 788 } else { 789 /* 790 * maybe we should just restore the old rasops_info methods 791 * and free the character/attribute buffer here? 792 */ 793 #ifdef VCONS_DEBUG 794 panic("vcons_free_screen: console"); 795 #else 796 printf("vcons_free_screen: console\n"); 797 #endif 798 } 799 800 if (vd->active == scr) 801 vd->active = NULL; 802 } 803 804 static int 805 vcons_show_screen(void *v, void *cookie, int waitok, 806 void (*cb)(void *, int, int), void *cb_arg) 807 { 808 struct vcons_data *vd = v; 809 struct vcons_screen *scr; 810 811 scr = cookie; 812 if (scr == vd->active) 813 return 0; 814 815 vd->wanted = scr; 816 vd->switch_cb = cb; 817 vd->switch_cb_arg = cb_arg; 818 if (cb) { 819 callout_schedule(&vd->switch_callout, 0); 820 return EAGAIN; 821 } 822 823 vcons_do_switch(vd); 824 return 0; 825 } 826 827 /* wrappers for rasops_info methods */ 828 829 static void 830 vcons_copycols_buffer(void *cookie, int row, int srccol, int dstcol, int ncols) 831 { 832 struct rasops_info *ri = cookie; 833 struct vcons_screen *scr = ri->ri_hw; 834 int from = srccol + row * ri->ri_cols; 835 int to = dstcol + row * ri->ri_cols; 836 837 #ifdef WSDISPLAY_SCROLLSUPPORT 838 int offset; 839 offset = scr->scr_offset_to_zero; 840 841 memmove(&scr->scr_attrs[offset + to], &scr->scr_attrs[offset + from], 842 ncols * sizeof(long)); 843 memmove(&scr->scr_chars[offset + to], &scr->scr_chars[offset + from], 844 ncols * sizeof(uint32_t)); 845 #else 846 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 847 ncols * sizeof(long)); 848 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 849 ncols * sizeof(uint32_t)); 850 #endif 851 852 #ifdef VCONS_DRAW_INTR 853 atomic_inc_uint(&scr->scr_dirty); 854 #endif 855 } 856 857 static void 858 vcons_copycols(void *cookie, int row, int srccol, int dstcol, int ncols) 859 { 860 struct rasops_info *ri = cookie; 861 struct vcons_screen *scr = ri->ri_hw; 862 863 vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols); 864 865 #if defined(VCONS_DRAW_INTR) 866 if (scr->scr_vd->use_intr) 867 return; 868 #endif 869 870 vcons_lock(scr); 871 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 872 #if defined(VCONS_DRAW_INTR) 873 vcons_update_screen(scr); 874 #else 875 scr->scr_vd->copycols(cookie, row, srccol, dstcol, ncols); 876 #endif 877 } 878 vcons_unlock(scr); 879 } 880 881 static void 882 vcons_copycols_noread(void *cookie, int row, int srccol, int dstcol, int ncols) 883 { 884 struct rasops_info *ri = cookie; 885 struct vcons_screen *scr = ri->ri_hw; 886 #ifdef VCONS_DRAW_INTR 887 struct vcons_data *vd = scr->scr_vd; 888 #endif 889 890 vcons_lock(scr); 891 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 892 int pos, c, offset, ppos; 893 894 #ifdef WSDISPLAY_SCROLLSUPPORT 895 offset = scr->scr_current_offset; 896 #else 897 offset = 0; 898 #endif 899 ppos = ri->ri_cols * row + dstcol; 900 pos = ppos + offset; 901 for (c = dstcol; c < (dstcol + ncols); c++) { 902 #ifdef VCONS_DRAW_INTR 903 if ((scr->scr_chars[pos] != vd->chars[ppos]) || 904 (scr->scr_attrs[pos] != vd->attrs[ppos])) { 905 scr->putchar(cookie, row, c, 906 scr->scr_chars[pos], scr->scr_attrs[pos]); 907 vd->chars[ppos] = scr->scr_chars[pos]; 908 vd->attrs[ppos] = scr->scr_attrs[pos]; 909 } 910 #else 911 scr->putchar(cookie, row, c, scr->scr_chars[pos], 912 scr->scr_attrs[pos]); 913 #endif 914 pos++; 915 ppos++; 916 } 917 } 918 vcons_unlock(scr); 919 } 920 921 static void 922 vcons_erasecols_buffer(void *cookie, int row, int startcol, int ncols, long fillattr) 923 { 924 struct rasops_info *ri = cookie; 925 struct vcons_screen *scr = ri->ri_hw; 926 int start = startcol + row * ri->ri_cols; 927 int end = start + ncols, i; 928 929 #ifdef WSDISPLAY_SCROLLSUPPORT 930 int offset; 931 offset = scr->scr_offset_to_zero; 932 933 for (i = start; i < end; i++) { 934 scr->scr_attrs[offset + i] = fillattr; 935 scr->scr_chars[offset + i] = 0x20; 936 } 937 #else 938 for (i = start; i < end; i++) { 939 scr->scr_attrs[i] = fillattr; 940 scr->scr_chars[i] = 0x20; 941 } 942 #endif 943 944 #ifdef VCONS_DRAW_INTR 945 atomic_inc_uint(&scr->scr_dirty); 946 #endif 947 } 948 949 #ifdef VCONS_DRAW_INTR 950 static void 951 vcons_erasecols_cached(void *cookie, int row, int startcol, int ncols, long fillattr) 952 { 953 struct rasops_info *ri = cookie; 954 struct vcons_screen *scr = ri->ri_hw; 955 struct vcons_data *vd = scr->scr_vd; 956 int i, pos = row * ri->ri_cols + startcol; 957 958 for (i = pos; i < ncols; i++) { 959 vd->chars[i] = 0x20; 960 vd->attrs[i] = fillattr; 961 } 962 vd->erasecols(cookie, row, startcol, ncols, fillattr); 963 } 964 #endif 965 966 static void 967 vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr) 968 { 969 struct rasops_info *ri = cookie; 970 struct vcons_screen *scr = ri->ri_hw; 971 972 vcons_erasecols_buffer(cookie, row, startcol, ncols, fillattr); 973 974 #if defined(VCONS_DRAW_INTR) 975 if (scr->scr_vd->use_intr) 976 return; 977 #endif 978 979 vcons_lock(scr); 980 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 981 #ifdef VCONS_DRAW_INTR 982 vcons_erasecols_cached(cookie, row, startcol, ncols, 983 fillattr); 984 #else 985 scr->scr_vd->erasecols(cookie, row, startcol, ncols, fillattr); 986 #endif 987 } 988 vcons_unlock(scr); 989 } 990 991 static void 992 vcons_copyrows_buffer(void *cookie, int srcrow, int dstrow, int nrows) 993 { 994 struct rasops_info *ri = cookie; 995 struct vcons_screen *scr = ri->ri_hw; 996 int from, to, len; 997 998 #ifdef WSDISPLAY_SCROLLSUPPORT 999 int offset; 1000 offset = scr->scr_offset_to_zero; 1001 1002 /* do we need to scroll the back buffer? */ 1003 if (dstrow == 0) { 1004 from = ri->ri_cols * srcrow; 1005 to = ri->ri_cols * dstrow; 1006 1007 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 1008 scr->scr_offset_to_zero * sizeof(long)); 1009 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 1010 scr->scr_offset_to_zero * sizeof(uint32_t)); 1011 } 1012 from = ri->ri_cols * srcrow + offset; 1013 to = ri->ri_cols * dstrow + offset; 1014 len = ri->ri_cols * nrows; 1015 1016 #else 1017 from = ri->ri_cols * srcrow; 1018 to = ri->ri_cols * dstrow; 1019 len = ri->ri_cols * nrows; 1020 #endif 1021 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 1022 len * sizeof(long)); 1023 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 1024 len * sizeof(uint32_t)); 1025 1026 #ifdef VCONS_DRAW_INTR 1027 atomic_inc_uint(&scr->scr_dirty); 1028 #endif 1029 } 1030 1031 static void 1032 vcons_copyrows(void *cookie, int srcrow, int dstrow, int nrows) 1033 { 1034 struct rasops_info *ri = cookie; 1035 struct vcons_screen *scr = ri->ri_hw; 1036 1037 vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows); 1038 1039 #if defined(VCONS_DRAW_INTR) 1040 if (scr->scr_vd->use_intr) 1041 return; 1042 #endif 1043 1044 vcons_lock(scr); 1045 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1046 #if defined(VCONS_DRAW_INTR) 1047 vcons_update_screen(scr); 1048 #else 1049 scr->scr_vd->copyrows(cookie, srcrow, dstrow, nrows); 1050 #endif 1051 } 1052 vcons_unlock(scr); 1053 } 1054 1055 static void 1056 vcons_copyrows_noread(void *cookie, int srcrow, int dstrow, int nrows) 1057 { 1058 struct rasops_info *ri = cookie; 1059 struct vcons_screen *scr = ri->ri_hw; 1060 #ifdef VCONS_DRAW_INTR 1061 struct vcons_data *vd = scr->scr_vd; 1062 #endif 1063 vcons_lock(scr); 1064 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1065 int pos, l, c, offset, ppos; 1066 1067 #ifdef WSDISPLAY_SCROLLSUPPORT 1068 offset = scr->scr_current_offset; 1069 #else 1070 offset = 0; 1071 #endif 1072 ppos = ri->ri_cols * dstrow; 1073 pos = ppos + offset; 1074 for (l = dstrow; l < (dstrow + nrows); l++) { 1075 for (c = 0; c < ri->ri_cols; c++) { 1076 #ifdef VCONS_DRAW_INTR 1077 if ((scr->scr_chars[pos] != vd->chars[ppos]) || 1078 (scr->scr_attrs[pos] != vd->attrs[ppos])) { 1079 scr->putchar(cookie, l, c, 1080 scr->scr_chars[pos], scr->scr_attrs[pos]); 1081 vd->chars[ppos] = scr->scr_chars[pos]; 1082 vd->attrs[ppos] = scr->scr_attrs[pos]; 1083 } 1084 #else 1085 scr->putchar(cookie, l, c, scr->scr_chars[pos], 1086 scr->scr_attrs[pos]); 1087 #endif 1088 pos++; 1089 ppos++; 1090 } 1091 } 1092 } 1093 vcons_unlock(scr); 1094 } 1095 1096 static void 1097 vcons_eraserows_buffer(void *cookie, int row, int nrows, long fillattr) 1098 { 1099 struct rasops_info *ri = cookie; 1100 struct vcons_screen *scr = ri->ri_hw; 1101 int start, end, i; 1102 1103 #ifdef WSDISPLAY_SCROLLSUPPORT 1104 int offset; 1105 offset = scr->scr_offset_to_zero; 1106 1107 start = ri->ri_cols * row + offset; 1108 end = ri->ri_cols * (row + nrows) + offset; 1109 #else 1110 start = ri->ri_cols * row; 1111 end = ri->ri_cols * (row + nrows); 1112 #endif 1113 1114 for (i = start; i < end; i++) { 1115 scr->scr_attrs[i] = fillattr; 1116 scr->scr_chars[i] = 0x20; 1117 } 1118 1119 #ifdef VCONS_DRAW_INTR 1120 atomic_inc_uint(&scr->scr_dirty); 1121 #endif 1122 } 1123 1124 #ifdef VCONS_DRAW_INTR 1125 static void 1126 vcons_eraserows_cached(void *cookie, int row, int nrows, long fillattr) 1127 { 1128 struct rasops_info *ri = cookie; 1129 struct vcons_screen *scr = ri->ri_hw; 1130 struct vcons_data *vd = scr->scr_vd; 1131 int i, pos = row * ri->ri_cols, end = (row+nrows) * ri->ri_cols; 1132 1133 for (i = pos; i < end; i++) { 1134 vd->chars[i] = 0x20; 1135 vd->attrs[i] = fillattr; 1136 } 1137 vd->eraserows(cookie, row, nrows, fillattr); 1138 } 1139 #endif 1140 1141 static void 1142 vcons_eraserows(void *cookie, int row, int nrows, long fillattr) 1143 { 1144 struct rasops_info *ri = cookie; 1145 struct vcons_screen *scr = ri->ri_hw; 1146 1147 vcons_eraserows_buffer(cookie, row, nrows, fillattr); 1148 1149 #if defined(VCONS_DRAW_INTR) 1150 if (scr->scr_vd->use_intr) 1151 return; 1152 #endif 1153 1154 vcons_lock(scr); 1155 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1156 #ifdef VCONS_DRAW_INTR 1157 vcons_eraserows_cached(cookie, row, nrows, fillattr); 1158 #else 1159 scr->scr_vd->eraserows(cookie, row, nrows, fillattr); 1160 #endif 1161 } 1162 vcons_unlock(scr); 1163 } 1164 1165 static void 1166 vcons_putchar_buffer(void *cookie, int row, int col, u_int c, long attr) 1167 { 1168 struct rasops_info *ri = cookie; 1169 struct vcons_screen *scr = ri->ri_hw; 1170 int pos; 1171 1172 #ifdef WSDISPLAY_SCROLLSUPPORT 1173 int offset; 1174 offset = scr->scr_offset_to_zero; 1175 1176 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) && 1177 (col < ri->ri_cols)) { 1178 pos = col + row * ri->ri_cols; 1179 scr->scr_attrs[pos + offset] = attr; 1180 scr->scr_chars[pos + offset] = c; 1181 } 1182 #else 1183 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) && 1184 (col < ri->ri_cols)) { 1185 pos = col + row * ri->ri_cols; 1186 scr->scr_attrs[pos] = attr; 1187 scr->scr_chars[pos] = c; 1188 } 1189 #endif 1190 1191 #ifdef VCONS_DRAW_INTR 1192 atomic_inc_uint(&scr->scr_dirty); 1193 #endif 1194 } 1195 1196 #ifdef VCONS_DRAW_INTR 1197 static void 1198 vcons_putchar_cached(void *cookie, int row, int col, u_int c, long attr) 1199 { 1200 struct rasops_info *ri = cookie; 1201 struct vcons_screen *scr = ri->ri_hw; 1202 struct vcons_data *vd = scr->scr_vd; 1203 int pos = row * ri->ri_cols + col; 1204 1205 if ((vd->chars == NULL) || (vd->attrs == NULL)) { 1206 scr->putchar(cookie, row, col, c, attr); 1207 return; 1208 } 1209 if ((vd->chars[pos] != c) || (vd->attrs[pos] != attr)) { 1210 vd->attrs[pos] = attr; 1211 vd->chars[pos] = c; 1212 scr->putchar(cookie, row, col, c, attr); 1213 } 1214 } 1215 #endif 1216 1217 static void 1218 vcons_putchar(void *cookie, int row, int col, u_int c, long attr) 1219 { 1220 struct rasops_info *ri = cookie; 1221 struct vcons_screen *scr = ri->ri_hw; 1222 1223 vcons_putchar_buffer(cookie, row, col, c, attr); 1224 1225 #if defined(VCONS_DRAW_INTR) 1226 if (scr->scr_vd->use_intr) 1227 return; 1228 #endif 1229 1230 vcons_lock(scr); 1231 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1232 #ifdef VCONS_DRAW_INTR 1233 vcons_putchar_cached(cookie, row, col, c, attr); 1234 #else 1235 scr->putchar(cookie, row, col, c, attr); 1236 #endif 1237 } 1238 vcons_unlock(scr); 1239 } 1240 1241 static void 1242 vcons_cursor(void *cookie, int on, int row, int col) 1243 { 1244 struct rasops_info *ri = cookie; 1245 struct vcons_screen *scr = ri->ri_hw; 1246 1247 1248 #if defined(VCONS_DRAW_INTR) 1249 if (scr->scr_vd->use_intr) { 1250 vcons_lock(scr); 1251 if (scr->scr_ri.ri_crow != row || scr->scr_ri.ri_ccol != col) { 1252 scr->scr_ri.ri_crow = row; 1253 scr->scr_ri.ri_ccol = col; 1254 atomic_inc_uint(&scr->scr_dirty); 1255 } 1256 vcons_unlock(scr); 1257 return; 1258 } 1259 #endif 1260 1261 vcons_lock(scr); 1262 1263 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1264 scr->scr_vd->cursor(cookie, on, row, col); 1265 } else { 1266 scr->scr_ri.ri_crow = row; 1267 scr->scr_ri.ri_ccol = col; 1268 } 1269 vcons_unlock(scr); 1270 } 1271 1272 /* methods to read/write characters via ioctl() */ 1273 1274 static int 1275 vcons_putwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 1276 { 1277 long attr; 1278 struct rasops_info *ri; 1279 int error; 1280 1281 KASSERT(scr != NULL && wsc != NULL); 1282 1283 ri = &scr->scr_ri; 1284 1285 if (__predict_false((unsigned int)wsc->col > ri->ri_cols || 1286 (unsigned int)wsc->row > ri->ri_rows)) 1287 return (EINVAL); 1288 1289 if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) && 1290 (wsc->col < ri->ri_cols)) { 1291 1292 error = ri->ri_ops.allocattr(ri, wsc->foreground, 1293 wsc->background, wsc->flags, &attr); 1294 if (error) 1295 return error; 1296 vcons_putchar(ri, wsc->row, wsc->col, wsc->letter, attr); 1297 #ifdef VCONS_DEBUG 1298 printf("vcons_putwschar(%d, %d, %x, %lx\n", wsc->row, wsc->col, 1299 wsc->letter, attr); 1300 #endif 1301 return 0; 1302 } else 1303 return EINVAL; 1304 } 1305 1306 static int 1307 vcons_getwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 1308 { 1309 int offset; 1310 long attr; 1311 struct rasops_info *ri; 1312 1313 KASSERT(scr != NULL && wsc != NULL); 1314 1315 ri = &scr->scr_ri; 1316 1317 if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) && 1318 (wsc->col < ri->ri_cols)) { 1319 1320 offset = ri->ri_cols * wsc->row + wsc->col; 1321 #ifdef WSDISPLAY_SCROLLSUPPORT 1322 offset += scr->scr_offset_to_zero; 1323 #endif 1324 wsc->letter = scr->scr_chars[offset]; 1325 attr = scr->scr_attrs[offset]; 1326 1327 /* 1328 * this is ugly. We need to break up an attribute into colours and 1329 * flags but there's no rasops method to do that so we must rely on 1330 * the 'canonical' encoding. 1331 */ 1332 #ifdef VCONS_DEBUG 1333 printf("vcons_getwschar: %d, %d, %x, %lx\n", wsc->row, 1334 wsc->col, wsc->letter, attr); 1335 #endif 1336 wsc->foreground = (attr >> 24) & 0xff; 1337 wsc->background = (attr >> 16) & 0xff; 1338 wsc->flags = attr & 0xff; 1339 return 0; 1340 } else 1341 return EINVAL; 1342 } 1343 1344 #ifdef WSDISPLAY_SCROLLSUPPORT 1345 1346 static void 1347 vcons_scroll(void *cookie, void *vs, int where) 1348 { 1349 struct vcons_screen *scr = vs; 1350 1351 if (where == 0) { 1352 scr->scr_line_wanted = 0; 1353 } else { 1354 scr->scr_line_wanted = scr->scr_line_wanted - where; 1355 if (scr->scr_line_wanted < 0) 1356 scr->scr_line_wanted = 0; 1357 if (scr->scr_line_wanted > scr->scr_lines_in_buffer) 1358 scr->scr_line_wanted = scr->scr_lines_in_buffer; 1359 } 1360 1361 if (scr->scr_line_wanted != scr->scr_current_line) { 1362 1363 vcons_do_scroll(scr); 1364 } 1365 } 1366 1367 static void 1368 vcons_do_scroll(struct vcons_screen *scr) 1369 { 1370 int dist, from, to, num; 1371 int r_offset, r_start; 1372 int i, j; 1373 1374 if (scr->scr_line_wanted == scr->scr_current_line) 1375 return; 1376 dist = scr->scr_line_wanted - scr->scr_current_line; 1377 scr->scr_current_line = scr->scr_line_wanted; 1378 scr->scr_current_offset = scr->scr_ri.ri_cols * 1379 (scr->scr_lines_in_buffer - scr->scr_current_line); 1380 if (abs(dist) >= scr->scr_ri.ri_rows) { 1381 vcons_redraw_screen(scr); 1382 return; 1383 } 1384 /* scroll and redraw only what we really have to */ 1385 if (dist > 0) { 1386 /* we scroll down */ 1387 from = 0; 1388 to = dist; 1389 num = scr->scr_ri.ri_rows - dist; 1390 /* now the redraw parameters */ 1391 r_offset = scr->scr_current_offset; 1392 r_start = 0; 1393 } else { 1394 /* scrolling up */ 1395 to = 0; 1396 from = -dist; 1397 num = scr->scr_ri.ri_rows + dist; 1398 r_offset = scr->scr_current_offset + num * scr->scr_ri.ri_cols; 1399 r_start = num; 1400 } 1401 scr->scr_vd->copyrows(scr, from, to, num); 1402 for (i = 0; i < abs(dist); i++) { 1403 for (j = 0; j < scr->scr_ri.ri_cols; j++) { 1404 #ifdef VCONS_DRAW_INTR 1405 vcons_putchar_cached(scr, i + r_start, j, 1406 scr->scr_chars[r_offset], 1407 scr->scr_attrs[r_offset]); 1408 #else 1409 scr->putchar(scr, i + r_start, j, 1410 scr->scr_chars[r_offset], 1411 scr->scr_attrs[r_offset]); 1412 #endif 1413 r_offset++; 1414 } 1415 } 1416 1417 if (scr->scr_line_wanted == 0) { 1418 /* this was a reset - need to draw the cursor */ 1419 scr->scr_ri.ri_flg &= ~RI_CURSOR; 1420 scr->scr_vd->cursor(scr, 1, scr->scr_ri.ri_crow, 1421 scr->scr_ri.ri_ccol); 1422 } 1423 } 1424 1425 #endif /* WSDISPLAY_SCROLLSUPPORT */ 1426 1427 #ifdef VCONS_DRAW_INTR 1428 static void 1429 vcons_intr(void *cookie) 1430 { 1431 struct vcons_data *vd = cookie; 1432 1433 softint_schedule(vd->intr_softint); 1434 } 1435 1436 static void 1437 vcons_softintr(void *cookie) 1438 { 1439 struct vcons_data *vd = cookie; 1440 struct vcons_screen *scr = vd->active; 1441 unsigned int dirty; 1442 1443 if (scr && vd->use_intr) { 1444 if (!SCREEN_IS_BUSY(scr)) { 1445 dirty = atomic_swap_uint(&scr->scr_dirty, 0); 1446 if (vd->use_intr == 2) { 1447 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) { 1448 vd->use_intr = 1; 1449 vcons_redraw_screen(scr); 1450 } 1451 } else if (dirty > 0) { 1452 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 1453 vcons_update_screen(scr); 1454 } 1455 } 1456 } 1457 1458 callout_schedule(&vd->intr, mstohz(33)); 1459 } 1460 1461 static void 1462 vcons_init_thread(void *cookie) 1463 { 1464 struct vcons_data *vd = (struct vcons_data *)cookie; 1465 1466 vd->use_intr = 2; 1467 callout_schedule(&vd->intr, mstohz(33)); 1468 kthread_exit(0); 1469 } 1470 #endif /* VCONS_DRAW_INTR */ 1471 1472 void 1473 vcons_enable_polling(struct vcons_data *vd) 1474 { 1475 struct vcons_screen *scr = vd->active; 1476 1477 #ifdef VCONS_DRAW_INTR 1478 vd->use_intr = 0; 1479 #endif 1480 1481 if (scr && !SCREEN_IS_BUSY(scr)) { 1482 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 1483 vcons_redraw_screen(scr); 1484 } 1485 } 1486 1487 void 1488 vcons_disable_polling(struct vcons_data *vd) 1489 { 1490 #ifdef VCONS_DRAW_INTR 1491 struct vcons_screen *scr = vd->active; 1492 1493 if (!vd->intr_valid) 1494 return; 1495 1496 vd->use_intr = 2; 1497 if (scr) 1498 atomic_inc_uint(&scr->scr_dirty); 1499 #endif 1500 } 1501 1502 void 1503 vcons_hard_switch(struct vcons_screen *scr) 1504 { 1505 struct vcons_data *vd = scr->scr_vd; 1506 struct vcons_screen *oldscr = vd->active; 1507 1508 if (oldscr) { 1509 SCREEN_INVISIBLE(oldscr); 1510 oldscr->scr_ri.ri_flg &= ~RI_CURSOR; 1511 } 1512 SCREEN_VISIBLE(scr); 1513 vd->active = scr; 1514 vd->wanted = NULL; 1515 1516 if (vd->show_screen_cb != NULL) 1517 vd->show_screen_cb(scr, vd->show_screen_cookie); 1518 } 1519 1520 #ifdef VCONS_DRAW_INTR 1521 void 1522 vcons_invalidate_cache(struct vcons_data *vd) 1523 { 1524 int i; 1525 1526 if (vd->cells == 0) 1527 return; 1528 1529 for (i = 0; i > vd->cells; i++) { 1530 vd->chars[i] = -1; 1531 vd->attrs[i] = -1; 1532 } 1533 } 1534 #endif 1535