Merge branch 'for-linus' of git://neil.brown.name/md
[linux-2.6] / drivers / video / console / 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/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
25 static short pw = 80 - 1, ph = 34 - 1;
26 static short px, py;
27 static unsigned long promcon_uni_pagedir[2];
28
29 extern u8 promfont_unicount[];
30 extern 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
40 static __inline__ void
41 promcon_puts(char *buf, int cnt)
42 {
43         prom_printf("%*.*s", cnt, cnt, buf);
44 }
45
46 static int
47 promcon_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
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         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
110 const 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
134 static void
135 promcon_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
163 static void
164 promcon_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
188 static void
189 promcon_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
198 static int
199 promcon_switch(struct vc_data *conp)
200 {
201         return 1;
202 }
203
204 static unsigned short *
205 promcon_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
234 static void
235 promcon_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
324 static void
325 promcon_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
336 static void
337 promcon_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                         
405 static void
406 promcon_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         /*
429          * FIXME: What to do here???
430          * Current console.c should not call it like that ever.
431          */
432         prom_printf("\033[7mFIXME: bmove not handled\033[m\n");
433 }
434
435 static void
436 promcon_cursor(struct vc_data *conp, int mode)
437 {
438         char buf[32], *b = buf;
439
440         switch (mode) {
441         case CM_ERASE:
442                 break;
443
444         case CM_MOVE:
445         case CM_DRAW:
446                 b += promcon_start(conp, b);
447                 if (px != conp->vc_x || py != conp->vc_y) {
448                         px = conp->vc_x;
449                         py = conp->vc_y;
450                         b += sprintf(b, "\033[%d;%dH", py + 1, px + 1);
451                 }
452                 promcon_puts(buf, b - buf);
453                 break;
454         }
455 }
456
457 static int
458 promcon_blank(struct vc_data *conp, int blank, int mode_switch)
459 {
460         if (blank) {
461                 promcon_puts("\033[H\033[J\033[7m \033[m\b", 15);
462                 return 0;
463         } else {
464                 /* Let console.c redraw */
465                 return 1;
466         }
467 }
468
469 static int
470 promcon_scroll(struct vc_data *conp, int t, int b, int dir, int count)
471 {
472         unsigned char buf[256], *p = buf;
473         unsigned short *s;
474         int i;
475
476         if (console_blanked)
477                 return 0;
478         
479         p += promcon_start(conp, p);
480
481         switch (dir) {
482         case SM_UP:
483                 if (b == ph + 1) {
484                         p += sprintf(p, "\033[%dH\033[%dM", t + 1, count);
485                         px = 0;
486                         py = t;
487                         p += promcon_end(conp, p);
488                         promcon_puts(buf, p - buf);
489                         break;
490                 }
491
492                 s = (unsigned short *)(conp->vc_origin
493                                        + (t + count) * conp->vc_size_row);
494
495                 p += sprintf(p, "\033[%dH", t + 1);
496
497                 for (i = t; i < b - count; i++)
498                         s = promcon_repaint_line(s, buf, &p);
499
500                 for (; i < b - 1; i++) {
501                         strcpy(p, "\033[K\n");
502                         p += 4;
503                         if (p - buf >= 224) {
504                                 promcon_puts(buf, p - buf);
505                                 p = buf;
506                         }
507                 }
508
509                 strcpy(p, "\033[K");
510                 p += 3;
511
512                 p += promcon_end(conp, p);
513                 promcon_puts(buf, p - buf);
514                 break;
515
516         case SM_DOWN:
517                 if (b == ph + 1) {
518                         p += sprintf(p, "\033[%dH\033[%dL", t + 1, count);
519                         px = 0;
520                         py = t;
521                         p += promcon_end(conp, p);
522                         promcon_puts(buf, p - buf);
523                         break;
524                 }
525
526                 s = (unsigned short *)(conp->vc_origin + t * conp->vc_size_row);
527
528                 p += sprintf(p, "\033[%dH", t + 1);
529
530                 for (i = t; i < t + count; i++) {
531                         strcpy(p, "\033[K\n");
532                         p += 4;
533                         if (p - buf >= 224) {
534                                 promcon_puts(buf, p - buf);
535                                 p = buf;
536                         }
537                 }
538
539                 for (; i < b; i++)
540                         s = promcon_repaint_line(s, buf, &p);
541
542                 p += promcon_end(conp, p);
543                 promcon_puts(buf, p - buf);
544                 break;
545         }
546
547         return 0;
548 }
549
550 #if !(PROMCON_COLOR)
551 static u8 promcon_build_attr(struct vc_data *conp, u8 _color, u8 _intensity,
552     u8 _blink, u8 _underline, u8 _reverse, u8 _italic)
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         .owner =                THIS_MODULE,
571         .con_startup =          promcon_startup,
572         .con_init =             promcon_init,
573         .con_deinit =           promcon_deinit,
574         .con_clear =            promcon_clear,
575         .con_putc =             promcon_putc,
576         .con_putcs =            promcon_putcs,
577         .con_cursor =           promcon_cursor,
578         .con_scroll =           promcon_scroll,
579         .con_bmove =            promcon_bmove,
580         .con_switch =           promcon_switch,
581         .con_blank =            promcon_blank,
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 }