summaryrefslogtreecommitdiffstats
path: root/drivers/char/vc_screen.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/vc_screen.c')
-rw-r--r--drivers/char/vc_screen.c212
1 files changed, 212 insertions, 0 deletions
diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c
new file mode 100644
index 000000000..f5f389cf8
--- /dev/null
+++ b/drivers/char/vc_screen.c
@@ -0,0 +1,212 @@
+/*
+ * linux/drivers/char/vc_screen.c
+ *
+ * Provide access to virtual console memory.
+ * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled)
+ * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
+ * [minor: N]
+ *
+ * /dev/vcsaN: idem, but including attributes, and prefixed with
+ * the 4 bytes lines,columns,x,y (as screendump used to give)
+ * [minor: N+128]
+ *
+ * This replaces screendump and part of selection, so that the system
+ * administrator can control access using file system permissions.
+ *
+ * aeb@cwi.nl - efter Friedas begravelse - 950211
+ */
+
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/fs.h>
+#include <asm/segment.h>
+#include "vt_kern.h"
+#include "selection.h"
+
+#define HEADER_SIZE 4
+
+static inline int
+vcs_size(struct inode *inode)
+{
+ int size = video_num_lines * video_num_columns;
+ if (MINOR(inode->i_rdev) & 128)
+ size = 2*size + HEADER_SIZE;
+ return size;
+}
+
+static int
+vcs_lseek(struct inode *inode, struct file *file, off_t offset, int orig)
+{
+ int size = vcs_size(inode);
+
+ switch (orig) {
+ case 0:
+ file->f_pos = offset;
+ break;
+ case 1:
+ file->f_pos += offset;
+ break;
+ case 2:
+ file->f_pos = size + offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (file->f_pos < 0 || file->f_pos > size)
+ return -EINVAL;
+ return file->f_pos;
+}
+
+static int
+vcs_read(struct inode *inode, struct file *file, char *buf, int count)
+{
+ unsigned long p = file->f_pos;
+ unsigned int cons = MINOR(inode->i_rdev);
+ int viewed, attr, size, read;
+ char *buf0;
+ unsigned short *org;
+
+ attr = (cons & 128);
+ cons = (cons & 127);
+ if (cons == 0) {
+ cons = fg_console;
+ viewed = 1;
+ } else {
+ cons--;
+ viewed = 0;
+ }
+ if (!vc_cons_allocated(cons))
+ return -ENXIO;
+
+ size = vcs_size(inode);
+ if (count < 0 || p > size)
+ return -EINVAL;
+ if (count > size - p)
+ count = size - p;
+
+ buf0 = buf;
+ if (!attr) {
+ org = screen_pos(cons, p, viewed);
+ while (count-- > 0)
+ put_fs_byte(scr_readw(org++) & 0xff, buf++);
+ } else {
+ if (p < HEADER_SIZE) {
+ char header[HEADER_SIZE];
+ header[0] = (char) video_num_lines;
+ header[1] = (char) video_num_columns;
+ getconsxy(cons, header+2);
+ while (p < HEADER_SIZE && count-- > 0)
+ put_fs_byte(header[p++], buf++);
+ }
+ p -= HEADER_SIZE;
+ org = screen_pos(cons, p/2, viewed);
+ if ((p & 1) && count-- > 0)
+ put_fs_byte(scr_readw(org++) >> 8, buf++);
+ while (count > 1) {
+ put_fs_word(scr_readw(org++), buf);
+ buf += 2;
+ count -= 2;
+ }
+ if (count > 0)
+ put_fs_byte(scr_readw(org) & 0xff, buf++);
+ }
+ read = buf - buf0;
+ file->f_pos += read;
+ return read;
+}
+
+static int
+vcs_write(struct inode *inode, struct file *file, char *buf, int count)
+{
+ unsigned long p = file->f_pos;
+ unsigned int cons = MINOR(inode->i_rdev);
+ int viewed, attr, size, written;
+ char *buf0;
+ unsigned short *org;
+
+ attr = (cons & 128);
+ cons = (cons & 127);
+ if (cons == 0) {
+ cons = fg_console;
+ viewed = 1;
+ } else {
+ cons--;
+ viewed = 0;
+ }
+ if (!vc_cons_allocated(cons))
+ return -ENXIO;
+
+ size = vcs_size(inode);
+ if (count < 0 || p > size)
+ return -EINVAL;
+ if (count > size - p)
+ count = size - p;
+
+ buf0 = buf;
+ if (!attr) {
+ org = screen_pos(cons, p, viewed);
+ while (count-- > 0) {
+ scr_writew((scr_readw(org) & 0xff00) |
+ get_fs_byte(buf++), org);
+ org++;
+ }
+ } else {
+ if (p < HEADER_SIZE) {
+ char header[HEADER_SIZE];
+ getconsxy(cons, header+2);
+ while (p < HEADER_SIZE && count-- > 0)
+ header[p++] = get_fs_byte(buf++);
+ if (!viewed)
+ putconsxy(cons, header+2);
+ }
+ p -= HEADER_SIZE;
+ org = screen_pos(cons, p/2, viewed);
+ if ((p & 1) && count-- > 0) {
+ scr_writew((get_fs_byte(buf++) << 8) |
+ (scr_readw(org) & 0xff), org);
+ org++;
+ }
+ while (count > 1) {
+ scr_writew(get_fs_word(buf), org++);
+ buf += 2;
+ count -= 2;
+ }
+ if (count > 0)
+ scr_writew((scr_readw(org) & 0xff00) |
+ get_fs_byte(buf++), org);
+ }
+ written = buf - buf0;
+ file->f_pos += written;
+ return written;
+}
+
+static int
+vcs_open(struct inode *inode, struct file *filp)
+{
+ unsigned int cons = (MINOR(inode->i_rdev) & 127);
+ if(cons && !vc_cons_allocated(cons-1))
+ return -ENXIO;
+ return 0;
+}
+
+static struct file_operations vcs_fops = {
+ vcs_lseek, /* lseek */
+ vcs_read, /* read */
+ vcs_write, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ vcs_open, /* open */
+ NULL, /* release */
+ NULL /* fsync */
+};
+
+long vcs_init(long kmem_start)
+{
+ if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops))
+ printk("unable to get major %d for vcs device", VCS_MAJOR);
+ return kmem_start;
+}