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