Commit 8b92e87d authored by Alan Cox's avatar Alan Cox Committed by Live-CD User

vt: add an event interface

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: default avatarAlan Cox <alan@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 4455e344
......@@ -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);
......
......@@ -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 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
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;
}
......
......@@ -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 */
......@@ -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,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment