1 /* $NetBSD: wsdisplay_vcons.c,v 1.19 2011/01/25 20:28:21 macallan Exp $ */ 2 3 /*- 4 * Copyright (c) 2005, 2006 Michael Lorenz 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: wsdisplay_vcons.c,v 1.19 2011/01/25 20:28:21 macallan Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/buf.h> 36 #include <sys/device.h> 37 #include <sys/ioctl.h> 38 #include <sys/malloc.h> 39 #include <sys/mman.h> 40 #include <sys/tty.h> 41 #include <sys/conf.h> 42 #include <sys/proc.h> 43 #include <sys/kthread.h> 44 #include <sys/tprintf.h> 45 #include <sys/atomic.h> 46 47 #include <dev/wscons/wsdisplayvar.h> 48 #include <dev/wscons/wsconsio.h> 49 #include <dev/wsfont/wsfont.h> 50 #include <dev/rasops/rasops.h> 51 52 #include <dev/wscons/wsdisplay_vconsvar.h> 53 54 #include "opt_wsemul.h" 55 #include "opt_wsdisplay_compat.h" 56 #include "opt_vcons.h" 57 58 static void vcons_dummy_init_screen(void *, struct vcons_screen *, int, 59 long *); 60 61 static int vcons_ioctl(void *, void *, u_long, void *, int, struct lwp *); 62 static int vcons_alloc_screen(void *, const struct wsscreen_descr *, void **, 63 int *, int *, long *); 64 static void vcons_free_screen(void *, void *); 65 static int vcons_show_screen(void *, void *, int, void (*)(void *, int, int), 66 void *); 67 68 #ifdef WSDISPLAY_SCROLLSUPPORT 69 static void vcons_scroll(void *, void *, int); 70 static void vcons_do_scroll(struct vcons_screen *); 71 #endif 72 73 static void vcons_do_switch(void *); 74 75 /* methods that work only on text buffers */ 76 static void vcons_copycols_buffer(void *, int, int, int, int); 77 static void vcons_erasecols_buffer(void *, int, int, int, long); 78 static void vcons_copyrows_buffer(void *, int, int, int); 79 static void vcons_eraserows_buffer(void *, int, int, long); 80 static void vcons_putchar_buffer(void *, int, int, u_int, long); 81 82 #ifdef VCONS_DRAW_ASYNC 83 /* methods that work asynchronously */ 84 static void vcons_copycols_async(void *, int, int, int, int); 85 static void vcons_erasecols_async(void *, int, int, int, long); 86 static void vcons_copyrows_async(void *, int, int, int); 87 static void vcons_eraserows_async(void *, int, int, long); 88 static void vcons_putchar_async(void *, int, int, u_int, long); 89 static void vcons_cursor_async(void *, int, int, int); 90 #endif 91 92 /* 93 * actual wrapper methods which call both the _buffer ones above and the 94 * driver supplied ones to do the drawing 95 */ 96 static void vcons_copycols(void *, int, int, int, int); 97 static void vcons_erasecols(void *, int, int, int, long); 98 static void vcons_copyrows(void *, int, int, int); 99 static void vcons_eraserows(void *, int, int, long); 100 static void vcons_putchar(void *, int, int, u_int, long); 101 static void vcons_cursor(void *, int, int, int); 102 103 /* 104 * methods that avoid framebuffer reads 105 */ 106 static void vcons_copycols_noread(void *, int, int, int, int); 107 static void vcons_copyrows_noread(void *, int, int, int); 108 109 110 /* support for reading/writing text buffers. For wsmoused */ 111 static int vcons_putwschar(struct vcons_screen *, struct wsdisplay_char *); 112 static int vcons_getwschar(struct vcons_screen *, struct wsdisplay_char *); 113 114 static void vcons_lock(struct vcons_screen *); 115 static void vcons_unlock(struct vcons_screen *); 116 117 #ifdef VCONS_DRAW_ASYNC 118 static void vcons_kthread(void *); 119 #endif 120 121 int 122 vcons_init(struct vcons_data *vd, void *cookie, struct wsscreen_descr *def, 123 struct wsdisplay_accessops *ao) 124 { 125 126 /* zero out everything so we can rely on untouched fields being 0 */ 127 memset(vd, 0, sizeof(struct vcons_data)); 128 129 vd->cookie = cookie; 130 131 vd->init_screen = vcons_dummy_init_screen; 132 vd->show_screen_cb = NULL; 133 134 /* keep a copy of the accessops that we replace below with our 135 * own wrappers */ 136 vd->ioctl = ao->ioctl; 137 138 /* configure the accessops */ 139 ao->ioctl = vcons_ioctl; 140 ao->alloc_screen = vcons_alloc_screen; 141 ao->free_screen = vcons_free_screen; 142 ao->show_screen = vcons_show_screen; 143 #ifdef WSDISPLAY_SCROLLSUPPORT 144 ao->scroll = vcons_scroll; 145 #endif 146 147 LIST_INIT(&vd->screens); 148 vd->active = NULL; 149 vd->wanted = NULL; 150 vd->currenttype = def; 151 callout_init(&vd->switch_callout, 0); 152 callout_setfunc(&vd->switch_callout, vcons_do_switch, vd); 153 154 /* 155 * a lock to serialize access to the framebuffer. 156 * when switching screens we need to make sure there's no rasops 157 * operation in progress 158 */ 159 #ifdef DIAGNOSTIC 160 vd->switch_poll_count = 0; 161 #endif 162 #ifdef VCONS_DRAW_ASYNC 163 kthread_create(PRI_NONE, 0, NULL, vcons_kthread, vd, 164 &vd->drawing_thread, "vcons_draw"); 165 #endif 166 return 0; 167 } 168 169 static void 170 vcons_lock(struct vcons_screen *scr) 171 { 172 #ifdef VCONS_PARANOIA 173 int s; 174 175 s = splhigh(); 176 #endif 177 SCREEN_BUSY(scr); 178 #ifdef VCONS_PARANOIA 179 splx(s); 180 #endif 181 } 182 183 static void 184 vcons_unlock(struct vcons_screen *scr) 185 { 186 #ifdef VCONS_PARANOIA 187 int s; 188 189 s = splhigh(); 190 #endif 191 SCREEN_IDLE(scr); 192 #ifdef VCONS_PARANOIA 193 splx(s); 194 #endif 195 } 196 197 static void 198 vcons_dummy_init_screen(void *cookie, 199 struct vcons_screen *scr, int exists, 200 long *defattr) 201 { 202 203 /* 204 * default init_screen() method. 205 * Needs to be overwritten so we bitch and whine in case anyone ends 206 * up in here. 207 */ 208 printf("vcons_init_screen: dummy function called. Your driver is " 209 "supposed to supply a replacement for proper operation\n"); 210 } 211 212 int 213 vcons_init_screen(struct vcons_data *vd, struct vcons_screen *scr, 214 int existing, long *defattr) 215 { 216 struct rasops_info *ri = &scr->scr_ri; 217 int cnt, i; 218 219 scr->scr_cookie = vd->cookie; 220 scr->scr_vd = scr->scr_origvd = vd; 221 scr->scr_busy = 0; 222 223 /* 224 * call the driver-supplied init_screen function which is expected 225 * to set up rasops_info, override cursor() and probably others 226 */ 227 vd->init_screen(vd->cookie, scr, existing, defattr); 228 229 /* 230 * save the non virtual console aware rasops and replace them with 231 * our wrappers 232 */ 233 vd->eraserows = ri->ri_ops.eraserows; 234 vd->erasecols = ri->ri_ops.erasecols; 235 vd->putchar = ri->ri_ops.putchar; 236 vd->cursor = ri->ri_ops.cursor; 237 238 if (scr->scr_flags & VCONS_NO_COPYCOLS) { 239 vd->copycols = vcons_copycols_noread; 240 } else { 241 vd->copycols = ri->ri_ops.copycols; 242 } 243 244 if (scr->scr_flags & VCONS_NO_COPYROWS) { 245 vd->copyrows = vcons_copyrows_noread; 246 } else { 247 vd->copyrows = ri->ri_ops.copyrows; 248 } 249 250 ri->ri_ops.eraserows = vcons_eraserows; 251 ri->ri_ops.erasecols = vcons_erasecols; 252 ri->ri_ops.putchar = vcons_putchar; 253 ri->ri_ops.cursor = vcons_cursor; 254 ri->ri_ops.copycols = vcons_copycols; 255 ri->ri_ops.copyrows = vcons_copyrows; 256 257 258 ri->ri_hw = scr; 259 260 /* 261 * we allocate both chars and attributes in one chunk, attributes first 262 * because they have the (potentially) bigger alignment 263 */ 264 #ifdef WSDISPLAY_SCROLLSUPPORT 265 cnt = (ri->ri_rows + WSDISPLAY_SCROLLBACK_LINES) * ri->ri_cols; 266 scr->scr_lines_in_buffer = WSDISPLAY_SCROLLBACK_LINES; 267 scr->scr_current_line = 0; 268 scr->scr_line_wanted = 0; 269 scr->scr_offset_to_zero = ri->ri_cols * WSDISPLAY_SCROLLBACK_LINES; 270 scr->scr_current_offset = scr->scr_offset_to_zero; 271 #else 272 cnt = ri->ri_rows * ri->ri_cols; 273 #endif 274 scr->scr_attrs = (long *)malloc(cnt * (sizeof(long) + 275 sizeof(uint16_t)), M_DEVBUF, M_WAITOK); 276 if (scr->scr_attrs == NULL) 277 return ENOMEM; 278 279 scr->scr_chars = (uint16_t *)&scr->scr_attrs[cnt]; 280 281 ri->ri_ops.allocattr(ri, WS_DEFAULT_FG, WS_DEFAULT_BG, 0, defattr); 282 scr->scr_defattr = *defattr; 283 284 /* 285 * fill the attribute buffer with *defattr, chars with 0x20 286 * since we don't know if the driver tries to mimic firmware output or 287 * reset everything we do nothing to VRAM here, any driver that feels 288 * the need to clear screen or something will have to do it on its own 289 * Additional screens will start out in the background anyway so 290 * cleaning or not only really affects the initial console screen 291 */ 292 for (i = 0; i < cnt; i++) { 293 scr->scr_attrs[i] = *defattr; 294 scr->scr_chars[i] = 0x20; 295 } 296 297 if(vd->active == NULL) { 298 vd->active = scr; 299 SCREEN_VISIBLE(scr); 300 } 301 302 if (existing) { 303 SCREEN_VISIBLE(scr); 304 vd->active = scr; 305 } else { 306 SCREEN_INVISIBLE(scr); 307 } 308 309 LIST_INSERT_HEAD(&vd->screens, scr, next); 310 return 0; 311 } 312 313 static void 314 vcons_do_switch(void *arg) 315 { 316 struct vcons_data *vd = arg; 317 struct vcons_screen *scr, *oldscr; 318 319 scr = vd->wanted; 320 if (!scr) { 321 printf("vcons_switch_screen: disappeared\n"); 322 vd->switch_cb(vd->switch_cb_arg, EIO, 0); 323 return; 324 } 325 oldscr = vd->active; /* can be NULL! */ 326 327 /* 328 * if there's an old, visible screen we mark it invisible and wait 329 * until it's not busy so we can safely switch 330 */ 331 if (oldscr != NULL) { 332 SCREEN_INVISIBLE(oldscr); 333 if (SCREEN_IS_BUSY(oldscr)) { 334 callout_schedule(&vd->switch_callout, 1); 335 #ifdef DIAGNOSTIC 336 /* bitch if we wait too long */ 337 vd->switch_poll_count++; 338 if (vd->switch_poll_count > 100) { 339 panic("vcons: screen still busy"); 340 } 341 #endif 342 return; 343 } 344 /* invisible screen -> no visible cursor image */ 345 oldscr->scr_ri.ri_flg &= ~RI_CURSOR; 346 #ifdef DIAGNOSTIC 347 vd->switch_poll_count = 0; 348 #endif 349 } 350 351 if (scr == oldscr) 352 return; 353 354 #ifdef DIAGNOSTIC 355 if (SCREEN_IS_VISIBLE(scr)) 356 printf("vcons_switch_screen: already active"); 357 #endif 358 359 #ifdef notyet 360 if (vd->currenttype != type) { 361 vcons_set_screentype(vd, type); 362 vd->currenttype = type; 363 } 364 #endif 365 366 SCREEN_VISIBLE(scr); 367 vd->active = scr; 368 vd->wanted = NULL; 369 370 if (vd->show_screen_cb != NULL) 371 vd->show_screen_cb(scr); 372 373 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) 374 vcons_redraw_screen(scr); 375 376 if (vd->switch_cb) 377 vd->switch_cb(vd->switch_cb_arg, 0, 0); 378 } 379 380 void 381 vcons_redraw_screen(struct vcons_screen *scr) 382 { 383 uint16_t *charptr = scr->scr_chars; 384 long *attrptr = scr->scr_attrs; 385 struct rasops_info *ri = &scr->scr_ri; 386 int i, j, offset; 387 388 vcons_lock(scr); 389 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 390 391 /* 392 * only clear the screen when RI_FULLCLEAR is set since we're 393 * going to overwrite every single character cell anyway 394 */ 395 if (ri->ri_flg & RI_FULLCLEAR) { 396 scr->scr_vd->eraserows(ri, 0, ri->ri_rows, 397 scr->scr_defattr); 398 } 399 400 /* redraw the screen */ 401 #ifdef WSDISPLAY_SCROLLSUPPORT 402 offset = scr->scr_current_offset; 403 #else 404 offset = 0; 405 #endif 406 for (i = 0; i < ri->ri_rows; i++) { 407 for (j = 0; j < ri->ri_cols; j++) { 408 /* 409 * no need to use the wrapper function - we 410 * don't change any characters or attributes 411 * and we already made sure the screen we're 412 * working on is visible 413 */ 414 scr->scr_vd->putchar(ri, i, j, 415 charptr[offset], attrptr[offset]); 416 offset++; 417 } 418 } 419 ri->ri_flg &= ~RI_CURSOR; 420 scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol); 421 } 422 vcons_unlock(scr); 423 } 424 425 static int 426 vcons_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 427 struct lwp *l) 428 { 429 struct vcons_data *vd = v; 430 int error; 431 432 switch (cmd) { 433 case WSDISPLAYIO_GETWSCHAR: 434 error = vcons_getwschar((struct vcons_screen *)vs, 435 (struct wsdisplay_char *)data); 436 break; 437 438 case WSDISPLAYIO_PUTWSCHAR: 439 error = vcons_putwschar((struct vcons_screen *)vs, 440 (struct wsdisplay_char *)data); 441 break; 442 443 default: 444 if (vd->ioctl != NULL) 445 error = (*vd->ioctl)(v, vs, cmd, data, flag, l); 446 else 447 error = EPASSTHROUGH; 448 } 449 450 return error; 451 } 452 453 static int 454 vcons_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep, 455 int *curxp, int *curyp, long *defattrp) 456 { 457 struct vcons_data *vd = v; 458 struct vcons_screen *scr; 459 int ret; 460 461 scr = malloc(sizeof(struct vcons_screen), M_DEVBUF, M_WAITOK | M_ZERO); 462 if (scr == NULL) 463 return ENOMEM; 464 465 scr->scr_flags = 0; 466 scr->scr_status = 0; 467 scr->scr_busy = 0; 468 scr->scr_type = type; 469 470 ret = vcons_init_screen(vd, scr, 0, defattrp); 471 if (ret != 0) { 472 free(scr, M_DEVBUF); 473 return ret; 474 } 475 476 if (vd->active == NULL) { 477 SCREEN_VISIBLE(scr); 478 vd->active = scr; 479 vd->currenttype = type; 480 } 481 482 *cookiep = scr; 483 *curxp = scr->scr_ri.ri_ccol; 484 *curyp = scr->scr_ri.ri_crow; 485 return 0; 486 } 487 488 static void 489 vcons_free_screen(void *v, void *cookie) 490 { 491 struct vcons_data *vd = v; 492 struct vcons_screen *scr = cookie; 493 494 vcons_lock(scr); 495 /* there should be no rasops activity here */ 496 497 LIST_REMOVE(scr, next); 498 499 if ((scr->scr_flags & VCONS_SCREEN_IS_STATIC) == 0) { 500 free(scr->scr_attrs, M_DEVBUF); 501 free(scr, M_DEVBUF); 502 } else { 503 /* 504 * maybe we should just restore the old rasops_info methods 505 * and free the character/attribute buffer here? 506 */ 507 #ifdef VCONS_DEBUG 508 panic("vcons_free_screen: console"); 509 #else 510 printf("vcons_free_screen: console\n"); 511 #endif 512 } 513 514 if (vd->active == scr) 515 vd->active = NULL; 516 } 517 518 static int 519 vcons_show_screen(void *v, void *cookie, int waitok, 520 void (*cb)(void *, int, int), void *cb_arg) 521 { 522 struct vcons_data *vd = v; 523 struct vcons_screen *scr; 524 525 scr = cookie; 526 if (scr == vd->active) 527 return 0; 528 529 vd->wanted = scr; 530 vd->switch_cb = cb; 531 vd->switch_cb_arg = cb_arg; 532 if (cb) { 533 callout_schedule(&vd->switch_callout, 0); 534 return EAGAIN; 535 } 536 537 vcons_do_switch(vd); 538 return 0; 539 } 540 541 /* wrappers for rasops_info methods */ 542 543 static void 544 vcons_copycols_buffer(void *cookie, int row, int srccol, int dstcol, int ncols) 545 { 546 struct rasops_info *ri = cookie; 547 struct vcons_screen *scr = ri->ri_hw; 548 int from = srccol + row * ri->ri_cols; 549 int to = dstcol + row * ri->ri_cols; 550 551 #ifdef WSDISPLAY_SCROLLSUPPORT 552 int offset; 553 offset = scr->scr_offset_to_zero; 554 555 memmove(&scr->scr_attrs[offset + to], &scr->scr_attrs[offset + from], 556 ncols * sizeof(long)); 557 memmove(&scr->scr_chars[offset + to], &scr->scr_chars[offset + from], 558 ncols * sizeof(uint16_t)); 559 #else 560 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 561 ncols * sizeof(long)); 562 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 563 ncols * sizeof(uint16_t)); 564 #endif 565 } 566 567 static void 568 vcons_copycols(void *cookie, int row, int srccol, int dstcol, int ncols) 569 { 570 struct rasops_info *ri = cookie; 571 struct vcons_screen *scr = ri->ri_hw; 572 573 vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols); 574 575 vcons_lock(scr); 576 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 577 #ifdef VCONS_DRAW_ASYNC 578 struct vcons_data *vd = scr->scr_vd; 579 if (vd->use_async) { 580 vcons_copycols_async(cookie, row, srccol, dstcol, ncols); 581 } else 582 #endif 583 scr->scr_vd->copycols(cookie, row, srccol, dstcol, ncols); 584 } 585 vcons_unlock(scr); 586 } 587 588 static void 589 vcons_copycols_noread(void *cookie, int row, int srccol, int dstcol, int ncols) 590 { 591 struct rasops_info *ri = cookie; 592 struct vcons_screen *scr = ri->ri_hw; 593 594 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 595 int pos, c, offset; 596 597 #ifdef WSDISPLAY_SCROLLSUPPORT 598 offset = scr->scr_current_offset; 599 #else 600 offset = 0; 601 #endif 602 pos = ri->ri_cols * row + dstcol + offset; 603 for (c = dstcol; c < (dstcol + ncols); c++) { 604 scr->scr_vd->putchar(cookie, row, c, 605 scr->scr_chars[pos], scr->scr_attrs[pos]); 606 pos++; 607 } 608 } 609 } 610 611 static void 612 vcons_erasecols_buffer(void *cookie, int row, int startcol, int ncols, long fillattr) 613 { 614 struct rasops_info *ri = cookie; 615 struct vcons_screen *scr = ri->ri_hw; 616 int start = startcol + row * ri->ri_cols; 617 int end = start + ncols, i; 618 619 #ifdef WSDISPLAY_SCROLLSUPPORT 620 int offset; 621 offset = scr->scr_offset_to_zero; 622 623 for (i = start; i < end; i++) { 624 scr->scr_attrs[offset + i] = fillattr; 625 scr->scr_chars[offset + i] = 0x20; 626 } 627 #else 628 for (i = start; i < end; i++) { 629 scr->scr_attrs[i] = fillattr; 630 scr->scr_chars[i] = 0x20; 631 } 632 #endif 633 } 634 635 static void 636 vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr) 637 { 638 struct rasops_info *ri = cookie; 639 struct vcons_screen *scr = ri->ri_hw; 640 641 vcons_erasecols_buffer(cookie, row, startcol, ncols, fillattr); 642 643 vcons_lock(scr); 644 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 645 #ifdef VCONS_DRAW_ASYNC 646 struct vcons_data *vd = scr->scr_vd; 647 if (vd->use_async) { 648 vcons_erasecols_async(cookie, row, startcol, ncols, 649 fillattr); 650 } else 651 #endif 652 scr->scr_vd->erasecols(cookie, row, startcol, ncols, 653 fillattr); 654 } 655 vcons_unlock(scr); 656 } 657 658 static void 659 vcons_copyrows_buffer(void *cookie, int srcrow, int dstrow, int nrows) 660 { 661 struct rasops_info *ri = cookie; 662 struct vcons_screen *scr = ri->ri_hw; 663 int from, to, len; 664 665 #ifdef WSDISPLAY_SCROLLSUPPORT 666 int offset; 667 offset = scr->scr_offset_to_zero; 668 669 /* do we need to scroll the back buffer? */ 670 if (dstrow == 0) { 671 from = ri->ri_cols * srcrow; 672 to = ri->ri_cols * dstrow; 673 674 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 675 scr->scr_offset_to_zero * sizeof(long)); 676 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 677 scr->scr_offset_to_zero * sizeof(uint16_t)); 678 } 679 from = ri->ri_cols * srcrow + offset; 680 to = ri->ri_cols * dstrow + offset; 681 len = ri->ri_cols * nrows; 682 683 #else 684 from = ri->ri_cols * srcrow; 685 to = ri->ri_cols * dstrow; 686 len = ri->ri_cols * nrows; 687 #endif 688 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from], 689 len * sizeof(long)); 690 memmove(&scr->scr_chars[to], &scr->scr_chars[from], 691 len * sizeof(uint16_t)); 692 } 693 694 static void 695 vcons_copyrows(void *cookie, int srcrow, int dstrow, int nrows) 696 { 697 struct rasops_info *ri = cookie; 698 struct vcons_screen *scr = ri->ri_hw; 699 700 vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows); 701 702 vcons_lock(scr); 703 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 704 #ifdef VCONS_DRAW_ASYNC 705 struct vcons_data *vd = scr->scr_vd; 706 if (vd->use_async) { 707 vcons_copyrows_async(cookie, srcrow, dstrow, nrows); 708 } else 709 #endif 710 scr->scr_vd->copyrows(cookie, srcrow, dstrow, nrows); 711 } 712 vcons_unlock(scr); 713 } 714 715 static void 716 vcons_copyrows_noread(void *cookie, int srcrow, int dstrow, int nrows) 717 { 718 struct rasops_info *ri = cookie; 719 struct vcons_screen *scr = ri->ri_hw; 720 721 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 722 int pos, l, c, offset; 723 724 #ifdef WSDISPLAY_SCROLLSUPPORT 725 offset = scr->scr_current_offset; 726 #else 727 offset = 0; 728 #endif 729 pos = ri->ri_cols * dstrow + offset; 730 for (l = dstrow; l < (dstrow + nrows); l++) { 731 for (c = 0; c < ri->ri_cols; c++) { 732 scr->scr_vd->putchar(cookie, l, c, 733 scr->scr_chars[pos], scr->scr_attrs[pos]); 734 pos++; 735 } 736 } 737 } 738 } 739 740 static void 741 vcons_eraserows_buffer(void *cookie, int row, int nrows, long fillattr) 742 { 743 struct rasops_info *ri = cookie; 744 struct vcons_screen *scr = ri->ri_hw; 745 int start, end, i; 746 747 #ifdef WSDISPLAY_SCROLLSUPPORT 748 int offset; 749 offset = scr->scr_offset_to_zero; 750 751 start = ri->ri_cols * row + offset; 752 end = ri->ri_cols * (row + nrows) + offset; 753 #else 754 start = ri->ri_cols * row; 755 end = ri->ri_cols * (row + nrows); 756 #endif 757 758 for (i = start; i < end; i++) { 759 scr->scr_attrs[i] = fillattr; 760 scr->scr_chars[i] = 0x20; 761 } 762 } 763 764 static void 765 vcons_eraserows(void *cookie, int row, int nrows, long fillattr) 766 { 767 struct rasops_info *ri = cookie; 768 struct vcons_screen *scr = ri->ri_hw; 769 770 vcons_eraserows_buffer(cookie, row, nrows, fillattr); 771 772 vcons_lock(scr); 773 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 774 #ifdef VCONS_DRAW_ASYNC 775 struct vcons_data *vd = scr->scr_vd; 776 if (vd->use_async) { 777 vcons_eraserows_async(cookie, row, nrows, fillattr); 778 } else 779 #endif 780 scr->scr_vd->eraserows(cookie, row, nrows, fillattr); 781 } 782 vcons_unlock(scr); 783 } 784 785 static void 786 vcons_putchar_buffer(void *cookie, int row, int col, u_int c, long attr) 787 { 788 struct rasops_info *ri = cookie; 789 struct vcons_screen *scr = ri->ri_hw; 790 int pos; 791 792 #ifdef WSDISPLAY_SCROLLSUPPORT 793 int offset; 794 offset = scr->scr_offset_to_zero; 795 796 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) && 797 (col < ri->ri_cols)) { 798 pos = col + row * ri->ri_cols; 799 scr->scr_attrs[pos + offset] = attr; 800 scr->scr_chars[pos + offset] = c; 801 } 802 #else 803 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) && 804 (col < ri->ri_cols)) { 805 pos = col + row * ri->ri_cols; 806 scr->scr_attrs[pos] = attr; 807 scr->scr_chars[pos] = c; 808 } 809 #endif 810 } 811 812 static void 813 vcons_putchar(void *cookie, int row, int col, u_int c, long attr) 814 { 815 struct rasops_info *ri = cookie; 816 struct vcons_screen *scr = ri->ri_hw; 817 818 vcons_putchar_buffer(cookie, row, col, c, attr); 819 820 vcons_lock(scr); 821 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 822 #ifdef VCONS_DRAW_ASYNC 823 struct vcons_data *vd = scr->scr_vd; 824 if (vd->use_async) { 825 vcons_putchar_async(cookie, row, col, c, attr); 826 } else 827 #endif 828 scr->scr_vd->putchar(cookie, row, col, c, attr); 829 } 830 vcons_unlock(scr); 831 } 832 833 static void 834 vcons_cursor(void *cookie, int on, int row, int col) 835 { 836 struct rasops_info *ri = cookie; 837 struct vcons_screen *scr = ri->ri_hw; 838 839 vcons_lock(scr); 840 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) { 841 #ifdef VCONS_DRAW_ASYNC 842 struct vcons_data *vd = scr->scr_vd; 843 if (vd->use_async) { 844 vcons_cursor_async(cookie, on, row, col); 845 } else 846 #endif 847 scr->scr_vd->cursor(cookie, on, row, col); 848 } else { 849 scr->scr_ri.ri_crow = row; 850 scr->scr_ri.ri_ccol = col; 851 } 852 vcons_unlock(scr); 853 } 854 855 /* methods to read/write characters via ioctl() */ 856 857 static int 858 vcons_putwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 859 { 860 long attr; 861 struct rasops_info *ri; 862 863 KASSERT(scr != NULL && wsc != NULL); 864 865 ri = &scr->scr_ri; 866 867 if (__predict_false((unsigned int)wsc->col > ri->ri_cols || 868 (unsigned int)wsc->row > ri->ri_rows)) 869 return (EINVAL); 870 871 if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) && 872 (wsc->col < ri->ri_cols)) { 873 874 ri->ri_ops.allocattr(ri, wsc->foreground, wsc->background, 875 wsc->flags, &attr); 876 vcons_putchar(ri, wsc->row, wsc->col, wsc->letter, attr); 877 #ifdef VCONS_DEBUG 878 printf("vcons_putwschar(%d, %d, %x, %lx\n", wsc->row, wsc->col, 879 wsc->letter, attr); 880 #endif 881 return 0; 882 } else 883 return EINVAL; 884 } 885 886 static int 887 vcons_getwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc) 888 { 889 int offset; 890 long attr; 891 struct rasops_info *ri; 892 893 KASSERT(scr != NULL && wsc != NULL); 894 895 ri = &scr->scr_ri; 896 897 if ((wsc->row >= 0) && (wsc->row < ri->ri_rows) && (wsc->col >= 0) && 898 (wsc->col < ri->ri_cols)) { 899 900 offset = ri->ri_cols * wsc->row + wsc->col; 901 #ifdef WSDISPLAY_SCROLLSUPPORT 902 offset += scr->scr_offset_to_zero; 903 #endif 904 wsc->letter = scr->scr_chars[offset]; 905 attr = scr->scr_attrs[offset]; 906 907 /* 908 * this is ugly. We need to break up an attribute into colours and 909 * flags but there's no rasops method to do that so we must rely on 910 * the 'canonical' encoding. 911 */ 912 #ifdef VCONS_DEBUG 913 printf("vcons_getwschar: %d, %d, %x, %lx\n", wsc->row, 914 wsc->col, wsc->letter, attr); 915 #endif 916 wsc->foreground = (attr >> 24) & 0xff; 917 wsc->background = (attr >> 16) & 0xff; 918 wsc->flags = attr & 0xff; 919 return 0; 920 } else 921 return EINVAL; 922 } 923 924 #ifdef WSDISPLAY_SCROLLSUPPORT 925 926 static void 927 vcons_scroll(void *cookie, void *vs, int where) 928 { 929 struct vcons_screen *scr = vs; 930 931 if (where == 0) { 932 scr->scr_line_wanted = 0; 933 } else { 934 scr->scr_line_wanted = scr->scr_line_wanted - where; 935 if (scr->scr_line_wanted < 0) 936 scr->scr_line_wanted = 0; 937 if (scr->scr_line_wanted > scr->scr_lines_in_buffer) 938 scr->scr_line_wanted = scr->scr_lines_in_buffer; 939 } 940 941 if (scr->scr_line_wanted != scr->scr_current_line) { 942 943 vcons_do_scroll(scr); 944 } 945 } 946 947 static void 948 vcons_do_scroll(struct vcons_screen *scr) 949 { 950 int dist, from, to, num; 951 int r_offset, r_start; 952 int i, j; 953 954 if (scr->scr_line_wanted == scr->scr_current_line) 955 return; 956 dist = scr->scr_line_wanted - scr->scr_current_line; 957 scr->scr_current_line = scr->scr_line_wanted; 958 scr->scr_current_offset = scr->scr_ri.ri_cols * 959 (scr->scr_lines_in_buffer - scr->scr_current_line); 960 if (abs(dist) >= scr->scr_ri.ri_rows) { 961 vcons_redraw_screen(scr); 962 return; 963 } 964 /* scroll and redraw only what we really have to */ 965 if (dist > 0) { 966 /* we scroll down */ 967 from = 0; 968 to = dist; 969 num = scr->scr_ri.ri_rows - dist; 970 /* now the redraw parameters */ 971 r_offset = scr->scr_current_offset; 972 r_start = 0; 973 } else { 974 /* scrolling up */ 975 to = 0; 976 from = -dist; 977 num = scr->scr_ri.ri_rows + dist; 978 r_offset = scr->scr_current_offset + num * scr->scr_ri.ri_cols; 979 r_start = num; 980 } 981 scr->scr_vd->copyrows(scr, from, to, num); 982 for (i = 0; i < abs(dist); i++) { 983 for (j = 0; j < scr->scr_ri.ri_cols; j++) { 984 scr->scr_vd->putchar(scr, i + r_start, j, 985 scr->scr_chars[r_offset], 986 scr->scr_attrs[r_offset]); 987 r_offset++; 988 } 989 } 990 991 if (scr->scr_line_wanted == 0) { 992 /* this was a reset - need to draw the cursor */ 993 scr->scr_ri.ri_flg &= ~RI_CURSOR; 994 scr->scr_vd->cursor(scr, 1, scr->scr_ri.ri_crow, 995 scr->scr_ri.ri_ccol); 996 } 997 } 998 999 #endif /* WSDISPLAY_SCROLLSUPPORT */ 1000 1001 /* async drawing using a kernel thread */ 1002 1003 #ifdef VCONS_DRAW_ASYNC 1004 1005 static inline uint32_t 1006 vcons_words_in_buffer(struct vcons_data *vd) 1007 { 1008 int len = vd->rb_write - vd->rb_read; 1009 1010 if (len < 0) len += VCONS_RING_BUFFER_LENGTH; 1011 if (len < 0) vd->use_async = 0; 1012 if (len >= VCONS_RING_BUFFER_LENGTH) vd->use_async = 0; 1013 return (uint32_t)len; 1014 } 1015 1016 static inline int 1017 vcons_wait_buffer(struct vcons_data *vd, uint32_t words) 1018 { 1019 int bail = 0; 1020 1021 mutex_enter(&vd->go_buffer_il); 1022 while (((VCONS_RING_BUFFER_LENGTH - vcons_words_in_buffer(vd)) < words) 1023 && (bail < 3)) { 1024 if (cv_timedwait(&vd->go_buffer, &vd->go_buffer_il, hz) 1025 == EWOULDBLOCK) 1026 bail++; 1027 } 1028 if (bail >= 3) { 1029 /* 1030 * waited too long, something is wrong so fall back to sync 1031 * we should probably kill the kthread here and try to empty 1032 * the command buffer as well 1033 */ 1034 vd->use_async = 0; 1035 } 1036 return 0; 1037 } 1038 1039 #define VRB_NEXT(idx) ((idx + 1) >= VCONS_RING_BUFFER_LENGTH) ? 0 : idx + 1 1040 1041 static void 1042 vcons_copycols_async(void *cookie, int row, int srccol, int dstcol, int ncols) 1043 { 1044 struct rasops_info *ri = cookie; 1045 struct vcons_screen *scr = ri->ri_hw; 1046 struct vcons_data *vd = scr->scr_vd; 1047 int idx; 1048 1049 vcons_wait_buffer(vd, 5); 1050 mutex_enter(&vd->drawing_mutex); 1051 mutex_exit(&vd->go_buffer_il); 1052 idx = vd->rb_write; 1053 vd->rb_buffer[idx] = VCMD_COPYCOLS; 1054 idx = VRB_NEXT(idx); 1055 vd->rb_buffer[idx] = row; 1056 idx = VRB_NEXT(idx); 1057 vd->rb_buffer[idx] = srccol; 1058 idx = VRB_NEXT(idx); 1059 vd->rb_buffer[idx] = dstcol; 1060 idx = VRB_NEXT(idx); 1061 vd->rb_buffer[idx] = ncols; 1062 idx = VRB_NEXT(idx); 1063 membar_producer(); 1064 vd->rb_write = idx; 1065 membar_enter(); 1066 mutex_exit(&vd->drawing_mutex); 1067 cv_signal(&vd->go_draw); 1068 } 1069 1070 static void 1071 vcons_erasecols_async(void *cookie, int row, int startcol, int ncols, 1072 long fillattr) 1073 { 1074 struct rasops_info *ri = cookie; 1075 struct vcons_screen *scr = ri->ri_hw; 1076 struct vcons_data *vd = scr->scr_vd; 1077 int idx; 1078 1079 vcons_wait_buffer(vd, 5); 1080 mutex_enter(&vd->drawing_mutex); 1081 mutex_exit(&vd->go_buffer_il); 1082 idx = vd->rb_write; 1083 vd->rb_buffer[idx] = VCMD_ERASECOLS; 1084 idx = VRB_NEXT(idx); 1085 vd->rb_buffer[idx] = row; 1086 idx = VRB_NEXT(idx); 1087 vd->rb_buffer[idx] = startcol; 1088 idx = VRB_NEXT(idx); 1089 vd->rb_buffer[idx] = ncols; 1090 idx = VRB_NEXT(idx); 1091 /* 1092 * XXX all drivers I've seen use 32bit attributes although fillattr is 1093 * a 64bit value on LP64 1094 */ 1095 vd->rb_buffer[idx] = (uint32_t)fillattr; 1096 idx = VRB_NEXT(idx); 1097 membar_producer(); 1098 vd->rb_write = idx; 1099 membar_enter(); 1100 mutex_exit(&vd->drawing_mutex); 1101 cv_signal(&vd->go_draw); 1102 } 1103 1104 static void 1105 vcons_copyrows_async(void *cookie, int srcrow, int dstrow, int nrows) 1106 { 1107 struct rasops_info *ri = cookie; 1108 struct vcons_screen *scr = ri->ri_hw; 1109 struct vcons_data *vd = scr->scr_vd; 1110 int idx; 1111 1112 vcons_wait_buffer(vd, 4); 1113 mutex_enter(&vd->drawing_mutex); 1114 mutex_exit(&vd->go_buffer_il); 1115 idx = vd->rb_write; 1116 vd->rb_buffer[idx] = VCMD_COPYROWS; 1117 idx = VRB_NEXT(idx); 1118 vd->rb_buffer[idx] = srcrow; 1119 idx = VRB_NEXT(idx); 1120 vd->rb_buffer[idx] = dstrow; 1121 idx = VRB_NEXT(idx); 1122 vd->rb_buffer[idx] = nrows; 1123 idx = VRB_NEXT(idx); 1124 membar_producer(); 1125 vd->rb_write = idx; 1126 membar_enter(); 1127 mutex_exit(&vd->drawing_mutex); 1128 cv_signal(&vd->go_draw); 1129 } 1130 1131 static void 1132 vcons_eraserows_async(void *cookie, int row, int nrows, long fillattr) 1133 { 1134 struct rasops_info *ri = cookie; 1135 struct vcons_screen *scr = ri->ri_hw; 1136 struct vcons_data *vd = scr->scr_vd; 1137 int idx; 1138 1139 vcons_wait_buffer(vd, 4); 1140 mutex_enter(&vd->drawing_mutex); 1141 mutex_exit(&vd->go_buffer_il); 1142 idx = vd->rb_write; 1143 vd->rb_buffer[idx] = VCMD_ERASEROWS; 1144 idx = VRB_NEXT(idx); 1145 vd->rb_buffer[idx] = row; 1146 idx = VRB_NEXT(idx); 1147 vd->rb_buffer[idx] = nrows; 1148 idx = VRB_NEXT(idx); 1149 vd->rb_buffer[idx] = (uint32_t)fillattr; 1150 idx = VRB_NEXT(idx); 1151 membar_producer(); 1152 vd->rb_write = idx; 1153 membar_enter(); 1154 mutex_exit(&vd->drawing_mutex); 1155 cv_signal(&vd->go_draw); 1156 } 1157 1158 static void 1159 vcons_putchar_async(void *cookie, int row, int col, u_int c, long attr) 1160 { 1161 struct rasops_info *ri = cookie; 1162 struct vcons_screen *scr = ri->ri_hw; 1163 struct vcons_data *vd = scr->scr_vd; 1164 int idx; 1165 1166 #ifdef VCONS_ASYNC_DEBUG 1167 /* mess with the background attribute so we can see if we draw async */ 1168 attr &= 0xff00ffff; 1169 attr |= (WSCOL_LIGHT_BROWN << 16); 1170 #endif 1171 1172 vcons_wait_buffer(vd, 5); 1173 mutex_enter(&vd->drawing_mutex); 1174 mutex_exit(&vd->go_buffer_il); 1175 idx = vd->rb_write; 1176 vd->rb_buffer[idx] = VCMD_PUTCHAR; 1177 idx = VRB_NEXT(idx); 1178 vd->rb_buffer[idx] = row; 1179 idx = VRB_NEXT(idx); 1180 vd->rb_buffer[idx] = col; 1181 idx = VRB_NEXT(idx); 1182 vd->rb_buffer[idx] = c; 1183 idx = VRB_NEXT(idx); 1184 vd->rb_buffer[idx] = (uint32_t)attr; 1185 idx = VRB_NEXT(idx); 1186 membar_producer(); 1187 vd->rb_write = idx; 1188 membar_enter(); 1189 mutex_exit(&vd->drawing_mutex); 1190 cv_signal(&vd->go_draw); 1191 } 1192 1193 static void 1194 vcons_cursor_async(void *cookie, int on, int row, int col) 1195 { 1196 struct rasops_info *ri = cookie; 1197 struct vcons_screen *scr = ri->ri_hw; 1198 struct vcons_data *vd = scr->scr_vd; 1199 int idx; 1200 1201 vcons_wait_buffer(vd, 4); 1202 mutex_enter(&vd->drawing_mutex); 1203 mutex_exit(&vd->go_buffer_il); 1204 idx = vd->rb_write; 1205 vd->rb_buffer[idx] = VCMD_CURSOR; 1206 idx = VRB_NEXT(idx); 1207 vd->rb_buffer[idx] = on; 1208 idx = VRB_NEXT(idx); 1209 vd->rb_buffer[idx] = row; 1210 idx = VRB_NEXT(idx); 1211 vd->rb_buffer[idx] = col; 1212 idx = VRB_NEXT(idx); 1213 membar_producer(); 1214 vd->rb_write = idx; 1215 membar_enter(); 1216 mutex_exit(&vd->drawing_mutex); 1217 cv_signal(&vd->go_draw); 1218 } 1219 1220 static int 1221 vcons_copy_params(struct vcons_data *vd, int len, uint32_t *buf) 1222 { 1223 int idx = vd->rb_read, i; 1224 1225 for (i = 0; i < len; i++) { 1226 buf[i] = vd->rb_buffer[idx]; 1227 idx = VRB_NEXT(idx); 1228 } 1229 return idx; 1230 } 1231 1232 1233 static void 1234 vcons_process_command(struct vcons_data *vd) 1235 { 1236 /* we take a command out of the buffer, run it and return */ 1237 void *cookie; 1238 int idx = vd->rb_read; 1239 uint32_t cmd = vd->rb_buffer[idx]; 1240 uint32_t params[10]; 1241 1242 KASSERT(vd->active != NULL); 1243 cookie = &vd->active->scr_ri; 1244 1245 switch (cmd) { 1246 case VCMD_COPYCOLS: 1247 idx = vcons_copy_params(vd, 5, params); 1248 vd->rb_read = idx; 1249 membar_producer(); 1250 vd->copycols(cookie, params[1], params[2], params[3], params[4]); 1251 break; 1252 case VCMD_ERASECOLS: 1253 idx = vcons_copy_params(vd, 5, params); 1254 vd->rb_read = idx; 1255 membar_producer(); 1256 vd->erasecols(cookie, params[1], params[2], params[3], params[4]); 1257 break; 1258 case VCMD_COPYROWS: 1259 idx = vcons_copy_params(vd, 4, params); 1260 vd->rb_read = idx; 1261 membar_producer(); 1262 vd->copyrows(cookie, params[1], params[2], params[3]); 1263 break; 1264 case VCMD_ERASEROWS: 1265 idx = vcons_copy_params(vd, 4, params); 1266 vd->rb_read = idx; 1267 membar_producer(); 1268 vd->eraserows(cookie, params[1], params[2], params[3]); 1269 break; 1270 case VCMD_PUTCHAR: 1271 idx = vcons_copy_params(vd, 5, params); 1272 vd->rb_read = idx; 1273 membar_producer(); 1274 vd->putchar(cookie, params[1], params[2], params[3], params[4]); 1275 break; 1276 case VCMD_CURSOR: 1277 idx = vcons_copy_params(vd, 4, params); 1278 vd->rb_read = idx; 1279 membar_producer(); 1280 vd->cursor(cookie, params[1], params[2], params[3]); 1281 break; 1282 default: 1283 /* 1284 * invalid command, something is wrong so we fall back 1285 * to synchronous operations 1286 */ 1287 vd->use_async = 0; 1288 vd->rb_read = 0; 1289 vd->rb_write = 0; 1290 } 1291 } 1292 1293 static void vcons_cursor(void *, int, int, int); 1294 1295 static void 1296 vcons_kthread(void *cookie) 1297 { 1298 struct vcons_data *vd = cookie; 1299 1300 /* initialize the synchronization goo */ 1301 cv_init(&vd->go_draw, "go_draw"); 1302 cv_init(&vd->go_buffer, "go_buffer"); 1303 mutex_init(&vd->drawing_mutex, MUTEX_DEFAULT, IPL_NONE); 1304 mutex_init(&vd->go_draw_il, MUTEX_DEFAULT, IPL_NONE); 1305 mutex_init(&vd->go_buffer_il, MUTEX_DEFAULT, IPL_NONE); 1306 vd->rb_read = 1000; 1307 vd->rb_write = 2; 1308 printf("%d\n", vcons_words_in_buffer(vd)); 1309 vd->rb_read = 0; 1310 vd->rb_write = 0; 1311 printf("%d\n", vcons_words_in_buffer(vd)); 1312 printf("%d %d\n", VRB_NEXT(1), VRB_NEXT(1023)); 1313 /* now we're good to go */ 1314 vd->use_async = 1; 1315 1316 while (1) { 1317 1318 while (vcons_words_in_buffer(vd) > 0) { 1319 vcons_process_command(vd); 1320 cv_signal(&vd->go_buffer); 1321 } 1322 /* 1323 * We don't really need the interlock here since there is no 1324 * need for serializing access to the buffer - we're the only 1325 * consumer. All we want is to sleep until someone gives us 1326 * something to so. 1327 */ 1328 mutex_enter(&vd->go_draw_il); 1329 cv_timedwait(&vd->go_draw, &vd->go_draw_il, hz); 1330 mutex_exit(&vd->go_draw_il); 1331 } 1332 } 1333 #endif /* VCONS_DRAW_ASYNC */ 1334