File: /usr/src/linux/drivers/video/promcon.c

1     /* $Id: promcon.c,v 1.17 2000/07/26 23:02:52 davem Exp $
2      * Console driver utilizing PROM sun terminal emulation
3      *
4      * Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
5      * Copyright (C) 1998  Jakub Jelinek  (jj@ultra.linux.cz)
6      */
7     
8     #include <linux/config.h>
9     #include <linux/module.h>
10     #include <linux/kernel.h>
11     #include <linux/errno.h>
12     #include <linux/string.h>
13     #include <linux/mm.h>
14     #include <linux/tty.h>
15     #include <linux/slab.h>
16     #include <linux/delay.h>
17     #include <linux/console.h>
18     #include <linux/console_struct.h>
19     #include <linux/vt_kern.h>
20     #include <linux/selection.h>
21     #include <linux/fb.h>
22     #include <linux/init.h>
23     #include <linux/kd.h>
24     
25     #include <asm/oplib.h>
26     #include <asm/uaccess.h>
27     
28     static short pw = 80 - 1, ph = 34 - 1;
29     static short px, py;
30     static unsigned long promcon_uni_pagedir[2];
31     
32     extern u8 promfont_unicount[];
33     extern u16 promfont_unitable[];
34     
35     #define PROMCON_COLOR 0
36     
37     #if PROMCON_COLOR
38     #define inverted(s)	((((s) & 0x7700) == 0x0700) ? 0 : 1)
39     #else
40     #define inverted(s)	(((s) & 0x0800) ? 1 : 0)
41     #endif
42     
43     static __inline__ void
44     promcon_puts(char *buf, int cnt)
45     {
46     	prom_printf("%*.*s", cnt, cnt, buf);
47     }
48     
49     static int
50     promcon_start(struct vc_data *conp, char *b)
51     {
52     	unsigned short *s = (unsigned short *)
53     			(conp->vc_origin + py * conp->vc_size_row + (px << 1));
54     
55     	if (px == pw) {
56     		unsigned short *t = s - 1;
57     
58     		if (inverted(*s) && inverted(*t))
59     			return sprintf(b, "\b\033[7m%c\b\033[@%c\033[m",
60     				       *s, *t);
61     		else if (inverted(*s))
62     			return sprintf(b, "\b\033[7m%c\033[m\b\033[@%c",
63     				       *s, *t);
64     		else if (inverted(*t))
65     			return sprintf(b, "\b%c\b\033[@\033[7m%c\033[m",
66     				       *s, *t);
67     		else
68     			return sprintf(b, "\b%c\b\033[@%c", *s, *t);
69     	}
70     
71     	if (inverted(*s))
72     		return sprintf(b, "\033[7m%c\033[m\b", *s);
73     	else
74     		return sprintf(b, "%c\b", *s);
75     }
76     
77     static int
78     promcon_end(struct vc_data *conp, char *b)
79     {
80     	unsigned short *s = (unsigned short *)
81     			(conp->vc_origin + py * conp->vc_size_row + (px << 1));
82     	char *p = b;
83     
84     	b += sprintf(b, "\033[%d;%dH", py + 1, px + 1);
85     
86     	if (px == pw) {
87     		unsigned short *t = s - 1;
88     
89     		if (inverted(*s) && inverted(*t))
90     			b += sprintf(b, "\b%c\b\033[@\033[7m%c\033[m", *s, *t);
91     		else if (inverted(*s))
92     			b += sprintf(b, "\b%c\b\033[@%c", *s, *t);
93     		else if (inverted(*t))
94     			b += sprintf(b, "\b\033[7m%c\b\033[@%c\033[m", *s, *t);
95     		else
96     			b += sprintf(b, "\b\033[7m%c\033[m\b\033[@%c", *s, *t);
97     		return b - p;
98     	}
99     
100     	if (inverted(*s))
101     		b += sprintf(b, "%c\b", *s);
102     	else
103     		b += sprintf(b, "\033[7m%c\033[m\b", *s);
104     	return b - p;
105     }
106     
107     const char __init *promcon_startup(void)
108     {
109     	const char *display_desc = "PROM";
110     	int node;
111     	char buf[40];
112     	
113     	node = prom_getchild(prom_root_node);
114     	node = prom_searchsiblings(node, "options");
115     	if (prom_getproperty(node,  "screen-#columns", buf, 40) != -1) {
116     		pw = simple_strtoul(buf, NULL, 0);
117     		if (pw < 10 || pw > 256)
118     			pw = 80;
119     		pw--;
120     	}
121     	if (prom_getproperty(node,  "screen-#rows", buf, 40) != -1) {
122     		ph = simple_strtoul(buf, NULL, 0);
123     		if (ph < 10 || ph > 256)
124     			ph = 34;
125     		ph--;
126     	}
127     	promcon_puts("\033[H\033[J", 6);
128     	return display_desc;
129     }
130     
131     static void __init 
132     promcon_init_unimap(struct vc_data *conp)
133     {
134     	mm_segment_t old_fs = get_fs();
135     	struct unipair *p, *p1;
136     	u16 *q;
137     	int i, j, k;
138     	
139     	p = kmalloc(256*sizeof(struct unipair), GFP_KERNEL);
140     	if (!p) return;
141     	
142     	q = promfont_unitable;
143     	p1 = p;
144     	k = 0;
145     	for (i = 0; i < 256; i++)
146     		for (j = promfont_unicount[i]; j; j--) {
147     			p1->unicode = *q++;
148     			p1->fontpos = i;
149     			p1++;
150     			k++;
151     		}
152     	set_fs(KERNEL_DS);
153     	con_clear_unimap(conp->vc_num, NULL);
154     	con_set_unimap(conp->vc_num, k, p);
155     	con_protect_unimap(conp->vc_num, 1);
156     	set_fs(old_fs);
157     	kfree(p);
158     }
159     
160     static void
161     promcon_init(struct vc_data *conp, int init)
162     {
163     	unsigned long p;
164     	
165     	conp->vc_can_do_color = PROMCON_COLOR;
166     	if (init) {
167     		conp->vc_cols = pw + 1;
168     		conp->vc_rows = ph + 1;
169     	}
170     	p = *conp->vc_uni_pagedir_loc;
171     	if (conp->vc_uni_pagedir_loc == &conp->vc_uni_pagedir ||
172     	    !--conp->vc_uni_pagedir_loc[1])
173     		con_free_unimap(conp->vc_num);
174     	conp->vc_uni_pagedir_loc = promcon_uni_pagedir;
175     	promcon_uni_pagedir[1]++;
176     	if (!promcon_uni_pagedir[0] && p) {
177     		promcon_init_unimap(conp);
178     	}
179     	if (!init) {
180     		if (conp->vc_cols != pw + 1 || conp->vc_rows != ph + 1)
181     			vc_resize_con(ph + 1, pw + 1, conp->vc_num);
182     	}
183     }
184     
185     static void
186     promcon_deinit(struct vc_data *conp)
187     {
188     	/* When closing the last console, reset video origin */
189     	if (!--promcon_uni_pagedir[1])
190     		con_free_unimap(conp->vc_num);
191     	conp->vc_uni_pagedir_loc = &conp->vc_uni_pagedir;
192     	con_set_default_unimap(conp->vc_num);
193     }
194     
195     static int
196     promcon_switch(struct vc_data *conp)
197     {
198     	return 1;
199     }
200     
201     static unsigned short *
202     promcon_repaint_line(unsigned short *s, unsigned char *buf, unsigned char **bp)
203     {
204     	int cnt = pw + 1;
205     	int attr = -1;
206     	unsigned char *b = *bp;
207     
208     	while (cnt--) {
209     		if (attr != inverted(*s)) {
210     			attr = inverted(*s);
211     			if (attr) {
212     				strcpy (b, "\033[7m");
213     				b += 4;
214     			} else {
215     				strcpy (b, "\033[m");
216     				b += 3;
217     			}
218     		}
219     		*b++ = *s++;
220     		if (b - buf >= 224) {
221     			promcon_puts(buf, b - buf);
222     			b = buf;
223     		}
224     	}
225     	*bp = b;
226     	return s;
227     }
228     
229     static void
230     promcon_putcs(struct vc_data *conp, const unsigned short *s,
231     	      int count, int y, int x)
232     {
233     	unsigned char buf[256], *b = buf;
234     	unsigned short attr = scr_readw(s);
235     	unsigned char save;
236     	int i, last = 0;
237     
238     	if (console_blanked)
239     		return;
240     	
241     	if (count <= 0)
242     		return;
243     
244     	b += promcon_start(conp, b);
245     
246     	if (x + count >= pw + 1) {
247     		if (count == 1) {
248     			x -= 1;
249     			save = *(unsigned short *)(conp->vc_origin
250     						   + y * conp->vc_size_row
251     						   + (x << 1));
252     
253     			if (px != x || py != y) {
254     				b += sprintf(b, "\033[%d;%dH", y + 1, x + 1);
255     				px = x;
256     				py = y;
257     			}
258     
259     			if (inverted(attr))
260     				b += sprintf(b, "\033[7m%c\033[m", scr_readw(s++));
261     			else
262     				b += sprintf(b, "%c", scr_readw(s++));
263     
264     			strcpy(b, "\b\033[@");
265     			b += 4;
266     
267     			if (inverted(save))
268     				b += sprintf(b, "\033[7m%c\033[m", save);
269     			else
270     				b += sprintf(b, "%c", save);
271     
272     			px++;
273     
274     			b += promcon_end(conp, b);
275     			promcon_puts(buf, b - buf);
276     			return;
277     		} else {
278     			last = 1;
279     			count = pw - x - 1;
280     		}
281     	}
282     
283     	if (inverted(attr)) {
284     		strcpy(b, "\033[7m");
285     		b += 4;
286     	}
287     
288     	if (px != x || py != y) {
289     		b += sprintf(b, "\033[%d;%dH", y + 1, x + 1);
290     		px = x;
291     		py = y;
292     	}
293     
294     	for (i = 0; i < count; i++) {
295     		if (b - buf >= 224) {
296     			promcon_puts(buf, b - buf);
297     			b = buf;
298     		}
299     		*b++ = scr_readw(s++);
300     	}
301     
302     	px += count;
303     
304     	if (last) {
305     		save = scr_readw(s++);
306     		b += sprintf(b, "%c\b\033[@%c", scr_readw(s++), save);
307     		px++;
308     	}
309     
310     	if (inverted(attr)) {
311     		strcpy(b, "\033[m");
312     		b += 3;
313     	}
314     
315     	b += promcon_end(conp, b);
316     	promcon_puts(buf, b - buf);
317     }
318     
319     static void
320     promcon_putc(struct vc_data *conp, int c, int y, int x)
321     {
322     	unsigned short s;
323     
324     	if (console_blanked)
325     		return;
326     	
327     	scr_writew(c, &s);
328     	promcon_putcs(conp, &s, 1, y, x);
329     }
330     
331     static void
332     promcon_clear(struct vc_data *conp, int sy, int sx, int height, int width)
333     {
334     	unsigned char buf[256], *b = buf;
335     	int i, j;
336     
337     	if (console_blanked)
338     		return;
339     	
340     	b += promcon_start(conp, b);
341     
342     	if (!sx && width == pw + 1) {
343     
344     		if (!sy && height == ph + 1) {
345     			strcpy(b, "\033[H\033[J");
346     			b += 6;
347     			b += promcon_end(conp, b);
348     			promcon_puts(buf, b - buf);
349     			return;
350     		} else if (sy + height == ph + 1) {
351     			b += sprintf(b, "\033[%dH\033[J", sy + 1);
352     			b += promcon_end(conp, b);
353     			promcon_puts(buf, b - buf);
354     			return;
355     		}
356     
357     		b += sprintf(b, "\033[%dH", sy + 1);
358     		for (i = 1; i < height; i++) {
359     			strcpy(b, "\033[K\n");
360     			b += 4;
361     		}
362     
363     		strcpy(b, "\033[K");
364     		b += 3;
365     
366     		b += promcon_end(conp, b);
367     		promcon_puts(buf, b - buf);
368     		return;
369     
370     	} else if (sx + width == pw + 1) {
371     
372     		b += sprintf(b, "\033[%d;%dH", sy + 1, sx + 1);
373     		for (i = 1; i < height; i++) {
374     			strcpy(b, "\033[K\n");
375     			b += 4;
376     		}
377     
378     		strcpy(b, "\033[K");
379     		b += 3;
380     
381     		b += promcon_end(conp, b);
382     		promcon_puts(buf, b - buf);
383     		return;
384     	}
385     
386     	for (i = sy + 1; i <= sy + height; i++) {
387     		b += sprintf(b, "\033[%d;%dH", i, sx + 1);
388     		for (j = 0; j < width; j++)
389     			*b++ = ' ';
390     		if (b - buf + width >= 224) {
391     			promcon_puts(buf, b - buf);
392     			b = buf;
393     		}
394     	}
395     
396     	b += promcon_end(conp, b);
397     	promcon_puts(buf, b - buf);
398     }
399                             
400     static void
401     promcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx,
402     	      int height, int width)
403     {
404     	char buf[256], *b = buf;
405     
406     	if (console_blanked)
407     		return;
408     	
409     	b += promcon_start(conp, b);
410     	if (sy == dy && height == 1) {
411     		if (dx > sx && dx + width == conp->vc_cols)
412     			b += sprintf(b, "\033[%d;%dH\033[%d@\033[%d;%dH",
413     				     sy + 1, sx + 1, dx - sx, py + 1, px + 1);
414     		else if (dx < sx && sx + width == conp->vc_cols)
415     			b += sprintf(b, "\033[%d;%dH\033[%dP\033[%d;%dH",
416     				     dy + 1, dx + 1, sx - dx, py + 1, px + 1);
417     
418     		b += promcon_end(conp, b);
419     		promcon_puts(buf, b - buf);
420     		return;
421     	}
422     
423     	/*
424     	 * FIXME: What to do here???
425     	 * Current console.c should not call it like that ever.
426     	 */
427     	prom_printf("\033[7mFIXME: bmove not handled\033[m\n");
428     }
429     
430     static void
431     promcon_cursor(struct vc_data *conp, int mode)
432     {
433     	char buf[32], *b = buf;
434     
435     	switch (mode) {
436     	case CM_ERASE:
437     		break;
438     
439     	case CM_MOVE:
440     	case CM_DRAW:
441     		b += promcon_start(conp, b);
442     		if (px != conp->vc_x || py != conp->vc_y) {
443     			px = conp->vc_x;
444     			py = conp->vc_y;
445     			b += sprintf(b, "\033[%d;%dH", py + 1, px + 1);
446     		}
447     		promcon_puts(buf, b - buf);
448     		break;
449     	}
450     }
451     
452     static int
453     promcon_font_op(struct vc_data *conp, struct console_font_op *op)
454     {
455     	return -ENOSYS;
456     }
457             
458     static int
459     promcon_blank(struct vc_data *conp, int blank)
460     {
461     	if (blank) {
462     		promcon_puts("\033[H\033[J\033[7m \033[m\b", 15);
463     		return 0;
464     	} else {
465     		/* Let console.c redraw */
466     		return 1;
467     	}
468     }
469     
470     static int
471     promcon_scroll(struct vc_data *conp, int t, int b, int dir, int count)
472     {
473     	unsigned char buf[256], *p = buf;
474     	unsigned short *s;
475     	int i;
476     
477     	if (console_blanked)
478     		return 0;
479     	
480     	p += promcon_start(conp, p);
481     
482     	switch (dir) {
483     	case SM_UP:
484     		if (b == ph + 1) {
485     			p += sprintf(p, "\033[%dH\033[%dM", t + 1, count);
486     			px = 0;
487     			py = t;
488     			p += promcon_end(conp, p);
489     			promcon_puts(buf, p - buf);
490     			break;
491     		}
492     
493     		s = (unsigned short *)(conp->vc_origin
494     				       + (t + count) * conp->vc_size_row);
495     
496     		p += sprintf(p, "\033[%dH", t + 1);
497     
498     		for (i = t; i < b - count; i++)
499     			s = promcon_repaint_line(s, buf, &p);
500     
501     		for (; i < b - 1; i++) {
502     			strcpy(p, "\033[K\n");
503     			p += 4;
504     			if (p - buf >= 224) {
505     				promcon_puts(buf, p - buf);
506     				p = buf;
507     			}
508     		}
509     
510     		strcpy(p, "\033[K");
511     		p += 3;
512     
513     		p += promcon_end(conp, p);
514     		promcon_puts(buf, p - buf);
515     		break;
516     
517     	case SM_DOWN:
518     		if (b == ph + 1) {
519     			p += sprintf(p, "\033[%dH\033[%dL", t + 1, count);
520     			px = 0;
521     			py = t;
522     			p += promcon_end(conp, p);
523     			promcon_puts(buf, p - buf);
524     			break;
525     		}
526     
527     		s = (unsigned short *)(conp->vc_origin + t * conp->vc_size_row);
528     
529     		p += sprintf(p, "\033[%dH", t + 1);
530     
531     		for (i = t; i < t + count; i++) {
532     			strcpy(p, "\033[K\n");
533     			p += 4;
534     			if (p - buf >= 224) {
535     				promcon_puts(buf, p - buf);
536     				p = buf;
537     			}
538     		}
539     
540     		for (; i < b; i++)
541     			s = promcon_repaint_line(s, buf, &p);
542     
543     		p += promcon_end(conp, p);
544     		promcon_puts(buf, p - buf);
545     		break;
546     	}
547     
548     	return 0;
549     }
550     
551     #if !(PROMCON_COLOR)
552     static u8 promcon_build_attr(struct vc_data *conp, u8 _color, u8 _intensity, u8 _blink, u8 _underline, u8 _reverse)
553     {
554     	return (_reverse) ? 0xf : 0x7;
555     }
556     #endif
557     
558     /*
559      *  The console 'switch' structure for the VGA based console
560      */
561     
562     static int promcon_dummy(void)
563     {
564             return 0;
565     }
566     
567     #define DUMMY (void *) promcon_dummy
568     
569     const struct consw prom_con = {
570     	con_startup:		promcon_startup,
571     	con_init:		promcon_init,
572     	con_deinit:		promcon_deinit,
573     	con_clear:		promcon_clear,
574     	con_putc:		promcon_putc,
575     	con_putcs:		promcon_putcs,
576     	con_cursor:		promcon_cursor,
577     	con_scroll:		promcon_scroll,
578     	con_bmove:		promcon_bmove,
579     	con_switch:		promcon_switch,
580     	con_blank:		promcon_blank,
581     	con_font_op:		promcon_font_op,
582     	con_set_palette:	DUMMY,
583     	con_scrolldelta:	DUMMY,
584     #if !(PROMCON_COLOR)
585     	con_build_attr:		promcon_build_attr,
586     #endif
587     };
588     
589     void __init prom_con_init(void)
590     {
591     #ifdef CONFIG_DUMMY_CONSOLE
592     	if (conswitchp == &dummy_con)
593     		take_over_console(&prom_con, 0, MAX_NR_CONSOLES-1, 1);
594     	else
595     #endif
596     	if (conswitchp == &prom_con)
597     		promcon_init_unimap(vc_cons[fg_console].d);
598     }
599