Import 2.1.118
[davej-history.git] / drivers / char / vc_screen.c
blobbbdf7e4d5fcc833a9f80927a6bf7db21265fae41
1 /*
2 * linux/drivers/char/vc_screen.c
4 * Provide access to virtual console memory.
5 * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled)
6 * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
7 * [minor: N]
9 * /dev/vcsaN: idem, but including attributes, and prefixed with
10 * the 4 bytes lines,columns,x,y (as screendump used to give).
11 * Attribute/character pair is in native endianity.
12 * [minor: N+128]
14 * This replaces screendump and part of selection, so that the system
15 * administrator can control access using file system permissions.
17 * aeb@cwi.nl - efter Friedas begravelse - 950211
19 * machek@k332.feld.cvut.cz - modified not to send characters to wrong console
20 * - fixed some fatal off-by-one bugs (0-- no longer == -1 -> looping and looping and looping...)
21 * - making it shorter - scr_readw are macros which expand in PRETTY long code
24 #include <linux/kernel.h>
25 #include <linux/major.h>
26 #include <linux/errno.h>
27 #include <linux/tty.h>
28 #include <linux/sched.h>
29 #include <linux/interrupt.h>
30 #include <linux/mm.h>
31 #include <linux/init.h>
32 #include <linux/vt_kern.h>
33 #include <linux/console_struct.h>
34 #include <linux/selection.h>
35 #include <asm/uaccess.h>
36 #include <asm/byteorder.h>
38 #undef attr
39 #undef org
40 #undef addr
41 #define HEADER_SIZE 4
43 static int
44 vcs_size(struct inode *inode)
46 int size;
47 int currcons = MINOR(inode->i_rdev) & 127;
48 if (currcons == 0)
49 currcons = fg_console;
50 else
51 currcons--;
52 if (!vc_cons_allocated(currcons))
53 return -ENXIO;
55 size = video_num_lines * video_num_columns;
57 if (MINOR(inode->i_rdev) & 128)
58 size = 2*size + HEADER_SIZE;
59 return size;
62 static long long vcs_lseek(struct file *file, long long offset, int orig)
64 int size = vcs_size(file->f_dentry->d_inode);
66 switch (orig) {
67 default:
68 return -EINVAL;
69 case 2:
70 offset += size;
71 break;
72 case 1:
73 offset += file->f_pos;
74 case 0:
75 break;
77 if (offset < 0 || offset > size)
78 return -EINVAL;
79 file->f_pos = offset;
80 return file->f_pos;
83 #define RETURN(x) { enable_bh(CONSOLE_BH); return x; }
84 static ssize_t
85 vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos)
87 struct inode *inode = file->f_dentry->d_inode;
88 unsigned int currcons = MINOR(inode->i_rdev);
89 long p = *ppos;
90 long viewed, attr, size, read;
91 char *buf0;
92 unsigned short *org = NULL;
94 attr = (currcons & 128);
95 currcons = (currcons & 127);
96 disable_bh(CONSOLE_BH);
97 if (currcons == 0) {
98 currcons = fg_console;
99 viewed = 1;
100 } else {
101 currcons--;
102 viewed = 0;
104 if (!vc_cons_allocated(currcons))
105 RETURN( -ENXIO );
107 size = vcs_size(inode);
108 if (p < 0 || p > size)
109 RETURN( -EINVAL );
110 if (count > size - p)
111 count = size - p;
113 buf0 = buf;
114 if (!attr) {
115 org = screen_pos(currcons, p, viewed);
116 while (count-- > 0)
117 put_user(vcs_scr_readw(currcons, org++) & 0xff, buf++);
118 } else {
119 if (p < HEADER_SIZE) {
120 char header[HEADER_SIZE];
121 header[0] = (char) video_num_lines;
122 header[1] = (char) video_num_columns;
123 getconsxy(currcons, header+2);
124 while (p < HEADER_SIZE && count > 0)
125 { count--; put_user(header[p++], buf++); }
127 if (count > 0) {
128 p -= HEADER_SIZE;
129 org = screen_pos(currcons, p/2, viewed);
130 if ((p & 1) && count > 0)
131 #ifdef __BIG_ENDIAN
132 { count--; put_user(vcs_scr_readw(currcons, org++) & 0xff, buf++); }
133 #else
134 { count--; put_user(vcs_scr_readw(currcons, org++) >> 8, buf++); }
135 #endif
137 while (count > 1) {
138 put_user(vcs_scr_readw(currcons, org++), (unsigned short *) buf);
139 buf += 2;
140 count -= 2;
142 if (count > 0)
143 #ifdef __BIG_ENDIAN
144 put_user(vcs_scr_readw(currcons, org) >> 8, buf++);
145 #else
146 put_user(vcs_scr_readw(currcons, org) & 0xff, buf++);
147 #endif
149 read = buf - buf0;
150 *ppos += read;
151 RETURN( read );
154 static ssize_t
155 vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
157 struct inode *inode = file->f_dentry->d_inode;
158 unsigned int currcons = MINOR(inode->i_rdev);
159 long p = *ppos;
160 long viewed, attr, size, written;
161 const char *buf0;
162 u16 *org0 = NULL, *org = NULL;
164 attr = (currcons & 128);
165 currcons = (currcons & 127);
166 disable_bh(CONSOLE_BH);
167 if (currcons == 0) {
168 currcons = fg_console;
169 viewed = 1;
170 } else {
171 currcons--;
172 viewed = 0;
174 if (!vc_cons_allocated(currcons))
175 RETURN( -ENXIO );
177 size = vcs_size(inode);
178 if (p < 0 || p > size)
179 RETURN( -EINVAL );
180 if (count > size - p)
181 count = size - p;
183 buf0 = buf;
184 if (!attr) {
185 org0 = org = screen_pos(currcons, p, viewed);
186 while (count > 0) {
187 unsigned char c;
188 count--;
189 get_user(c, (const unsigned char*)buf++);
190 vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org);
191 org++;
193 } else {
194 if (p < HEADER_SIZE) {
195 char header[HEADER_SIZE];
196 getconsxy(currcons, header+2);
197 while (p < HEADER_SIZE && count > 0)
198 { count--; get_user(header[p++], buf++); }
199 if (!viewed)
200 putconsxy(currcons, header+2);
202 if (count > 0) {
203 p -= HEADER_SIZE;
204 org0 = org = screen_pos(currcons, p/2, viewed);
205 if ((p & 1) && count > 0) {
206 char c;
207 count--;
208 get_user(c,buf++);
209 #ifdef __BIG_ENDIAN
210 vcs_scr_writew(currcons, c |
211 (vcs_scr_readw(currcons, org) & 0xff00), org);
212 #else
213 vcs_scr_writew(currcons, (c << 8) |
214 (vcs_scr_readw(currcons, org) & 0xff), org);
215 #endif
216 org++;
219 while (count > 1) {
220 unsigned short w;
221 get_user(w, (const unsigned short *) buf);
222 vcs_scr_writew(currcons, w, org++);
223 buf += 2;
224 count -= 2;
226 if (count > 0) {
227 unsigned char c;
228 get_user(c, (const unsigned char*)buf++);
229 #ifdef __BIG_ENDIAN
230 vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff) | (c << 8), org);
231 #else
232 vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org);
233 #endif
236 if (org0)
237 update_region(currcons, (unsigned long)(org0), org-org0);
238 written = buf - buf0;
239 *ppos += written;
240 RETURN( written );
243 static int
244 vcs_open(struct inode *inode, struct file *filp)
246 unsigned int currcons = (MINOR(inode->i_rdev) & 127);
247 if(currcons && !vc_cons_allocated(currcons-1))
248 return -ENXIO;
249 return 0;
252 static struct file_operations vcs_fops = {
253 vcs_lseek, /* lseek */
254 vcs_read, /* read */
255 vcs_write, /* write */
256 NULL, /* readdir */
257 NULL, /* poll */
258 NULL, /* ioctl */
259 NULL, /* mmap */
260 vcs_open, /* open */
261 NULL, /* flush */
262 NULL, /* release */
263 NULL /* fsync */
266 __initfunc(int vcs_init(void))
268 int error;
270 error = register_chrdev(VCS_MAJOR, "vcs", &vcs_fops);
271 if (error)
272 printk("unable to get major %d for vcs device", VCS_MAJOR);
273 return error;