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