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 58static void vcons_dummy_init_screen(void *, struct vcons_screen *, int, 59 long *); 60 61static int vcons_ioctl(void *, void *, u_long, void *, int, struct lwp *); 62static int vcons_alloc_screen(void *, const struct wsscreen_descr *, void **, 63 int *, int *, long *); 64static void vcons_free_screen(void *, void *); 65static int vcons_show_screen(void *, void *, int, void (*)(void *, int, int), 66 void *); 67 68#ifdef WSDISPLAY_SCROLLSUPPORT 69static void vcons_scroll(void *, void *, int); 70static void vcons_do_scroll(struct vcons_screen *); 71#endif 72 73static void vcons_do_switch(void *); 74 75/* methods that work only on text buffers */ 76static void vcons_copycols_buffer(void *, int, int, int, int); 77static void vcons_erasecols_buffer(void *, int, int, int, long); 78static void vcons_copyrows_buffer(void *, int, int, int); 79static void vcons_eraserows_buffer(void *, int, int, long); 80static 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 */ 86static void vcons_copycols(void *, int, int, int, int); 87static void vcons_erasecols(void *, int, int, int, long); 88static void vcons_copyrows(void *, int, int, int); 89static void vcons_eraserows(void *, int, int, long); 90static void vcons_putchar(void *, int, int, u_int, long); 91#ifdef VCONS_DRAW_INTR 92static void vcons_putchar_cached(void *, int, int, u_int, long); 93#endif 94static void vcons_cursor(void *, int, int, int); 95 96/* 97 * methods that avoid framebuffer reads 98 */ 99static void vcons_copycols_noread(void *, int, int, int, int); 100static void vcons_copyrows_noread(void *, int, int, int); 101 102 103/* support for reading/writing text buffers. For wsmoused */ 104static int vcons_putwschar(struct vcons_screen *, struct wsdisplay_char *); 105static int vcons_getwschar(struct vcons_screen *, struct wsdisplay_char *); 106 107static void vcons_lock(struct vcons_screen *); 108static void vcons_unlock(struct vcons_screen *); 109 110#ifdef VCONS_DRAW_INTR 111static void vcons_intr(void *); 112static void vcons_softintr(void *); 113static void vcons_intr_enable(device_t); 114#endif 115 116int 117vcons_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 176static void 177vcons_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 190static void 191vcons_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 204static void 205vcons_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 219int 220vcons_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 335static void 336vcons_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 402void 403vcons_redraw_screen(struct vcons_screen *scr) 404{ 405 uint32_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 457void 458vcons_update_screen(struct vcons_screen *scr) 459{ 460 uint32_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 511static int 512vcons_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 = 0; 517 518 519 switch (cmd) { 520 case WSDISPLAYIO_GETWSCHAR: 521 error = vcons_getwschar((struct vcons_screen *)vs, 522 (struct wsdisplay_char *)data); 523 break; 524 525 case WSDISPLAYIO_PUTWSCHAR: 526 error = vcons_putwschar((struct vcons_screen *)vs, 527 (struct wsdisplay_char *)data); 528 break; 529 530 case WSDISPLAYIO_SET_POLLING: { 531 int poll = *(int *)data; 532 533 /* first call the driver's ioctl handler */ 534 if (vd->ioctl != NULL) 535 error = (*vd->ioctl)(v, vs, cmd, data, flag, l); 536 if (poll) { 537 vcons_enable_polling(vd); 538 vcons_hard_switch(LIST_FIRST(&vd->screens)); 539 } else 540 vcons_disable_polling(vd); 541 } 542 break; 543 544 default: 545 if (vd->ioctl != NULL) 546 error = (*vd->ioctl)(v, vs, cmd, data, flag, l); 547 else 548 error = EPASSTHROUGH; 549 } 550 551 return error; 552} 553 554static int 555vcons_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep, 556 int *curxp, int *curyp, long *defattrp) 557{ 558 struct vcons_data *vd = v; 559 struct vcons_screen *scr; 560 int ret; 561 562 scr = malloc(sizeof(struct vcons_screen), M_DEVBUF, M_WAITOK | M_ZERO); 563 if (scr == NULL) 564 return ENOMEM; 565 566 scr->scr_flags = 0; 567 scr->scr_status = 0; 568 scr->scr_busy = 0; 569 scr->scr_type = type; 570 571 ret = vcons_init_screen(vd, scr, 0, defattrp); 572 if (ret != 0) { 573 free(scr, M_DEVBUF); 574 return ret; 575 } 576 577 if (vd->active == NULL) { 578 SCREEN_VISIBLE(scr); 579 vd->active = scr; 580 vd->currenttype = type; 581 } 582 583 *cookiep = scr; 584 *curxp = scr->scr_ri.ri_ccol; 585 *curyp = scr->scr_ri.ri_crow; 586 return 0; 587} 588 589static void 590vcons_free_screen(void *v, void *cookie) 591{ 592 struct vcons_data *vd = v; 593 struct vcons_screen *scr = cookie; 594 595 vcons_lock(scr); 596 /* there should be no rasops activity here */ 597 598 LIST_REMOVE(scr, next); 599 600 if ((scr->scr_flags & VCONS_SCREEN_IS_STATIC) == 0) { 601 free(scr->scr_attrs, M_DEVBUF); 602 free(scr, M_DEVBUF); 603 } else { 604 /* 605 * maybe we should just restore the old rasops_info methods 606 * and free the character/attribute buffer here? 607 */ 608#ifdef VCONS_DEBUG 609 panic("vcons_free_screen: console"); 610#else 611 printf("vcons_free_screen: console\n"); 612#endif 613 } 614 615 if (vd->active == scr) 616 vd->active = NULL; 617} 618 619static int 620vcons_show_screen(void *v, void *cookie, int waitok, 621 void (*cb)(void *, int, int), void *cb_arg) 622{ 623 struct vcons_data *vd = v; 624 struct vcons_screen *scr; 625 626 scr = cookie; 627 if (scr == vd->active) 628 return 0; 629 630 vd->wanted = scr; 631 vd->switch_cb = cb; 632 vd->switch_cb_arg = cb_arg; 633 if (cb) { 634 callout_schedule(&vd->switch_callout, 0); 635 return EAGAIN; 636 } 637 638 vcons_do_switch(vd); 639 return 0; 640} 641 642/* wrappers for rasops_info methods */ 643 644static void 645vcons_copycols_buffer(void *cookie, int row, int srccol, int dstcol, int ncols) 646{ 647 struct rasops_info *ri = cookie; 648 struct vcons_screen *scr = ri->ri_hw; 649 int from = srccol + row * ri->ri_cols; 650 int to = dstcol + row * ri->ri_cols; 651 652#ifdef WSDISPLAY_SCROLLSUPPORT 653 int offset; 654 offset = scr->scr_offset_to_zero; 655 656 memmove(&scr->scr_attrs[offset + to], &scr->scr_attrs[offset + from], 657 ncols * sizeof(long)); 658 memmove(&scr->scr_chars[offset + to], &scr->scr_chars[offset + from], 659 ncols * sizeof(uint32_t)); 660#else 661 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 662 ncols * sizeof(long)); 663 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 664 ncols * sizeof(uint32_t)); 665#endif 666 667#ifdef VCONS_DRAW_INTR 668 atomic_inc_uint(&scr->scr_dirty); 669#endif 670} 671 672static void 673vcons_copycols(void *cookie, int row, int srccol, int dstcol, int ncols) 674{ 675 struct rasops_info *ri = cookie; 676 struct vcons_screen *scr = ri->ri_hw; 677 678 vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols); 679 680#if defined(VCONS_DRAW_INTR) 681 if (scr->scr_vd->use_intr) 682 return; 683#endif 684 685 vcons_lock(scr); 686 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 687 scr->scr_vd->copycols(cookie, row, srccol, dstcol, ncols); 688 } 689 vcons_unlock(scr); 690} 691 692static void 693vcons_copycols_noread(void *cookie, int row, int srccol, int dstcol, int ncols) 694{ 695 struct rasops_info *ri = cookie; 696 struct vcons_screen *scr = ri->ri_hw; 697 struct vcons_data *vd = scr->scr_vd; 698 699 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 700 int pos, c, offset, ppos; 701 702#ifdef WSDISPLAY_SCROLLSUPPORT 703 offset = scr->scr_current_offset; 704#else 705 offset = 0; 706#endif 707 ppos = ri->ri_cols * row + dstcol; 708 pos = ppos + offset; 709 for (c = dstcol; c < (dstcol + ncols); c++) { 710#ifdef VCONS_DRAW_INTR 711 if ((scr->scr_chars[pos] != vd->chars[ppos]) || 712 (scr->scr_attrs[pos] != vd->attrs[ppos])) { 713 vd->putchar(cookie, row, c, 714 scr->scr_chars[pos], scr->scr_attrs[pos]); 715 vd->chars[ppos] = scr->scr_chars[pos]; 716 vd->attrs[ppos] = scr->scr_attrs[pos]; 717 } 718#else 719 vd->putchar(cookie, row, c, scr->scr_chars[pos], 720 scr->scr_attrs[pos]); 721#endif 722 pos++; 723 ppos++; 724 } 725 } 726} 727 728static void 729vcons_erasecols_buffer(void *cookie, int row, int startcol, int ncols, long fillattr) 730{ 731 struct rasops_info *ri = cookie; 732 struct vcons_screen *scr = ri->ri_hw; 733 int start = startcol + row * ri->ri_cols; 734 int end = start + ncols, i; 735 736#ifdef WSDISPLAY_SCROLLSUPPORT 737 int offset; 738 offset = scr->scr_offset_to_zero; 739 740 for (i = start; i < end; i++) { 741 scr->scr_attrs[offset + i] = fillattr; 742 scr->scr_chars[offset + i] = 0x20; 743 } 744#else 745 for (i = start; i < end; i++) { 746 scr->scr_attrs[i] = fillattr; 747 scr->scr_chars[i] = 0x20; 748 } 749#endif 750 751#ifdef VCONS_DRAW_INTR 752 atomic_inc_uint(&scr->scr_dirty); 753#endif 754} 755 756#ifdef VCONS_DRAW_INTR 757static void 758vcons_erasecols_cached(void *cookie, int row, int startcol, int ncols, long fillattr) 759{ 760 struct rasops_info *ri = cookie; 761 struct vcons_screen *scr = ri->ri_hw; 762 struct vcons_data *vd = scr->scr_vd; 763 int i, pos = row * ri->ri_cols + startcol; 764 765 for (i = pos; i < ncols; i++) { 766 vd->chars[i] = 0x20; 767 vd->attrs[i] = fillattr; 768 } 769 vd->erasecols(cookie, row, startcol, ncols, fillattr); 770} 771#endif 772 773static void 774vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr) 775{ 776 struct rasops_info *ri = cookie; 777 struct vcons_screen *scr = ri->ri_hw; 778 779 vcons_erasecols_buffer(cookie, row, startcol, ncols, fillattr); 780 781#if defined(VCONS_DRAW_INTR) 782 if (scr->scr_vd->use_intr) 783 return; 784#endif 785 786 vcons_lock(scr); 787 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 788#ifdef VCONS_DRAW_INTR 789 vcons_erasecols_cached(cookie, row, startcol, ncols, 790 fillattr); 791#else 792 scr->scr_vd->erasecols(cookie, row, startcol, ncols, fillattr); 793#endif 794 } 795 vcons_unlock(scr); 796} 797 798static void 799vcons_copyrows_buffer(void *cookie, int srcrow, int dstrow, int nrows) 800{ 801 struct rasops_info *ri = cookie; 802 struct vcons_screen *scr = ri->ri_hw; 803 int from, to, len; 804 805#ifdef WSDISPLAY_SCROLLSUPPORT 806 int offset; 807 offset = scr->scr_offset_to_zero; 808 809 /* do we need to scroll the back buffer? */ 810 if (dstrow == 0) { 811 from = ri->ri_cols * srcrow; 812 to = ri->ri_cols * dstrow; 813 814 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 815 scr->scr_offset_to_zero * sizeof(long)); 816 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 817 scr->scr_offset_to_zero * sizeof(uint32_t)); 818 } 819 from = ri->ri_cols * srcrow + offset; 820 to = ri->ri_cols * dstrow + offset; 821 len = ri->ri_cols * nrows; 822 823#else 824 from = ri->ri_cols * srcrow; 825 to = ri->ri_cols * dstrow; 826 len = ri->ri_cols * nrows; 827#endif 828 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 829 len * sizeof(long)); 830 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 831 len * sizeof(uint32_t)); 832 833#ifdef VCONS_DRAW_INTR 834 atomic_inc_uint(&scr->scr_dirty); 835#endif 836} 837 838static void 839vcons_copyrows(void *cookie, int srcrow, int dstrow, int nrows) 840{ 841 struct rasops_info *ri = cookie; 842 struct vcons_screen *scr = ri->ri_hw; 843 844 vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows); 845 846#if defined(VCONS_DRAW_INTR) 847 if (scr->scr_vd->use_intr) 848 return; 849#endif 850 851 vcons_lock(scr); 852 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 853 scr->scr_vd->copyrows(cookie, srcrow, dstrow, nrows); 854 } 855 vcons_unlock(scr); 856} 857 858static void 859vcons_copyrows_noread(void *cookie, int srcrow, int dstrow, int nrows) 860{ 861 struct rasops_info *ri = cookie; 862 struct vcons_screen *scr = ri->ri_hw; 863 struct vcons_data *vd = scr->scr_vd; 864 int dist; 865 866 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 867 int pos, l, c, offset, ppos; 868 869#ifdef WSDISPLAY_SCROLLSUPPORT 870 offset = scr->scr_current_offset; 871#else 872 offset = 0; 873#endif 874 dist = (dstrow - srcrow) * ri->ri_cols; 875 ppos = ri->ri_cols * dstrow; 876 pos = ppos + offset; 877 for (l = dstrow; l < (dstrow + nrows); l++) { 878 for (c = 0; c < ri->ri_cols; c++) { 879#ifdef VCONS_DRAW_INTR 880 if ((scr->scr_chars[pos] != vd->chars[ppos]) || 881 (scr->scr_attrs[pos] != vd->attrs[ppos])) { 882 vd->putchar(cookie, l, c, 883 scr->scr_chars[pos], scr->scr_attrs[pos]); 884 vd->chars[ppos] = scr->scr_chars[pos]; 885 vd->attrs[ppos] = scr->scr_attrs[pos]; 886 } 887#else 888 vd->putchar(cookie, l, c, scr->scr_chars[pos], 889 scr->scr_attrs[pos]); 890#endif 891 pos++; 892 ppos++; 893 } 894 } 895 } 896} 897 898static void 899vcons_eraserows_buffer(void *cookie, int row, int nrows, long fillattr) 900{ 901 struct rasops_info *ri = cookie; 902 struct vcons_screen *scr = ri->ri_hw; 903 int start, end, i; 904 905#ifdef WSDISPLAY_SCROLLSUPPORT 906 int offset; 907 offset = scr->scr_offset_to_zero; 908 909 start = ri->ri_cols * row + offset; 910 end = ri->ri_cols * (row + nrows) + offset; 911#else 912 start = ri->ri_cols * row; 913 end = ri->ri_cols * (row + nrows); 914#endif 915 916 for (i = start; i < end; i++) { 917 scr->scr_attrs[i] = fillattr; 918 scr->scr_chars[i] = 0x20; 919 } 920 921#ifdef VCONS_DRAW_INTR 922 atomic_inc_uint(&scr->scr_dirty); 923#endif 924} 925 926static void 927vcons_eraserows(void *cookie, int row, int nrows, long fillattr) 928{ 929 struct rasops_info *ri = cookie; 930 struct vcons_screen *scr = ri->ri_hw; 931 932 vcons_eraserows_buffer(cookie, row, nrows, fillattr); 933 934#if defined(VCONS_DRAW_INTR) 935 if (scr->scr_vd->use_intr) 936 return; 937#endif 938 939 vcons_lock(scr); 940 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 941 scr->scr_vd->eraserows(cookie, row, nrows, fillattr); 942 } 943 vcons_unlock(scr); 944} 945 946static void 947vcons_putchar_buffer(void *cookie, int row, int col, u_int c, long attr) 948{ 949 struct rasops_info *ri = cookie; 950 struct vcons_screen *scr = ri->ri_hw; 951 int pos; 952 953#ifdef WSDISPLAY_SCROLLSUPPORT 954 int offset; 955 offset = scr->scr_offset_to_zero; 956 957 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) && 958 (col < ri->ri_cols)) { 959 pos = col + row * ri->ri_cols; 960 scr->scr_attrs[pos + offset] = attr; 961 scr->scr_chars[pos + offset] = c; 962 } 963#else 964 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) && 965 (col < ri->ri_cols)) { 966 pos = col + row * ri->ri_cols; 967 scr->scr_attrs[pos] = attr; 968 scr->scr_chars[pos] = c; 969 } 970#endif 971 972#ifdef VCONS_DRAW_INTR 973 atomic_inc_uint(&scr->scr_dirty); 974#endif 975} 976 977#ifdef VCONS_DRAW_INTR 978static void 979vcons_putchar_cached(void *cookie, int row, int col, u_int c, long attr) 980{ 981 struct rasops_info *ri = cookie; 982 struct vcons_screen *scr = ri->ri_hw; 983 struct vcons_data *vd = scr->scr_vd; 984 int pos = row * ri->ri_cols + col; 985 986 if ((vd->chars == NULL) || (vd->attrs == NULL)) { 987 vd->putchar(cookie, row, col, c, attr); 988 return; 989 } 990 if ((vd->chars[pos] != c) || (vd->attrs[pos] != attr)) { 991 vd->attrs[pos] = attr; 992 vd->chars[pos] = c; 993 vd->putchar(cookie, row, col, c, attr); 994 } 995} 996#endif 997 998static void 999vcons_putchar(void *cookie, int row, int col, u_int c, long attr) 1000{ 1001 struct rasops_info *ri = cookie; 1002 struct vcons_screen *scr = ri->ri_hw; 1003 1004 vcons_putchar_buffer(cookie, row, col, c, attr); 1005 1006#if defined(VCONS_DRAW_INTR) 1007 if (scr->scr_vd->use_intr) 1008 return; 1009#endif 1010 1011 vcons_lock(scr); 1012 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1013#ifdef VCONS_DRAW_INTR 1014 vcons_putchar_cached(cookie, row, col, c, attr); 1015#else 1016 scr->scr_vd->putchar(cookie, row, col, c, attr); 1017#endif 1018 } 1019 vcons_unlock(scr); 1020} 1021 1022static void 1023vcons_cursor(void *cookie, int on, int row, int col) 1024{ 1025 struct rasops_info *ri = cookie; 1026 struct vcons_screen *scr = ri->ri_hw; 1027 1028 1029#if defined(VCONS_DRAW_INTR) 1030 if (scr->scr_vd->use_intr) { 1031 vcons_lock(scr); 1032 if (scr->scr_ri.ri_crow != row || scr->scr_ri.ri_ccol != col) { 1033 scr->scr_ri.ri_crow = row; 1034 scr->scr_ri.ri_ccol = col; 1035 atomic_inc_uint(&scr->scr_dirty); 1036 } 1037 vcons_unlock(scr); 1038 return; 1039 } 1040#endif 1041 1042 vcons_lock(scr); 1043 1044 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1045 scr->scr_vd->cursor(cookie, on, row, col); 1046 } else { 1047 scr->scr_ri.ri_crow = row; 1048 scr->scr_ri.ri_ccol = col; 1049 } 1050 vcons_unlock(scr); 1051} 1052 1053/* methods to read/write characters via ioctl() */ 1054 1055static int 1056vcons_putwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 1057{ 1058 long attr; 1059 struct rasops_info *ri; 1060 1061 KASSERT(scr != NULL && wsc != NULL); 1062 1063 ri = &scr->scr_ri; 1064 1065 if (__predict_false((unsigned int)wsc->col > ri->ri_cols || 1066 (unsigned int)wsc->row > ri->ri_rows)) 1067 return (EINVAL); 1068 1069 if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) && 1070 (wsc->col < ri->ri_cols)) { 1071 1072 ri->ri_ops.allocattr(ri, wsc->foreground, wsc->background, 1073 wsc->flags, &attr); 1074 vcons_putchar(ri, wsc->row, wsc->col, wsc->letter, attr); 1075#ifdef VCONS_DEBUG 1076 printf("vcons_putwschar(%d, %d, %x, %lx\n", wsc->row, wsc->col, 1077 wsc->letter, attr); 1078#endif 1079 return 0; 1080 } else 1081 return EINVAL; 1082} 1083 1084static int 1085vcons_getwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 1086{ 1087 int offset; 1088 long attr; 1089 struct rasops_info *ri; 1090 1091 KASSERT(scr != NULL && wsc != NULL); 1092 1093 ri = &scr->scr_ri; 1094 1095 if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) && 1096 (wsc->col < ri->ri_cols)) { 1097 1098 offset = ri->ri_cols * wsc->row + wsc->col; 1099#ifdef WSDISPLAY_SCROLLSUPPORT 1100 offset += scr->scr_offset_to_zero; 1101#endif 1102 wsc->letter = scr->scr_chars[offset]; 1103 attr = scr->scr_attrs[offset]; 1104 1105 /* 1106 * this is ugly. We need to break up an attribute into colours and 1107 * flags but there's no rasops method to do that so we must rely on 1108 * the 'canonical' encoding. 1109 */ 1110#ifdef VCONS_DEBUG 1111 printf("vcons_getwschar: %d, %d, %x, %lx\n", wsc->row, 1112 wsc->col, wsc->letter, attr); 1113#endif 1114 wsc->foreground = (attr >> 24) & 0xff; 1115 wsc->background = (attr >> 16) & 0xff; 1116 wsc->flags = attr & 0xff; 1117 return 0; 1118 } else 1119 return EINVAL; 1120} 1121 1122#ifdef WSDISPLAY_SCROLLSUPPORT 1123 1124static void 1125vcons_scroll(void *cookie, void *vs, int where) 1126{ 1127 struct vcons_screen *scr = vs; 1128 1129 if (where == 0) { 1130 scr->scr_line_wanted = 0; 1131 } else { 1132 scr->scr_line_wanted = scr->scr_line_wanted - where; 1133 if (scr->scr_line_wanted < 0) 1134 scr->scr_line_wanted = 0; 1135 if (scr->scr_line_wanted > scr->scr_lines_in_buffer) 1136 scr->scr_line_wanted = scr->scr_lines_in_buffer; 1137 } 1138 1139 if (scr->scr_line_wanted != scr->scr_current_line) { 1140 1141 vcons_do_scroll(scr); 1142 } 1143} 1144 1145static void 1146vcons_do_scroll(struct vcons_screen *scr) 1147{ 1148 int dist, from, to, num; 1149 int r_offset, r_start; 1150 int i, j; 1151 1152 if (scr->scr_line_wanted == scr->scr_current_line) 1153 return; 1154 dist = scr->scr_line_wanted - scr->scr_current_line; 1155 scr->scr_current_line = scr->scr_line_wanted; 1156 scr->scr_current_offset = scr->scr_ri.ri_cols * 1157 (scr->scr_lines_in_buffer - scr->scr_current_line); 1158 if (abs(dist) >= scr->scr_ri.ri_rows) { 1159 vcons_redraw_screen(scr); 1160 return; 1161 } 1162 /* scroll and redraw only what we really have to */ 1163 if (dist > 0) { 1164 /* we scroll down */ 1165 from = 0; 1166 to = dist; 1167 num = scr->scr_ri.ri_rows - dist; 1168 /* now the redraw parameters */ 1169 r_offset = scr->scr_current_offset; 1170 r_start = 0; 1171 } else { 1172 /* scrolling up */ 1173 to = 0; 1174 from = -dist; 1175 num = scr->scr_ri.ri_rows + dist; 1176 r_offset = scr->scr_current_offset + num * scr->scr_ri.ri_cols; 1177 r_start = num; 1178 } 1179 scr->scr_vd->copyrows(scr, from, to, num); 1180 for (i = 0; i < abs(dist); i++) { 1181 for (j = 0; j < scr->scr_ri.ri_cols; j++) { 1182#ifdef VCONS_DRAW_INTR 1183 vcons_putchar_cached(scr, i + r_start, j, 1184 scr->scr_chars[r_offset], 1185 scr->scr_attrs[r_offset]); 1186#else 1187 scr->scr_vd->putchar(scr, i + r_start, j, 1188 scr->scr_chars[r_offset], 1189 scr->scr_attrs[r_offset]); 1190#endif 1191 r_offset++; 1192 } 1193 } 1194 1195 if (scr->scr_line_wanted == 0) { 1196 /* this was a reset - need to draw the cursor */ 1197 scr->scr_ri.ri_flg &= ~RI_CURSOR; 1198 scr->scr_vd->cursor(scr, 1, scr->scr_ri.ri_crow, 1199 scr->scr_ri.ri_ccol); 1200 } 1201} 1202 1203#endif /* WSDISPLAY_SCROLLSUPPORT */ 1204 1205#ifdef VCONS_DRAW_INTR 1206static void 1207vcons_intr(void *cookie) 1208{ 1209 struct vcons_data *vd = cookie; 1210 1211 softint_schedule(vd->intr_softint); 1212} 1213 1214static void 1215vcons_softintr(void *cookie) 1216{ 1217 struct vcons_data *vd = cookie; 1218 struct vcons_screen *scr = vd->active; 1219 unsigned int dirty; 1220 1221 if (scr && vd->use_intr == 1) { 1222 if (!SCREEN_IS_BUSY(scr)) { 1223 dirty = atomic_swap_uint(&scr->scr_dirty, 0); 1224 if (dirty > 0) { 1225 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 1226 vcons_update_screen(scr); 1227 } 1228 } 1229 } 1230 1231 callout_schedule(&vd->intr, mstohz(33)); 1232} 1233 1234static void 1235vcons_intr_enable(device_t dev) 1236{ 1237 /* the 'dev' arg we pass to config_interrupts isn't a device_t */ 1238 struct vcons_data *vd = (struct vcons_data *)dev; 1239 vd->use_intr = 1; 1240 callout_schedule(&vd->intr, mstohz(33)); 1241} 1242#endif /* VCONS_DRAW_INTR */ 1243 1244void 1245vcons_enable_polling(struct vcons_data *vd) 1246{ 1247 struct vcons_screen *scr = vd->active; 1248 1249#ifdef VCONS_DRAW_INTR 1250 vd->use_intr = 0; 1251#endif 1252 1253 if (scr && !SCREEN_IS_BUSY(scr)) { 1254 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 1255 vcons_redraw_screen(scr); 1256 } 1257} 1258 1259void 1260vcons_disable_polling(struct vcons_data *vd) 1261{ 1262#ifdef VCONS_DRAW_INTR 1263 struct vcons_screen *scr = vd->active; 1264 1265 if (!vd->intr_valid) 1266 return; 1267 1268 vd->use_intr = 1; 1269 if (scr) 1270 atomic_inc_uint(&scr->scr_dirty); 1271#endif 1272} 1273 1274void 1275vcons_hard_switch(struct vcons_screen *scr) 1276{ 1277 struct vcons_data *vd = scr->scr_vd; 1278 struct vcons_screen *oldscr = vd->active; 1279 1280 if (oldscr) { 1281 SCREEN_INVISIBLE(oldscr); 1282 oldscr->scr_ri.ri_flg &= ~RI_CURSOR; 1283 } 1284 SCREEN_VISIBLE(scr); 1285 vd->active = scr; 1286 vd->wanted = NULL; 1287 1288 if (vd->show_screen_cb != NULL) 1289 vd->show_screen_cb(scr); 1290} 1291 1292#ifdef VCONS_DRAW_INTR 1293void 1294vcons_invalidate_cache(struct vcons_data *vd) 1295{ 1296 int i; 1297 1298 if (vd->cells == 0) 1299 return; 1300 1301 for (i = 0; i > vd->cells; i++) { 1302 vd->chars[i] = -1; 1303 vd->attrs[i] = -1; 1304 } 1305} 1306#endif 1307