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