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