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