1 /* $NetBSD: wsdisplay_vcons.c,v 1.70 2025/04/28 07:43:41 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.70 2025/04/28 07:43:41 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 #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 int 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) 358 free(vdp->chars, M_DEVBUF); 359 if (vdp->attrs != NULL) 360 free(vdp->attrs, M_DEVBUF); 361 vdp->cells = size; 362 vdp->chars = malloc(size * sizeof(uint32_t), M_DEVBUF, 363 M_WAITOK|M_ZERO); 364 vdp->attrs = malloc(size * sizeof(long), M_DEVBUF, 365 M_WAITOK|M_ZERO); 366 vcons_invalidate_cache(vd); 367 } else if (SCREEN_IS_VISIBLE(scr)) 368 vcons_invalidate_cache(vd); 369 #endif 370 return 0; 371 } 372 373 int 374 vcons_init_screen(struct vcons_data *vd, struct vcons_screen *scr, 375 int existing, long *defattr) 376 { 377 struct vcons_data_private *vdp = vd->private; 378 struct rasops_info *ri = &scr->scr_ri; 379 int i; 380 381 scr->scr_cookie = vd->cookie; 382 scr->scr_vd = scr->scr_origvd = vd; 383 scr->scr_busy = 0; 384 385 if (scr->scr_type == NULL) 386 scr->scr_type = vdp->defaulttype; 387 388 /* 389 * call the driver-supplied init_screen function which is expected 390 * to set up rasops_info, override cursor() and probably others 391 */ 392 vd->init_screen(vd->cookie, scr, existing, defattr); 393 394 /* 395 * save the non virtual console aware rasops and replace them with 396 * our wrappers 397 */ 398 vdp->eraserows = ri->ri_ops.eraserows; 399 vdp->erasecols = ri->ri_ops.erasecols; 400 scr->putchar = ri->ri_ops.putchar; 401 402 if (scr->scr_flags & VCONS_NO_COPYCOLS) { 403 vdp->copycols = vcons_copycols_noread; 404 } else { 405 vdp->copycols = ri->ri_ops.copycols; 406 } 407 408 if (scr->scr_flags & VCONS_NO_COPYROWS) { 409 vdp->copyrows = vcons_copyrows_noread; 410 } else { 411 vdp->copyrows = ri->ri_ops.copyrows; 412 } 413 414 if (scr->scr_flags & VCONS_NO_CURSOR) { 415 vdp->cursor = vcons_cursor_noread; 416 } else { 417 vdp->cursor = ri->ri_ops.cursor; 418 } 419 420 ri->ri_ops.eraserows = vcons_eraserows; 421 ri->ri_ops.erasecols = vcons_erasecols; 422 ri->ri_ops.putchar = vcons_putchar; 423 ri->ri_ops.cursor = vcons_cursor; 424 ri->ri_ops.copycols = vcons_copycols; 425 ri->ri_ops.copyrows = vcons_copyrows; 426 427 428 ri->ri_hw = scr; 429 430 i = ri->ri_ops.allocattr(ri, WS_DEFAULT_FG, WS_DEFAULT_BG, 0, defattr); 431 if (i != 0) { 432 #ifdef DIAGNOSTIC 433 printf("vcons: error allocating attribute %d\n", i); 434 #endif 435 scr->scr_defattr = 0; 436 } else 437 scr->scr_defattr = *defattr; 438 439 vcons_alloc_buffers(vd, scr); 440 441 if (vd->active == NULL) { 442 vd->active = scr; 443 SCREEN_VISIBLE(scr); 444 } 445 446 if (existing) { 447 SCREEN_VISIBLE(scr); 448 vd->active = scr; 449 } else { 450 SCREEN_INVISIBLE(scr); 451 } 452 453 LIST_INSERT_HEAD(&vdp->screens, scr, next); 454 return 0; 455 } 456 457 static int 458 vcons_load_font(void *v, void *cookie, struct wsdisplay_font *f) 459 { 460 struct vcons_data *vd = v; 461 struct vcons_data_private *vdp = vd->private; 462 struct vcons_screen *scr = cookie; 463 struct rasops_info *ri; 464 struct wsdisplay_font *font; 465 char buf[64], *at; 466 int flags = WSFONT_FIND_BITMAP, fcookie, h = 0; 467 468 /* see if we're asked to add a font or use it */ 469 if (scr == NULL) 470 return 0; 471 472 ri = &scr->scr_ri; 473 474 /* see if the driver knows how to handle multiple fonts */ 475 if ((scr->scr_flags & VCONS_LOADFONT) == 0) { 476 return EOPNOTSUPP; 477 } 478 479 /* now see what fonts we can use */ 480 if (ri->ri_flg & RI_ENABLE_ALPHA) { 481 flags |= WSFONT_FIND_ALPHA; 482 } 483 484 strncpy(buf, f->name, 63); 485 buf[63] = 0; 486 at = strchr(buf, '@'); 487 if (at != NULL) { 488 int stat; 489 at[0] = 0; 490 at++; 491 DPRINTF("got '%s'\n", at); 492 h = strtoi(at, NULL, 10, 1, 99, &stat); 493 if (stat != 0) h = 0; 494 DPRINTF("looking for %d\n", h); 495 } 496 fcookie = wsfont_find(buf, 0, h, 0, 497 /* bitorder */ 498 scr->scr_flags & VCONS_FONT_BITS_R2L ? 499 WSDISPLAY_FONTORDER_R2L : WSDISPLAY_FONTORDER_L2R, 500 /* byteorder */ 501 scr->scr_flags & VCONS_FONT_BYTES_R2L ? 502 WSDISPLAY_FONTORDER_R2L : WSDISPLAY_FONTORDER_L2R, 503 flags); 504 if (fcookie == -1) 505 return ENOENT; 506 507 wsfont_lock(fcookie, &font); 508 if (font == NULL) 509 return EINVAL; 510 511 /* ok, we got a font. Now clear the screen with the old parameters */ 512 if (SCREEN_IS_VISIBLE(scr)) 513 vdp->eraserows(ri, 0, ri->ri_rows, scr->scr_defattr); 514 515 vcons_lock(vd->active); 516 #ifdef VCONS_DRAW_INTR 517 callout_halt(&vdp->intr, NULL); 518 #endif 519 /* set the new font and re-initialize things */ 520 ri->ri_font = font; 521 wsfont_unlock(ri->ri_wsfcookie); 522 ri->ri_wsfcookie = fcookie; 523 524 vd->init_screen(vd->cookie, scr, 1, &scr->scr_defattr); 525 DPRINTF("caps %x %x\n", scr->scr_type->capabilities, ri->ri_caps); 526 if (scr->scr_type->capabilities & WSSCREEN_RESIZE) { 527 scr->scr_type->nrows = ri->ri_rows; 528 scr->scr_type->ncols = ri->ri_cols; 529 DPRINTF("new size %d %d\n", ri->ri_rows, ri->ri_cols); 530 } 531 532 533 /* now, throw the old buffers away */ 534 if (scr->scr_attrs) 535 free(scr->scr_attrs, M_DEVBUF); 536 /* allocate new buffers */ 537 vcons_alloc_buffers(vd, scr); 538 539 /* save the potentially changed ri_ops */ 540 vdp->eraserows = ri->ri_ops.eraserows; 541 vdp->erasecols = ri->ri_ops.erasecols; 542 scr->putchar = ri->ri_ops.putchar; 543 vdp->cursor = ri->ri_ops.cursor; 544 545 if (scr->scr_flags & VCONS_NO_COPYCOLS) { 546 vdp->copycols = vcons_copycols_noread; 547 } else { 548 vdp->copycols = ri->ri_ops.copycols; 549 } 550 551 if (scr->scr_flags & VCONS_NO_COPYROWS) { 552 vdp->copyrows = vcons_copyrows_noread; 553 } else { 554 vdp->copyrows = ri->ri_ops.copyrows; 555 } 556 557 if (scr->scr_flags & VCONS_NO_CURSOR) { 558 vdp->cursor = vcons_cursor_noread; 559 } else { 560 vdp->cursor = ri->ri_ops.cursor; 561 } 562 563 /* and put our wrappers back */ 564 ri->ri_ops.eraserows = vcons_eraserows; 565 ri->ri_ops.erasecols = vcons_erasecols; 566 ri->ri_ops.putchar = vcons_putchar; 567 ri->ri_ops.cursor = vcons_cursor; 568 ri->ri_ops.copycols = vcons_copycols; 569 ri->ri_ops.copyrows = vcons_copyrows; 570 vcons_unlock(vd->active); 571 572 /* notify things that we're about to redraw */ 573 if (vd->show_screen_cb != NULL) 574 vd->show_screen_cb(scr, vd->show_screen_cookie); 575 576 #ifdef VCONS_DRAW_INTR 577 /* 578 * XXX 579 * Something(tm) craps all over VRAM somewhere up there if we're 580 * using VCONS_DRAW_INTR. Until I figure out what causes it, just 581 * redraw the screen for now. 582 */ 583 vcons_redraw_screen(vd->active); 584 callout_schedule(&vdp->intr, mstohz(33)); 585 #endif 586 /* no need to draw anything, wsdisplay should reset the terminal */ 587 588 return 0; 589 } 590 591 static void 592 vcons_do_switch(void *arg) 593 { 594 struct vcons_data *vd = arg; 595 struct vcons_data_private *vdp = vd->private; 596 struct vcons_screen *scr, *oldscr; 597 598 scr = vdp->wanted; 599 if (!scr) { 600 printf("vcons_switch_screen: disappeared\n"); 601 vdp->switch_cb(vdp->switch_cb_arg, EIO, 0); 602 return; 603 } 604 oldscr = vd->active; /* can be NULL! */ 605 606 /* 607 * if there's an old, visible screen we mark it invisible and wait 608 * until it's not busy so we can safely switch 609 */ 610 if (oldscr != NULL) { 611 SCREEN_INVISIBLE(oldscr); 612 if (SCREEN_IS_BUSY(oldscr)) { 613 callout_schedule(&vdp->switch_callout, 1); 614 #ifdef DIAGNOSTIC 615 /* bitch if we wait too long */ 616 vdp->switch_poll_count++; 617 if (vdp->switch_poll_count > 100) { 618 panic("vcons: screen still busy"); 619 } 620 #endif 621 return; 622 } 623 /* invisible screen -> no visible cursor image */ 624 oldscr->scr_ri.ri_flg &= ~RI_CURSOR; 625 #ifdef DIAGNOSTIC 626 vdp->switch_poll_count = 0; 627 #endif 628 } 629 630 if (scr == oldscr) 631 return; 632 633 #ifdef DIAGNOSTIC 634 if (SCREEN_IS_VISIBLE(scr)) 635 printf("vcons_switch_screen: already active"); 636 #endif 637 638 #ifdef notyet 639 if (vdp->currenttype != type) { 640 vcons_set_screentype(vd, type); 641 vdp->currenttype = type; 642 } 643 #endif 644 645 SCREEN_VISIBLE(scr); 646 vd->active = scr; 647 vdp->wanted = NULL; 648 649 #ifdef VCONS_DRAW_INTR 650 vcons_invalidate_cache(vd); 651 #endif 652 653 if (vd->show_screen_cb != NULL) 654 vd->show_screen_cb(scr, vd->show_screen_cookie); 655 656 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 657 vcons_redraw_screen(scr); 658 659 if (vdp->switch_cb) 660 vdp->switch_cb(vdp->switch_cb_arg, 0, 0); 661 } 662 663 void 664 vcons_redraw_screen(struct vcons_screen *scr) 665 { 666 uint32_t *charptr = scr->scr_chars, c; 667 long *attrptr = scr->scr_attrs, a, last_a = 0, mask, cmp, acmp; 668 struct rasops_info *ri = &scr->scr_ri; 669 struct vcons_data *vd = scr->scr_vd; 670 struct vcons_data_private *vdp = vd->private; 671 int i, j, offset, boffset = 0, start = -1; 672 673 mask = 0x00ff00ff; /* background and flags */ 674 cmp = 0xffffffff; /* never match anything */ 675 vcons_lock(scr); 676 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 677 678 /* 679 * only clear the screen when RI_FULLCLEAR is set since we're 680 * going to overwrite every single character cell anyway 681 */ 682 if (ri->ri_flg & RI_FULLCLEAR) { 683 vdp->eraserows(ri, 0, ri->ri_rows, 684 scr->scr_defattr); 685 cmp = scr->scr_defattr & mask; 686 } 687 688 /* redraw the screen */ 689 #ifdef WSDISPLAY_SCROLLSUPPORT 690 offset = scr->scr_current_offset; 691 #else 692 offset = 0; 693 #endif 694 for (i = 0; i < ri->ri_rows; i++) { 695 start = -1; 696 for (j = 0; j < ri->ri_cols; j++) { 697 /* 698 * no need to use the wrapper function - we 699 * don't change any characters or attributes 700 * and we already made sure the screen we're 701 * working on is visible 702 */ 703 c = charptr[offset]; 704 a = attrptr[offset]; 705 acmp = a & mask; 706 if (c == ' ') { 707 /* 708 * if we already erased the background 709 * and if this blank uses the same 710 * colour and flags we don't need to do 711 * anything here 712 */ 713 if (acmp == cmp && start == -1) 714 goto next; 715 /* 716 * see if we can optimize things a 717 * little bit by drawing stretches of 718 * blanks using erasecols 719 */ 720 721 if (start == -1) { 722 start = j; 723 last_a = acmp; 724 } else if (acmp != last_a) { 725 /* 726 * different attr, need to 727 * flush & restart 728 */ 729 vdp->erasecols(ri, i, start, 730 j - start, last_a); 731 start = j; 732 last_a = acmp; 733 } 734 } else { 735 if (start != -1) { 736 vdp->erasecols(ri, i, start, 737 j - start, last_a); 738 start = -1; 739 } 740 741 scr->putchar(ri, i, j, c, a); 742 } 743 next: 744 #ifdef VCONS_DRAW_INTR 745 vdp->chars[boffset] = charptr[offset]; 746 vdp->attrs[boffset] = attrptr[offset]; 747 #endif 748 offset++; 749 boffset++; 750 } 751 /* end of the line - draw all deferred blanks, if any */ 752 if (start != -1) { 753 vdp->erasecols(ri, i, start, j - start, last_a); 754 } 755 } 756 ri->ri_flg &= ~RI_CURSOR; 757 scr->scr_vd->private->cursor(ri, 1, ri->ri_crow, ri->ri_ccol); 758 #ifdef VCONS_DRAW_INTR 759 vdp->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol; 760 #endif 761 } 762 vcons_unlock(scr); 763 } 764 765 void 766 vcons_update_screen(struct vcons_screen *scr) 767 { 768 #ifdef VCONS_DRAW_INTR 769 uint32_t *charptr = scr->scr_chars; 770 long *attrptr = scr->scr_attrs; 771 struct rasops_info *ri = &scr->scr_ri; 772 struct vcons_data *vd = scr->scr_vd; 773 struct vcons_data_private *vdp = vd->private; 774 int i, j, offset, boffset = 0; 775 776 vcons_lock(scr); 777 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 778 779 /* redraw the screen */ 780 #ifdef WSDISPLAY_SCROLLSUPPORT 781 offset = scr->scr_current_offset; 782 #else 783 offset = 0; 784 #endif 785 /* 786 * we mark the character cell occupied by the cursor as dirty 787 * so we don't have to deal with it 788 * notice that this isn't necessarily the position where rasops 789 * thinks it is, just where we drew it the last time 790 */ 791 if (vdp->cursor_offset >= 0) 792 vdp->attrs[vdp->cursor_offset] = 0xffffffff; 793 794 for (i = 0; i < ri->ri_rows; i++) { 795 for (j = 0; j < ri->ri_cols; j++) { 796 /* 797 * no need to use the wrapper function - we 798 * don't change any characters or attributes 799 * and we already made sure the screen we're 800 * working on is visible 801 */ 802 if ((vdp->chars[boffset] != charptr[offset]) || 803 (vdp->attrs[boffset] != attrptr[offset])) { 804 scr->putchar(ri, i, j, 805 charptr[offset], attrptr[offset]); 806 vdp->chars[boffset] = charptr[offset]; 807 vdp->attrs[boffset] = attrptr[offset]; 808 } 809 offset++; 810 boffset++; 811 } 812 } 813 ri->ri_flg &= ~RI_CURSOR; 814 scr->scr_vd->private->cursor(ri, 1, ri->ri_crow, ri->ri_ccol); 815 vdp->cursor_offset = ri->ri_crow * ri->ri_cols + ri->ri_ccol; 816 } 817 vcons_unlock(scr); 818 #else /* !VCONS_DRAW_INTR */ 819 vcons_redraw_screen(scr); 820 #endif 821 } 822 823 static int 824 vcons_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 825 struct lwp *l) 826 { 827 struct vcons_data *vd = v; 828 struct vcons_data_private *vdp = vd->private; 829 int error = 0; 830 831 832 switch (cmd) { 833 case WSDISPLAYIO_GETWSCHAR: 834 error = vcons_getwschar((struct vcons_screen *)vs, 835 (struct wsdisplay_char *)data); 836 break; 837 838 case WSDISPLAYIO_PUTWSCHAR: 839 error = vcons_putwschar((struct vcons_screen *)vs, 840 (struct wsdisplay_char *)data); 841 break; 842 843 case WSDISPLAYIO_SET_POLLING: { 844 int poll = *(int *)data; 845 846 /* first call the driver's ioctl handler */ 847 if (vdp->ioctl != NULL) 848 error = (*vdp->ioctl)(v, vs, cmd, data, flag, l); 849 if (poll) { 850 vcons_enable_polling(vd); 851 vcons_hard_switch(LIST_FIRST(&vdp->screens)); 852 } else 853 vcons_disable_polling(vd); 854 } 855 break; 856 857 case WSDISPLAYIO_GFONT: { 858 struct wsdisplay_getfont *gf = data; 859 size_t actual; 860 struct wsdisplay_font *font; 861 const char *fontname; 862 863 font = ((struct vcons_screen *)vs)->scr_ri.ri_font; 864 fontname = font && font->name ? font->name : ""; 865 error = copyoutstr(fontname, gf->gf_name, gf->gf_size, &actual); 866 if (!error) 867 gf->gf_actual = actual; 868 } 869 break; 870 871 default: 872 if (vdp->ioctl != NULL) 873 error = (*vdp->ioctl)(v, vs, cmd, data, flag, l); 874 else 875 error = EPASSTHROUGH; 876 } 877 878 return error; 879 } 880 881 static int 882 vcons_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep, 883 int *curxp, int *curyp, long *defattrp) 884 { 885 struct vcons_data *vd = v; 886 struct vcons_data_private *vdp = vd->private; 887 struct vcons_screen *scr; 888 struct wsscreen_descr *t = __UNCONST(type); 889 int ret; 890 891 scr = malloc(sizeof(struct vcons_screen), M_DEVBUF, M_WAITOK | M_ZERO); 892 if (scr == NULL) 893 return ENOMEM; 894 895 scr->scr_flags = 0; 896 scr->scr_status = 0; 897 scr->scr_busy = 0; 898 scr->scr_type = __UNCONST(type); 899 900 ret = vcons_init_screen(vd, scr, 0, defattrp); 901 if (ret != 0) { 902 free(scr, M_DEVBUF); 903 return ret; 904 } 905 if (t->capabilities & WSSCREEN_RESIZE) { 906 t->nrows = scr->scr_ri.ri_rows; 907 t->ncols = scr->scr_ri.ri_cols; 908 } 909 910 if (vd->active == NULL) { 911 SCREEN_VISIBLE(scr); 912 vd->active = scr; 913 vdp->currenttype = type; 914 } 915 916 *cookiep = scr; 917 *curxp = scr->scr_ri.ri_ccol; 918 *curyp = scr->scr_ri.ri_crow; 919 return 0; 920 } 921 922 static void 923 vcons_free_screen(void *v, void *cookie) 924 { 925 struct vcons_data *vd = v; 926 struct vcons_screen *scr = cookie; 927 928 vcons_lock(scr); 929 /* there should be no rasops activity here */ 930 931 LIST_REMOVE(scr, next); 932 933 if ((scr->scr_flags & VCONS_SCREEN_IS_STATIC) == 0) { 934 free(scr->scr_attrs, M_DEVBUF); 935 free(scr, M_DEVBUF); 936 } else { 937 /* 938 * maybe we should just restore the old rasops_info methods 939 * and free the character/attribute buffer here? 940 */ 941 #ifdef VCONS_DEBUG 942 panic("vcons_free_screen: console"); 943 #else 944 printf("vcons_free_screen: console\n"); 945 #endif 946 } 947 948 if (vd->active == scr) 949 vd->active = NULL; 950 } 951 952 static int 953 vcons_show_screen(void *v, void *cookie, int waitok, 954 void (*cb)(void *, int, int), void *cb_arg) 955 { 956 struct vcons_data *vd = v; 957 struct vcons_data_private *vdp = vd->private; 958 struct vcons_screen *scr; 959 960 scr = cookie; 961 if (scr == vd->active) 962 return 0; 963 964 vdp->wanted = scr; 965 vdp->switch_cb = cb; 966 vdp->switch_cb_arg = cb_arg; 967 if (cb) { 968 callout_schedule(&vdp->switch_callout, 0); 969 return EAGAIN; 970 } 971 972 vcons_do_switch(vd); 973 return 0; 974 } 975 976 /* wrappers for rasops_info methods */ 977 978 static void 979 vcons_copycols_buffer(void *cookie, int row, int srccol, int dstcol, int ncols) 980 { 981 struct rasops_info *ri = cookie; 982 struct vcons_screen *scr = ri->ri_hw; 983 int from = srccol + row * ri->ri_cols; 984 int to = dstcol + row * ri->ri_cols; 985 int offset = vcons_offset_to_zero(scr); 986 987 memmove(&scr->scr_attrs[offset + to], &scr->scr_attrs[offset + from], 988 ncols * sizeof(long)); 989 memmove(&scr->scr_chars[offset + to], &scr->scr_chars[offset + from], 990 ncols * sizeof(uint32_t)); 991 992 vcons_dirty(scr); 993 } 994 995 static void 996 vcons_copycols(void *cookie, int row, int srccol, int dstcol, int ncols) 997 { 998 struct rasops_info *ri = cookie; 999 struct vcons_screen *scr = ri->ri_hw; 1000 1001 vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols); 1002 1003 if (vcons_use_intr(scr)) 1004 return; 1005 1006 vcons_lock(scr); 1007 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1008 #if defined(VCONS_DRAW_INTR) 1009 vcons_update_screen(scr); 1010 #else 1011 scr->scr_vd->private->copycols(cookie, row, srccol, dstcol, 1012 ncols); 1013 #endif 1014 } 1015 vcons_unlock(scr); 1016 } 1017 1018 static void 1019 vcons_copycols_noread(void *cookie, int row, int srccol, int dstcol, int ncols) 1020 { 1021 struct rasops_info *ri = cookie; 1022 struct vcons_screen *scr = ri->ri_hw; 1023 #ifdef VCONS_DRAW_INTR 1024 struct vcons_data *vd = scr->scr_vd; 1025 struct vcons_data_private *vdp = vd->private; 1026 #endif 1027 1028 vcons_lock(scr); 1029 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1030 int pos, c, offset, ppos; 1031 1032 #ifdef WSDISPLAY_SCROLLSUPPORT 1033 offset = scr->scr_current_offset; 1034 #else 1035 offset = 0; 1036 #endif 1037 ppos = ri->ri_cols * row + dstcol; 1038 pos = ppos + offset; 1039 for (c = dstcol; c < (dstcol + ncols); c++) { 1040 #ifdef VCONS_DRAW_INTR 1041 if ((scr->scr_chars[pos] != vdp->chars[ppos]) || 1042 (scr->scr_attrs[pos] != vdp->attrs[ppos])) { 1043 scr->putchar(cookie, row, c, 1044 scr->scr_chars[pos], scr->scr_attrs[pos]); 1045 vdp->chars[ppos] = scr->scr_chars[pos]; 1046 vdp->attrs[ppos] = scr->scr_attrs[pos]; 1047 } 1048 #else 1049 scr->putchar(cookie, row, c, scr->scr_chars[pos], 1050 scr->scr_attrs[pos]); 1051 #endif 1052 pos++; 1053 ppos++; 1054 } 1055 if (ri->ri_crow == row && 1056 (ri->ri_ccol >= dstcol && ri->ri_ccol < (dstcol + ncols ))) 1057 ri->ri_flg &= ~RI_CURSOR; 1058 } 1059 vcons_unlock(scr); 1060 } 1061 1062 static void 1063 vcons_erasecols_buffer(void *cookie, int row, int startcol, int ncols, long fillattr) 1064 { 1065 struct rasops_info *ri = cookie; 1066 struct vcons_screen *scr = ri->ri_hw; 1067 int start = startcol + row * ri->ri_cols; 1068 int end = start + ncols, i; 1069 int offset = vcons_offset_to_zero(scr); 1070 1071 for (i = start; i < end; i++) { 1072 scr->scr_attrs[offset + i] = fillattr; 1073 scr->scr_chars[offset + i] = 0x20; 1074 } 1075 1076 vcons_dirty(scr); 1077 } 1078 1079 #ifdef VCONS_DRAW_INTR 1080 static void 1081 vcons_erasecols_cached(void *cookie, int row, int startcol, int ncols, long fillattr) 1082 { 1083 struct rasops_info *ri = cookie; 1084 struct vcons_screen *scr = ri->ri_hw; 1085 struct vcons_data *vd = scr->scr_vd; 1086 struct vcons_data_private *vdp = vd->private; 1087 int i, pos = row * ri->ri_cols + startcol; 1088 1089 vdp->erasecols(cookie, row, startcol, ncols, fillattr); 1090 for (i = pos; i < ncols; i++) { 1091 vdp->chars[i] = scr->scr_chars[i]; 1092 vdp->attrs[i] = scr->scr_attrs[i]; 1093 } 1094 } 1095 #endif 1096 1097 static void 1098 vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr) 1099 { 1100 struct rasops_info *ri = cookie; 1101 struct vcons_screen *scr = ri->ri_hw; 1102 1103 vcons_erasecols_buffer(cookie, row, startcol, ncols, fillattr); 1104 1105 if (vcons_use_intr(scr)) 1106 return; 1107 1108 vcons_lock(scr); 1109 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1110 #ifdef VCONS_DRAW_INTR 1111 vcons_erasecols_cached(cookie, row, startcol, ncols, 1112 fillattr); 1113 #else 1114 scr->scr_vd->private->erasecols(cookie, row, startcol, ncols, 1115 fillattr); 1116 #endif 1117 } 1118 vcons_unlock(scr); 1119 } 1120 1121 static void 1122 vcons_copyrows_buffer(void *cookie, int srcrow, int dstrow, int nrows) 1123 { 1124 struct rasops_info *ri = cookie; 1125 struct vcons_screen *scr = ri->ri_hw; 1126 int from, to, len; 1127 int offset = vcons_offset_to_zero(scr); 1128 1129 /* do we need to scroll the back buffer? */ 1130 if (dstrow == 0 && offset != 0) { 1131 from = ri->ri_cols * srcrow; 1132 to = ri->ri_cols * dstrow; 1133 1134 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 1135 offset * sizeof(long)); 1136 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 1137 offset * sizeof(uint32_t)); 1138 } 1139 from = ri->ri_cols * srcrow + offset; 1140 to = ri->ri_cols * dstrow + offset; 1141 len = ri->ri_cols * nrows; 1142 1143 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 1144 len * sizeof(long)); 1145 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 1146 len * sizeof(uint32_t)); 1147 1148 vcons_dirty(scr); 1149 } 1150 1151 static void 1152 vcons_copyrows(void *cookie, int srcrow, int dstrow, int nrows) 1153 { 1154 struct rasops_info *ri = cookie; 1155 struct vcons_screen *scr = ri->ri_hw; 1156 1157 vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows); 1158 1159 if (vcons_use_intr(scr)) 1160 return; 1161 1162 vcons_lock(scr); 1163 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1164 #if defined(VCONS_DRAW_INTR) 1165 vcons_update_screen(scr); 1166 #else 1167 scr->scr_vd->private->copyrows(cookie, srcrow, dstrow, nrows); 1168 #endif 1169 } 1170 vcons_unlock(scr); 1171 } 1172 1173 static void 1174 vcons_copyrows_noread(void *cookie, int srcrow, int dstrow, int nrows) 1175 { 1176 struct rasops_info *ri = cookie; 1177 struct vcons_screen *scr = ri->ri_hw; 1178 #ifdef VCONS_DRAW_INTR 1179 struct vcons_data *vd = scr->scr_vd; 1180 struct vcons_data_private *vdp = vd->private; 1181 #endif 1182 vcons_lock(scr); 1183 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1184 int pos, l, c, offset, ppos; 1185 1186 #ifdef WSDISPLAY_SCROLLSUPPORT 1187 offset = scr->scr_current_offset; 1188 #else 1189 offset = 0; 1190 #endif 1191 ppos = ri->ri_cols * dstrow; 1192 pos = ppos + offset; 1193 for (l = dstrow; l < (dstrow + nrows); l++) { 1194 for (c = 0; c < ri->ri_cols; c++) { 1195 #ifdef VCONS_DRAW_INTR 1196 if ((scr->scr_chars[pos] != vdp->chars[ppos]) || 1197 (scr->scr_attrs[pos] != vdp->attrs[ppos])) { 1198 scr->putchar(cookie, l, c, 1199 scr->scr_chars[pos], scr->scr_attrs[pos]); 1200 vdp->chars[ppos] = scr->scr_chars[pos]; 1201 vdp->attrs[ppos] = scr->scr_attrs[pos]; 1202 } 1203 #else 1204 scr->putchar(cookie, l, c, scr->scr_chars[pos], 1205 scr->scr_attrs[pos]); 1206 #endif 1207 pos++; 1208 ppos++; 1209 } 1210 } 1211 if (ri->ri_crow >= dstrow && ri->ri_crow < (dstrow + nrows)) 1212 ri->ri_flg &= ~RI_CURSOR; 1213 } 1214 vcons_unlock(scr); 1215 } 1216 1217 static void 1218 vcons_eraserows_buffer(void *cookie, int row, int nrows, long fillattr) 1219 { 1220 struct rasops_info *ri = cookie; 1221 struct vcons_screen *scr = ri->ri_hw; 1222 int offset = vcons_offset_to_zero(scr); 1223 int start, end, i; 1224 1225 start = ri->ri_cols * row + offset; 1226 end = ri->ri_cols * (row + nrows) + offset; 1227 1228 for (i = start; i < end; i++) { 1229 scr->scr_attrs[i] = fillattr; 1230 scr->scr_chars[i] = 0x20; 1231 } 1232 1233 vcons_dirty(scr); 1234 } 1235 1236 #ifdef VCONS_DRAW_INTR 1237 static void 1238 vcons_eraserows_cached(void *cookie, int row, int nrows, long fillattr) 1239 { 1240 struct rasops_info *ri = cookie; 1241 struct vcons_screen *scr = ri->ri_hw; 1242 struct vcons_data *vd = scr->scr_vd; 1243 struct vcons_data_private *vdp = vd->private; 1244 int i, pos = row * ri->ri_cols, end = (row+nrows) * ri->ri_cols; 1245 1246 for (i = pos; i < end; i++) { 1247 vdp->chars[i] = 0x20; 1248 vdp->attrs[i] = fillattr; 1249 } 1250 vdp->eraserows(cookie, row, nrows, fillattr); 1251 } 1252 #endif 1253 1254 static void 1255 vcons_eraserows(void *cookie, int row, int nrows, long fillattr) 1256 { 1257 struct rasops_info *ri = cookie; 1258 struct vcons_screen *scr = ri->ri_hw; 1259 1260 vcons_eraserows_buffer(cookie, row, nrows, fillattr); 1261 1262 if (vcons_use_intr(scr)) 1263 return; 1264 1265 vcons_lock(scr); 1266 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1267 #ifdef VCONS_DRAW_INTR 1268 vcons_eraserows_cached(cookie, row, nrows, fillattr); 1269 #else 1270 scr->scr_vd->private->eraserows(cookie, row, nrows, fillattr); 1271 #endif 1272 } 1273 vcons_unlock(scr); 1274 } 1275 1276 static int 1277 vcons_putchar_buffer(void *cookie, int row, int col, u_int c, long attr) 1278 { 1279 struct rasops_info *ri = cookie; 1280 struct vcons_screen *scr = ri->ri_hw; 1281 int offset = vcons_offset_to_zero(scr); 1282 int pos, ret = 0; 1283 1284 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) && 1285 (col < ri->ri_cols)) { 1286 pos = col + row * ri->ri_cols; 1287 ret = (scr->scr_attrs[pos + offset] != attr) || 1288 (scr->scr_chars[pos + offset] != c); 1289 scr->scr_attrs[pos + offset] = attr; 1290 scr->scr_chars[pos + offset] = c; 1291 } 1292 1293 if (ret) 1294 vcons_dirty(scr); 1295 return ret; 1296 } 1297 1298 #ifdef VCONS_DRAW_INTR 1299 static void 1300 vcons_putchar_cached(void *cookie, int row, int col, u_int c, long attr) 1301 { 1302 struct rasops_info *ri = cookie; 1303 struct vcons_screen *scr = ri->ri_hw; 1304 struct vcons_data *vd = scr->scr_vd; 1305 struct vcons_data_private *vdp = vd->private; 1306 int pos = row * ri->ri_cols + col; 1307 1308 if ((vdp->chars == NULL) || (vdp->attrs == NULL)) { 1309 scr->putchar(cookie, row, col, c, attr); 1310 return; 1311 } 1312 if ((vdp->chars[pos] != c) || (vdp->attrs[pos] != attr)) { 1313 vdp->attrs[pos] = attr; 1314 vdp->chars[pos] = c; 1315 scr->putchar(cookie, row, col, c, attr); 1316 } 1317 } 1318 #endif 1319 1320 static void 1321 vcons_putchar(void *cookie, int row, int col, u_int c, long attr) 1322 { 1323 struct rasops_info *ri = cookie; 1324 struct vcons_screen *scr = ri->ri_hw; 1325 int need_draw; 1326 1327 need_draw = vcons_putchar_buffer(cookie, row, col, c, attr); 1328 1329 if (vcons_use_intr(scr)) 1330 return; 1331 1332 vcons_lock(scr); 1333 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1334 #ifdef VCONS_DRAW_INTR 1335 if (need_draw) 1336 vcons_putchar_cached(cookie, row, col, c, attr); 1337 #else 1338 if (row == ri->ri_crow && col == ri->ri_ccol) { 1339 ri->ri_flg &= ~RI_CURSOR; 1340 scr->putchar(cookie, row, col, c, attr); 1341 } else if (need_draw) 1342 scr->putchar(cookie, row, col, c, attr); 1343 #endif 1344 } 1345 vcons_unlock(scr); 1346 } 1347 1348 static void 1349 vcons_cursor(void *cookie, int on, int row, int col) 1350 { 1351 struct rasops_info *ri = cookie; 1352 struct vcons_screen *scr = ri->ri_hw; 1353 1354 if (vcons_use_intr(scr)) { 1355 vcons_lock(scr); 1356 if (scr->scr_ri.ri_crow != row || scr->scr_ri.ri_ccol != col) { 1357 scr->scr_ri.ri_crow = row; 1358 scr->scr_ri.ri_ccol = col; 1359 vcons_dirty(scr); 1360 } 1361 vcons_unlock(scr); 1362 return; 1363 } 1364 1365 vcons_lock(scr); 1366 1367 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 1368 scr->scr_vd->private->cursor(cookie, on, row, col); 1369 } else { 1370 scr->scr_ri.ri_crow = row; 1371 scr->scr_ri.ri_ccol = col; 1372 } 1373 vcons_unlock(scr); 1374 } 1375 1376 static void 1377 vcons_cursor_noread(void *cookie, int on, int row, int col) 1378 { 1379 struct rasops_info *ri = cookie; 1380 struct vcons_screen *scr = ri->ri_hw; 1381 int offset = 0, ofs; 1382 1383 #ifdef WSDISPLAY_SCROLLSUPPORT 1384 offset = scr->scr_current_offset; 1385 #endif 1386 ofs = offset + ri->ri_crow * ri->ri_cols + ri->ri_ccol; 1387 if ((ri->ri_flg & RI_CURSOR) && 1388 (((scr->scr_flags & VCONS_DONT_READ) != VCONS_DONT_READ) || on)) { 1389 scr->putchar(cookie, ri->ri_crow, ri->ri_ccol, 1390 scr->scr_chars[ofs], scr->scr_attrs[ofs]); 1391 ri->ri_flg &= ~RI_CURSOR; 1392 } 1393 ri->ri_crow = row; 1394 ri->ri_ccol = col; 1395 ofs = offset + ri->ri_crow * ri->ri_cols + ri->ri_ccol; 1396 if (on) { 1397 scr->putchar(cookie, row, col, scr->scr_chars[ofs], 1398 #ifdef VCONS_DEBUG_CURSOR_NOREAD 1399 /* draw a red cursor so we can tell which cursor() 1400 * implementation is being used */ 1401 ((scr->scr_attrs[ofs] & 0xff00ffff) ^ 0x0f000000) | 1402 0x00010000); 1403 #else 1404 scr->scr_attrs[ofs] ^ 0x0f0f0000); 1405 #endif 1406 ri->ri_flg |= RI_CURSOR; 1407 } 1408 } 1409 1410 /* methods to read/write characters via ioctl() */ 1411 1412 static int 1413 vcons_putwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 1414 { 1415 long attr; 1416 struct rasops_info *ri; 1417 int error; 1418 1419 KASSERT(scr != NULL); 1420 KASSERT(wsc != NULL); 1421 1422 ri = &scr->scr_ri; 1423 1424 /* allow col as linear index if row == 0 */ 1425 if (wsc->row == 0) { 1426 if (wsc->col < 0 || wsc->col > (ri->ri_cols * ri->ri_rows)) 1427 return EINVAL; 1428 int rem; 1429 rem = wsc->col % ri->ri_cols; 1430 wsc->row = wsc->col / ri->ri_cols; 1431 DPRINTF("off %d -> %d, %d\n", wsc->col, rem, wsc->row); 1432 wsc->col = rem; 1433 } else { 1434 if (__predict_false(wsc->col < 0 || wsc->col >= ri->ri_cols)) 1435 return EINVAL; 1436 1437 if (__predict_false(wsc->row < 0 || wsc->row >= ri->ri_rows)) 1438 return EINVAL; 1439 } 1440 1441 error = ri->ri_ops.allocattr(ri, wsc->foreground, 1442 wsc->background, wsc->flags, &attr); 1443 if (error) 1444 return error; 1445 vcons_putchar(ri, wsc->row, wsc->col, wsc->letter, attr); 1446 DPRINTF("vcons_putwschar(%d, %d, %x, %lx\n", wsc->row, wsc->col, 1447 wsc->letter, attr); 1448 return 0; 1449 } 1450 1451 static int 1452 vcons_getwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 1453 { 1454 int offset; 1455 long attr; 1456 struct rasops_info *ri; 1457 int fg, bg, ul; 1458 1459 KASSERT(scr != NULL); 1460 KASSERT(wsc != NULL); 1461 1462 ri = &scr->scr_ri; 1463 1464 /* allow col as linear index if row == 0 */ 1465 if (wsc->row == 0) { 1466 if (wsc->col < 0 || wsc->col > (ri->ri_cols * ri->ri_rows)) 1467 return EINVAL; 1468 int rem; 1469 rem = wsc->col % ri->ri_cols; 1470 wsc->row = wsc->col / ri->ri_cols; 1471 DPRINTF("off %d -> %d, %d\n", wsc->col, rem, wsc->row); 1472 wsc->col = rem; 1473 } else { 1474 if (__predict_false(wsc->col < 0 || wsc->col >= ri->ri_cols)) 1475 return EINVAL; 1476 1477 if (__predict_false(wsc->row < 0 || wsc->row >= ri->ri_rows)) 1478 return EINVAL; 1479 } 1480 1481 offset = ri->ri_cols * wsc->row + wsc->col; 1482 offset += vcons_offset_to_zero(scr); 1483 wsc->letter = scr->scr_chars[offset]; 1484 attr = scr->scr_attrs[offset]; 1485 1486 DPRINTF("vcons_getwschar: %d, %d, %x, %lx\n", wsc->row, 1487 wsc->col, wsc->letter, attr); 1488 1489 /* 1490 * this is ugly. We need to break up an attribute into colours and 1491 * flags but there's no rasops method to do that so we must rely on 1492 * the 'canonical' encoding. 1493 */ 1494 1495 /* only fetches underline attribute */ 1496 /* rasops_unpack_attr(attr, &fg, &bg, &ul); */ 1497 fg = (attr >> 24) & 0xf; 1498 bg = (attr >> 16) & 0xf; 1499 ul = (attr & 1); 1500 1501 wsc->foreground = fg; 1502 wsc->background = bg; 1503 1504 /* clear trashed bits and restore underline flag */ 1505 attr &= ~(WSATTR_HILIT | WSATTR_BLINK | WSATTR_UNDERLINE); 1506 if (ul) 1507 attr |= WSATTR_UNDERLINE; 1508 1509 /* restore highlight boost */ 1510 if (attr & WSATTR_HILIT) 1511 if (wsc->foreground >= 8) 1512 wsc->foreground -= 8; 1513 1514 /* we always use colors, even when not stored */ 1515 attr |= WSATTR_WSCOLORS; 1516 return 0; 1517 } 1518 1519 int 1520 vcons_offset_to_zero(const struct vcons_screen *scr) 1521 { 1522 #ifdef WSDISPLAY_SCROLLSUPPORT 1523 return scr->scr_offset_to_zero; 1524 #else 1525 return 0; 1526 #endif 1527 } 1528 1529 #ifdef WSDISPLAY_SCROLLSUPPORT 1530 1531 static void 1532 vcons_scroll(void *cookie, void *vs, int where) 1533 { 1534 struct vcons_screen *scr = vs; 1535 1536 if (where == 0) { 1537 scr->scr_line_wanted = 0; 1538 } else { 1539 scr->scr_line_wanted = scr->scr_line_wanted - where; 1540 if (scr->scr_line_wanted < 0) 1541 scr->scr_line_wanted = 0; 1542 if (scr->scr_line_wanted > scr->scr_lines_in_buffer) 1543 scr->scr_line_wanted = scr->scr_lines_in_buffer; 1544 } 1545 1546 if (scr->scr_line_wanted != scr->scr_current_line) { 1547 1548 vcons_do_scroll(scr); 1549 } 1550 } 1551 1552 static void 1553 vcons_do_scroll(struct vcons_screen *scr) 1554 { 1555 int dist, from, to, num; 1556 int r_offset, r_start; 1557 int i, j; 1558 1559 if (scr->scr_line_wanted == scr->scr_current_line) 1560 return; 1561 dist = scr->scr_line_wanted - scr->scr_current_line; 1562 scr->scr_current_line = scr->scr_line_wanted; 1563 scr->scr_current_offset = scr->scr_ri.ri_cols * 1564 (scr->scr_lines_in_buffer - scr->scr_current_line); 1565 if (abs(dist) >= scr->scr_ri.ri_rows) { 1566 vcons_redraw_screen(scr); 1567 return; 1568 } 1569 /* scroll and redraw only what we really have to */ 1570 if (dist > 0) { 1571 /* we scroll down */ 1572 from = 0; 1573 to = dist; 1574 num = scr->scr_ri.ri_rows - dist; 1575 /* now the redraw parameters */ 1576 r_offset = scr->scr_current_offset; 1577 r_start = 0; 1578 } else { 1579 /* scrolling up */ 1580 to = 0; 1581 from = -dist; 1582 num = scr->scr_ri.ri_rows + dist; 1583 r_offset = scr->scr_current_offset + num * scr->scr_ri.ri_cols; 1584 r_start = num; 1585 } 1586 scr->scr_vd->private->copyrows(scr, from, to, num); 1587 for (i = 0; i < abs(dist); i++) { 1588 for (j = 0; j < scr->scr_ri.ri_cols; j++) { 1589 #ifdef VCONS_DRAW_INTR 1590 vcons_putchar_cached(scr, i + r_start, j, 1591 scr->scr_chars[r_offset], 1592 scr->scr_attrs[r_offset]); 1593 #else 1594 scr->putchar(scr, i + r_start, j, 1595 scr->scr_chars[r_offset], 1596 scr->scr_attrs[r_offset]); 1597 #endif 1598 r_offset++; 1599 } 1600 } 1601 1602 if (scr->scr_line_wanted == 0) { 1603 /* this was a reset - need to draw the cursor */ 1604 scr->scr_ri.ri_flg &= ~RI_CURSOR; 1605 scr->scr_vd->private->cursor(scr, 1, scr->scr_ri.ri_crow, 1606 scr->scr_ri.ri_ccol); 1607 } 1608 } 1609 1610 #endif /* WSDISPLAY_SCROLLSUPPORT */ 1611 1612 #ifdef VCONS_DRAW_INTR 1613 static void 1614 vcons_intr(void *cookie) 1615 { 1616 struct vcons_data *vd = cookie; 1617 struct vcons_data_private *vdp = vd->private; 1618 1619 softint_schedule(vdp->intr_softint); 1620 } 1621 1622 static void 1623 vcons_softintr(void *cookie) 1624 { 1625 struct vcons_data *vd = cookie; 1626 struct vcons_data_private *vdp = vd->private; 1627 struct vcons_screen *scr = vd->active; 1628 unsigned int dirty; 1629 1630 if (scr && vdp->use_intr) { 1631 if (!SCREEN_IS_BUSY(scr)) { 1632 dirty = atomic_swap_uint(&scr->scr_dirty, 0); 1633 membar_acquire(); 1634 if (vdp->use_intr == 2) { 1635 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) { 1636 vdp->use_intr = 1; 1637 vcons_redraw_screen(scr); 1638 } 1639 } else if (dirty > 0) { 1640 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 1641 vcons_update_screen(scr); 1642 } 1643 } 1644 } 1645 1646 callout_schedule(&vdp->intr, mstohz(33)); 1647 } 1648 1649 static void 1650 vcons_init_thread(void *cookie) 1651 { 1652 struct vcons_data *vd = (struct vcons_data *)cookie; 1653 struct vcons_data_private *vdp = vd->private; 1654 1655 vdp->use_intr = 2; 1656 callout_schedule(&vdp->intr, mstohz(33)); 1657 kthread_exit(0); 1658 } 1659 #endif /* VCONS_DRAW_INTR */ 1660 1661 void 1662 vcons_enable_polling(struct vcons_data *vd) 1663 { 1664 struct vcons_screen *scr = vd->active; 1665 1666 #ifdef VCONS_DRAW_INTR 1667 struct vcons_data_private *vdp = vd->private; 1668 1669 vdp->use_intr = 0; 1670 #endif 1671 1672 if (scr && !SCREEN_IS_BUSY(scr)) { 1673 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 1674 vcons_redraw_screen(scr); 1675 } 1676 } 1677 1678 void 1679 vcons_disable_polling(struct vcons_data *vd) 1680 { 1681 #ifdef VCONS_DRAW_INTR 1682 struct vcons_data_private *vdp = vd->private; 1683 struct vcons_screen *scr = vd->active; 1684 1685 if (!vdp->intr_valid) 1686 return; 1687 1688 vdp->use_intr = 2; 1689 if (scr) 1690 vcons_dirty(scr); 1691 #endif 1692 } 1693 1694 void 1695 vcons_hard_switch(struct vcons_screen *scr) 1696 { 1697 struct vcons_data *vd = scr->scr_vd; 1698 struct vcons_data_private *vdp = vd->private; 1699 struct vcons_screen *oldscr = vd->active; 1700 1701 if (oldscr) { 1702 SCREEN_INVISIBLE(oldscr); 1703 oldscr->scr_ri.ri_flg &= ~RI_CURSOR; 1704 } 1705 SCREEN_VISIBLE(scr); 1706 vd->active = scr; 1707 vdp->wanted = NULL; 1708 1709 if (vd->show_screen_cb != NULL) 1710 vd->show_screen_cb(scr, vd->show_screen_cookie); 1711 } 1712 1713 #ifdef VCONS_DRAW_INTR 1714 static void 1715 vcons_invalidate_cache(struct vcons_data *vd) 1716 { 1717 struct vcons_data_private *vdp = vd->private; 1718 int i; 1719 1720 for (i = 0; i < vdp->cells; i++) { 1721 vdp->chars[i] = -1; 1722 vdp->attrs[i] = -1; 1723 } 1724 } 1725 #endif 1726