1 /* $NetBSD: wsdisplay_vcons.c,v 1.64 2022/07/18 11:09:22 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 2005, 2006 Michael Lorenz 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: wsdisplay_vcons.c,v 1.64 2022/07/18 11:09:22 martin Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/buf.h> 36 #include <sys/device.h> 37 #include <sys/ioctl.h> 38 #include <sys/malloc.h> 39 #include <sys/mman.h> 40 #include <sys/tty.h> 41 #include <sys/conf.h> 42 #include <sys/proc.h> 43 #include <sys/kthread.h> 44 #include <sys/tprintf.h> 45 #include <sys/atomic.h> 46 #include <sys/kmem.h> 47 48 #include <dev/wscons/wsdisplayvar.h> 49 #include <dev/wscons/wsconsio.h> 50 #include <dev/wsfont/wsfont.h> 51 #include <dev/rasops/rasops.h> 52 53 #include <dev/wscons/wsdisplay_vconsvar.h> 54 55 #ifdef _KERNEL_OPT 56 #include "opt_wsemul.h" 57 #include "opt_wsdisplay_compat.h" 58 #include "opt_vcons.h" 59 #endif 60 61 #ifdef VCONS_DEBUG 62 #define DPRINTF printf 63 #else 64 #define DPRINTF if (0) printf 65 #endif 66 67 struct vcons_data_private { 68 /* accessops */ 69 int (*ioctl)(void *, void *, u_long, void *, int, struct lwp *); 70 71 /* rasops */ 72 void (*copycols)(void *, int, int, int, int); 73 void (*erasecols)(void *, int, int, int, long); 74 void (*copyrows)(void *, int, int, int); 75 void (*eraserows)(void *, int, int, long); 76 void (*cursor)(void *, int, int, int); 77 78 /* virtual screen management stuff */ 79 void (*switch_cb)(void *, int, int); 80 void *switch_cb_arg; 81 struct callout switch_callout; 82 uint32_t switch_pending; 83 LIST_HEAD(, vcons_screen) screens; 84 struct vcons_screen *wanted; 85 const struct wsscreen_descr *currenttype; 86 struct wsscreen_descr *defaulttype; 87 int switch_poll_count; 88 89 #ifdef VCONS_DRAW_INTR 90 int cells; 91 long *attrs; 92 uint32_t *chars; 93 int cursor_offset; 94 callout_t intr; 95 int intr_valid; 96 void *intr_softint; 97 int use_intr; /* use intr drawing when non-zero */ 98 #endif 99 }; 100 101 static void vcons_dummy_init_screen(void *, struct vcons_screen *, int, 102 long *); 103 104 static int vcons_ioctl(void *, void *, u_long, void *, int, struct lwp *); 105 static int vcons_alloc_screen(void *, const struct wsscreen_descr *, void **, 106 int *, int *, long *); 107 static void vcons_free_screen(void *, void *); 108 static int vcons_show_screen(void *, void *, int, void (*)(void *, int, int), 109 void *); 110 static int vcons_load_font(void *, void *, struct wsdisplay_font *); 111 112 #ifdef WSDISPLAY_SCROLLSUPPORT 113 static void vcons_scroll(void *, void *, int); 114 static void vcons_do_scroll(struct vcons_screen *); 115 #endif 116 117 static void vcons_do_switch(void *); 118 119 /* methods that work only on text buffers */ 120 static void vcons_copycols_buffer(void *, int, int, int, int); 121 static void vcons_erasecols_buffer(void *, int, int, int, long); 122 static void vcons_copyrows_buffer(void *, int, int, int); 123 static void vcons_eraserows_buffer(void *, int, int, long); 124 static void vcons_putchar_buffer(void *, int, int, u_int, long); 125 126 /* 127 * actual wrapper methods which call both the _buffer ones above and the 128 * driver supplied ones to do the drawing 129 */ 130 static void vcons_copycols(void *, int, int, int, int); 131 static void vcons_erasecols(void *, int, int, int, long); 132 static void vcons_copyrows(void *, int, int, int); 133 static void vcons_eraserows(void *, int, int, long); 134 static void vcons_putchar(void *, int, int, u_int, long); 135 #ifdef VCONS_DRAW_INTR 136 static void vcons_erasecols_cached(void *, int, int, int, long); 137 static void vcons_eraserows_cached(void *, int, int, long); 138 static void vcons_putchar_cached(void *, int, int, u_int, long); 139 #endif 140 static void vcons_cursor(void *, int, int, int); 141 static void vcons_cursor_noread(void *, int, int, int); 142 143 /* 144 * methods that avoid framebuffer reads 145 */ 146 static void vcons_copycols_noread(void *, int, int, int, int); 147 static void vcons_copyrows_noread(void *, int, int, int); 148 149 150 /* support for reading/writing text buffers. For wsmoused */ 151 static int vcons_putwschar(struct vcons_screen *, struct wsdisplay_char *); 152 static int vcons_getwschar(struct vcons_screen *, struct wsdisplay_char *); 153 154 static void vcons_lock(struct vcons_screen *); 155 static void vcons_unlock(struct vcons_screen *); 156 157 #ifdef VCONS_DRAW_INTR 158 static void vcons_intr(void *); 159 static void vcons_softintr(void *); 160 static void vcons_init_thread(void *); 161 static void vcons_invalidate_cache(struct vcons_data *); 162 #endif 163 164 static inline bool 165 vcons_use_intr(const struct vcons_screen *scr) 166 { 167 #ifdef VCONS_DRAW_INTR 168 return scr->scr_vd->private->use_intr; 169 #else 170 return false; 171 #endif 172 } 173 174 static inline void 175 vcons_dirty(struct vcons_screen *scr) 176 { 177 #ifdef VCONS_DRAW_INTR 178 membar_release(); 179 atomic_inc_uint(&scr->scr_dirty); 180 #endif 181 } 182 183 static int 184 vcons_init_common(struct vcons_data *vd, void *cookie, 185 struct wsscreen_descr *def, struct wsdisplay_accessops *ao, 186 int enable_intr) 187 { 188 struct vcons_data_private *vdp; 189 190 /* zero out everything so we can rely on untouched fields being 0 */ 191 memset(vd, 0, sizeof(struct vcons_data)); 192 193 vd->private = vdp = kmem_zalloc(sizeof(*vdp), KM_SLEEP); 194 vd->cookie = cookie; 195 196 vd->init_screen = vcons_dummy_init_screen; 197 vd->show_screen_cb = NULL; 198 199 /* keep a copy of the accessops that we replace below with our 200 * own wrappers */ 201 vdp->ioctl = ao->ioctl; 202 203 /* configure the accessops */ 204 ao->ioctl = vcons_ioctl; 205 ao->alloc_screen = vcons_alloc_screen; 206 ao->free_screen = vcons_free_screen; 207 ao->show_screen = vcons_show_screen; 208 ao->load_font = vcons_load_font; 209 #ifdef WSDISPLAY_SCROLLSUPPORT 210 ao->scroll = vcons_scroll; 211 #endif 212 213 LIST_INIT(&vdp->screens); 214 vd->active = NULL; 215 vdp->wanted = NULL; 216 vdp->currenttype = def; 217 vdp->defaulttype = def; 218 callout_init(&vdp->switch_callout, 0); 219 callout_setfunc(&vdp->switch_callout, vcons_do_switch, vd); 220 #ifdef VCONS_DRAW_INTR 221 vdp->cells = 0; 222 vdp->attrs = NULL; 223 vdp->chars = NULL; 224 vdp->cursor_offset = -1; 225 #endif 226 227 /* 228 * a lock to serialize access to the framebuffer. 229 * when switching screens we need to make sure there's no rasops 230 * operation in progress 231 */ 232 #ifdef DIAGNOSTIC 233 vdp->switch_poll_count = 0; 234 #endif 235 #ifdef VCONS_DRAW_INTR 236 if (enable_intr) { 237 vdp->intr_softint = softint_establish(SOFTINT_SERIAL, 238 vcons_softintr, vd); 239 callout_init(&vdp->intr, CALLOUT_MPSAFE); 240 callout_setfunc(&vdp->intr, vcons_intr, vd); 241 vdp->intr_valid = 1; 242 243 if (kthread_create(PRI_NONE, 0, NULL, vcons_init_thread, vd, 244 NULL, "vcons_init") != 0) { 245 printf("%s: unable to create thread.\n", __func__); 246 return -1; 247 } 248 } 249 #endif 250 return 0; 251 } 252 253 int 254 vcons_init(struct vcons_data *vd, void *cookie, 255 struct wsscreen_descr *def, struct wsdisplay_accessops *ao) 256 { 257 return vcons_init_common(vd, cookie, def, ao, 1); 258 } 259 260 int 261 vcons_earlyinit(struct vcons_data *vd, void *cookie, 262 struct wsscreen_descr *def, struct wsdisplay_accessops *ao) 263 { 264 return vcons_init_common(vd, cookie, def, ao, 0); 265 } 266 267 static void 268 vcons_lock(struct vcons_screen *scr) 269 { 270 #ifdef VCONS_PARANOIA 271 int s; 272 273 s = splhigh(); 274 #endif 275 SCREEN_BUSY(scr); 276 #ifdef VCONS_PARANOIA 277 splx(s); 278 #endif 279 } 280 281 static void 282 vcons_unlock(struct vcons_screen *scr) 283 { 284 #ifdef VCONS_PARANOIA 285 int s; 286 287 s = splhigh(); 288 #endif 289 SCREEN_IDLE(scr); 290 #ifdef VCONS_PARANOIA 291 splx(s); 292 #endif 293 } 294 295 static void 296 vcons_dummy_init_screen(void *cookie, 297 struct vcons_screen *scr, int exists, 298 long *defattr) 299 { 300 301 /* 302 * default init_screen() method. 303 * Needs to be overwritten so we bitch and whine in case anyone ends 304 * up in here. 305 */ 306 printf("vcons_init_screen: dummy function called. Your driver is " 307 "supposed to supply a replacement for proper operation\n"); 308 } 309 310 static int 311 vcons_alloc_buffers(struct vcons_data *vd, struct vcons_screen *scr) 312 { 313 struct rasops_info *ri = &scr->scr_ri; 314 int cnt, i; 315 #ifdef VCONS_DRAW_INTR 316 struct vcons_data_private *vdp = vd->private; 317 int size; 318 #endif 319 320 /* 321 * we allocate both chars and attributes in one chunk, attributes first 322 * because they have the (potentially) bigger alignment 323 */ 324 #ifdef WSDISPLAY_SCROLLSUPPORT 325 cnt = (ri->ri_rows + WSDISPLAY_SCROLLBACK_LINES) * ri->ri_cols; 326 scr->scr_lines_in_buffer = WSDISPLAY_SCROLLBACK_LINES; 327 scr->scr_current_line = 0; 328 scr->scr_line_wanted = 0; 329 scr->scr_offset_to_zero = ri->ri_cols * WSDISPLAY_SCROLLBACK_LINES; 330 scr->scr_current_offset = scr->scr_offset_to_zero; 331 #else 332 cnt = ri->ri_rows * ri->ri_cols; 333 #endif 334 scr->scr_attrs = malloc(cnt * (sizeof(long) + 335 sizeof(uint32_t)), M_DEVBUF, M_WAITOK); 336 if (scr->scr_attrs == NULL) 337 return ENOMEM; 338 339 scr->scr_chars = (uint32_t *)&scr->scr_attrs[cnt]; 340 341 /* 342 * fill the attribute buffer with *defattr, chars with 0x20 343 * since we don't know if the driver tries to mimic firmware output or 344 * reset everything we do nothing to VRAM here, any driver that feels 345 * the need to clear screen or something will have to do it on its own 346 * Additional screens will start out in the background anyway so 347 * cleaning or not only really affects the initial console screen 348 */ 349 for (i = 0; i < cnt; i++) { 350 scr->scr_attrs[i] = scr->scr_defattr; 351 scr->scr_chars[i] = 0x20; 352 } 353 354 #ifdef VCONS_DRAW_INTR 355 size = ri->ri_cols * ri->ri_rows; 356 if (size > vdp->cells) { 357 if (vdp->chars != NULL) free(vdp->chars, M_DEVBUF); 358 if (vdp->attrs != NULL) free(vdp->attrs, M_DEVBUF); 359 vdp->cells = size; 360 vdp->chars = malloc(size * sizeof(uint32_t), M_DEVBUF, 361 M_WAITOK|M_ZERO); 362 vdp->attrs = malloc(size * sizeof(long), M_DEVBUF, 363 M_WAITOK|M_ZERO); 364 vcons_invalidate_cache(vd); 365 } else if (SCREEN_IS_VISIBLE(scr)) 366 vcons_invalidate_cache(vd); 367 #endif 368 return 0; 369 } 370 371 int 372 vcons_init_screen(struct vcons_data *vd, struct vcons_screen *scr, 373 int existing, long *defattr) 374 { 375 struct vcons_data_private *vdp = vd->private; 376 struct rasops_info *ri = &scr->scr_ri; 377 int i; 378 379 scr->scr_cookie = vd->cookie; 380 scr->scr_vd = scr->scr_origvd = vd; 381 scr->scr_busy = 0; 382 383 if (scr->scr_type == NULL) 384 scr->scr_type = vdp->defaulttype; 385 386 /* 387 * call the driver-supplied init_screen function which is expected 388 * to set up rasops_info, override cursor() and probably others 389 */ 390 vd->init_screen(vd->cookie, scr, existing, defattr); 391 392 /* 393 * save the non virtual console aware rasops and replace them with 394 * our wrappers 395 */ 396 vdp->eraserows = ri->ri_ops.eraserows; 397 vdp->erasecols = ri->ri_ops.erasecols; 398 scr->putchar = ri->ri_ops.putchar; 399 400 if (scr->scr_flags & VCONS_NO_COPYCOLS) { 401 vdp->copycols = vcons_copycols_noread; 402 } else { 403 vdp->copycols = ri->ri_ops.copycols; 404 } 405 406 if (scr->scr_flags & VCONS_NO_COPYROWS) { 407 vdp->copyrows = vcons_copyrows_noread; 408 } else { 409 vdp->copyrows = ri->ri_ops.copyrows; 410 } 411 412 if (scr->scr_flags & VCONS_NO_CURSOR) { 413 vdp->cursor = vcons_cursor_noread; 414 } else { 415 vdp->cursor = ri->ri_ops.cursor; 416 } 417 418 ri->ri_ops.eraserows = vcons_eraserows; 419 ri->ri_ops.erasecols = vcons_erasecols; 420 ri->ri_ops.putchar = vcons_putchar; 421 ri->ri_ops.cursor = vcons_cursor; 422 ri->ri_ops.copycols = vcons_copycols; 423 ri->ri_ops.copyrows = vcons_copyrows; 424 425 426 ri->ri_hw = scr; 427 428 i = ri->ri_ops.allocattr(ri, WS_DEFAULT_FG, WS_DEFAULT_BG, 0, defattr); 429 if (i != 0) { 430 #ifdef DIAGNOSTIC 431 printf("vcons: error allocating attribute %d\n", i); 432 #endif 433 scr->scr_defattr = 0; 434 } else 435 scr->scr_defattr = *defattr; 436 437 vcons_alloc_buffers(vd, scr); 438 439 if (vd->active == NULL) { 440 vd->active = scr; 441 SCREEN_VISIBLE(scr); 442 } 443 444 if (existing) { 445 SCREEN_VISIBLE(scr); 446 vd->active = scr; 447 } else { 448 SCREEN_INVISIBLE(scr); 449 } 450 451 LIST_INSERT_HEAD(&vdp->screens, scr, next); 452 return 0; 453 } 454 455 static int 456 vcons_load_font(void *v, void *cookie, struct wsdisplay_font *f) 457 { 458 struct vcons_data *vd = v; 459 struct vcons_data_private *vdp = vd->private; 460 struct vcons_screen *scr = cookie; 461 struct rasops_info *ri; 462 struct wsdisplay_font *font; 463 int flags = WSFONT_FIND_BITMAP, fcookie; 464 465 /* see if we're asked to add a font or use it */ 466 if (scr == NULL) 467 return 0; 468 469 ri = &scr->scr_ri; 470 471 /* see if the driver knows how to handle multiple fonts */ 472 if ((scr->scr_flags & VCONS_LOADFONT) == 0) { 473 return EOPNOTSUPP; 474 } 475 476 /* now see what fonts we can use */ 477 if (ri->ri_flg & RI_ENABLE_ALPHA) { 478 flags |= WSFONT_FIND_ALPHA; 479 } 480 481 fcookie = wsfont_find(f->name, 0, 0, 0, 0, 0, flags); 482 if (fcookie == -1) 483 return EINVAL; 484 485 wsfont_lock(fcookie, &font); 486 if (font == NULL) 487 return EINVAL; 488 489 /* ok, we got a font. Now clear the screen with the old parameters */ 490 if (SCREEN_IS_VISIBLE(scr)) 491 vdp->eraserows(ri, 0, ri->ri_rows, scr->scr_defattr); 492 493 vcons_lock(vd->active); 494 #ifdef VCONS_DRAW_INTR 495 callout_halt(&vdp->intr, NULL); 496 #endif 497 /* set the new font and re-initialize things */ 498 ri->ri_font = font; 499 wsfont_unlock(ri->ri_wsfcookie); 500 ri->ri_wsfcookie = fcookie; 501 502 vd->init_screen(vd->cookie, scr, 1, &scr->scr_defattr); 503 DPRINTF("caps %x %x\n", scr->scr_type->capabilities, ri->ri_caps); 504 if (scr->scr_type->capabilities & WSSCREEN_RESIZE) { 505 scr->scr_type->nrows = ri->ri_rows; 506 scr->scr_type->ncols = ri->ri_cols; 507 DPRINTF("new size %d %d\n", ri->ri_rows, ri->ri_cols); 508 } 509 510 511 /* now, throw the old buffers away */ 512 if (scr->scr_attrs) 513 free(scr->scr_attrs, M_DEVBUF); 514 /* allocate new buffers */ 515 vcons_alloc_buffers(vd, scr); 516 517 /* save the potentially changed ri_ops */ 518 vdp->eraserows = ri->ri_ops.eraserows; 519 vdp->erasecols = ri->ri_ops.erasecols; 520 scr->putchar = ri->ri_ops.putchar; 521 vdp->cursor = ri->ri_ops.cursor; 522 523 if (scr->scr_flags & VCONS_NO_COPYCOLS) { 524 vdp->copycols = vcons_copycols_noread; 525 } else { 526 vdp->copycols = ri->ri_ops.copycols; 527 } 528 529 if (scr->scr_flags & VCONS_NO_COPYROWS) { 530 vdp->copyrows = vcons_copyrows_noread; 531 } else { 532 vdp->copyrows = ri->ri_ops.copyrows; 533 } 534 535 if (scr->scr_flags & VCONS_NO_CURSOR) { 536 vdp->cursor = vcons_cursor_noread; 537 } else { 538 vdp->cursor = ri->ri_ops.cursor; 539 } 540 541 /* and put our wrappers back */ 542 ri->ri_ops.eraserows = vcons_eraserows; 543 ri->ri_ops.erasecols = vcons_erasecols; 544 ri->ri_ops.putchar = vcons_putchar; 545 ri->ri_ops.cursor = vcons_cursor; 546 ri->ri_ops.copycols = vcons_copycols; 547 ri->ri_ops.copyrows = vcons_copyrows; 548 vcons_unlock(vd->active); 549 550 /* notify things that we're about to redraw */ 551 if (vd->show_screen_cb != NULL) 552 vd->show_screen_cb(scr, vd->show_screen_cookie); 553 554 #ifdef VCONS_DRAW_INTR 555 /* 556 * XXX 557 * Something(tm) craps all over VRAM somewhere up there if we're 558 * using VCONS_DRAW_INTR. Until I figure out what causes it, just 559 * redraw the screen for now. 560 */ 561 vcons_redraw_screen(vd->active); 562 callout_schedule(&vdp->intr, mstohz(33)); 563 #endif 564 /* no need to draw anything, wsdisplay should reset the terminal */ 565 566 return 0; 567 } 568 569 static void 570 vcons_do_switch(void *arg) 571 { 572 struct vcons_data *vd = arg; 573 struct vcons_data_private *vdp = vd->private; 574 struct vcons_screen *scr, *oldscr; 575 576 scr = vdp->wanted; 577 if (!scr) { 578 printf("vcons_switch_screen: disappeared\n"); 579 vdp->switch_cb(vdp->switch_cb_arg, EIO, 0); 580 return; 581 } 582 oldscr = vd->active; /* can be NULL! */ 583 584 /* 585 * if there's an old, visible screen we mark it invisible and wait 586 * until it's not busy so we can safely switch 587 */ 588 if (oldscr != NULL) { 589 SCREEN_INVISIBLE(oldscr); 590 if (SCREEN_IS_BUSY(oldscr)) { 591 callout_schedule(&vdp->switch_callout, 1); 592 #ifdef DIAGNOSTIC 593 /* bitch if we wait too long */ 594 vdp->switch_poll_count++; 595 if (vdp->switch_poll_count > 100) { 596 panic("vcons: screen still busy"); 597 } 598 #endif 599 return; 600 } 601 /* invisible screen -> no visible cursor image */ 602 oldscr->scr_ri.ri_flg &= ~RI_CURSOR; 603 #ifdef DIAGNOSTIC 604 vdp->switch_poll_count = 0; 605 #endif 606 } 607 608 if (scr == oldscr) 609 return; 610 611 #ifdef DIAGNOSTIC 612 if (SCREEN_IS_VISIBLE(scr)) 613 printf("vcons_switch_screen: already active"); 614 #endif 615 616 #ifdef notyet 617 if (vdp->currenttype != type) { 618 vcons_set_screentype(vd, type); 619 vdp->currenttype = type; 620 } 621 #endif 622 623 SCREEN_VISIBLE(scr); 624 vd->active = scr; 625 vdp->wanted = NULL; 626 627 #ifdef VCONS_DRAW_INTR 628 vcons_invalidate_cache(vd); 629 #endif 630 631 if (vd->show_screen_cb != NULL) 632 vd->show_screen_cb(scr, vd->show_screen_cookie); 633 634 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 635 vcons_redraw_screen(scr); 636 637 if (vdp->switch_cb) 638 vdp->switch_cb(vdp->switch_cb_arg, 0, 0); 639 } 640 641 void 642 vcons_redraw_screen(struct vcons_screen *scr) 643 { 644 uint32_t *charptr = scr->scr_chars, c; 645 long *attrptr = scr->scr_attrs, a, last_a = 0, mask, cmp, acmp; 646 struct rasops_info *ri = &scr->scr_ri; 647 struct vcons_data *vd = scr->scr_vd; 648 struct vcons_data_private *vdp = vd->private; 649 int i, j, offset, boffset = 0, start = -1; 650 651 mask = 0x00ff00ff; /* background and flags */ 652 cmp = 0xffffffff; /* never match anything */ 653 vcons_lock(scr); 654 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 655 656 /* 657 * only clear the screen when RI_FULLCLEAR is set since we're 658 * going to overwrite every single character cell anyway 659 */ 660 if (ri->ri_flg & RI_FULLCLEAR) { 661 vdp->eraserows(ri, 0, ri->ri_rows, 662 scr->scr_defattr); 663 cmp = scr->scr_defattr & mask; 664 } 665 666 /* redraw the screen */ 667 #ifdef WSDISPLAY_SCROLLSUPPORT 668 offset = scr->scr_current_offset; 669 #else 670 offset = 0; 671 #endif 672 for (i = 0; i < ri->ri_rows; i++) { 673 start = -1; 674 for (j = 0; j < ri->ri_cols; j++) { 675 /* 676 * no need to use the wrapper function - we 677 * don't change any characters or attributes 678 * and we already made sure the screen we're 679 * working on is visible 680 */ 681 c = charptr[offset]; 682 a = attrptr[offset]; 683 acmp = a & mask; 684 if (c == ' ') { 685 /* 686 * if we already erased the background 687 * and if this blank uses the same 688 * colour and flags we don't need to do 689 * anything here 690 */ 691 if (acmp == cmp && start == -1) 692 goto next; 693 /* 694 * see if we can optimize things a 695 * little bit by drawing stretches of 696 * blanks using erasecols 697 */ 698 699 if (start == -1) { 700 start = j; 701 last_a = acmp; 702 } else if (acmp != last_a) { 703 /* 704 * different attr, need to 705 * flush & restart 706 */ 707 vdp->erasecols(ri, i, start, 708 j - start, last_a); 709 start = j; 710 last_a = acmp; 711 } 712 } else { 713 if (start != -1) { 714 vdp->erasecols(ri, i, start, 715 j - start, last_a); 716 start = -1; 717 } 718 719 scr->putchar(ri, i, j, c, a); 720 } 721 next: 722 #ifdef VCONS_DRAW_INTR 723 vdp->chars[boffset] = charptr[offset]; 724 vdp->attrs[boffset] = attrptr[offset]; 725 #endif 726 offset++; 727 boffset++; 728 } 729 /* end of the line - draw all defered blanks, if any */ 730 if (start != -1) { 731 vdp->erasecols(ri, i, start, j - start, last_a); 732 } 733 } 734 ri->ri_flg &= ~RI_CURSOR; 735 scr->scr_vd->private->cursor(ri, 1, ri->ri_crow, ri->ri_ccol); 736 #ifdef VCONS_DRAW_INTR 737 vdp->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol; 738 #endif 739 } 740 vcons_unlock(scr); 741 } 742 743 void 744 vcons_update_screen(struct vcons_screen *scr) 745 { 746 #ifdef VCONS_DRAW_INTR 747 uint32_t *charptr = scr->scr_chars; 748 long *attrptr = scr->scr_attrs; 749 struct rasops_info *ri = &scr->scr_ri; 750 struct vcons_data *vd = scr->scr_vd; 751 struct vcons_data_private *vdp = vd->private; 752 int i, j, offset, boffset = 0; 753 754 vcons_lock(scr); 755 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 756 757 /* redraw the screen */ 758 #ifdef WSDISPLAY_SCROLLSUPPORT 759 offset = scr->scr_current_offset; 760 #else 761 offset = 0; 762 #endif 763 /* 764 * we mark the character cell occupied by the cursor as dirty 765 * so we don't have to deal with it 766 * notice that this isn't necessarily the position where rasops 767 * thinks it is, just where we drew it the last time 768 */ 769 if (vdp->cursor_offset >= 0) 770 vdp->attrs[vdp->cursor_offset] = 0xffffffff; 771 772 for (i = 0; i < ri->ri_rows; i++) { 773 for (j = 0; j < ri->ri_cols; j++) { 774 /* 775 * no need to use the wrapper function - we 776 * don't change any characters or attributes 777 * and we already made sure the screen we're 778 * working on is visible 779 */ 780 if ((vdp->chars[boffset] != charptr[offset]) || 781 (vdp->attrs[boffset] != attrptr[offset])) { 782 scr->putchar(ri, i, j, 783 charptr[offset], attrptr[offset]); 784 vdp->chars[boffset] = charptr[offset]; 785 vdp->attrs[boffset] = attrptr[offset]; 786 } 787 offset++; 788 boffset++; 789 } 790 } 791 ri->ri_flg &= ~RI_CURSOR; 792 scr->scr_vd->private->cursor(ri, 1, ri->ri_crow, ri->ri_ccol); 793 vdp->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol; 794 } 795 vcons_unlock(scr); 796 #else /* !VCONS_DRAW_INTR */ 797 vcons_redraw_screen(scr); 798 #endif 799 } 800 801 static int 802 vcons_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 803 struct lwp *l) 804 { 805 struct vcons_data *vd = v; 806 struct vcons_data_private *vdp = vd->private; 807 int error = 0; 808 809 810 switch (cmd) { 811 case WSDISPLAYIO_GETWSCHAR: 812 error = vcons_getwschar((struct vcons_screen *)vs, 813 (struct wsdisplay_char *)data); 814 break; 815 816 case WSDISPLAYIO_PUTWSCHAR: 817 error = vcons_putwschar((struct vcons_screen *)vs, 818 (struct wsdisplay_char *)data); 819 break; 820 821 case WSDISPLAYIO_SET_POLLING: { 822 int poll = *(int *)data; 823 824 /* first call the driver's ioctl handler */ 825 if (vdp->ioctl != NULL) 826 error = (*vdp->ioctl)(v, vs, cmd, data, flag, l); 827 if (poll) { 828 vcons_enable_polling(vd); 829 vcons_hard_switch(LIST_FIRST(&vdp->screens)); 830 } else 831 vcons_disable_polling(vd); 832 } 833 break; 834 835 default: 836 if (vdp->ioctl != NULL) 837 error = (*vdp->ioctl)(v, vs, cmd, data, flag, l); 838 else 839 error = EPASSTHROUGH; 840 } 841 842 return error; 843 } 844 845 static int 846 vcons_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep, 847 int *curxp, int *curyp, long *defattrp) 848 { 849 struct vcons_data *vd = v; 850 struct vcons_data_private *vdp = vd->private; 851 struct vcons_screen *scr; 852 struct wsscreen_descr *t = __UNCONST(type); 853 int ret; 854 855 scr = malloc(sizeof(struct vcons_screen), M_DEVBUF, M_WAITOK | M_ZERO); 856 if (scr == NULL) 857 return ENOMEM; 858 859 scr->scr_flags = 0; 860 scr->scr_status = 0; 861 scr->scr_busy = 0; 862 scr->scr_type = __UNCONST(type); 863 864 ret = vcons_init_screen(vd, scr, 0, defattrp); 865 if (ret != 0) { 866 free(scr, M_DEVBUF); 867 return ret; 868 } 869 if (t->capabilities & WSSCREEN_RESIZE) { 870 t->nrows = scr->scr_ri.ri_rows; 871 t->ncols = scr->scr_ri.ri_cols; 872 } 873 874 if (vd->active == NULL) { 875 SCREEN_VISIBLE(scr); 876 vd->active = scr; 877 vdp->currenttype = type; 878 } 879 880 *cookiep = scr; 881 *curxp = scr->scr_ri.ri_ccol; 882 *curyp = scr->scr_ri.ri_crow; 883 return 0; 884 } 885 886 static void 887 vcons_free_screen(void *v, void *cookie) 888 { 889 struct vcons_data *vd = v; 890 struct vcons_screen *scr = cookie; 891 892 vcons_lock(scr); 893 /* there should be no rasops activity here */ 894 895 LIST_REMOVE(scr, next); 896 897 if ((scr->scr_flags & VCONS_SCREEN_IS_STATIC) == 0) { 898 free(scr->scr_attrs, M_DEVBUF); 899 free(scr, M_DEVBUF); 900 } else { 901 /* 902 * maybe we should just restore the old rasops_info methods 903 * and free the character/attribute buffer here? 904 */ 905 #ifdef VCONS_DEBUG 906 panic("vcons_free_screen: console"); 907 #else 908 printf("vcons_free_screen: console\n"); 909 #endif 910 } 911 912 if (vd->active == scr) 913 vd->active = NULL; 914 } 915 916 static int 917 vcons_show_screen(void *v, void *cookie, int waitok, 918 void (*cb)(void *, int, int), void *cb_arg) 919 { 920 struct vcons_data *vd = v; 921 struct vcons_data_private *vdp = vd->private; 922 struct vcons_screen *scr; 923 924 scr = cookie; 925 if (scr == vd->active) 926 return 0; 927 928 vdp->wanted = scr; 929 vdp->switch_cb = cb; 930 vdp->switch_cb_arg = cb_arg; 931 if (cb) { 932 callout_schedule(&vdp->switch_callout, 0); 933 return EAGAIN; 934 } 935 936 vcons_do_switch(vd); 937 return 0; 938 } 939 940 /* wrappers for rasops_info methods */ 941 942 static void 943 vcons_copycols_buffer(void *cookie, int row, int srccol, int dstcol, int ncols) 944 { 945 struct rasops_info *ri = cookie; 946 struct vcons_screen *scr = ri->ri_hw; 947 int from = srccol + row * ri->ri_cols; 948 int to = dstcol + row * ri->ri_cols; 949 int offset = vcons_offset_to_zero(scr); 950 951 memmove(&scr->scr_attrs[offset + to], &scr->scr_attrs[offset + from], 952 ncols * sizeof(long)); 953 memmove(&scr->scr_chars[offset + to], &scr->scr_chars[offset + from], 954 ncols * sizeof(uint32_t)); 955 956 vcons_dirty(scr); 957 } 958 959 static void 960 vcons_copycols(void *cookie, int row, int srccol, int dstcol, int ncols) 961 { 962 struct rasops_info *ri = cookie; 963 struct vcons_screen *scr = ri->ri_hw; 964 965 vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols); 966 967 if (vcons_use_intr(scr)) 968 return; 969 970 vcons_lock(scr); 971 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 972 #if defined(VCONS_DRAW_INTR) 973 vcons_update_screen(scr); 974 #else 975 scr->scr_vd->private->copycols(cookie, row, srccol, dstcol, 976 ncols); 977 #endif 978 } 979 vcons_unlock(scr); 980 } 981 982 static void 983 vcons_copycols_noread(void *cookie, int row, int srccol, int dstcol, int ncols) 984 { 985 struct rasops_info *ri = cookie; 986 struct vcons_screen *scr = ri->ri_hw; 987 #ifdef VCONS_DRAW_INTR 988 struct vcons_data *vd = scr->scr_vd; 989 struct vcons_data_private *vdp = vd->private; 990 #endif 991 992 vcons_lock(scr); 993 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 994 int pos, c, offset, ppos; 995 996 #ifdef WSDISPLAY_SCROLLSUPPORT 997 offset = scr->scr_current_offset; 998 #else 999 offset = 0; 1000 #endif 1001 ppos = ri->ri_cols * row + dstcol; 1002 pos = ppos + offset; 1003 for (c = dstcol; c < (dstcol + ncols); c++) { 1004 #ifdef VCONS_DRAW_INTR 1005 if ((scr->scr_chars[pos] != vdp->chars[ppos]) || 1006 (scr->scr_attrs[pos] != vdp->attrs[ppos])) { 1007 scr->putchar(cookie, row, c, 1008 scr->scr_chars[pos], scr->scr_attrs[pos]); 1009 vdp->chars[ppos] = scr->scr_chars[pos]; 1010 vdp->attrs[ppos] = scr->scr_attrs[pos]; 1011 } 1012 #else 1013 scr->putchar(cookie, row, c, scr->scr_chars[pos], 1014 scr->scr_attrs[pos]); 1015 #endif 1016 pos++; 1017 ppos++; 1018 } 1019 if (ri->ri_crow == row && 1020 (ri->ri_ccol >= dstcol && ri->ri_ccol < (dstcol + ncols ))) 1021 ri->ri_flg &= ~RI_CURSOR; 1022 } 1023 vcons_unlock(scr); 1024 } 1025 1026 static void 1027 vcons_erasecols_buffer(void *cookie, int row, int startcol, int ncols, long fillattr) 1028 { 1029 struct rasops_info *ri = cookie; 1030 struct vcons_screen *scr = ri->ri_hw; 1031 int start = startcol + row * ri->ri_cols; 1032 int end = start + ncols, i; 1033 int offset = vcons_offset_to_zero(scr); 1034 1035 for (i = start; i < end; i++) { 1036 scr->scr_attrs[offset + i] = fillattr; 1037 scr->scr_chars[offset + i] = 0x20; 1038 } 1039 1040 vcons_dirty(scr); 1041 } 1042 1043 #ifdef VCONS_DRAW_INTR 1044 static void 1045 vcons_erasecols_cached(void *cookie, int row, int startcol, int ncols, long fillattr) 1046 { 1047 struct rasops_info *ri = cookie; 1048 struct vcons_screen *scr = ri->ri_hw; 1049 struct vcons_data *vd = scr->scr_vd; 1050 struct vcons_data_private *vdp = vd->private; 1051 int i, pos = row * ri->ri_cols + startcol; 1052 1053 vdp->erasecols(cookie, row, startcol, ncols, fillattr); 1054 for (i = pos; i < ncols; i++) { 1055 vdp->chars[i] = scr->scr_chars[i]; 1056 vdp->attrs[i] = scr->scr_attrs[i]; 1057 } 1058 } 1059 #endif 1060 1061 static void 1062 vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr) 1063 { 1064 struct rasops_info *ri = cookie; 1065 struct vcons_screen *scr = ri->ri_hw; 1066 1067 vcons_erasecols_buffer(cookie, row, startcol, ncols, fillattr); 1068 1069 if (vcons_use_intr(scr)) 1070 return; 1071 1072 vcons_lock(scr); 1073 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1074 #ifdef VCONS_DRAW_INTR 1075 vcons_erasecols_cached(cookie, row, startcol, ncols, 1076 fillattr); 1077 #else 1078 scr->scr_vd->private->erasecols(cookie, row, startcol, ncols, 1079 fillattr); 1080 #endif 1081 } 1082 vcons_unlock(scr); 1083 } 1084 1085 static void 1086 vcons_copyrows_buffer(void *cookie, int srcrow, int dstrow, int nrows) 1087 { 1088 struct rasops_info *ri = cookie; 1089 struct vcons_screen *scr = ri->ri_hw; 1090 int from, to, len; 1091 int offset = vcons_offset_to_zero(scr); 1092 1093 /* do we need to scroll the back buffer? */ 1094 if (dstrow == 0 && offset != 0) { 1095 from = ri->ri_cols * srcrow; 1096 to = ri->ri_cols * dstrow; 1097 1098 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 1099 offset * sizeof(long)); 1100 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 1101 offset * sizeof(uint32_t)); 1102 } 1103 from = ri->ri_cols * srcrow + offset; 1104 to = ri->ri_cols * dstrow + offset; 1105 len = ri->ri_cols * nrows; 1106 1107 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 1108 len * sizeof(long)); 1109 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 1110 len * sizeof(uint32_t)); 1111 1112 vcons_dirty(scr); 1113 } 1114 1115 static void 1116 vcons_copyrows(void *cookie, int srcrow, int dstrow, int nrows) 1117 { 1118 struct rasops_info *ri = cookie; 1119 struct vcons_screen *scr = ri->ri_hw; 1120 1121 vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows); 1122 1123 if (vcons_use_intr(scr)) 1124 return; 1125 1126 vcons_lock(scr); 1127 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1128 #if defined(VCONS_DRAW_INTR) 1129 vcons_update_screen(scr); 1130 #else 1131 scr->scr_vd->private->copyrows(cookie, srcrow, dstrow, nrows); 1132 #endif 1133 } 1134 vcons_unlock(scr); 1135 } 1136 1137 static void 1138 vcons_copyrows_noread(void *cookie, int srcrow, int dstrow, int nrows) 1139 { 1140 struct rasops_info *ri = cookie; 1141 struct vcons_screen *scr = ri->ri_hw; 1142 #ifdef VCONS_DRAW_INTR 1143 struct vcons_data *vd = scr->scr_vd; 1144 struct vcons_data_private *vdp = vd->private; 1145 #endif 1146 vcons_lock(scr); 1147 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1148 int pos, l, c, offset, ppos; 1149 1150 #ifdef WSDISPLAY_SCROLLSUPPORT 1151 offset = scr->scr_current_offset; 1152 #else 1153 offset = 0; 1154 #endif 1155 ppos = ri->ri_cols * dstrow; 1156 pos = ppos + offset; 1157 for (l = dstrow; l < (dstrow + nrows); l++) { 1158 for (c = 0; c < ri->ri_cols; c++) { 1159 #ifdef VCONS_DRAW_INTR 1160 if ((scr->scr_chars[pos] != vdp->chars[ppos]) || 1161 (scr->scr_attrs[pos] != vdp->attrs[ppos])) { 1162 scr->putchar(cookie, l, c, 1163 scr->scr_chars[pos], scr->scr_attrs[pos]); 1164 vdp->chars[ppos] = scr->scr_chars[pos]; 1165 vdp->attrs[ppos] = scr->scr_attrs[pos]; 1166 } 1167 #else 1168 scr->putchar(cookie, l, c, scr->scr_chars[pos], 1169 scr->scr_attrs[pos]); 1170 #endif 1171 pos++; 1172 ppos++; 1173 } 1174 } 1175 if (ri->ri_crow >= dstrow && ri->ri_crow < (dstrow + nrows)) 1176 ri->ri_flg &= ~RI_CURSOR; 1177 } 1178 vcons_unlock(scr); 1179 } 1180 1181 static void 1182 vcons_eraserows_buffer(void *cookie, int row, int nrows, long fillattr) 1183 { 1184 struct rasops_info *ri = cookie; 1185 struct vcons_screen *scr = ri->ri_hw; 1186 int offset = vcons_offset_to_zero(scr); 1187 int start, end, i; 1188 1189 start = ri->ri_cols * row + offset; 1190 end = ri->ri_cols * (row + nrows) + offset; 1191 1192 for (i = start; i < end; i++) { 1193 scr->scr_attrs[i] = fillattr; 1194 scr->scr_chars[i] = 0x20; 1195 } 1196 1197 vcons_dirty(scr); 1198 } 1199 1200 #ifdef VCONS_DRAW_INTR 1201 static void 1202 vcons_eraserows_cached(void *cookie, int row, int nrows, long fillattr) 1203 { 1204 struct rasops_info *ri = cookie; 1205 struct vcons_screen *scr = ri->ri_hw; 1206 struct vcons_data *vd = scr->scr_vd; 1207 struct vcons_data_private *vdp = vd->private; 1208 int i, pos = row * ri->ri_cols, end = (row+nrows) * ri->ri_cols; 1209 1210 for (i = pos; i < end; i++) { 1211 vdp->chars[i] = 0x20; 1212 vdp->attrs[i] = fillattr; 1213 } 1214 vdp->eraserows(cookie, row, nrows, fillattr); 1215 } 1216 #endif 1217 1218 static void 1219 vcons_eraserows(void *cookie, int row, int nrows, long fillattr) 1220 { 1221 struct rasops_info *ri = cookie; 1222 struct vcons_screen *scr = ri->ri_hw; 1223 1224 vcons_eraserows_buffer(cookie, row, nrows, fillattr); 1225 1226 if (vcons_use_intr(scr)) 1227 return; 1228 1229 vcons_lock(scr); 1230 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1231 #ifdef VCONS_DRAW_INTR 1232 vcons_eraserows_cached(cookie, row, nrows, fillattr); 1233 #else 1234 scr->scr_vd->private->eraserows(cookie, row, nrows, fillattr); 1235 #endif 1236 } 1237 vcons_unlock(scr); 1238 } 1239 1240 static void 1241 vcons_putchar_buffer(void *cookie, int row, int col, u_int c, long attr) 1242 { 1243 struct rasops_info *ri = cookie; 1244 struct vcons_screen *scr = ri->ri_hw; 1245 int offset = vcons_offset_to_zero(scr); 1246 int pos; 1247 1248 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) && 1249 (col < ri->ri_cols)) { 1250 pos = col + row * ri->ri_cols; 1251 scr->scr_attrs[pos + offset] = attr; 1252 scr->scr_chars[pos + offset] = c; 1253 } 1254 1255 vcons_dirty(scr); 1256 } 1257 1258 #ifdef VCONS_DRAW_INTR 1259 static void 1260 vcons_putchar_cached(void *cookie, int row, int col, u_int c, long attr) 1261 { 1262 struct rasops_info *ri = cookie; 1263 struct vcons_screen *scr = ri->ri_hw; 1264 struct vcons_data *vd = scr->scr_vd; 1265 struct vcons_data_private *vdp = vd->private; 1266 int pos = row * ri->ri_cols + col; 1267 1268 if ((vdp->chars == NULL) || (vdp->attrs == NULL)) { 1269 scr->putchar(cookie, row, col, c, attr); 1270 return; 1271 } 1272 if ((vdp->chars[pos] != c) || (vdp->attrs[pos] != attr)) { 1273 vdp->attrs[pos] = attr; 1274 vdp->chars[pos] = c; 1275 scr->putchar(cookie, row, col, c, attr); 1276 } 1277 } 1278 #endif 1279 1280 static void 1281 vcons_putchar(void *cookie, int row, int col, u_int c, long attr) 1282 { 1283 struct rasops_info *ri = cookie; 1284 struct vcons_screen *scr = ri->ri_hw; 1285 1286 vcons_putchar_buffer(cookie, row, col, c, attr); 1287 1288 if (vcons_use_intr(scr)) 1289 return; 1290 1291 vcons_lock(scr); 1292 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1293 #ifdef VCONS_DRAW_INTR 1294 vcons_putchar_cached(cookie, row, col, c, attr); 1295 #else 1296 if (row == ri->ri_crow && col == ri->ri_ccol) { 1297 ri->ri_flg &= ~RI_CURSOR; 1298 } 1299 scr->putchar(cookie, row, col, c, attr); 1300 #endif 1301 } 1302 vcons_unlock(scr); 1303 } 1304 1305 static void 1306 vcons_cursor(void *cookie, int on, int row, int col) 1307 { 1308 struct rasops_info *ri = cookie; 1309 struct vcons_screen *scr = ri->ri_hw; 1310 1311 if (vcons_use_intr(scr)) { 1312 vcons_lock(scr); 1313 if (scr->scr_ri.ri_crow != row || scr->scr_ri.ri_ccol != col) { 1314 scr->scr_ri.ri_crow = row; 1315 scr->scr_ri.ri_ccol = col; 1316 vcons_dirty(scr); 1317 } 1318 vcons_unlock(scr); 1319 return; 1320 } 1321 1322 vcons_lock(scr); 1323 1324 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1325 scr->scr_vd->private->cursor(cookie, on, row, col); 1326 } else { 1327 scr->scr_ri.ri_crow = row; 1328 scr->scr_ri.ri_ccol = col; 1329 } 1330 vcons_unlock(scr); 1331 } 1332 1333 static void 1334 vcons_cursor_noread(void *cookie, int on, int row, int col) 1335 { 1336 struct rasops_info *ri = cookie; 1337 struct vcons_screen *scr = ri->ri_hw; 1338 int offset = 0, ofs; 1339 1340 #ifdef WSDISPLAY_SCROLLSUPPORT 1341 offset = scr->scr_current_offset; 1342 #endif 1343 ofs = offset + ri->ri_crow * ri->ri_cols + ri->ri_ccol; 1344 if ((ri->ri_flg & RI_CURSOR) && 1345 (((scr->scr_flags & VCONS_DONT_READ) != VCONS_DONT_READ) || on)) { 1346 scr->putchar(cookie, ri->ri_crow, ri->ri_ccol, 1347 scr->scr_chars[ofs], scr->scr_attrs[ofs]); 1348 ri->ri_flg &= ~RI_CURSOR; 1349 } 1350 ri->ri_crow = row; 1351 ri->ri_ccol = col; 1352 ofs = offset + ri->ri_crow * ri->ri_cols + ri->ri_ccol; 1353 if (on) { 1354 scr->putchar(cookie, row, col, scr->scr_chars[ofs], 1355 #ifdef VCONS_DEBUG_CURSOR_NOREAD 1356 /* draw a red cursor so we can tell which cursor() 1357 * implementation is being used */ 1358 ((scr->scr_attrs[ofs] & 0xff00ffff) ^ 0x0f000000) | 1359 0x00010000); 1360 #else 1361 scr->scr_attrs[ofs] ^ 0x0f0f0000); 1362 #endif 1363 ri->ri_flg |= RI_CURSOR; 1364 } 1365 } 1366 1367 /* methods to read/write characters via ioctl() */ 1368 1369 static int 1370 vcons_putwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 1371 { 1372 long attr; 1373 struct rasops_info *ri; 1374 int error; 1375 1376 KASSERT(scr != NULL); 1377 KASSERT(wsc != NULL); 1378 1379 ri = &scr->scr_ri; 1380 1381 /* allow col as linear index if row == 0 */ 1382 if (wsc->row == 0) { 1383 if (wsc->col < 0 || wsc->col > (ri->ri_cols * ri->ri_rows)) 1384 return EINVAL; 1385 int rem; 1386 rem = wsc->col % ri->ri_cols; 1387 wsc->row = wsc->col / ri->ri_cols; 1388 DPRINTF("off %d -> %d, %d\n", wsc->col, rem, wsc->row); 1389 wsc->col = rem; 1390 } else { 1391 if (__predict_false(wsc->col < 0 || wsc->col >= ri->ri_cols)) 1392 return EINVAL; 1393 1394 if (__predict_false(wsc->row < 0 || wsc->row >= ri->ri_rows)) 1395 return EINVAL; 1396 } 1397 1398 error = ri->ri_ops.allocattr(ri, wsc->foreground, 1399 wsc->background, wsc->flags, &attr); 1400 if (error) 1401 return error; 1402 vcons_putchar(ri, wsc->row, wsc->col, wsc->letter, attr); 1403 DPRINTF("vcons_putwschar(%d, %d, %x, %lx\n", wsc->row, wsc->col, 1404 wsc->letter, attr); 1405 return 0; 1406 } 1407 1408 static int 1409 vcons_getwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 1410 { 1411 int offset; 1412 long attr; 1413 struct rasops_info *ri; 1414 int fg, bg, ul; 1415 1416 KASSERT(scr != NULL); 1417 KASSERT(wsc != NULL); 1418 1419 ri = &scr->scr_ri; 1420 1421 /* allow col as linear index if row == 0 */ 1422 if (wsc->row == 0) { 1423 if (wsc->col < 0 || wsc->col > (ri->ri_cols * ri->ri_rows)) 1424 return EINVAL; 1425 int rem; 1426 rem = wsc->col % ri->ri_cols; 1427 wsc->row = wsc->col / ri->ri_cols; 1428 DPRINTF("off %d -> %d, %d\n", wsc->col, rem, wsc->row); 1429 wsc->col = rem; 1430 } else { 1431 if (__predict_false(wsc->col < 0 || wsc->col >= ri->ri_cols)) 1432 return EINVAL; 1433 1434 if (__predict_false(wsc->row < 0 || wsc->row >= ri->ri_rows)) 1435 return EINVAL; 1436 } 1437 1438 offset = ri->ri_cols * wsc->row + wsc->col; 1439 offset += vcons_offset_to_zero(scr); 1440 wsc->letter = scr->scr_chars[offset]; 1441 attr = scr->scr_attrs[offset]; 1442 1443 DPRINTF("vcons_getwschar: %d, %d, %x, %lx\n", wsc->row, 1444 wsc->col, wsc->letter, attr); 1445 1446 /* 1447 * this is ugly. We need to break up an attribute into colours and 1448 * flags but there's no rasops method to do that so we must rely on 1449 * the 'canonical' encoding. 1450 */ 1451 1452 /* only fetches underline attribute */ 1453 /* rasops_unpack_attr(attr, &fg, &bg, &ul); */ 1454 fg = (attr >> 24) & 0xf; 1455 bg = (attr >> 16) & 0xf; 1456 ul = (attr & 1); 1457 1458 wsc->foreground = fg; 1459 wsc->background = bg; 1460 1461 /* clear trashed bits and restore underline flag */ 1462 attr &= ~(WSATTR_HILIT | WSATTR_BLINK | WSATTR_UNDERLINE); 1463 if (ul) 1464 attr |= WSATTR_UNDERLINE; 1465 1466 /* restore highlight boost */ 1467 if (attr & WSATTR_HILIT) 1468 if (wsc->foreground >= 8) 1469 wsc->foreground -= 8; 1470 1471 /* we always use colors, even when not stored */ 1472 attr |= WSATTR_WSCOLORS; 1473 return 0; 1474 } 1475 1476 int 1477 vcons_offset_to_zero(const struct vcons_screen *scr) 1478 { 1479 #ifdef WSDISPLAY_SCROLLSUPPORT 1480 return scr->scr_offset_to_zero; 1481 #else 1482 return 0; 1483 #endif 1484 } 1485 1486 #ifdef WSDISPLAY_SCROLLSUPPORT 1487 1488 static void 1489 vcons_scroll(void *cookie, void *vs, int where) 1490 { 1491 struct vcons_screen *scr = vs; 1492 1493 if (where == 0) { 1494 scr->scr_line_wanted = 0; 1495 } else { 1496 scr->scr_line_wanted = scr->scr_line_wanted - where; 1497 if (scr->scr_line_wanted < 0) 1498 scr->scr_line_wanted = 0; 1499 if (scr->scr_line_wanted > scr->scr_lines_in_buffer) 1500 scr->scr_line_wanted = scr->scr_lines_in_buffer; 1501 } 1502 1503 if (scr->scr_line_wanted != scr->scr_current_line) { 1504 1505 vcons_do_scroll(scr); 1506 } 1507 } 1508 1509 static void 1510 vcons_do_scroll(struct vcons_screen *scr) 1511 { 1512 int dist, from, to, num; 1513 int r_offset, r_start; 1514 int i, j; 1515 1516 if (scr->scr_line_wanted == scr->scr_current_line) 1517 return; 1518 dist = scr->scr_line_wanted - scr->scr_current_line; 1519 scr->scr_current_line = scr->scr_line_wanted; 1520 scr->scr_current_offset = scr->scr_ri.ri_cols * 1521 (scr->scr_lines_in_buffer - scr->scr_current_line); 1522 if (abs(dist) >= scr->scr_ri.ri_rows) { 1523 vcons_redraw_screen(scr); 1524 return; 1525 } 1526 /* scroll and redraw only what we really have to */ 1527 if (dist > 0) { 1528 /* we scroll down */ 1529 from = 0; 1530 to = dist; 1531 num = scr->scr_ri.ri_rows - dist; 1532 /* now the redraw parameters */ 1533 r_offset = scr->scr_current_offset; 1534 r_start = 0; 1535 } else { 1536 /* scrolling up */ 1537 to = 0; 1538 from = -dist; 1539 num = scr->scr_ri.ri_rows + dist; 1540 r_offset = scr->scr_current_offset + num * scr->scr_ri.ri_cols; 1541 r_start = num; 1542 } 1543 scr->scr_vd->private->copyrows(scr, from, to, num); 1544 for (i = 0; i < abs(dist); i++) { 1545 for (j = 0; j < scr->scr_ri.ri_cols; j++) { 1546 #ifdef VCONS_DRAW_INTR 1547 vcons_putchar_cached(scr, i + r_start, j, 1548 scr->scr_chars[r_offset], 1549 scr->scr_attrs[r_offset]); 1550 #else 1551 scr->putchar(scr, i + r_start, j, 1552 scr->scr_chars[r_offset], 1553 scr->scr_attrs[r_offset]); 1554 #endif 1555 r_offset++; 1556 } 1557 } 1558 1559 if (scr->scr_line_wanted == 0) { 1560 /* this was a reset - need to draw the cursor */ 1561 scr->scr_ri.ri_flg &= ~RI_CURSOR; 1562 scr->scr_vd->private->cursor(scr, 1, scr->scr_ri.ri_crow, 1563 scr->scr_ri.ri_ccol); 1564 } 1565 } 1566 1567 #endif /* WSDISPLAY_SCROLLSUPPORT */ 1568 1569 #ifdef VCONS_DRAW_INTR 1570 static void 1571 vcons_intr(void *cookie) 1572 { 1573 struct vcons_data *vd = cookie; 1574 struct vcons_data_private *vdp = vd->private; 1575 1576 softint_schedule(vdp->intr_softint); 1577 } 1578 1579 static void 1580 vcons_softintr(void *cookie) 1581 { 1582 struct vcons_data *vd = cookie; 1583 struct vcons_data_private *vdp = vd->private; 1584 struct vcons_screen *scr = vd->active; 1585 unsigned int dirty; 1586 1587 if (scr && vdp->use_intr) { 1588 if (!SCREEN_IS_BUSY(scr)) { 1589 dirty = atomic_swap_uint(&scr->scr_dirty, 0); 1590 membar_acquire(); 1591 if (vdp->use_intr == 2) { 1592 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) { 1593 vdp->use_intr = 1; 1594 vcons_redraw_screen(scr); 1595 } 1596 } else if (dirty > 0) { 1597 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 1598 vcons_update_screen(scr); 1599 } 1600 } 1601 } 1602 1603 callout_schedule(&vdp->intr, mstohz(33)); 1604 } 1605 1606 static void 1607 vcons_init_thread(void *cookie) 1608 { 1609 struct vcons_data *vd = (struct vcons_data *)cookie; 1610 struct vcons_data_private *vdp = vd->private; 1611 1612 vdp->use_intr = 2; 1613 callout_schedule(&vdp->intr, mstohz(33)); 1614 kthread_exit(0); 1615 } 1616 #endif /* VCONS_DRAW_INTR */ 1617 1618 void 1619 vcons_enable_polling(struct vcons_data *vd) 1620 { 1621 struct vcons_screen *scr = vd->active; 1622 1623 #ifdef VCONS_DRAW_INTR 1624 struct vcons_data_private *vdp = vd->private; 1625 1626 vdp->use_intr = 0; 1627 #endif 1628 1629 if (scr && !SCREEN_IS_BUSY(scr)) { 1630 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 1631 vcons_redraw_screen(scr); 1632 } 1633 } 1634 1635 void 1636 vcons_disable_polling(struct vcons_data *vd) 1637 { 1638 #ifdef VCONS_DRAW_INTR 1639 struct vcons_data_private *vdp = vd->private; 1640 struct vcons_screen *scr = vd->active; 1641 1642 if (!vdp->intr_valid) 1643 return; 1644 1645 vdp->use_intr = 2; 1646 if (scr) 1647 vcons_dirty(scr); 1648 #endif 1649 } 1650 1651 void 1652 vcons_hard_switch(struct vcons_screen *scr) 1653 { 1654 struct vcons_data *vd = scr->scr_vd; 1655 struct vcons_data_private *vdp = vd->private; 1656 struct vcons_screen *oldscr = vd->active; 1657 1658 if (oldscr) { 1659 SCREEN_INVISIBLE(oldscr); 1660 oldscr->scr_ri.ri_flg &= ~RI_CURSOR; 1661 } 1662 SCREEN_VISIBLE(scr); 1663 vd->active = scr; 1664 vdp->wanted = NULL; 1665 1666 if (vd->show_screen_cb != NULL) 1667 vd->show_screen_cb(scr, vd->show_screen_cookie); 1668 } 1669 1670 #ifdef VCONS_DRAW_INTR 1671 static void 1672 vcons_invalidate_cache(struct vcons_data *vd) 1673 { 1674 struct vcons_data_private *vdp = vd->private; 1675 int i; 1676 1677 for (i = 0; i < vdp->cells; i++) { 1678 vdp->chars[i] = -1; 1679 vdp->attrs[i] = -1; 1680 } 1681 } 1682 #endif 1683