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