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