diff --git a/drivers/char/vt.c b/drivers/char/vt.c index d7ff7fdb6fe3972f19f5b8db1e192e712ac08930..d788a0eaf26ab4a97ae41df7a77beb163a4b7623 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -98,9 +98,21 @@ #include #include +#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 */ diff --git a/include/linux/console.h b/include/linux/console.h index d0f8a8009490bea35186211a792714c9aa2ff459..3bdf2155e565ce1a1e4d2cdeb5b7bbfa03ff3de9 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -63,9 +63,11 @@ extern const struct consw vga_con; /* VGA text console */ extern const struct consw newport_con; /* SGI Newport console */ extern const struct consw prom_con; /* SPARC PROM console */ +int con_is_bound(const struct consw *csw); +int register_con_driver(const struct consw *csw, int first, int last); +int unregister_con_driver(const struct consw *csw); int take_over_console(const struct consw *sw, int first, int last, int deflt); void give_up_console(const struct consw *sw); - /* scroll */ #define SM_UP (1) #define SM_DOWN (2)