fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / video / console / fbcon.c
index 041d069..31f476a 100644 (file)
 
 #undef FBCONDEBUG
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>       /* MSch: for IRQ probe */
-#include <linux/tty.h>
 #include <linux/console.h>
 #include <linux/string.h>
 #include <linux/kd.h>
@@ -125,6 +123,8 @@ static int softback_lines;
 static int first_fb_vc;
 static int last_fb_vc = MAX_NR_CONSOLES - 1;
 static int fbcon_is_default = 1; 
+static int fbcon_has_exited;
+
 /* font data */
 static char fontname[40];
 
@@ -133,6 +133,7 @@ static int info_idx = -1;
 
 /* console rotation */
 static int rotate;
+static int fbcon_has_sysfs;
 
 static const struct consw fb_con;
 
@@ -140,7 +141,6 @@ static const struct consw fb_con;
 
 #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
 
-static void fbcon_free_font(struct display *);
 static int fbcon_set_origin(struct vc_data *);
 
 #define CURSOR_DRAW_DELAY              (1)
@@ -194,6 +194,9 @@ static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
                              int line, int count, int dy);
 static void fbcon_modechanged(struct fb_info *info);
 static void fbcon_set_all_vcs(struct fb_info *info);
+static void fbcon_start(void);
+static void fbcon_exit(void);
+static struct class_device *fbcon_class_device;
 
 #ifdef CONFIG_MAC
 /*
@@ -201,7 +204,7 @@ static void fbcon_set_all_vcs(struct fb_info *info);
  */
 static int vbl_detected;
 
-static irqreturn_t fb_vbl_detect(int irq, void *dummy, struct pt_regs *fp)
+static irqreturn_t fb_vbl_detect(int irq, void *dummy)
 {
        vbl_detected++;
        return IRQ_HANDLED;
@@ -252,7 +255,7 @@ static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
        if (!ops || ops->currcon < 0 || rotate > 3)
                return;
 
-       for (i = 0; i < MAX_NR_CONSOLES; i++) {
+       for (i = first_fb_vc; i <= last_fb_vc; i++) {
                vc = vc_cons[i].d;
                if (!vc || vc->vc_mode != KD_TEXT ||
                    registered_fb[con2fb_map[i]] != info)
@@ -380,24 +383,26 @@ static void fbcon_update_softback(struct vc_data *vc)
                softback_top = 0;
 }
 
-static void fb_flashcursor(void *private)
+static void fb_flashcursor(struct work_struct *work)
 {
-       struct fb_info *info = private;
+       struct fb_info *info = container_of(work, struct fb_info, queue);
        struct fbcon_ops *ops = info->fbcon_par;
        struct display *p;
        struct vc_data *vc = NULL;
        int c;
        int mode;
 
-       if (ops->currcon != -1)
+       acquire_console_sem();
+       if (ops && ops->currcon != -1)
                vc = vc_cons[ops->currcon].d;
 
        if (!vc || !CON_IS_VISIBLE(vc) ||
-           fbcon_is_inactive(vc, info) ||
            registered_fb[con2fb_map[vc->vc_num]] != info ||
-           vc_cons[ops->currcon].d->vc_deccm != 1)
+           vc->vc_deccm != 1) {
+               release_console_sem();
                return;
-       acquire_console_sem();
+       }
+
        p = &fb_display[vc->vc_num];
        c = scr_readw((u16 *) vc->vc_pos);
        mode = (!ops->cursor_flash || ops->cursor_state.enable) ?
@@ -409,7 +414,7 @@ static void fb_flashcursor(void *private)
 
 #if defined(CONFIG_ATARI) || defined(CONFIG_MAC)
 static int cursor_blink_rate;
-static irqreturn_t fb_vbl_handler(int irq, void *dev_id, struct pt_regs *fp)
+static irqreturn_t fb_vbl_handler(int irq, void *dev_id)
 {
        struct fb_info *info = dev_id;
 
@@ -437,7 +442,7 @@ static void fbcon_add_cursor_timer(struct fb_info *info)
        if ((!info->queue.func || info->queue.func == fb_flashcursor) &&
            !(ops->flags & FBCON_FLAGS_CURSOR_TIMER)) {
                if (!info->queue.func)
-                       INIT_WORK(&info->queue, fb_flashcursor, info);
+                       INIT_WORK(&info->queue, fb_flashcursor);
 
                init_timer(&ops->cursor_timer);
                ops->cursor_timer.function = cursor_timer_handler;
@@ -466,7 +471,7 @@ static int __init fb_console_setup(char *this_opt)
        int i, j;
 
        if (!this_opt || !*this_opt)
-               return 0;
+               return 1;
 
        while ((options = strsep(&this_opt, ",")) != NULL) {
                if (!strncmp(options, "font:", 5))
@@ -481,10 +486,10 @@ static int __init fb_console_setup(char *this_opt)
                                        options++;
                                }
                                if (*options != ',')
-                                       return 0;
+                                       return 1;
                                options++;
                        } else
-                               return 0;
+                               return 1;
                }
                
                if (!strncmp(options, "map:", 4)) {
@@ -496,7 +501,7 @@ static int __init fb_console_setup(char *this_opt)
                                        con2fb_map_boot[i] =
                                                (options[j++]-'0') % FB_MAX;
                                }
-                       return 0;
+                       return 1;
                }
 
                if (!strncmp(options, "vc:", 3)) {
@@ -518,7 +523,7 @@ static int __init fb_console_setup(char *this_opt)
                                rotate = 0;
                }
        }
-       return 0;
+       return 1;
 }
 
 __setup("fbcon=", fb_console_setup);
@@ -528,7 +533,7 @@ static int search_fb_in_map(int idx)
 {
        int i, retval = 0;
 
-       for (i = 0; i < MAX_NR_CONSOLES; i++) {
+       for (i = first_fb_vc; i <= last_fb_vc; i++) {
                if (con2fb_map[i] == idx)
                        retval = 1;
        }
@@ -539,7 +544,7 @@ static int search_for_mapped_con(void)
 {
        int i, retval = 0;
 
-       for (i = 0; i < MAX_NR_CONSOLES; i++) {
+       for (i = first_fb_vc; i <= last_fb_vc; i++) {
                if (con2fb_map[i] != -1)
                        retval = 1;
        }
@@ -561,6 +566,7 @@ static int fbcon_takeover(int show_logo)
 
        err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
                                fbcon_is_default);
+
        if (err) {
                for (i = first_fb_vc; i <= last_fb_vc; i++) {
                        con2fb_map[i] = -1;
@@ -795,8 +801,8 @@ static int set_con2fb_map(int unit, int newidx, int user)
        if (oldidx == newidx)
                return 0;
 
-       if (!info)
-               err =  -EINVAL;
+       if (!info || fbcon_has_exited)
+               return -EINVAL;
 
        if (!err && !search_for_mapped_con()) {
                info_idx = newidx;
@@ -832,6 +838,9 @@ static int set_con2fb_map(int unit, int newidx, int user)
                con2fb_init_display(vc, info, unit, show_logo);
        }
 
+       if (!search_fb_in_map(info_idx))
+               info_idx = newidx;
+
        release_console_sem();
        return err;
 }
@@ -1034,6 +1043,7 @@ static const char *fbcon_startup(void)
 #endif                         /* CONFIG_MAC */
 
        fbcon_add_cursor_timer(info);
+       fbcon_has_exited = 0;
        return display_desc;
 }
 
@@ -1061,17 +1071,36 @@ static void fbcon_init(struct vc_data *vc, int init)
 
        /* If we are not the first console on this
           fb, copy the font from that console */
-       t = &fb_display[svc->vc_num];
-       if (!vc->vc_font.data) {
-               vc->vc_font.data = (void *)(p->fontdata = t->fontdata);
-               vc->vc_font.width = (*default_mode)->vc_font.width;
-               vc->vc_font.height = (*default_mode)->vc_font.height;
-               p->userfont = t->userfont;
-               if (p->userfont)
-                       REFCOUNT(p->fontdata)++;
+       t = &fb_display[fg_console];
+       if (!p->fontdata) {
+               if (t->fontdata) {
+                       struct vc_data *fvc = vc_cons[fg_console].d;
+
+                       vc->vc_font.data = (void *)(p->fontdata =
+                                                   fvc->vc_font.data);
+                       vc->vc_font.width = fvc->vc_font.width;
+                       vc->vc_font.height = fvc->vc_font.height;
+                       p->userfont = t->userfont;
+
+                       if (p->userfont)
+                               REFCOUNT(p->fontdata)++;
+               } else {
+                       const struct font_desc *font = NULL;
+
+                       if (!fontname[0] || !(font = find_font(fontname)))
+                               font = get_default_font(info->var.xres,
+                                                       info->var.yres);
+                       vc->vc_font.width = font->width;
+                       vc->vc_font.height = font->height;
+                       vc->vc_font.data = (void *)(p->fontdata = font->data);
+                       vc->vc_font.charcount = 256; /* FIXME  Need to
+                                                       support more fonts */
+               }
        }
+
        if (p->userfont)
                charcnt = FNTCHARCNT(p->fontdata);
+
        vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
        vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
        if (charcnt == 256) {
@@ -1142,15 +1171,50 @@ static void fbcon_init(struct vc_data *vc, int init)
                set_blitting_type(vc, info);
        }
 
+       ops->p = &fb_display[fg_console];
+}
+
+static void fbcon_free_font(struct display *p)
+{
+       if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
+               kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
+       p->fontdata = NULL;
+       p->userfont = 0;
 }
 
 static void fbcon_deinit(struct vc_data *vc)
 {
        struct display *p = &fb_display[vc->vc_num];
+       struct fb_info *info;
+       struct fbcon_ops *ops;
+       int idx;
 
-       if (info_idx != -1)
-           return;
        fbcon_free_font(p);
+       idx = con2fb_map[vc->vc_num];
+
+       if (idx == -1)
+               goto finished;
+
+       info = registered_fb[idx];
+
+       if (!info)
+               goto finished;
+
+       ops = info->fbcon_par;
+
+       if (!ops)
+               goto finished;
+
+       if (CON_IS_VISIBLE(vc))
+               fbcon_del_cursor_timer(info);
+
+       ops->flags &= ~FBCON_FLAGS_INIT;
+finished:
+
+       if (!con_is_bound(&fb_con))
+               fbcon_exit();
+
+       return;
 }
 
 /* ====================================================================== */
@@ -1744,7 +1808,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir,
                                        fbcon_redraw_move(vc, p, 0, t, count);
                                ypan_up_redraw(vc, t, count);
                                if (vc->vc_rows - b > 0)
-                                       fbcon_redraw_move(vc, p, b - count,
+                                       fbcon_redraw_move(vc, p, b,
                                                          vc->vc_rows - b, b);
                        } else
                                fbcon_redraw_move(vc, p, t + count, b - t - count, t);
@@ -2098,12 +2162,16 @@ static int fbcon_switch(struct vc_data *vc)
                if (info->fbops->fb_set_par)
                        info->fbops->fb_set_par(info);
 
-               if (old_info != info) {
+               if (old_info != info)
                        fbcon_del_cursor_timer(old_info);
-                       fbcon_add_cursor_timer(info);
-               }
        }
 
+       if (fbcon_is_inactive(vc, info) ||
+           ops->blank_state != FB_BLANK_UNBLANK)
+               fbcon_del_cursor_timer(info);
+       else
+               fbcon_add_cursor_timer(info);
+
        set_blitting_type(vc, info);
        ops->cursor_reset = 1;
 
@@ -2213,22 +2281,15 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
                        update_screen(vc);
        }
 
-       if (!blank)
-               fbcon_add_cursor_timer(info);
-       else
+       if (fbcon_is_inactive(vc, info) ||
+           ops->blank_state != FB_BLANK_UNBLANK)
                fbcon_del_cursor_timer(info);
+       else
+               fbcon_add_cursor_timer(info);
 
        return 0;
 }
 
-static void fbcon_free_font(struct display *p)
-{
-       if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
-               kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
-       p->fontdata = NULL;
-       p->userfont = 0;
-}
-
 static int fbcon_get_font(struct vc_data *vc, struct console_font *font)
 {
        u8 *fontdata = vc->vc_font.data;
@@ -2442,7 +2503,7 @@ static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigne
 
        FNTSUM(new_data) = csum;
        /* Check if the same font is on some other console already */
-       for (i = 0; i < MAX_NR_CONSOLES; i++) {
+       for (i = first_fb_vc; i <= last_fb_vc; i++) {
                struct vc_data *tmp = vc_cons[i].d;
                
                if (fb_display[i].userfont &&
@@ -2630,7 +2691,7 @@ static int fbcon_scrolldelta(struct vc_data *vc, int lines)
                                        scr_memcpyw((u16 *) q, (u16 *) p,
                                                    vc->vc_size_row);
                                }
-                               softback_in = p;
+                               softback_in = softback_curr = p;
                                update_region(vc, vc->vc_origin,
                                              logo_lines * vc->vc_cols);
                        }
@@ -2767,7 +2828,7 @@ static void fbcon_set_all_vcs(struct fb_info *info)
        if (!ops || ops->currcon < 0)
                return;
 
-       for (i = 0; i < MAX_NR_CONSOLES; i++) {
+       for (i = first_fb_vc; i <= last_fb_vc; i++) {
                vc = vc_cons[i].d;
                if (!vc || vc->vc_mode != KD_TEXT ||
                    registered_fb[con2fb_map[i]] != info)
@@ -2829,22 +2890,57 @@ static int fbcon_mode_deleted(struct fb_info *info,
        return found;
 }
 
+static int fbcon_fb_unregistered(int idx)
+{
+       int i;
+
+       for (i = first_fb_vc; i <= last_fb_vc; i++) {
+               if (con2fb_map[i] == idx)
+                       con2fb_map[i] = -1;
+       }
+
+       if (idx == info_idx) {
+               info_idx = -1;
+
+               for (i = 0; i < FB_MAX; i++) {
+                       if (registered_fb[i] != NULL) {
+                               info_idx = i;
+                               break;
+                       }
+               }
+       }
+
+       if (info_idx != -1) {
+               for (i = first_fb_vc; i <= last_fb_vc; i++) {
+                       if (con2fb_map[i] == -1)
+                               con2fb_map[i] = info_idx;
+               }
+       }
+
+       if (!num_registered_fb)
+               unregister_con_driver(&fb_con);
+
+       return 0;
+}
+
 static int fbcon_fb_registered(int idx)
 {
        int ret = 0, i;
 
        if (info_idx == -1) {
-               for (i = 0; i < MAX_NR_CONSOLES; i++) {
+               for (i = first_fb_vc; i <= last_fb_vc; i++) {
                        if (con2fb_map_boot[i] == idx) {
                                info_idx = idx;
                                break;
                        }
                }
+
                if (info_idx != -1)
                        ret = fbcon_takeover(1);
        } else {
-               for (i = 0; i < MAX_NR_CONSOLES; i++) {
-                       if (con2fb_map_boot[i] == idx)
+               for (i = first_fb_vc; i <= last_fb_vc; i++) {
+                       if (con2fb_map_boot[i] == idx &&
+                           con2fb_map[i] == -1)
                                set_con2fb_map(i, idx, 0);
                }
        }
@@ -2881,7 +2977,7 @@ static void fbcon_new_modelist(struct fb_info *info)
        struct fb_var_screeninfo var;
        struct fb_videomode *mode;
 
-       for (i = 0; i < MAX_NR_CONSOLES; i++) {
+       for (i = first_fb_vc; i <= last_fb_vc; i++) {
                if (registered_fb[con2fb_map[i]] != info)
                        continue;
                if (!fb_display[i].mode)
@@ -2909,6 +3005,14 @@ static int fbcon_event_notify(struct notifier_block *self,
        struct fb_con2fbmap *con2fb;
        int ret = 0;
 
+       /*
+        * ignore all events except driver registration and deregistration
+        * if fbcon is not active
+        */
+       if (fbcon_has_exited && !(action == FB_EVENT_FB_REGISTERED ||
+                                 action == FB_EVENT_FB_UNREGISTERED))
+               goto done;
+
        switch(action) {
        case FB_EVENT_SUSPEND:
                fbcon_suspended(info);
@@ -2929,6 +3033,9 @@ static int fbcon_event_notify(struct notifier_block *self,
        case FB_EVENT_FB_REGISTERED:
                ret = fbcon_fb_registered(info->node);
                break;
+       case FB_EVENT_FB_UNREGISTERED:
+               ret = fbcon_fb_unregistered(info->node);
+               break;
        case FB_EVENT_SET_CONSOLE_MAP:
                con2fb = event->data;
                ret = set_con2fb_map(con2fb->console - 1,
@@ -2944,16 +3051,9 @@ static int fbcon_event_notify(struct notifier_block *self,
        case FB_EVENT_NEW_MODELIST:
                fbcon_new_modelist(info);
                break;
-       case FB_EVENT_SET_CON_ROTATE:
-               fbcon_rotate(info, *(int *)event->data);
-               break;
-       case FB_EVENT_GET_CON_ROTATE:
-               ret = fbcon_get_rotate(info);
-               break;
-       case FB_EVENT_SET_CON_ROTATE_ALL:
-               fbcon_rotate_all(info, *(int *)event->data);
        }
 
+done:
        return ret;
 }
 
@@ -2991,27 +3091,199 @@ static struct notifier_block fbcon_event_notifier = {
        .notifier_call  = fbcon_event_notify,
 };
 
-static int __init fb_console_init(void)
+static ssize_t store_rotate(struct class_device *class_device,
+                           const char *buf, size_t count)
 {
-       int i;
+       struct fb_info *info;
+       int rotate, idx;
+       char **last = NULL;
+
+       if (fbcon_has_exited)
+               return count;
 
        acquire_console_sem();
-       fb_register_client(&fbcon_event_notifier);
+       idx = con2fb_map[fg_console];
+
+       if (idx == -1 || registered_fb[idx] == NULL)
+               goto err;
+
+       info = registered_fb[idx];
+       rotate = simple_strtoul(buf, last, 0);
+       fbcon_rotate(info, rotate);
+err:
        release_console_sem();
+       return count;
+}
 
-       for (i = 0; i < MAX_NR_CONSOLES; i++)
-               con2fb_map[i] = -1;
+static ssize_t store_rotate_all(struct class_device *class_device,
+                               const char *buf, size_t count)
+{
+       struct fb_info *info;
+       int rotate, idx;
+       char **last = NULL;
 
+       if (fbcon_has_exited)
+               return count;
+
+       acquire_console_sem();
+       idx = con2fb_map[fg_console];
+
+       if (idx == -1 || registered_fb[idx] == NULL)
+               goto err;
+
+       info = registered_fb[idx];
+       rotate = simple_strtoul(buf, last, 0);
+       fbcon_rotate_all(info, rotate);
+err:
+       release_console_sem();
+       return count;
+}
+
+static ssize_t show_rotate(struct class_device *class_device, char *buf)
+{
+       struct fb_info *info;
+       int rotate = 0, idx;
+
+       if (fbcon_has_exited)
+               return 0;
+
+       acquire_console_sem();
+       idx = con2fb_map[fg_console];
+
+       if (idx == -1 || registered_fb[idx] == NULL)
+               goto err;
+
+       info = registered_fb[idx];
+       rotate = fbcon_get_rotate(info);
+err:
+       release_console_sem();
+       return snprintf(buf, PAGE_SIZE, "%d\n", rotate);
+}
+
+static struct class_device_attribute class_device_attrs[] = {
+       __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
+       __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all),
+};
+
+static int fbcon_init_class_device(void)
+{
+       int i, error = 0;
+
+       fbcon_has_sysfs = 1;
+
+       for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) {
+               error = class_device_create_file(fbcon_class_device,
+                                                &class_device_attrs[i]);
+
+               if (error)
+                       break;
+       }
+
+       if (error) {
+               while (--i >= 0)
+                       class_device_remove_file(fbcon_class_device,
+                                                &class_device_attrs[i]);
+
+               fbcon_has_sysfs = 0;
+       }
+
+       return 0;
+}
+
+static void fbcon_start(void)
+{
        if (num_registered_fb) {
+               int i;
+
+               acquire_console_sem();
+
                for (i = 0; i < FB_MAX; i++) {
                        if (registered_fb[i] != NULL) {
                                info_idx = i;
                                break;
                        }
                }
+
+               release_console_sem();
                fbcon_takeover(0);
        }
+}
+
+static void fbcon_exit(void)
+{
+       struct fb_info *info;
+       int i, j, mapped;
+
+       if (fbcon_has_exited)
+               return;
+
+#ifdef CONFIG_ATARI
+       free_irq(IRQ_AUTO_4, fb_vbl_handler);
+#endif
+#ifdef CONFIG_MAC
+       if (MACH_IS_MAC && vbl_detected)
+               free_irq(IRQ_MAC_VBL, fb_vbl_handler);
+#endif
+
+       kfree((void *)softback_buf);
+       softback_buf = 0UL;
+
+       for (i = 0; i < FB_MAX; i++) {
+               mapped = 0;
+               info = registered_fb[i];
+
+               if (info == NULL)
+                       continue;
+
+               for (j = first_fb_vc; j <= last_fb_vc; j++) {
+                       if (con2fb_map[j] == i)
+                               mapped = 1;
+               }
+
+               if (mapped) {
+                       if (info->fbops->fb_release)
+                               info->fbops->fb_release(info, 0);
+                       module_put(info->fbops->owner);
+
+                       if (info->fbcon_par) {
+                               struct fbcon_ops *ops = info->fbcon_par;
+
+                               fbcon_del_cursor_timer(info);
+                               kfree(ops->cursor_src);
+                               kfree(info->fbcon_par);
+                               info->fbcon_par = NULL;
+                       }
+
+                       if (info->queue.func == fb_flashcursor)
+                               info->queue.func = NULL;
+               }
+       }
 
+       fbcon_has_exited = 1;
+}
+
+static int __init fb_console_init(void)
+{
+       int i;
+
+       acquire_console_sem();
+       fb_register_client(&fbcon_event_notifier);
+       fbcon_class_device =
+           class_device_create(fb_class, NULL, MKDEV(0, 0), NULL, "fbcon");
+
+       if (IS_ERR(fbcon_class_device)) {
+               printk(KERN_WARNING "Unable to create class_device "
+                      "for fbcon; errno = %ld\n",
+                      PTR_ERR(fbcon_class_device));
+               fbcon_class_device = NULL;
+       } else
+               fbcon_init_class_device();
+
+       for (i = 0; i < MAX_NR_CONSOLES; i++)
+               con2fb_map[i] = -1;
+
+       release_console_sem();
+       fbcon_start();
        return 0;
 }
 
@@ -3019,12 +3291,28 @@ module_init(fb_console_init);
 
 #ifdef MODULE
 
+static void __exit fbcon_deinit_class_device(void)
+{
+       int i;
+
+       if (fbcon_has_sysfs) {
+               for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
+                       class_device_remove_file(fbcon_class_device,
+                                                &class_device_attrs[i]);
+
+               fbcon_has_sysfs = 0;
+       }
+}
+
 static void __exit fb_console_exit(void)
 {
        acquire_console_sem();
        fb_unregister_client(&fbcon_event_notifier);
+       fbcon_deinit_class_device();
+       class_device_destroy(fb_class, MKDEV(0, 0));
+       fbcon_exit();
        release_console_sem();
-       give_up_console(&fb_con);
+       unregister_con_driver(&fb_con);
 }      
 
 module_exit(fb_console_exit);