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