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