[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>
This commit is contained in:
committed by
Linus Torvalds
parent
79062a0d39
commit
6db4063c5b
@@ -100,12 +100,14 @@
|
|||||||
|
|
||||||
#define MAX_NR_CON_DRIVER 16
|
#define MAX_NR_CON_DRIVER 16
|
||||||
|
|
||||||
#define CON_DRIVER_FLAG_BIND 1
|
#define CON_DRIVER_FLAG_MODULE 1
|
||||||
#define CON_DRIVER_FLAG_INIT 2
|
#define CON_DRIVER_FLAG_INIT 2
|
||||||
|
|
||||||
struct con_driver {
|
struct con_driver {
|
||||||
const struct consw *con;
|
const struct consw *con;
|
||||||
const char *desc;
|
const char *desc;
|
||||||
|
struct class_device *class_dev;
|
||||||
|
int node;
|
||||||
int first;
|
int first;
|
||||||
int last;
|
int last;
|
||||||
int flag;
|
int flag;
|
||||||
@@ -2685,6 +2687,25 @@ int __init vty_init(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef VT_SINGLE_DRIVER
|
#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,
|
static int bind_con_driver(const struct consw *csw, int first, int last,
|
||||||
int deflt)
|
int deflt)
|
||||||
@@ -2805,7 +2826,7 @@ static int unbind_con_driver(const struct consw *csw, int first, int last,
|
|||||||
con_driver = ®istered_con_driver[i];
|
con_driver = ®istered_con_driver[i];
|
||||||
|
|
||||||
if (con_driver->con == csw &&
|
if (con_driver->con == csw &&
|
||||||
con_driver->flag & CON_DRIVER_FLAG_BIND) {
|
con_driver->flag & CON_DRIVER_FLAG_MODULE) {
|
||||||
retval = 0;
|
retval = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2816,12 +2837,14 @@ static int unbind_con_driver(const struct consw *csw, int first, int last,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
retval = -ENODEV;
|
||||||
|
|
||||||
/* check if backup driver exists */
|
/* check if backup driver exists */
|
||||||
for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
|
for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
|
||||||
con_back = ®istered_con_driver[i];
|
con_back = ®istered_con_driver[i];
|
||||||
|
|
||||||
if (con_back->con &&
|
if (con_back->con &&
|
||||||
!(con_back->flag & CON_DRIVER_FLAG_BIND)) {
|
!(con_back->flag & CON_DRIVER_FLAG_MODULE)) {
|
||||||
defcsw = con_back->con;
|
defcsw = con_back->con;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
break;
|
break;
|
||||||
@@ -2849,21 +2872,177 @@ static int unbind_con_driver(const struct consw *csw, int first, int last,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!con_is_bound(defcsw)) {
|
if (!con_is_bound(defcsw)) {
|
||||||
|
const struct consw *defconsw = conswitchp;
|
||||||
|
|
||||||
defcsw->con_startup();
|
defcsw->con_startup();
|
||||||
con_back->flag |= CON_DRIVER_FLAG_INIT;
|
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))
|
if (!con_is_bound(csw))
|
||||||
con_driver->flag &= ~CON_DRIVER_FLAG_INIT;
|
con_driver->flag &= ~CON_DRIVER_FLAG_INIT;
|
||||||
|
|
||||||
release_console_sem();
|
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:
|
err:
|
||||||
module_put(owner);
|
module_put(owner);
|
||||||
return retval;
|
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
|
* con_is_bound - checks if driver is bound to the console
|
||||||
* @csw: console driver
|
* @csw: console driver
|
||||||
@@ -2934,7 +3113,8 @@ int register_con_driver(const struct consw *csw, int first, int last)
|
|||||||
if (con_driver->con == NULL) {
|
if (con_driver->con == NULL) {
|
||||||
con_driver->con = csw;
|
con_driver->con = csw;
|
||||||
con_driver->desc = desc;
|
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_FLAG_INIT;
|
||||||
con_driver->first = first;
|
con_driver->first = first;
|
||||||
con_driver->last = last;
|
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:
|
err:
|
||||||
release_console_sem();
|
release_console_sem();
|
||||||
module_put(owner);
|
module_put(owner);
|
||||||
@@ -2975,9 +3171,14 @@ int unregister_con_driver(const struct consw *csw)
|
|||||||
struct con_driver *con_driver = ®istered_con_driver[i];
|
struct con_driver *con_driver = ®istered_con_driver[i];
|
||||||
|
|
||||||
if (con_driver->con == csw &&
|
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->con = NULL;
|
||||||
con_driver->desc = NULL;
|
con_driver->desc = NULL;
|
||||||
|
con_driver->class_dev = NULL;
|
||||||
|
con_driver->node = 0;
|
||||||
con_driver->flag = 0;
|
con_driver->flag = 0;
|
||||||
con_driver->first = 0;
|
con_driver->first = 0;
|
||||||
con_driver->last = 0;
|
con_driver->last = 0;
|
||||||
@@ -2985,7 +3186,6 @@ int unregister_con_driver(const struct consw *csw)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err:
|
err:
|
||||||
release_console_sem();
|
release_console_sem();
|
||||||
return retval;
|
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);
|
err = register_con_driver(csw, first, last);
|
||||||
|
|
||||||
if (!err)
|
if (!err)
|
||||||
err = bind_con_driver(csw, first, last, deflt);
|
bind_con_driver(csw, first, last, deflt);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@@ -3020,159 +3220,44 @@ void give_up_console(const struct consw *csw)
|
|||||||
unregister_con_driver(csw);
|
unregister_con_driver(csw);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static int __init vtconsole_class_init(void)
|
||||||
* this function is intended to be called by the tty layer only
|
|
||||||
*/
|
|
||||||
int vt_bind(int index)
|
|
||||||
{
|
{
|
||||||
const struct consw *defcsw = NULL, *csw = NULL;
|
int i;
|
||||||
struct con_driver *con;
|
|
||||||
int i, more = 1, first = -1, last = -1, deflt = 0;
|
|
||||||
|
|
||||||
if (index >= MAX_NR_CON_DRIVER)
|
vtconsole_class = class_create(THIS_MODULE, "vtconsole");
|
||||||
goto err;
|
if (IS_ERR(vtconsole_class)) {
|
||||||
|
printk(KERN_WARNING "Unable to create vt console class; "
|
||||||
con = ®istered_con_driver[index];
|
"errno = %ld\n", PTR_ERR(vtconsole_class));
|
||||||
|
vtconsole_class = NULL;
|
||||||
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++) {
|
for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
|
||||||
struct con_driver *con = ®istered_con_driver[i];
|
struct con_driver *con = ®istered_con_driver[i];
|
||||||
|
|
||||||
if (con->con && !(con->flag & CON_DRIVER_FLAG_BIND)) {
|
if (con->con && !con->class_dev) {
|
||||||
defcsw = con->con;
|
con->class_dev =
|
||||||
break;
|
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 (!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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
postcore_initcall(vtconsole_class_init);
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
#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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
* Screen blanking
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user