/* * linux/drivers/char/console.c * * Copyright (C) 1991, 1992 Linus Torvalds */ /* * 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. * * Copy and paste function by Andrew Haylett, * some enhancements by Alessandro Rubini. * * Code to check for different video-cards mostly by Galen Hunt, * <g-hunt@ee.utah.edu> * * Rudimentary ISO 10646/Unicode/UTF-8 character set support by * Markus Kuhn, <mskuhn@immd4.informatik.uni-erlangen.de>. * * Dynamic allocation of consoles, aeb@cwi.nl, May 1994 * Resizing of consoles, aeb, 940926 * * Code for xterm like mouse click reporting by Peter Orbaek 20-Jul-94 * <poe@daimi.aau.dk> * * User-defined bell sound, new setterm control sequences and printk * redirection by Martin Mares <mj@k332.feld.cvut.cz> 19-Nov-95 * * APM screenblank bug fixed Takashi Manabe <manabe@roy.dsl.tutics.tut.jp> * * Merge with the abstract console driver by Geert Uytterhoeven * <geert@linux-m68k.org>, Jan 1997. * * Original m68k console driver modifications by * * - Arno Griffioen <arno@usn.nl> * - David Carter <carter@cs.bris.ac.uk> * * Note that the abstract console driver allows all consoles to be of * potentially different sizes, so the following variables depend on the * current console (currcons): * * - video_num_columns * - video_num_lines * - video_size_row * - can_do_color * * The abstract console driver provides a generic interface for a text * console. It supports VGA text mode, frame buffer based graphical consoles * and special graphics processors that are only accessible through some * registers (e.g. a TMS340x0 GSP). * * The interface to the hardware is specified using a special structure * (struct consw) which contains function pointers to console operations * (see <linux/console.h> for more information). * * Support for changeable cursor shape * by Pavel Machek <pavel@atrey.karlin.mff.cuni.cz>, August 1997 * * Ported to i386 and con_scrolldelta fixed * by Emmanuel Marty <core@ggi-project.org>, April 1998 * * Resurrected character buffers in videoram plus lots of other trickery * by Martin Mares <mj@atrey.karlin.mff.cuni.cz>, July 1998 * * Removed old-style timers, introduced console_timer, made timer * deletion SMP-safe. 17Jun00, Andrew Morton <andrewm@uow.edu.au> */ #include <linux/module.h> #include <linux/sched.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/kd.h> #include <linux/malloc.h> #include <linux/major.h> #include <linux/mm.h> #include <linux/console.h> #include <linux/init.h> #include <linux/devfs_fs_kernel.h> #include <linux/vt_kern.h> #include <linux/selection.h> #include <linux/console_struct.h> #include <linux/kbd_kern.h> #include <linux/consolemap.h> #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/config.h> #include <linux/version.h> #include <linux/tqueue.h> #include <linux/bootmem.h> #include <linux/pm.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> #include <asm/bitops.h> #include <asm/linux_logo.h> #include "console_macros.h" const struct consw *conswitchp; /* A bitmap for codes <32. A bit of 1 indicates that the code * corresponding to that bit number invokes some special action * (such as cursor movement) and should not be displayed as a * glyph unless the disp_ctrl mode is explicitly enabled. */ #define CTRL_ACTION 0x0d00ff81 #define CTRL_ALWAYS 0x0800f501 /* Cannot be overridden by disp_ctrl */ /* * Here is the default bell parameters: 750HZ, 1/8th of a second */ #define DEFAULT_BELL_PITCH 750 #define DEFAULT_BELL_DURATION (HZ/8) extern void vcs_make_devfs (unsigned int index, int unregister); #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif static struct tty_struct *console_table[MAX_NR_CONSOLES]; static struct termios *console_termios[MAX_NR_CONSOLES]; static struct termios *console_termios_locked[MAX_NR_CONSOLES]; struct vc vc_cons [MAX_NR_CONSOLES]; #ifndef VT_SINGLE_DRIVER static const struct consw *con_driver_map[MAX_NR_CONSOLES]; #endif static int con_open(struct tty_struct *, struct file *); static void vc_init(unsigned int console, unsigned int rows, unsigned int cols, int do_clear); static void blank_screen(unsigned long dummy); static void gotoxy(int currcons, int new_x, int new_y); static void save_cur(int currcons); static void reset_terminal(int currcons, int do_clear); static void con_flush_chars(struct tty_struct *tty); static void set_vesa_blanking(unsigned long arg); static void set_cursor(int currcons); static void hide_cursor(int currcons); static void unblank_screen_t(unsigned long dummy); static int printable; /* Is console ready for printing? */ int do_poke_blanked_console; int console_blanked; static int vesa_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */ static int blankinterval = 10*60*HZ; static int vesa_off_interval; /* * fg_console is the current virtual console, * last_console is the last used one, * want_console is the console we want to switch to, * kmsg_redirect is the console for kernel messages, */ int fg_console; int last_console; int want_console = -1; int kmsg_redirect; /* * For each existing display, we have a pointer to console currently visible * on that display, allowing consoles other than fg_console to be refreshed * appropriately. Unless the low-level driver supplies its own display_fg * variable, we use this one for the "master display". */ static struct vc_data *master_display_fg; /* * Unfortunately, we need to delay tty echo when we're currently writing to the * console since the code is (and always was) not re-entrant, so we insert * all filp requests to con_task_queue instead of tq_timer and run it from * the console_tasklet. The console_tasklet is protected by the IRQ * protected console_lock. */ DECLARE_TASK_QUEUE(con_task_queue); /* * For the same reason, we defer scrollback to the console tasklet. */ static int scrollback_delta; /* * Hook so that the power management routines can (un)blank * the console on our behalf. */ int (*console_blank_hook)(int); static struct timer_list console_timer; /* * Low-Level Functions */ #define IS_FG (currcons == fg_console) #define IS_VISIBLE CON_IS_VISIBLE(vc_cons[currcons].d) #ifdef VT_BUF_VRAM_ONLY #define DO_UPDATE 0 #else #define DO_UPDATE IS_VISIBLE #endif static int pm_con_request(struct pm_dev *dev, pm_request_t rqst, void *data); static struct pm_dev *pm_con; 221 static inline unsigned short *screenpos(int currcons, int offset, int viewed) { unsigned short *p; 225 if (!viewed) p = (unsigned short *)(origin + offset); 227 else if (!sw->con_screen_pos) p = (unsigned short *)(visible_origin + offset); 229 else p = sw->con_screen_pos(vc_cons[currcons].d, offset); 231 return p; } 234 static inline void scrolldelta(int lines) { scrollback_delta += lines; tasklet_schedule(&console_tasklet); } 240 static void scrup(int currcons, unsigned int t, unsigned int b, int nr) { unsigned short *d, *s; 244 if (t+nr >= b) nr = b - t - 1; 246 if (b > video_num_lines || t >= b || nr < 1) 247 return; 248 if (IS_VISIBLE && sw->con_scroll(vc_cons[currcons].d, t, b, SM_UP, nr)) 249 return; d = (unsigned short *) (origin+video_size_row*t); s = (unsigned short *) (origin+video_size_row*(t+nr)); scr_memcpyw(d, s, (b-t-nr) * video_size_row); scr_memsetw(d + (b-t-nr) * video_num_columns, video_erase_char, video_size_row*nr); } static void 257 scrdown(int currcons, unsigned int t, unsigned int b, int nr) { unsigned short *s; unsigned int step; 262 if (t+nr >= b) nr = b - t - 1; 264 if (b > video_num_lines || t >= b || nr < 1) 265 return; 266 if (IS_VISIBLE && sw->con_scroll(vc_cons[currcons].d, t, b, SM_DOWN, nr)) 267 return; s = (unsigned short *) (origin+video_size_row*t); step = video_num_columns * nr; scr_memmovew(s + step, s, (b-t-nr)*video_size_row); scr_memsetw(s, video_erase_char, 2*step); } 274 static void do_update_region(int currcons, unsigned long start, int count) { #ifndef VT_BUF_VRAM_ONLY unsigned int xx, yy, offset; u16 *p; p = (u16 *) start; 281 if (!sw->con_getxy) { offset = (start - origin) / 2; xx = offset % video_num_columns; yy = offset / video_num_columns; 285 } else { int nxx, nyy; start = sw->con_getxy(vc_cons[currcons].d, start, &nxx, &nyy); xx = nxx; yy = nyy; } 290 for(;;) { u16 attrib = scr_readw(p) & 0xff00; int startx = xx; u16 *q = p; 294 while (xx < video_num_columns && count) { 295 if (attrib != (scr_readw(p) & 0xff00)) { 296 if (p > q) sw->con_putcs(vc_cons[currcons].d, q, p-q, yy, startx); startx = xx; q = p; attrib = scr_readw(p) & 0xff00; } p++; xx++; count--; } 306 if (p > q) sw->con_putcs(vc_cons[currcons].d, q, p-q, yy, startx); 308 if (!count) 309 break; xx = 0; yy++; 312 if (sw->con_getxy) { p = (u16 *)start; start = sw->con_getxy(vc_cons[currcons].d, start, NULL, NULL); } } #endif } 320 void update_region(int currcons, unsigned long start, int count) { 322 if (DO_UPDATE) { hide_cursor(currcons); do_update_region(currcons, start, count); set_cursor(currcons); } } /* Structure of attributes is hardware-dependent */ 331 static u8 build_attr(int currcons, u8 _color, u8 _intensity, u8 _blink, u8 _underline, u8 _reverse) { 333 if (sw->con_build_attr) 334 return sw->con_build_attr(vc_cons[currcons].d, _color, _intensity, _blink, _underline, _reverse); #ifndef VT_BUF_VRAM_ONLY /* * ++roman: I completely changed the attribute format for monochrome * mode (!can_do_color). The formerly used MDA (monochrome display * adapter) format didn't allow the combination of certain effects. * Now the attribute is just a bit vector: * Bit 0..1: intensity (0..2) * Bit 2 : underline * Bit 3 : reverse * Bit 7 : blink */ { u8 a = color; 349 if (!can_do_color) return _intensity | (_underline ? 4 : 0) | (_reverse ? 8 : 0) | 353 (_blink ? 0x80 : 0); 354 if (_underline) a = (a & 0xf0) | ulcolor; 356 else if (_intensity == 0) a = (a & 0xf0) | halfcolor; 358 if (_reverse) a = ((a) & 0x88) | ((((a) >> 4) | ((a) << 4)) & 0x77); 360 if (_blink) a ^= 0x80; 362 if (_intensity == 2) a ^= 0x08; 364 if (hi_font_mask == 0x100) a <<= 1; 366 return a; } #else return 0; #endif } 373 static void update_attr(int currcons) { attr = build_attr(currcons, color, intensity, blink, underline, reverse ^ decscnm); video_erase_char = (build_attr(currcons, color, 1, blink, 0, decscnm) << 8) | ' '; } /* Note: inverting the screen twice should revert to the original state */ 381 void invert_screen(int currcons, int offset, int count, int viewed) { unsigned short *p; count /= 2; p = screenpos(currcons, offset, viewed); 387 if (sw->con_invert_region) sw->con_invert_region(vc_cons[currcons].d, p, count); #ifndef VT_BUF_VRAM_ONLY 390 else { u16 *q = p; int cnt = count; 394 if (!can_do_color) { 395 while (cnt--) *q++ ^= 0x0800; 396 } else if (hi_font_mask == 0x100) { 397 while (cnt--) { u16 a = *q; a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) | (((a) & 0x0e00) << 4); *q++ = a; } 402 } else { 403 while (cnt--) { u16 a = *q; a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4); *q++ = a; } } } #endif 411 if (DO_UPDATE) do_update_region(currcons, (unsigned long) p, count); } /* used by selection: complement pointer position */ 416 void complement_pos(int currcons, int offset) { static unsigned short *p; static unsigned short old; static unsigned short oldx, oldy; 422 if (p) { scr_writew(old, p); 424 if (DO_UPDATE) sw->con_putc(vc_cons[currcons].d, old, oldy, oldx); } 427 if (offset == -1) p = NULL; 429 else { unsigned short new; p = screenpos(currcons, offset, 1); old = scr_readw(p); new = old ^ complement_mask; scr_writew(new, p); 435 if (DO_UPDATE) { oldx = (offset >> 1) % video_num_columns; oldy = (offset >> 1) / video_num_columns; sw->con_putc(vc_cons[currcons].d, new, oldy, oldx); } } } 443 static void insert_char(int currcons, unsigned int nr) { unsigned short *p, *q = (unsigned short *) pos; p = q + video_num_columns - nr - x; 448 while (--p >= q) scr_writew(scr_readw(p), p + nr); scr_memsetw(q, video_erase_char, nr*2); need_wrap = 0; 452 if (DO_UPDATE) { unsigned short oldattr = attr; sw->con_bmove(vc_cons[currcons].d,y,x,y,x+nr,1, video_num_columns-x-nr); attr = video_erase_char >> 8; 457 while (nr--) sw->con_putc(vc_cons[currcons].d, video_erase_char,y,x+nr); attr = oldattr; } } 464 static void delete_char(int currcons, unsigned int nr) { unsigned int i = x; unsigned short *p = (unsigned short *) pos; 469 while (++i <= video_num_columns - nr) { scr_writew(scr_readw(p+nr), p); p++; } scr_memsetw(p, video_erase_char, nr*2); need_wrap = 0; 475 if (DO_UPDATE) { unsigned short oldattr = attr; sw->con_bmove(vc_cons[currcons].d, y, x+nr, y, x, 1, video_num_columns-x-nr); attr = video_erase_char >> 8; 480 while (nr--) sw->con_putc(vc_cons[currcons].d, video_erase_char, y, video_num_columns-1-nr); attr = oldattr; } } static int softcursor_original; 490 static void add_softcursor(int currcons) { int i = scr_readw((u16 *) pos); u32 type = cursor_type; 495 if (! (type & 0x10)) return; 496 if (softcursor_original != -1) return; softcursor_original = i; i |= ((type >> 8) & 0xff00 ); i ^= ((type) & 0xff00 ); 500 if ((type & 0x20) && ((softcursor_original & 0x7000) == (i & 0x7000))) i ^= 0x7000; 501 if ((type & 0x40) && ((i & 0x700) == ((i & 0x7000) >> 4))) i ^= 0x0700; scr_writew(i, (u16 *) pos); 503 if (DO_UPDATE) sw->con_putc(vc_cons[currcons].d, i, y, x); } 507 static void hide_cursor(int currcons) { 509 if (currcons == sel_cons) clear_selection(); 511 if (softcursor_original != -1) { scr_writew(softcursor_original,(u16 *) pos); 513 if (DO_UPDATE) sw->con_putc(vc_cons[currcons].d, softcursor_original, y, x); softcursor_original = -1; } sw->con_cursor(vc_cons[currcons].d,CM_ERASE); } 520 static void set_cursor(int currcons) { 522 if (!IS_FG || console_blanked || vcmode == KD_GRAPHICS) 523 return; 524 if (deccm) { 525 if (currcons == sel_cons) clear_selection(); add_softcursor(currcons); 528 if ((cursor_type & 0x0f) != 1) sw->con_cursor(vc_cons[currcons].d,CM_DRAW); 530 } else hide_cursor(currcons); } 534 static void set_origin(int currcons) { if (!IS_VISIBLE || !sw->con_set_origin || 538 !sw->con_set_origin(vc_cons[currcons].d)) origin = (unsigned long) screenbuf; visible_origin = origin; scr_end = origin + screenbuf_size; pos = origin + video_size_row*y + 2*x; } 545 static inline void save_screen(int currcons) { 547 if (sw->con_save_screen) sw->con_save_screen(vc_cons[currcons].d); } /* * Redrawing of screen */ 555 void redraw_screen(int new_console, int is_switch) { int redraw = 1; int currcons, old_console; 560 if (!vc_cons_allocated(new_console)) { /* strange ... */ /* printk("redraw_screen: tty %d not allocated ??\n", new_console+1); */ 563 return; } 566 if (is_switch) { currcons = fg_console; hide_cursor(currcons); 569 if (fg_console != new_console) { struct vc_data **display = vc_cons[new_console].d->vc_display_fg; old_console = (*display) ? (*display)->vc_num : fg_console; *display = vc_cons[new_console].d; fg_console = new_console; currcons = old_console; 575 if (!IS_VISIBLE) { save_screen(currcons); set_origin(currcons); } currcons = new_console; 580 if (old_console == new_console) redraw = 0; } 583 } else { currcons = new_console; hide_cursor(currcons); } 588 if (redraw) { int update; set_origin(currcons); update = sw->con_switch(vc_cons[currcons].d); set_palette(currcons); 593 if (update && vcmode != KD_GRAPHICS) do_update_region(currcons, origin, screenbuf_size/2); } set_cursor(currcons); 597 if (is_switch) { set_leds(); compute_shiftstate(); } } /* * Allocation, freeing and resizing of VTs. */ 607 int vc_cons_allocated(unsigned int i) { 609 return (i < MAX_NR_CONSOLES && vc_cons[i].d); } 612 static void visual_init(int currcons, int init) { /* ++Geert: sw->con_init determines console size */ sw = conswitchp; #ifndef VT_SINGLE_DRIVER 617 if (con_driver_map[currcons]) sw = con_driver_map[currcons]; #endif cons_num = currcons; display_fg = &master_display_fg; vc_cons[currcons].d->vc_uni_pagedir_loc = &vc_cons[currcons].d->vc_uni_pagedir; vc_cons[currcons].d->vc_uni_pagedir = 0; hi_font_mask = 0; complement_mask = 0; can_do_color = 0; sw->con_init(vc_cons[currcons].d, init); 628 if (!complement_mask) complement_mask = can_do_color ? 0x7700 : 0x0800; s_complement_mask = complement_mask; video_size_row = video_num_columns<<1; screenbuf_size = video_num_lines*video_size_row; } 635 int vc_allocate(unsigned int currcons) /* return 0 on success */ { 637 if (currcons >= MAX_NR_CONSOLES) 638 return -ENXIO; 639 if (!vc_cons[currcons].d) { long p, q; /* prevent users from taking too much memory */ 643 if (currcons >= MAX_NR_USER_CONSOLES && !capable(CAP_SYS_RESOURCE)) 644 return -EPERM; /* due to the granularity of kmalloc, we waste some memory here */ /* the alloc is done in two steps, to optimize the common situation of a 25x80 console (structsize=216, screenbuf_size=4000) */ /* although the numbers above are not valid since long ago, the point is still up-to-date and the comment still has its value even if only as a historical artifact. --mj, July 1998 */ p = (long) kmalloc(structsize, GFP_KERNEL); 653 if (!p) 654 return -ENOMEM; vc_cons[currcons].d = (struct vc_data *)p; vt_cons[currcons] = (struct vt_struct *)(p+sizeof(struct vc_data)); visual_init(currcons, 1); 658 if (!*vc_cons[currcons].d->vc_uni_pagedir_loc) con_set_default_unimap(currcons); q = (long)kmalloc(screenbuf_size, GFP_KERNEL); 661 if (!q) { kfree((char *) p); vc_cons[currcons].d = NULL; vt_cons[currcons] = NULL; 665 return -ENOMEM; } screenbuf = (unsigned short *) q; kmalloced = 1; vc_init(currcons, video_num_lines, video_num_columns, 1); 671 if (!pm_con) { pm_con = pm_register(PM_SYS_DEV, PM_SYS_VGA, pm_con_request); } } 677 return 0; } /* * Change # of rows and columns (0 means unchanged/the size of fg_console) * [this is to be used together with some user program * like resize that changes the hardware videomode] */ 685 int vc_resize(unsigned int lines, unsigned int cols, unsigned int first, unsigned int last) { unsigned int cc, ll, ss, sr, todo = 0; unsigned int currcons = fg_console, i; unsigned short *newscreens[MAX_NR_CONSOLES]; cc = (cols ? cols : video_num_columns); ll = (lines ? lines : video_num_lines); sr = cc << 1; ss = sr * ll; 697 for (currcons = first; currcons <= last; currcons++) { if (!vc_cons_allocated(currcons) || 699 (cc == video_num_columns && ll == video_num_lines)) newscreens[currcons] = NULL; 701 else { unsigned short *p = (unsigned short *) kmalloc(ss, GFP_USER); 703 if (!p) { 704 for (i = first; i < currcons; i++) 705 if (newscreens[i]) kfree(newscreens[i]); 707 return -ENOMEM; } newscreens[currcons] = p; todo++; } } 713 if (!todo) 714 return 0; 716 for (currcons = first; currcons <= last; currcons++) { unsigned int occ, oll, oss, osr; unsigned long ol, nl, nlend, rlth, rrem; 719 if (!newscreens[currcons] || !vc_cons_allocated(currcons)) 720 continue; oll = video_num_lines; occ = video_num_columns; osr = video_size_row; oss = screenbuf_size; video_num_lines = ll; video_num_columns = cc; video_size_row = sr; screenbuf_size = ss; rlth = MIN(osr, sr); rrem = sr - rlth; ol = origin; nl = (long) newscreens[currcons]; nlend = nl + ss; 737 if (ll < oll) ol += (oll - ll) * osr; update_attr(currcons); 742 while (ol < scr_end) { scr_memcpyw((unsigned short *) nl, (unsigned short *) ol, rlth); 744 if (rrem) scr_memsetw((void *)(nl + rlth), video_erase_char, rrem); ol += osr; nl += sr; } 749 if (nlend > nl) scr_memsetw((void *) nl, video_erase_char, nlend - nl); 751 if (kmalloced) kfree(screenbuf); screenbuf = newscreens[currcons]; kmalloced = 1; screenbuf_size = ss; set_origin(currcons); /* do part of a reset_terminal() */ top = 0; bottom = video_num_lines; gotoxy(currcons, x, y); save_cur(currcons); 764 if (console_table[currcons]) { struct winsize ws, *cws = &console_table[currcons]->winsize; memset(&ws, 0, sizeof(ws)); ws.ws_row = video_num_lines; ws.ws_col = video_num_columns; if ((ws.ws_row != cws->ws_row || ws.ws_col != cws->ws_col) && 770 console_table[currcons]->pgrp > 0) kill_pg(console_table[currcons]->pgrp, SIGWINCH, 1); *cws = ws; } 775 if (IS_VISIBLE) update_screen(currcons); } 779 return 0; } 783 void vc_disallocate(unsigned int currcons) { 785 if (vc_cons_allocated(currcons)) { sw->con_deinit(vc_cons[currcons].d); 787 if (kmalloced) kfree(screenbuf); 789 if (currcons >= MIN_NR_CONSOLES) kfree(vc_cons[currcons].d); vc_cons[currcons].d = NULL; } } /* * VT102 emulator */ #define set_kbd(x) set_vc_kbd_mode(kbd_table+currcons,x) #define clr_kbd(x) clr_vc_kbd_mode(kbd_table+currcons,x) #define is_kbd(x) vc_kbd_mode(kbd_table+currcons,x) #define decarm VC_REPEAT #define decckm VC_CKMODE #define kbdapplic VC_APPLIC #define lnm VC_CRLF /* * this is what the terminal answers to a ESC-Z or csi0c query. */ #define VT100ID "\033[?1;2c" #define VT102ID "\033[?6c" unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7, 8,12,10,14, 9,13,11,15 }; /* the default colour table, for VGA+ colour systems */ int default_red[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa, 0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff}; int default_grn[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa, 0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff}; int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa, 0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff}; /* * 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. */ 830 static void gotoxy(int currcons, int new_x, int new_y) { int min_y, max_y; 834 if (new_x < 0) x = 0; else 837 if (new_x >= video_num_columns) x = video_num_columns - 1; 839 else x = new_x; 841 if (decom) { min_y = top; max_y = bottom; 844 } else { min_y = 0; max_y = video_num_lines; } 848 if (new_y < min_y) y = min_y; 850 else if (new_y >= max_y) y = max_y - 1; 852 else y = new_y; pos = origin + y*video_size_row + (x<<1); need_wrap = 0; } /* for absolute user moves, when decom is set */ 859 static void gotoxay(int currcons, int new_x, int new_y) { gotoxy(currcons, new_x, decom ? (top+new_y) : new_y); } 864 void scrollback(int lines) { int currcons = fg_console; 868 if (!lines) lines = video_num_lines/2; scrolldelta(-lines); } 873 void scrollfront(int lines) { int currcons = fg_console; 877 if (!lines) lines = video_num_lines/2; scrolldelta(lines); } 882 static void lf(int currcons) { /* don't scroll if above bottom of scrolling region, or * if below scrolling region */ 887 if (y+1 == bottom) scrup(currcons,top,bottom,1); 889 else if (y < video_num_lines-1) { y++; pos += video_size_row; } need_wrap = 0; } 896 static void ri(int currcons) { /* don't scroll if below top of scrolling region, or * if above scrolling region */ 901 if (y == top) scrdown(currcons,top,bottom,1); 903 else if (y > 0) { y--; pos -= video_size_row; } need_wrap = 0; } 910 static inline void cr(int currcons) { pos -= x<<1; need_wrap = x = 0; } 916 static inline void bs(int currcons) { 918 if (x) { pos -= 2; x--; need_wrap = 0; } } 925 static inline void del(int currcons) { /* ignored */ } 930 static void csi_J(int currcons, int vpar) { unsigned int count; unsigned short * start; 935 switch (vpar) { 936 case 0: /* erase from cursor to end of display */ count = (scr_end-pos)>>1; start = (unsigned short *) pos; 939 if (DO_UPDATE) { /* do in two stages */ sw->con_clear(vc_cons[currcons].d, y, x, 1, video_num_columns-x); sw->con_clear(vc_cons[currcons].d, y+1, 0, video_num_lines-y-1, video_num_columns); } 947 break; 948 case 1: /* erase from start to cursor */ count = ((pos-origin)>>1)+1; start = (unsigned short *) origin; 951 if (DO_UPDATE) { /* do in two stages */ sw->con_clear(vc_cons[currcons].d, 0, 0, y, video_num_columns); sw->con_clear(vc_cons[currcons].d, y, 0, 1, x + 1); } 958 break; 959 case 2: /* erase whole display */ count = video_num_columns * video_num_lines; start = (unsigned short *) origin; 962 if (DO_UPDATE) sw->con_clear(vc_cons[currcons].d, 0, 0, video_num_lines, video_num_columns); 966 break; 967 default: 968 return; } scr_memsetw(start, video_erase_char, 2*count); need_wrap = 0; } 974 static void csi_K(int currcons, int vpar) { unsigned int count; unsigned short * start; 979 switch (vpar) { 980 case 0: /* erase from cursor to end of line */ count = video_num_columns-x; start = (unsigned short *) pos; 983 if (DO_UPDATE) sw->con_clear(vc_cons[currcons].d, y, x, 1, video_num_columns-x); 986 break; 987 case 1: /* erase from start of line to cursor */ start = (unsigned short *) (pos - (x<<1)); count = x+1; 990 if (DO_UPDATE) sw->con_clear(vc_cons[currcons].d, y, 0, 1, x + 1); 993 break; 994 case 2: /* erase whole line */ start = (unsigned short *) (pos - (x<<1)); count = video_num_columns; 997 if (DO_UPDATE) sw->con_clear(vc_cons[currcons].d, y, 0, 1, video_num_columns); 1000 break; 1001 default: 1002 return; } scr_memsetw(start, video_erase_char, 2 * count); need_wrap = 0; } 1008 static void csi_X(int currcons, int vpar) /* erase the following vpar positions */ { /* not vt100? */ int count; 1012 if (!vpar) vpar++; count = (vpar > video_num_columns-x) ? (video_num_columns-x) : vpar; scr_memsetw((unsigned short *) pos, video_erase_char, 2 * count); 1017 if (DO_UPDATE) sw->con_clear(vc_cons[currcons].d, y, x, 1, count); need_wrap = 0; } 1022 static void default_attr(int currcons) { intensity = 1; underline = 0; reverse = 0; blink = 0; color = def_color; } 1031 static void csi_m(int currcons) { int i; 1035 for (i=0;i<=npar;i++) 1036 switch (par[i]) { 1037 case 0: /* all attributes off */ default_attr(currcons); 1039 break; 1040 case 1: intensity = 2; 1042 break; 1043 case 2: intensity = 0; 1045 break; 1046 case 4: underline = 1; 1048 break; 1049 case 5: blink = 1; 1051 break; 1052 case 7: reverse = 1; 1054 break; 1055 case 10: /* ANSI X3.64-1979 (SCO-ish?) * Select primary font, don't display * control chars if defined, don't set * bit 8 on output. */ translate = set_translate(charset == 0 ? G0_charset : G1_charset,currcons); disp_ctrl = 0; toggle_meta = 0; 1065 break; 1066 case 11: /* ANSI X3.64-1979 (SCO-ish?) * Select first alternate font, lets * chars < 32 be displayed as ROM chars. */ translate = set_translate(IBMPC_MAP,currcons); disp_ctrl = 1; toggle_meta = 0; 1073 break; 1074 case 12: /* ANSI X3.64-1979 (SCO-ish?) * Select second alternate font, toggle * high bit before displaying as ROM char. */ translate = set_translate(IBMPC_MAP,currcons); disp_ctrl = 1; toggle_meta = 1; 1081 break; 1082 case 21: 1083 case 22: intensity = 1; 1085 break; 1086 case 24: underline = 0; 1088 break; 1089 case 25: blink = 0; 1091 break; 1092 case 27: reverse = 0; 1094 break; 1095 case 38: /* ANSI X3.64-1979 (SCO-ish?) * Enables underscore, white foreground * with white underscore (Linux - use * default foreground). */ color = (def_color & 0x0f) | background; underline = 1; 1102 break; 1103 case 39: /* ANSI X3.64-1979 (SCO-ish?) * Disable underline option. * Reset colour to default? It did this * before... */ color = (def_color & 0x0f) | background; underline = 0; 1110 break; 1111 case 49: color = (def_color & 0xf0) | foreground; 1113 break; 1114 default: 1115 if (par[i] >= 30 && par[i] <= 37) color = color_table[par[i]-30] | background; 1118 else if (par[i] >= 40 && par[i] <= 47) color = (color_table[par[i]-40]<<4) | foreground; 1121 break; } update_attr(currcons); } 1126 static void respond_string(const char * p, struct tty_struct * tty) { 1128 while (*p) { tty_insert_flip_char(tty, *p, 0); p++; } con_schedule_flip(tty); } 1135 static void cursor_report(int currcons, struct tty_struct * tty) { char buf[40]; sprintf(buf, "\033[%d;%dR", y + (decom ? top+1 : 1), x+1); respond_string(buf, tty); } 1143 static inline void status_report(struct tty_struct * tty) { respond_string("\033[0n", tty); /* Terminal ok */ } 1148 static inline void respond_ID(struct tty_struct * tty) { respond_string(VT102ID, tty); } 1153 void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry) { char buf[8]; sprintf(buf, "\033[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx), (char)('!' + mry)); respond_string(buf, tty); } /* invoked via ioctl(TIOCLINUX) and through set_selection */ 1163 int mouse_reporting(void) { int currcons = fg_console; 1167 return report_mouse; } 1170 static void set_mode(int currcons, int on_off) { int i; 1174 for (i=0; i<=npar; i++) 1175 if (ques) switch(par[i]) { /* DEC private modes set/reset */ 1176 case 1: /* Cursor keys send ^[Ox/^[[x */ 1177 if (on_off) set_kbd(decckm); 1179 else clr_kbd(decckm); 1181 break; 1182 case 3: /* 80/132 mode switch unimplemented */ deccolm = on_off; #if 0 (void) vc_resize(video_num_lines, deccolm ? 132 : 80); /* this alone does not suffice; some user mode utility has to change the hardware regs */ #endif 1189 break; 1190 case 5: /* Inverted screen on/off */ 1191 if (decscnm != on_off) { decscnm = on_off; invert_screen(currcons, 0, screenbuf_size, 0); update_attr(currcons); } 1196 break; 1197 case 6: /* Origin relative/absolute */ decom = on_off; gotoxay(currcons,0,0); 1200 break; 1201 case 7: /* Autowrap on/off */ decawm = on_off; 1203 break; 1204 case 8: /* Autorepeat on/off */ 1205 if (on_off) set_kbd(decarm); 1207 else clr_kbd(decarm); 1209 break; 1210 case 9: report_mouse = on_off ? 1 : 0; 1212 break; 1213 case 25: /* Cursor on/off */ deccm = on_off; 1215 break; 1216 case 1000: report_mouse = on_off ? 2 : 0; 1218 break; 1219 } else switch(par[i]) { /* ANSI modes set/reset */ 1220 case 3: /* Monitor (display ctrls) */ disp_ctrl = on_off; 1222 break; 1223 case 4: /* Insert Mode on/off */ decim = on_off; 1225 break; 1226 case 20: /* Lf, Enter == CrLf/Lf */ 1227 if (on_off) set_kbd(lnm); 1229 else clr_kbd(lnm); 1231 break; } } 1235 static void setterm_command(int currcons) { 1237 switch(par[0]) { 1238 case 1: /* set color for underline mode */ 1239 if (can_do_color && par[1] < 16) { ulcolor = color_table[par[1]]; 1241 if (underline) update_attr(currcons); } 1244 break; 1245 case 2: /* set color for half intensity mode */ 1246 if (can_do_color && par[1] < 16) { halfcolor = color_table[par[1]]; 1248 if (intensity == 0) update_attr(currcons); } 1251 break; 1252 case 8: /* store colors as defaults */ def_color = attr; 1254 if (hi_font_mask == 0x100) def_color >>= 1; default_attr(currcons); update_attr(currcons); 1258 break; 1259 case 9: /* set blanking interval */ blankinterval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ; poke_blanked_console(); 1262 break; 1263 case 10: /* set bell frequency in Hz */ 1264 if (npar >= 1) bell_pitch = par[1]; 1266 else bell_pitch = DEFAULT_BELL_PITCH; 1268 break; 1269 case 11: /* set bell duration in msec */ 1270 if (npar >= 1) bell_duration = (par[1] < 2000) ? par[1]*HZ/1000 : 0; 1273 else bell_duration = DEFAULT_BELL_DURATION; 1275 break; 1276 case 12: /* bring specified console to the front */ 1277 if (par[1] >= 1 && vc_cons_allocated(par[1]-1)) set_console(par[1] - 1); 1279 break; 1280 case 13: /* unblank the screen */ poke_blanked_console(); 1282 break; 1283 case 14: /* set vesa powerdown interval */ vesa_off_interval = ((par[1] < 60) ? par[1] : 60) * 60 * HZ; 1285 break; } } 1289 static void insert_line(int currcons, unsigned int nr) { scrdown(currcons,y,bottom,nr); need_wrap = 0; } 1296 static void delete_line(int currcons, unsigned int nr) { scrup(currcons,y,bottom,nr); need_wrap = 0; } 1302 static void csi_at(int currcons, unsigned int nr) { 1304 if (nr > video_num_columns - x) nr = video_num_columns - x; 1306 else if (!nr) nr = 1; insert_char(currcons, nr); } 1311 static void csi_L(int currcons, unsigned int nr) { 1313 if (nr > video_num_lines - y) nr = video_num_lines - y; 1315 else if (!nr) nr = 1; insert_line(currcons, nr); } 1320 static void csi_P(int currcons, unsigned int nr) { 1322 if (nr > video_num_columns - x) nr = video_num_columns - x; 1324 else if (!nr) nr = 1; delete_char(currcons, nr); } 1329 static void csi_M(int currcons, unsigned int nr) { 1331 if (nr > video_num_lines - y) nr = video_num_lines - y; 1333 else if (!nr) nr=1; delete_line(currcons, nr); } 1338 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; } 1352 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 = set_translate(charset ? G1_charset : G0_charset,currcons); update_attr(currcons); need_wrap = 0; } enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey, EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd, ESpalette }; 1372 static void reset_terminal(int currcons, int do_clear) { top = 0; bottom = video_num_lines; vc_state = ESnormal; ques = 0; translate = set_translate(LAT1_MAP,currcons); G0_charset = LAT1_MAP; G1_charset = GRAF_MAP; charset = 0; need_wrap = 0; report_mouse = 0; utf = 0; utf_count = 0; disp_ctrl = 0; toggle_meta = 0; decscnm = 0; decom = 0; decawm = 1; deccm = 1; decim = 0; set_kbd(decarm); clr_kbd(decckm); clr_kbd(kbdapplic); clr_kbd(lnm); kbd_table[currcons].lockstate = 0; kbd_table[currcons].slockstate = 0; kbd_table[currcons].ledmode = LED_SHOW_FLAGS; kbd_table[currcons].ledflagstate = kbd_table[currcons].default_ledflagstate; set_leds(); cursor_type = CUR_DEFAULT; complement_mask = s_complement_mask; default_attr(currcons); update_attr(currcons); tab_stop[0] = 0x01010100; tab_stop[1] = tab_stop[2] = tab_stop[3] = tab_stop[4] = 0x01010101; bell_pitch = DEFAULT_BELL_PITCH; bell_duration = DEFAULT_BELL_DURATION; gotoxy(currcons,0,0); save_cur(currcons); 1423 if (do_clear) csi_J(currcons,2); } 1427 static void do_con_trol(struct tty_struct *tty, unsigned int currcons, int c) { /* * Control characters can be used in the _middle_ * of an escape sequence. */ 1433 switch (c) { 1434 case 0: 1435 return; 1436 case 7: 1437 if (bell_duration) kd_mksound(bell_pitch, bell_duration); 1439 return; 1440 case 8: bs(currcons); 1442 return; 1443 case 9: pos -= (x << 1); 1445 while (x < video_num_columns - 1) { x++; 1447 if (tab_stop[x >> 5] & (1 << (x & 31))) 1448 break; } pos += (x << 1); 1451 return; 1452 case 10: case 11: case 12: lf(currcons); 1454 if (!is_kbd(lnm)) 1455 return; 1456 case 13: cr(currcons); 1458 return; 1459 case 14: charset = 1; translate = set_translate(G1_charset,currcons); disp_ctrl = 1; 1463 return; 1464 case 15: charset = 0; translate = set_translate(G0_charset,currcons); disp_ctrl = 0; 1468 return; 1469 case 24: case 26: vc_state = ESnormal; 1471 return; 1472 case 27: vc_state = ESesc; 1474 return; 1475 case 127: del(currcons); 1477 return; 1478 case 128+27: vc_state = ESsquare; 1480 return; } 1482 switch(vc_state) { 1483 case ESesc: vc_state = ESnormal; 1485 switch (c) { 1486 case '[': vc_state = ESsquare; 1488 return; 1489 case ']': vc_state = ESnonstd; 1491 return; 1492 case '%': vc_state = ESpercent; 1494 return; 1495 case 'E': cr(currcons); lf(currcons); 1498 return; 1499 case 'M': ri(currcons); 1501 return; 1502 case 'D': lf(currcons); 1504 return; 1505 case 'H': tab_stop[x >> 5] |= (1 << (x & 31)); 1507 return; 1508 case 'Z': respond_ID(tty); 1510 return; 1511 case '7': save_cur(currcons); 1513 return; 1514 case '8': restore_cur(currcons); 1516 return; 1517 case '(': vc_state = ESsetG0; 1519 return; 1520 case ')': vc_state = ESsetG1; 1522 return; 1523 case '#': vc_state = EShash; 1525 return; 1526 case 'c': reset_terminal(currcons,1); 1528 return; 1529 case '>': /* Numeric keypad */ clr_kbd(kbdapplic); 1531 return; 1532 case '=': /* Appl. keypad */ set_kbd(kbdapplic); 1534 return; } 1536 return; 1537 case ESnonstd: 1538 if (c=='P') { /* palette escape sequence */ 1539 for (npar=0; npar<NPAR; npar++) par[npar] = 0 ; npar = 0 ; vc_state = ESpalette; 1543 return; 1544 } else if (c=='R') { /* reset palette */ reset_palette(currcons); vc_state = ESnormal; 1547 } else vc_state = ESnormal; 1549 return; 1550 case ESpalette: 1551 if ( (c>='0'&&c<='9') || (c>='A'&&c<='F') || (c>='a'&&c<='f') ) { par[npar++] = (c>'9' ? (c&0xDF)-'A'+10 : c-'0') ; 1553 if (npar==7) { int i = par[0]*3, j = 1; palette[i] = 16*par[j++]; palette[i++] += par[j++]; palette[i] = 16*par[j++]; palette[i++] += par[j++]; palette[i] = 16*par[j++]; palette[i] += par[j]; set_palette(currcons); vc_state = ESnormal; } 1564 } else vc_state = ESnormal; 1566 return; 1567 case ESsquare: 1568 for(npar = 0 ; npar < NPAR ; npar++) par[npar] = 0; npar = 0; vc_state = ESgetpars; 1572 if (c == '[') { /* Function key */ vc_state=ESfunckey; 1574 return; } ques = (c=='?'); 1577 if (ques) 1578 return; 1579 case ESgetpars: 1580 if (c==';' && npar<NPAR-1) { npar++; 1582 return; 1583 } else if (c>='0' && c<='9') { par[npar] *= 10; par[npar] += c-'0'; 1586 return; 1587 } else vc_state=ESgotpars; 1588 case ESgotpars: vc_state = ESnormal; 1590 switch(c) { 1591 case 'h': set_mode(currcons,1); 1593 return; 1594 case 'l': set_mode(currcons,0); 1596 return; 1597 case 'c': 1598 if (ques) { 1599 if (par[0]) cursor_type = par[0] | (par[1]<<8) | (par[2]<<16); 1601 else cursor_type = CUR_DEFAULT; 1603 return; } 1605 break; 1606 case 'm': 1607 if (ques) { clear_selection(); 1609 if (par[0]) complement_mask = par[0]<<8 | par[1]; 1611 else complement_mask = s_complement_mask; 1613 return; } 1615 break; 1616 case 'n': 1617 if (!ques) { 1618 if (par[0] == 5) status_report(tty); 1620 else if (par[0] == 6) cursor_report(currcons,tty); } 1623 return; } 1625 if (ques) { ques = 0; 1627 return; } 1629 switch(c) { 1630 case 'G': case '`': 1631 if (par[0]) par[0]--; gotoxy(currcons,par[0],y); 1633 return; 1634 case 'A': 1635 if (!par[0]) par[0]++; gotoxy(currcons,x,y-par[0]); 1637 return; 1638 case 'B': case 'e': 1639 if (!par[0]) par[0]++; gotoxy(currcons,x,y+par[0]); 1641 return; 1642 case 'C': case 'a': 1643 if (!par[0]) par[0]++; gotoxy(currcons,x+par[0],y); 1645 return; 1646 case 'D': 1647 if (!par[0]) par[0]++; gotoxy(currcons,x-par[0],y); 1649 return; 1650 case 'E': 1651 if (!par[0]) par[0]++; gotoxy(currcons,0,y+par[0]); 1653 return; 1654 case 'F': 1655 if (!par[0]) par[0]++; gotoxy(currcons,0,y-par[0]); 1657 return; 1658 case 'd': 1659 if (par[0]) par[0]--; gotoxay(currcons,x,par[0]); 1661 return; 1662 case 'H': case 'f': 1663 if (par[0]) par[0]--; 1664 if (par[1]) par[1]--; gotoxay(currcons,par[1],par[0]); 1666 return; 1667 case 'J': csi_J(currcons,par[0]); 1669 return; 1670 case 'K': csi_K(currcons,par[0]); 1672 return; 1673 case 'L': csi_L(currcons,par[0]); 1675 return; 1676 case 'M': csi_M(currcons,par[0]); 1678 return; 1679 case 'P': csi_P(currcons,par[0]); 1681 return; 1682 case 'c': 1683 if (!par[0]) respond_ID(tty); 1685 return; 1686 case 'g': 1687 if (!par[0]) tab_stop[x >> 5] &= ~(1 << (x & 31)); 1689 else if (par[0] == 3) { tab_stop[0] = tab_stop[1] = tab_stop[2] = tab_stop[3] = tab_stop[4] = 0; } 1696 return; 1697 case 'm': csi_m(currcons); 1699 return; 1700 case 'q': /* DECLL - but only 3 leds */ /* map 0,1,2,3 to 0,1,2,4 */ 1702 if (par[0] < 4) setledstate(kbd_table + currcons, (par[0] < 3) ? par[0] : 4); 1705 return; 1706 case 'r': 1707 if (!par[0]) par[0]++; 1709 if (!par[1]) par[1] = video_num_lines; /* Minimum allowed region is 2 lines */ if (par[0] < par[1] && 1713 par[1] <= video_num_lines) { top=par[0]-1; bottom=par[1]; gotoxay(currcons,0,0); } 1718 return; 1719 case 's': save_cur(currcons); 1721 return; 1722 case 'u': restore_cur(currcons); 1724 return; 1725 case 'X': csi_X(currcons, par[0]); 1727 return; 1728 case '@': csi_at(currcons,par[0]); 1730 return; 1731 case ']': /* setterm functions */ setterm_command(currcons); 1733 return; } 1735 return; 1736 case ESpercent: vc_state = ESnormal; 1738 switch (c) { 1739 case '@': /* defined in ISO 2022 */ utf = 0; 1741 return; 1742 case 'G': /* prelim official escape code */ 1743 case '8': /* retained for compatibility */ utf = 1; 1745 return; } 1747 return; 1748 case ESfunckey: vc_state = ESnormal; 1750 return; 1751 case EShash: vc_state = ESnormal; 1753 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) | ' '; do_update_region(currcons, origin, screenbuf_size/2); } 1762 return; 1763 case ESsetG0: 1764 if (c == '0') G0_charset = GRAF_MAP; 1766 else if (c == 'B') G0_charset = LAT1_MAP; 1768 else if (c == 'U') G0_charset = IBMPC_MAP; 1770 else if (c == 'K') G0_charset = USER_MAP; 1772 if (charset == 0) translate = set_translate(G0_charset,currcons); vc_state = ESnormal; 1775 return; 1776 case ESsetG1: 1777 if (c == '0') G1_charset = GRAF_MAP; 1779 else if (c == 'B') G1_charset = LAT1_MAP; 1781 else if (c == 'U') G1_charset = IBMPC_MAP; 1783 else if (c == 'K') G1_charset = USER_MAP; 1785 if (charset == 1) translate = set_translate(G1_charset,currcons); vc_state = ESnormal; 1788 return; 1789 default: vc_state = ESnormal; } } /* This is a temporary buffer used to prepare a tty console write * so that we can easily avoid touching user space while holding the * console spinlock. It is allocated in con_init and is shared by * this code and the vc_screen read/write tty calls. * * We have to allocate this statically in the kernel data section * since console_init (and thus con_init) are called before any * kernel memory allocation is available. */ char con_buf[PAGE_SIZE]; #define CON_BUF_SIZE PAGE_SIZE DECLARE_MUTEX(con_buf_sem); 1807 static int do_con_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { #ifdef VT_BUF_VRAM_ONLY #define FLUSH do { } while(0); #else #define FLUSH if (draw_x >= 0) { \ sw->con_putcs(vc_cons[currcons].d, (u16 *)draw_from, (u16 *)draw_to-(u16 *)draw_from, y, draw_x); \ draw_x = -1; \ } #endif int c, tc, ok, n = 0, draw_x = -1; unsigned int currcons; unsigned long draw_from = 0, draw_to = 0; struct vt_struct *vt = (struct vt_struct *)tty->driver_data; u16 himask, charmask; const unsigned char *orig_buf = NULL; int orig_count; currcons = vt->vc_num; 1828 if (!vc_cons_allocated(currcons)) { /* could this happen? */ static int error = 0; 1831 if (!error) { error = 1; printk("con_write: tty %d not allocated\n", currcons+1); } 1835 return 0; } orig_buf = buf; orig_count = count; 1841 if (from_user) { down(&con_buf_sem); again: 1845 if (count > CON_BUF_SIZE) count = CON_BUF_SIZE; 1847 if (copy_from_user(con_buf, buf, count)) { n = 0; /* ?? are error codes legal here ?? */ 1849 goto out; } buf = con_buf; } /* At this point 'buf' is guarenteed to be a kernel buffer * and therefore no access to userspace (and therefore sleeping) * will be needed. The con_buf_sem serializes all tty based * console rendering and vcs write/read operations. We hold * the console spinlock during the entire write. */ 1862 spin_lock_irq(&console_lock); himask = hi_font_mask; charmask = himask ? 0x1ff : 0xff; /* undraw cursor first */ 1868 if (IS_FG) hide_cursor(currcons); 1871 while (!tty->stopped && count) { c = *buf; buf++; n++; count--; 1877 if (utf) { /* Combine UTF-8 into Unicode */ /* Incomplete characters silently ignored */ 1880 if(c > 0x7f) { 1881 if (utf_count > 0 && (c & 0xc0) == 0x80) { utf_char = (utf_char << 6) | (c & 0x3f); utf_count--; 1884 if (utf_count == 0) tc = c = utf_char; 1886 else continue; 1887 } else { 1888 if ((c & 0xe0) == 0xc0) { utf_count = 1; utf_char = (c & 0x1f); 1891 } else if ((c & 0xf0) == 0xe0) { utf_count = 2; utf_char = (c & 0x0f); 1894 } else if ((c & 0xf8) == 0xf0) { utf_count = 3; utf_char = (c & 0x07); 1897 } else if ((c & 0xfc) == 0xf8) { utf_count = 4; utf_char = (c & 0x03); 1900 } else if ((c & 0xfe) == 0xfc) { utf_count = 5; utf_char = (c & 0x01); 1903 } else utf_count = 0; 1905 continue; } 1907 } else { tc = c; utf_count = 0; } 1911 } else { /* no utf */ tc = translate[toggle_meta ? (c|0x80) : c]; } /* If the original code was a control character we * only allow a glyph to be displayed if the code is * not normally used (such as for cursor movement) or * if the disp_ctrl mode has been explicitly enabled. * Certain characters (as given by the CTRL_ALWAYS * bitmap) are always displayed as control characters, * as the console would be pretty useless without * them; to display an arbitrary font position use the * direct-to-font zone in UTF-8 mode. */ ok = tc && (c >= 32 || (!utf && !(((disp_ctrl ? CTRL_ALWAYS : CTRL_ACTION) >> c) & 1))) && (c != 127 || disp_ctrl) && (c != 128+27); 1931 if (vc_state == ESnormal && ok) { /* Now try to find out how to display it */ tc = conv_uni_to_pc(vc_cons[currcons].d, tc); 1934 if ( tc == -4 ) { /* If we got -4 (not found) then see if we have defined a replacement character (U+FFFD) */ tc = conv_uni_to_pc(vc_cons[currcons].d, 0xfffd); /* One reason for the -4 can be that we just did a clear_unimap(); try at least to show something. */ 1942 if (tc == -4) tc = c; 1944 } else if ( tc == -3 ) { /* Bad hash table -- hope for the best */ tc = c; } 1948 if (tc & ~charmask) 1949 continue; /* Conversion failed */ 1951 if (need_wrap || decim) 1952 FLUSH 1953 if (need_wrap) { cr(currcons); lf(currcons); } 1957 if (decim) insert_char(currcons, 1); scr_writew(himask ? ((attr << 8) & ~himask) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) : (attr << 8) + tc, (u16 *) pos); 1963 if (DO_UPDATE && draw_x < 0) { draw_x = x; draw_from = pos; } 1967 if (x == video_num_columns - 1) { need_wrap = decawm; draw_to = pos+2; 1970 } else { x++; draw_to = (pos+=2); } 1974 continue; } 1976 FLUSH do_con_trol(tty, currcons, c); } 1979 FLUSH 1980 spin_unlock_irq(&console_lock); out: 1983 if (from_user) { /* If the user requested something larger than * the CON_BUF_SIZE, and the tty is not stopped, * keep going. */ 1988 if ((orig_count > CON_BUF_SIZE) && !tty->stopped) { orig_count -= CON_BUF_SIZE; orig_buf += CON_BUF_SIZE; count = orig_count; buf = orig_buf; 1993 goto again; } up(&con_buf_sem); } 1999 return n; #undef FLUSH } /* * This is the console switching tasklet. * * Doing console switching in a tasklet allows * us to do the switches asynchronously (needed when we want * to switch due to a keyboard interrupt). Synchronization * with other console code and prevention of re-entrancy is * ensured with console_lock. */ 2012 static void console_softint(unsigned long ignored) { /* Runs the task queue outside of the console lock. These * callbacks can come back into the console code and thus * will perform their own locking. */ run_task_queue(&con_task_queue); 2020 spin_lock_irq(&console_lock); 2022 if (want_console >= 0) { 2023 if (want_console != fg_console && vc_cons_allocated(want_console)) { hide_cursor(fg_console); change_console(want_console); /* we only changed when the console had already been allocated - a new console is not created in an interrupt routine */ } want_console = -1; } 2032 if (do_poke_blanked_console) { /* do not unblank for a LED change */ do_poke_blanked_console = 0; poke_blanked_console(); } 2036 if (scrollback_delta) { int currcons = fg_console; clear_selection(); 2039 if (vcmode == KD_TEXT) sw->con_scrolldelta(vc_cons[currcons].d, scrollback_delta); scrollback_delta = 0; } 2044 spin_unlock_irq(&console_lock); } #ifdef CONFIG_VT_CONSOLE /* * Console on virtual terminal * * The console_lock must be held when we get here. */ 2055 void vt_console_print(struct console *co, const char * b, unsigned count) { int currcons = fg_console; unsigned char c; static unsigned long printing; const ushort *start; ushort cnt = 0; ushort myx; /* console busy or not yet initialized */ 2065 if (!printable || test_and_set_bit(0, &printing)) 2066 return; pm_access(pm_con); 2070 if (kmsg_redirect && vc_cons_allocated(kmsg_redirect - 1)) currcons = kmsg_redirect - 1; /* read `x' only after setting currecons properly (otherwise the `x' macro will read the x of the foreground console). */ myx = x; 2077 if (!vc_cons_allocated(currcons)) { /* impossible */ /* printk("vt_console_print: tty %d not allocated ??\n", currcons+1); */ 2080 goto quit; } 2083 if (vcmode != KD_TEXT) 2084 goto quit; /* undraw cursor first */ 2087 if (IS_FG) hide_cursor(currcons); start = (ushort *)pos; /* Contrived structure to try to emulate original need_wrap behaviour * Problems caused when we have need_wrap set on '\n' character */ 2094 while (count--) { c = *b++; 2096 if (c == 10 || c == 13 || c == 8 || need_wrap) { 2097 if (cnt > 0) { 2098 if (IS_VISIBLE) sw->con_putcs(vc_cons[currcons].d, start, cnt, y, x); x += cnt; 2101 if (need_wrap) x--; cnt = 0; } 2105 if (c == 8) { /* backspace */ bs(currcons); start = (ushort *)pos; myx = x; 2109 continue; } 2111 if (c != 13) lf(currcons); cr(currcons); start = (ushort *)pos; myx = x; 2116 if (c == 10 || c == 13) 2117 continue; } scr_writew((attr << 8) + c, (unsigned short *) pos); cnt++; 2121 if (myx == video_num_columns - 1) { need_wrap = 1; 2123 continue; } pos+=2; myx++; } 2128 if (cnt > 0) { 2129 if (IS_VISIBLE) sw->con_putcs(vc_cons[currcons].d, start, cnt, y, x); x += cnt; 2132 if (x == video_num_columns) { x--; need_wrap = 1; } } set_cursor(currcons); quit: clear_bit(0, &printing); } 2143 static kdev_t vt_console_device(struct console *c) { 2145 return MKDEV(TTY_MAJOR, c->index ? c->index : fg_console + 1); } struct console vt_console_driver = { name: "tty", write: vt_console_print, device: vt_console_device, wait_key: keyboard_wait_for_keypress, unblank: unblank_screen, flags: CON_PRINTBUFFER, index: -1, }; #endif /* * Handling of Linux-specific VC ioctls */ 2163 int tioclinux(struct tty_struct *tty, unsigned long arg) { char type, data; 2167 if (tty->driver.type != TTY_DRIVER_TYPE_CONSOLE) 2168 return -EINVAL; 2169 if (current->tty != tty && !suser()) 2170 return -EPERM; 2171 if (get_user(type, (char *)arg)) 2172 return -EFAULT; 2173 switch (type) { 2175 case 2: 2176 return set_selection(arg, tty, 1); 2177 case 3: 2178 return paste_selection(tty); 2179 case 4: unblank_screen(); 2181 return 0; 2182 case 5: 2183 return sel_loadlut(arg); 2184 case 6: /* * Make it possible to react to Shift+Mousebutton. * Note that 'shift_state' is an undocumented * kernel-internal variable; programs not closely * related to the kernel should not use this. */ data = shift_state; 2193 return __put_user(data, (char *) arg); 2194 case 7: data = mouse_reporting(); 2196 return __put_user(data, (char *) arg); 2197 case 10: set_vesa_blanking(arg); 2199 return 0; 2200 case 11: /* set kmsg redirect */ 2201 if (!suser()) 2202 return -EPERM; 2203 if (get_user(data, (char *)arg+1)) 2204 return -EFAULT; kmsg_redirect = data; 2206 return 0; 2207 case 12: /* get fg_console */ 2208 return fg_console; } 2210 return -EINVAL; } /* * /dev/ttyN handling */ 2217 static int con_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { int retval; pm_access(pm_con); retval = do_con_write(tty, from_user, buf, count); con_flush_chars(tty); 2226 return retval; } 2229 static void con_put_char(struct tty_struct *tty, unsigned char ch) { pm_access(pm_con); do_con_write(tty, 0, &ch, 1); } 2235 static int con_write_room(struct tty_struct *tty) { 2237 if (tty->stopped) 2238 return 0; 2239 return 4096; /* No limit, really; we're not buffering */ } 2242 static int con_chars_in_buffer(struct tty_struct *tty) { 2244 return 0; /* we're not buffering */ } /* * con_throttle and con_unthrottle are only used for * paste_selection(), which has to stuff in a large number of * characters... */ 2252 static void con_throttle(struct tty_struct *tty) { } 2256 static void con_unthrottle(struct tty_struct *tty) { struct vt_struct *vt = (struct vt_struct *) tty->driver_data; wake_up_interruptible(&vt->paste_wait); } /* * Turn the Scroll-Lock LED on when the tty is stopped */ 2266 static void con_stop(struct tty_struct *tty) { int console_num; 2269 if (!tty) 2270 return; console_num = MINOR(tty->device) - (tty->driver.minor_start); 2272 if (!vc_cons_allocated(console_num)) 2273 return; set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); set_leds(); } /* * Turn the Scroll-Lock LED off when the console is started */ 2281 static void con_start(struct tty_struct *tty) { int console_num; 2284 if (!tty) 2285 return; console_num = MINOR(tty->device) - (tty->driver.minor_start); 2287 if (!vc_cons_allocated(console_num)) 2288 return; clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); set_leds(); } 2293 static void con_flush_chars(struct tty_struct *tty) { unsigned long flags; struct vt_struct *vt = (struct vt_struct *)tty->driver_data; pm_access(pm_con); 2299 spin_lock_irqsave(&console_lock, flags); set_cursor(vt->vc_num); 2301 spin_unlock_irqrestore(&console_lock, flags); } /* * Allocate the console screen memory. */ 2307 static int con_open(struct tty_struct *tty, struct file * filp) { unsigned int currcons; int i; currcons = MINOR(tty->device) - tty->driver.minor_start; i = vc_allocate(currcons); 2315 if (i) 2316 return i; vt_cons[currcons]->vc_num = currcons; tty->driver_data = vt_cons[currcons]; 2321 if (!tty->winsize.ws_row && !tty->winsize.ws_col) { tty->winsize.ws_row = video_num_lines; tty->winsize.ws_col = video_num_columns; } 2325 if (tty->count == 1) vcs_make_devfs (currcons, 0); 2327 return 0; } 2330 static void con_close(struct tty_struct *tty, struct file * filp) { 2332 if (!tty) 2333 return; 2334 if (tty->count != 1) return; vcs_make_devfs (MINOR (tty->device) - tty->driver.minor_start, 1); tty->driver_data = 0; } 2339 static void vc_init(unsigned int currcons, unsigned int rows, unsigned int cols, int do_clear) { int j, k ; video_num_columns = cols; video_num_lines = rows; video_size_row = cols<<1; screenbuf_size = video_num_lines * video_size_row; set_origin(currcons); pos = origin; reset_vc(currcons); 2351 for (j=k=0; j<16; j++) { vc_cons[currcons].d->vc_palette[k++] = default_red[j] ; vc_cons[currcons].d->vc_palette[k++] = default_grn[j] ; vc_cons[currcons].d->vc_palette[k++] = default_blu[j] ; } def_color = 0x07; /* white */ ulcolor = 0x0f; /* bold white */ halfcolor = 0x08; /* grey */ init_waitqueue_head(&vt_cons[currcons]->paste_wait); reset_terminal(currcons, do_clear); } /* * This routine initializes console interrupts, and does nothing * else. If you want the screen to clear, call tty_write with * the appropriate escape-sequence. */ struct tty_driver console_driver; static int console_refcount; DECLARE_TASKLET_DISABLED(console_tasklet, console_softint, 0); 2374 void __init con_init(void) { const char *display_desc = NULL; unsigned int currcons = 0; 2379 if (conswitchp) display_desc = conswitchp->con_startup(); 2381 if (!display_desc) { fg_console = 0; 2383 return; } memset(&console_driver, 0, sizeof(struct tty_driver)); console_driver.magic = TTY_DRIVER_MAGIC; console_driver.name = "vc/%d"; console_driver.name_base = 1; console_driver.major = TTY_MAJOR; console_driver.minor_start = 1; console_driver.num = MAX_NR_CONSOLES; console_driver.type = TTY_DRIVER_TYPE_CONSOLE; console_driver.init_termios = tty_std_termios; console_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; /* Tell tty_register_driver() to skip consoles because they are * registered before kmalloc() is ready. We'll patch them in later. * See comments at console_init(); see also con_init_devfs(). */ console_driver.flags |= TTY_DRIVER_NO_DEVFS; console_driver.refcount = &console_refcount; console_driver.table = console_table; console_driver.termios = console_termios; console_driver.termios_locked = console_termios_locked; console_driver.open = con_open; console_driver.close = con_close; console_driver.write = con_write; console_driver.write_room = con_write_room; console_driver.put_char = con_put_char; console_driver.flush_chars = con_flush_chars; console_driver.chars_in_buffer = con_chars_in_buffer; console_driver.ioctl = vt_ioctl; console_driver.stop = con_stop; console_driver.start = con_start; console_driver.throttle = con_throttle; console_driver.unthrottle = con_unthrottle; 2419 if (tty_register_driver(&console_driver)) panic("Couldn't register console driver\n"); init_timer(&console_timer); console_timer.function = blank_screen; 2424 if (blankinterval) { mod_timer(&console_timer, jiffies + blankinterval); } /* * kmalloc is not running yet - we use the bootmem allocator. */ 2431 for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) { vc_cons[currcons].d = (struct vc_data *) alloc_bootmem(sizeof(struct vc_data)); vt_cons[currcons] = (struct vt_struct *) alloc_bootmem(sizeof(struct vt_struct)); visual_init(currcons, 1); screenbuf = (unsigned short *) alloc_bootmem(screenbuf_size); kmalloced = 0; vc_init(currcons, video_num_lines, video_num_columns, currcons || !sw->con_save_screen); } currcons = fg_console = 0; master_display_fg = vc_cons[currcons].d; set_origin(currcons); save_screen(currcons); gotoxy(currcons,x,y); csi_J(currcons, 0); update_screen(fg_console); printk("Console: %s %s %dx%d", can_do_color ? "colour" : "mono", display_desc, video_num_columns, video_num_lines); printable = 1; printk("\n"); #ifdef CONFIG_VT_CONSOLE register_console(&vt_console_driver); #endif tasklet_enable(&console_tasklet); tasklet_schedule(&console_tasklet); } #ifndef VT_SINGLE_DRIVER 2465 static void clear_buffer_attributes(int currcons) { unsigned short *p = (unsigned short *) origin; int count = screenbuf_size/2; int mask = hi_font_mask | 0xff; 2471 for (; count > 0; count--, p++) { scr_writew((scr_readw(p)&mask) | (video_erase_char&~mask), p); } } /* * If we support more console drivers, this function is used * when a driver wants to take over some existing consoles * and become default driver for newly opened ones. */ 2482 void take_over_console(const struct consw *csw, int first, int last, int deflt) { int i, j = -1; const char *desc; desc = csw->con_startup(); 2488 if (!desc) return; 2489 if (deflt) conswitchp = csw; 2492 for (i = first; i <= last; i++) { int old_was_color; int currcons = i; con_driver_map[i] = csw; 2498 if (!vc_cons[i].d || !vc_cons[i].d->vc_sw) 2499 continue; j = i; 2502 if (IS_VISIBLE) save_screen(i); old_was_color = vc_cons[i].d->vc_can_do_color; vc_cons[i].d->vc_sw->con_deinit(vc_cons[i].d); visual_init(i, 0); update_attr(i); /* If the console changed between mono <-> color, then * the attributes in the screenbuf will be wrong. The * following resets all attributes to something sane. */ 2513 if (old_was_color != vc_cons[i].d->vc_can_do_color) clear_buffer_attributes(i); 2516 if (IS_VISIBLE) update_screen(i); } printk("Console: switching "); 2520 if (!deflt) printk("consoles %d-%d ", first+1, last+1); 2522 if (j >= 0) printk("to %s %s %dx%d\n", vc_cons[j].d->vc_can_do_color ? "colour" : "mono", desc, vc_cons[j].d->vc_cols, vc_cons[j].d->vc_rows); 2526 else printk("to %s\n", desc); } 2530 void give_up_console(const struct consw *csw) { int i; 2534 for(i = 0; i < MAX_NR_CONSOLES; i++) 2535 if (con_driver_map[i] == csw) con_driver_map[i] = NULL; } #endif /* * Screen blanking */ 2545 static void set_vesa_blanking(unsigned long arg) { char *argp = (char *)arg + 1; unsigned int mode; get_user(mode, argp); vesa_blank_mode = (mode < 4) ? mode : 0; } /* We can't register the console with devfs during con_init(), because it * is called before kmalloc() works. This function is called later to * do the registration. */ 2557 void __init con_init_devfs (void) { int i; 2561 for (i = 0; i < console_driver.num; i++) tty_register_devfs (&console_driver, DEVFS_FL_AOPEN_NOTIFY, console_driver.minor_start + i); } 2566 static void vesa_powerdown(void) { struct vc_data *c = vc_cons[fg_console].d; /* * Power down if currently suspended (1 or 2), * suspend if currently blanked (0), * else do nothing (i.e. already powered down (3)). * Called only if powerdown features are allowed. */ 2575 switch (vesa_blank_mode) { 2576 case VESA_NO_BLANKING: c->vc_sw->con_blank(c, VESA_VSYNC_SUSPEND+1); 2578 break; 2579 case VESA_VSYNC_SUSPEND: 2580 case VESA_HSYNC_SUSPEND: c->vc_sw->con_blank(c, VESA_POWERDOWN+1); 2582 break; } } 2586 static void vesa_powerdown_screen(unsigned long dummy) { console_timer.function = unblank_screen_t; /* I don't have a clue why this is necessary */ vesa_powerdown(); } 2593 static void timer_do_blank_screen(int entering_gfx, int from_timer_handler) { int currcons = fg_console; int i; 2598 if (console_blanked) 2599 return; /* entering graphics mode? */ 2602 if (entering_gfx) { hide_cursor(currcons); save_screen(currcons); sw->con_blank(vc_cons[currcons].d, -1); console_blanked = fg_console + 1; set_origin(currcons); 2608 return; } /* don't blank graphics */ 2612 if (vcmode != KD_TEXT) { console_blanked = fg_console + 1; 2614 return; } hide_cursor(currcons); 2618 if (!from_timer_handler) del_timer_sync(&console_timer); 2620 if (vesa_off_interval) { console_timer.function = vesa_powerdown_screen; mod_timer(&console_timer, jiffies + vesa_off_interval); 2623 } else { 2624 if (!from_timer_handler) del_timer_sync(&console_timer); console_timer.function = unblank_screen_t; } save_screen(currcons); /* In case we need to reset origin, blanking hook returns 1 */ i = sw->con_blank(vc_cons[currcons].d, 1); console_blanked = fg_console + 1; 2633 if (i) set_origin(currcons); 2636 if (console_blank_hook && console_blank_hook(1)) 2637 return; 2638 if (vesa_blank_mode) sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1); } 2642 void do_blank_screen(int entering_gfx) { timer_do_blank_screen(entering_gfx, 0); } 2647 static void unblank_screen_t(unsigned long dummy) { unblank_screen(); } 2652 void unblank_screen(void) { int currcons; 2656 if (!console_blanked) 2657 return; 2658 if (!vc_cons_allocated(fg_console)) { /* impossible */ printk("unblank_screen: tty %d not allocated ??\n", fg_console+1); 2661 return; } console_timer.function = blank_screen; 2664 if (blankinterval) { mod_timer(&console_timer, jiffies + blankinterval); } currcons = fg_console; console_blanked = 0; 2670 if (console_blank_hook) console_blank_hook(0); set_palette(currcons); 2673 if (sw->con_blank(vc_cons[currcons].d, 0)) /* Low-level driver cannot restore -> do it ourselves */ update_screen(fg_console); set_cursor(fg_console); } 2679 static void blank_screen(unsigned long dummy) { timer_do_blank_screen(0, 1); } 2684 void poke_blanked_console(void) { del_timer(&console_timer); /* Can't use _sync here: called from tasklet */ 2687 if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) 2688 return; 2689 if (console_blanked) { console_timer.function = unblank_screen_t; mod_timer(&console_timer, jiffies); /* Now */ 2692 } else if (blankinterval) { mod_timer(&console_timer, jiffies + blankinterval); } } /* * Palettes */ 2701 void set_palette(int currcons) { 2703 if (vcmode != KD_GRAPHICS) sw->con_set_palette(vc_cons[currcons].d, color_table); } 2707 static int set_get_cmap(unsigned char *arg, int set) { int i, j, k; 2711 for (i = 0; i < 16; i++) 2712 if (set) { get_user(default_red[i], arg++); get_user(default_grn[i], arg++); get_user(default_blu[i], arg++); 2716 } else { put_user(default_red[i], arg++); put_user(default_grn[i], arg++); put_user(default_blu[i], arg++); } 2721 if (set) { 2722 for (i = 0; i < MAX_NR_CONSOLES; i++) 2723 if (vc_cons_allocated(i)) { 2724 for (j = k = 0; j < 16; j++) { vc_cons[i].d->vc_palette[k++] = default_red[j]; vc_cons[i].d->vc_palette[k++] = default_grn[j]; vc_cons[i].d->vc_palette[k++] = default_blu[j]; } set_palette(i); } } 2732 return 0; } /* * Load palette into the DAC registers. arg points to a colour * map, 3 bytes per colour, 16 colours, range from 0 to 255. */ 2740 int con_set_cmap(unsigned char *arg) { 2742 return set_get_cmap (arg,1); } 2745 int con_get_cmap(unsigned char *arg) { 2747 return set_get_cmap (arg,0); } 2750 void reset_palette(int currcons) { int j, k; 2753 for (j=k=0; j<16; j++) { palette[k++] = default_red[j]; palette[k++] = default_grn[j]; palette[k++] = default_blu[j]; } set_palette(currcons); } /* * Font switching * * Currently we only support fonts up to 32 pixels wide, at a maximum height * of 32 pixels. Userspace fontdata is stored with 32 bytes (shorts/ints, * depending on width) reserved for each character which is kinda wasty, but * this is done in order to maintain compatibility with the EGA/VGA fonts. It * is upto the actual low-level console-driver convert data into its favorite * format (maybe we should add a `fontoffset' field to the `display' * structure so we wont have to convert the fontdata all the time. * /Jes */ #define max_font_size 65536 2776 int con_font_op(int currcons, struct console_font_op *op) { int rc = -EINVAL; int size = max_font_size, set; u8 *temp = NULL; struct console_font_op old_op; 2783 if (vt_cons[currcons]->vc_mode != KD_TEXT) 2784 goto quit; memcpy(&old_op, op, sizeof(old_op)); 2786 if (op->op == KD_FONT_OP_SET) { 2787 if (!op->data) 2788 return -EINVAL; 2789 if (op->charcount > 512) 2790 goto quit; 2791 if (!op->height) { /* Need to guess font height [compat] */ int h, i; u8 *charmap = op->data, tmp; /* If from KDFONTOP ioctl, don't allow things which can be done in userland, so that we can get rid of this soon */ 2797 if (!(op->flags & KD_FONT_FLAG_OLD)) 2798 goto quit; rc = -EFAULT; 2800 for (h = 32; h > 0; h--) 2801 for (i = 0; i < op->charcount; i++) { 2802 if (get_user(tmp, &charmap[32*i+h-1])) 2803 goto quit; 2804 if (tmp) 2805 goto nonzero; } rc = -EINVAL; 2808 goto quit; nonzero: rc = -EINVAL; op->height = h; } 2813 if (op->width > 32 || op->height > 32) 2814 goto quit; size = (op->width+7)/8 * 32 * op->charcount; 2816 if (size > max_font_size) 2817 return -ENOSPC; set = 1; 2819 } else if (op->op == KD_FONT_OP_GET) set = 0; 2821 else 2822 return sw->con_font_op(vc_cons[currcons].d, op); 2823 if (op->data) { temp = kmalloc(size, GFP_KERNEL); 2825 if (!temp) 2826 return -ENOMEM; 2827 if (set && copy_from_user(temp, op->data, size)) { rc = -EFAULT; 2829 goto quit; } op->data = temp; } 2834 spin_lock_irq(&console_lock); rc = sw->con_font_op(vc_cons[currcons].d, op); 2836 spin_unlock_irq(&console_lock); op->data = old_op.data; 2839 if (!rc && !set) { int c = (op->width+7)/8 * 32 * op->charcount; 2842 if (op->data && op->charcount > old_op.charcount) rc = -ENOSPC; 2844 if (!(op->flags & KD_FONT_FLAG_OLD)) { if (op->width > old_op.width || 2846 op->height > old_op.height) rc = -ENOSPC; 2848 } else { 2849 if (op->width != 8) rc = -EIO; else if ((old_op.height && op->height > old_op.height) || 2852 op->height > 32) rc = -ENOSPC; } 2855 if (!rc && op->data && copy_to_user(op->data, temp, c)) rc = -EFAULT; } 2858 quit: if (temp) kfree(temp); 2860 return rc; } /* * Interface exported to selection and vcs. */ /* used by selection */ 2868 u16 screen_glyph(int currcons, int offset) { u16 w = scr_readw(screenpos(currcons, offset, 1)); u16 c = w & 0xff; 2873 if (w & hi_font_mask) c |= 0x100; 2875 return c; } /* used by vcs - note the word offset */ 2879 unsigned short *screen_pos(int currcons, int w_offset, int viewed) { 2881 return screenpos(currcons, 2 * w_offset, viewed); } 2884 void getconsxy(int currcons, char *p) { p[0] = x; p[1] = y; } 2890 void putconsxy(int currcons, char *p) { gotoxy(currcons, p[0], p[1]); set_cursor(currcons); } 2896 u16 vcs_scr_readw(int currcons, const u16 *org) { 2898 if ((unsigned long)org == pos && softcursor_original != -1) 2899 return softcursor_original; 2900 return scr_readw(org); } 2903 void vcs_scr_writew(int currcons, u16 val, u16 *org) { scr_writew(val, org); 2906 if ((unsigned long)org == pos) { softcursor_original = -1; add_softcursor(currcons); } } 2912 static int pm_con_request(struct pm_dev *dev, pm_request_t rqst, void *data) { 2914 switch (rqst) { 2916 case PM_RESUME: unblank_screen(); 2918 break; 2919 case PM_SUSPEND: do_blank_screen(0); 2921 break; } 2923 return 0; } /* * Visible symbols for modules */ EXPORT_SYMBOL(color_table); EXPORT_SYMBOL(default_red); EXPORT_SYMBOL(default_grn); EXPORT_SYMBOL(default_blu); EXPORT_SYMBOL(video_font_height); EXPORT_SYMBOL(video_scan_lines); EXPORT_SYMBOL(vc_resize); EXPORT_SYMBOL(fg_console); EXPORT_SYMBOL(console_blank_hook); #ifdef CONFIG_VT EXPORT_SYMBOL(vt_cons); #endif #ifndef VT_SINGLE_DRIVER EXPORT_SYMBOL(take_over_console); EXPORT_SYMBOL(give_up_console); #endif