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