aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/vc_screen.c
diff options
context:
space:
mode:
authorNicolas Pitre <nico@fluxnic.net>2010-10-05 14:22:37 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-10-22 13:20:05 -0400
commit47725ac76f51328d467b1430dfd027aba8706a11 (patch)
tree88a335ba4a1525fab44d774b7897690b848f0fbd /drivers/char/vc_screen.c
parentf4a3e0bceb57466c31757f25e4e0ed108d1299ec (diff)
vcs: add poll/fasync support
The /dev/vcs* devices are used, amongst other things, by accessibility applications such as BRLTTY to display the screen content onto refreshable braille displays. Currently this is performed by constantly reading from /dev/vcsa0 whether or not the screen content has changed. Given the default braille refresh rate of 25 times per second, this easily qualifies as the biggest source of wake-up events preventing laptops from entering deeper power saving states. To avoid this periodic polling, let's add support for select()/poll() and SIGIO with the /dev/vcs* devices. The implemented semantic is to report data availability whenever the corresponding vt has seen some update after the last read() operation. The application still has to lseek() back as usual in order to read() the new data. Not to create unwanted overhead, the needed data structure is allocated and the vt notification callback is registered only when the poll or fasync method is invoked for the first time per file instance. Signed-off-by: Nicolas Pitre <nicolas.pitre@canonical.com> Cc: Andrew Morton <akpm@linux-foundation.org> Acked-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/char/vc_screen.c')
-rw-r--r--drivers/char/vc_screen.c133
1 files changed, 133 insertions, 0 deletions
diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c
index bcce46c96b88..6f7054e1a516 100644
--- a/drivers/char/vc_screen.c
+++ b/drivers/char/vc_screen.c
@@ -35,6 +35,12 @@
35#include <linux/console.h> 35#include <linux/console.h>
36#include <linux/device.h> 36#include <linux/device.h>
37#include <linux/smp_lock.h> 37#include <linux/smp_lock.h>
38#include <linux/sched.h>
39#include <linux/fs.h>
40#include <linux/poll.h>
41#include <linux/signal.h>
42#include <linux/slab.h>
43#include <linux/notifier.h>
38 44
39#include <asm/uaccess.h> 45#include <asm/uaccess.h>
40#include <asm/byteorder.h> 46#include <asm/byteorder.h>
@@ -45,6 +51,86 @@
45#undef addr 51#undef addr
46#define HEADER_SIZE 4 52#define HEADER_SIZE 4
47 53
54struct vcs_poll_data {
55 struct notifier_block notifier;
56 unsigned int cons_num;
57 bool seen_last_update;
58 wait_queue_head_t waitq;
59 struct fasync_struct *fasync;
60};
61
62static int
63vcs_notifier(struct notifier_block *nb, unsigned long code, void *_param)
64{
65 struct vt_notifier_param *param = _param;
66 struct vc_data *vc = param->vc;
67 struct vcs_poll_data *poll =
68 container_of(nb, struct vcs_poll_data, notifier);
69 int currcons = poll->cons_num;
70
71 if (code != VT_UPDATE)
72 return NOTIFY_DONE;
73
74 if (currcons == 0)
75 currcons = fg_console;
76 else
77 currcons--;
78 if (currcons != vc->vc_num)
79 return NOTIFY_DONE;
80
81 poll->seen_last_update = false;
82 wake_up_interruptible(&poll->waitq);
83 kill_fasync(&poll->fasync, SIGIO, POLL_IN);
84 return NOTIFY_OK;
85}
86
87static void
88vcs_poll_data_free(struct vcs_poll_data *poll)
89{
90 unregister_vt_notifier(&poll->notifier);
91 kfree(poll);
92}
93
94static struct vcs_poll_data *
95vcs_poll_data_get(struct file *file)
96{
97 struct vcs_poll_data *poll = file->private_data;
98
99 if (poll)
100 return poll;
101
102 poll = kzalloc(sizeof(*poll), GFP_KERNEL);
103 if (!poll)
104 return NULL;
105 poll->cons_num = iminor(file->f_path.dentry->d_inode) & 127;
106 init_waitqueue_head(&poll->waitq);
107 poll->notifier.notifier_call = vcs_notifier;
108 if (register_vt_notifier(&poll->notifier) != 0) {
109 kfree(poll);
110 return NULL;
111 }
112
113 /*
114 * This code may be called either through ->poll() or ->fasync().
115 * If we have two threads using the same file descriptor, they could
116 * both enter this function, both notice that the structure hasn't
117 * been allocated yet and go ahead allocating it in parallel, but
118 * only one of them must survive and be shared otherwise we'd leak
119 * memory with a dangling notifier callback.
120 */
121 spin_lock(&file->f_lock);
122 if (!file->private_data) {
123 file->private_data = poll;
124 } else {
125 /* someone else raced ahead of us */
126 vcs_poll_data_free(poll);
127 poll = file->private_data;
128 }
129 spin_unlock(&file->f_lock);
130
131 return poll;
132}
133
48static int 134static int
49vcs_size(struct inode *inode) 135vcs_size(struct inode *inode)
50{ 136{
@@ -102,6 +188,7 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
102 struct inode *inode = file->f_path.dentry->d_inode; 188 struct inode *inode = file->f_path.dentry->d_inode;
103 unsigned int currcons = iminor(inode); 189 unsigned int currcons = iminor(inode);
104 struct vc_data *vc; 190 struct vc_data *vc;
191 struct vcs_poll_data *poll;
105 long pos; 192 long pos;
106 long viewed, attr, read; 193 long viewed, attr, read;
107 int col, maxcol; 194 int col, maxcol;
@@ -134,6 +221,9 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
134 ret = -EINVAL; 221 ret = -EINVAL;
135 if (pos < 0) 222 if (pos < 0)
136 goto unlock_out; 223 goto unlock_out;
224 poll = file->private_data;
225 if (count && poll)
226 poll->seen_last_update = true;
137 read = 0; 227 read = 0;
138 ret = 0; 228 ret = 0;
139 while (count) { 229 while (count) {
@@ -457,6 +547,37 @@ unlock_out:
457 return ret; 547 return ret;
458} 548}
459 549
550static unsigned int
551vcs_poll(struct file *file, poll_table *wait)
552{
553 struct vcs_poll_data *poll = vcs_poll_data_get(file);
554 int ret = 0;
555
556 if (poll) {
557 poll_wait(file, &poll->waitq, wait);
558 if (!poll->seen_last_update)
559 ret = POLLIN | POLLRDNORM;
560 }
561 return ret;
562}
563
564static int
565vcs_fasync(int fd, struct file *file, int on)
566{
567 struct vcs_poll_data *poll = file->private_data;
568
569 if (!poll) {
570 /* don't allocate anything if all we want is disable fasync */
571 if (!on)
572 return 0;
573 poll = vcs_poll_data_get(file);
574 if (!poll)
575 return -ENOMEM;
576 }
577
578 return fasync_helper(fd, file, on, &poll->fasync);
579}
580
460static int 581static int
461vcs_open(struct inode *inode, struct file *filp) 582vcs_open(struct inode *inode, struct file *filp)
462{ 583{
@@ -470,11 +591,23 @@ vcs_open(struct inode *inode, struct file *filp)
470 return ret; 591 return ret;
471} 592}
472 593
594static int vcs_release(struct inode *inode, struct file *file)
595{
596 struct vcs_poll_data *poll = file->private_data;
597
598 if (poll)
599 vcs_poll_data_free(poll);
600 return 0;
601}
602
473static const struct file_operations vcs_fops = { 603static const struct file_operations vcs_fops = {
474 .llseek = vcs_lseek, 604 .llseek = vcs_lseek,
475 .read = vcs_read, 605 .read = vcs_read,
476 .write = vcs_write, 606 .write = vcs_write,
607 .poll = vcs_poll,
608 .fasync = vcs_fasync,
477 .open = vcs_open, 609 .open = vcs_open,
610 .release = vcs_release,
478}; 611};
479 612
480static struct class *vc_class; 613static struct class *vc_class;