1 /* $NetBSD: wsdisplay_vcons.c,v 1.26 2011/05/25 06:13:29 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.26 2011/05/25 06:13:29 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 #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(uint16_t)), M_DEVBUF, M_WAITOK); 286 if (scr->scr_attrs == NULL) 287 return ENOMEM; 288 289 scr->scr_chars = (uint16_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(uint16_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 uint16_t *charptr = scr->scr_chars; 406 long *attrptr = scr->scr_attrs; 407 struct rasops_info *ri = &scr->scr_ri; 408 struct vcons_data *vd = scr->scr_vd; 409 int i, j, offset, boffset = 0; 410 411 vcons_lock(scr); 412 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 413 414 /* 415 * only clear the screen when RI_FULLCLEAR is set since we're 416 * going to overwrite every single character cell anyway 417 */ 418 if (ri->ri_flg & RI_FULLCLEAR) { 419 vd->eraserows(ri, 0, ri->ri_rows, 420 scr->scr_defattr); 421 } 422 423 /* redraw the screen */ 424 #ifdef WSDISPLAY_SCROLLSUPPORT 425 offset = scr->scr_current_offset; 426 #else 427 offset = 0; 428 #endif 429 for (i = 0; i < ri->ri_rows; i++) { 430 for (j = 0; j < ri->ri_cols; j++) { 431 /* 432 * no need to use the wrapper function - we 433 * don't change any characters or attributes 434 * and we already made sure the screen we're 435 * working on is visible 436 */ 437 vd->putchar(ri, i, j, 438 charptr[offset], attrptr[offset]); 439 #ifdef VCONS_DRAW_INTR 440 vd->chars[boffset] = charptr[offset]; 441 vd->attrs[boffset] = attrptr[offset]; 442 #endif 443 offset++; 444 boffset++; 445 } 446 } 447 ri->ri_flg &= ~RI_CURSOR; 448 scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol); 449 #ifdef VCONS_DRAW_INTR 450 vd->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol; 451 #endif 452 } 453 vcons_unlock(scr); 454 } 455 456 #ifdef VCONS_DRAW_INTR 457 void 458 vcons_update_screen(struct vcons_screen *scr) 459 { 460 uint16_t *charptr = scr->scr_chars; 461 long *attrptr = scr->scr_attrs; 462 struct rasops_info *ri = &scr->scr_ri; 463 struct vcons_data *vd = scr->scr_vd; 464 int i, j, offset, boffset = 0; 465 466 vcons_lock(scr); 467 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 468 469 /* redraw the screen */ 470 #ifdef WSDISPLAY_SCROLLSUPPORT 471 offset = scr->scr_current_offset; 472 #else 473 offset = 0; 474 #endif 475 /* 476 * we mark the character cell occupied by the cursor as dirty 477 * so we don't have to deal with it 478 * notice that this isn't necessarily the position where rasops 479 * thinks it is, just where we drew it the last time 480 */ 481 if (vd->cursor_offset >= 0) 482 vd->attrs[vd->cursor_offset] = 0xffffffff; 483 484 for (i = 0; i < ri->ri_rows; i++) { 485 for (j = 0; j < ri->ri_cols; j++) { 486 /* 487 * no need to use the wrapper function - we 488 * don't change any characters or attributes 489 * and we already made sure the screen we're 490 * working on is visible 491 */ 492 if ((vd->chars[boffset] != charptr[offset]) || 493 (vd->attrs[boffset] != attrptr[offset])) { 494 vd->putchar(ri, i, j, 495 charptr[offset], attrptr[offset]); 496 vd->chars[boffset] = charptr[offset]; 497 vd->attrs[boffset] = attrptr[offset]; 498 } 499 offset++; 500 boffset++; 501 } 502 } 503 ri->ri_flg &= ~RI_CURSOR; 504 scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol); 505 vd->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol; 506 } 507 vcons_unlock(scr); 508 } 509 #endif 510 511 static int 512 vcons_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 513 struct lwp *l) 514 { 515 struct vcons_data *vd = v; 516 int error; 517 518 switch (cmd) { 519 case WSDISPLAYIO_GETWSCHAR: 520 error = vcons_getwschar((struct vcons_screen *)vs, 521 (struct wsdisplay_char *)data); 522 break; 523 524 case WSDISPLAYIO_PUTWSCHAR: 525 error = vcons_putwschar((struct vcons_screen *)vs, 526 (struct wsdisplay_char *)data); 527 break; 528 529 default: 530 if (vd->ioctl != NULL) 531 error = (*vd->ioctl)(v, vs, cmd, data, flag, l); 532 else 533 error = EPASSTHROUGH; 534 } 535 536 return error; 537 } 538 539 static int 540 vcons_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep, 541 int *curxp, int *curyp, long *defattrp) 542 { 543 struct vcons_data *vd = v; 544 struct vcons_screen *scr; 545 int ret; 546 547 scr = malloc(sizeof(struct vcons_screen), M_DEVBUF, M_WAITOK | M_ZERO); 548 if (scr == NULL) 549 return ENOMEM; 550 551 scr->scr_flags = 0; 552 scr->scr_status = 0; 553 scr->scr_busy = 0; 554 scr->scr_type = type; 555 556 ret = vcons_init_screen(vd, scr, 0, defattrp); 557 if (ret != 0) { 558 free(scr, M_DEVBUF); 559 return ret; 560 } 561 562 if (vd->active == NULL) { 563 SCREEN_VISIBLE(scr); 564 vd->active = scr; 565 vd->currenttype = type; 566 } 567 568 *cookiep = scr; 569 *curxp = scr->scr_ri.ri_ccol; 570 *curyp = scr->scr_ri.ri_crow; 571 return 0; 572 } 573 574 static void 575 vcons_free_screen(void *v, void *cookie) 576 { 577 struct vcons_data *vd = v; 578 struct vcons_screen *scr = cookie; 579 580 vcons_lock(scr); 581 /* there should be no rasops activity here */ 582 583 LIST_REMOVE(scr, next); 584 585 if ((scr->scr_flags & VCONS_SCREEN_IS_STATIC) == 0) { 586 free(scr->scr_attrs, M_DEVBUF); 587 free(scr, M_DEVBUF); 588 } else { 589 /* 590 * maybe we should just restore the old rasops_info methods 591 * and free the character/attribute buffer here? 592 */ 593 #ifdef VCONS_DEBUG 594 panic("vcons_free_screen: console"); 595 #else 596 printf("vcons_free_screen: console\n"); 597 #endif 598 } 599 600 if (vd->active == scr) 601 vd->active = NULL; 602 } 603 604 static int 605 vcons_show_screen(void *v, void *cookie, int waitok, 606 void (*cb)(void *, int, int), void *cb_arg) 607 { 608 struct vcons_data *vd = v; 609 struct vcons_screen *scr; 610 611 scr = cookie; 612 if (scr == vd->active) 613 return 0; 614 615 vd->wanted = scr; 616 vd->switch_cb = cb; 617 vd->switch_cb_arg = cb_arg; 618 if (cb) { 619 callout_schedule(&vd->switch_callout, 0); 620 return EAGAIN; 621 } 622 623 vcons_do_switch(vd); 624 return 0; 625 } 626 627 /* wrappers for rasops_info methods */ 628 629 static void 630 vcons_copycols_buffer(void *cookie, int row, int srccol, int dstcol, int ncols) 631 { 632 struct rasops_info *ri = cookie; 633 struct vcons_screen *scr = ri->ri_hw; 634 int from = srccol + row * ri->ri_cols; 635 int to = dstcol + row * ri->ri_cols; 636 637 #ifdef WSDISPLAY_SCROLLSUPPORT 638 int offset; 639 offset = scr->scr_offset_to_zero; 640 641 memmove(&scr->scr_attrs[offset + to], &scr->scr_attrs[offset + from], 642 ncols * sizeof(long)); 643 memmove(&scr->scr_chars[offset + to], &scr->scr_chars[offset + from], 644 ncols * sizeof(uint16_t)); 645 #else 646 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 647 ncols * sizeof(long)); 648 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 649 ncols * sizeof(uint16_t)); 650 #endif 651 652 #ifdef VCONS_DRAW_INTR 653 atomic_inc_uint(&scr->scr_dirty); 654 #endif 655 } 656 657 static void 658 vcons_copycols(void *cookie, int row, int srccol, int dstcol, int ncols) 659 { 660 struct rasops_info *ri = cookie; 661 struct vcons_screen *scr = ri->ri_hw; 662 663 vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols); 664 665 #if defined(VCONS_DRAW_INTR) 666 if (scr->scr_vd->use_intr) 667 return; 668 #endif 669 670 vcons_lock(scr); 671 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 672 scr->scr_vd->copycols(cookie, row, srccol, dstcol, ncols); 673 } 674 vcons_unlock(scr); 675 } 676 677 static void 678 vcons_copycols_noread(void *cookie, int row, int srccol, int dstcol, int ncols) 679 { 680 struct rasops_info *ri = cookie; 681 struct vcons_screen *scr = ri->ri_hw; 682 struct vcons_data *vd = scr->scr_vd; 683 684 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 685 int pos, c, offset, ppos; 686 687 #ifdef WSDISPLAY_SCROLLSUPPORT 688 offset = scr->scr_current_offset; 689 #else 690 offset = 0; 691 #endif 692 ppos = ri->ri_cols * row + dstcol; 693 pos = ppos + offset; 694 for (c = dstcol; c < (dstcol + ncols); c++) { 695 #ifdef VCONS_DRAW_INTR 696 if ((scr->scr_chars[pos] != vd->chars[ppos]) || 697 (scr->scr_attrs[pos] != vd->attrs[ppos])) { 698 vd->putchar(cookie, row, c, 699 scr->scr_chars[pos], scr->scr_attrs[pos]); 700 vd->chars[ppos] = scr->scr_chars[pos]; 701 vd->attrs[ppos] = scr->scr_attrs[pos]; 702 } 703 #else 704 vd->putchar(cookie, row, c, scr->scr_chars[pos], 705 scr->scr_attrs[pos]); 706 #endif 707 pos++; 708 ppos++; 709 } 710 } 711 } 712 713 static void 714 vcons_erasecols_buffer(void *cookie, int row, int startcol, int ncols, long fillattr) 715 { 716 struct rasops_info *ri = cookie; 717 struct vcons_screen *scr = ri->ri_hw; 718 int start = startcol + row * ri->ri_cols; 719 int end = start + ncols, i; 720 721 #ifdef WSDISPLAY_SCROLLSUPPORT 722 int offset; 723 offset = scr->scr_offset_to_zero; 724 725 for (i = start; i < end; i++) { 726 scr->scr_attrs[offset + i] = fillattr; 727 scr->scr_chars[offset + i] = 0x20; 728 } 729 #else 730 for (i = start; i < end; i++) { 731 scr->scr_attrs[i] = fillattr; 732 scr->scr_chars[i] = 0x20; 733 } 734 #endif 735 736 #ifdef VCONS_DRAW_INTR 737 atomic_inc_uint(&scr->scr_dirty); 738 #endif 739 } 740 741 #ifdef VCONS_DRAW_INTR 742 static void 743 vcons_erasecols_cached(void *cookie, int row, int startcol, int ncols, long fillattr) 744 { 745 struct rasops_info *ri = cookie; 746 struct vcons_screen *scr = ri->ri_hw; 747 struct vcons_data *vd = scr->scr_vd; 748 int i, pos = row * ri->ri_cols + startcol; 749 750 for (i = pos; i < ncols; i++) { 751 vd->chars[i] = 0x20; 752 vd->attrs[i] = fillattr; 753 } 754 vd->erasecols(cookie, row, startcol, ncols, fillattr); 755 } 756 #endif 757 758 static void 759 vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr) 760 { 761 struct rasops_info *ri = cookie; 762 struct vcons_screen *scr = ri->ri_hw; 763 764 vcons_erasecols_buffer(cookie, row, startcol, ncols, fillattr); 765 766 #if defined(VCONS_DRAW_INTR) 767 if (scr->scr_vd->use_intr) 768 return; 769 #endif 770 771 vcons_lock(scr); 772 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 773 #ifdef VCONS_DRAW_INTR 774 vcons_erasecols_cached(cookie, row, startcol, ncols, 775 fillattr); 776 #else 777 scr->scr_vd->erasecols(cookie, row, startcol, ncols, fillattr); 778 #endif 779 } 780 vcons_unlock(scr); 781 } 782 783 static void 784 vcons_copyrows_buffer(void *cookie, int srcrow, int dstrow, int nrows) 785 { 786 struct rasops_info *ri = cookie; 787 struct vcons_screen *scr = ri->ri_hw; 788 int from, to, len; 789 790 #ifdef WSDISPLAY_SCROLLSUPPORT 791 int offset; 792 offset = scr->scr_offset_to_zero; 793 794 /* do we need to scroll the back buffer? */ 795 if (dstrow == 0) { 796 from = ri->ri_cols * srcrow; 797 to = ri->ri_cols * dstrow; 798 799 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 800 scr->scr_offset_to_zero * sizeof(long)); 801 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 802 scr->scr_offset_to_zero * sizeof(uint16_t)); 803 } 804 from = ri->ri_cols * srcrow + offset; 805 to = ri->ri_cols * dstrow + offset; 806 len = ri->ri_cols * nrows; 807 808 #else 809 from = ri->ri_cols * srcrow; 810 to = ri->ri_cols * dstrow; 811 len = ri->ri_cols * nrows; 812 #endif 813 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 814 len * sizeof(long)); 815 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 816 len * sizeof(uint16_t)); 817 818 #ifdef VCONS_DRAW_INTR 819 atomic_inc_uint(&scr->scr_dirty); 820 #endif 821 } 822 823 static void 824 vcons_copyrows(void *cookie, int srcrow, int dstrow, int nrows) 825 { 826 struct rasops_info *ri = cookie; 827 struct vcons_screen *scr = ri->ri_hw; 828 829 vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows); 830 831 #if defined(VCONS_DRAW_INTR) 832 if (scr->scr_vd->use_intr) 833 return; 834 #endif 835 836 vcons_lock(scr); 837 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 838 scr->scr_vd->copyrows(cookie, srcrow, dstrow, nrows); 839 } 840 vcons_unlock(scr); 841 } 842 843 static void 844 vcons_copyrows_noread(void *cookie, int srcrow, int dstrow, int nrows) 845 { 846 struct rasops_info *ri = cookie; 847 struct vcons_screen *scr = ri->ri_hw; 848 struct vcons_data *vd = scr->scr_vd; 849 int dist; 850 851 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 852 int pos, l, c, offset, ppos; 853 854 #ifdef WSDISPLAY_SCROLLSUPPORT 855 offset = scr->scr_current_offset; 856 #else 857 offset = 0; 858 #endif 859 dist = (dstrow - srcrow) * ri->ri_cols; 860 ppos = ri->ri_cols * dstrow; 861 pos = ppos + offset; 862 for (l = dstrow; l < (dstrow + nrows); l++) { 863 for (c = 0; c < ri->ri_cols; c++) { 864 #ifdef VCONS_DRAW_INTR 865 if ((scr->scr_chars[pos] != vd->chars[ppos]) || 866 (scr->scr_attrs[pos] != vd->attrs[ppos])) { 867 vd->putchar(cookie, l, c, 868 scr->scr_chars[pos], scr->scr_attrs[pos]); 869 vd->chars[ppos] = scr->scr_chars[pos]; 870 vd->attrs[ppos] = scr->scr_attrs[pos]; 871 } 872 #else 873 vd->putchar(cookie, l, c, scr->scr_chars[pos], 874 scr->scr_attrs[pos]); 875 #endif 876 pos++; 877 ppos++; 878 } 879 } 880 } 881 } 882 883 static void 884 vcons_eraserows_buffer(void *cookie, int row, int nrows, long fillattr) 885 { 886 struct rasops_info *ri = cookie; 887 struct vcons_screen *scr = ri->ri_hw; 888 int start, end, i; 889 890 #ifdef WSDISPLAY_SCROLLSUPPORT 891 int offset; 892 offset = scr->scr_offset_to_zero; 893 894 start = ri->ri_cols * row + offset; 895 end = ri->ri_cols * (row + nrows) + offset; 896 #else 897 start = ri->ri_cols * row; 898 end = ri->ri_cols * (row + nrows); 899 #endif 900 901 for (i = start; i < end; i++) { 902 scr->scr_attrs[i] = fillattr; 903 scr->scr_chars[i] = 0x20; 904 } 905 906 #ifdef VCONS_DRAW_INTR 907 atomic_inc_uint(&scr->scr_dirty); 908 #endif 909 } 910 911 static void 912 vcons_eraserows(void *cookie, int row, int nrows, long fillattr) 913 { 914 struct rasops_info *ri = cookie; 915 struct vcons_screen *scr = ri->ri_hw; 916 917 vcons_eraserows_buffer(cookie, row, nrows, fillattr); 918 919 #if defined(VCONS_DRAW_INTR) 920 if (scr->scr_vd->use_intr) 921 return; 922 #endif 923 924 vcons_lock(scr); 925 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 926 scr->scr_vd->eraserows(cookie, row, nrows, fillattr); 927 } 928 vcons_unlock(scr); 929 } 930 931 static void 932 vcons_putchar_buffer(void *cookie, int row, int col, u_int c, long attr) 933 { 934 struct rasops_info *ri = cookie; 935 struct vcons_screen *scr = ri->ri_hw; 936 int pos; 937 938 #ifdef WSDISPLAY_SCROLLSUPPORT 939 int offset; 940 offset = scr->scr_offset_to_zero; 941 942 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) && 943 (col < ri->ri_cols)) { 944 pos = col + row * ri->ri_cols; 945 scr->scr_attrs[pos + offset] = attr; 946 scr->scr_chars[pos + offset] = c; 947 } 948 #else 949 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) && 950 (col < ri->ri_cols)) { 951 pos = col + row * ri->ri_cols; 952 scr->scr_attrs[pos] = attr; 953 scr->scr_chars[pos] = c; 954 } 955 #endif 956 957 #ifdef VCONS_DRAW_INTR 958 atomic_inc_uint(&scr->scr_dirty); 959 #endif 960 } 961 962 #ifdef VCONS_DRAW_INTR 963 static void 964 vcons_putchar_cached(void *cookie, int row, int col, u_int c, long attr) 965 { 966 struct rasops_info *ri = cookie; 967 struct vcons_screen *scr = ri->ri_hw; 968 struct vcons_data *vd = scr->scr_vd; 969 int pos = row * ri->ri_cols + col; 970 971 if ((vd->chars == NULL) || (vd->attrs == NULL)) { 972 vd->putchar(cookie, row, col, c, attr); 973 return; 974 } 975 if ((vd->chars[pos] != c) || (vd->attrs[pos] != attr)) { 976 vd->attrs[pos] = attr; 977 vd->chars[pos] = c; 978 vd->putchar(cookie, row, col, c, attr); 979 } 980 } 981 #endif 982 983 static void 984 vcons_putchar(void *cookie, int row, int col, u_int c, long attr) 985 { 986 struct rasops_info *ri = cookie; 987 struct vcons_screen *scr = ri->ri_hw; 988 989 vcons_putchar_buffer(cookie, row, col, c, attr); 990 991 #if defined(VCONS_DRAW_INTR) 992 if (scr->scr_vd->use_intr) 993 return; 994 #endif 995 996 vcons_lock(scr); 997 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 998 #ifdef VCONS_DRAW_INTR 999 vcons_putchar_cached(cookie, row, col, c, attr); 1000 #else 1001 scr->scr_vd->putchar(cookie, row, col, c, attr); 1002 #endif 1003 } 1004 vcons_unlock(scr); 1005 } 1006 1007 static void 1008 vcons_cursor(void *cookie, int on, int row, int col) 1009 { 1010 struct rasops_info *ri = cookie; 1011 struct vcons_screen *scr = ri->ri_hw; 1012 1013 1014 #if defined(VCONS_DRAW_INTR) 1015 if (scr->scr_vd->use_intr) { 1016 vcons_lock(scr); 1017 if (scr->scr_ri.ri_crow != row || scr->scr_ri.ri_ccol != col) { 1018 scr->scr_ri.ri_crow = row; 1019 scr->scr_ri.ri_ccol = col; 1020 atomic_inc_uint(&scr->scr_dirty); 1021 } 1022 vcons_unlock(scr); 1023 return; 1024 } 1025 #endif 1026 1027 vcons_lock(scr); 1028 1029 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1030 scr->scr_vd->cursor(cookie, on, row, col); 1031 } else { 1032 scr->scr_ri.ri_crow = row; 1033 scr->scr_ri.ri_ccol = col; 1034 } 1035 vcons_unlock(scr); 1036 } 1037 1038 /* methods to read/write characters via ioctl() */ 1039 1040 static int 1041 vcons_putwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 1042 { 1043 long attr; 1044 struct rasops_info *ri; 1045 1046 KASSERT(scr != NULL && wsc != NULL); 1047 1048 ri = &scr->scr_ri; 1049 1050 if (__predict_false((unsigned int)wsc->col > ri->ri_cols || 1051 (unsigned int)wsc->row > ri->ri_rows)) 1052 return (EINVAL); 1053 1054 if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) && 1055 (wsc->col < ri->ri_cols)) { 1056 1057 ri->ri_ops.allocattr(ri, wsc->foreground, wsc->background, 1058 wsc->flags, &attr); 1059 vcons_putchar(ri, wsc->row, wsc->col, wsc->letter, attr); 1060 #ifdef VCONS_DEBUG 1061 printf("vcons_putwschar(%d, %d, %x, %lx\n", wsc->row, wsc->col, 1062 wsc->letter, attr); 1063 #endif 1064 return 0; 1065 } else 1066 return EINVAL; 1067 } 1068 1069 static int 1070 vcons_getwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 1071 { 1072 int offset; 1073 long attr; 1074 struct rasops_info *ri; 1075 1076 KASSERT(scr != NULL && wsc != NULL); 1077 1078 ri = &scr->scr_ri; 1079 1080 if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) && 1081 (wsc->col < ri->ri_cols)) { 1082 1083 offset = ri->ri_cols * wsc->row + wsc->col; 1084 #ifdef WSDISPLAY_SCROLLSUPPORT 1085 offset += scr->scr_offset_to_zero; 1086 #endif 1087 wsc->letter = scr->scr_chars[offset]; 1088 attr = scr->scr_attrs[offset]; 1089 1090 /* 1091 * this is ugly. We need to break up an attribute into colours and 1092 * flags but there's no rasops method to do that so we must rely on 1093 * the 'canonical' encoding. 1094 */ 1095 #ifdef VCONS_DEBUG 1096 printf("vcons_getwschar: %d, %d, %x, %lx\n", wsc->row, 1097 wsc->col, wsc->letter, attr); 1098 #endif 1099 wsc->foreground = (attr >> 24) & 0xff; 1100 wsc->background = (attr >> 16) & 0xff; 1101 wsc->flags = attr & 0xff; 1102 return 0; 1103 } else 1104 return EINVAL; 1105 } 1106 1107 #ifdef WSDISPLAY_SCROLLSUPPORT 1108 1109 static void 1110 vcons_scroll(void *cookie, void *vs, int where) 1111 { 1112 struct vcons_screen *scr = vs; 1113 1114 if (where == 0) { 1115 scr->scr_line_wanted = 0; 1116 } else { 1117 scr->scr_line_wanted = scr->scr_line_wanted - where; 1118 if (scr->scr_line_wanted < 0) 1119 scr->scr_line_wanted = 0; 1120 if (scr->scr_line_wanted > scr->scr_lines_in_buffer) 1121 scr->scr_line_wanted = scr->scr_lines_in_buffer; 1122 } 1123 1124 if (scr->scr_line_wanted != scr->scr_current_line) { 1125 1126 vcons_do_scroll(scr); 1127 } 1128 } 1129 1130 static void 1131 vcons_do_scroll(struct vcons_screen *scr) 1132 { 1133 int dist, from, to, num; 1134 int r_offset, r_start; 1135 int i, j; 1136 1137 if (scr->scr_line_wanted == scr->scr_current_line) 1138 return; 1139 dist = scr->scr_line_wanted - scr->scr_current_line; 1140 scr->scr_current_line = scr->scr_line_wanted; 1141 scr->scr_current_offset = scr->scr_ri.ri_cols * 1142 (scr->scr_lines_in_buffer - scr->scr_current_line); 1143 if (abs(dist) >= scr->scr_ri.ri_rows) { 1144 vcons_redraw_screen(scr); 1145 return; 1146 } 1147 /* scroll and redraw only what we really have to */ 1148 if (dist > 0) { 1149 /* we scroll down */ 1150 from = 0; 1151 to = dist; 1152 num = scr->scr_ri.ri_rows - dist; 1153 /* now the redraw parameters */ 1154 r_offset = scr->scr_current_offset; 1155 r_start = 0; 1156 } else { 1157 /* scrolling up */ 1158 to = 0; 1159 from = -dist; 1160 num = scr->scr_ri.ri_rows + dist; 1161 r_offset = scr->scr_current_offset + num * scr->scr_ri.ri_cols; 1162 r_start = num; 1163 } 1164 scr->scr_vd->copyrows(scr, from, to, num); 1165 for (i = 0; i < abs(dist); i++) { 1166 for (j = 0; j < scr->scr_ri.ri_cols; j++) { 1167 #ifdef VCONS_DRAW_INTR 1168 vcons_putchar_cached(scr, i + r_start, j, 1169 scr->scr_chars[r_offset], 1170 scr->scr_attrs[r_offset]); 1171 #else 1172 scr->scr_vd->putchar(scr, i + r_start, j, 1173 scr->scr_chars[r_offset], 1174 scr->scr_attrs[r_offset]); 1175 #endif 1176 r_offset++; 1177 } 1178 } 1179 1180 if (scr->scr_line_wanted == 0) { 1181 /* this was a reset - need to draw the cursor */ 1182 scr->scr_ri.ri_flg &= ~RI_CURSOR; 1183 scr->scr_vd->cursor(scr, 1, scr->scr_ri.ri_crow, 1184 scr->scr_ri.ri_ccol); 1185 } 1186 } 1187 1188 #endif /* WSDISPLAY_SCROLLSUPPORT */ 1189 1190 #ifdef VCONS_DRAW_INTR 1191 static void 1192 vcons_intr(void *cookie) 1193 { 1194 struct vcons_data *vd = cookie; 1195 1196 softint_schedule(vd->intr_softint); 1197 } 1198 1199 static void 1200 vcons_softintr(void *cookie) 1201 { 1202 struct vcons_data *vd = cookie; 1203 struct vcons_screen *scr = vd->active; 1204 unsigned int dirty; 1205 1206 if (scr && vd->use_intr == 1) { 1207 if (!SCREEN_IS_BUSY(scr)) { 1208 dirty = atomic_swap_uint(&scr->scr_dirty, 0); 1209 if (dirty > 0) { 1210 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 1211 vcons_update_screen(scr); 1212 } 1213 } 1214 } 1215 1216 callout_schedule(&vd->intr, mstohz(33)); 1217 } 1218 1219 static void 1220 vcons_intr_enable(device_t dev) 1221 { 1222 /* the 'dev' arg we pass to config_interrupts isn't a device_t */ 1223 struct vcons_data *vd = (struct vcons_data *)dev; 1224 vd->use_intr = 1; 1225 callout_schedule(&vd->intr, mstohz(33)); 1226 } 1227 #endif /* VCONS_DRAW_INTR */ 1228 1229 void 1230 vcons_enable_polling(struct vcons_data *vd) 1231 { 1232 struct vcons_screen *scr = vd->active; 1233 1234 #ifdef VCONS_DRAW_INTR 1235 vd->use_intr = 0; 1236 #endif 1237 1238 if (scr && !SCREEN_IS_BUSY(scr)) { 1239 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 1240 vcons_redraw_screen(scr); 1241 } 1242 } 1243 1244 void 1245 vcons_disable_polling(struct vcons_data *vd) 1246 { 1247 #ifdef VCONS_DRAW_INTR 1248 struct vcons_screen *scr = vd->active; 1249 1250 if (!vd->intr_valid) 1251 return; 1252 1253 vd->use_intr = 1; 1254 if (scr) 1255 atomic_inc_uint(&scr->scr_dirty); 1256 #endif 1257 } 1258 1259 void 1260 vcons_hard_switch(struct vcons_screen *scr) 1261 { 1262 struct vcons_data *vd = scr->scr_vd; 1263 struct vcons_screen *oldscr = vd->active; 1264 1265 if (oldscr) { 1266 SCREEN_INVISIBLE(oldscr); 1267 oldscr->scr_ri.ri_flg &= ~RI_CURSOR; 1268 } 1269 SCREEN_VISIBLE(scr); 1270 vd->active = scr; 1271 vd->wanted = NULL; 1272 1273 if (vd->show_screen_cb != NULL) 1274 vd->show_screen_cb(scr); 1275 } 1276 1277 #ifdef VCONS_DRAW_INTR 1278 void 1279 vcons_invalidate_cache(struct vcons_data *vd) 1280 { 1281 int i; 1282 1283 if (vd->cells == 0) 1284 return; 1285 1286 for (i = 0; i > vd->cells; i++) { 1287 vd->chars[i] = -1; 1288 vd->attrs[i] = -1; 1289 } 1290 } 1291 #endif 1292