--- zzzz-none-000/linux-3.10.107/drivers/tty/vt/vt.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/tty/vt/vt.c 2021-02-04 17:41:59.000000000 +0000 @@ -108,6 +108,7 @@ #define CON_DRIVER_FLAG_MODULE 1 #define CON_DRIVER_FLAG_INIT 2 #define CON_DRIVER_FLAG_ATTR 4 +#define CON_DRIVER_FLAG_ZOMBIE 8 struct con_driver { const struct consw *con; @@ -135,6 +136,7 @@ */ #define DEFAULT_BELL_PITCH 750 #define DEFAULT_BELL_DURATION (HZ/8) +#define DEFAULT_CURSOR_BLINK_MS 200 struct vc vc_cons [MAX_NR_CONSOLES]; @@ -153,9 +155,12 @@ static void set_cursor(struct vc_data *vc); static void hide_cursor(struct vc_data *vc); static void console_callback(struct work_struct *ignored); +static void con_driver_unregister_callback(struct work_struct *ignored); static void blank_screen_t(unsigned long dummy); static void set_palette(struct vc_data *vc); +#define vt_get_kmsg_redirect() vt_kmsg_redirect(-1) + static int printable; /* Is console ready for printing? */ int default_utf8 = true; module_param(default_utf8, int, S_IRUGO | S_IWUSR); @@ -180,6 +185,7 @@ core_param(consoleblank, blankinterval, int, 0444); static DECLARE_WORK(console_work, console_callback); +static DECLARE_WORK(con_driver_unregister_work, con_driver_unregister_callback); /* * fg_console is the current virtual console, @@ -736,12 +742,15 @@ __module_get(vc->vc_sw->owner); vc->vc_num = num; vc->vc_display_fg = &master_display_fg; + if (vc->vc_uni_pagedir_loc) + con_free_unimap(vc); vc->vc_uni_pagedir_loc = &vc->vc_uni_pagedir; - vc->vc_uni_pagedir = 0; + vc->vc_uni_pagedir = NULL; vc->vc_hi_font_mask = 0; vc->vc_complement_mask = 0; vc->vc_can_do_color = 0; vc->vc_panic_force_write = false; + vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS; vc->vc_sw->con_init(vc, init); if (!vc->vc_complement_mask) vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; @@ -830,7 +839,7 @@ * 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 mutex and + * Caller must hold the console semaphore. Takes the termios rwsem and * ctrl_lock of the tty IFF a tty is passed. */ @@ -979,7 +988,7 @@ * the actual work. * * Takes the console sem and the called methods then take the tty - * termios_mutex and the tty ctrl_lock in that order. + * termios_rwsem and the tty ctrl_lock in that order. */ static int vt_resize(struct tty_struct *tty, struct winsize *ws) { @@ -1238,6 +1247,52 @@ vc->vc_color = vc->vc_def_color; } +struct rgb { u8 r; u8 g; u8 b; }; + +static struct rgb rgb_from_256(int i) +{ + struct rgb c; + if (i < 8) { /* Standard colours. */ + c.r = i&1 ? 0xaa : 0x00; + c.g = i&2 ? 0xaa : 0x00; + c.b = i&4 ? 0xaa : 0x00; + } else if (i < 16) { + c.r = i&1 ? 0xff : 0x55; + 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; + } else /* Grayscale ramp. */ + c.r = c.g = c.b = i * 10 - 2312; + return c; +} + +static void rgb_foreground(struct vc_data *vc, struct rgb c) +{ + u8 hue, max = c.r; + if (c.g > max) + max = c.g; + if (c.b > max) + max = c.b; + hue = (c.r > max/2 ? 4 : 0) + | (c.g > max/2 ? 2 : 0) + | (c.b > max/2 ? 1 : 0); + if (hue == 7 && max <= 0x55) + hue = 0, vc->vc_intensity = 2; + else + vc->vc_intensity = (max > 0xaa) + 1; + vc->vc_color = (vc->vc_color & 0xf0) | hue; +} + +static void rgb_background(struct vc_data *vc, struct rgb c) +{ + /* For backgrounds, err on the dark side. */ + vc->vc_color = (vc->vc_color & 0x0f) + | (c.r&0x80) >> 1 | (c.g&0x80) >> 2 | (c.b&0x80) >> 3; +} + /* console_lock is held */ static void csi_m(struct vc_data *vc) { @@ -1309,21 +1364,59 @@ case 27: vc->vc_reverse = 0; break; - case 38: /* ANSI X3.64-1979 (SCO-ish?) - * Enables underscore, white foreground - * with white underscore (Linux - use - * default foreground). + case 38: /* ITU T.416 + * Higher colour modes. + * They break the usual properties of SGR codes + * and thus need to be detected and ignored by + * hand. Strictly speaking, that standard also + * wants : rather than ; as separators, contrary + * to ECMA-48, but no one produces such codes + * and almost no one accepts them. */ - vc->vc_color = (vc->vc_def_color & 0x0f) | (vc->vc_color & 0xf0); - vc->vc_underline = 1; + i++; + if (i > vc->vc_npar) + break; + if (vc->vc_par[i] == 5 && /* 256 colours */ + i < vc->vc_npar) { /* ubiquitous */ + i++; + rgb_foreground(vc, + rgb_from_256(vc->vc_par[i])); + } else if (vc->vc_par[i] == 2 && /* 24 bit */ + i <= vc->vc_npar + 3) {/* extremely rare */ + struct rgb c = { + .r = vc->vc_par[i + 1], + .g = vc->vc_par[i + 2], + .b = vc->vc_par[i + 3], + }; + rgb_foreground(vc, c); + i += 3; + } + /* Subcommands 3 (CMY) and 4 (CMYK) are so insane + * there's no point in supporting them. + */ break; - case 39: /* ANSI X3.64-1979 (SCO-ish?) - * Disable underline option. - * Reset colour to default? It did this - * before... - */ + case 48: + i++; + if (i > vc->vc_npar) + break; + if (vc->vc_par[i] == 5 && /* 256 colours */ + i < vc->vc_npar) { + i++; + rgb_background(vc, + rgb_from_256(vc->vc_par[i])); + } else if (vc->vc_par[i] == 2 && /* 24 bit */ + i <= vc->vc_npar + 3) { + struct rgb c = { + .r = vc->vc_par[i + 1], + .g = vc->vc_par[i + 2], + .b = vc->vc_par[i + 3], + }; + rgb_background(vc, c); + i += 3; + } + break; + case 39: vc->vc_color = (vc->vc_def_color & 0x0f) | (vc->vc_color & 0xf0); - vc->vc_underline = 0; break; case 49: vc->vc_color = (vc->vc_def_color & 0xf0) | (vc->vc_color & 0x0f); @@ -1492,7 +1585,7 @@ case 11: /* set bell duration in msec */ if (vc->vc_npar >= 1) vc->vc_bell_duration = (vc->vc_par[1] < 2000) ? - vc->vc_par[1] * HZ / 1000 : 0; + msecs_to_jiffies(vc->vc_par[1]) : 0; else vc->vc_bell_duration = DEFAULT_BELL_DURATION; break; @@ -1509,6 +1602,13 @@ case 15: /* activate the previous console */ set_console(last_console); break; + case 16: /* set cursor blink duration in msec */ + 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]; + else + vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS; + break; } } @@ -1588,9 +1688,9 @@ vc->vc_need_wrap = 0; } -enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey, +enum { ESnormal, ESesc, ESsquare, ESgetpars, ESfunckey, EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd, - ESpalette }; + ESpalette, ESosc }; /* console_lock is held (except via vc_init()) */ static void reset_terminal(struct vc_data *vc, int do_clear) @@ -1636,6 +1736,7 @@ vc->vc_bell_pitch = DEFAULT_BELL_PITCH; vc->vc_bell_duration = DEFAULT_BELL_DURATION; + vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS; gotoxy(vc, 0, 0); save_cur(vc); @@ -1650,11 +1751,15 @@ * Control characters can be used in the _middle_ * of an escape sequence. */ + if (vc->vc_state == ESosc && c>=8 && c<=13) /* ... except for OSC */ + return; switch (c) { case 0: return; case 7: - if (vc->vc_bell_duration) + if (vc->vc_state == ESosc) + vc->vc_state = ESnormal; + else if (vc->vc_bell_duration) kd_mksound(vc->vc_bell_pitch, vc->vc_bell_duration); return; case 8: @@ -1765,7 +1870,9 @@ } else if (c=='R') { /* reset palette */ reset_palette(vc); vc->vc_state = ESnormal; - } else + } else if (c>='0' && c<='9') + vc->vc_state = ESosc; + else vc->vc_state = ESnormal; return; case ESpalette: @@ -1805,9 +1912,7 @@ vc->vc_par[vc->vc_npar] *= 10; vc->vc_par[vc->vc_npar] += c - '0'; return; - } else - vc->vc_state = ESgotpars; - case ESgotpars: + } vc->vc_state = ESnormal; switch(c) { case 'h': @@ -2021,6 +2126,8 @@ vc->vc_translate = set_translate(vc->vc_G1_charset, vc); vc->vc_state = ESnormal; return; + case ESosc: + return; default: vc->vc_state = ESnormal; } @@ -2818,8 +2925,10 @@ console_unlock(); } +static int default_color = 7; /* white */ static int default_italic_color = 2; // green (ASCII) static int default_underline_color = 3; // cyan (ASCII) +module_param_named(color, default_color, int, S_IRUGO | S_IWUSR); module_param_named(italic, default_italic_color, int, S_IRUGO | S_IWUSR); module_param_named(underline, default_underline_color, int, S_IRUGO | S_IWUSR); @@ -2841,7 +2950,7 @@ vc->vc_palette[k++] = default_grn[j] ; vc->vc_palette[k++] = default_blu[j] ; } - vc->vc_def_color = 0x07; /* white */ + vc->vc_def_color = default_color; vc->vc_ulcolor = default_underline_color; vc->vc_itcolor = default_italic_color; vc->vc_halfcolor = 0x08; /* grey */ @@ -2952,17 +3061,24 @@ } static DEVICE_ATTR(active, S_IRUGO, show_tty_active, NULL); +static struct attribute *vt_dev_attrs[] = { + &dev_attr_active.attr, + NULL +}; + +ATTRIBUTE_GROUPS(vt_dev); + int __init vty_init(const struct file_operations *console_fops) { cdev_init(&vc0_cdev, console_fops); if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) || register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0) panic("Couldn't register /dev/tty0 driver\n"); - tty0dev = device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0"); + tty0dev = device_create_with_groups(tty_class, NULL, + MKDEV(TTY_MAJOR, 0), NULL, + vt_dev_groups, "tty0"); if (IS_ERR(tty0dev)) tty0dev = NULL; - else - WARN_ON(device_create_file(tty0dev, &dev_attr_active) < 0); vcs_init(); @@ -3095,62 +3211,7 @@ }; -static int bind_con_driver(const struct consw *csw, int first, int last, - int deflt) -{ - int ret; - - console_lock(); - ret = do_bind_con_driver(csw, first, last, deflt); - console_unlock(); - return ret; -} - #ifdef CONFIG_VT_HW_CONSOLE_BINDING -static int con_is_graphics(const struct consw *csw, int first, int last) -{ - int i, retval = 0; - - for (i = first; i <= last; i++) { - struct vc_data *vc = vc_cons[i].d; - - if (vc && vc->vc_mode == KD_GRAPHICS) { - retval = 1; - break; - } - } - - return retval; -} - -/** - * unbind_con_driver - unbind a console driver - * @csw: pointer to console driver to unregister - * @first: first in range of consoles that @csw should be unbound from - * @last: last in range of consoles that @csw should be unbound from - * @deflt: should next bound console driver be default after @csw is unbound? - * - * To unbind a driver from all possible consoles, pass 0 as @first and - * %MAX_NR_CONSOLES as @last. - * - * @deflt controls whether the console that ends up replacing @csw should be - * the default console. - * - * RETURNS: - * -ENODEV if @csw isn't a registered console driver or can't be unregistered - * or 0 on success. - */ -int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) -{ - int retval; - - console_lock(); - retval = do_unbind_con_driver(csw, first, last, deflt); - console_unlock(); - return retval; -} -EXPORT_SYMBOL(unbind_con_driver); - /* unlocked version of unbind_con_driver() */ int do_unbind_con_driver(const struct consw *csw, int first, int last, int deflt) { @@ -3184,8 +3245,7 @@ for (i = 0; i < MAX_NR_CON_DRIVER; i++) { con_back = ®istered_con_driver[i]; - if (con_back->con && - !(con_back->flag & CON_DRIVER_FLAG_MODULE)) { + if (con_back->con && con_back->con != csw) { defcsw = con_back->con; retval = 0; break; @@ -3237,8 +3297,7 @@ const struct consw *defcsw = NULL, *csw = NULL; int i, more = 1, first = -1, last = -1, deflt = 0; - if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) || - con_is_graphics(con->con, con->first, con->last)) + if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE)) goto err; csw = con->con; @@ -3272,7 +3331,7 @@ deflt = 1; if (first != -1) - bind_con_driver(csw, first, last, deflt); + do_bind_con_driver(csw, first, last, deflt); first = -1; last = -1; @@ -3287,9 +3346,9 @@ { const struct consw *csw = NULL; int i, more = 1, first = -1, last = -1, deflt = 0; + int ret; - if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) || - con_is_graphics(con->con, con->first, con->last)) + if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE)) goto err; csw = con->con; @@ -3310,8 +3369,11 @@ if (first == 0 && last == MAX_NR_CONSOLES -1) deflt = 1; - if (first != -1) - unbind_con_driver(csw, first, last, deflt); + if (first != -1) { + ret = do_unbind_con_driver(csw, first, last, deflt); + if (ret != 0) + return ret; + } first = -1; last = -1; @@ -3338,11 +3400,15 @@ struct con_driver *con = dev_get_drvdata(dev); int bind = simple_strtoul(buf, NULL, 0); + console_lock(); + if (bind) vt_bind(con); else vt_unbind(con); + console_unlock(); + return count; } @@ -3366,42 +3432,26 @@ } -static struct device_attribute device_attrs[] = { - __ATTR(bind, S_IRUGO|S_IWUSR, show_bind, store_bind), - __ATTR(name, S_IRUGO, show_name, NULL), +static DEVICE_ATTR(bind, S_IRUGO|S_IWUSR, show_bind, store_bind); +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static struct attribute *con_dev_attrs[] = { + &dev_attr_bind.attr, + &dev_attr_name.attr, + NULL }; +ATTRIBUTE_GROUPS(con_dev); + static int vtconsole_init_device(struct con_driver *con) { - int i; - int error = 0; - con->flag |= CON_DRIVER_FLAG_ATTR; - dev_set_drvdata(con->dev, con); - for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { - error = device_create_file(con->dev, &device_attrs[i]); - if (error) - break; - } - - if (error) { - while (--i >= 0) - device_remove_file(con->dev, &device_attrs[i]); - con->flag &= ~CON_DRIVER_FLAG_ATTR; - } - - return error; + return 0; } static void vtconsole_deinit_device(struct con_driver *con) { - int i; - - if (con->flag & CON_DRIVER_FLAG_ATTR) { - for (i = 0; i < ARRAY_SIZE(device_attrs); i++) - device_remove_file(con->dev, &device_attrs[i]); - con->flag &= ~CON_DRIVER_FLAG_ATTR; - } + con->flag &= ~CON_DRIVER_FLAG_ATTR; } /** @@ -3549,7 +3599,8 @@ for (i = 0; i < MAX_NR_CON_DRIVER; i++) { con_driver = ®istered_con_driver[i]; - if (con_driver->con == NULL) { + if (con_driver->con == NULL && + !(con_driver->flag & CON_DRIVER_FLAG_ZOMBIE)) { con_driver->con = csw; con_driver->desc = desc; con_driver->node = i; @@ -3565,11 +3616,11 @@ if (retval) goto err; - con_driver->dev = device_create(vtconsole_class, NULL, - MKDEV(0, con_driver->node), - NULL, "vtcon%i", - con_driver->node); - + con_driver->dev = + device_create_with_groups(vtconsole_class, NULL, + MKDEV(0, con_driver->node), + con_driver, con_dev_groups, + "vtcon%i", con_driver->node); if (IS_ERR(con_driver->dev)) { printk(KERN_WARNING "Unable to create device for %s; " "errno = %ld\n", con_driver->desc, @@ -3584,29 +3635,9 @@ return retval; } -/** - * register_con_driver - register console driver to console layer - * @csw: console driver - * @first: the first console to take over, minimum value is 0 - * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 - * - * DESCRIPTION: This function registers a console driver which can later - * bind to a range of consoles specified by @first and @last. It will - * also initialize the console driver by calling con_startup(). - */ -int register_con_driver(const struct consw *csw, int first, int last) -{ - int retval; - - console_lock(); - retval = do_register_con_driver(csw, first, last); - console_unlock(); - return retval; -} -EXPORT_SYMBOL(register_con_driver); /** - * unregister_con_driver - unregister console driver from console layer + * do_unregister_con_driver - unregister console driver from console layer * @csw: console driver * * DESCRIPTION: All drivers that registers to the console layer must @@ -3616,87 +3647,88 @@ * * The driver must unbind first prior to unregistration. */ -int unregister_con_driver(const struct consw *csw) -{ - int retval; - - console_lock(); - retval = do_unregister_con_driver(csw); - console_unlock(); - return retval; -} -EXPORT_SYMBOL(unregister_con_driver); - int do_unregister_con_driver(const struct consw *csw) { - int i, retval = -ENODEV; + int i; /* cannot unregister a bound driver */ if (con_is_bound(csw)) - goto err; + 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 && - con_driver->flag & CON_DRIVER_FLAG_MODULE) { - vtconsole_deinit_device(con_driver); - device_destroy(vtconsole_class, - MKDEV(0, con_driver->node)); + 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->desc = NULL; - con_driver->dev = NULL; - con_driver->node = 0; - con_driver->flag = 0; - con_driver->first = 0; - con_driver->last = 0; - retval = 0; - break; + con_driver->flag = CON_DRIVER_FLAG_ZOMBIE; + schedule_work(&con_driver_unregister_work); + + return 0; } } -err: - return retval; + + return -ENODEV; } EXPORT_SYMBOL_GPL(do_unregister_con_driver); -/* - * 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. - * - * take_over_console is basically a register followed by unbind - */ -int do_take_over_console(const struct consw *csw, int first, int last, int deflt) +static void con_driver_unregister_callback(struct work_struct *ignored) { - int err; + int i; - 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); + console_lock(); - return err; + for (i = 0; i < MAX_NR_CON_DRIVER; i++) { + struct con_driver *con_driver = ®istered_con_driver[i]; + + if (!(con_driver->flag & CON_DRIVER_FLAG_ZOMBIE)) + continue; + + console_unlock(); + + vtconsole_deinit_device(con_driver); + device_destroy(vtconsole_class, MKDEV(0, con_driver->node)); + + console_lock(); + + if (WARN_ON_ONCE(con_driver->con)) + con_driver->con = NULL; + con_driver->desc = NULL; + con_driver->dev = NULL; + con_driver->node = 0; + WARN_ON_ONCE(con_driver->flag != CON_DRIVER_FLAG_ZOMBIE); + con_driver->flag = 0; + con_driver->first = 0; + con_driver->last = 0; + } + + console_unlock(); } -EXPORT_SYMBOL_GPL(do_take_over_console); /* * 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. * - * take_over_console is basically a register followed by unbind + * do_take_over_console is basically a register followed by unbind */ -int take_over_console(const struct consw *csw, int first, int last, int deflt) +int do_take_over_console(const struct consw *csw, int first, int last, int deflt) { int err; - err = register_con_driver(csw, first, last); + 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 @@ -3705,10 +3737,12 @@ if (err == -EBUSY) err = 0; if (!err) - bind_con_driver(csw, first, last, deflt); + do_bind_con_driver(csw, first, last, deflt); return err; } +EXPORT_SYMBOL_GPL(do_take_over_console); + /* * give_up_console is a wrapper to unregister_con_driver. It will only @@ -3716,7 +3750,9 @@ */ void give_up_console(const struct consw *csw) { - unregister_con_driver(csw); + console_lock(); + do_unregister_con_driver(csw); + console_unlock(); } static int __init vtconsole_class_init(void) @@ -3735,10 +3771,11 @@ struct con_driver *con = ®istered_con_driver[i]; if (con->con && !con->dev) { - con->dev = device_create(vtconsole_class, NULL, - MKDEV(0, con->node), - NULL, "vtcon%i", - con->node); + con->dev = + device_create_with_groups(vtconsole_class, NULL, + MKDEV(0, con->node), + con, con_dev_groups, + "vtcon%i", con->node); if (IS_ERR(con->dev)) { printk(KERN_WARNING "Unable to create " @@ -3851,8 +3888,8 @@ return; if (!vc_cons_allocated(fg_console)) { /* impossible */ - pr_warning("unblank_screen: tty %d not allocated ??\n", - fg_console+1); + pr_warn("unblank_screen: tty %d not allocated ??\n", + fg_console + 1); return; } vc = vc_cons[fg_console].d; @@ -4272,6 +4309,5 @@ EXPORT_SYMBOL(vc_cons); EXPORT_SYMBOL(global_cursor_default); #ifndef VT_SINGLE_DRIVER -EXPORT_SYMBOL(take_over_console); EXPORT_SYMBOL(give_up_console); #endif