diff options
Diffstat (limited to 'drivers/char/vt.c')
-rw-r--r-- | drivers/char/vt.c | 187 |
1 files changed, 103 insertions, 84 deletions
diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 935f1c207a1f..d8f83e26e4a4 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c | |||
@@ -59,7 +59,7 @@ | |||
59 | * by Martin Mares <mj@atrey.karlin.mff.cuni.cz>, July 1998 | 59 | * by Martin Mares <mj@atrey.karlin.mff.cuni.cz>, July 1998 |
60 | * | 60 | * |
61 | * Removed old-style timers, introduced console_timer, made timer | 61 | * Removed old-style timers, introduced console_timer, made timer |
62 | * deletion SMP-safe. 17Jun00, Andrew Morton <andrewm@uow.edu.au> | 62 | * deletion SMP-safe. 17Jun00, Andrew Morton |
63 | * | 63 | * |
64 | * Removed console_lock, enabled interrupts across all console operations | 64 | * Removed console_lock, enabled interrupts across all console operations |
65 | * 13 March 2001, Andrew Morton | 65 | * 13 March 2001, Andrew Morton |
@@ -100,10 +100,10 @@ | |||
100 | #include <linux/font.h> | 100 | #include <linux/font.h> |
101 | #include <linux/bitops.h> | 101 | #include <linux/bitops.h> |
102 | #include <linux/notifier.h> | 102 | #include <linux/notifier.h> |
103 | 103 | #include <linux/device.h> | |
104 | #include <asm/io.h> | 104 | #include <linux/io.h> |
105 | #include <asm/system.h> | 105 | #include <asm/system.h> |
106 | #include <asm/uaccess.h> | 106 | #include <linux/uaccess.h> |
107 | 107 | ||
108 | #define MAX_NR_CON_DRIVER 16 | 108 | #define MAX_NR_CON_DRIVER 16 |
109 | 109 | ||
@@ -261,7 +261,7 @@ static void notify_update(struct vc_data *vc) | |||
261 | #ifdef VT_BUF_VRAM_ONLY | 261 | #ifdef VT_BUF_VRAM_ONLY |
262 | #define DO_UPDATE(vc) 0 | 262 | #define DO_UPDATE(vc) 0 |
263 | #else | 263 | #else |
264 | #define DO_UPDATE(vc) CON_IS_VISIBLE(vc) | 264 | #define DO_UPDATE(vc) (CON_IS_VISIBLE(vc) && !console_blanked) |
265 | #endif | 265 | #endif |
266 | 266 | ||
267 | static inline unsigned short *screenpos(struct vc_data *vc, int offset, int viewed) | 267 | static inline unsigned short *screenpos(struct vc_data *vc, int offset, int viewed) |
@@ -301,7 +301,7 @@ static void scrup(struct vc_data *vc, unsigned int t, unsigned int b, int nr) | |||
301 | d = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t); | 301 | d = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t); |
302 | s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * (t + nr)); | 302 | s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * (t + nr)); |
303 | scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row); | 303 | scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row); |
304 | scr_memsetw(d + (b - t - nr) * vc->vc_cols, vc->vc_scrl_erase_char, | 304 | scr_memsetw(d + (b - t - nr) * vc->vc_cols, vc->vc_video_erase_char, |
305 | vc->vc_size_row * nr); | 305 | vc->vc_size_row * nr); |
306 | } | 306 | } |
307 | 307 | ||
@@ -319,7 +319,7 @@ static void scrdown(struct vc_data *vc, unsigned int t, unsigned int b, int nr) | |||
319 | s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t); | 319 | s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t); |
320 | step = vc->vc_cols * nr; | 320 | step = vc->vc_cols * nr; |
321 | scr_memmovew(s + step, s, (b - t - nr) * vc->vc_size_row); | 321 | scr_memmovew(s + step, s, (b - t - nr) * vc->vc_size_row); |
322 | scr_memsetw(s, vc->vc_scrl_erase_char, 2 * step); | 322 | scr_memsetw(s, vc->vc_video_erase_char, 2 * step); |
323 | } | 323 | } |
324 | 324 | ||
325 | static void do_update_region(struct vc_data *vc, unsigned long start, int count) | 325 | static void do_update_region(struct vc_data *vc, unsigned long start, int count) |
@@ -434,7 +434,6 @@ static void update_attr(struct vc_data *vc) | |||
434 | vc->vc_blink, vc->vc_underline, | 434 | vc->vc_blink, vc->vc_underline, |
435 | vc->vc_reverse ^ vc->vc_decscnm, vc->vc_italic); | 435 | vc->vc_reverse ^ vc->vc_decscnm, vc->vc_italic); |
436 | vc->vc_video_erase_char = (build_attr(vc, vc->vc_color, 1, vc->vc_blink, 0, vc->vc_decscnm, 0) << 8) | ' '; | 436 | vc->vc_video_erase_char = (build_attr(vc, vc->vc_color, 1, vc->vc_blink, 0, vc->vc_decscnm, 0) << 8) | ' '; |
437 | vc->vc_scrl_erase_char = (build_attr(vc, vc->vc_def_color, 1, false, false, vc->vc_decscnm, false) << 8) | ' '; | ||
438 | } | 437 | } |
439 | 438 | ||
440 | /* Note: inverting the screen twice should revert to the original state */ | 439 | /* Note: inverting the screen twice should revert to the original state */ |
@@ -803,7 +802,25 @@ static inline int resize_screen(struct vc_data *vc, int width, int height, | |||
803 | */ | 802 | */ |
804 | #define VC_RESIZE_MAXCOL (32767) | 803 | #define VC_RESIZE_MAXCOL (32767) |
805 | #define VC_RESIZE_MAXROW (32767) | 804 | #define VC_RESIZE_MAXROW (32767) |
806 | int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) | 805 | |
806 | /** | ||
807 | * vc_do_resize - resizing method for the tty | ||
808 | * @tty: tty being resized | ||
809 | * @real_tty: real tty (different to tty if a pty/tty pair) | ||
810 | * @vc: virtual console private data | ||
811 | * @cols: columns | ||
812 | * @lines: lines | ||
813 | * | ||
814 | * Resize a virtual console, clipping according to the actual constraints. | ||
815 | * If the caller passes a tty structure then update the termios winsize | ||
816 | * information and perform any neccessary signal handling. | ||
817 | * | ||
818 | * Caller must hold the console semaphore. Takes the termios mutex and | ||
819 | * ctrl_lock of the tty IFF a tty is passed. | ||
820 | */ | ||
821 | |||
822 | static int vc_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, | ||
823 | struct vc_data *vc, unsigned int cols, unsigned int lines) | ||
807 | { | 824 | { |
808 | unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0; | 825 | unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0; |
809 | unsigned int old_cols, old_rows, old_row_size, old_screen_size; | 826 | unsigned int old_cols, old_rows, old_row_size, old_screen_size; |
@@ -907,26 +924,15 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) | |||
907 | gotoxy(vc, vc->vc_x, vc->vc_y); | 924 | gotoxy(vc, vc->vc_x, vc->vc_y); |
908 | save_cur(vc); | 925 | save_cur(vc); |
909 | 926 | ||
910 | if (vc->vc_tty) { | 927 | if (tty) { |
911 | struct winsize ws, *cws = &vc->vc_tty->winsize; | 928 | /* Rewrite the requested winsize data with the actual |
912 | struct pid *pgrp = NULL; | 929 | resulting sizes */ |
913 | 930 | struct winsize ws; | |
914 | memset(&ws, 0, sizeof(ws)); | 931 | memset(&ws, 0, sizeof(ws)); |
915 | ws.ws_row = vc->vc_rows; | 932 | ws.ws_row = vc->vc_rows; |
916 | ws.ws_col = vc->vc_cols; | 933 | ws.ws_col = vc->vc_cols; |
917 | ws.ws_ypixel = vc->vc_scan_lines; | 934 | ws.ws_ypixel = vc->vc_scan_lines; |
918 | 935 | tty_do_resize(tty, real_tty, &ws); | |
919 | mutex_lock(&vc->vc_tty->termios_mutex); | ||
920 | spin_lock_irq(&vc->vc_tty->ctrl_lock); | ||
921 | if ((ws.ws_row != cws->ws_row || ws.ws_col != cws->ws_col)) | ||
922 | pgrp = get_pid(vc->vc_tty->pgrp); | ||
923 | spin_unlock_irq(&vc->vc_tty->ctrl_lock); | ||
924 | if (pgrp) { | ||
925 | kill_pgrp(vc->vc_tty->pgrp, SIGWINCH, 1); | ||
926 | put_pid(pgrp); | ||
927 | } | ||
928 | *cws = ws; | ||
929 | mutex_unlock(&vc->vc_tty->termios_mutex); | ||
930 | } | 936 | } |
931 | 937 | ||
932 | if (CON_IS_VISIBLE(vc)) | 938 | if (CON_IS_VISIBLE(vc)) |
@@ -934,14 +940,47 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) | |||
934 | return err; | 940 | return err; |
935 | } | 941 | } |
936 | 942 | ||
937 | int vc_lock_resize(struct vc_data *vc, unsigned int cols, unsigned int lines) | 943 | /** |
944 | * vc_resize - resize a VT | ||
945 | * @vc: virtual console | ||
946 | * @cols: columns | ||
947 | * @rows: rows | ||
948 | * | ||
949 | * Resize a virtual console as seen from the console end of things. We | ||
950 | * use the common vc_do_resize methods to update the structures. The | ||
951 | * caller must hold the console sem to protect console internals and | ||
952 | * vc->vc_tty | ||
953 | */ | ||
954 | |||
955 | int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows) | ||
938 | { | 956 | { |
939 | int rc; | 957 | return vc_do_resize(vc->vc_tty, vc->vc_tty, vc, cols, rows); |
958 | } | ||
959 | |||
960 | /** | ||
961 | * vt_resize - resize a VT | ||
962 | * @tty: tty to resize | ||
963 | * @real_tty: tty if a pty/tty pair | ||
964 | * @ws: winsize attributes | ||
965 | * | ||
966 | * Resize a virtual terminal. This is called by the tty layer as we | ||
967 | * register our own handler for resizing. The mutual helper does all | ||
968 | * the actual work. | ||
969 | * | ||
970 | * Takes the console sem and the called methods then take the tty | ||
971 | * termios_mutex and the tty ctrl_lock in that order. | ||
972 | */ | ||
973 | |||
974 | int vt_resize(struct tty_struct *tty, struct tty_struct *real_tty, | ||
975 | struct winsize *ws) | ||
976 | { | ||
977 | struct vc_data *vc = tty->driver_data; | ||
978 | int ret; | ||
940 | 979 | ||
941 | acquire_console_sem(); | 980 | acquire_console_sem(); |
942 | rc = vc_resize(vc, cols, lines); | 981 | ret = vc_do_resize(tty, real_tty, vc, ws->ws_col, ws->ws_row); |
943 | release_console_sem(); | 982 | release_console_sem(); |
944 | return rc; | 983 | return ret; |
945 | } | 984 | } |
946 | 985 | ||
947 | void vc_deallocate(unsigned int currcons) | 986 | void vc_deallocate(unsigned int currcons) |
@@ -2096,27 +2135,9 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co | |||
2096 | release_console_sem(); | 2135 | release_console_sem(); |
2097 | return 0; | 2136 | return 0; |
2098 | } | 2137 | } |
2099 | release_console_sem(); | ||
2100 | |||
2101 | orig_buf = buf; | 2138 | orig_buf = buf; |
2102 | orig_count = count; | 2139 | orig_count = count; |
2103 | 2140 | ||
2104 | /* At this point 'buf' is guaranteed to be a kernel buffer | ||
2105 | * and therefore no access to userspace (and therefore sleeping) | ||
2106 | * will be needed. The con_buf_mtx serializes all tty based | ||
2107 | * console rendering and vcs write/read operations. We hold | ||
2108 | * the console spinlock during the entire write. | ||
2109 | */ | ||
2110 | |||
2111 | acquire_console_sem(); | ||
2112 | |||
2113 | vc = tty->driver_data; | ||
2114 | if (vc == NULL) { | ||
2115 | printk(KERN_ERR "vt: argh, driver_data _became_ NULL !\n"); | ||
2116 | release_console_sem(); | ||
2117 | goto out; | ||
2118 | } | ||
2119 | |||
2120 | himask = vc->vc_hi_font_mask; | 2141 | himask = vc->vc_hi_font_mask; |
2121 | charmask = himask ? 0x1ff : 0xff; | 2142 | charmask = himask ? 0x1ff : 0xff; |
2122 | 2143 | ||
@@ -2211,7 +2232,7 @@ rescan_last_byte: | |||
2211 | c = 0xfffd; | 2232 | c = 0xfffd; |
2212 | tc = c; | 2233 | tc = c; |
2213 | } else { /* no utf or alternate charset mode */ | 2234 | } else { /* no utf or alternate charset mode */ |
2214 | tc = vc->vc_translate[vc->vc_toggle_meta ? (c | 0x80) : c]; | 2235 | tc = vc_translate(vc, c); |
2215 | } | 2236 | } |
2216 | 2237 | ||
2217 | param.c = tc; | 2238 | param.c = tc; |
@@ -2330,8 +2351,6 @@ rescan_last_byte: | |||
2330 | FLUSH | 2351 | FLUSH |
2331 | console_conditional_schedule(); | 2352 | console_conditional_schedule(); |
2332 | release_console_sem(); | 2353 | release_console_sem(); |
2333 | |||
2334 | out: | ||
2335 | notify_update(vc); | 2354 | notify_update(vc); |
2336 | return n; | 2355 | return n; |
2337 | #undef FLUSH | 2356 | #undef FLUSH |
@@ -2543,8 +2562,6 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) | |||
2543 | int lines; | 2562 | int lines; |
2544 | int ret; | 2563 | int ret; |
2545 | 2564 | ||
2546 | if (tty->driver->type != TTY_DRIVER_TYPE_CONSOLE) | ||
2547 | return -EINVAL; | ||
2548 | if (current->signal->tty != tty && !capable(CAP_SYS_ADMIN)) | 2565 | if (current->signal->tty != tty && !capable(CAP_SYS_ADMIN)) |
2549 | return -EPERM; | 2566 | return -EPERM; |
2550 | if (get_user(type, p)) | 2567 | if (get_user(type, p)) |
@@ -2738,6 +2755,12 @@ static int con_open(struct tty_struct *tty, struct file *filp) | |||
2738 | ret = vc_allocate(currcons); | 2755 | ret = vc_allocate(currcons); |
2739 | if (ret == 0) { | 2756 | if (ret == 0) { |
2740 | struct vc_data *vc = vc_cons[currcons].d; | 2757 | struct vc_data *vc = vc_cons[currcons].d; |
2758 | |||
2759 | /* Still being freed */ | ||
2760 | if (vc->vc_tty) { | ||
2761 | release_console_sem(); | ||
2762 | return -ERESTARTSYS; | ||
2763 | } | ||
2741 | tty->driver_data = vc; | 2764 | tty->driver_data = vc; |
2742 | vc->vc_tty = tty; | 2765 | vc->vc_tty = tty; |
2743 | 2766 | ||
@@ -2749,8 +2772,8 @@ static int con_open(struct tty_struct *tty, struct file *filp) | |||
2749 | tty->termios->c_iflag |= IUTF8; | 2772 | tty->termios->c_iflag |= IUTF8; |
2750 | else | 2773 | else |
2751 | tty->termios->c_iflag &= ~IUTF8; | 2774 | tty->termios->c_iflag &= ~IUTF8; |
2752 | release_console_sem(); | ||
2753 | vcs_make_sysfs(tty); | 2775 | vcs_make_sysfs(tty); |
2776 | release_console_sem(); | ||
2754 | return ret; | 2777 | return ret; |
2755 | } | 2778 | } |
2756 | } | 2779 | } |
@@ -2758,34 +2781,20 @@ static int con_open(struct tty_struct *tty, struct file *filp) | |||
2758 | return ret; | 2781 | return ret; |
2759 | } | 2782 | } |
2760 | 2783 | ||
2761 | /* | ||
2762 | * We take tty_mutex in here to prevent another thread from coming in via init_dev | ||
2763 | * and taking a ref against the tty while we're in the process of forgetting | ||
2764 | * about it and cleaning things up. | ||
2765 | * | ||
2766 | * This is because vcs_remove_sysfs() can sleep and will drop the BKL. | ||
2767 | */ | ||
2768 | static void con_close(struct tty_struct *tty, struct file *filp) | 2784 | static void con_close(struct tty_struct *tty, struct file *filp) |
2769 | { | 2785 | { |
2770 | mutex_lock(&tty_mutex); | 2786 | /* Nothing to do - we defer to shutdown */ |
2771 | acquire_console_sem(); | 2787 | } |
2772 | if (tty && tty->count == 1) { | ||
2773 | struct vc_data *vc = tty->driver_data; | ||
2774 | 2788 | ||
2775 | if (vc) | 2789 | static void con_shutdown(struct tty_struct *tty) |
2776 | vc->vc_tty = NULL; | 2790 | { |
2777 | tty->driver_data = NULL; | 2791 | struct vc_data *vc = tty->driver_data; |
2778 | release_console_sem(); | 2792 | BUG_ON(vc == NULL); |
2779 | vcs_remove_sysfs(tty); | 2793 | acquire_console_sem(); |
2780 | mutex_unlock(&tty_mutex); | 2794 | vc->vc_tty = NULL; |
2781 | /* | 2795 | vcs_remove_sysfs(tty); |
2782 | * tty_mutex is released, but we still hold BKL, so there is | ||
2783 | * still exclusion against init_dev() | ||
2784 | */ | ||
2785 | return; | ||
2786 | } | ||
2787 | release_console_sem(); | 2796 | release_console_sem(); |
2788 | mutex_unlock(&tty_mutex); | 2797 | tty_shutdown(tty); |
2789 | } | 2798 | } |
2790 | 2799 | ||
2791 | static int default_italic_color = 2; // green (ASCII) | 2800 | static int default_italic_color = 2; // green (ASCII) |
@@ -2909,10 +2918,20 @@ static const struct tty_operations con_ops = { | |||
2909 | .start = con_start, | 2918 | .start = con_start, |
2910 | .throttle = con_throttle, | 2919 | .throttle = con_throttle, |
2911 | .unthrottle = con_unthrottle, | 2920 | .unthrottle = con_unthrottle, |
2921 | .resize = vt_resize, | ||
2922 | .shutdown = con_shutdown | ||
2912 | }; | 2923 | }; |
2913 | 2924 | ||
2914 | int __init vty_init(void) | 2925 | static struct cdev vc0_cdev; |
2926 | |||
2927 | int __init vty_init(const struct file_operations *console_fops) | ||
2915 | { | 2928 | { |
2929 | cdev_init(&vc0_cdev, console_fops); | ||
2930 | if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) || | ||
2931 | register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0) | ||
2932 | panic("Couldn't register /dev/tty0 driver\n"); | ||
2933 | device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0"); | ||
2934 | |||
2916 | vcs_init(); | 2935 | vcs_init(); |
2917 | 2936 | ||
2918 | console_driver = alloc_tty_driver(MAX_NR_CONSOLES); | 2937 | console_driver = alloc_tty_driver(MAX_NR_CONSOLES); |
@@ -2931,7 +2950,6 @@ int __init vty_init(void) | |||
2931 | tty_set_operations(console_driver, &con_ops); | 2950 | tty_set_operations(console_driver, &con_ops); |
2932 | if (tty_register_driver(console_driver)) | 2951 | if (tty_register_driver(console_driver)) |
2933 | panic("Couldn't register console driver\n"); | 2952 | panic("Couldn't register console driver\n"); |
2934 | |||
2935 | kbd_init(); | 2953 | kbd_init(); |
2936 | console_map_init(); | 2954 | console_map_init(); |
2937 | #ifdef CONFIG_PROM_CONSOLE | 2955 | #ifdef CONFIG_PROM_CONSOLE |
@@ -3426,8 +3444,9 @@ int register_con_driver(const struct consw *csw, int first, int last) | |||
3426 | goto err; | 3444 | goto err; |
3427 | 3445 | ||
3428 | con_driver->dev = device_create(vtconsole_class, NULL, | 3446 | con_driver->dev = device_create(vtconsole_class, NULL, |
3429 | MKDEV(0, con_driver->node), | 3447 | MKDEV(0, con_driver->node), |
3430 | "vtcon%i", con_driver->node); | 3448 | NULL, "vtcon%i", |
3449 | con_driver->node); | ||
3431 | 3450 | ||
3432 | if (IS_ERR(con_driver->dev)) { | 3451 | if (IS_ERR(con_driver->dev)) { |
3433 | printk(KERN_WARNING "Unable to create device for %s; " | 3452 | printk(KERN_WARNING "Unable to create device for %s; " |
@@ -3536,8 +3555,9 @@ static int __init vtconsole_class_init(void) | |||
3536 | 3555 | ||
3537 | if (con->con && !con->dev) { | 3556 | if (con->con && !con->dev) { |
3538 | con->dev = device_create(vtconsole_class, NULL, | 3557 | con->dev = device_create(vtconsole_class, NULL, |
3539 | MKDEV(0, con->node), | 3558 | MKDEV(0, con->node), |
3540 | "vtcon%i", con->node); | 3559 | NULL, "vtcon%i", |
3560 | con->node); | ||
3541 | 3561 | ||
3542 | if (IS_ERR(con->dev)) { | 3562 | if (IS_ERR(con->dev)) { |
3543 | printk(KERN_WARNING "Unable to create " | 3563 | printk(KERN_WARNING "Unable to create " |
@@ -4061,7 +4081,6 @@ EXPORT_SYMBOL(default_blu); | |||
4061 | EXPORT_SYMBOL(update_region); | 4081 | EXPORT_SYMBOL(update_region); |
4062 | EXPORT_SYMBOL(redraw_screen); | 4082 | EXPORT_SYMBOL(redraw_screen); |
4063 | EXPORT_SYMBOL(vc_resize); | 4083 | EXPORT_SYMBOL(vc_resize); |
4064 | EXPORT_SYMBOL(vc_lock_resize); | ||
4065 | EXPORT_SYMBOL(fg_console); | 4084 | EXPORT_SYMBOL(fg_console); |
4066 | EXPORT_SYMBOL(console_blank_hook); | 4085 | EXPORT_SYMBOL(console_blank_hook); |
4067 | EXPORT_SYMBOL(console_blanked); | 4086 | EXPORT_SYMBOL(console_blanked); |