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