vt: add an event interface
authorAlan Cox <alan@linux.intel.com>
Sat, 19 Sep 2009 20:13:24 +0000 (13:13 -0700)
committerLive-CD User <linux@linux.site>
Sat, 19 Sep 2009 20:13:24 +0000 (13:13 -0700)
This is needed and requested in various forms for ConsoleKit, screenblank
handling and the like so do the job with a single interface. Also build the
interface so that unlike VT_WAITACTIVE and friends it won't miss events.

FIXME: Should this be a waitactive ioctl or a new device file you can poll
and read events from. We need the code anyway to fix up the existing broken
wait for console switch logic but the ConsoleKit people would prefer the
new device to the ioctl we have here

Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/char/vt.c
drivers/char/vt_ioctl.c
include/linux/vt.h
include/linux/vt_kern.h

index e47a4c88976b43312d12eb1e7f38ab945bbdf2e9..33214d92ca4cc4d255fb09f576019984adca1077 100644 (file)
@@ -252,7 +252,6 @@ static void notify_update(struct vc_data *vc)
        struct vt_notifier_param param = { .vc = vc };
        atomic_notifier_call_chain(&vt_notifier_list, VT_UPDATE, &param);
 }
-
 /*
  *     Low-Level Functions
  */
@@ -935,6 +934,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
 
        if (CON_IS_VISIBLE(vc))
                update_screen(vc);
+       vt_event_post(VT_EVENT_RESIZE, vc->vc_num, vc->vc_num);
        return err;
 }
 
@@ -3637,6 +3637,7 @@ void do_blank_screen(int entering_gfx)
                blank_state = blank_vesa_wait;
                mod_timer(&console_timer, jiffies + vesa_off_interval);
        }
+       vt_event_post(VT_EVENT_BLANK, vc->vc_num, vc->vc_num);
 }
 EXPORT_SYMBOL(do_blank_screen);
 
@@ -3681,6 +3682,7 @@ void do_unblank_screen(int leaving_gfx)
                console_blank_hook(0);
        set_palette(vc);
        set_cursor(vc);
+       vt_event_post(VT_EVENT_UNBLANK, vc->vc_num, vc->vc_num);
 }
 EXPORT_SYMBOL(do_unblank_screen);
 
index 95189f288f8ccffcab9204c9fbeec0cd1357e3a4..35ad94e65d0dde5362f9680207ea8e7492eb53a9 100644 (file)
@@ -61,6 +61,133 @@ extern struct tty_driver *console_driver;
 
 static void complete_change_console(struct vc_data *vc);
 
+/*
+ *     User space VT_EVENT handlers
+ */
+
+struct vt_event_wait {
+       struct list_head list;
+       struct vt_event event;
+       int done;
+};
+
+static LIST_HEAD(vt_events);
+static DEFINE_SPINLOCK(vt_event_lock);
+static DECLARE_WAIT_QUEUE_HEAD(vt_event_waitqueue);
+
+/**
+ *     vt_event_post
+ *     @event: the event that occurred
+ *     @old: old console
+ *     @new: new console
+ *
+ *     Post an VT event to interested VT handlers
+ */
+
+void vt_event_post(unsigned int event, unsigned int old, unsigned int new)
+{
+       struct list_head *pos, *head;
+       unsigned long flags;
+       int wake = 0;
+
+       spin_lock_irqsave(&vt_event_lock, flags);
+       head = &vt_events;
+
+       list_for_each(pos, head) {
+               struct vt_event_wait *ve = list_entry(pos,
+                                               struct vt_event_wait, list);
+               if (!(ve->event.event & event))
+                       continue;
+               ve->event.event = event;
+               /* kernel view is consoles 0..n-1, user space view is
+                  console 1..n with 0 meaning current, so we must bias */
+               ve->event.old = old + 1;
+               ve->event.new = new + 1;
+               wake = 1;
+               ve->done = 1;
+       }
+       spin_unlock_irqrestore(&vt_event_lock, flags);
+       if (wake)
+               wake_up_interruptible(&vt_event_waitqueue);
+}
+
+/**
+ *     vt_event_wait           -       wait for an event
+ *     @vw: our event
+ *
+ *     Waits for an event to occur which completes our vt_event_wait
+ *     structure. On return the structure has wv->done set to 1 for success
+ *     or 0 if some event such as a signal ended the wait.
+ */
+
+static void vt_event_wait(struct vt_event_wait *vw)
+{
+       unsigned long flags;
+       /* Prepare the event */
+       INIT_LIST_HEAD(&vw->list);
+       vw->done = 0;
+       /* Queue our event */
+       spin_lock_irqsave(&vt_event_lock, flags);
+       list_add(&vw->list, &vt_events);
+       spin_unlock_irqrestore(&vt_event_lock, flags);
+       /* Wait for it to pass */
+       wait_event_interruptible(vt_event_waitqueue, vw->done);
+       /* Dequeue it */
+       spin_lock_irqsave(&vt_event_lock, flags);
+       list_del(&vw->list);
+       spin_unlock_irqrestore(&vt_event_lock, flags);
+}
+
+/**
+ *     vt_event_wait_ioctl     -       event ioctl handler
+ *     @arg: argument to ioctl
+ *
+ *     Implement the VT_WAITEVENT ioctl using the VT event interface
+ */
+
+static int vt_event_wait_ioctl(struct vt_event __user *event)
+{
+       struct vt_event_wait vw;
+
+       if (copy_from_user(&vw.event, event, sizeof(struct vt_event)))
+               return -EFAULT;
+       /* Highest supported event for now */
+       if (vw.event.event & ~VT_MAX_EVENT)
+               return -EINVAL;
+
+       vt_event_wait(&vw);
+       /* If it occurred report it */
+       if (vw.done) {
+               if (copy_to_user(event, &vw.event, sizeof(struct vt_event)))
+                       return -EFAULT;
+               return 0;
+       }
+       return -EINTR;
+}
+
+/**
+ *     vt_waitactive   -       active console wait
+ *     @event: event code
+ *     @n: new console
+ *
+ *     Helper for event waits. Used to implement the legacy
+ *     event waiting ioctls in terms of events
+ */
+
+int vt_waitactive(int n)
+{
+       struct vt_event_wait vw;
+       do {
+               if (n == fg_console + 1)
+                       break;
+               vw.event.event = VT_EVENT_SWITCH;
+               vt_event_wait(&vw);
+               if (vw.done == 0)
+                       return -EINTR;
+       } while (vw.event.new != n);
+       return 0;
+}
+
 /*
  * these are the valid i/o ports we're allowed to change. they map all the
  * video ports
@@ -360,6 +487,8 @@ do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, int perm, struct vc_
        return 0;
 }
 
+
+
 /*
  * We handle the console-specific ioctl's here.  We allow the
  * capability to modify any console, not just the fg_console. 
@@ -851,7 +980,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                if (arg == 0 || arg > MAX_NR_CONSOLES)
                        ret = -ENXIO;
                else
-                       ret = vt_waitactive(arg - 1);
+                       ret = vt_waitactive(arg);
                break;
 
        /*
@@ -1159,6 +1288,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                ret = put_user(vc->vc_hi_font_mask,
                                        (unsigned short __user *)arg);
                break;
+       case VT_WAITEVENT:
+               ret = vt_event_wait_ioctl((struct vt_event __user *)arg);
+               break;
        default:
                ret = -ENOIOCTLCMD;
        }
@@ -1170,54 +1302,6 @@ eperm:
        goto out;
 }
 
-/*
- * Sometimes we want to wait until a particular VT has been activated. We
- * do it in a very simple manner. Everybody waits on a single queue and
- * get woken up at once. Those that are satisfied go on with their business,
- * while those not ready go back to sleep. Seems overkill to add a wait
- * to each vt just for this - usually this does nothing!
- */
-static DECLARE_WAIT_QUEUE_HEAD(vt_activate_queue);
-
-/*
- * Sleeps until a vt is activated, or the task is interrupted. Returns
- * 0 if activation, -EINTR if interrupted by a signal handler.
- */
-int vt_waitactive(int vt)
-{
-       int retval;
-       DECLARE_WAITQUEUE(wait, current);
-
-       add_wait_queue(&vt_activate_queue, &wait);
-       for (;;) {
-               retval = 0;
-
-               /*
-                * Synchronize with redraw_screen(). By acquiring the console
-                * semaphore we make sure that the console switch is completed
-                * before we return. If we didn't wait for the semaphore, we
-                * could return at a point where fg_console has already been
-                * updated, but the console switch hasn't been completed.
-                */
-               acquire_console_sem();
-               set_current_state(TASK_INTERRUPTIBLE);
-               if (vt == fg_console) {
-                       release_console_sem();
-                       break;
-               }
-               release_console_sem();
-               retval = -ERESTARTNOHAND;
-               if (signal_pending(current))
-                       break;
-               schedule();
-       }
-       remove_wait_queue(&vt_activate_queue, &wait);
-       __set_current_state(TASK_RUNNING);
-       return retval;
-}
-
-#define vt_wake_waitactive() wake_up(&vt_activate_queue)
-
 void reset_vc(struct vc_data *vc)
 {
        vc->vc_mode = KD_TEXT;
@@ -1262,6 +1346,7 @@ void vc_SAK(struct work_struct *work)
 static void complete_change_console(struct vc_data *vc)
 {
        unsigned char old_vc_mode;
+       int old = fg_console;
 
        last_console = fg_console;
 
@@ -1325,7 +1410,7 @@ static void complete_change_console(struct vc_data *vc)
        /*
         * Wake anyone waiting for their VT to activate
         */
-       vt_wake_waitactive();
+       vt_event_post(VT_EVENT_SWITCH, old, vc->vc_num);
        return;
 }
 
index 02c1c02887703b6d044af1bc57685729cb242278..89c03a11e19358fc40de16333b5e20b4703f4da9 100644 (file)
@@ -74,4 +74,18 @@ struct vt_consize {
 #define VT_UNLOCKSWITCH 0x560C  /* allow vt switching */
 #define VT_GETHIFONTMASK 0x560D  /* return hi font mask */
 
+struct vt_event {
+       unsigned int event;
+#define VT_EVENT_SWITCH                0x0001  /* Console switch */
+#define VT_EVENT_BLANK         0x0002  /* Screen blank */
+#define VT_EVENT_UNBLANK       0x0004  /* Screen unblank */
+#define VT_EVENT_RESIZE                0x0008  /* Resize display */
+#define VT_MAX_EVENT           0x000F
+       unsigned int old;               /* Old console */
+       unsigned int new;               /* New console (if changing) */
+       unsigned int pad[4];            /* Padding for expansion */
+};
+
+#define VT_WAITEVENT   0x560E  /* Wait for an event */
+
 #endif /* _LINUX_VT_H */
index 2f1113467f707b5b9e03a3d6243eb2beda5f1abc..f8c797d57c8b507c14ba5d0843e35b13c4ffdcfc 100644 (file)
@@ -91,7 +91,8 @@ int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc);
 #endif
 
 /* vt.c */
-int vt_waitactive(int vt);
+void vt_event_post(unsigned int event, unsigned int old, unsigned int new);
+int vt_waitactive(int n);
 void change_console(struct vc_data *new_vc);
 void reset_vc(struct vc_data *vc);
 extern int unbind_con_driver(const struct consw *csw, int first, int last,