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