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