[PATCH] VT binding: Add sysfs control to the VT layer
Antonino A. Daplas [Mon, 26 Jun 2006 07:27:12 +0000 (00:27 -0700)]
Add sysfs control to the VT layer.  A new sysfs class, 'vtconsole', and class
devices 'vtcon[n]' are added.  Each class device file has the following
attributes:

/sys/class/vtconsole/vtcon[n]/name - read-only attribute showing the
                                     name of the current backend

/sys/class/vtconsole/vtcon[n]/bind - read/write attribute
             where: 0 - backend is unbound/unbind backend from the VT layer
                    1 - backend is bound/bind backend to the VT layer

In addition, if any of the consoles are in KD_GRAPHICS mode, binding and
unbinding will not succeed.  KD_GRAPHICS mode usually indicates that the
underlying console hardware is used for other purposes other than displaying
text (ie X).  This feature should prevent binding/unbinding from interfering
with a graphics application using the VT.

[akpm@osdl.org: warning fixes]
Signed-off-by: Antonino Daplas <adaplas@pol.net>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

drivers/char/vt.c

index d788a0e..037d8a3 100644 (file)
 
 #define MAX_NR_CON_DRIVER 16
 
-#define CON_DRIVER_FLAG_BIND 1
+#define CON_DRIVER_FLAG_MODULE 1
 #define CON_DRIVER_FLAG_INIT 2
 
 struct con_driver {
        const struct consw *con;
        const char *desc;
+       struct class_device *class_dev;
+       int node;
        int first;
        int last;
        int flag;
@@ -2685,6 +2687,25 @@ int __init vty_init(void)
 }
 
 #ifndef VT_SINGLE_DRIVER
+#include <linux/device.h>
+
+static struct class *vtconsole_class;
+
+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;
+}
 
 static int bind_con_driver(const struct consw *csw, int first, int last,
                           int deflt)
@@ -2805,7 +2826,7 @@ static int unbind_con_driver(const struct consw *csw, int first, int last,
                con_driver = &registered_con_driver[i];
 
                if (con_driver->con == csw &&
-                   con_driver->flag & CON_DRIVER_FLAG_BIND) {
+                   con_driver->flag & CON_DRIVER_FLAG_MODULE) {
                        retval = 0;
                        break;
                }
@@ -2816,12 +2837,14 @@ static int unbind_con_driver(const struct consw *csw, int first, int last,
                goto err;
        }
 
+       retval = -ENODEV;
+
        /* check if backup driver exists */
        for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
                con_back = &registered_con_driver[i];
 
                if (con_back->con &&
-                   !(con_back->flag & CON_DRIVER_FLAG_BIND)) {
+                   !(con_back->flag & CON_DRIVER_FLAG_MODULE)) {
                        defcsw = con_back->con;
                        retval = 0;
                        break;
@@ -2849,21 +2872,177 @@ static int unbind_con_driver(const struct consw *csw, int first, int last,
        }
 
        if (!con_is_bound(defcsw)) {
+               const struct consw *defconsw = conswitchp;
+
                defcsw->con_startup();
                con_back->flag |= CON_DRIVER_FLAG_INIT;
+               /*
+                * vgacon may change the default driver to point
+                * to dummycon, we restore it here...
+                */
+               conswitchp = defconsw;
        }
 
        if (!con_is_bound(csw))
                con_driver->flag &= ~CON_DRIVER_FLAG_INIT;
 
        release_console_sem();
-       retval = bind_con_driver(defcsw, first, last, deflt);
+       /* ignore return value, binding should not fail */
+       bind_con_driver(defcsw, first, last, deflt);
 err:
        module_put(owner);
        return retval;
 
 }
 
+static int vt_bind(struct con_driver *con)
+{
+       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))
+               goto err;
+
+       csw = con->con;
+
+       for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+               struct con_driver *con = &registered_con_driver[i];
+
+               if (con->con && !(con->flag & CON_DRIVER_FLAG_MODULE)) {
+                       defcsw = con->con;
+                       break;
+               }
+       }
+
+       if (!defcsw)
+               goto err;
+
+       while (more) {
+               more = 0;
+
+               for (i = con->first; i <= con->last; i++) {
+                       if (con_driver_map[i] == defcsw) {
+                               if (first == -1)
+                                       first = i;
+                               last = i;
+                               more = 1;
+                       } else if (first != -1)
+                               break;
+               }
+
+               if (first == 0 && last == MAX_NR_CONSOLES -1)
+                       deflt = 1;
+
+               if (first != -1)
+                       bind_con_driver(csw, first, last, deflt);
+
+               first = -1;
+               last = -1;
+               deflt = 0;
+       }
+
+err:
+       return 0;
+}
+
+static int vt_unbind(struct con_driver *con)
+{
+       const struct consw *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))
+               goto err;
+
+       csw = con->con;
+
+       while (more) {
+               more = 0;
+
+               for (i = con->first; i <= con->last; i++) {
+                       if (con_driver_map[i] == csw) {
+                               if (first == -1)
+                                       first = i;
+                               last = i;
+                               more = 1;
+                       } else if (first != -1)
+                               break;
+               }
+
+               if (first == 0 && last == MAX_NR_CONSOLES -1)
+                       deflt = 1;
+
+               if (first != -1)
+                       unbind_con_driver(csw, first, last, deflt);
+
+               first = -1;
+               last = -1;
+               deflt = 0;
+       }
+
+err:
+       return 0;
+}
+
+static ssize_t store_bind(struct class_device *class_device,
+                         const char *buf, size_t count)
+{
+       struct con_driver *con = class_get_devdata(class_device);
+       int bind = simple_strtoul(buf, NULL, 0);
+
+       if (bind)
+               vt_bind(con);
+       else
+               vt_unbind(con);
+
+       return count;
+}
+
+static ssize_t show_bind(struct class_device *class_device, char *buf)
+{
+       struct con_driver *con = class_get_devdata(class_device);
+       int bind = con_is_bound(con->con);
+
+       return snprintf(buf, PAGE_SIZE, "%i\n", bind);
+}
+
+static ssize_t show_name(struct class_device *class_device, char *buf)
+{
+       struct con_driver *con = class_get_devdata(class_device);
+
+       return snprintf(buf, PAGE_SIZE, "%s %s\n",
+                       (con->flag & CON_DRIVER_FLAG_MODULE) ? "(M)" : "(S)",
+                        con->desc);
+
+}
+
+static struct class_device_attribute class_device_attrs[] = {
+       __ATTR(bind, S_IRUGO|S_IWUSR, show_bind, store_bind),
+       __ATTR(name, S_IRUGO, show_name, NULL),
+};
+
+static int vtconsole_init_class_device(struct con_driver *con)
+{
+       int i;
+
+       class_set_devdata(con->class_dev, con);
+       for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
+               class_device_create_file(con->class_dev,
+                                        &class_device_attrs[i]);
+
+       return 0;
+}
+
+static void vtconsole_deinit_class_device(struct con_driver *con)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
+               class_device_remove_file(con->class_dev,
+                                        &class_device_attrs[i]);
+}
+
 /**
  * con_is_bound - checks if driver is bound to the console
  * @csw: console driver
@@ -2934,7 +3113,8 @@ int register_con_driver(const struct consw *csw, int first, int last)
                if (con_driver->con == NULL) {
                        con_driver->con = csw;
                        con_driver->desc = desc;
-                       con_driver->flag = CON_DRIVER_FLAG_BIND |
+                       con_driver->node = i;
+                       con_driver->flag = CON_DRIVER_FLAG_MODULE |
                                           CON_DRIVER_FLAG_INIT;
                        con_driver->first = first;
                        con_driver->last = last;
@@ -2943,6 +3123,22 @@ int register_con_driver(const struct consw *csw, int first, int last)
                }
        }
 
+       if (retval)
+               goto err;
+
+       con_driver->class_dev = class_device_create(vtconsole_class, NULL,
+                                                   MKDEV(0, con_driver->node),
+                                                   NULL, "vtcon%i",
+                                                   con_driver->node);
+
+       if (IS_ERR(con_driver->class_dev)) {
+               printk(KERN_WARNING "Unable to create class_device for %s; "
+                      "errno = %ld\n", con_driver->desc,
+                      PTR_ERR(con_driver->class_dev));
+               con_driver->class_dev = NULL;
+       } else {
+               vtconsole_init_class_device(con_driver);
+       }
 err:
        release_console_sem();
        module_put(owner);
@@ -2975,9 +3171,14 @@ int unregister_con_driver(const struct consw *csw)
                struct con_driver *con_driver = &registered_con_driver[i];
 
                if (con_driver->con == csw &&
-                   con_driver->flag & CON_DRIVER_FLAG_BIND) {
+                   con_driver->flag & CON_DRIVER_FLAG_MODULE) {
+                       vtconsole_deinit_class_device(con_driver);
+                       class_device_destroy(vtconsole_class,
+                                            MKDEV(0, con_driver->node));
                        con_driver->con = NULL;
                        con_driver->desc = NULL;
+                       con_driver->class_dev = NULL;
+                       con_driver->node = 0;
                        con_driver->flag = 0;
                        con_driver->first = 0;
                        con_driver->last = 0;
@@ -2985,7 +3186,6 @@ int unregister_con_driver(const struct consw *csw)
                        break;
                }
        }
-
 err:
        release_console_sem();
        return retval;
@@ -3006,7 +3206,7 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt)
        err = register_con_driver(csw, first, last);
 
        if (!err)
-               err = bind_con_driver(csw, first, last, deflt);
+               bind_con_driver(csw, first, last, deflt);
 
        return err;
 }
@@ -3020,160 +3220,45 @@ void give_up_console(const struct consw *csw)
        unregister_con_driver(csw);
 }
 
-/*
- * this function is intended to be called by the tty layer only
- */
-int vt_bind(int index)
+static int __init vtconsole_class_init(void)
 {
-       const struct consw *defcsw = NULL, *csw = NULL;
-       struct con_driver *con;
-       int i, more = 1, first = -1, last = -1, deflt = 0;
-
-       if (index >= MAX_NR_CON_DRIVER)
-               goto err;
-
-       con = &registered_con_driver[index];
-
-       if (!con->con || !(con->flag & CON_DRIVER_FLAG_BIND))
-               goto err;
+       int i;
 
-       csw = con->con;
+       vtconsole_class = class_create(THIS_MODULE, "vtconsole");
+       if (IS_ERR(vtconsole_class)) {
+               printk(KERN_WARNING "Unable to create vt console class; "
+                      "errno = %ld\n", PTR_ERR(vtconsole_class));
+               vtconsole_class = NULL;
+       }
 
+       /* Add system drivers to sysfs */
        for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
                struct con_driver *con = &registered_con_driver[i];
 
-               if (con->con && !(con->flag & CON_DRIVER_FLAG_BIND)) {
-                       defcsw = con->con;
-                       break;
-               }
-       }
-
-       if (!defcsw)
-               goto err;
-
-       while (more) {
-               more = 0;
-
-               for (i = con->first; i <= con->last; i++) {
-                       if (con_driver_map[i] == defcsw) {
-                               if (first == -1)
-                                       first = i;
-                               last = i;
-                               more = 1;
-                       } else if (first != -1)
-                               break;
-               }
-
-               if (first == 0 && last == MAX_NR_CONSOLES -1)
-                       deflt = 1;
-
-               if (first != -1)
-                       bind_con_driver(csw, first, last, deflt);
-
-               first = -1;
-               last = -1;
-               deflt = 0;
-       }
-
-err:
-       return 0;
-}
-
-/*
- * this function is intended to be called by the tty layer only
- */
-int vt_unbind(int index)
-{
-       const struct consw *csw = NULL;
-       struct con_driver *con;
-       int i, more = 1, first = -1, last = -1, deflt = 0;
-
-       if (index >= MAX_NR_CON_DRIVER)
-               goto err;
-
-       con = &registered_con_driver[index];
-
-       if (!con->con || !(con->flag & CON_DRIVER_FLAG_BIND))
-               goto err;
-
-       csw = con->con;
-
-       while (more) {
-               more = 0;
-
-               for (i = con->first; i <= con->last; i++) {
-                       if (con_driver_map[i] == csw) {
-                               if (first == -1)
-                                       first = i;
-                               last = i;
-                               more = 1;
-                       } else if (first != -1)
-                               break;
+               if (con->con && !con->class_dev) {
+                       con->class_dev =
+                               class_device_create(vtconsole_class, NULL,
+                                                   MKDEV(0, con->node), NULL,
+                                                   "vtcon%i", con->node);
+
+                       if (IS_ERR(con->class_dev)) {
+                               printk(KERN_WARNING "Unable to create "
+                                      "class_device for %s; errno = %ld\n",
+                                      con->desc, PTR_ERR(con->class_dev));
+                               con->class_dev = NULL;
+                       } else {
+                               vtconsole_init_class_device(con);
+                       }
                }
-
-               if (first == 0 && last == MAX_NR_CONSOLES -1)
-                       deflt = 1;
-
-               if (first != -1)
-                       unbind_con_driver(csw, first, last, deflt);
-
-               first = -1;
-               last = -1;
-               deflt = 0;
        }
 
-err:
-       return 0;
-}
-#else
-int vt_bind(int index)
-{
        return 0;
 }
+postcore_initcall(vtconsole_class_init);
 
-int vt_unbind(int index)
-{
-       return 0;
-}
 #endif
 
 /*
- * this function is intended to be called by the tty layer only
- */
-int vt_show_drivers(char *buf)
-{
-       int i, j, read, offset = 0, cnt = PAGE_SIZE;
-
-       for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
-               struct con_driver *con_driver = &registered_con_driver[i];
-
-               if (con_driver->con != NULL) {
-                       int sys = 0;
-
-                       if (con_driver->flag & CON_DRIVER_FLAG_BIND) {
-                               sys = 2;
-
-                               for (j = 0; j < MAX_NR_CONSOLES; j++) {
-                                       if (con_driver_map[j] ==
-                                           con_driver->con) {
-                                               sys = 1;
-                                               break;
-                                       }
-                               }
-                       }
-
-                       read = snprintf(buf + offset, cnt, "%i %s: %s\n",
-                                       i, (sys) ? ((sys == 1) ? "B" : "U") :
-                                       "S", con_driver->desc);
-                       offset += read;
-                       cnt -= read;
-               }
-       }
-
-       return offset;
-}
-
-/*
  *     Screen blanking
  */