| /* $Id: promcon.c,v 1.17 2000/07/26 23:02:52 davem Exp $ |
| * Console driver utilizing PROM sun terminal emulation |
| * |
| * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) |
| * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/errno.h> |
| #include <linux/string.h> |
| #include <linux/mm.h> |
| #include <linux/slab.h> |
| #include <linux/delay.h> |
| #include <linux/console.h> |
| #include <linux/vt_kern.h> |
| #include <linux/selection.h> |
| #include <linux/fb.h> |
| #include <linux/init.h> |
| #include <linux/kd.h> |
| |
| #include <asm/oplib.h> |
| #include <asm/uaccess.h> |
| |
| static short pw = 80 - 1, ph = 34 - 1; |
| static short px, py; |
| static unsigned long promcon_uni_pagedir[2]; |
| |
| extern u8 promfont_unicount[]; |
| extern u16 promfont_unitable[]; |
| |
| #define PROMCON_COLOR 0 |
| |
| #if PROMCON_COLOR |
| #define inverted(s) ((((s) & 0x7700) == 0x0700) ? 0 : 1) |
| #else |
| #define inverted(s) (((s) & 0x0800) ? 1 : 0) |
| #endif |
| |
| static __inline__ void |
| promcon_puts(char *buf, int cnt) |
| { |
| prom_printf("%*.*s", cnt, cnt, buf); |
| } |
| |
| static int |
| promcon_start(struct vc_data *conp, char *b) |
| { |
| unsigned short *s = (unsigned short *) |
| (conp->vc_origin + py * conp->vc_size_row + (px << 1)); |
| u16 cs; |
| |
| cs = scr_readw(s); |
| if (px == pw) { |
| unsigned short *t = s - 1; |
| u16 ct = scr_readw(t); |
| |
| if (inverted(cs) && inverted(ct)) |
| return sprintf(b, "\b\033[7m%c\b\033[@%c\033[m", cs, |
| ct); |
| else if (inverted(cs)) |
| return sprintf(b, "\b\033[7m%c\033[m\b\033[@%c", cs, |
| ct); |
| else if (inverted(ct)) |
| return sprintf(b, "\b%c\b\033[@\033[7m%c\033[m", cs, |
| ct); |
| else |
| return sprintf(b, "\b%c\b\033[@%c", cs, ct); |
| } |
| |
| if (inverted(cs)) |
| return sprintf(b, "\033[7m%c\033[m\b", cs); |
| else |
| return sprintf(b, "%c\b", cs); |
| } |
| |
| static int |
| promcon_end(struct vc_data *conp, char *b) |
| { |
| unsigned short *s = (unsigned short *) |
| (conp->vc_origin + py * conp->vc_size_row + (px << 1)); |
| char *p = b; |
| u16 cs; |
| |
| b += sprintf(b, "\033[%d;%dH", py + 1, px + 1); |
| |
| cs = scr_readw(s); |
| if (px == pw) { |
| unsigned short *t = s - 1; |
| u16 ct = scr_readw(t); |
| |
| if (inverted(cs) && inverted(ct)) |
| b += sprintf(b, "\b%c\b\033[@\033[7m%c\033[m", cs, ct); |
| else if (inverted(cs)) |
| b += sprintf(b, "\b%c\b\033[@%c", cs, ct); |
| else if (inverted(ct)) |
| b += sprintf(b, "\b\033[7m%c\b\033[@%c\033[m", cs, ct); |
| else |
| b += sprintf(b, "\b\033[7m%c\033[m\b\033[@%c", cs, ct); |
| return b - p; |
| } |
| |
| if (inverted(cs)) |
| b += sprintf(b, "%c\b", cs); |
| else |
| b += sprintf(b, "\033[7m%c\033[m\b", cs); |
| return b - p; |
| } |
| |
| const char *promcon_startup(void) |
| { |
| const char *display_desc = "PROM"; |
| int node; |
| char buf[40]; |
| |
| node = prom_getchild(prom_root_node); |
| node = prom_searchsiblings(node, "options"); |
| if (prom_getproperty(node, "screen-#columns", buf, 40) != -1) { |
| pw = simple_strtoul(buf, NULL, 0); |
| if (pw < 10 || pw > 256) |
| pw = 80; |
| pw--; |
| } |
| if (prom_getproperty(node, "screen-#rows", buf, 40) != -1) { |
| ph = simple_strtoul(buf, NULL, 0); |
| if (ph < 10 || ph > 256) |
| ph = 34; |
| ph--; |
| } |
| promcon_puts("\033[H\033[J", 6); |
| return display_desc; |
| } |
| |
| static void |
| promcon_init_unimap(struct vc_data *conp) |
| { |
| mm_segment_t old_fs = get_fs(); |
| struct unipair *p, *p1; |
| u16 *q; |
| int i, j, k; |
| |
| p = kmalloc(256*sizeof(struct unipair), GFP_KERNEL); |
| if (!p) return; |
| |
| q = promfont_unitable; |
| p1 = p; |
| k = 0; |
| for (i = 0; i < 256; i++) |
| for (j = promfont_unicount[i]; j; j--) { |
| p1->unicode = *q++; |
| p1->fontpos = i; |
| p1++; |
| k++; |
| } |
| set_fs(KERNEL_DS); |
| con_clear_unimap(conp, NULL); |
| con_set_unimap(conp, k, p); |
| con_protect_unimap(conp, 1); |
| set_fs(old_fs); |
| kfree(p); |
| } |
| |
| static void |
| promcon_init(struct vc_data *conp, int init) |
| { |
| unsigned long p; |
| |
| conp->vc_can_do_color = PROMCON_COLOR; |
| if (init) { |
| conp->vc_cols = pw + 1; |
| conp->vc_rows = ph + 1; |
| } |
| p = *conp->vc_uni_pagedir_loc; |
| if (conp->vc_uni_pagedir_loc == &conp->vc_uni_pagedir || |
| !--conp->vc_uni_pagedir_loc[1]) |
| con_free_unimap(conp); |
| conp->vc_uni_pagedir_loc = promcon_uni_pagedir; |
| promcon_uni_pagedir[1]++; |
| if (!promcon_uni_pagedir[0] && p) { |
| promcon_init_unimap(conp); |
| } |
| if (!init) { |
| if (conp->vc_cols != pw + 1 || conp->vc_rows != ph + 1) |
| vc_resize(conp, pw + 1, ph + 1); |
| } |
| } |
| |
| static void |
| promcon_deinit(struct vc_data *conp) |
| { |
| /* When closing the last console, reset video origin */ |
| if (!--promcon_uni_pagedir[1]) |
| con_free_unimap(conp); |
| conp->vc_uni_pagedir_loc = &conp->vc_uni_pagedir; |
| con_set_default_unimap(conp); |
| } |
| |
| static int |
| promcon_switch(struct vc_data *conp) |
| { |
| return 1; |
| } |
| |
| static unsigned short * |
| promcon_repaint_line(unsigned short *s, unsigned char *buf, unsigned char **bp) |
| { |
| int cnt = pw + 1; |
| int attr = -1; |
| unsigned char *b = *bp; |
| |
| while (cnt--) { |
| u16 c = scr_readw(s); |
| if (attr != inverted(c)) { |
| attr = inverted(c); |
| if (attr) { |
| strcpy (b, "\033[7m"); |
| b += 4; |
| } else { |
| strcpy (b, "\033[m"); |
| b += 3; |
| } |
| } |
| *b++ = c; |
| s++; |
| if (b - buf >= 224) { |
| promcon_puts(buf, b - buf); |
| b = buf; |
| } |
| } |
| *bp = b; |
| return s; |
| } |
| |
| static void |
| promcon_putcs(struct vc_data *conp, const unsigned short *s, |
| int count, int y, int x) |
| { |
| unsigned char buf[256], *b = buf; |
| unsigned short attr = scr_readw(s); |
| unsigned char save; |
| int i, last = 0; |
| |
| if (console_blanked) |
| return; |
| |
| if (count <= 0) |
| return; |
| |
| b += promcon_start(conp, b); |
| |
| if (x + count >= pw + 1) { |
| if (count == 1) { |
| x -= 1; |
| save = scr_readw((unsigned short *)(conp->vc_origin |
| + y * conp->vc_size_row |
| + (x << 1))); |
| |
| if (px != x || py != y) { |
| b += sprintf(b, "\033[%d;%dH", y + 1, x + 1); |
| px = x; |
| py = y; |
| } |
| |
| if (inverted(attr)) |
| b += sprintf(b, "\033[7m%c\033[m", scr_readw(s++)); |
| else |
| b += sprintf(b, "%c", scr_readw(s++)); |
| |
| strcpy(b, "\b\033[@"); |
| b += 4; |
| |
| if (inverted(save)) |
| b += sprintf(b, "\033[7m%c\033[m", save); |
| else |
| b += sprintf(b, "%c", save); |
| |
| px++; |
| |
| b += promcon_end(conp, b); |
| promcon_puts(buf, b - buf); |
| return; |
| } else { |
| last = 1; |
| count = pw - x - 1; |
| } |
| } |
| |
| if (inverted(attr)) { |
| strcpy(b, "\033[7m"); |
| b += 4; |
| } |
| |
| if (px != x || py != y) { |
| b += sprintf(b, "\033[%d;%dH", y + 1, x + 1); |
| px = x; |
| py = y; |
| } |
| |
| for (i = 0; i < count; i++) { |
| if (b - buf >= 224) { |
| promcon_puts(buf, b - buf); |
| b = buf; |
| } |
| *b++ = scr_readw(s++); |
| } |
| |
| px += count; |
| |
| if (last) { |
| save = scr_readw(s++); |
| b += sprintf(b, "%c\b\033[@%c", scr_readw(s++), save); |
| px++; |
| } |
| |
| if (inverted(attr)) { |
| strcpy(b, "\033[m"); |
| b += 3; |
| } |
| |
| b += promcon_end(conp, b); |
| promcon_puts(buf, b - buf); |
| } |
| |
| static void |
| promcon_putc(struct vc_data *conp, int c, int y, int x) |
| { |
| unsigned short s; |
| |
| if (console_blanked) |
| return; |
| |
| scr_writew(c, &s); |
| promcon_putcs(conp, &s, 1, y, x); |
| } |
| |
| static void |
| promcon_clear(struct vc_data *conp, int sy, int sx, int height, int width) |
| { |
| unsigned char buf[256], *b = buf; |
| int i, j; |
| |
| if (console_blanked) |
| return; |
| |
| b += promcon_start(conp, b); |
| |
| if (!sx && width == pw + 1) { |
| |
| if (!sy && height == ph + 1) { |
| strcpy(b, "\033[H\033[J"); |
| b += 6; |
| b += promcon_end(conp, b); |
| promcon_puts(buf, b - buf); |
| return; |
| } else if (sy + height == ph + 1) { |
| b += sprintf(b, "\033[%dH\033[J", sy + 1); |
| b += promcon_end(conp, b); |
| promcon_puts(buf, b - buf); |
| return; |
| } |
| |
| b += sprintf(b, "\033[%dH", sy + 1); |
| for (i = 1; i < height; i++) { |
| strcpy(b, "\033[K\n"); |
| b += 4; |
| } |
| |
| strcpy(b, "\033[K"); |
| b += 3; |
| |
| b += promcon_end(conp, b); |
| promcon_puts(buf, b - buf); |
| return; |
| |
| } else if (sx + width == pw + 1) { |
| |
| b += sprintf(b, "\033[%d;%dH", sy + 1, sx + 1); |
| for (i = 1; i < height; i++) { |
| strcpy(b, "\033[K\n"); |
| b += 4; |
| } |
| |
| strcpy(b, "\033[K"); |
| b += 3; |
| |
| b += promcon_end(conp, b); |
| promcon_puts(buf, b - buf); |
| return; |
| } |
| |
| for (i = sy + 1; i <= sy + height; i++) { |
| b += sprintf(b, "\033[%d;%dH", i, sx + 1); |
| for (j = 0; j < width; j++) |
| *b++ = ' '; |
| if (b - buf + width >= 224) { |
| promcon_puts(buf, b - buf); |
| b = buf; |
| } |
| } |
| |
| b += promcon_end(conp, b); |
| promcon_puts(buf, b - buf); |
| } |
| |
| static void |
| promcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, |
| int height, int width) |
| { |
| char buf[256], *b = buf; |
| |
| if (console_blanked) |
| return; |
| |
| b += promcon_start(conp, b); |
| if (sy == dy && height == 1) { |
| if (dx > sx && dx + width == conp->vc_cols) |
| b += sprintf(b, "\033[%d;%dH\033[%d@\033[%d;%dH", |
| sy + 1, sx + 1, dx - sx, py + 1, px + 1); |
| else if (dx < sx && sx + width == conp->vc_cols) |
| b += sprintf(b, "\033[%d;%dH\033[%dP\033[%d;%dH", |
| dy + 1, dx + 1, sx - dx, py + 1, px + 1); |
| |
| b += promcon_end(conp, b); |
| promcon_puts(buf, b - buf); |
| return; |
| } |
| |
| /* |
| * FIXME: What to do here??? |
| * Current console.c should not call it like that ever. |
| */ |
| prom_printf("\033[7mFIXME: bmove not handled\033[m\n"); |
| } |
| |
| static void |
| promcon_cursor(struct vc_data *conp, int mode) |
| { |
| char buf[32], *b = buf; |
| |
| switch (mode) { |
| case CM_ERASE: |
| break; |
| |
| case CM_MOVE: |
| case CM_DRAW: |
| b += promcon_start(conp, b); |
| if (px != conp->vc_x || py != conp->vc_y) { |
| px = conp->vc_x; |
| py = conp->vc_y; |
| b += sprintf(b, "\033[%d;%dH", py + 1, px + 1); |
| } |
| promcon_puts(buf, b - buf); |
| break; |
| } |
| } |
| |
| static int |
| promcon_blank(struct vc_data *conp, int blank, int mode_switch) |
| { |
| if (blank) { |
| promcon_puts("\033[H\033[J\033[7m \033[m\b", 15); |
| return 0; |
| } else { |
| /* Let console.c redraw */ |
| return 1; |
| } |
| } |
| |
| static int |
| promcon_scroll(struct vc_data *conp, int t, int b, int dir, int count) |
| { |
| unsigned char buf[256], *p = buf; |
| unsigned short *s; |
| int i; |
| |
| if (console_blanked) |
| return 0; |
| |
| p += promcon_start(conp, p); |
| |
| switch (dir) { |
| case SM_UP: |
| if (b == ph + 1) { |
| p += sprintf(p, "\033[%dH\033[%dM", t + 1, count); |
| px = 0; |
| py = t; |
| p += promcon_end(conp, p); |
| promcon_puts(buf, p - buf); |
| break; |
| } |
| |
| s = (unsigned short *)(conp->vc_origin |
| + (t + count) * conp->vc_size_row); |
| |
| p += sprintf(p, "\033[%dH", t + 1); |
| |
| for (i = t; i < b - count; i++) |
| s = promcon_repaint_line(s, buf, &p); |
| |
| for (; i < b - 1; i++) { |
| strcpy(p, "\033[K\n"); |
| p += 4; |
| if (p - buf >= 224) { |
| promcon_puts(buf, p - buf); |
| p = buf; |
| } |
| } |
| |
| strcpy(p, "\033[K"); |
| p += 3; |
| |
| p += promcon_end(conp, p); |
| promcon_puts(buf, p - buf); |
| break; |
| |
| case SM_DOWN: |
| if (b == ph + 1) { |
| p += sprintf(p, "\033[%dH\033[%dL", t + 1, count); |
| px = 0; |
| py = t; |
| p += promcon_end(conp, p); |
| promcon_puts(buf, p - buf); |
| break; |
| } |
| |
| s = (unsigned short *)(conp->vc_origin + t * conp->vc_size_row); |
| |
| p += sprintf(p, "\033[%dH", t + 1); |
| |
| for (i = t; i < t + count; i++) { |
| strcpy(p, "\033[K\n"); |
| p += 4; |
| if (p - buf >= 224) { |
| promcon_puts(buf, p - buf); |
| p = buf; |
| } |
| } |
| |
| for (; i < b; i++) |
| s = promcon_repaint_line(s, buf, &p); |
| |
| p += promcon_end(conp, p); |
| promcon_puts(buf, p - buf); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| #if !(PROMCON_COLOR) |
| static u8 promcon_build_attr(struct vc_data *conp, u8 _color, u8 _intensity, u8 _blink, u8 _underline, u8 _reverse) |
| { |
| return (_reverse) ? 0xf : 0x7; |
| } |
| #endif |
| |
| /* |
| * The console 'switch' structure for the VGA based console |
| */ |
| |
| static int promcon_dummy(void) |
| { |
| return 0; |
| } |
| |
| #define DUMMY (void *) promcon_dummy |
| |
| const struct consw prom_con = { |
| .owner = THIS_MODULE, |
| .con_startup = promcon_startup, |
| .con_init = promcon_init, |
| .con_deinit = promcon_deinit, |
| .con_clear = promcon_clear, |
| .con_putc = promcon_putc, |
| .con_putcs = promcon_putcs, |
| .con_cursor = promcon_cursor, |
| .con_scroll = promcon_scroll, |
| .con_bmove = promcon_bmove, |
| .con_switch = promcon_switch, |
| .con_blank = promcon_blank, |
| .con_set_palette = DUMMY, |
| .con_scrolldelta = DUMMY, |
| #if !(PROMCON_COLOR) |
| .con_build_attr = promcon_build_attr, |
| #endif |
| }; |
| |
| void __init prom_con_init(void) |
| { |
| #ifdef CONFIG_DUMMY_CONSOLE |
| if (conswitchp == &dummy_con) |
| take_over_console(&prom_con, 0, MAX_NR_CONSOLES-1, 1); |
| else |
| #endif |
| if (conswitchp == &prom_con) |
| promcon_init_unimap(vc_cons[fg_console].d); |
| } |