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