/* * 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;
staticinlineint vc_translate_ascii(conststruct vc_data *vc, int c)
{ if (IS_ENABLED(CONFIG_CONSOLE_TRANSLATIONS)) { if (vc->vc_toggle_meta)
c |= 0x80;
return vc->vc_translate[c];
}
return c;
}
/** * vc_sanitize_unicode - Replace invalid Unicode code points with ``U+FFFD`` * @c: the received code point
*/ staticinlineint vc_sanitize_unicode(constint c)
{ if (c >= 0xd800 && c <= 0xdfff) return 0xfffd;
return c;
}
/** * vc_translate_unicode - Combine UTF-8 into Unicode in &vc_data.vc_utf_char * @vc: virtual console * @c: UTF-8 byte to translate * @rescan: set to true iff @c wasn't consumed here and needs to be re-processed * * * &vc_data.vc_utf_char is the being-constructed Unicode code point. * * &vc_data.vc_utf_count is the number of continuation bytes still expected to * arrive. * * &vc_data.vc_npar is the number of continuation bytes arrived so far. * * Return: * * %-1 - Input OK so far, @c consumed, further bytes expected. * * %0xFFFD - Possibility 1: input invalid, @c may have been consumed (see * desc. of @rescan). Possibility 2: input OK, @c consumed, * ``U+FFFD`` is the resulting code point. ``U+FFFD`` is valid, * ``REPLACEMENT CHARACTER``. * * otherwise - Input OK, @c consumed, resulting code point returned.
*/ staticint vc_translate_unicode(struct vc_data *vc, int c, bool *rescan)
{ staticconst u32 utf8_length_changes[] = {0x7f, 0x7ff, 0xffff, 0x10ffff};
/* Continuation byte received */ if ((c & 0xc0) == 0x80) { /* Unexpected continuation byte? */ if (!vc->vc_utf_count) goto bad_sequence;
/* Got a whole character */
c = vc->vc_utf_char; /* Reject overlong sequences */ if (c <= utf8_length_changes[vc->vc_npar - 1] ||
c > utf8_length_changes[vc->vc_npar]) goto bad_sequence;
return vc_sanitize_unicode(c);
}
/* Single ASCII byte or first byte of a sequence received */ if (vc->vc_utf_count) { /* A continuation byte was expected */
*rescan = true;
vc->vc_utf_count = 0; goto bad_sequence;
}
/* Nothing to do if an ASCII byte was received */ if (c <= 0x7f) return c;
staticint vc_translate(struct vc_data *vc, int *c, bool *rescan)
{ /* Do no translation at all in control states */ if (vc->vc_state != ESnormal) return *c;
if (vc->vc_utf && !vc->vc_disp_ctrl) return *c = vc_translate_unicode(vc, *c, rescan);
/* no utf or alternate charset mode */ return vc_translate_ascii(vc, *c);
}
staticbool vc_is_control(struct vc_data *vc, int tc, int c)
{ /* * 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.
*/ staticconst u32 CTRL_ACTION = BIT(ASCII_NULL) |
GENMASK(ASCII_SHIFTIN, ASCII_BELL) | BIT(ASCII_CANCEL) |
BIT(ASCII_SUBSTITUTE) | BIT(ASCII_ESCAPE); /* Cannot be overridden by disp_ctrl */ staticconst u32 CTRL_ALWAYS = BIT(ASCII_NULL) | BIT(ASCII_BACKSPACE) |
BIT(ASCII_LINEFEED) | BIT(ASCII_SHIFTIN) | BIT(ASCII_SHIFTOUT) |
BIT(ASCII_CAR_RET) | BIT(ASCII_FORMFEED) | BIT(ASCII_ESCAPE);
if (vc->vc_state != ESnormal) returntrue;
if (!tc) returntrue;
/* * 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.
*/ if (c < BITS_PER_TYPE(CTRL_ALWAYS)) { if (vc->vc_disp_ctrl) return CTRL_ALWAYS & BIT(c); else return vc->vc_utf || (CTRL_ACTION & BIT(c));
}
if (c == ASCII_DEL && !vc->vc_disp_ctrl) returntrue;
#define UCS_ZWS 0x200b /* Zero Width Space */ #define UCS_VS16 0xfe0f /* Variation Selector 16 */ #define UCS_REPLACEMENT 0xfffd /* Replacement Character */
staticint vc_process_ucs(struct vc_data *vc, int *c, int *tc)
{
u32 prev_c, curr_c = *c;
if (ucs_is_double_width(curr_c)) { /* * The Unicode screen memory is allocated only when * required. This is one such case as we need to remember * which displayed characters are double-width.
*/
vc_uniscr_check(vc); return 2;
}
if (!ucs_is_zero_width(curr_c)) return 1;
/* From here curr_c is known to be zero-width. */
if (ucs_is_double_width(vc_uniscr_getc(vc, -2))) { /* * Let's merge this zero-width code point with the preceding * double-width code point by replacing the existing * zero-width space padding. To do so we rewind one column * and pretend this has a width of 1. * We give the legacy display the same initial space padding.
*/
vc_con_rewind(vc);
*tc = ' '; return 1;
}
/* From here the preceding character, if any, must be single-width. */
prev_c = vc_uniscr_getc(vc, -1);
if (curr_c == UCS_VS16 && prev_c != 0) { /* * VS16 (U+FE0F) is special. It typically turns the preceding * single-width character into a double-width one. Let it * have a width of 1 effectively making the combination with * the preceding character double-width.
*/
*tc = ' '; return 1;
}
staticint vc_get_glyph(struct vc_data *vc, int tc)
{ int glyph = conv_uni_to_pc(vc, tc);
u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
if (!(glyph & ~charmask)) return glyph;
if (glyph == -1) return -1; /* nothing to display */
/* Glyph not found */ if ((!vc->vc_utf || vc->vc_disp_ctrl || tc < 128) && !(tc & ~charmask)) { /* * In legacy mode use the glyph we get by a 1:1 mapping. * This would make absolutely no sense with Unicode in mind, but do this for * ASCII characters since a font may lack Unicode mapping info and we don't * want to end up with having question marks only.
*/ return tc;
}
/* * The Unicode screen memory is allocated only when required. * This is one such case: we're about to "cheat" with the displayed * character meaning the simple screen buffer won't hold the original * information, whereas the Unicode screen buffer always does.
*/
vc_uniscr_check(vc);
/* Try getting a simpler fallback character. */
tc = ucs_get_fallback(tc); if (tc) return vc_get_glyph(vc, tc);
staticint vc_con_write_normal(struct vc_data *vc, int tc, int c, struct vc_draw_region *draw)
{ int next_c; unsignedchar vc_attr = vc->vc_attr;
u16 himask = vc->vc_hi_font_mask;
u8 width = 1; bool inverse = false;
if (vc->vc_utf && !vc->vc_disp_ctrl) {
width = vc_process_ucs(vc, &c, &tc); if (!width) goto out;
}
/* Now try to find out how to display it */
tc = vc_get_glyph(vc, tc); if (tc == -1) return -1; /* nothing to display */ if (tc < 0) {
inverse = true;
tc = conv_uni_to_pc(vc, '?'); if (tc < 0)
tc = '?';
/* A space is printed in the second column */
tc = conv_uni_to_pc(vc, ' '); if (tc < 0)
tc = ' '; /* * Store a zero-width space in the Unicode screen given that * the previous code point is semantically double width.
*/
next_c = UCS_ZWS;
}
/* * This is the console switching callback. * * Doing console switching in a process context 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.
*/ staticvoid console_callback(struct work_struct *ignored)
{
console_lock();
if (want_console >= 0) { if (want_console != fg_console &&
vc_cons_allocated(want_console)) {
hide_cursor(vc_cons[fg_console].d);
change_console(vc_cons[want_console].d); /* we only changed when the console had already been allocated - a new console is not created
in an interrupt routine */
}
want_console = -1;
} if (do_poke_blanked_console) { /* do not unblank for a LED change */
do_poke_blanked_console = 0;
poke_blanked_console();
} if (scrollback_delta) { struct vc_data *vc = vc_cons[fg_console].d;
clear_selection(); if (vc->vc_mode == KD_TEXT && vc->vc_sw->con_scrolldelta)
vc->vc_sw->con_scrolldelta(vc, scrollback_delta);
scrollback_delta = 0;
} if (blank_timer_expired) {
do_blank_screen(0);
blank_timer_expired = 0;
}
notify_update(vc_cons[fg_console].d);
console_unlock();
}
int set_console(int nr)
{ struct vc_data *vc = vc_cons[fg_console].d;
/* * Console switch will fail in console_callback() or * change_console() so there is no point scheduling * the callback * * Existing set_console() users don't check the return * value so this shouldn't break anything
*/ return -EINVAL;
}
want_console = nr;
schedule_console_callback();
return 0;
}
struct tty_driver *console_driver;
#ifdef CONFIG_VT_CONSOLE
/** * vt_kmsg_redirect() - sets/gets the kernel message console * @new: the new virtual terminal number or -1 if the console should stay * unchanged * * By default, the kernel messages are always printed on the current virtual * console. However, the user may modify that default with the * %TIOCL_SETKMSGREDIRECT ioctl call. * * This function sets the kernel message console to be @new. It returns the old * virtual console number. The virtual terminal number %0 (both as parameter and * return value) means no redirection (i.e. always printed on the currently * active console). * * The parameter -1 means that only the current console is returned, but the * value is not modified. You may use the macro vt_get_kmsg_redirect() in that * case to make the code more understandable. * * When the kernel is compiled without %CONFIG_VT_CONSOLE, this function ignores * the parameter and always returns %0.
*/ int vt_kmsg_redirect(intnew)
{ staticint kmsg_con;
if (new != -1) return xchg(&kmsg_con, new); else return kmsg_con;
}
/* * Console on virtual terminal * * The console must be locked when we get here.
*/
/* * Generally a bit racy with respect to console_lock();. * * There are some functions which don't need it. * * There are some functions which can sleep for arbitrary periods * (paste_selection) but we don't need the lock there anyway. * * set_selection_user has locking, and definitely needs it
*/
if (current->signal->tty != tty && !capable(CAP_SYS_ADMIN)) return -EPERM; if (get_user(type, p)) return -EFAULT;
ret = 0;
switch (type) { case TIOCL_SETSEL: return set_selection_user(param, tty); case TIOCL_PASTESEL: if (!capable(CAP_SYS_ADMIN)) return -EPERM; return paste_selection(tty); case TIOCL_UNBLANKSCREEN:
console_lock();
unblank_screen();
console_unlock(); break; case TIOCL_SELLOADLUT: if (!capable(CAP_SYS_ADMIN)) return -EPERM; return sel_loadlut(param_aligned32); case TIOCL_GETSHIFTSTATE: /* * 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 = vt_get_shift_state(); return put_user(data, p); case TIOCL_GETMOUSEREPORTING:
console_lock(); /* May be overkill */
data = mouse_reporting();
console_unlock(); return put_user(data, p); case TIOCL_SETVESABLANK: return set_vesa_blanking(param); case TIOCL_GETKMSGREDIRECT:
data = vt_get_kmsg_redirect(); return put_user(data, p); case TIOCL_SETKMSGREDIRECT: if (!capable(CAP_SYS_ADMIN)) return -EPERM;
if (get_user(data, p+1)) return -EFAULT;
vt_kmsg_redirect(data);
break; case TIOCL_GETFGCONSOLE: /* * No locking needed as this is a transiently correct return * anyway if the caller hasn't disabled switching.
*/ return fg_console; case TIOCL_SCROLLCONSOLE: if (get_user(lines, (s32 __user *)param_aligned32)) return -EFAULT;
/* * Needs the console lock here. Note that lots of other calls * need fixing before the lock is actually useful!
*/
console_lock();
scrollfront(vc_cons[fg_console].d, lines);
console_unlock(); break; case TIOCL_BLANKSCREEN: /* until explicitly unblanked, not only poked */
console_lock();
ignore_poke = 1;
do_blank_screen(0);
console_unlock(); break; case TIOCL_BLANKEDSCREEN: return console_blanked; case TIOCL_GETBRACKETEDPASTE: return get_bracketed_paste(tty); default: return -EINVAL;
}
staticunsignedint con_write_room(struct tty_struct *tty)
{ if (tty->flow.stopped) return 0; return 32768; /* No limit, really; 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...
*/ staticvoid con_throttle(struct tty_struct *tty)
{
}
/* * Turn the Scroll-Lock LED on when the tty is stopped
*/ staticvoid con_stop(struct tty_struct *tty)
{ int console_num; if (!tty) return;
console_num = tty->index; if (!vc_cons_allocated(console_num)) return;
vt_kbd_con_stop(console_num);
}
/* * Turn the Scroll-Lock LED off when the console is started
*/ staticvoid con_start(struct tty_struct *tty)
{ int console_num; if (!tty) return;
console_num = tty->index; if (!vc_cons_allocated(console_num)) return;
vt_kbd_con_start(console_num);
}
/* * We can't deal with anything but the N_TTY ldisc, * because we can sleep in our write() routine.
*/ staticint con_ldisc_ok(struct tty_struct *tty, int ldisc)
{ return ldisc == N_TTY ? 0 : -EINVAL;
}
/* * This routine initializes console interrupts, and does nothing * else. If you want the screen to clear, call tty_write with * the appropriate escape-sequence.
*/
staticint do_bind_con_driver(conststruct consw *csw, int first, int last, int deflt)
{ struct module *owner = csw->owner; constchar *desc = NULL; struct con_driver *con_driver; int i, j = -1, k = -1, retval = -ENODEV;
if (!try_module_get(owner)) return -ENODEV;
WARN_CONSOLE_UNLOCKED();
/* check if driver is registered */ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
con_driver = ®istered_con_driver[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.
*/ if (old_was_color != vc->vc_can_do_color)
clear_buffer_attributes(vc);
}
pr_info("Console: switching "); if (!deflt)
pr_cont("consoles %d-%d ", first + 1, last + 1); if (j >= 0) { struct vc_data *vc = vc_cons[j].d;
first = max(first, con_driver->first);
last = min(last, con_driver->last);
for (i = first; i <= last; i++) { if (con_driver_map[i] == csw) {
module_put(csw->owner);
con_driver_map[i] = NULL;
}
}
if (!con_is_bound(defcsw)) { conststruct consw *defconsw = conswitchp;
defcsw->con_startup();
con_back->flag |= CON_DRIVER_FLAG_INIT; /* * vgacon may change the default driver to point * to dummycon, we restore it here...
*/
conswitchp = defconsw;
}
if (!con_is_bound(csw))
con_driver->flag &= ~CON_DRIVER_FLAG_INIT;
/* ignore return value, binding should not fail */
do_bind_con_driver(defcsw, first, last, deflt);
err:
module_put(owner); return retval;
}
EXPORT_SYMBOL_GPL(do_unbind_con_driver);
staticint vt_bind(struct con_driver *con)
{ conststruct consw *defcsw = NULL, *csw = NULL; int i, more = 1, first = -1, last = -1, deflt = 0;
if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE)) goto err;
csw = con->con;
for (i = 0; i < MAX_NR_CON_DRIVER; i++) { struct con_driver *con = ®istered_con_driver[i];
for (i = con->first; i <= con->last; i++) { if (con_driver_map[i] == defcsw) { if (first == -1)
first = i;
last = i;
more = 1;
} elseif (first != -1) break;
}
if (first == 0 && last == MAX_NR_CONSOLES -1)
deflt = 1;
if (first != -1)
do_bind_con_driver(csw, first, last, deflt);
first = -1;
last = -1;
deflt = 0;
}
err: return 0;
}
staticint vt_unbind(struct con_driver *con)
{ conststruct consw *csw = NULL; int i, more = 1, first = -1, last = -1, deflt = 0; int ret;
if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE)) goto err;
csw = con->con;
while (more) {
more = 0;
for (i = con->first; i <= con->last; i++) { if (con_driver_map[i] == csw) { if (first == -1)
first = i;
last = i;
more = 1;
} elseif (first != -1) break;
}
if (first == 0 && last == MAX_NR_CONSOLES -1)
deflt = 1;
if (first != -1) {
ret = do_unbind_con_driver(csw, first, last, deflt); if (ret != 0) return ret;
}
/** * con_is_bound - checks if driver is bound to the console * @csw: console driver * * RETURNS: zero if unbound, nonzero if bound * * Drivers can call this and if zero, they should release * all resources allocated on &consw.con_startup()
*/ int con_is_bound(conststruct consw *csw)
{ int i, bound = 0;
WARN_CONSOLE_UNLOCKED();
for (i = 0; i < MAX_NR_CONSOLES; i++) { if (con_driver_map[i] == csw) {
bound = 1; break;
}
}
return bound;
}
EXPORT_SYMBOL(con_is_bound);
/** * con_is_visible - checks whether the current console is visible * @vc: virtual console * * RETURNS: zero if not visible, nonzero if visible
*/ bool con_is_visible(conststruct vc_data *vc)
{
WARN_CONSOLE_UNLOCKED();
/** * con_debug_enter - prepare the console for the kernel debugger * @vc: virtual console * * Called when the console is taken over by the kernel debugger, this * function needs to save the current console state, then put the console * into a state suitable for the kernel debugger.
*/ void con_debug_enter(struct vc_data *vc)
{
saved_fg_console = fg_console;
saved_last_console = last_console;
saved_want_console = want_console;
saved_vc_mode = vc->vc_mode;
saved_console_blanked = console_blanked;
vc->vc_mode = KD_TEXT;
console_blanked = 0; if (vc->vc_sw->con_debug_enter)
vc->vc_sw->con_debug_enter(vc); #ifdef CONFIG_KGDB_KDB /* Set the initial LINES variable if it is not already set */ if (vc->vc_rows < 999) { int linecount; char lns[4]; constchar *setargs[3] = { "set", "LINES",
lns,
}; if (kdbgetintenv(setargs[0], &linecount)) {
snprintf(lns, 4, "%i", vc->vc_rows);
kdb_set(2, setargs);
}
} if (vc->vc_cols < 999) { int colcount; char cols[4]; constchar *setargs[3] = { "set", "COLUMNS",
cols,
}; if (kdbgetintenv(setargs[0], &colcount)) {
snprintf(cols, 4, "%i", vc->vc_cols);
kdb_set(2, setargs);
}
} #endif/* CONFIG_KGDB_KDB */
}
EXPORT_SYMBOL_GPL(con_debug_enter);
/** * con_debug_leave - restore console state * * Restore the console state to what it was before the kernel debugger * was invoked.
*/ void con_debug_leave(void)
{ struct vc_data *vc;
/** * do_unregister_con_driver - unregister console driver from console layer * @csw: console driver * * DESCRIPTION: All drivers that registers to the console layer must * call this function upon exit, or if the console driver is in a state * where it won't be able to handle console services, such as the * framebuffer console without loaded framebuffer drivers. * * The driver must unbind first prior to unregistration.
*/ int do_unregister_con_driver(conststruct consw *csw)
{ int i;
/* cannot unregister a bound driver */ if (con_is_bound(csw)) return -EBUSY;
if (csw == conswitchp) return -EINVAL;
for (i = 0; i < MAX_NR_CON_DRIVER; i++) { struct con_driver *con_driver = ®istered_con_driver[i];
if (con_driver->con == csw) { /* * Defer the removal of the sysfs entries since that * will acquire the kernfs s_active lock and we can't * acquire this lock while holding the console lock: * the unbind sysfs entry imposes already the opposite * order. Reset con already here to prevent any later * lookup to succeed and mark this slot as zombie, so * it won't get reused until we complete the removal * in the deferred work.
*/
con_driver->con = NULL;
con_driver->flag = CON_DRIVER_FLAG_ZOMBIE;
schedule_work(&con_driver_unregister_work);
/* * 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. * * do_take_over_console is basically a register followed by bind
*/ int do_take_over_console(conststruct consw *csw, int first, int last, int deflt)
{ int err;
err = do_register_con_driver(csw, first, last); /* * If we get an busy error we still want to bind the console driver * and return success, as we may have unbound the console driver * but not unregistered it.
*/ if (err == -EBUSY)
err = 0; if (!err)
do_bind_con_driver(csw, first, last, deflt);
/* * give_up_console is a wrapper to unregister_con_driver. It will only * work if driver is fully unbound.
*/ void give_up_console(conststruct consw *csw)
{
console_lock();
do_unregister_con_driver(csw);
console_unlock();
}
EXPORT_SYMBOL(give_up_console);
staticint __init vtconsole_class_init(void)
{ int i;
i = class_register(&vtconsole_class); if (i)
pr_warn("Unable to create vt console class; errno = %d\n", i);
/* Add system drivers to sysfs */ for (i = 0; i < MAX_NR_CON_DRIVER; i++) { struct con_driver *con = ®istered_con_driver[i];
save_screen(vc); /* In case we need to reset origin, blanking hook returns 1 */
i = vc->vc_sw->con_blank(vc, vesa_off_interval ? VESA_VSYNC_SUSPEND :
(vesa_blank_mode + 1), 0);
console_blanked = fg_console + 1; if (i)
set_origin(vc);
if (console_blank_hook && console_blank_hook(1)) return;
/* * Called by timer as well as from vt_console_driver
*/ void do_unblank_screen(int leaving_gfx)
{ struct vc_data *vc;
/* This should now always be called from a "sane" (read: can schedule) * context for the sake of the low level drivers, except in the special * case of oops_in_progress
*/ if (!oops_in_progress)
might_sleep();
WARN_CONSOLE_UNLOCKED();
ignore_poke = 0; if (!console_blanked) return; if (!vc_cons_allocated(fg_console)) { /* impossible */
pr_warn("unblank_screen: tty %d not allocated ??\n",
fg_console + 1); return;
}
vc = vc_cons[fg_console].d; if (vc->vc_mode != KD_TEXT) return; /* but leave console_blanked != 0 */
console_blanked = 0; if (vc->vc_sw->con_blank(vc, VESA_NO_BLANKING, leaving_gfx)) /* Low-level driver cannot restore -> do it ourselves */
update_screen(vc); if (console_blank_hook)
console_blank_hook(0);
set_palette(vc);
set_cursor(vc);
vt_event_post(VT_EVENT_UNBLANK, vc->vc_num, vc->vc_num);
notify_update(vc);
}
EXPORT_SYMBOL(do_unblank_screen);
/* * This is called by the outside world to cause a forced unblank, mostly for * oopses. Currently, I just call do_unblank_screen(0), but we could eventually * call it with 1 as an argument and so force a mode restore... that may kill * X or at least garbage the screen but would also make the Oops visible...
*/ staticvoid unblank_screen(void)
{
do_unblank_screen(0);
}
/* * We defer the timer blanking to work queue so it can take the console mutex * (console operations can still happen at irq time, but only from printk which * has the console mutex. Not perfect yet, but better than no locking
*/ staticvoid blank_screen_t(struct timer_list *unused)
{
blank_timer_expired = 1;
schedule_work(&console_work);
}
/* Add this so we quickly catch whoever might call us in a non * safe context. Nowadays, unblank_screen() isn't to be called in * atomic contexts and is allowed to schedule (with the special case * of oops_in_progress, but that isn't of any concern for this * function. --BenH.
*/
might_sleep();
/* This isn't perfectly race free, but a race here would be mostly harmless, * at worst, we'll do a spurious blank and it's unlikely
*/
timer_delete(&console_timer);
blank_timer_expired = 0;
/* * Font switching * * Currently we only support fonts up to 128 pixels wide, at a maximum height * of 128 pixels. Userspace fontdata may have to be 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 up to 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 won't have to convert the fontdata all the time. * /Jes
*/
console_lock(); if (vc->vc_mode != KD_TEXT) {
console_unlock(); return -EINVAL;
} if (vc->vc_sw->con_font_default) { if (vc_is_sel(vc))
clear_selection();
rc = vc->vc_sw->con_font_default(vc, &font, s);
} else
rc = -ENOSYS;
console_unlock(); if (!rc) {
op->width = font.width;
op->height = font.height;
} return rc;
}
int con_font_op(struct vc_data *vc, struct console_font_op *op)
{ switch (op->op) { case KD_FONT_OP_SET: case KD_FONT_OP_SET_TALL: return con_font_set(vc, op); case KD_FONT_OP_GET: case KD_FONT_OP_GET_TALL: return con_font_get(vc, op); case KD_FONT_OP_SET_DEFAULT: return con_font_default(vc, op); case KD_FONT_OP_COPY: /* was buggy and never really used */ return -EINVAL;
} return -ENOSYS;
}
/* * Interface exported to selection and vcs.
*/
/* used by selection */
u16 screen_glyph(conststruct vc_data *vc, int offset)
{
u16 w = scr_readw(screenpos(vc, offset, true));
u16 c = w & 0xff;
if (w & vc->vc_hi_font_mask)
c |= 0x100; return c;
}
EXPORT_SYMBOL_GPL(screen_glyph);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.