diff options
Diffstat (limited to 'drivers/tty/vt/vt.c')
-rw-r--r-- | drivers/tty/vt/vt.c | 1531 |
1 files changed, 831 insertions, 700 deletions
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 38a765eadbe2..9b5b98dfc8b4 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -145,7 +145,7 @@ static void gotoxy(struct vc_data *vc, int new_x, int new_y); static void save_cur(struct vc_data *vc); static void reset_terminal(struct vc_data *vc, int do_clear); static void con_flush_chars(struct tty_struct *tty); -static int set_vesa_blanking(char __user *p); +static int set_vesa_blanking(u8 __user *mode); static void set_cursor(struct vc_data *vc); static void hide_cursor(struct vc_data *vc); static void console_callback(struct work_struct *ignored); @@ -175,7 +175,7 @@ int do_poke_blanked_console; int console_blanked; EXPORT_SYMBOL(console_blanked); -static int vesa_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */ +static enum vesa_blank_mode vesa_blank_mode; static int vesa_off_interval; static int blankinterval; core_param(consoleblank, blankinterval, int, 0444); @@ -286,18 +286,20 @@ static inline bool con_should_update(const struct vc_data *vc) return con_is_visible(vc) && !console_blanked; } -static inline unsigned short *screenpos(const struct vc_data *vc, int offset, - bool viewed) +static inline u16 *screenpos(const struct vc_data *vc, unsigned int offset, + bool viewed) { - unsigned short *p; - - if (!viewed) - p = (unsigned short *)(vc->vc_origin + offset); - else if (!vc->vc_sw->con_screen_pos) - p = (unsigned short *)(vc->vc_visible_origin + offset); + unsigned long origin = viewed ? vc->vc_visible_origin : vc->vc_origin; + + return (u16 *)(origin + offset); +} + +static void con_putc(struct vc_data *vc, u16 ca, unsigned int y, unsigned int x) +{ + if (vc->vc_sw->con_putc) + vc->vc_sw->con_putc(vc, ca, y, x); else - p = vc->vc_sw->con_screen_pos(vc, offset); - return p; + vc->vc_sw->con_putcs(vc, &ca, 1, y, x); } /* Called from the keyboard irq path.. */ @@ -591,18 +593,12 @@ static void con_scroll(struct vc_data *vc, unsigned int top, static void do_update_region(struct vc_data *vc, unsigned long start, int count) { unsigned int xx, yy, offset; - u16 *p; + u16 *p = (u16 *)start; + + offset = (start - vc->vc_origin) / 2; + xx = offset % vc->vc_cols; + yy = offset / vc->vc_cols; - p = (u16 *) start; - if (!vc->vc_sw->con_getxy) { - offset = (start - vc->vc_origin) / 2; - xx = offset % vc->vc_cols; - yy = offset / vc->vc_cols; - } else { - int nxx, nyy; - start = vc->vc_sw->con_getxy(vc, start, &nxx, &nyy); - xx = nxx; yy = nyy; - } for(;;) { u16 attrib = scr_readw(p) & 0xff00; int startx = xx; @@ -625,10 +621,6 @@ static void do_update_region(struct vc_data *vc, unsigned long start, int count) break; xx = 0; yy++; - if (vc->vc_sw->con_getxy) { - p = (u16 *)start; - start = vc->vc_sw->con_getxy(vc, start, NULL, NULL); - } } } @@ -703,7 +695,7 @@ static void update_attr(struct vc_data *vc) /* Note: inverting the screen twice should revert to the original state */ void invert_screen(struct vc_data *vc, int offset, int count, bool viewed) { - unsigned short *p; + u16 *p; WARN_CONSOLE_UNLOCKED(); @@ -762,7 +754,7 @@ void complement_pos(struct vc_data *vc, int offset) old_offset < vc->vc_screenbuf_size) { scr_writew(old, screenpos(vc, old_offset, true)); if (con_should_update(vc)) - vc->vc_sw->con_putc(vc, old, oldy, oldx); + con_putc(vc, old, oldy, oldx); notify_update(vc); } @@ -771,15 +763,14 @@ void complement_pos(struct vc_data *vc, int offset) if (offset != -1 && offset >= 0 && offset < vc->vc_screenbuf_size) { unsigned short new; - unsigned short *p; - p = screenpos(vc, offset, true); + u16 *p = screenpos(vc, offset, true); old = scr_readw(p); new = old ^ vc->vc_complement_mask; scr_writew(new, p); if (con_should_update(vc)) { oldx = (offset >> 1) % vc->vc_cols; oldy = (offset >> 1) / vc->vc_cols; - vc->vc_sw->con_putc(vc, new, oldy, oldx); + con_putc(vc, new, oldy, oldx); } notify_update(vc); } @@ -833,7 +824,7 @@ static void add_softcursor(struct vc_data *vc) i ^= CUR_FG; scr_writew(i, (u16 *)vc->vc_pos); if (con_should_update(vc)) - vc->vc_sw->con_putc(vc, i, vc->state.y, vc->state.x); + con_putc(vc, i, vc->state.y, vc->state.x); } static void hide_softcursor(struct vc_data *vc) @@ -841,8 +832,8 @@ static void hide_softcursor(struct vc_data *vc) if (softcursor_original != -1) { scr_writew(softcursor_original, (u16 *)vc->vc_pos); if (con_should_update(vc)) - vc->vc_sw->con_putc(vc, softcursor_original, - vc->state.y, vc->state.x); + con_putc(vc, softcursor_original, vc->state.y, + vc->state.x); softcursor_original = -1; } } @@ -852,7 +843,7 @@ static void hide_cursor(struct vc_data *vc) if (vc_is_sel(vc)) clear_selection(); - vc->vc_sw->con_cursor(vc, CM_ERASE); + vc->vc_sw->con_cursor(vc, false); hide_softcursor(vc); } @@ -865,7 +856,7 @@ static void set_cursor(struct vc_data *vc) clear_selection(); add_softcursor(vc); if (CUR_SIZE(vc->vc_cursor_type) != CUR_NONE) - vc->vc_sw->con_cursor(vc, CM_DRAW); + vc->vc_sw->con_cursor(vc, true); } else hide_cursor(vc); } @@ -897,21 +888,18 @@ static void flush_scrollback(struct vc_data *vc) WARN_CONSOLE_UNLOCKED(); set_origin(vc); - if (vc->vc_sw->con_flush_scrollback) { - vc->vc_sw->con_flush_scrollback(vc); - } else if (con_is_visible(vc)) { - /* - * When no con_flush_scrollback method is provided then 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); - } + if (!con_is_visible(vc)) + return; + + /* + * 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); } /* @@ -962,7 +950,7 @@ void redraw_screen(struct vc_data *vc, int is_switch) } if (redraw) { - int update; + bool update; int old_was_color = vc->vc_can_do_color; set_origin(vc); @@ -999,7 +987,7 @@ int vc_cons_allocated(unsigned int i) return (i < MAX_NR_CONSOLES && vc_cons[i].d); } -static void visual_init(struct vc_data *vc, int num, int init) +static void visual_init(struct vc_data *vc, int num, bool init) { /* ++Geert: vc->vc_sw->con_init determines console size */ if (vc->vc_sw) @@ -1083,7 +1071,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ vc->port.ops = &vc_port_ops; INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); - visual_init(vc, currcons, 1); + visual_init(vc, currcons, true); if (!*vc->uni_pagedict_loc) con_set_default_unimap(vc); @@ -1115,51 +1103,44 @@ err_free: } static inline int resize_screen(struct vc_data *vc, int width, int height, - int user) + 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, user); + 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 + * 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. + * 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. * - * Caller must hold the console semaphore. Takes the termios rwsem and - * ctrl.lock of the tty IFF a tty is passed. + * Locking: Caller must hold the console semaphore. Takes the termios rwsem and + * ctrl.lock of the tty IFF a tty is passed. */ - static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, - unsigned int cols, unsigned int lines) + unsigned int cols, unsigned int lines, bool from_user) { unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0; unsigned long end; unsigned int old_rows, old_row_size, first_copied_row; unsigned int new_cols, new_rows, new_row_size, new_screen_size; - unsigned int user; unsigned short *oldscreen, *newscreen; u32 **new_uniscr = NULL; WARN_CONSOLE_UNLOCKED(); - if (!vc) - return -ENXIO; - - user = vc->vc_resize_user; - vc->vc_resize_user = 0; - if (cols > VC_MAXCOL || lines > VC_MAXROW) return -EINVAL; @@ -1185,7 +1166,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, * 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, user); + return resize_screen(vc, new_cols, new_rows, from_user); } if (new_screen_size > KMALLOC_MAX_SIZE || !new_screen_size) @@ -1208,7 +1189,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, old_rows = vc->vc_rows; old_row_size = vc->vc_size_row; - err = resize_screen(vc, new_cols, new_rows, user); + err = resize_screen(vc, new_cols, new_rows, from_user); if (err) { kfree(newscreen); vc_uniscr_free(new_uniscr); @@ -1295,34 +1276,35 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, } /** - * vc_resize - resize a VT - * @vc: virtual console - * @cols: columns - * @rows: rows + * __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. * - * Resize a virtual console as seen from the console end of things. We - * use the common vc_do_resize methods to update the structures. The - * caller must hold the console sem to protect console internals and - * vc->port.tty + * Locking: The caller must hold the console sem to protect console internals + * and @vc->port.tty. */ - -int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows) +int __vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows, + bool from_user) { - return vc_do_resize(vc->port.tty, vc, cols, rows); + return vc_do_resize(vc->port.tty, vc, cols, rows, from_user); } -EXPORT_SYMBOL(vc_resize); +EXPORT_SYMBOL(__vc_resize); /** - * vt_resize - resize a VT - * @tty: tty to resize - * @ws: winsize attributes + * 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. + * 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. * - * Takes the console sem and the called methods then take the tty - * termios_rwsem and the tty ctrl.lock in that order. + * Locking: Takes the console sem and the called methods then take the tty + * termios_rwsem and the tty ctrl.lock in that order. */ static int vt_resize(struct tty_struct *tty, struct winsize *ws) { @@ -1330,7 +1312,7 @@ static int vt_resize(struct tty_struct *tty, struct winsize *ws) int ret; console_lock(); - ret = vc_do_resize(tty, vc, ws->ws_col, ws->ws_row); + ret = vc_do_resize(tty, vc, ws->ws_col, ws->ws_row, false); console_unlock(); return ret; } @@ -1503,36 +1485,43 @@ static inline void del(struct vc_data *vc) /* ignored */ } -static void csi_J(struct vc_data *vc, int vpar) +enum CSI_J { + CSI_J_CURSOR_TO_END = 0, + CSI_J_START_TO_CURSOR = 1, + CSI_J_VISIBLE = 2, + CSI_J_FULL = 3, +}; + +static void csi_J(struct vc_data *vc, enum CSI_J vpar) { + unsigned short *start; unsigned int count; - unsigned short * start; switch (vpar) { - case 0: /* erase from cursor to end of display */ - vc_uniscr_clear_line(vc, vc->state.x, - vc->vc_cols - vc->state.x); - vc_uniscr_clear_lines(vc, vc->state.y + 1, - vc->vc_rows - vc->state.y - 1); - count = (vc->vc_scr_end - vc->vc_pos) >> 1; - start = (unsigned short *)vc->vc_pos; - break; - case 1: /* erase from start to cursor */ - vc_uniscr_clear_line(vc, 0, vc->state.x + 1); - vc_uniscr_clear_lines(vc, 0, vc->state.y); - count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1; - start = (unsigned short *)vc->vc_origin; - break; - case 3: /* include scrollback */ - flush_scrollback(vc); - fallthrough; - case 2: /* erase whole display */ - vc_uniscr_clear_lines(vc, 0, vc->vc_rows); - count = vc->vc_cols * vc->vc_rows; - start = (unsigned short *)vc->vc_origin; - break; - default: - return; + case CSI_J_CURSOR_TO_END: + vc_uniscr_clear_line(vc, vc->state.x, + vc->vc_cols - vc->state.x); + vc_uniscr_clear_lines(vc, vc->state.y + 1, + vc->vc_rows - vc->state.y - 1); + count = (vc->vc_scr_end - vc->vc_pos) >> 1; + start = (unsigned short *)vc->vc_pos; + break; + case CSI_J_START_TO_CURSOR: + vc_uniscr_clear_line(vc, 0, vc->state.x + 1); + vc_uniscr_clear_lines(vc, 0, vc->state.y); + count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1; + start = (unsigned short *)vc->vc_origin; + break; + case CSI_J_FULL: + flush_scrollback(vc); + fallthrough; + case CSI_J_VISIBLE: + vc_uniscr_clear_lines(vc, 0, vc->vc_rows); + count = vc->vc_cols * vc->vc_rows; + start = (unsigned short *)vc->vc_origin; + break; + default: + return; } scr_memsetw(start, vc->vc_video_erase_char, 2 * count); if (con_should_update(vc)) @@ -1540,27 +1529,33 @@ static void csi_J(struct vc_data *vc, int vpar) vc->vc_need_wrap = 0; } -static void csi_K(struct vc_data *vc, int vpar) +enum { + CSI_K_CURSOR_TO_LINEEND = 0, + CSI_K_LINESTART_TO_CURSOR = 1, + CSI_K_LINE = 2, +}; + +static void csi_K(struct vc_data *vc) { unsigned int count; unsigned short *start = (unsigned short *)vc->vc_pos; int offset; - switch (vpar) { - case 0: /* erase from cursor to end of line */ - offset = 0; - count = vc->vc_cols - vc->state.x; - break; - case 1: /* erase from start of line to cursor */ - offset = -vc->state.x; - count = vc->state.x + 1; - break; - case 2: /* erase whole line */ - offset = -vc->state.x; - count = vc->vc_cols; - break; - default: - return; + switch (vc->vc_par[0]) { + case CSI_K_CURSOR_TO_LINEEND: + offset = 0; + count = vc->vc_cols - vc->state.x; + break; + case CSI_K_LINESTART_TO_CURSOR: + offset = -vc->state.x; + count = vc->state.x + 1; + break; + case CSI_K_LINE: + offset = -vc->state.x; + count = vc->vc_cols; + break; + default: + return; } vc_uniscr_clear_line(vc, vc->state.x + offset, count); scr_memsetw(start + offset, vc->vc_video_erase_char, 2 * count); @@ -1569,20 +1564,15 @@ static void csi_K(struct vc_data *vc, int vpar) do_update_region(vc, (unsigned long)(start + offset), count); } -/* erase the following vpar positions */ -static void csi_X(struct vc_data *vc, unsigned int vpar) +/* erase the following count positions */ +static void csi_X(struct vc_data *vc) { /* not vt100? */ - unsigned int count; - - if (!vpar) - vpar++; - - count = min(vpar, vc->vc_cols - vc->state.x); + unsigned int count = clamp(vc->vc_par[0], 1, vc->vc_cols - vc->state.x); vc_uniscr_clear_line(vc, vc->state.x, count); scr_memsetw((unsigned short *)vc->vc_pos, vc->vc_video_erase_char, 2 * count); if (con_should_update(vc)) - vc->vc_sw->con_clear(vc, vc->state.y, vc->state.x, 1, count); + vc->vc_sw->con_clear(vc, vc->state.y, vc->state.x, count); vc->vc_need_wrap = 0; } @@ -1598,7 +1588,7 @@ static void default_attr(struct vc_data *vc) struct rgb { u8 r; u8 g; u8 b; }; -static void rgb_from_256(int i, struct rgb *c) +static void rgb_from_256(unsigned int i, struct rgb *c) { if (i < 8) { /* Standard colours. */ c->r = i&1 ? 0xaa : 0x00; @@ -1609,9 +1599,12 @@ static void rgb_from_256(int i, struct rgb *c) c->g = i&2 ? 0xff : 0x55; c->b = i&4 ? 0xff : 0x55; } else if (i < 232) { /* 6x6x6 colour cube. */ - c->r = (i - 16) / 36 * 85 / 2; - c->g = (i - 16) / 6 % 6 * 85 / 2; - c->b = (i - 16) % 6 * 85 / 2; + i -= 16; + c->b = i % 6 * 255 / 6; + i /= 6; + c->g = i % 6 * 255 / 6; + i /= 6; + c->r = i * 255 / 6; } else /* Grayscale ramp. */ c->r = c->g = c->b = i * 10 - 2312; } @@ -1681,6 +1674,39 @@ static int vc_t416_color(struct vc_data *vc, int i, return i; } +enum { + CSI_m_DEFAULT = 0, + CSI_m_BOLD = 1, + CSI_m_HALF_BRIGHT = 2, + CSI_m_ITALIC = 3, + CSI_m_UNDERLINE = 4, + CSI_m_BLINK = 5, + CSI_m_REVERSE = 7, + CSI_m_PRI_FONT = 10, + CSI_m_ALT_FONT1 = 11, + CSI_m_ALT_FONT2 = 12, + CSI_m_DOUBLE_UNDERLINE = 21, + CSI_m_NORMAL_INTENSITY = 22, + CSI_m_NO_ITALIC = 23, + CSI_m_NO_UNDERLINE = 24, + CSI_m_NO_BLINK = 25, + CSI_m_NO_REVERSE = 27, + CSI_m_FG_COLOR_BEG = 30, + CSI_m_FG_COLOR_END = 37, + CSI_m_FG_COLOR = 38, + CSI_m_DEFAULT_FG_COLOR = 39, + CSI_m_BG_COLOR_BEG = 40, + CSI_m_BG_COLOR_END = 47, + CSI_m_BG_COLOR = 48, + CSI_m_DEFAULT_BG_COLOR = 49, + CSI_m_BRIGHT_FG_COLOR_BEG = 90, + CSI_m_BRIGHT_FG_COLOR_END = 97, + CSI_m_BRIGHT_FG_COLOR_OFF = CSI_m_BRIGHT_FG_COLOR_BEG - CSI_m_FG_COLOR_BEG, + CSI_m_BRIGHT_BG_COLOR_BEG = 100, + CSI_m_BRIGHT_BG_COLOR_END = 107, + CSI_m_BRIGHT_BG_COLOR_OFF = CSI_m_BRIGHT_BG_COLOR_BEG - CSI_m_BG_COLOR_BEG, +}; + /* console_lock is held */ static void csi_m(struct vc_data *vc) { @@ -1688,33 +1714,33 @@ static void csi_m(struct vc_data *vc) for (i = 0; i <= vc->vc_npar; i++) switch (vc->vc_par[i]) { - case 0: /* all attributes off */ + case CSI_m_DEFAULT: /* all attributes off */ default_attr(vc); break; - case 1: + case CSI_m_BOLD: vc->state.intensity = VCI_BOLD; break; - case 2: + case CSI_m_HALF_BRIGHT: vc->state.intensity = VCI_HALF_BRIGHT; break; - case 3: + case CSI_m_ITALIC: vc->state.italic = true; break; - case 21: + case CSI_m_DOUBLE_UNDERLINE: /* * No console drivers support double underline, so * convert it to a single underline. */ - case 4: + case CSI_m_UNDERLINE: vc->state.underline = true; break; - case 5: + case CSI_m_BLINK: vc->state.blink = true; break; - case 7: + case CSI_m_REVERSE: vc->state.reverse = true; break; - case 10: /* ANSI X3.64-1979 (SCO-ish?) + case CSI_m_PRI_FONT: /* ANSI X3.64-1979 (SCO-ish?) * Select primary font, don't display control chars if * defined, don't set bit 8 on output. */ @@ -1722,7 +1748,7 @@ static void csi_m(struct vc_data *vc) vc->vc_disp_ctrl = 0; vc->vc_toggle_meta = 0; break; - case 11: /* ANSI X3.64-1979 (SCO-ish?) + case CSI_m_ALT_FONT1: /* ANSI X3.64-1979 (SCO-ish?) * Select first alternate font, lets chars < 32 be * displayed as ROM chars. */ @@ -1730,7 +1756,7 @@ static void csi_m(struct vc_data *vc) vc->vc_disp_ctrl = 1; vc->vc_toggle_meta = 0; break; - case 12: /* ANSI X3.64-1979 (SCO-ish?) + case CSI_m_ALT_FONT2: /* ANSI X3.64-1979 (SCO-ish?) * Select second alternate font, toggle high bit * before displaying as ROM char. */ @@ -1738,47 +1764,51 @@ static void csi_m(struct vc_data *vc) vc->vc_disp_ctrl = 1; vc->vc_toggle_meta = 1; break; - case 22: + case CSI_m_NORMAL_INTENSITY: vc->state.intensity = VCI_NORMAL; break; - case 23: + case CSI_m_NO_ITALIC: vc->state.italic = false; break; - case 24: + case CSI_m_NO_UNDERLINE: vc->state.underline = false; break; - case 25: + case CSI_m_NO_BLINK: vc->state.blink = false; break; - case 27: + case CSI_m_NO_REVERSE: vc->state.reverse = false; break; - case 38: + case CSI_m_FG_COLOR: i = vc_t416_color(vc, i, rgb_foreground); break; - case 48: + case CSI_m_BG_COLOR: i = vc_t416_color(vc, i, rgb_background); break; - case 39: + case CSI_m_DEFAULT_FG_COLOR: vc->state.color = (vc->vc_def_color & 0x0f) | (vc->state.color & 0xf0); break; - case 49: + case CSI_m_DEFAULT_BG_COLOR: vc->state.color = (vc->vc_def_color & 0xf0) | (vc->state.color & 0x0f); break; - default: - if (vc->vc_par[i] >= 90 && vc->vc_par[i] <= 107) { - if (vc->vc_par[i] < 100) - vc->state.intensity = VCI_BOLD; - vc->vc_par[i] -= 60; - } - if (vc->vc_par[i] >= 30 && vc->vc_par[i] <= 37) - vc->state.color = color_table[vc->vc_par[i] - 30] - | (vc->state.color & 0xf0); - else if (vc->vc_par[i] >= 40 && vc->vc_par[i] <= 47) - vc->state.color = (color_table[vc->vc_par[i] - 40] << 4) - | (vc->state.color & 0x0f); + case CSI_m_BRIGHT_FG_COLOR_BEG ... CSI_m_BRIGHT_FG_COLOR_END: + vc->state.intensity = VCI_BOLD; + vc->vc_par[i] -= CSI_m_BRIGHT_FG_COLOR_OFF; + fallthrough; + case CSI_m_FG_COLOR_BEG ... CSI_m_FG_COLOR_END: + vc->vc_par[i] -= CSI_m_FG_COLOR_BEG; + vc->state.color = color_table[vc->vc_par[i]] | + (vc->state.color & 0xf0); + break; + case CSI_m_BRIGHT_BG_COLOR_BEG ... CSI_m_BRIGHT_BG_COLOR_END: + vc->vc_par[i] -= CSI_m_BRIGHT_BG_COLOR_OFF; + fallthrough; + case CSI_m_BG_COLOR_BEG ... CSI_m_BG_COLOR_END: + vc->vc_par[i] -= CSI_m_BG_COLOR_BEG; + vc->state.color = (color_table[vc->vc_par[i]] << 4) | + (vc->state.color & 0x0f); break; } update_attr(vc); @@ -1832,133 +1862,175 @@ int mouse_reporting(void) return vc_cons[fg_console].d->vc_report_mouse; } +enum { + CSI_DEC_hl_CURSOR_KEYS = 1, /* CKM: cursor keys send ^[Ox/^[[x */ + CSI_DEC_hl_132_COLUMNS = 3, /* COLM: 80/132 mode switch */ + CSI_DEC_hl_REVERSE_VIDEO = 5, /* SCNM */ + CSI_DEC_hl_ORIGIN_MODE = 6, /* OM: origin relative/absolute */ + CSI_DEC_hl_AUTOWRAP = 7, /* AWM */ + CSI_DEC_hl_AUTOREPEAT = 8, /* ARM */ + CSI_DEC_hl_MOUSE_X10 = 9, + CSI_DEC_hl_SHOW_CURSOR = 25, /* TCEM */ + CSI_DEC_hl_MOUSE_VT200 = 1000, +}; + /* console_lock is held */ -static void set_mode(struct vc_data *vc, int on_off) +static void csi_DEC_hl(struct vc_data *vc, bool on_off) { - int i; + unsigned int i; for (i = 0; i <= vc->vc_npar; i++) - if (vc->vc_priv == EPdec) { - switch(vc->vc_par[i]) { /* DEC private modes set/reset */ - case 1: /* Cursor keys send ^[Ox/^[[x */ - if (on_off) - set_kbd(vc, decckm); - else - clr_kbd(vc, decckm); - break; - case 3: /* 80/132 mode switch unimplemented */ + switch (vc->vc_par[i]) { + case CSI_DEC_hl_CURSOR_KEYS: + if (on_off) + set_kbd(vc, decckm); + else + clr_kbd(vc, decckm); + break; + case CSI_DEC_hl_132_COLUMNS: /* unimplemented */ #if 0 - vc_resize(deccolm ? 132 : 80, vc->vc_rows); - /* this alone does not suffice; some user mode - utility has to change the hardware regs */ + vc_resize(deccolm ? 132 : 80, vc->vc_rows); + /* this alone does not suffice; some user mode + utility has to change the hardware regs */ #endif - break; - case 5: /* Inverted screen on/off */ - if (vc->vc_decscnm != on_off) { - vc->vc_decscnm = on_off; - invert_screen(vc, 0, - vc->vc_screenbuf_size, - false); - update_attr(vc); - } - break; - case 6: /* Origin relative/absolute */ - vc->vc_decom = on_off; - gotoxay(vc, 0, 0); - break; - case 7: /* Autowrap on/off */ - vc->vc_decawm = on_off; - break; - case 8: /* Autorepeat on/off */ - if (on_off) - set_kbd(vc, decarm); - else - clr_kbd(vc, decarm); - break; - case 9: - vc->vc_report_mouse = on_off ? 1 : 0; - break; - case 25: /* Cursor on/off */ - vc->vc_deccm = on_off; - break; - case 1000: - vc->vc_report_mouse = on_off ? 2 : 0; - break; - } - } else { - switch(vc->vc_par[i]) { /* ANSI modes set/reset */ - case 3: /* Monitor (display ctrls) */ - vc->vc_disp_ctrl = on_off; - break; - case 4: /* Insert Mode on/off */ - vc->vc_decim = on_off; - break; - case 20: /* Lf, Enter == CrLf/Lf */ - if (on_off) - set_kbd(vc, lnm); - else - clr_kbd(vc, lnm); - break; + break; + case CSI_DEC_hl_REVERSE_VIDEO: + if (vc->vc_decscnm != on_off) { + vc->vc_decscnm = on_off; + invert_screen(vc, 0, vc->vc_screenbuf_size, + false); + update_attr(vc); } + break; + case CSI_DEC_hl_ORIGIN_MODE: + vc->vc_decom = on_off; + gotoxay(vc, 0, 0); + break; + case CSI_DEC_hl_AUTOWRAP: + vc->vc_decawm = on_off; + break; + case CSI_DEC_hl_AUTOREPEAT: + if (on_off) + set_kbd(vc, decarm); + else + clr_kbd(vc, decarm); + break; + case CSI_DEC_hl_MOUSE_X10: + vc->vc_report_mouse = on_off ? 1 : 0; + break; + case CSI_DEC_hl_SHOW_CURSOR: + vc->vc_deccm = on_off; + break; + case CSI_DEC_hl_MOUSE_VT200: + vc->vc_report_mouse = on_off ? 2 : 0; + break; } } +enum { + CSI_hl_DISPLAY_CTRL = 3, /* handle ansi control chars */ + CSI_hl_INSERT = 4, /* IRM: insert/replace */ + CSI_hl_AUTO_NL = 20, /* LNM: Enter == CrLf/Lf */ +}; + /* console_lock is held */ -static void setterm_command(struct vc_data *vc) +static void csi_hl(struct vc_data *vc, bool on_off) +{ + unsigned int i; + + for (i = 0; i <= vc->vc_npar; i++) + switch (vc->vc_par[i]) { /* ANSI modes set/reset */ + case CSI_hl_DISPLAY_CTRL: + vc->vc_disp_ctrl = on_off; + break; + case CSI_hl_INSERT: + vc->vc_decim = on_off; + break; + case CSI_hl_AUTO_NL: + if (on_off) + set_kbd(vc, lnm); + else + clr_kbd(vc, lnm); + break; + } +} + +enum CSI_right_square_bracket { + CSI_RSB_COLOR_FOR_UNDERLINE = 1, + CSI_RSB_COLOR_FOR_HALF_BRIGHT = 2, + CSI_RSB_MAKE_CUR_COLOR_DEFAULT = 8, + CSI_RSB_BLANKING_INTERVAL = 9, + CSI_RSB_BELL_FREQUENCY = 10, + CSI_RSB_BELL_DURATION = 11, + CSI_RSB_BRING_CONSOLE_TO_FRONT = 12, + CSI_RSB_UNBLANK = 13, + CSI_RSB_VESA_OFF_INTERVAL = 14, + CSI_RSB_BRING_PREV_CONSOLE_TO_FRONT = 15, + CSI_RSB_CURSOR_BLINK_INTERVAL = 16, +}; + +/* + * csi_RSB - csi+] (Right Square Bracket) handler + * + * These are linux console private sequences. + * + * console_lock is held + */ +static void csi_RSB(struct vc_data *vc) { switch (vc->vc_par[0]) { - case 1: /* set color for underline mode */ + case CSI_RSB_COLOR_FOR_UNDERLINE: if (vc->vc_can_do_color && vc->vc_par[1] < 16) { vc->vc_ulcolor = color_table[vc->vc_par[1]]; if (vc->state.underline) update_attr(vc); } break; - case 2: /* set color for half intensity mode */ + case CSI_RSB_COLOR_FOR_HALF_BRIGHT: if (vc->vc_can_do_color && vc->vc_par[1] < 16) { vc->vc_halfcolor = color_table[vc->vc_par[1]]; if (vc->state.intensity == VCI_HALF_BRIGHT) update_attr(vc); } break; - case 8: /* store colors as defaults */ + case CSI_RSB_MAKE_CUR_COLOR_DEFAULT: vc->vc_def_color = vc->vc_attr; if (vc->vc_hi_font_mask == 0x100) vc->vc_def_color >>= 1; default_attr(vc); update_attr(vc); break; - case 9: /* set blanking interval */ + case CSI_RSB_BLANKING_INTERVAL: blankinterval = min(vc->vc_par[1], 60U) * 60; poke_blanked_console(); break; - case 10: /* set bell frequency in Hz */ + case CSI_RSB_BELL_FREQUENCY: if (vc->vc_npar >= 1) vc->vc_bell_pitch = vc->vc_par[1]; else vc->vc_bell_pitch = DEFAULT_BELL_PITCH; break; - case 11: /* set bell duration in msec */ + case CSI_RSB_BELL_DURATION: if (vc->vc_npar >= 1) vc->vc_bell_duration = (vc->vc_par[1] < 2000) ? msecs_to_jiffies(vc->vc_par[1]) : 0; else vc->vc_bell_duration = DEFAULT_BELL_DURATION; break; - case 12: /* bring specified console to the front */ + case CSI_RSB_BRING_CONSOLE_TO_FRONT: if (vc->vc_par[1] >= 1 && vc_cons_allocated(vc->vc_par[1] - 1)) set_console(vc->vc_par[1] - 1); break; - case 13: /* unblank the screen */ + case CSI_RSB_UNBLANK: poke_blanked_console(); break; - case 14: /* set vesa powerdown interval */ + case CSI_RSB_VESA_OFF_INTERVAL: vesa_off_interval = min(vc->vc_par[1], 60U) * 60 * HZ; break; - case 15: /* activate the previous console */ + case CSI_RSB_BRING_PREV_CONSOLE_TO_FRONT: set_console(last_console); break; - case 16: /* set cursor blink duration in msec */ + case CSI_RSB_CURSOR_BLINK_INTERVAL: if (vc->vc_npar >= 1 && vc->vc_par[1] >= 50 && vc->vc_par[1] <= USHRT_MAX) vc->vc_cur_blink_ms = vc->vc_par[1]; @@ -1971,41 +2043,32 @@ static void setterm_command(struct vc_data *vc) /* console_lock is held */ static void csi_at(struct vc_data *vc, unsigned int nr) { - if (nr > vc->vc_cols - vc->state.x) - nr = vc->vc_cols - vc->state.x; - else if (!nr) - nr = 1; + nr = clamp(nr, 1, vc->vc_cols - vc->state.x); insert_char(vc, nr); } /* console_lock is held */ -static void csi_L(struct vc_data *vc, unsigned int nr) +static void csi_L(struct vc_data *vc) { - if (nr > vc->vc_rows - vc->state.y) - nr = vc->vc_rows - vc->state.y; - else if (!nr) - nr = 1; + unsigned int nr = clamp(vc->vc_par[0], 1, vc->vc_rows - vc->state.y); + con_scroll(vc, vc->state.y, vc->vc_bottom, SM_DOWN, nr); vc->vc_need_wrap = 0; } /* console_lock is held */ -static void csi_P(struct vc_data *vc, unsigned int nr) +static void csi_P(struct vc_data *vc) { - if (nr > vc->vc_cols - vc->state.x) - nr = vc->vc_cols - vc->state.x; - else if (!nr) - nr = 1; + unsigned int nr = clamp(vc->vc_par[0], 1, vc->vc_cols - vc->state.x); + delete_char(vc, nr); } /* console_lock is held */ -static void csi_M(struct vc_data *vc, unsigned int nr) +static void csi_M(struct vc_data *vc) { - if (nr > vc->vc_rows - vc->state.y) - nr = vc->vc_rows - vc->state.y; - else if (!nr) - nr=1; + unsigned int nr = clamp(vc->vc_par[0], 1, vc->vc_rows - vc->state.y); + con_scroll(vc, vc->state.y, vc->vc_bottom, SM_UP, nr); vc->vc_need_wrap = 0; } @@ -2028,9 +2091,48 @@ static void restore_cur(struct vc_data *vc) vc->vc_need_wrap = 0; } -enum { ESnormal, ESesc, ESsquare, ESgetpars, ESfunckey, - EShash, ESsetG0, ESsetG1, ESpercent, EScsiignore, ESnonstd, - ESpalette, ESosc, ESapc, ESpm, ESdcs }; +/** + * enum vc_ctl_state - control characters state of a vt + * + * @ESnormal: initial state, no control characters parsed + * @ESesc: ESC parsed + * @ESsquare: CSI parsed -- modifiers/parameters/ctrl chars expected + * @ESgetpars: CSI parsed -- parameters/ctrl chars expected + * @ESfunckey: CSI [ parsed + * @EShash: ESC # parsed + * @ESsetG0: ESC ( parsed + * @ESsetG1: ESC ) parsed + * @ESpercent: ESC % parsed + * @EScsiignore: CSI [0x20-0x3f] parsed + * @ESnonstd: OSC parsed + * @ESpalette: OSC P parsed + * @ESosc: OSC [0-9] parsed + * @ESANSI_first: first state for ignoring ansi control sequences + * @ESapc: ESC _ parsed + * @ESpm: ESC ^ parsed + * @ESdcs: ESC P parsed + * @ESANSI_last: last state for ignoring ansi control sequences + */ +enum vc_ctl_state { + ESnormal, + ESesc, + ESsquare, + ESgetpars, + ESfunckey, + EShash, + ESsetG0, + ESsetG1, + ESpercent, + EScsiignore, + ESnonstd, + ESpalette, + ESosc, + ESANSI_first = ESosc, + ESapc, + ESpm, + ESdcs, + ESANSI_last = ESdcs, +}; /* console_lock is held (except via vc_init()) */ static void reset_terminal(struct vc_data *vc, int do_clear) @@ -2078,10 +2180,10 @@ static void reset_terminal(struct vc_data *vc, int do_clear) gotoxy(vc, 0, 0); save_cur(vc); if (do_clear) - csi_J(vc, 2); + csi_J(vc, CSI_J_VISIBLE); } -static void vc_setGx(struct vc_data *vc, unsigned int which, int c) +static void vc_setGx(struct vc_data *vc, unsigned int which, u8 c) { unsigned char *charset = &vc->state.Gx_charset[which]; @@ -2104,36 +2206,54 @@ static void vc_setGx(struct vc_data *vc, unsigned int which, int c) vc->vc_translate = set_translate(*charset, vc); } -/* is this state an ANSI control string? */ -static bool ansi_control_string(unsigned int state) +static bool ansi_control_string(enum vc_ctl_state state) { - if (state == ESosc || state == ESapc || state == ESpm || state == ESdcs) - return true; - return false; + return state >= ESANSI_first && state <= ESANSI_last; } -/* console_lock is held */ -static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) +enum { + ASCII_NULL = 0, + ASCII_BELL = 7, + ASCII_BACKSPACE = 8, + ASCII_IGNORE_FIRST = ASCII_BACKSPACE, + ASCII_HTAB = 9, + ASCII_LINEFEED = 10, + ASCII_VTAB = 11, + ASCII_FORMFEED = 12, + ASCII_CAR_RET = 13, + ASCII_IGNORE_LAST = ASCII_CAR_RET, + ASCII_SHIFTOUT = 14, + ASCII_SHIFTIN = 15, + ASCII_CANCEL = 24, + ASCII_SUBSTITUTE = 26, + ASCII_ESCAPE = 27, + ASCII_CSI_IGNORE_FIRST = ' ', /* 0x2x, 0x3a and 0x3c - 0x3f */ + ASCII_CSI_IGNORE_LAST = '?', + ASCII_DEL = 127, + ASCII_EXT_CSI = 128 + ASCII_ESCAPE, +}; + +/* + * Handle ascii characters in control sequences and change states accordingly. + * E.g |