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