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