|
@@ -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 = ®istered_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 = ®istered_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,32 +2780,399 @@ 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 = ®istered_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 = ®istered_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 = ®istered_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 = ®istered_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 = ®istered_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 = ®istered_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 = ®istered_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 = ®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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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
|
|
|
*/
|