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