wsdisplay_vcons.c revision 1.56 1 /* $NetBSD: wsdisplay_vcons.c,v 1.56 2022/07/17 10:28:09 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.56 2022/07/17 10:28:09 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 int offset = vcons_offset_to_zero(scr);
882
883 memmove(&scr->scr_attrs[offset + to], &scr->scr_attrs[offset + from],
884 ncols * sizeof(long));
885 memmove(&scr->scr_chars[offset + to], &scr->scr_chars[offset + from],
886 ncols * sizeof(uint32_t));
887
888 #ifdef VCONS_DRAW_INTR
889 atomic_inc_uint(&scr->scr_dirty);
890 #endif
891 }
892
893 static void
894 vcons_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
895 {
896 struct rasops_info *ri = cookie;
897 struct vcons_screen *scr = ri->ri_hw;
898
899 vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols);
900
901 #if defined(VCONS_DRAW_INTR)
902 if (scr->scr_vd->use_intr)
903 return;
904 #endif
905
906 vcons_lock(scr);
907 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
908 #if defined(VCONS_DRAW_INTR)
909 vcons_update_screen(scr);
910 #else
911 scr->scr_vd->copycols(cookie, row, srccol, dstcol, ncols);
912 #endif
913 }
914 vcons_unlock(scr);
915 }
916
917 static void
918 vcons_copycols_noread(void *cookie, int row, int srccol, int dstcol, int ncols)
919 {
920 struct rasops_info *ri = cookie;
921 struct vcons_screen *scr = ri->ri_hw;
922 #ifdef VCONS_DRAW_INTR
923 struct vcons_data *vd = scr->scr_vd;
924 #endif
925
926 vcons_lock(scr);
927 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
928 int pos, c, offset, ppos;
929
930 #ifdef WSDISPLAY_SCROLLSUPPORT
931 offset = scr->scr_current_offset;
932 #else
933 offset = 0;
934 #endif
935 ppos = ri->ri_cols * row + dstcol;
936 pos = ppos + offset;
937 for (c = dstcol; c < (dstcol + ncols); c++) {
938 #ifdef VCONS_DRAW_INTR
939 if ((scr->scr_chars[pos] != vd->chars[ppos]) ||
940 (scr->scr_attrs[pos] != vd->attrs[ppos])) {
941 scr->putchar(cookie, row, c,
942 scr->scr_chars[pos], scr->scr_attrs[pos]);
943 vd->chars[ppos] = scr->scr_chars[pos];
944 vd->attrs[ppos] = scr->scr_attrs[pos];
945 }
946 #else
947 scr->putchar(cookie, row, c, scr->scr_chars[pos],
948 scr->scr_attrs[pos]);
949 #endif
950 pos++;
951 ppos++;
952 }
953 if (ri->ri_crow == row &&
954 (ri->ri_ccol >= dstcol && ri->ri_ccol < (dstcol + ncols )))
955 ri->ri_flg &= ~RI_CURSOR;
956 }
957 vcons_unlock(scr);
958 }
959
960 static void
961 vcons_erasecols_buffer(void *cookie, int row, int startcol, int ncols, long fillattr)
962 {
963 struct rasops_info *ri = cookie;
964 struct vcons_screen *scr = ri->ri_hw;
965 int start = startcol + row * ri->ri_cols;
966 int end = start + ncols, i;
967 int offset = vcons_offset_to_zero(scr);
968
969 for (i = start; i < end; i++) {
970 scr->scr_attrs[offset + i] = fillattr;
971 scr->scr_chars[offset + i] = 0x20;
972 }
973
974 #ifdef VCONS_DRAW_INTR
975 atomic_inc_uint(&scr->scr_dirty);
976 #endif
977 }
978
979 #ifdef VCONS_DRAW_INTR
980 static void
981 vcons_erasecols_cached(void *cookie, int row, int startcol, int ncols, long fillattr)
982 {
983 struct rasops_info *ri = cookie;
984 struct vcons_screen *scr = ri->ri_hw;
985 struct vcons_data *vd = scr->scr_vd;
986 int i, pos = row * ri->ri_cols + startcol;
987
988 vd->erasecols(cookie, row, startcol, ncols, fillattr);
989 for (i = pos; i < ncols; i++) {
990 vd->chars[i] = scr->scr_chars[i];
991 vd->attrs[i] = scr->scr_attrs[i];
992 }
993 }
994 #endif
995
996 static void
997 vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
998 {
999 struct rasops_info *ri = cookie;
1000 struct vcons_screen *scr = ri->ri_hw;
1001
1002 vcons_erasecols_buffer(cookie, row, startcol, ncols, fillattr);
1003
1004 #if defined(VCONS_DRAW_INTR)
1005 if (scr->scr_vd->use_intr)
1006 return;
1007 #endif
1008
1009 vcons_lock(scr);
1010 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1011 #ifdef VCONS_DRAW_INTR
1012 vcons_erasecols_cached(cookie, row, startcol, ncols,
1013 fillattr);
1014 #else
1015 scr->scr_vd->erasecols(cookie, row, startcol, ncols, fillattr);
1016 #endif
1017 }
1018 vcons_unlock(scr);
1019 }
1020
1021 static void
1022 vcons_copyrows_buffer(void *cookie, int srcrow, int dstrow, int nrows)
1023 {
1024 struct rasops_info *ri = cookie;
1025 struct vcons_screen *scr = ri->ri_hw;
1026 int from, to, len;
1027 int offset = vcons_offset_to_zero(scr);
1028
1029 /* do we need to scroll the back buffer? */
1030 if (dstrow == 0 && offset != 0) {
1031 from = ri->ri_cols * srcrow;
1032 to = ri->ri_cols * dstrow;
1033
1034 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
1035 offset * sizeof(long));
1036 memmove(&scr->scr_chars[to], &scr->scr_chars[from],
1037 offset * sizeof(uint32_t));
1038 }
1039 from = ri->ri_cols * srcrow + offset;
1040 to = ri->ri_cols * dstrow + offset;
1041 len = ri->ri_cols * nrows;
1042
1043 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
1044 len * sizeof(long));
1045 memmove(&scr->scr_chars[to], &scr->scr_chars[from],
1046 len * sizeof(uint32_t));
1047
1048 #ifdef VCONS_DRAW_INTR
1049 atomic_inc_uint(&scr->scr_dirty);
1050 #endif
1051 }
1052
1053 static void
1054 vcons_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
1055 {
1056 struct rasops_info *ri = cookie;
1057 struct vcons_screen *scr = ri->ri_hw;
1058
1059 vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows);
1060
1061 #if defined(VCONS_DRAW_INTR)
1062 if (scr->scr_vd->use_intr)
1063 return;
1064 #endif
1065
1066 vcons_lock(scr);
1067 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1068 #if defined(VCONS_DRAW_INTR)
1069 vcons_update_screen(scr);
1070 #else
1071 scr->scr_vd->copyrows(cookie, srcrow, dstrow, nrows);
1072 #endif
1073 }
1074 vcons_unlock(scr);
1075 }
1076
1077 static void
1078 vcons_copyrows_noread(void *cookie, int srcrow, int dstrow, int nrows)
1079 {
1080 struct rasops_info *ri = cookie;
1081 struct vcons_screen *scr = ri->ri_hw;
1082 #ifdef VCONS_DRAW_INTR
1083 struct vcons_data *vd = scr->scr_vd;
1084 #endif
1085 vcons_lock(scr);
1086 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1087 int pos, l, c, offset, ppos;
1088
1089 #ifdef WSDISPLAY_SCROLLSUPPORT
1090 offset = scr->scr_current_offset;
1091 #else
1092 offset = 0;
1093 #endif
1094 ppos = ri->ri_cols * dstrow;
1095 pos = ppos + offset;
1096 for (l = dstrow; l < (dstrow + nrows); l++) {
1097 for (c = 0; c < ri->ri_cols; c++) {
1098 #ifdef VCONS_DRAW_INTR
1099 if ((scr->scr_chars[pos] != vd->chars[ppos]) ||
1100 (scr->scr_attrs[pos] != vd->attrs[ppos])) {
1101 scr->putchar(cookie, l, c,
1102 scr->scr_chars[pos], scr->scr_attrs[pos]);
1103 vd->chars[ppos] = scr->scr_chars[pos];
1104 vd->attrs[ppos] = scr->scr_attrs[pos];
1105 }
1106 #else
1107 scr->putchar(cookie, l, c, scr->scr_chars[pos],
1108 scr->scr_attrs[pos]);
1109 #endif
1110 pos++;
1111 ppos++;
1112 }
1113 }
1114 if (ri->ri_crow >= dstrow && ri->ri_crow < (dstrow + nrows))
1115 ri->ri_flg &= ~RI_CURSOR;
1116 }
1117 vcons_unlock(scr);
1118 }
1119
1120 static void
1121 vcons_eraserows_buffer(void *cookie, int row, int nrows, long fillattr)
1122 {
1123 struct rasops_info *ri = cookie;
1124 struct vcons_screen *scr = ri->ri_hw;
1125 int offset = vcons_offset_to_zero(scr);
1126 int start, end, i;
1127
1128 start = ri->ri_cols * row + offset;
1129 end = ri->ri_cols * (row + nrows) + offset;
1130
1131 for (i = start; i < end; i++) {
1132 scr->scr_attrs[i] = fillattr;
1133 scr->scr_chars[i] = 0x20;
1134 }
1135
1136 #ifdef VCONS_DRAW_INTR
1137 atomic_inc_uint(&scr->scr_dirty);
1138 #endif
1139 }
1140
1141 #ifdef VCONS_DRAW_INTR
1142 static void
1143 vcons_eraserows_cached(void *cookie, int row, int nrows, long fillattr)
1144 {
1145 struct rasops_info *ri = cookie;
1146 struct vcons_screen *scr = ri->ri_hw;
1147 struct vcons_data *vd = scr->scr_vd;
1148 int i, pos = row * ri->ri_cols, end = (row+nrows) * ri->ri_cols;
1149
1150 for (i = pos; i < end; i++) {
1151 vd->chars[i] = 0x20;
1152 vd->attrs[i] = fillattr;
1153 }
1154 vd->eraserows(cookie, row, nrows, fillattr);
1155 }
1156 #endif
1157
1158 static void
1159 vcons_eraserows(void *cookie, int row, int nrows, long fillattr)
1160 {
1161 struct rasops_info *ri = cookie;
1162 struct vcons_screen *scr = ri->ri_hw;
1163
1164 vcons_eraserows_buffer(cookie, row, nrows, fillattr);
1165
1166 #if defined(VCONS_DRAW_INTR)
1167 if (scr->scr_vd->use_intr)
1168 return;
1169 #endif
1170
1171 vcons_lock(scr);
1172 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1173 #ifdef VCONS_DRAW_INTR
1174 vcons_eraserows_cached(cookie, row, nrows, fillattr);
1175 #else
1176 scr->scr_vd->eraserows(cookie, row, nrows, fillattr);
1177 #endif
1178 }
1179 vcons_unlock(scr);
1180 }
1181
1182 static void
1183 vcons_putchar_buffer(void *cookie, int row, int col, u_int c, long attr)
1184 {
1185 struct rasops_info *ri = cookie;
1186 struct vcons_screen *scr = ri->ri_hw;
1187 int offset = vcons_offset_to_zero(scr);
1188 int pos;
1189
1190 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) &&
1191 (col < ri->ri_cols)) {
1192 pos = col + row * ri->ri_cols;
1193 scr->scr_attrs[pos + offset] = attr;
1194 scr->scr_chars[pos + offset] = c;
1195 }
1196
1197 #ifdef VCONS_DRAW_INTR
1198 atomic_inc_uint(&scr->scr_dirty);
1199 #endif
1200 }
1201
1202 #ifdef VCONS_DRAW_INTR
1203 static void
1204 vcons_putchar_cached(void *cookie, int row, int col, u_int c, long attr)
1205 {
1206 struct rasops_info *ri = cookie;
1207 struct vcons_screen *scr = ri->ri_hw;
1208 struct vcons_data *vd = scr->scr_vd;
1209 int pos = row * ri->ri_cols + col;
1210
1211 if ((vd->chars == NULL) || (vd->attrs == NULL)) {
1212 scr->putchar(cookie, row, col, c, attr);
1213 return;
1214 }
1215 if ((vd->chars[pos] != c) || (vd->attrs[pos] != attr)) {
1216 vd->attrs[pos] = attr;
1217 vd->chars[pos] = c;
1218 scr->putchar(cookie, row, col, c, attr);
1219 }
1220 }
1221 #endif
1222
1223 static void
1224 vcons_putchar(void *cookie, int row, int col, u_int c, long attr)
1225 {
1226 struct rasops_info *ri = cookie;
1227 struct vcons_screen *scr = ri->ri_hw;
1228
1229 vcons_putchar_buffer(cookie, row, col, c, attr);
1230
1231 #if defined(VCONS_DRAW_INTR)
1232 if (scr->scr_vd->use_intr)
1233 return;
1234 #endif
1235
1236 vcons_lock(scr);
1237 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1238 #ifdef VCONS_DRAW_INTR
1239 vcons_putchar_cached(cookie, row, col, c, attr);
1240 #else
1241 if (row == ri->ri_crow && col == ri->ri_ccol) {
1242 ri->ri_flg &= ~RI_CURSOR;
1243 }
1244 scr->putchar(cookie, row, col, c, attr);
1245 #endif
1246 }
1247 vcons_unlock(scr);
1248 }
1249
1250 static void
1251 vcons_cursor(void *cookie, int on, int row, int col)
1252 {
1253 struct rasops_info *ri = cookie;
1254 struct vcons_screen *scr = ri->ri_hw;
1255
1256
1257 #if defined(VCONS_DRAW_INTR)
1258 if (scr->scr_vd->use_intr) {
1259 vcons_lock(scr);
1260 if (scr->scr_ri.ri_crow != row || scr->scr_ri.ri_ccol != col) {
1261 scr->scr_ri.ri_crow = row;
1262 scr->scr_ri.ri_ccol = col;
1263 atomic_inc_uint(&scr->scr_dirty);
1264 }
1265 vcons_unlock(scr);
1266 return;
1267 }
1268 #endif
1269
1270 vcons_lock(scr);
1271
1272 if (SCREEN_IS_VISIBLE(scr) && SCREEN_CAN_DRAW(scr)) {
1273 scr->scr_vd->cursor(cookie, on, row, col);
1274 } else {
1275 scr->scr_ri.ri_crow = row;
1276 scr->scr_ri.ri_ccol = col;
1277 }
1278 vcons_unlock(scr);
1279 }
1280
1281 static void
1282 vcons_cursor_noread(void *cookie, int on, int row, int col)
1283 {
1284 struct rasops_info *ri = cookie;
1285 struct vcons_screen *scr = ri->ri_hw;
1286 int offset = 0, ofs;
1287
1288 #ifdef WSDISPLAY_SCROLLSUPPORT
1289 offset = scr->scr_current_offset;
1290 #endif
1291 ofs = offset + ri->ri_crow * ri->ri_cols + ri->ri_ccol;
1292 if ((ri->ri_flg & RI_CURSOR) &&
1293 (((scr->scr_flags & VCONS_DONT_READ) != VCONS_DONT_READ) || on)) {
1294 scr->putchar(cookie, ri->ri_crow, ri->ri_ccol,
1295 scr->scr_chars[ofs], scr->scr_attrs[ofs]);
1296 ri->ri_flg &= ~RI_CURSOR;
1297 }
1298 ri->ri_crow = row;
1299 ri->ri_ccol = col;
1300 ofs = offset + ri->ri_crow * ri->ri_cols + ri->ri_ccol;
1301 if (on) {
1302 scr->putchar(cookie, row, col, scr->scr_chars[ofs],
1303 #ifdef VCONS_DEBUG_CURSOR_NOREAD
1304 /* draw a red cursor so we can tell which cursor()
1305 * implementation is being used */
1306 ((scr->scr_attrs[ofs] & 0xff00ffff) ^ 0x0f000000) |
1307 0x00010000);
1308 #else
1309 scr->scr_attrs[ofs] ^ 0x0f0f0000);
1310 #endif
1311 ri->ri_flg |= RI_CURSOR;
1312 }
1313 }
1314
1315 /* methods to read/write characters via ioctl() */
1316
1317 static int
1318 vcons_putwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc)
1319 {
1320 long attr;
1321 struct rasops_info *ri;
1322 int error;
1323
1324 KASSERT(scr != NULL && wsc != NULL);
1325
1326 ri = &scr->scr_ri;
1327
1328 /* allow col as linear index if row == 0 */
1329 if (wsc->row == 0) {
1330 if (wsc->col < 0 || wsc->col > (ri->ri_cols * ri->ri_rows))
1331 return EINVAL;
1332 int rem;
1333 rem = wsc->col % ri->ri_cols;
1334 wsc->row = wsc->col / ri->ri_cols;
1335 DPRINTF("off %d -> %d, %d\n", wsc->col, rem, wsc->row);
1336 wsc->col = rem;
1337 } else {
1338 if (__predict_false(wsc->col < 0 || wsc->col >= ri->ri_cols))
1339 return (EINVAL);
1340
1341 if (__predict_false(wsc->row < 0 || wsc->row >= ri->ri_rows))
1342 return (EINVAL);
1343 }
1344
1345 error = ri->ri_ops.allocattr(ri, wsc->foreground,
1346 wsc->background, wsc->flags, &attr);
1347 if (error)
1348 return error;
1349 vcons_putchar(ri, wsc->row, wsc->col, wsc->letter, attr);
1350 #ifdef VCONS_DEBUG
1351 printf("vcons_putwschar(%d, %d, %x, %lx\n", wsc->row, wsc->col,
1352 wsc->letter, attr);
1353 #endif
1354 return 0;
1355 }
1356
1357 static int
1358 vcons_getwschar(struct vcons_screen *scr, struct wsdisplay_char *wsc)
1359 {
1360 int offset;
1361 long attr;
1362 struct rasops_info *ri;
1363 int fg, bg, ul;
1364
1365 KASSERT(scr != NULL && wsc != NULL);
1366
1367 ri = &scr->scr_ri;
1368
1369 /* allow col as linear index if row == 0 */
1370 if (wsc->row == 0) {
1371 if (wsc->col < 0 || wsc->col > (ri->ri_cols * ri->ri_rows))
1372 return EINVAL;
1373 int rem;
1374 rem = wsc->col % ri->ri_cols;
1375 wsc->row = wsc->col / ri->ri_cols;
1376 DPRINTF("off %d -> %d, %d\n", wsc->col, rem, wsc->row);
1377 wsc->col = rem;
1378 } else {
1379 if (__predict_false(wsc->col < 0 || wsc->col >= ri->ri_cols))
1380 return (EINVAL);
1381
1382 if (__predict_false(wsc->row < 0 || wsc->row >= ri->ri_rows))
1383 return (EINVAL);
1384 }
1385
1386 offset = ri->ri_cols * wsc->row + wsc->col;
1387 offset += vcons_offset_to_zero(scr);
1388 wsc->letter = scr->scr_chars[offset];
1389 attr = scr->scr_attrs[offset];
1390
1391 #ifdef VCONS_DEBUG
1392 printf("vcons_getwschar: %d, %d, %x, %lx\n", wsc->row,
1393 wsc->col, wsc->letter, attr);
1394 #endif
1395
1396 /*
1397 * this is ugly. We need to break up an attribute into colours and
1398 * flags but there's no rasops method to do that so we must rely on
1399 * the 'canonical' encoding.
1400 */
1401
1402 /* only fetches underline attribute */
1403 /* rasops_unpack_attr(attr, &fg, &bg, &ul); */
1404 fg = (attr >> 24) & 0xf;
1405 bg = (attr >> 16) & 0xf;
1406 ul = (attr & 1);
1407
1408 wsc->foreground = fg;
1409 wsc->background = bg;
1410
1411 /* clear trashed bits and restore underline flag */
1412 attr &= ~(WSATTR_HILIT | WSATTR_BLINK | WSATTR_UNDERLINE);
1413 if (ul)
1414 attr |= WSATTR_UNDERLINE;
1415
1416 /* restore highlight boost */
1417 if (attr & WSATTR_HILIT)
1418 if (wsc->foreground >= 8)
1419 wsc->foreground -= 8;
1420
1421 /* we always use colors, even when not stored */
1422 attr |= WSATTR_WSCOLORS;
1423 return 0;
1424 }
1425
1426 #ifdef WSDISPLAY_SCROLLSUPPORT
1427
1428 static void
1429 vcons_scroll(void *cookie, void *vs, int where)
1430 {
1431 struct vcons_screen *scr = vs;
1432
1433 if (where == 0) {
1434 scr->scr_line_wanted = 0;
1435 } else {
1436 scr->scr_line_wanted = scr->scr_line_wanted - where;
1437 if (scr->scr_line_wanted < 0)
1438 scr->scr_line_wanted = 0;
1439 if (scr->scr_line_wanted > scr->scr_lines_in_buffer)
1440 scr->scr_line_wanted = scr->scr_lines_in_buffer;
1441 }
1442
1443 if (scr->scr_line_wanted != scr->scr_current_line) {
1444
1445 vcons_do_scroll(scr);
1446 }
1447 }
1448
1449 static void
1450 vcons_do_scroll(struct vcons_screen *scr)
1451 {
1452 int dist, from, to, num;
1453 int r_offset, r_start;
1454 int i, j;
1455
1456 if (scr->scr_line_wanted == scr->scr_current_line)
1457 return;
1458 dist = scr->scr_line_wanted - scr->scr_current_line;
1459 scr->scr_current_line = scr->scr_line_wanted;
1460 scr->scr_current_offset = scr->scr_ri.ri_cols *
1461 (scr->scr_lines_in_buffer - scr->scr_current_line);
1462 if (abs(dist) >= scr->scr_ri.ri_rows) {
1463 vcons_redraw_screen(scr);
1464 return;
1465 }
1466 /* scroll and redraw only what we really have to */
1467 if (dist > 0) {
1468 /* we scroll down */
1469 from = 0;
1470 to = dist;
1471 num = scr->scr_ri.ri_rows - dist;
1472 /* now the redraw parameters */
1473 r_offset = scr->scr_current_offset;
1474 r_start = 0;
1475 } else {
1476 /* scrolling up */
1477 to = 0;
1478 from = -dist;
1479 num = scr->scr_ri.ri_rows + dist;
1480 r_offset = scr->scr_current_offset + num * scr->scr_ri.ri_cols;
1481 r_start = num;
1482 }
1483 scr->scr_vd->copyrows(scr, from, to, num);
1484 for (i = 0; i < abs(dist); i++) {
1485 for (j = 0; j < scr->scr_ri.ri_cols; j++) {
1486 #ifdef VCONS_DRAW_INTR
1487 vcons_putchar_cached(scr, i + r_start, j,
1488 scr->scr_chars[r_offset],
1489 scr->scr_attrs[r_offset]);
1490 #else
1491 scr->putchar(scr, i + r_start, j,
1492 scr->scr_chars[r_offset],
1493 scr->scr_attrs[r_offset]);
1494 #endif
1495 r_offset++;
1496 }
1497 }
1498
1499 if (scr->scr_line_wanted == 0) {
1500 /* this was a reset - need to draw the cursor */
1501 scr->scr_ri.ri_flg &= ~RI_CURSOR;
1502 scr->scr_vd->cursor(scr, 1, scr->scr_ri.ri_crow,
1503 scr->scr_ri.ri_ccol);
1504 }
1505 }
1506
1507 #endif /* WSDISPLAY_SCROLLSUPPORT */
1508
1509 #ifdef VCONS_DRAW_INTR
1510 static void
1511 vcons_intr(void *cookie)
1512 {
1513 struct vcons_data *vd = cookie;
1514
1515 softint_schedule(vd->intr_softint);
1516 }
1517
1518 static void
1519 vcons_softintr(void *cookie)
1520 {
1521 struct vcons_data *vd = cookie;
1522 struct vcons_screen *scr = vd->active;
1523 unsigned int dirty;
1524
1525 if (scr && vd->use_intr) {
1526 if (!SCREEN_IS_BUSY(scr)) {
1527 dirty = atomic_swap_uint(&scr->scr_dirty, 0);
1528 if (vd->use_intr == 2) {
1529 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0) {
1530 vd->use_intr = 1;
1531 vcons_redraw_screen(scr);
1532 }
1533 } else if (dirty > 0) {
1534 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
1535 vcons_update_screen(scr);
1536 }
1537 }
1538 }
1539
1540 callout_schedule(&vd->intr, mstohz(33));
1541 }
1542
1543 static void
1544 vcons_init_thread(void *cookie)
1545 {
1546 struct vcons_data *vd = (struct vcons_data *)cookie;
1547
1548 vd->use_intr = 2;
1549 callout_schedule(&vd->intr, mstohz(33));
1550 kthread_exit(0);
1551 }
1552 #endif /* VCONS_DRAW_INTR */
1553
1554 void
1555 vcons_enable_polling(struct vcons_data *vd)
1556 {
1557 struct vcons_screen *scr = vd->active;
1558
1559 #ifdef VCONS_DRAW_INTR
1560 vd->use_intr = 0;
1561 #endif
1562
1563 if (scr && !SCREEN_IS_BUSY(scr)) {
1564 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
1565 vcons_redraw_screen(scr);
1566 }
1567 }
1568
1569 void
1570 vcons_disable_polling(struct vcons_data *vd)
1571 {
1572 #ifdef VCONS_DRAW_INTR
1573 struct vcons_screen *scr = vd->active;
1574
1575 if (!vd->intr_valid)
1576 return;
1577
1578 vd->use_intr = 2;
1579 if (scr)
1580 atomic_inc_uint(&scr->scr_dirty);
1581 #endif
1582 }
1583
1584 void
1585 vcons_hard_switch(struct vcons_screen *scr)
1586 {
1587 struct vcons_data *vd = scr->scr_vd;
1588 struct vcons_screen *oldscr = vd->active;
1589
1590 if (oldscr) {
1591 SCREEN_INVISIBLE(oldscr);
1592 oldscr->scr_ri.ri_flg &= ~RI_CURSOR;
1593 }
1594 SCREEN_VISIBLE(scr);
1595 vd->active = scr;
1596 vd->wanted = NULL;
1597
1598 if (vd->show_screen_cb != NULL)
1599 vd->show_screen_cb(scr, vd->show_screen_cookie);
1600 }
1601
1602 #ifdef VCONS_DRAW_INTR
1603 void
1604 vcons_invalidate_cache(struct vcons_data *vd)
1605 {
1606 int i;
1607
1608 for (i = 0; i < vd->cells; i++) {
1609 vd->chars[i] = -1;
1610 vd->attrs[i] = -1;
1611 }
1612 }
1613 #endif
1614