/* * 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> * * 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 * * Removed console_lock, enabled interrupts across all console operations * 13 March 2001, Andrew Morton * * Fixed UTF-8 mode so alternate charset modes always work according * to control sequences interpreted in do_con_trol function * preserving backward VT100 semigraphics compatibility, * malformed UTF sequences represented as sequences of replacement glyphs, * original codes or '?' as a last resort if replacement glyph is undefined * by Adam Tla/lka <atlka@pg.gda.pl>, Aug 2006
*/
/* * Here is the default bell parameters: 750HZ, 1/8th of a second
*/ #define DEFAULT_BELL_PITCH 750 #define DEFAULT_BELL_DURATION (HZ/8) #define DEFAULT_CURSOR_BLINK_MS 200
/* * ignore_poke: don't unblank the screen when things are typed. This is * mainly for the privacy of braille terminal users.
*/ staticint ignore_poke;
int do_poke_blanked_console; int console_blanked;
EXPORT_SYMBOL(console_blanked);
/* * fg_console is the current virtual console, * last_console is the last used one, * want_console is the console we want to switch to, * saved_* variants are for save/restore around kernel debugger enter/leave
*/ int fg_console;
EXPORT_SYMBOL(fg_console); int last_console; int want_console = -1;
/* * 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".
*/ staticstruct 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 schedule * all flip requests to process context with schedule-task() and run it from * console_callback().
*/
/* * For the same reason, we defer scrollback to the console callback.
*/ staticint scrollback_delta;
/* * Hook so that the power management routines can (un)blank * the console on our behalf.
*/ int (*console_blank_hook)(int);
EXPORT_SYMBOL(console_blank_hook);
/* * /sys/class/tty/tty0/ * * the attribute 'active' contains the name of the current vc * console and it supports poll() to detect vc switches
*/ staticstruct device *tty0dev;
/* * Notifier list for console events.
*/ static ATOMIC_NOTIFIER_HEAD(vt_notifier_list);
int register_vt_notifier(struct notifier_block *nb)
{ return atomic_notifier_chain_register(&vt_notifier_list, nb);
}
EXPORT_SYMBOL_GPL(register_vt_notifier);
int unregister_vt_notifier(struct notifier_block *nb)
{ return atomic_notifier_chain_unregister(&vt_notifier_list, nb);
}
EXPORT_SYMBOL_GPL(unregister_vt_notifier);
staticvoid con_putc(struct vc_data *vc, u16 ca, unsignedint y, unsignedint x)
{ if (vc->vc_sw->con_putc)
vc->vc_sw->con_putc(vc, ca, y, x); else
vc->vc_sw->con_putcs(vc, &ca, 1, y, x);
}
/* Called from the keyboard irq path.. */ staticinlinevoid scrolldelta(int lines)
{ /* FIXME */ /* scrolldelta needs some kind of consistency lock, but the BKL was
and still is not protecting versus the scheduled back end */
scrollback_delta += lines;
schedule_console_callback();
}
/* * Called from vcs_read() to make sure unicode screen retrieval is possible. * This will initialize the unicode screen buffer if not already done. * This returns 0 if OK, or a negative error code otherwise. * In particular, -ENODATA is returned if the console is not in UTF-8 mode.
*/ int vc_uniscr_check(struct vc_data *vc)
{
u32 **uni_lines; unsignedshort *p; int x, y, mask;
WARN_CONSOLE_UNLOCKED();
if (!vc->vc_utf) return -ENODATA;
if (vc->vc_uni_lines) return 0;
uni_lines = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows); if (!uni_lines) return -ENOMEM;
/* * Let's populate it initially with (imperfect) reverse translation. * This is the next best thing we can do short of having it enabled * from the start even when no users rely on this functionality. True * unicode content will be available after a complete screen refresh.
*/
p = (unsignedshort *)vc->vc_origin;
mask = vc->vc_hi_font_mask | 0xff; for (y = 0; y < vc->vc_rows; y++) {
u32 *line = uni_lines[y]; for (x = 0; x < vc->vc_cols; x++) {
u16 glyph = scr_readw(p++) & mask;
line[x] = inverse_translate(vc, glyph, true);
}
}
vc->vc_uni_lines = uni_lines;
return 0;
}
/* * Called from vcs_read() to get the unicode data from the screen. * This must be preceded by a successful call to vc_uniscr_check() once * the console lock has been taken.
*/ void vc_uniscr_copy_line(conststruct vc_data *vc, void *dest, bool viewed, unsignedint row, unsignedint col, unsignedint nr)
{
u32 **uni_lines = vc->vc_uni_lines; int offset = row * vc->vc_size_row + col * 2; unsignedlong pos;
if (WARN_ON_ONCE(!uni_lines)) return;
pos = (unsignedlong)screenpos(vc, offset, viewed); if (pos >= vc->vc_origin && pos < vc->vc_scr_end) { /* * Desired position falls in the main screen buffer. * However the actual row/col might be different if * scrollback is active.
*/
row = (pos - vc->vc_origin) / vc->vc_size_row;
col = ((pos - vc->vc_origin) % vc->vc_size_row) / 2;
memcpy(dest, &uni_lines[row][col], nr * sizeof(u32));
} else { /* * Scrollback is active. For now let's simply backtranslate * the screen glyphs until the unicode screen buffer does * synchronize with console display drivers for a scrollback * buffer of its own.
*/
u16 *p = (u16 *)pos; int mask = vc->vc_hi_font_mask | 0xff;
u32 *uni_buf = dest; while (nr--) {
u16 glyph = scr_readw(p++) & mask;
*uni_buf++ = inverse_translate(vc, glyph, true);
}
}
}
/* * ++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; if (!vc->vc_can_do_color) return _intensity |
(_italic << 1) |
(_underline << 2) |
(_reverse << 3) |
(_blink << 7); if (_italic)
a = (a & 0xF0) | vc->vc_itcolor; elseif (_underline)
a = (a & 0xf0) | vc->vc_ulcolor; elseif (_intensity == VCI_HALF_BRIGHT)
a = (a & 0xf0) | vc->vc_halfcolor; if (_reverse)
a = (a & 0x88) | (((a >> 4) | (a << 4)) & 0x77); if (_blink)
a ^= 0x80; if (_intensity == VCI_BOLD)
a ^= 0x08; if (vc->vc_hi_font_mask == 0x100)
a <<= 1; return a;
}
}
/* Note: inverting the screen twice should revert to the original state */ void invert_screen(struct vc_data *vc, int offset, int count, bool viewed)
{
u16 *p;
WARN_CONSOLE_UNLOCKED();
count /= 2;
p = screenpos(vc, offset, viewed); if (vc->vc_sw->con_invert_region) {
vc->vc_sw->con_invert_region(vc, p, count);
} else {
u16 *q = p; int cnt = count;
u16 a;
if (!vc->vc_can_do_color) { while (cnt--) {
a = scr_readw(q);
a ^= 0x0800;
scr_writew(a, q);
q++;
}
} elseif (vc->vc_hi_font_mask == 0x100) { while (cnt--) {
a = scr_readw(q);
a = (a & 0x11ff) |
((a & 0xe000) >> 4) |
((a & 0x0e00) << 4);
scr_writew(a, q);
q++;
}
} else { while (cnt--) {
a = scr_readw(q);
a = (a & 0x88ff) |
((a & 0x7000) >> 4) |
((a & 0x0700) << 4);
scr_writew(a, q);
q++;
}
}
}
if (con_should_update(vc))
do_update_region(vc, (unsignedlong) p, count);
notify_update(vc);
}
/* used by selection: complement pointer position */ void complement_pos(struct vc_data *vc, int offset)
{ staticint old_offset = -1; staticunsignedshort old; staticunsignedshort oldx, oldy;
/* * The legacy way for flushing the scrollback buffer is to use a side * effect of the con_switch method. We do it only on the foreground * console as background consoles have no scrollback buffers in that * case and we obviously don't want to switch to them.
*/
hide_cursor(vc);
vc->vc_sw->con_switch(vc);
set_cursor(vc);
}
void redraw_screen(struct vc_data *vc, int is_switch)
{ int redraw = 0;
WARN_CONSOLE_UNLOCKED();
if (!vc) { /* strange ... */ /* printk("redraw_screen: tty %d not allocated ??\n", new_console+1); */ return;
}
if (is_switch) { struct vc_data *old_vc = vc_cons[fg_console].d; if (old_vc == vc) return; if (!con_is_visible(vc))
redraw = 1;
*vc->vc_display_fg = vc;
fg_console = vc->vc_num;
hide_cursor(old_vc); if (!con_is_visible(old_vc)) {
save_screen(old_vc);
set_origin(old_vc);
} if (tty0dev)
sysfs_notify(&tty0dev->kobj, NULL, "active");
} else {
hide_cursor(vc);
redraw = 1;
}
if (redraw) { bool update; int old_was_color = vc->vc_can_do_color;
set_origin(vc);
update = vc->vc_sw->con_switch(vc);
set_palette(vc); /* * If console changed from mono<->color, the best we can do * is to clear the buffer attributes. As it currently stands, * rebuilding new attributes from the old buffer is not doable * without overly complex code.
*/ if (old_was_color != vc->vc_can_do_color) {
update_attr(vc);
clear_buffer_attributes(vc);
}
/* * 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]
*/ #define VC_MAXCOL (32767) #define VC_MAXROW (32767)
int vc_allocate(unsignedint currcons) /* return 0 on success */
{ struct vt_notifier_param param; struct vc_data *vc; int err;
WARN_CONSOLE_UNLOCKED();
if (currcons >= MAX_NR_CONSOLES) return -ENXIO;
if (vc_cons[currcons].d) return 0;
/* 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 */
param.vc = vc = kzalloc(sizeof(struct vc_data), GFP_KERNEL); if (!vc) return -ENOMEM;
/* If no drivers have overridden us and the user didn't pass a
boot option, default to displaying the cursor */ if (global_cursor_default == -1)
global_cursor_default = 1;
staticinlineint resize_screen(struct vc_data *vc, int width, int height, bool from_user)
{ /* Resizes the resolution of the display adapater */ int err = 0;
if (vc->vc_sw->con_resize)
err = vc->vc_sw->con_resize(vc, width, height, from_user);
return err;
}
/** * vc_do_resize - resizing method for the tty * @tty: tty being resized * @vc: virtual console private data * @cols: columns * @lines: lines * @from_user: invoked by a user? * * Resize a virtual console, clipping according to the actual constraints. If * the caller passes a tty structure then update the termios winsize * information and perform any necessary signal handling. * * Locking: Caller must hold the console semaphore. Takes the termios rwsem and * ctrl.lock of the tty IFF a tty is passed.
*/ staticint vc_do_resize(struct tty_struct *tty, struct vc_data *vc, unsignedint cols, unsignedint lines, bool from_user)
{ unsignedlong old_origin, new_origin, new_scr_end, rlth, rrem, err = 0; unsignedlong end; unsignedint old_rows, old_row_size, first_copied_row; unsignedint new_cols, new_rows, new_row_size, new_screen_size; unsignedshort *oldscreen, *newscreen;
u32 **new_uniscr = NULL;
WARN_CONSOLE_UNLOCKED();
if (cols > VC_MAXCOL || lines > VC_MAXROW) return -EINVAL;
if (new_cols == vc->vc_cols && new_rows == vc->vc_rows) { /* * This function is being called here to cover the case * where the userspace calls the FBIOPUT_VSCREENINFO twice, * passing the same fb_var_screeninfo containing the fields * yres/xres equal to a number non-multiple of vc_font.height * and yres_virtual/xres_virtual equal to number lesser than the * vc_font.height and yres/xres. * In the second call, the struct fb_var_screeninfo isn't * being modified by the underlying driver because of the * if above, and this causes the fbcon_display->vrows to become * negative and it eventually leads to out-of-bound * access by the imageblit function. * To give the correct values to the struct and to not have * to deal with possible errors from the code below, we call * the resize_screen here as well.
*/ return resize_screen(vc, new_cols, new_rows, from_user);
}
if (new_screen_size > KMALLOC_MAX_SIZE || !new_screen_size) return -EINVAL;
newscreen = kzalloc(new_screen_size, GFP_USER); if (!newscreen) return -ENOMEM;
if (vc->vc_uni_lines) {
new_uniscr = vc_uniscr_alloc(new_cols, new_rows); if (!new_uniscr) {
kfree(newscreen); return -ENOMEM;
}
}
if (vc->state.y > new_rows) { if (old_rows - vc->state.y < new_rows) { /* * Cursor near the bottom, copy contents from the * bottom of buffer
*/
first_copied_row = (old_rows - new_rows);
} else { /* * Cursor is in no man's land, copy 1/2 screenful * from the top and bottom of cursor position
*/
first_copied_row = (vc->state.y - new_rows/2);
}
old_origin += first_copied_row * old_row_size;
} else
first_copied_row = 0;
end = old_origin + old_row_size * min(old_rows, new_rows);
/* do part of a reset_terminal() */
vc->vc_top = 0;
vc->vc_bottom = vc->vc_rows;
gotoxy(vc, vc->state.x, vc->state.y);
save_cur(vc);
if (tty) { /* Rewrite the requested winsize data with the actual
resulting sizes */ struct winsize ws;
memset(&ws, 0, sizeof(ws));
ws.ws_row = vc->vc_rows;
ws.ws_col = vc->vc_cols;
ws.ws_ypixel = vc->vc_scan_lines;
tty_do_resize(tty, &ws);
}
if (con_is_visible(vc))
update_screen(vc);
vt_event_post(VT_EVENT_RESIZE, vc->vc_num, vc->vc_num);
notify_update(vc); return err;
}
/** * __vc_resize - resize a VT * @vc: virtual console * @cols: columns * @rows: rows * @from_user: invoked by a user? * * Resize a virtual console as seen from the console end of things. We use the * common vc_do_resize() method to update the structures. * * Locking: The caller must hold the console sem to protect console internals * and @vc->port.tty.
*/ int __vc_resize(struct vc_data *vc, unsignedint cols, unsignedint rows, bool from_user)
{ return vc_do_resize(vc->port.tty, vc, cols, rows, from_user);
}
EXPORT_SYMBOL(__vc_resize);
/** * vt_resize - resize a VT * @tty: tty to resize * @ws: winsize attributes * * Resize a virtual terminal. This is called by the tty layer as we register * our own handler for resizing. The mutual helper does all the actual work. * * Locking: Takes the console sem and the called methods then take the tty * termios_rwsem and the tty ctrl.lock in that order.
*/ staticint vt_resize(struct tty_struct *tty, struct winsize *ws)
{ struct vc_data *vc = tty->driver_data; int ret;
/* * 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.
*/ staticvoid gotoxy(struct vc_data *vc, int new_x, int new_y)
{ int min_y, max_y;
/* for absolute user moves, when decom is set */ staticvoid gotoxay(struct vc_data *vc, int new_x, int new_y)
{
gotoxy(vc, new_x, vc->vc_decom ? (vc->vc_top + new_y) : new_y);
}
staticvoid rgb_background(struct vc_data *vc, conststruct rgb *c)
{ /* For backgrounds, err on the dark side. */
vc->state.color = (vc->state.color & 0x0f)
| (c->r&0x80) >> 1 | (c->g&0x80) >> 2 | (c->b&0x80) >> 3;
}
/* * ITU T.416 Higher colour modes. They break the usual properties of SGR codes * and thus need to be detected and ignored by hand. That standard also * wants : rather than ; as separators but sequences containing : are currently * completely ignored by the parser. * * Subcommands 3 (CMY) and 4 (CMYK) are so insane there's no point in * supporting them.
*/ staticint vc_t416_color(struct vc_data *vc, int i, void(*set_color)(struct vc_data *vc, conststruct rgb *c))
{ struct rgb c;
/* * Handle ascii characters in control sequences and change states accordingly. * E.g. ESC sets the state of vc to ESesc. * * Returns: true if @c handled.
*/ staticbool handle_ascii(struct tty_struct *tty, struct vc_data *vc, u8 c)
{ switch (c) { case ASCII_NULL: returntrue; case ASCII_BELL: if (ansi_control_string(vc->vc_state))
vc->vc_state = ESnormal; elseif (vc->vc_bell_duration)
kd_mksound(vc->vc_bell_pitch, vc->vc_bell_duration); returntrue; case ASCII_BACKSPACE:
bs(vc); returntrue; case ASCII_HTAB:
vc->vc_pos -= (vc->state.x << 1);
/* console_lock is held */ staticvoid do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
{ /* * Control characters can be used in the _middle_ * of an escape sequence, aside from ANSI control strings.
*/ if (ansi_control_string(vc->vc_state) && c >= ASCII_IGNORE_FIRST &&
c <= ASCII_IGNORE_LAST) return;