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