vserver 1.9.3
[linux-2.6.git] / drivers / char / vt.c
index f3d43dc..31da37d 100644 (file)
 #include <linux/workqueue.h>
 #include <linux/bootmem.h>
 #include <linux/pm.h>
+#include <linux/font.h>
 
 #include <asm/io.h>
 #include <asm/system.h>
@@ -135,9 +136,6 @@ extern void prom_con_init(void);
 #ifdef CONFIG_MDA_CONSOLE
 extern int mda_console_init(void);
 #endif
-#ifdef CONFIG_FRAMEBUFFER_CONSOLE
-extern int fb_console_init(void);
-#endif
 
 struct vc vc_cons [MAX_NR_CONSOLES];
 
@@ -152,7 +150,7 @@ static void gotoxy(int currcons, int new_x, int new_y);
 static void save_cur(int currcons);
 static void reset_terminal(int currcons, int do_clear);
 static void con_flush_chars(struct tty_struct *tty);
-static void set_vesa_blanking(unsigned long arg);
+static void set_vesa_blanking(char __user *p);
 static void set_cursor(int currcons);
 static void hide_cursor(int currcons);
 static void console_callback(void *ignored);
@@ -599,6 +597,17 @@ static inline void save_screen(int currcons)
  *     Redrawing of screen
  */
 
+static void clear_buffer_attributes(int currcons)
+{
+       unsigned short *p = (unsigned short *) origin;
+       int count = screenbuf_size/2;
+       int mask = hi_font_mask | 0xff;
+
+       for (; count > 0; count--, p++) {
+               scr_writew((scr_readw(p)&mask) | (video_erase_char&~mask), p);
+       }
+}
+
 void redraw_screen(int new_console, int is_switch)
 {
        int redraw = 1;
@@ -636,9 +645,21 @@ void redraw_screen(int new_console, int is_switch)
 
        if (redraw) {
                int update;
+               int old_was_color = vc_cons[currcons].d->vc_can_do_color;
+
                set_origin(currcons);
                update = sw->con_switch(vc_cons[currcons].d);
                set_palette(currcons);
+               /*
+                * 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_cons[currcons].d->vc_can_do_color) {
+                       update_attr(currcons);
+                       clear_buffer_attributes(currcons);
+               }
                if (update && vcmode != KD_GRAPHICS)
                        do_update_region(currcons, origin, screenbuf_size/2);
        }
@@ -661,11 +682,14 @@ int vc_cons_allocated(unsigned int i)
 static void visual_init(int currcons, int init)
 {
     /* ++Geert: sw->con_init determines console size */
+    if (sw)
+       module_put(sw->owner);
     sw = conswitchp;
 #ifndef VT_SINGLE_DRIVER
     if (con_driver_map[currcons])
        sw = con_driver_map[currcons];
 #endif
+    __module_get(sw->owner);
     cons_num = currcons;
     display_fg = &master_display_fg;
     vc_cons[currcons].d->vc_uni_pagedir_loc = &vc_cons[currcons].d->vc_uni_pagedir;
@@ -773,17 +797,17 @@ int vc_resize(int currcons, unsigned int cols, unsigned int lines)
        old_row_size = video_size_row;
        old_screen_size = screenbuf_size;
 
-       video_num_lines = new_rows;
-       video_num_columns = new_cols;
-       video_size_row = new_row_size;
-       screenbuf_size = new_screen_size;
-
        err = resize_screen(currcons, new_cols, new_rows);
        if (err) {
                kfree(newscreen);
                return err;
        }
 
+       video_num_lines = new_rows;
+       video_num_columns = new_cols;
+       video_size_row = new_row_size;
+       screenbuf_size = new_screen_size;
+
        rlth = min(old_row_size, new_row_size);
        rrem = new_row_size - rlth;
        old_origin = origin;
@@ -808,8 +832,6 @@ int vc_resize(int currcons, unsigned int cols, unsigned int lines)
        screenbuf = newscreen;
        kmalloced = 1;
        screenbuf_size = new_screen_size;
-       if (IS_VISIBLE)
-               err = resize_screen(currcons, new_cols, new_rows);
        set_origin(currcons);
 
        /* do part of a reset_terminal() */
@@ -1863,8 +1885,7 @@ static void do_con_trol(struct tty_struct *tty, unsigned int currcons, int c)
  * since console_init (and thus con_init) are called before any
  * kernel memory allocation is available.
  */
-char con_buf[PAGE_SIZE];
-#define CON_BUF_SIZE   PAGE_SIZE
+char con_buf[CON_BUF_SIZE];
 DECLARE_MUTEX(con_buf_sem);
 
 /* acquires console_sem */
@@ -1957,12 +1978,16 @@ again:
                hide_cursor(currcons);
 
        while (!tty->stopped && count) {
-               c = *buf;
+               int orig = *buf;
+               c = orig;
                buf++;
                n++;
                count--;
 
-               if (utf) {
+               /* Do no translation at all in control states */
+               if (vc_state != ESnormal) {
+                       tc = c;
+               } else if (utf) {
                    /* Combine UTF-8 into Unicode */
                    /* Incomplete characters silently ignored */
                    if(c > 0x7f) {
@@ -2062,7 +2087,7 @@ again:
                        continue;
                }
                FLUSH
-               do_con_trol(tty, currcons, c);
+               do_con_trol(tty, currcons, orig);
        }
        FLUSH
        console_conditional_schedule();
@@ -2273,6 +2298,7 @@ struct console vt_console_driver = {
 int tioclinux(struct tty_struct *tty, unsigned long arg)
 {
        char type, data;
+       char __user *p = (char __user *)arg;
        int lines;
        int ret;
 
@@ -2280,14 +2306,14 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
                return -EINVAL;
        if (current->signal->tty != tty && !capable(CAP_SYS_ADMIN))
                return -EPERM;
-       if (get_user(type, (char *)arg))
+       if (get_user(type, p))
                return -EFAULT;
        ret = 0;
        switch (type)
        {
                case TIOCL_SETSEL:
                        acquire_console_sem();
-                       ret = set_selection((struct tiocl_selection *)((char *)arg+1), tty, 1);
+                       ret = set_selection((struct tiocl_selection __user *)(p+1), tty);
                        release_console_sem();
                        break;
                case TIOCL_PASTESEL:
@@ -2297,7 +2323,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
                        unblank_screen();
                        break;
                case TIOCL_SELLOADLUT:
-                       ret = sel_loadlut(arg);
+                       ret = sel_loadlut(p);
                        break;
                case TIOCL_GETSHIFTSTATE:
                        
@@ -2308,20 +2334,20 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
         * related to the kernel should not use this.
         */
                        data = shift_state;
-                       ret = __put_user(data, (char *) arg);
+                       ret = __put_user(data, p);
                        break;
                case TIOCL_GETMOUSEREPORTING:
                        data = mouse_reporting();
-                       ret = __put_user(data, (char *) arg);
+                       ret = __put_user(data, p);
                        break;
                case TIOCL_SETVESABLANK:
-                       set_vesa_blanking(arg);
+                       set_vesa_blanking(p);
                        break;
                case TIOCL_SETKMSGREDIRECT:
                        if (!capable(CAP_SYS_ADMIN)) {
                                ret = -EPERM;
                        } else {
-                               if (get_user(data, (char *)arg+1))
+                               if (get_user(data, p+1))
                                        ret = -EFAULT;
                                else
                                        kmsg_redirect = data;
@@ -2331,7 +2357,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
                        ret = fg_console;
                        break;
                case TIOCL_SCROLLCONSOLE:
-                       if (get_user(lines, (s32 *)((char *)arg+4))) {
+                       if (get_user(lines, (s32 __user *)(p+4))) {
                                ret = -EFAULT;
                        } else {
                                scrollfront(lines);
@@ -2497,7 +2523,7 @@ static void con_close(struct tty_struct *tty, struct file *filp)
                vt = tty->driver_data;
                if (vt)
                        vc_cons[vt->vc_num].d->vc_tty = NULL;
-               tty->driver_data = 0;
+               tty->driver_data = NULL;
                release_console_sem();
                vcs_remove_devfs(tty);
                up(&tty_sem);
@@ -2643,50 +2669,49 @@ int __init vty_init(void)
 #ifdef CONFIG_MDA_CONSOLE
        mda_console_init();
 #endif
-#ifdef CONFIG_FRAMEBUFFER_CONSOLE
-       fb_console_init();
-#endif 
        return 0;
 }
 
 #ifndef VT_SINGLE_DRIVER
 
-static void clear_buffer_attributes(int currcons)
-{
-       unsigned short *p = (unsigned short *) origin;
-       int count = screenbuf_size/2;
-       int mask = hi_font_mask | 0xff;
-
-       for (; count > 0; count--, p++) {
-               scr_writew((scr_readw(p)&mask) | (video_erase_char&~mask), p);
-       }
-}
-
 /*
  *     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.
  */
 
-void take_over_console(const struct consw *csw, int first, int last, int deflt)
+int take_over_console(const struct consw *csw, int first, int last, int deflt)
 {
        int i, j = -1;
        const char *desc;
+       struct module *owner;
+
+       owner = csw->owner;
+       if (!try_module_get(owner))
+               return -ENODEV;
 
        acquire_console_sem();
 
        desc = csw->con_startup();
        if (!desc) {
                release_console_sem();
-               return;
+               module_put(owner);
+               return -ENODEV;
        }
-       if (deflt)
+       if (deflt) {
+               if (conswitchp)
+                       module_put(conswitchp->owner);
+               __module_get(owner);
                conswitchp = csw;
+       }
 
        for (i = first; i <= last; i++) {
                int old_was_color;
                int currcons = i;
 
+               if (con_driver_map[i])
+                       module_put(con_driver_map[i]->owner);
+               __module_get(owner);
                con_driver_map[i] = csw;
 
                if (!vc_cons[i].d || !vc_cons[i].d->vc_sw)
@@ -2697,6 +2722,10 @@ void take_over_console(const struct consw *csw, int first, int last, int deflt)
                        save_screen(i);
                old_was_color = vc_cons[i].d->vc_can_do_color;
                vc_cons[i].d->vc_sw->con_deinit(vc_cons[i].d);
+               origin = (unsigned long) screenbuf;
+               visible_origin = origin;
+               scr_end = origin + screenbuf_size;
+               pos = origin + video_size_row*y + 2*x;
                visual_init(i, 0);
                update_attr(i);
 
@@ -2721,6 +2750,9 @@ void take_over_console(const struct consw *csw, int first, int last, int deflt)
                printk("to %s\n", desc);
 
        release_console_sem();
+
+       module_put(owner);
+       return 0;
 }
 
 void give_up_console(const struct consw *csw)
@@ -2728,8 +2760,10 @@ void give_up_console(const struct consw *csw)
        int i;
 
        for(i = 0; i < MAX_NR_CONSOLES; i++)
-               if (con_driver_map[i] == csw)
+               if (con_driver_map[i] == csw) {
+                       module_put(csw->owner);
                        con_driver_map[i] = NULL;
+               }
 }
 
 #endif
@@ -2738,11 +2772,10 @@ void give_up_console(const struct consw *csw)
  *     Screen blanking
  */
 
-static void set_vesa_blanking(unsigned long arg)
+static void set_vesa_blanking(char __user *p)
 {
-    char *argp = (char *)arg + 1;
     unsigned int mode;
-    get_user(mode, argp);
+    get_user(mode, p + 1);
     vesa_blank_mode = (mode < 4) ? mode : 0;
 }
 
@@ -2918,7 +2951,7 @@ void set_palette(int currcons)
                sw->con_set_palette(vc_cons[currcons].d, color_table);
 }
 
-static int set_get_cmap(unsigned char *arg, int set)
+static int set_get_cmap(unsigned char __user *arg, int set)
 {
     int i, j, k;
 
@@ -2953,7 +2986,7 @@ static int set_get_cmap(unsigned char *arg, int set)
  * map, 3 bytes per colour, 16 colours, range from 0 to 255.
  */
 
-int con_set_cmap(unsigned char *arg)
+int con_set_cmap(unsigned char __user *arg)
 {
        int rc;
 
@@ -2964,7 +2997,7 @@ int con_set_cmap(unsigned char *arg)
        return rc;
 }
 
-int con_get_cmap(unsigned char *arg)
+int con_get_cmap(unsigned char __user *arg)
 {
        int rc;
 
@@ -3001,97 +3034,182 @@ void reset_palette(int currcons)
 
 #define max_font_size 65536
 
-int con_font_op(int currcons, struct console_font_op *op)
+int con_font_get(int currcons, struct console_font_op *op)
 {
+       struct console_font font;
        int rc = -EINVAL;
-       int size = max_font_size, set;
-       u8 *temp = NULL;
-       struct console_font_op old_op;
+       int c;
 
        if (vt_cons[currcons]->vc_mode != KD_TEXT)
-               goto quit;
-       memcpy(&old_op, op, sizeof(old_op));
-       if (op->op == KD_FONT_OP_SET) {
-               if (!op->data)
-                       return -EINVAL;
-               if (op->charcount > 512)
-                       goto quit;
-               if (!op->height) {              /* Need to guess font height [compat] */
-                       int h, i;
-                       u8 *charmap = op->data, tmp;
-                       
-                       /* If from KDFONTOP ioctl, don't allow things which can be done in userland,
-                          so that we can get rid of this soon */
-                       if (!(op->flags & KD_FONT_FLAG_OLD))
-                               goto quit;
-                       rc = -EFAULT;
-                       for (h = 32; h > 0; h--)
-                               for (i = 0; i < op->charcount; i++) {
-                                       if (get_user(tmp, &charmap[32*i+h-1]))
-                                               goto quit;
-                                       if (tmp)
-                                               goto nonzero;
-                               }
-                       rc = -EINVAL;
-                       goto quit;
-               nonzero:
-                       rc = -EINVAL;
-                       op->height = h;
-               }
-               if (op->width > 32 || op->height > 32)
-                       goto quit;
-               size = (op->width+7)/8 * 32 * op->charcount;
-               if (size > max_font_size)
-                       return -ENOSPC;
-               set = 1;
-       } else if (op->op == KD_FONT_OP_GET)
-               set = 0;
-       else {
-               acquire_console_sem();
-               rc = sw->con_font_op(vc_cons[currcons].d, op);
-               release_console_sem();
-               return rc;
-       }
+               return -EINVAL;
+
        if (op->data) {
-               temp = kmalloc(size, GFP_KERNEL);
-               if (!temp)
+               font.data = kmalloc(max_font_size, GFP_KERNEL);
+               if (!font.data)
                        return -ENOMEM;
-               if (set && copy_from_user(temp, op->data, size)) {
-                       rc = -EFAULT;
-                       goto quit;
-               }
-               op->data = temp;
-       }
+       } else
+               font.data = NULL;
 
        acquire_console_sem();
-       rc = sw->con_font_op(vc_cons[currcons].d, op);
+       if (sw->con_font_get)
+               rc = sw->con_font_get(vc_cons[currcons].d, &font);
+       else
+               rc = -ENOSYS;
        release_console_sem();
 
-       op->data = old_op.data;
-       if (!rc && !set) {
-               int c = (op->width+7)/8 * 32 * op->charcount;
-               
-               if (op->data && op->charcount > old_op.charcount)
+       if (rc)
+               goto out;
+
+       c = (font.width+7)/8 * 32 * font.charcount;
+       
+       if (op->data && font.charcount > op->charcount)
+               rc = -ENOSPC;
+       if (!(op->flags & KD_FONT_FLAG_OLD)) {
+               if (font.width > op->width || font.height > op->height) 
                        rc = -ENOSPC;
-               if (!(op->flags & KD_FONT_FLAG_OLD)) {
-                       if (op->width > old_op.width || 
-                           op->height > old_op.height)
-                               rc = -ENOSPC;
-               } else {
-                       if (op->width != 8)
-                               rc = -EIO;
-                       else if ((old_op.height && op->height > old_op.height) ||
-                                op->height > 32)
-                               rc = -ENOSPC;
-               }
-               if (!rc && op->data && copy_to_user(op->data, temp, c))
-                       rc = -EFAULT;
+       } else {
+               if (font.width != 8)
+                       rc = -EIO;
+               else if ((op->height && font.height > op->height) ||
+                        font.height > 32)
+                       rc = -ENOSPC;
+       }
+       if (rc)
+               goto out;
+
+       if (op->data && copy_to_user(op->data, font.data, c))
+               rc = -EFAULT;
+
+out:
+       kfree(font.data);
+       return rc;
+}
+
+int con_font_set(int currcons, struct console_font_op *op)
+{
+       struct console_font font;
+       int rc = -EINVAL;
+       int size;
+
+       if (vt_cons[currcons]->vc_mode != KD_TEXT)
+               return -EINVAL;
+       if (!op->data)
+               return -EINVAL;
+       if (op->charcount > 512)
+               return -EINVAL;
+       if (!op->height) {              /* Need to guess font height [compat] */
+               int h, i;
+               u8 __user *charmap = op->data;
+               u8 tmp;
+               
+               /* If from KDFONTOP ioctl, don't allow things which can be done in userland,
+                  so that we can get rid of this soon */
+               if (!(op->flags & KD_FONT_FLAG_OLD))
+                       return -EINVAL;
+               for (h = 32; h > 0; h--)
+                       for (i = 0; i < op->charcount; i++) {
+                               if (get_user(tmp, &charmap[32*i+h-1]))
+                                       return -EFAULT;
+                               if (tmp)
+                                       goto nonzero;
+                       }
+               return -EINVAL;
+       nonzero:
+               op->height = h;
        }
-quit:  if (temp)
-               kfree(temp);
+       if (op->width <= 0 || op->width > 32 || op->height > 32)
+               return -EINVAL;
+       size = (op->width+7)/8 * 32 * op->charcount;
+       if (size > max_font_size)
+               return -ENOSPC;
+       font.charcount = op->charcount;
+       font.height = op->height;
+       font.width = op->width;
+       font.data = kmalloc(size, GFP_KERNEL);
+       if (!font.data)
+               return -ENOMEM;
+       if (copy_from_user(font.data, op->data, size)) {
+               kfree(font.data);
+               return -EFAULT;
+       }
+       acquire_console_sem();
+       if (sw->con_font_set)
+               rc = sw->con_font_set(vc_cons[currcons].d, &font, op->flags);
+       else
+               rc = -ENOSYS;
+       release_console_sem();
+       kfree(font.data);
        return rc;
 }
 
+int con_font_default(int currcons, struct console_font_op *op)
+{
+       struct console_font font = {.width = op->width, .height = op->height};
+       char name[MAX_FONT_NAME];
+       char *s = name;
+       int rc;
+
+       if (vt_cons[currcons]->vc_mode != KD_TEXT)
+               return -EINVAL;
+
+       if (!op->data)
+               s = NULL;
+       else if (strncpy_from_user(name, op->data, MAX_FONT_NAME - 1) < 0)
+               return -EFAULT;
+       else
+               name[MAX_FONT_NAME - 1] = 0;
+
+       acquire_console_sem();
+       if (sw->con_font_default)
+               rc = sw->con_font_default(vc_cons[currcons].d, &font, s);
+       else
+               rc = -ENOSYS;
+       release_console_sem();
+       if (!rc) {
+               op->width = font.width;
+               op->height = font.height;
+       }
+       return rc;
+}
+
+int con_font_copy(int currcons, struct console_font_op *op)
+{
+       int con = op->height;
+       struct vc_data *vc;
+       int rc;
+
+       if (vt_cons[currcons]->vc_mode != KD_TEXT)
+               return -EINVAL;
+
+       acquire_console_sem();
+       vc = vc_cons[currcons].d;
+       if (!sw->con_font_copy)
+               rc = -ENOSYS;
+       else if (con < 0 || !vc_cons_allocated(con))
+               rc = -ENOTTY;
+       else if (con == vc->vc_num)     /* nothing to do */
+               rc = 0;
+       else
+               rc = sw->con_font_copy(vc, con);
+       release_console_sem();
+       return rc;
+}
+
+int con_font_op(int currcons, struct console_font_op *op)
+{
+       switch (op->op) {
+       case KD_FONT_OP_SET:
+               return con_font_set(currcons, op);
+       case KD_FONT_OP_GET:
+               return con_font_get(currcons, op);
+       case KD_FONT_OP_SET_DEFAULT:
+               return con_font_default(currcons, op);
+       case KD_FONT_OP_COPY:
+               return con_font_copy(currcons, op);
+       }
+       return -ENOSYS;
+}
+
 /*
  *     Interface exported to selection and vcs.
  */