summaryrefslogtreecommitdiff
path: root/drivers/char/vt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/vt.c')
-rw-r--r--drivers/char/vt.c467
1 files changed, 434 insertions, 33 deletions
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index d7ff7fdb6fe3..d788a0eaf26a 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -98,9 +98,21 @@
#include <asm/system.h>
#include <asm/uaccess.h>
+#define MAX_NR_CON_DRIVER 16
+#define CON_DRIVER_FLAG_BIND 1
+#define CON_DRIVER_FLAG_INIT 2
+
+struct con_driver {
+ const struct consw *con;
+ const char *desc;
+ int first;
+ int last;
+ int flag;
+};
+
+static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];
const struct consw *conswitchp;
-static struct consw *defcsw; /* default console */
/* A bitmap for codes <32. A bit of 1 indicates that the code
* corresponding to that bit number invokes some special action
@@ -2558,7 +2570,7 @@ static int __init con_init(void)
{
const char *display_desc = NULL;
struct vc_data *vc;
- unsigned int currcons = 0;
+ unsigned int currcons = 0, i;
acquire_console_sem();
@@ -2570,6 +2582,22 @@ static int __init con_init(void)
return 0;
}
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ struct con_driver *con_driver = &registered_con_driver[i];
+
+ if (con_driver->con == NULL) {
+ con_driver->con = conswitchp;
+ con_driver->desc = display_desc;
+ con_driver->flag = CON_DRIVER_FLAG_INIT;
+ con_driver->first = 0;
+ con_driver->last = MAX_NR_CONSOLES - 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++)
+ con_driver_map[i] = conswitchp;
+
init_timer(&console_timer);
console_timer.function = blank_screen_t;
if (blankinterval) {
@@ -2658,33 +2686,36 @@ int __init vty_init(void)
#ifndef VT_SINGLE_DRIVER
-/*
- * 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.
- */
-
-int take_over_console(const struct consw *csw, int first, int last, int deflt)
+static int bind_con_driver(const struct consw *csw, int first, int last,
+ int deflt)
{
- int i, j = -1, k = -1;
- const char *desc;
- struct module *owner;
+ struct module *owner = csw->owner;
+ const char *desc = NULL;
+ struct con_driver *con_driver;
+ int i, j = -1, k = -1, retval = -ENODEV;
- owner = csw->owner;
if (!try_module_get(owner))
return -ENODEV;
- /* save default console, for possible recovery later on */
- if (!defcsw)
- defcsw = (struct consw *) conswitchp;
-
acquire_console_sem();
- desc = csw->con_startup();
- if (!desc) {
- release_console_sem();
- module_put(owner);
- return -ENODEV;
+ /* check if driver is registered */
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = &registered_con_driver[i];
+
+ if (con_driver->con == csw) {
+ desc = con_driver->desc;
+ retval = 0;
+ break;
+ }
+ }
+
+ if (retval)
+ goto err;
+
+ if (!(con_driver->flag & CON_DRIVER_FLAG_INIT)) {
+ csw->con_startup();
+ con_driver->flag |= CON_DRIVER_FLAG_INIT;
}
if (deflt) {
@@ -2695,6 +2726,9 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt)
conswitchp = csw;
}
+ first = max(first, con_driver->first);
+ last = min(last, con_driver->last);
+
for (i = first; i <= last; i++) {
int old_was_color;
struct vc_data *vc = vc_cons[i].d;
@@ -2746,33 +2780,400 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt)
} else
printk("to %s\n", desc);
+ retval = 0;
+err:
release_console_sem();
module_put(owner);
- return 0;
-}
+ return retval;
+};
-void give_up_console(const struct consw *csw)
+static int unbind_con_driver(const struct consw *csw, int first, int last,
+ int deflt)
{
- int i, first = -1, last = -1, deflt = 0;
+ struct module *owner = csw->owner;
+ const struct consw *defcsw = NULL;
+ struct con_driver *con_driver = NULL, *con_back = NULL;
+ int i, retval = -ENODEV;
- for (i = 0; i < MAX_NR_CONSOLES; i++)
+ if (!try_module_get(owner))
+ return -ENODEV;
+
+ acquire_console_sem();
+
+ /* check if driver is registered and if it is unbindable */
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = &registered_con_driver[i];
+
+ if (con_driver->con == csw &&
+ con_driver->flag & CON_DRIVER_FLAG_BIND) {
+ retval = 0;
+ break;
+ }
+ }
+
+ if (retval) {
+ release_console_sem();
+ goto err;
+ }
+
+ /* 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)) {
+ defcsw = con_back->con;
+ retval = 0;
+ break;
+ }
+ }
+
+ if (retval) {
+ release_console_sem();
+ goto err;
+ }
+
+ if (!con_is_bound(csw)) {
+ release_console_sem();
+ goto err;
+ }
+
+ first = max(first, con_driver->first);
+ last = min(last, con_driver->last);
+
+ for (i = first; i <= last; i++) {
if (con_driver_map[i] == csw) {
- if (first == -1)
- first = i;
- last = i;
module_put(csw->owner);
con_driver_map[i] = NULL;
}
+ }
+
+ if (!con_is_bound(defcsw)) {
+ defcsw->con_startup();
+ con_back->flag |= CON_DRIVER_FLAG_INIT;
+ }
+
+ if (!con_is_bound(csw))
+ con_driver->flag &= ~CON_DRIVER_FLAG_INIT;
+
+ release_console_sem();
+ retval = bind_con_driver(defcsw, first, last, deflt);
+err:
+ module_put(owner);
+ return retval;
+
+}
+
+/**
+ * con_is_bound - checks if driver is bound to the console
+ * @csw: console driver
+ *
+ * RETURNS: zero if unbound, nonzero if bound
+ *
+ * Drivers can call this and if zero, they should release
+ * all resources allocated on con_startup()
+ */
+int con_is_bound(const struct consw *csw)
+{
+ int i, bound = 0;
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ if (con_driver_map[i] == csw) {
+ bound = 1;
+ break;
+ }
+ }
+
+ return bound;
+}
+EXPORT_SYMBOL(con_is_bound);
+
+/**
+ * register_con_driver - register console driver to console layer
+ * @csw: console driver
+ * @first: the first console to take over, minimum value is 0
+ * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1
+ *
+ * DESCRIPTION: This function registers a console driver which can later
+ * bind to a range of consoles specified by @first and @last. It will
+ * also initialize the console driver by calling con_startup().
+ */
+int register_con_driver(const struct consw *csw, int first, int last)
+{
+ struct module *owner = csw->owner;
+ struct con_driver *con_driver;
+ const char *desc;
+ int i, retval = 0;
+
+ if (!try_module_get(owner))
+ return -ENODEV;
+
+ acquire_console_sem();
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = &registered_con_driver[i];
+
+ /* already registered */
+ if (con_driver->con == csw)
+ retval = -EINVAL;
+ }
+
+ if (retval)
+ goto err;
+
+ desc = csw->con_startup();
+
+ if (!desc)
+ goto err;
+
+ retval = -EINVAL;
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = &registered_con_driver[i];
+
+ if (con_driver->con == NULL) {
+ con_driver->con = csw;
+ con_driver->desc = desc;
+ con_driver->flag = CON_DRIVER_FLAG_BIND |
+ CON_DRIVER_FLAG_INIT;
+ con_driver->first = first;
+ con_driver->last = last;
+ retval = 0;
+ break;
+ }
+ }
+
+err:
+ release_console_sem();
+ module_put(owner);
+ return retval;
+}
+EXPORT_SYMBOL(register_con_driver);
+
+/**
+ * unregister_con_driver - unregister console driver from console layer
+ * @csw: console driver
+ *
+ * DESCRIPTION: All drivers that registers to the console layer must
+ * call this function upon exit, or if the console driver is in a state
+ * where it won't be able to handle console services, such as the
+ * framebuffer console without loaded framebuffer drivers.
+ *
+ * The driver must unbind first prior to unregistration.
+ */
+int unregister_con_driver(const struct consw *csw)
+{
+ int i, retval = -ENODEV;
+
+ acquire_console_sem();
+
+ /* cannot unregister a bound driver */
+ if (con_is_bound(csw))
+ goto err;
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ struct con_driver *con_driver = &registered_con_driver[i];
+
+ if (con_driver->con == csw &&
+ con_driver->flag & CON_DRIVER_FLAG_BIND) {
+ con_driver->con = NULL;
+ con_driver->desc = NULL;
+ con_driver->flag = 0;
+ con_driver->first = 0;
+ con_driver->last = 0;
+ retval = 0;
+ break;
+ }
+ }
+
+err:
+ release_console_sem();
+ return retval;
+}
+EXPORT_SYMBOL(unregister_con_driver);
+
+/*
+ * 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.
+ *
+ * take_over_console is basically a register followed by unbind
+ */
+int take_over_console(const struct consw *csw, int first, int last, int deflt)
+{
+ int err;
+
+ err = register_con_driver(csw, first, last);
+
+ if (!err)
+ err = bind_con_driver(csw, first, last, deflt);
+
+ return err;
+}
+
+/*
+ * give_up_console is a wrapper to unregister_con_driver. It will only
+ * work if driver is fully unbound.
+ */
+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)
+{
+ 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;
+
+ 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_BIND)) {
+ defcsw = con->con;
+ break;
+ }
+ }
- if (first != -1 && defcsw) {
- if (first == 0 && last == MAX_NR_CONSOLES - 1)
+ 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;
- take_over_console(defcsw, first, last, deflt);
+
+ 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 (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 = &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
*/