| /* |
| * linux/kernel/console.c |
| * |
| * (C) 1991 Linus Torvalds |
| */ |
| |
| /* |
| * console.c |
| * |
| * This module implements the console io functions |
| * 'long con_init(long)' |
| * 'void con_write(struct tty_queue * queue)' |
| * Hopefully this will be a rather complete VT102 implementation. |
| * |
| * Beeping thanks to John T Kohl. |
| * |
| * Virtual Consoles, Screen Blanking, Screen Dumping, Color, Graphics |
| * Chars, and VT100 enhancements by Peter MacDonald. |
| */ |
| |
| /* |
| * NOTE!!! We sometimes disable and enable interrupts for a short while |
| * (to put a word in video IO), but this will work even for keyboard |
| * interrupts. We know interrupts aren't enabled when getting a keyboard |
| * interrupt, as we use trap-gates. Hopefully all is well. |
| */ |
| |
| /* |
| * Code to check for different video-cards mostly by Galen Hunt, |
| * <g-hunt@ee.utah.edu> |
| */ |
| |
| #include <linux/sched.h> |
| #include <linux/timer.h> |
| #include <linux/tty.h> |
| #include <linux/config.h> |
| #include <linux/kernel.h> |
| |
| #include <asm/io.h> |
| #include <asm/system.h> |
| #include <asm/segment.h> |
| |
| #include <linux/string.h> |
| #include <errno.h> |
| |
| #include <sys/kd.h> |
| #include "vt_kern.h" |
| |
| /* |
| * These are set up by the setup-routine at boot-time: |
| */ |
| |
| #define ORIG_X (*(unsigned char *)0x90000) |
| #define ORIG_Y (*(unsigned char *)0x90001) |
| #define ORIG_VIDEO_PAGE (*(unsigned short *)0x90004) |
| #define ORIG_VIDEO_MODE ((*(unsigned short *)0x90006) & 0xff) |
| #define ORIG_VIDEO_COLS (((*(unsigned short *)0x90006) & 0xff00) >> 8) |
| #define ORIG_VIDEO_LINES ((*(unsigned short *)0x9000e) & 0xff) |
| #define ORIG_VIDEO_EGA_AX (*(unsigned short *)0x90008) |
| #define ORIG_VIDEO_EGA_BX (*(unsigned short *)0x9000a) |
| #define ORIG_VIDEO_EGA_CX (*(unsigned short *)0x9000c) |
| |
| #define VIDEO_TYPE_MDA 0x10 /* Monochrome Text Display */ |
| #define VIDEO_TYPE_CGA 0x11 /* CGA Display */ |
| #define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA in Monochrome Mode */ |
| #define VIDEO_TYPE_EGAC 0x21 /* EGA/VGA in Color Mode */ |
| |
| #define NPAR 16 |
| |
| extern void vt_init(void); |
| extern void keyboard_interrupt(void); |
| extern void set_leds(void); |
| extern unsigned char kapplic; |
| extern unsigned char ckmode; |
| extern unsigned char krepeat; |
| extern unsigned char kleds; |
| extern unsigned char kmode; |
| extern unsigned char kraw; |
| extern unsigned char ke0; |
| extern unsigned char lfnlmode; |
| |
| unsigned long video_num_columns; /* Number of text columns */ |
| unsigned long video_num_lines; /* Number of test lines */ |
| |
| static unsigned char video_type; /* Type of display being used */ |
| static unsigned long video_mem_base; /* Base of video memory */ |
| static unsigned long video_mem_term; /* End of video memory */ |
| static unsigned long video_size_row; /* Bytes per row */ |
| static unsigned char video_page; /* Initial video page */ |
| static unsigned short video_port_reg; /* Video register select port */ |
| static unsigned short video_port_val; /* Video register value port */ |
| static int can_do_color = 0; |
| |
| static struct { |
| unsigned short vc_video_erase_char; /* Background erase character */ |
| unsigned char vc_attr; /* Current attributes */ |
| unsigned char vc_def_color; /* Default colors */ |
| unsigned char vc_color; /* Foreground & background */ |
| unsigned char vc_s_color; /* Saved foreground & background */ |
| unsigned char vc_ulcolor; /* Colour for underline mode */ |
| unsigned char vc_halfcolor; /* Colour for half intensity mode */ |
| unsigned long vc_origin; /* Used for EGA/VGA fast scroll */ |
| unsigned long vc_scr_end; /* Used for EGA/VGA fast scroll */ |
| unsigned long vc_pos; |
| unsigned long vc_x,vc_y; |
| unsigned long vc_top,vc_bottom; |
| unsigned long vc_state; |
| unsigned long vc_npar,vc_par[NPAR]; |
| unsigned long vc_video_mem_start; /* Start of video RAM */ |
| unsigned long vc_video_mem_end; /* End of video RAM (sort of) */ |
| unsigned long vc_saved_x; |
| unsigned long vc_saved_y; |
| /* mode flags */ |
| unsigned long vc_kbdapplic : 1; /* Application keyboard */ |
| unsigned long vc_charset : 1; /* Character set G0 / G1 */ |
| unsigned long vc_s_charset : 1; /* Saved character set */ |
| unsigned long vc_decckm : 1; /* Cursor Keys Mode */ |
| unsigned long vc_decscnm : 1; /* Screen Mode */ |
| unsigned long vc_decom : 1; /* Origin Mode */ |
| unsigned long vc_decawm : 1; /* Autowrap Mode */ |
| unsigned long vc_decarm : 1; /* Autorepeat Mode */ |
| unsigned long vc_deccm : 1; /* Cursor Visible */ |
| unsigned long vc_decim : 1; /* Insert Mode */ |
| unsigned long vc_lnm : 1; /* Line feed New line Mode */ |
| /* attribute flags */ |
| unsigned long vc_intensity : 2; /* 0=half-bright, 1=normal, 2=bold */ |
| unsigned long vc_underline : 1; |
| unsigned long vc_blink : 1; |
| unsigned long vc_reverse : 1; |
| unsigned long vc_s_intensity : 2; /* saved rendition */ |
| unsigned long vc_s_underline : 1; |
| unsigned long vc_s_blink : 1; |
| unsigned long vc_s_reverse : 1; |
| /* misc */ |
| unsigned long vc_ques : 1; |
| unsigned long vc_need_wrap : 1; |
| unsigned long vc_tab_stop[5]; /* Tab stops. 160 columns. */ |
| unsigned char vc_kbdmode; |
| char * vc_translate; |
| char * vc_G0_charset; |
| char * vc_G1_charset; |
| char * vc_saved_G0; |
| char * vc_saved_G1; |
| /* additional information is in vt_kern.h */ |
| } vc_cons [NR_CONSOLES]; |
| |
| #define MEM_BUFFER_SIZE (2*80*50*8) |
| |
| unsigned short *vc_scrbuf[NR_CONSOLES]; |
| static unsigned short * vc_scrmembuf; |
| static int console_blanked = 0; |
| |
| #define origin (vc_cons[currcons].vc_origin) |
| #define scr_end (vc_cons[currcons].vc_scr_end) |
| #define pos (vc_cons[currcons].vc_pos) |
| #define top (vc_cons[currcons].vc_top) |
| #define bottom (vc_cons[currcons].vc_bottom) |
| #define x (vc_cons[currcons].vc_x) |
| #define y (vc_cons[currcons].vc_y) |
| #define state (vc_cons[currcons].vc_state) |
| #define npar (vc_cons[currcons].vc_npar) |
| #define par (vc_cons[currcons].vc_par) |
| #define ques (vc_cons[currcons].vc_ques) |
| #define attr (vc_cons[currcons].vc_attr) |
| #define saved_x (vc_cons[currcons].vc_saved_x) |
| #define saved_y (vc_cons[currcons].vc_saved_y) |
| #define translate (vc_cons[currcons].vc_translate) |
| #define G0_charset (vc_cons[currcons].vc_G0_charset) |
| #define G1_charset (vc_cons[currcons].vc_G1_charset) |
| #define saved_G0 (vc_cons[currcons].vc_saved_G0) |
| #define saved_G1 (vc_cons[currcons].vc_saved_G1) |
| #define video_mem_start (vc_cons[currcons].vc_video_mem_start) |
| #define video_mem_end (vc_cons[currcons].vc_video_mem_end) |
| #define video_erase_char (vc_cons[currcons].vc_video_erase_char) |
| #define decckm (vc_cons[currcons].vc_decckm) |
| #define decscnm (vc_cons[currcons].vc_decscnm) |
| #define decom (vc_cons[currcons].vc_decom) |
| #define decawm (vc_cons[currcons].vc_decawm) |
| #define decarm (vc_cons[currcons].vc_decarm) |
| #define deccm (vc_cons[currcons].vc_deccm) |
| #define decim (vc_cons[currcons].vc_decim) |
| #define lnm (vc_cons[currcons].vc_lnm) |
| #define kbdapplic (vc_cons[currcons].vc_kbdapplic) |
| #define need_wrap (vc_cons[currcons].vc_need_wrap) |
| #define color (vc_cons[currcons].vc_color) |
| #define s_color (vc_cons[currcons].vc_s_color) |
| #define def_color (vc_cons[currcons].vc_def_color) |
| #define foreground (color & 0x0f) |
| #define background (color & 0xf0) |
| #define charset (vc_cons[currcons].vc_charset) |
| #define s_charset (vc_cons[currcons].vc_s_charset) |
| #define intensity (vc_cons[currcons].vc_intensity) |
| #define underline (vc_cons[currcons].vc_underline) |
| #define blink (vc_cons[currcons].vc_blink) |
| #define reverse (vc_cons[currcons].vc_reverse) |
| #define s_intensity (vc_cons[currcons].vc_s_intensity) |
| #define s_underline (vc_cons[currcons].vc_s_underline) |
| #define s_blink (vc_cons[currcons].vc_s_blink) |
| #define s_reverse (vc_cons[currcons].vc_s_reverse) |
| #define ulcolor (vc_cons[currcons].vc_ulcolor) |
| #define halfcolor (vc_cons[currcons].vc_halfcolor) |
| #define kbdmode (vc_cons[currcons].vc_kbdmode) |
| #define tab_stop (vc_cons[currcons].vc_tab_stop) |
| #define kbdraw (vt_cons[currcons].vc_kbdraw) |
| #define kbdleds (vt_cons[currcons].vc_kbdleds) |
| #define vtmode (vt_cons[currcons].vt_mode) |
| |
| #define SET(mode,fg,v) \ |
| (mode) = (v); \ |
| if (currcons == fg_console) \ |
| (fg) = (v) |
| |
| int blankinterval = 5*60*HZ; |
| static int screen_size = 0; |
| |
| static void sysbeep(void); |
| |
| /* |
| * this is what the terminal answers to a ESC-Z or csi0c query. |
| */ |
| #define VT100ID "\033[?1;2c" |
| #define VT102ID "\033[?6c" |
| |
| static char * translations[] = { |
| /* 8-bit Latin-1 mapped to the PC charater set: '\0' means non-printable */ |
| "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| " !\"#$%&'()*+,-./0123456789:;<=>?" |
| "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" |
| "`abcdefghijklmnopqrstuvwxyz{|}~\0" |
| "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| "\040\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376" |
| "\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250" |
| "\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376" |
| "\376\245\376\376\376\376\231\376\376\376\376\376\232\376\376\341" |
| "\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213" |
| "\376\244\225\242\223\376\224\366\376\227\243\226\201\376\376\230", |
| /* vt100 graphics */ |
| "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| " !\"#$%&'()*+,-./0123456789:;<=>?" |
| "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ " |
| "\004\261\007\007\007\007\370\361\007\007\275\267\326\323\327\304" |
| "\304\304\304\304\307\266\320\322\272\363\362\343\007\234\007\0" |
| "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" |
| "\040\255\233\234\376\235\174\025\376\376\246\256\252\055\376\376" |
| "\370\361\375\376\376\346\024\371\376\376\247\257\254\253\376\250" |
| "\376\376\376\376\216\217\222\200\376\220\376\376\376\376\376\376" |
| "\376\245\376\376\376\376\231\376\376\376\376\376\232\376\376\341" |
| "\205\240\203\376\204\206\221\207\212\202\210\211\215\241\214\213" |
| "\376\244\225\242\223\376\224\366\376\227\243\226\201\376\376\230" |
| }; |
| |
| #define NORM_TRANS (translations[0]) |
| #define GRAF_TRANS (translations[1]) |
| |
| static unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7, |
| 8,12,10,14, 9,13,11,15 }; |
| |
| /* |
| * gotoxy() must verify all boundaries, because the arguments |
| * might also be negative. If the given position is out of |
| * bounds, the cursor is placed at the nearest margin. |
| */ |
| static void gotoxy(int currcons, int new_x, int new_y) |
| { |
| int max_y; |
| |
| if (new_x < 0) |
| x = 0; |
| else |
| if (new_x >= video_num_columns) |
| x = video_num_columns - 1; |
| else |
| x = new_x; |
| if (decom) { |
| new_y += top; |
| max_y = bottom; |
| } else |
| max_y = video_num_lines; |
| if (new_y < 0) |
| y = 0; |
| else |
| if (new_y >= max_y) |
| y = max_y - 1; |
| else |
| y = new_y; |
| pos = origin + y*video_size_row + (x<<1); |
| need_wrap = 0; |
| } |
| |
| static void set_origin(int currcons) |
| { |
| if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_EGAM) |
| return; |
| if (currcons != fg_console || vtmode == KD_GRAPHICS) |
| return; |
| cli(); |
| outb_p(12, video_port_reg); |
| outb_p(0xff&((origin-video_mem_base)>>9), video_port_val); |
| outb_p(13, video_port_reg); |
| outb_p(0xff&((origin-video_mem_base)>>1), video_port_val); |
| sti(); |
| } |
| |
| static void scrup(int currcons, unsigned int t, unsigned int b) |
| { |
| int hardscroll = 1; |
| |
| if (b > video_num_lines || t >= b) |
| return; |
| if (video_type != VIDEO_TYPE_EGAC && video_type != VIDEO_TYPE_EGAM) |
| hardscroll = 0; |
| else if (t || b != video_num_lines) |
| hardscroll = 0; |
| if (hardscroll) { |
| origin += video_size_row; |
| pos += video_size_row; |
| scr_end += video_size_row; |
| if (scr_end > video_mem_end) { |
| __asm__("cld\n\t" |
| "rep\n\t" |
| "movsl\n\t" |
| "movl _video_num_columns,%1\n\t" |
| "rep\n\t" |
| "stosw" |
| ::"a" (video_erase_char), |
| "c" ((video_num_lines-1)*video_num_columns>>1), |
| "D" (video_mem_start), |
| "S" (origin) |
| :"cx","di","si"); |
| scr_end -= origin-video_mem_start; |
| pos -= origin-video_mem_start; |
| origin = video_mem_start; |
| } else { |
| __asm__("cld\n\t" |
| "rep\n\t" |
| "stosw" |
| ::"a" (video_erase_char), |
| "c" (video_num_columns), |
| "D" (scr_end-video_size_row) |
| :"cx","di"); |
| } |
| set_origin(currcons); |
| } else { |
| __asm__("cld\n\t" |
| "rep\n\t" |
| "movsl\n\t" |
| "movl _video_num_columns,%%ecx\n\t" |
| "rep\n\t" |
| "stosw" |
| ::"a" (video_erase_char), |
| "c" ((b-t-1)*video_num_columns>>1), |
| "D" (origin+video_size_row*t), |
| "S" (origin+video_size_row*(t+1)) |
| :"cx","di","si"); |
| } |
| } |
| |
| static void scrdown(int currcons, unsigned int t, unsigned int b) |
| { |
| if (b > video_num_lines || t >= b) |
| return; |
| __asm__("std\n\t" |
| "rep\n\t" |
| "movsl\n\t" |
| "addl $2,%%edi\n\t" /* %edi has been decremented by 4 */ |
| "movl _video_num_columns,%%ecx\n\t" |
| "rep\n\t" |
| "stosw\n\t" |
| "cld" |
| ::"a" (video_erase_char), |
| "c" ((b-t-1)*video_num_columns>>1), |
| "D" (origin+video_size_row*b-4), |
| "S" (origin+video_size_row*(b-1)-4) |
| :"ax","cx","di","si"); |
| } |
| |
| static void lf(int currcons) |
| { |
| if (y+1<bottom) { |
| y++; |
| pos += video_size_row; |
| return; |
| } else |
| scrup(currcons,top,bottom); |
| need_wrap = 0; |
| } |
| |
| static void ri(int currcons) |
| { |
| if (y>top) { |
| y--; |
| pos -= video_size_row; |
| return; |
| } else |
| scrdown(currcons,top,bottom); |
| need_wrap = 0; |
| } |
| |
| static inline void cr(int currcons) |
| { |
| pos -= x<<1; |
| need_wrap = x = 0; |
| } |
| |
| static inline void bs(int currcons) |
| { |
| if (x) { |
| pos -= 2; |
| x--; |
| need_wrap = 0; |
| } |
| } |
| |
| static inline void del(int currcons) |
| { |
| if (x) { |
| pos -= 2; |
| x--; |
| *(unsigned short *)pos = video_erase_char; |
| need_wrap = 0; |
| } |
| } |
| |
| static void csi_J(int currcons, int vpar) |
| { |
| unsigned long count; |
| unsigned long start; |
| |
| switch (vpar) { |
| case 0: /* erase from cursor to end of display */ |
| count = (scr_end-pos)>>1; |
| start = pos; |
| break; |
| case 1: /* erase from start to cursor */ |
| count = ((pos-origin)>>1)+1; |
| start = origin; |
| break; |
| case 2: /* erase whole display */ |
| count = video_num_columns * video_num_lines; |
| start = origin; |
| break; |
| default: |
| return; |
| } |
| __asm__("cld\n\t" |
| "rep\n\t" |
| "stosw\n\t" |
| ::"c" (count), |
| "D" (start),"a" (video_erase_char) |
| :"cx","di"); |
| need_wrap = 0; |
| } |
| |
| static void csi_K(int currcons, int vpar) |
| { |
| long count; |
| long start; |
| |
| switch (vpar) { |
| case 0: /* erase from cursor to end of line */ |
| count = video_num_columns-x; |
| start = pos; |
| break; |
| case 1: /* erase from start of line to cursor */ |
| start = pos - (x<<1); |
| count = x+1; |
| break; |
| case 2: /* erase whole line */ |
| start = pos - (x<<1); |
| count = video_num_columns; |
| break; |
| default: |
| return; |
| } |
| __asm__("cld\n\t" |
| "rep\n\t" |
| "stosw\n\t" |
| ::"c" (count), |
| "D" (start),"a" (video_erase_char) |
| :"cx","di"); |
| need_wrap = 0; |
| } |
| |
| /* |
| * I hope this works. The monochrome part is untested. |
| */ |
| static void update_attr(int currcons) |
| { |
| attr = color; |
| if (can_do_color) { |
| if (underline) |
| attr = (attr & 0xf0) | ulcolor; |
| else if (intensity == 0) |
| attr = (attr & 0xf0) | halfcolor; |
| } |
| if (reverse ^ decscnm) |
| attr = (attr & 0x88) | (((attr >> 4) | (attr << 4)) & 0x77); |
| if (blink) |
| attr ^= 0x80; |
| if (intensity == 2) |
| attr ^= 0x08; |
| if (!can_do_color) { |
| if (underline) |
| attr = (attr & 0xf8) | 0x01; |
| else if (intensity == 0) |
| attr = (attr & 0xf0) | 0x08; |
| } |
| if (decscnm) |
| video_erase_char = ((color & 0x88) | (((color >> 4) | |
| (color << 4)) & 0x77) << 8) | ' '; |
| else |
| video_erase_char = (color << 8) | ' '; |
| } |
| |
| static void default_attr(int currcons) { |
| intensity = 1; |
| underline = 0; |
| reverse = 0; |
| blink = 0; |
| color = def_color; |
| } |
| |
| static void csi_m(int currcons) |
| { |
| int i; |
| |
| for (i=0;i<=npar;i++) |
| switch (par[i]) { |
| case 0: /* all attributes off */ |
| default_attr(currcons); |
| break; |
| case 1: |
| intensity = 2; |
| break; |
| case 2: |
| intensity = 0; |
| break; |
| case 4: |
| underline = 1; |
| break; |
| case 5: |
| blink = 1; |
| break; |
| case 7: |
| reverse = 1; |
| break; |
| case 21: |
| case 22: |
| intensity = 1; |
| break; |
| case 24: |
| underline = 0; |
| break; |
| case 25: |
| blink = 0; |
| break; |
| case 27: |
| reverse = 0; |
| break; |
| case 39: |
| color = (def_color & 0x0f) | background; |
| break; |
| case 49: |
| color = (def_color & 0xf0) | foreground; |
| break; |
| default: |
| if (par[i] >= 30 && par[i] <= 37) |
| color = color_table[par[i]-30] |
| | background; |
| else if (par[i] >= 40 && par[i] <= 47) |
| color = (color_table[par[i]-40]<<4) |
| | foreground; |
| break; |
| } |
| update_attr(currcons); |
| } |
| |
| static inline void hide_cursor(int currcons) |
| { |
| outb_p(14, video_port_reg); |
| outb_p(0xff&((scr_end-video_mem_base)>>9), video_port_val); |
| outb_p(15, video_port_reg); |
| outb_p(0xff&((scr_end-video_mem_base)>>1), video_port_val); |
| } |
| |
| static inline void set_cursor(int currcons) |
| { |
| if (currcons != fg_console) |
| return; |
| cli(); |
| if (deccm) { |
| outb_p(14, video_port_reg); |
| outb_p(0xff&((pos-video_mem_base)>>9), video_port_val); |
| outb_p(15, video_port_reg); |
| outb_p(0xff&((pos-video_mem_base)>>1), video_port_val); |
| } else |
| hide_cursor(currcons); |
| sti(); |
| } |
| |
| static void respond_string(char * p, int currcons, struct tty_struct * tty) |
| { |
| while (*p) { |
| PUTCH(*p,tty->read_q); |
| p++; |
| } |
| TTY_READ_FLUSH(tty); |
| } |
| |
| static void respond_num(unsigned int n, int currcons, struct tty_struct * tty) |
| { |
| char buff[3]; |
| int i = 0; |
| |
| do { |
| buff[i++] = (n%10)+'0'; |
| n /= 10; |
| } while(n && i < 3); /* We'll take no chances */ |
| while (i--) { |
| PUTCH(buff[i],tty->read_q); |
| } |
| /* caller must flush */ |
| } |
| |
| static void cursor_report(int currcons, struct tty_struct * tty) |
| { |
| PUTCH('\033', tty->read_q); |
| PUTCH('[', tty->read_q); |
| respond_num(y + (decom ? top+1 : 1), currcons, tty); |
| PUTCH(';', tty->read_q); |
| respond_num(x+1, currcons, tty); |
| PUTCH('R', tty->read_q); |
| TTY_READ_FLUSH(tty); |
| } |
| |
| static inline void status_report(int currcons, struct tty_struct * tty) |
| { |
| respond_string("\033[0n", currcons, tty); /* Terminal ok */ |
| } |
| |
| static inline void respond_ID(int currcons, struct tty_struct * tty) |
| { |
| respond_string(VT102ID, currcons, tty); |
| } |
| |
| static void invert_screen(int currcons) { |
| unsigned char *p; |
| |
| if (can_do_color) |
| for (p = (unsigned char *)origin+1; p < (unsigned char *)scr_end; p+=2) |
| *p = (*p & 0x88) | (((*p >> 4) | (*p << 4)) & 0x77); |
| else |
| for (p = (unsigned char *)origin+1; p < (unsigned char *)scr_end; p+=2) |
| *p ^= *p & 0x07 == 1 ? 0x70 : 0x77; |
| } |
| |
| static void set_mode(int currcons, int on_off) |
| { |
| int i; |
| |
| for (i=0; i<=npar; i++) |
| if (ques) switch(par[i]) { /* DEC private modes set/reset */ |
| case 1: /* Cursor keys send ^[Ox/^[[x */ |
| SET(decckm,ckmode,on_off); |
| break; |
| case 3: /* 80/132 mode switch unimplemented */ |
| csi_J(currcons,2); |
| gotoxy(currcons,0,0); |
| break; |
| case 5: /* Inverted screen on/off */ |
| if (decscnm != on_off) { |
| decscnm = on_off; |
| invert_screen(currcons); |
| update_attr(currcons); |
| } |
| break; |
| case 6: /* Origin relative/absolute */ |
| decom = on_off; |
| gotoxy(currcons,0,0); |
| break; |
| case 7: /* Autowrap on/off */ |
| decawm = on_off; |
| break; |
| case 8: /* Autorepeat on/off */ |
| SET(decarm,krepeat,on_off); |
| break; |
| case 25: /* Cursor on/off */ |
| deccm = on_off; |
| set_cursor(currcons); |
| break; |
| } else switch(par[i]) { /* ANSI modes set/reset */ |
| case 4: /* Insert Mode on/off */ |
| decim = on_off; |
| break; |
| case 20: /* Lf, Enter == CrLf/Lf */ |
| SET(lnm,lfnlmode,on_off); |
| break; |
| } |
| } |
| |
| static void setterm_command(int currcons) |
| { |
| switch(par[0]) { |
| case 1: /* set color for underline mode */ |
| if (can_do_color && par[1] < 16) { |
| ulcolor = color_table[par[1]]; |
| if (underline) |
| update_attr(currcons); |
| } |
| break; |
| case 2: /* set color for half intensity mode */ |
| if (can_do_color && par[1] < 16) { |
| halfcolor = color_table[par[1]]; |
| if (intensity == 0) |
| update_attr(currcons); |
| } |
| break; |
| case 8: /* store colors as defaults */ |
| def_color = attr; |
| default_attr(currcons); |
| update_attr(currcons); |
| break; |
| case 9: /* set blanking interval */ |
| blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ; |
| break; |
| } |
| } |
| |
| static void insert_char(int currcons) |
| { |
| unsigned int i = x; |
| unsigned short tmp, old = video_erase_char; |
| unsigned short * p = (unsigned short *) pos; |
| |
| while (i++ < video_num_columns) { |
| tmp = *p; |
| *p = old; |
| old = tmp; |
| p++; |
| } |
| need_wrap = 0; |
| } |
| |
| static void insert_line(int currcons) |
| { |
| scrdown(currcons,y,bottom); |
| need_wrap = 0; |
| } |
| |
| static void delete_char(int currcons) |
| { |
| unsigned int i = x; |
| unsigned short * p = (unsigned short *) pos; |
| |
| while (++i < video_num_columns) { |
| *p = *(p+1); |
| p++; |
| } |
| *p = video_erase_char; |
| need_wrap = 0; |
| } |
| |
| static void delete_line(int currcons) |
| { |
| scrup(currcons,y,bottom); |
| need_wrap = 0; |
| } |
| |
| static void csi_at(int currcons, unsigned int nr) |
| { |
| if (nr > video_num_columns) |
| nr = video_num_columns; |
| else if (!nr) |
| nr = 1; |
| while (nr--) |
| insert_char(currcons); |
| } |
| |
| static void csi_L(int currcons, unsigned int nr) |
| { |
| if (nr > video_num_lines) |
| nr = video_num_lines; |
| else if (!nr) |
| nr = 1; |
| while (nr--) |
| insert_line(currcons); |
| } |
| |
| static void csi_P(int currcons, unsigned int nr) |
| { |
| if (nr > video_num_columns) |
| nr = video_num_columns; |
| else if (!nr) |
| nr = 1; |
| while (nr--) |
| delete_char(currcons); |
| } |
| |
| static void csi_M(int currcons, unsigned int nr) |
| { |
| if (nr > video_num_lines) |
| nr = video_num_lines; |
| else if (!nr) |
| nr=1; |
| while (nr--) |
| delete_line(currcons); |
| } |
| |
| static void save_cur(int currcons) |
| { |
| saved_x = x; |
| saved_y = y; |
| s_intensity = intensity; |
| s_underline = underline; |
| s_blink = blink; |
| s_reverse = reverse; |
| s_charset = charset; |
| s_color = color; |
| saved_G0 = G0_charset; |
| saved_G1 = G1_charset; |
| } |
| |
| static void restore_cur(int currcons) |
| { |
| gotoxy(currcons,saved_x,saved_y); |
| intensity = s_intensity; |
| underline = s_underline; |
| blink = s_blink; |
| reverse = s_reverse; |
| charset = s_charset; |
| color = s_color; |
| G0_charset = saved_G0; |
| G1_charset = saved_G1; |
| translate = charset ? G1_charset : G0_charset; |
| update_attr(currcons); |
| need_wrap = 0; |
| } |
| |
| enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey, |
| EShash, ESsetG0, ESsetG1, ESignore }; |
| |
| static void reset_terminal(int currcons, int do_clear) |
| { |
| top = 0; |
| bottom = video_num_lines; |
| state = ESnormal; |
| ques = 0; |
| translate = NORM_TRANS; |
| G0_charset = NORM_TRANS; |
| G1_charset = GRAF_TRANS; |
| charset = 0; |
| need_wrap = 0; |
| |
| decscnm = 0; |
| decom = 0; |
| decawm = 1; |
| deccm = 1; |
| decim = 0; |
| |
| if (currcons == fg_console) { |
| krepeat = 1; |
| ckmode = 0; |
| kapplic = 0; |
| lfnlmode = 0; |
| kleds = 2; |
| kmode = 0; |
| set_leds(); |
| } else { |
| decarm = 1; |
| decckm = 0; |
| kbdapplic = 0; |
| lnm = 0; |
| kbdleds = 2; |
| kbdmode = 0; |
| } |
| |
| default_attr(currcons); |
| update_attr(currcons); |
| |
| tab_stop[0] = 0x01010100; |
| tab_stop[1] = |
| tab_stop[2] = |
| tab_stop[3] = |
| tab_stop[4] = 0x01010101; |
| |
| if (do_clear) { |
| gotoxy(currcons,0,0); |
| csi_J(currcons,2); |
| save_cur(currcons); |
| } |
| } |
| |
| void con_write(struct tty_struct * tty) |
| { |
| int c; |
| unsigned int currcons; |
| |
| wake_up(&tty->write_q->proc_list); |
| currcons = tty - tty_table; |
| if (currcons >= NR_CONSOLES) { |
| printk("con_write: illegal tty\n\r"); |
| return; |
| } |
| while (!tty->stopped && (c = GETCH(tty->write_q)) >= 0) { |
| if (state == ESnormal && translate[c]) { |
| if (need_wrap) { |
| cr(currcons); |
| lf(currcons); |
| } |
| if (decim) |
| insert_char(currcons); |
| c = translate[c]; |
| *(char *) pos = c; |
| *(char *) (pos+1) = attr; |
| if (x == video_num_columns - 1) |
| need_wrap = decawm; |
| else { |
| x++; |
| pos+=2; |
| } |
| continue; |
| } |
| |
| /* |
| * Control characters can be used in the _middle_ |
| * of an escape sequence. |
| */ |
| if (c < 32 || c == 127) switch(c) { |
| case 7: |
| sysbeep(); |
| break; |
| case 8: |
| bs(currcons); |
| break; |
| case 9: |
| pos -= (x << 1); |
| while (x < video_num_columns - 1) { |
| x++; |
| if (tab_stop[x >> 5] & (1 << (x & 31))) |
| break; |
| } |
| pos += (x << 1); |
| break; |
| case 10: case 11: case 12: |
| lf(currcons); |
| if (!lfnlmode) |
| break; |
| case 13: |
| cr(currcons); |
| break; |
| case 14: |
| charset = 1; |
| translate = G1_charset; |
| break; |
| case 15: |
| charset = 0; |
| translate = G0_charset; |
| break; |
| case 24: case 26: |
| state = ESnormal; |
| break; |
| case 27: |
| state = ESesc; |
| break; |
| case 127: |
| del(currcons); |
| break; |
| } else switch(state) { |
| case ESesc: |
| state = ESnormal; |
| switch (c) { |
| case '[': |
| state = ESsquare; |
| break; |
| case 'E': |
| cr(currcons); |
| lf(currcons); |
| break; |
| case 'M': |
| ri(currcons); |
| break; |
| case 'D': |
| lf(currcons); |
| break; |
| case 'H': |
| tab_stop[x >> 5] |= (1 << (x & 31)); |
| break; |
| case 'Z': |
| respond_ID(currcons,tty); |
| break; |
| case '7': |
| save_cur(currcons); |
| break; |
| case '8': |
| restore_cur(currcons); |
| break; |
| case '(': |
| state = ESsetG0; |
| break; |
| case ')': |
| state = ESsetG1; |
| break; |
| case '#': |
| state = EShash; |
| break; |
| case 'c': |
| reset_terminal(currcons,1); |
| break; |
| case '>': /* Numeric keypad */ |
| SET(kbdapplic,kapplic,0); |
| break; |
| case '=': /* Appl. keypad */ |
| SET(kbdapplic,kapplic,1); |
| break; |
| } |
| break; |
| case ESsquare: |
| for(npar = 0 ; npar < NPAR ; npar++) |
| par[npar] = 0; |
| npar = 0; |
| state = ESgetpars; |
| if (c == '[') { /* Function key */ |
| state=ESfunckey; |
| break; |
| } |
| if (ques=(c=='?')) |
| break; |
| case ESgetpars: |
| if (c==';' && npar<NPAR-1) { |
| npar++; |
| break; |
| } else if (c>='0' && c<='9') { |
| par[npar] *= 10; |
| par[npar] += c-'0'; |
| break; |
| } else state=ESgotpars; |
| case ESgotpars: |
| state = ESnormal; |
| switch(c) { |
| case 'h': |
| set_mode(currcons,1); |
| break; |
| case 'l': |
| set_mode(currcons,0); |
| break; |
| case 'n': |
| if (!ques) |
| if (par[0] == 5) |
| status_report(currcons,tty); |
| else if (par[0] == 6) |
| cursor_report(currcons,tty); |
| break; |
| } |
| if (ques) { |
| ques = 0; |
| break; |
| } |
| switch(c) { |
| case 'G': case '`': |
| if (par[0]) par[0]--; |
| gotoxy(currcons,par[0],y); |
| break; |
| case 'A': |
| if (!par[0]) par[0]++; |
| gotoxy(currcons,x,y-par[0]); |
| break; |
| case 'B': case 'e': |
| if (!par[0]) par[0]++; |
| gotoxy(currcons,x,y+par[0]); |
| break; |
| case 'C': case 'a': |
| if (!par[0]) par[0]++; |
| gotoxy(currcons,x+par[0],y); |
| break; |
| case 'D': |
| if (!par[0]) par[0]++; |
| gotoxy(currcons,x-par[0],y); |
| break; |
| case 'E': |
| if (!par[0]) par[0]++; |
| gotoxy(currcons,0,y+par[0]); |
| break; |
| case 'F': |
| if (!par[0]) par[0]++; |
| gotoxy(currcons,0,y-par[0]); |
| break; |
| case 'd': |
| if (par[0]) par[0]--; |
| gotoxy(currcons,x,par[0]); |
| break; |
| case 'H': case 'f': |
| if (par[0]) par[0]--; |
| if (par[1]) par[1]--; |
| gotoxy(currcons,par[1],par[0]); |
| break; |
| case 'J': |
| csi_J(currcons,par[0]); |
| break; |
| case 'K': |
| csi_K(currcons,par[0]); |
| break; |
| case 'L': |
| csi_L(currcons,par[0]); |
| break; |
| case 'M': |
| csi_M(currcons,par[0]); |
| break; |
| case 'P': |
| csi_P(currcons,par[0]); |
| break; |
| case 'c': |
| if (!par[0]) |
| respond_ID(currcons,tty); |
| break; |
| case 'g': |
| if (!par[0]) |
| tab_stop[x >> 5] &= ~(1 << (x & 31)); |
| else if (par[0] == 3) { |
| tab_stop[0] = |
| tab_stop[1] = |
| tab_stop[2] = |
| tab_stop[3] = |
| tab_stop[4] = 0; |
| } |
| break; |
| case 'm': |
| csi_m(currcons); |
| break; |
| case 'r': |
| if (!par[0]) |
| par[0]++; |
| if (!par[1]) |
| par[1] = video_num_lines; |
| /* Minimum allowed region is 2 lines */ |
| if (par[0] < par[1] && |
| par[1] <= video_num_lines) { |
| top=par[0]-1; |
| bottom=par[1]; |
| gotoxy(currcons,0,0); |
| } |
| break; |
| case 's': |
| save_cur(currcons); |
| break; |
| case 'u': |
| restore_cur(currcons); |
| break; |
| case '@': |
| csi_at(currcons,par[0]); |
| break; |
| case ']': /* setterm functions */ |
| setterm_command(currcons); |
| break; |
| } |
| break; |
| case ESfunckey: |
| state = ESnormal; |
| break; |
| case EShash: |
| state = ESnormal; |
| if (c == '8') { |
| /* DEC screen alignment test. kludge :-) */ |
| video_erase_char = |
| (video_erase_char & 0xff00) | 'E'; |
| csi_J(currcons, 2); |
| video_erase_char = |
| (video_erase_char & 0xff00) | ' '; |
| } |
| break; |
| case ESsetG0: |
| if (c == '0') |
| G0_charset = GRAF_TRANS; |
| else if (c == 'B') |
| G0_charset = NORM_TRANS; |
| if (charset == 0) |
| translate = G0_charset; |
| state = ESnormal; |
| break; |
| case ESsetG1: |
| if (c == '0') |
| G1_charset = GRAF_TRANS; |
| else if (c == 'B') |
| G1_charset = NORM_TRANS; |
| if (charset == 1) |
| translate = G1_charset; |
| state = ESnormal; |
| break; |
| default: |
| state = ESnormal; |
| } |
| } |
| timer_active &= ~(1<<BLANK_TIMER); |
| if (vtmode == KD_GRAPHICS) |
| return; |
| set_cursor(currcons); |
| if (currcons == fg_console) |
| if (console_blanked) { |
| timer_table[BLANK_TIMER].expires = 0; |
| timer_active |= 1<<BLANK_TIMER; |
| } else if (blankinterval) { |
| timer_table[BLANK_TIMER].expires = jiffies + blankinterval; |
| timer_active |= 1<<BLANK_TIMER; |
| } |
| } |
| |
| void do_keyboard_interrupt(void) |
| { |
| TTY_READ_FLUSH(TTY_TABLE(0)); |
| timer_active &= ~(1<<BLANK_TIMER); |
| if (vt_cons[fg_console].vt_mode == KD_GRAPHICS) |
| return; |
| if (console_blanked) { |
| timer_table[BLANK_TIMER].expires = 0; |
| timer_active |= 1<<BLANK_TIMER; |
| } else if (blankinterval) { |
| timer_table[BLANK_TIMER].expires = jiffies + blankinterval; |
| timer_active |= 1<<BLANK_TIMER; |
| } |
| } |
| |
| void * memsetw(void * s,unsigned short c,int count) |
| { |
| __asm__("cld\n\t" |
| "rep\n\t" |
| "stosw" |
| ::"a" (c),"D" (s),"c" (count) |
| :"cx","di"); |
| return s; |
| } |
| |
| /* |
| * long con_init(long); |
| * |
| * This routine initalizes console interrupts, and does nothing |
| * else. If you want the screen to clear, call tty_write with |
| * the appropriate escape-sequece. |
| * |
| * Reads the information preserved by setup.s to determine the current display |
| * type and sets everything accordingly. |
| */ |
| long con_init(long kmem_start) |
| { |
| register unsigned char a; |
| char *display_desc = "????"; |
| char *display_ptr; |
| int currcons = 0; |
| long base; |
| int orig_x = ORIG_X; |
| int orig_y = ORIG_Y; |
| |
| vc_scrmembuf = (unsigned short *) kmem_start; |
| video_num_columns = ORIG_VIDEO_COLS; |
| video_size_row = video_num_columns * 2; |
| video_num_lines = ORIG_VIDEO_LINES; |
| video_page = ORIG_VIDEO_PAGE; |
| screen_size = (video_num_lines * video_size_row); |
| kmem_start += NR_CONSOLES * screen_size; |
| timer_table[BLANK_TIMER].fn = blank_screen; |
| timer_table[BLANK_TIMER].expires = 0; |
| if (blankinterval) { |
| timer_table[BLANK_TIMER].expires = jiffies+blankinterval; |
| timer_active |= 1<<BLANK_TIMER; |
| } |
| |
| if (ORIG_VIDEO_MODE == 7) /* Is this a monochrome display? */ |
| { |
| video_mem_base = 0xb0000; |
| video_port_reg = 0x3b4; |
| video_port_val = 0x3b5; |
| if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) |
| { |
| video_type = VIDEO_TYPE_EGAM; |
| video_mem_term = 0xb8000; |
| display_desc = "EGAm"; |
| } |
| else |
| { |
| video_type = VIDEO_TYPE_MDA; |
| video_mem_term = 0xb2000; |
| display_desc = "*MDA"; |
| } |
| } |
| else /* If not, it is color. */ |
| { |
| can_do_color = 1; |
| video_mem_base = 0xb8000; |
| video_port_reg = 0x3d4; |
| video_port_val = 0x3d5; |
| if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) |
| { |
| video_type = VIDEO_TYPE_EGAC; |
| video_mem_term = 0xc0000; |
| display_desc = "EGAc"; |
| } |
| else |
| { |
| video_type = VIDEO_TYPE_CGA; |
| video_mem_term = 0xba000; |
| display_desc = "*CGA"; |
| } |
| } |
| |
| /* Let the user know what kind of display driver we are using */ |
| |
| display_ptr = ((char *)video_mem_base) + video_size_row - 8; |
| while (*display_desc) |
| { |
| *display_ptr++ = *display_desc++; |
| display_ptr++; |
| } |
| |
| /* Initialize the variables used for scrolling (mostly EGA/VGA) */ |
| |
| base = (long)vc_scrmembuf; |
| for (currcons = 0; currcons<NR_CONSOLES; currcons++) { |
| pos = origin = video_mem_start = base; |
| scr_end = video_mem_end = (base += screen_size); |
| vc_scrbuf[currcons] = (unsigned short *) origin; |
| vtmode = KD_TEXT; |
| kbdraw = 0; |
| def_color = 0x07; /* white */ |
| ulcolor = 0x0f; /* bold white */ |
| halfcolor = 0x08; /* grey */ |
| reset_terminal(currcons, currcons); |
| } |
| currcons = fg_console = 0; |
| |
| video_mem_start = video_mem_base; |
| video_mem_end = video_mem_term; |
| origin = video_mem_start; |
| scr_end = video_mem_start + video_num_lines * video_size_row; |
| gotoxy(currcons,0,0); |
| save_cur(currcons); |
| gotoxy(currcons,orig_x,orig_y); |
| update_screen(fg_console); |
| |
| set_trap_gate(0x21,&keyboard_interrupt); |
| outb_p(inb_p(0x21)&0xfd,0x21); |
| a=inb_p(0x61); |
| outb_p(a|0x80,0x61); |
| outb_p(a,0x61); |
| return kmem_start; |
| } |
| |
| void kbdsave(int new_console) |
| { |
| int currcons = fg_console; |
| kbdmode = kmode; |
| kbdraw = kraw; |
| kbdleds = kleds; |
| kbdapplic = kapplic; |
| decckm = ckmode; |
| decarm = krepeat; |
| lnm = lfnlmode; |
| currcons = new_console; |
| kmode = (kmode & 0x3F) | (kbdmode & 0xC0); |
| kraw = kbdraw; |
| kleds = kbdleds; |
| kapplic = kbdapplic; |
| ckmode = decckm; |
| krepeat = decarm; |
| lfnlmode = lnm; |
| set_leds(); |
| } |
| |
| static void get_scrmem(int currcons) |
| { |
| memcpy((void *)vc_scrbuf[fg_console],(void *)origin, screen_size); |
| video_mem_start = (unsigned long)vc_scrbuf[fg_console]; |
| origin = video_mem_start; |
| scr_end = video_mem_end = video_mem_start+screen_size; |
| pos = origin + y*video_size_row + (x<<1); |
| } |
| |
| static void set_scrmem(int currcons) |
| { |
| video_mem_start = video_mem_base; |
| video_mem_end = video_mem_term; |
| origin = video_mem_start; |
| scr_end = video_mem_start + screen_size; |
| pos = origin + y*video_size_row + (x<<1); |
| memcpy((void *)video_mem_base, (void *)vc_scrbuf[fg_console], screen_size); |
| } |
| |
| void blank_screen(void) |
| { |
| timer_table[BLANK_TIMER].fn = unblank_screen; |
| get_scrmem(fg_console); |
| hide_cursor(fg_console); |
| console_blanked = 1; |
| memsetw((void *)video_mem_base, 0x0020, video_mem_term-video_mem_base ); |
| } |
| |
| void unblank_screen(void) |
| { |
| timer_table[BLANK_TIMER].fn = blank_screen; |
| if (blankinterval) { |
| timer_table[BLANK_TIMER].expires = jiffies + blankinterval; |
| timer_active |= 1<<BLANK_TIMER; |
| } |
| console_blanked = 0; |
| set_scrmem(fg_console); |
| set_origin(fg_console); |
| set_cursor(fg_console); |
| } |
| |
| void update_screen(int new_console) |
| { |
| static int lock = 0; |
| |
| if (new_console == fg_console || lock) |
| return; |
| lock = 1; |
| kbdsave(new_console); |
| get_scrmem(fg_console); |
| fg_console = new_console; |
| set_scrmem(fg_console); |
| set_origin(fg_console); |
| set_cursor(new_console); |
| lock = 0; |
| } |
| |
| /* from bsd-net-2: */ |
| |
| static void sysbeepstop(void) |
| { |
| /* disable counter 2 */ |
| outb(inb_p(0x61)&0xFC, 0x61); |
| } |
| |
| static void sysbeep(void) |
| { |
| /* enable counter 2 */ |
| outb_p(inb_p(0x61)|3, 0x61); |
| /* set command for counter 2, 2 byte write */ |
| outb_p(0xB6, 0x43); |
| /* send 0x637 for 750 HZ */ |
| outb_p(0x37, 0x42); |
| outb(0x06, 0x42); |
| /* 1/8 second */ |
| timer_table[BEEP_TIMER].expires = jiffies + HZ/8; |
| timer_table[BEEP_TIMER].fn = sysbeepstop; |
| timer_active |= 1<<BEEP_TIMER; |
| } |
| |
| int do_screendump(int arg) |
| { |
| char *sptr, *buf = (char *)arg; |
| int currcons, l; |
| |
| verify_area(buf,2+video_num_columns*video_num_lines); |
| currcons = get_fs_byte(buf+1); |
| if ((currcons<0) || (currcons>=NR_CONSOLES)) |
| return -EIO; |
| put_fs_byte((char)(video_num_lines),buf++); |
| put_fs_byte((char)(video_num_columns),buf++); |
| currcons = (currcons ? currcons-1 : fg_console); |
| sptr = (char *) origin; |
| for (l=video_num_lines*video_num_columns; l>0 ; l--, sptr++) |
| put_fs_byte(*sptr++,buf++); |
| return(0); |
| } |
| |
| void console_print(const char * b) |
| { |
| int currcons = fg_console; |
| char c; |
| |
| if (currcons<0 || currcons>=NR_CONSOLES) |
| currcons = 0; |
| while (c = *(b++)) { |
| if (c == 10 || c == 13 || need_wrap) { |
| cr(currcons); |
| if (c == 10 || need_wrap) |
| lf(currcons); |
| need_wrap = 0; |
| continue; |
| } |
| *(char *) pos = c; |
| *(char *) (pos+1) = attr; |
| if (x == video_num_columns - 1) { |
| need_wrap = 1; |
| continue; |
| } |
| x++; |
| pos+=2; |
| } |
| set_cursor(currcons); |
| } |