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