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