]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/video/console/fbcon.c
[PATCH] Detaching fbcon: add capability to attach/detach fbcon
[linux-2.6-omap-h63xx.git] / drivers / video / console / fbcon.c
index 47ba1a79adcd350e6487b42d1d5e6b1a4e1b6c7a..4b7be685c1604c6acc24390d87ebe5eb4a072c7d 100644 (file)
@@ -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
 /*
@@ -389,15 +392,18 @@ static void fb_flashcursor(void *private)
        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_cons[ops->currcon].d->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) ?
@@ -2099,12 +2105,11 @@ 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);
-               }
        }
 
+       fbcon_add_cursor_timer(info);
        set_blitting_type(vc, info);
        ops->cursor_reset = 1;
 
@@ -2845,7 +2850,8 @@ static int fbcon_fb_registered(int idx)
                        ret = fbcon_takeover(1);
        } else {
                for (i = 0; i < MAX_NR_CONSOLES; i++) {
-                       if (con2fb_map_boot[i] == idx)
+                       if (con2fb_map_boot[i] == idx &&
+                           con2fb_map[i] == -1)
                                set_con2fb_map(i, idx, 0);
                }
        }
@@ -2945,14 +2951,6 @@ 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);
        }
 
        return ret;
@@ -2992,27 +2990,198 @@ 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;
 
        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;
+
+       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;
+
+       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 ssize_t store_attach(struct class_device *class_device,
+                           const char *buf, size_t count)
+{
+       if (info_idx == -1)
+               fbcon_start();
+
+       return count;
+}
+
+static ssize_t store_detach(struct class_device *class_device,
+                           const char *buf, size_t count)
+{
+       if (info_idx != -1) {
+               fbcon_exit();
+               give_up_console(&fb_con);
+       }
+
+       info_idx = -1;
+       return count;
+}
+
+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),
+       __ATTR(attach, S_IWUSR, NULL, store_attach),
+       __ATTR(detach, S_IWUSR, NULL, store_detach),
+};
+
+static int fbcon_init_class_device(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
+               class_device_create_file(fbcon_class_device,
+                                        &class_device_attrs[i]);
+       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;
+
+       acquire_console_sem();
+#ifdef CONFIG_ATARI
+       free_irq(IRQ_AUTO_4, fbcon_vbl_handler);
+#endif
+#ifdef CONFIG_MAC
+       if (MACH_IS_MAC && vbl_detected)
+               free_irq(IRQ_MAC_VBL, fbcon_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 = 0; j < MAX_NR_CONSOLES; j++) {
+                       if (con2fb_map[j] == i) {
+                               con2fb_map[j] = -1;
+                               mapped = 1;
+                       }
+               }
+
+               if (mapped) {
+                       if (info->fbops->fb_release)
+                               info->fbops->fb_release(info, 0);
+                       module_put(info->fbops->owner);
+
+                       if (info->fbcon_par) {
+                               fbcon_del_cursor_timer(info);
+                               kfree(info->fbcon_par);
+                               info->fbcon_par = NULL;
+                       }
+
+                       if (info->queue.func == fb_flashcursor)
+                               info->queue.func = NULL;
+
+               }
+       }
+
+       release_console_sem();
+}
+
+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(FB_MAJOR, FB_MAX), 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;
 }
 
@@ -3020,12 +3189,22 @@ module_init(fb_console_init);
 
 #ifdef MODULE
 
+static void fbcon_deinit_class_device(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
+               class_device_remove_file(fbcon_class_device,
+                                        &class_device_attrs[i]);
+}
+
 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(FB_MAJOR, FB_MAX));
        release_console_sem();
-       give_up_console(&fb_con);
 }      
 
 module_exit(fb_console_exit);