wsdisplay_vcons.c revision 1.3 1 /* $NetBSD: wsdisplay_vcons.c,v 1.3 2006/02/14 16:02:00 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 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the NetBSD
18 * Foundation, Inc. and its contributors.
19 * 4. Neither the name of The NetBSD Foundation nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: wsdisplay_vcons.c,v 1.3 2006/02/14 16:02:00 macallan Exp $");
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/buf.h>
43 #include <sys/device.h>
44 #include <sys/ioctl.h>
45 #include <sys/malloc.h>
46 #include <sys/mman.h>
47 #include <sys/tty.h>
48 #include <sys/conf.h>
49 #include <sys/proc.h>
50 #include <sys/kthread.h>
51 #include <sys/tprintf.h>
52
53 #include <dev/wscons/wsdisplayvar.h>
54 #include <dev/wscons/wsconsio.h>
55 #include <dev/wsfont/wsfont.h>
56 #include <dev/rasops/rasops.h>
57
58 #include <dev/wscons/wsdisplay_vconsvar.h>
59
60 static void vcons_dummy_init_screen(void *, struct vcons_screen *, int,
61 long *);
62
63 static int vcons_alloc_screen(void *, const struct wsscreen_descr *, void **,
64 int *, int *, long *);
65 static void vcons_free_screen(void *, void *);
66 static int vcons_show_screen(void *, void *, int, void (*)(void *, int, int),
67 void *);
68
69 static void vcons_do_switch(struct vcons_data *);
70
71 /* methods that work only on text buffers */
72 static void vcons_copycols_buffer(void *, int, int, int, int);
73 static void vcons_erasecols_buffer(void *, int, int, int, long);
74 static void vcons_copyrows_buffer(void *, int, int, int);
75 static void vcons_eraserows_buffer(void *, int, int, long);
76 static void vcons_putchar_buffer(void *, int, int, u_int, long);
77
78 /*
79 * actual wrapper methods which call both the _buffer ones above and the
80 * driver supplied ones to do the drawing
81 */
82 static void vcons_copycols(void *, int, int, int, int);
83 static void vcons_erasecols(void *, int, int, int, long);
84 static void vcons_copyrows(void *, int, int, int);
85 static void vcons_eraserows(void *, int, int, long);
86 static void vcons_putchar(void *, int, int, u_int, long);
87 static void vcons_cursor(void *, int, int, int);
88
89 /* support for readin/writing text buffers. For wsmoused */
90 static int vcons_putwschar(void *, struct wsdisplay_char *);
91 static int vcons_getwschar(void *, struct wsdisplay_char *);
92
93 static void vcons_lock(struct vcons_screen *);
94 static void vcons_unlock(struct vcons_screen *);
95
96
97 int
98 vcons_init(struct vcons_data *vd, void *cookie, struct wsscreen_descr *def,
99 struct wsdisplay_accessops *ao)
100 {
101
102 /* zero out everything so we can rely on untouched fields being 0 */
103 memset(vd, 0, sizeof(struct vcons_data));
104
105 vd->cookie = cookie;
106
107 vd->init_screen = vcons_dummy_init_screen;
108 vd->show_screen_cb = NULL;
109
110 ao->alloc_screen = vcons_alloc_screen;
111 ao->free_screen = vcons_free_screen;
112 ao->show_screen = vcons_show_screen;
113 ao->getwschar = vcons_getwschar;
114 ao->putwschar = vcons_putwschar;
115
116 LIST_INIT(&vd->screens);
117 vd->active = NULL;
118 vd->wanted = NULL;
119 vd->currenttype = def;
120 callout_init(&vd->switch_callout);
121
122 /*
123 * a lock to serialize access to the framebuffer.
124 * when switching screens we need to make sure there's no rasops
125 * operation in progress
126 */
127 #ifdef DIAGNOSTIC
128 vd->switch_poll_count = 0;
129 #endif
130 return 0;
131 }
132
133 static void
134 vcons_lock(struct vcons_screen *scr)
135 {
136 #ifdef VCONS_PARANOIA
137 int s;
138
139 s = splhigh();
140 #endif
141 SCREEN_BUSY(scr);
142 #ifdef VCONS_PARANOIA
143 splx(s);
144 #endif
145 }
146
147 static void
148 vcons_unlock(struct vcons_screen *scr)
149 {
150 #ifdef VCONS_PARANOIA
151 int s;
152
153 s = splhigh();
154 #endif
155 SCREEN_IDLE(scr);
156 #ifdef VCONS_PARANOIA
157 splx(s);
158 #endif
159 }
160
161 static void
162 vcons_dummy_init_screen(void *cookie, struct vcons_screen *scr, int exists,
163 long *defattr)
164 {
165
166 /*
167 * default init_screen() method.
168 * Needs to be overwritten so we bitch and whine in case anyone ends
169 * up in here.
170 */
171 printf("vcons_init_screen: dummy function called. Your driver is "
172 "supposed to supply a replacement for proper operation\n");
173 }
174
175 int
176 vcons_init_screen(struct vcons_data *vd, struct vcons_screen *scr,
177 int existing, long *defattr)
178 {
179 struct rasops_info *ri = &scr->scr_ri;
180 int cnt, i;
181
182 scr->scr_cookie = vd->cookie;
183 scr->scr_vd = vd;
184 SCREEN_IDLE(scr);
185
186 /*
187 * call the driver-supplied init_screen function which is expected
188 * to set up rasops_info, override cursor() and probably others
189 */
190 vd->init_screen(vd->cookie, scr, existing, defattr);
191
192 /*
193 * save the non virtual console aware rasops and replace them with
194 * our wrappers
195 */
196 vd->eraserows = ri->ri_ops.eraserows;
197 vd->copyrows = ri->ri_ops.copyrows;
198 vd->erasecols = ri->ri_ops.erasecols;
199 vd->copycols = ri->ri_ops.copycols;
200 vd->putchar = ri->ri_ops.putchar;
201 vd->cursor = ri->ri_ops.cursor;
202
203 ri->ri_ops.eraserows = vcons_eraserows;
204 ri->ri_ops.copyrows = vcons_copyrows;
205 ri->ri_ops.erasecols = vcons_erasecols;
206 ri->ri_ops.copycols = vcons_copycols;
207 ri->ri_ops.putchar = vcons_putchar;
208 ri->ri_ops.cursor = vcons_cursor;
209 ri->ri_hw = scr;
210
211 /*
212 * we allocate both chars and attributes in one chunk, attributes first
213 * because they have the (potentially) bigger alignment
214 */
215 cnt = ri->ri_rows * ri->ri_cols;
216 scr->scr_attrs = (long *)malloc(cnt * (sizeof(long) +
217 sizeof(uint16_t)), M_DEVBUF, M_WAITOK);
218 if (scr->scr_attrs == NULL)
219 return ENOMEM;
220
221 scr->scr_chars = (uint16_t *)&scr->scr_attrs[cnt];
222
223 ri->ri_ops.allocattr(ri, WS_DEFAULT_FG, WS_DEFAULT_BG, 0, defattr);
224 scr->scr_defattr = *defattr;
225
226 /*
227 * fill the attribute buffer with *defattr, chars with 0x20
228 * since we don't know if the driver tries to mimic firmware output or
229 * reset everything we do nothing to VRAM here, any driver that feels
230 * the need to clear screen or something will have to do it on its own
231 * Additional screens will start out in the background anyway so
232 * cleaning or not only really affects the initial console screen
233 */
234 for (i = 0; i < cnt; i++) {
235 scr->scr_attrs[i] = *defattr;
236 scr->scr_chars[i] = 0x20;
237 }
238
239 if(vd->active == NULL) {
240 vd->active = scr;
241 SCREEN_VISIBLE(scr);
242 }
243
244 if (existing) {
245 SCREEN_VISIBLE(scr);
246 vd->active = scr;
247 } else {
248 SCREEN_INVISIBLE(scr);
249 }
250
251 LIST_INSERT_HEAD(&vd->screens, scr, next);
252 return 0;
253 }
254
255 static void
256 vcons_do_switch(struct vcons_data *vd)
257 {
258 struct vcons_screen *scr, *oldscr;
259
260 scr = vd->wanted;
261 if (!scr) {
262 printf("vcons_switch_screen: disappeared\n");
263 vd->switch_cb(vd->switch_cb_arg, EIO, 0);
264 return;
265 }
266 oldscr = vd->active; /* can be NULL! */
267
268 /*
269 * if there's an old, visible screen we mark it invisible and wait
270 * until it's not busy so we can safely switch
271 */
272 if (oldscr != NULL) {
273 SCREEN_INVISIBLE(oldscr);
274 if (SCREEN_IS_BUSY(oldscr)) {
275 callout_reset(&vd->switch_callout, 1,
276 (void(*)(void *))vcons_do_switch, vd);
277 #ifdef DIAGNOSTIC
278 /* bitch if we wait too long */
279 vd->switch_poll_count++;
280 if (vd->switch_poll_count > 100) {
281 panic("vcons: screen still busy");
282 }
283 #endif
284 return;
285 }
286 /* invisible screen -> no visible cursor image */
287 oldscr->scr_ri.ri_flg &= ~RI_CURSOR;
288 #ifdef DIAGNOSTIC
289 vd->switch_poll_count = 0;
290 #endif
291 }
292
293 if (scr == oldscr)
294 return;
295
296 #ifdef DIAGNOSTIC
297 if (SCREEN_IS_VISIBLE(scr))
298 panic("vcons_switch_screen: already active");
299 #endif
300
301 #ifdef notyet
302 if (vd->currenttype != type) {
303 vcons_set_screentype(vd, type);
304 vd->currenttype = type;
305 }
306 #endif
307
308 SCREEN_VISIBLE(scr);
309 vd->active = scr;
310 vd->wanted = NULL;
311
312 if (vd->show_screen_cb != NULL)
313 vd->show_screen_cb(scr);
314
315 if ((scr->scr_flags & VCONS_NO_REDRAW) == 0)
316 vcons_redraw_screen(scr);
317
318 if (vd->switch_cb)
319 vd->switch_cb(vd->switch_cb_arg, 0, 0);
320 }
321
322 void
323 vcons_redraw_screen(struct vcons_screen *scr)
324 {
325 uint16_t *charptr = scr->scr_chars;
326 long *attrptr = scr->scr_attrs;
327 struct rasops_info *ri = &scr->scr_ri;
328 int i, j, offset;
329
330 vcons_lock(scr);
331 if (SCREEN_IS_VISIBLE(scr)) {
332
333 /*
334 * only clear the screen when RI_FULLCLEAR is set since we're
335 * going to overwrite every single character cell anyway
336 */
337 if (ri->ri_flg & RI_FULLCLEAR) {
338 scr->scr_vd->eraserows(ri, 0, ri->ri_rows,
339 scr->scr_defattr);
340 }
341
342 /* redraw the screen */
343 offset = 0;
344 for (i = 0; i < ri->ri_rows; i++) {
345 for (j = 0; j < ri->ri_cols; j++) {
346 /*
347 * no need to use the wrapper function - we
348 * don't change any characters or attributes
349 * and we already made sure the screen we're
350 * working on is visible
351 */
352 scr->scr_vd->putchar(ri, i, j,
353 charptr[offset], attrptr[offset]);
354 offset++;
355 }
356 }
357 ri->ri_flg &= ~RI_CURSOR;
358 scr->scr_vd->cursor(ri, 1, ri->ri_crow, ri->ri_ccol);
359 }
360 vcons_unlock(scr);
361 }
362
363 static int
364 vcons_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
365 int *curxp, int *curyp, long *defattrp)
366 {
367 struct vcons_data *vd = v;
368 struct vcons_screen *scr;
369 int ret;
370
371 scr = malloc(sizeof(struct vcons_screen), M_DEVBUF, M_WAITOK | M_ZERO);
372 if (scr == NULL)
373 return ENOMEM;
374
375 scr->scr_flags = 0;
376 scr->scr_status = 0;
377 scr->scr_busy = 0;
378 scr->scr_type = type;
379
380 ret = vcons_init_screen(vd, scr, 0, defattrp);
381 if (ret != 0) {
382 free(scr, M_DEVBUF);
383 return ret;
384 }
385
386 if (vd->active == NULL) {
387 SCREEN_VISIBLE(scr);
388 vd->active = scr;
389 vd->currenttype = type;
390 }
391
392 *cookiep = scr;
393 *curxp = scr->scr_ri.ri_ccol;
394 *curyp = scr->scr_ri.ri_crow;
395 return 0;
396 }
397
398 static void
399 vcons_free_screen(void *v, void *cookie)
400 {
401 struct vcons_data *vd = v;
402 struct vcons_screen *scr = cookie;
403
404 vcons_lock(scr);
405 /* there should be no rasops activity here */
406
407 LIST_REMOVE(scr, next);
408
409 if ((scr->scr_flags & VCONS_SCREEN_IS_STATIC) == 0) {
410 free(scr->scr_attrs, M_DEVBUF);
411 free(scr, M_DEVBUF);
412 } else {
413 /*
414 * maybe we should just restore the old rasops_info methods
415 * and free the character/attribute buffer here?
416 */
417 #ifdef VCONS_DEBUG
418 panic("vcons_free_screen: console");
419 #else
420 printf("vcons_free_screen: console\n");
421 #endif
422 }
423
424 if (vd->active == scr)
425 vd->active = NULL;
426 }
427
428 static int
429 vcons_show_screen(void *v, void *cookie, int waitok,
430 void (*cb)(void *, int, int), void *cb_arg)
431 {
432 struct vcons_data *vd = v;
433 struct vcons_screen *scr;
434
435 scr = cookie;
436 if (scr == vd->active)
437 return 0;
438
439 vd->wanted = scr;
440 vd->switch_cb = cb;
441 vd->switch_cb_arg = cb_arg;
442 if (cb) {
443 callout_reset(&vd->switch_callout, 0,
444 (void(*)(void *))vcons_do_switch, vd);
445 return EAGAIN;
446 }
447
448 vcons_do_switch(vd);
449 return 0;
450 }
451
452 /* wrappers for rasops_info methods */
453
454 static void
455 vcons_copycols_buffer(void *cookie, int row, int srccol, int dstcol, int ncols)
456 {
457 struct rasops_info *ri = cookie;
458 struct vcons_screen *scr = ri->ri_hw;
459 int from = srccol + row * ri->ri_cols;
460 int to = dstcol + row * ri->ri_cols;
461
462 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
463 ncols * sizeof(long));
464 memmove(&scr->scr_chars[to], &scr->scr_chars[from],
465 ncols * sizeof(uint16_t));
466 }
467
468 static void
469 vcons_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
470 {
471 struct rasops_info *ri = cookie;
472 struct vcons_screen *scr = ri->ri_hw;
473
474 vcons_copycols_buffer(cookie, row, srccol, dstcol, ncols);
475
476 vcons_lock(scr);
477 if (SCREEN_IS_VISIBLE(scr)) {
478 scr->scr_vd->copycols(cookie, row, srccol, dstcol, ncols);
479 }
480 vcons_unlock(scr);
481 }
482
483 static void
484 vcons_erasecols_buffer(void *cookie, int row, int startcol, int ncols, long fillattr)
485 {
486 struct rasops_info *ri = cookie;
487 struct vcons_screen *scr = ri->ri_hw;
488 int start = startcol + row * ri->ri_cols;
489 int end = start + ncols, i;
490
491 for (i = start; i < end; i++) {
492 scr->scr_attrs[i] = fillattr;
493 scr->scr_chars[i] = 0x20;
494 }
495 }
496
497 static void
498 vcons_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
499 {
500 struct rasops_info *ri = cookie;
501 struct vcons_screen *scr = ri->ri_hw;
502
503 vcons_erasecols_buffer(cookie, row, startcol, ncols, fillattr);
504
505 vcons_lock(scr);
506 if (SCREEN_IS_VISIBLE(scr)) {
507 scr->scr_vd->erasecols(cookie, row, startcol, ncols,
508 fillattr);
509 }
510 vcons_unlock(scr);
511 }
512
513 static void
514 vcons_copyrows_buffer(void *cookie, int srcrow, int dstrow, int nrows)
515 {
516 struct rasops_info *ri = cookie;
517 struct vcons_screen *scr = ri->ri_hw;
518 int from, to, len;
519
520 from = ri->ri_cols * srcrow;
521 to = ri->ri_cols * dstrow;
522 len = ri->ri_cols * nrows;
523
524 memmove(&scr->scr_attrs[to], &scr->scr_attrs[from],
525 len * sizeof(long));
526 memmove(&scr->scr_chars[to], &scr->scr_chars[from],
527 len * sizeof(uint16_t));
528 }
529
530 static void
531 vcons_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
532 {
533 struct rasops_info *ri = cookie;
534 struct vcons_screen *scr = ri->ri_hw;
535
536 vcons_copyrows_buffer(cookie, srcrow, dstrow, nrows);
537
538 vcons_lock(scr);
539 if (SCREEN_IS_VISIBLE(scr)) {
540 scr->scr_vd->copyrows(cookie, srcrow, dstrow, nrows);
541 }
542 vcons_unlock(scr);
543 }
544
545 static void
546 vcons_eraserows_buffer(void *cookie, int row, int nrows, long fillattr)
547 {
548 struct rasops_info *ri = cookie;
549 struct vcons_screen *scr = ri->ri_hw;
550 int start, end, i;
551
552 start = ri->ri_cols * row;
553 end = ri->ri_cols * (row + nrows);
554
555 for (i = start; i < end; i++) {
556 scr->scr_attrs[i] = fillattr;
557 scr->scr_chars[i] = 0x20;
558 }
559 }
560
561 static void
562 vcons_eraserows(void *cookie, int row, int nrows, long fillattr)
563 {
564 struct rasops_info *ri = cookie;
565 struct vcons_screen *scr = ri->ri_hw;
566
567 vcons_eraserows_buffer(cookie, row, nrows, fillattr);
568
569 vcons_lock(scr);
570 if (SCREEN_IS_VISIBLE(scr)) {
571 scr->scr_vd->eraserows(cookie, row, nrows, fillattr);
572 }
573 vcons_unlock(scr);
574 }
575
576 static void
577 vcons_putchar_buffer(void *cookie, int row, int col, u_int c, long attr)
578 {
579 struct rasops_info *ri = cookie;
580 struct vcons_screen *scr = ri->ri_hw;
581 int pos;
582
583 if ((row >= 0) && (row < ri->ri_rows) && (col >= 0) &&
584 (col < ri->ri_cols)) {
585 pos = col + row * ri->ri_cols;
586 scr->scr_attrs[pos] = attr;
587 scr->scr_chars[pos] = c;
588 }
589 }
590
591 static void
592 vcons_putchar(void *cookie, int row, int col, u_int c, long attr)
593 {
594 struct rasops_info *ri = cookie;
595 struct vcons_screen *scr = ri->ri_hw;
596
597 vcons_putchar_buffer(cookie, row, col, c, attr);
598
599 vcons_lock(scr);
600 if (SCREEN_IS_VISIBLE(scr)) {
601 scr->scr_vd->putchar(cookie, row, col, c, attr);
602 }
603 vcons_unlock(scr);
604 }
605
606 static void
607 vcons_cursor(void *cookie, int on, int row, int col)
608 {
609 struct rasops_info *ri = cookie;
610 struct vcons_screen *scr = ri->ri_hw;
611
612 vcons_lock(scr);
613 if (SCREEN_IS_VISIBLE(scr)) {
614 scr->scr_vd->cursor(cookie, on, row, col);
615 } else {
616 scr->scr_ri.ri_crow = row;
617 scr->scr_ri.ri_ccol = col;
618 }
619 vcons_unlock(scr);
620 }
621
622 /* methods to read/write characters via ioctl() */
623
624 static int
625 vcons_putwschar(void *cookie, struct wsdisplay_char *wsc)
626 {
627 struct rasops_info *ri = cookie;
628 long attr;
629
630 ri->ri_ops.allocattr(ri, wsc->foreground, wsc->background,
631 wsc->flags, &attr);
632 vcons_putchar(ri, wsc->row, wsc->col, wsc->letter, attr);
633 return 0;
634 }
635
636 static int
637 vcons_getwschar(void *cookie, struct wsdisplay_char *wsc)
638 {
639 struct rasops_info *ri = cookie;
640 struct vcons_screen *scr = ri->ri_hw;
641 long attr;
642 int offset = ri->ri_cols * wsc->row + wsc->col;
643
644 wsc->letter = scr->scr_chars[offset];
645 attr = scr->scr_attrs[offset];
646
647 /*
648 * this is ugly. We need to break up an attribute into colours and
649 * flags but there's no rasops method to do that so we must rely on
650 * the 'canonical' encoding.
651 */
652 wsc->foreground = (attr & 0xff000000) >> 24;
653 wsc->background = (attr & 0x00ff0000) >> 16;
654 wsc->flags = (attr & 0x0000ff00) >> 8;
655 return 0;
656 }
657