[PATCH] VT binding: Add sysfs control to the VT layer
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>
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index d788a0e..037d8a3 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -100,12 +100,14 @@
#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 @@
}
#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 @@
con_driver = ®istered_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 @@
goto err;
}
+ retval = -ENODEV;
+
/* check if backup driver exists */
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_BIND)) {
+ !(con_back->flag & CON_DRIVER_FLAG_MODULE)) {
defcsw = con_back->con;
retval = 0;
break;
@@ -2849,21 +2872,177 @@
}
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 = ®istered_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 @@
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 @@
}
}
+ 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 @@
struct con_driver *con_driver = ®istered_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 @@
break;
}
}
-
err:
release_console_sem();
return retval;
@@ -3006,7 +3206,7 @@
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,158 +3220,43 @@
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;
+ int i;
- if (index >= MAX_NR_CON_DRIVER)
- goto err;
+ 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;
+ }
- con = ®istered_con_driver[index];
-
- if (!con->con || !(con->flag & CON_DRIVER_FLAG_BIND))
- goto err;
-
- csw = con->con;
-
+ /* Add system drivers to sysfs */
for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
struct con_driver *con = ®istered_con_driver[i];
- if (con->con && !(con->flag & CON_DRIVER_FLAG_BIND)) {
- defcsw = con->con;
- 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 (!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 = ®istered_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 (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;
-}
-
-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 = ®istered_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;
- }
- }
+ 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);
}
-
- 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;
+ return 0;
}
+postcore_initcall(vtconsole_class_init);
+
+#endif
/*
* Screen blanking