MOXA linux-2.6.x / linux-2.6.9-uc0 from sdlinux-moxaart.tgz
[linux-2.6.9-moxart.git] / drivers / char / vc_screen.c
blobc5c6b5e26850e55072b6f304ce56cb57fbaff23c
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/config.h>
25 #include <linux/kernel.h>
26 #include <linux/major.h>
27 #include <linux/errno.h>
28 #include <linux/tty.h>
29 #include <linux/devfs_fs_kernel.h>
30 #include <linux/sched.h>
31 #include <linux/interrupt.h>
32 #include <linux/mm.h>
33 #include <linux/init.h>
34 #include <linux/vt_kern.h>
35 #include <linux/selection.h>
36 #include <linux/kbd_kern.h>
37 #include <linux/console.h>
38 #include <linux/smp_lock.h>
39 #include <linux/device.h>
40 #include <asm/uaccess.h>
41 #include <asm/byteorder.h>
42 #include <asm/unaligned.h>
44 #undef attr
45 #undef org
46 #undef addr
47 #define HEADER_SIZE 4
49 static int
50 vcs_size(struct inode *inode)
52 int size;
53 int minor = iminor(inode);
54 int currcons = minor & 127;
55 if (currcons == 0)
56 currcons = fg_console;
57 else
58 currcons--;
59 if (!vc_cons_allocated(currcons))
60 return -ENXIO;
62 size = video_num_lines * video_num_columns;
64 if (minor & 128)
65 size = 2*size + HEADER_SIZE;
66 return size;
69 static loff_t vcs_lseek(struct file *file, loff_t offset, int orig)
71 int size;
73 down(&con_buf_sem);
74 size = vcs_size(file->f_dentry->d_inode);
75 switch (orig) {
76 default:
77 up(&con_buf_sem);
78 return -EINVAL;
79 case 2:
80 offset += size;
81 break;
82 case 1:
83 offset += file->f_pos;
84 case 0:
85 break;
87 if (offset < 0 || offset > size) {
88 up(&con_buf_sem);
89 return -EINVAL;
91 file->f_pos = offset;
92 up(&con_buf_sem);
93 return file->f_pos;
97 static ssize_t
98 vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
100 struct inode *inode = file->f_dentry->d_inode;
101 unsigned int currcons = iminor(inode);
102 long pos;
103 long viewed, attr, read;
104 int col, maxcol;
105 unsigned short *org = NULL;
106 ssize_t ret;
108 down(&con_buf_sem);
110 pos = *ppos;
112 /* Select the proper current console and verify
113 * sanity of the situation under the console lock.
115 acquire_console_sem();
117 attr = (currcons & 128);
118 currcons = (currcons & 127);
119 if (currcons == 0) {
120 currcons = fg_console;
121 viewed = 1;
122 } else {
123 currcons--;
124 viewed = 0;
126 ret = -ENXIO;
127 if (!vc_cons_allocated(currcons))
128 goto unlock_out;
130 ret = -EINVAL;
131 if (pos < 0)
132 goto unlock_out;
133 read = 0;
134 ret = 0;
135 while (count) {
136 char *con_buf0, *con_buf_start;
137 long this_round, size;
138 ssize_t orig_count;
139 long p = pos;
141 /* Check whether we are above size each round,
142 * as copy_to_user at the end of this loop
143 * could sleep.
145 size = vcs_size(inode);
146 if (pos >= size)
147 break;
148 if (count > size - pos)
149 count = size - pos;
151 this_round = count;
152 if (this_round > CON_BUF_SIZE)
153 this_round = CON_BUF_SIZE;
155 /* Perform the whole read into the local con_buf.
156 * Then we can drop the console spinlock and safely
157 * attempt to move it to userspace.
160 con_buf_start = con_buf0 = con_buf;
161 orig_count = this_round;
162 maxcol = video_num_columns;
163 if (!attr) {
164 org = screen_pos(currcons, p, viewed);
165 col = p % maxcol;
166 p += maxcol - col;
167 while (this_round-- > 0) {
168 *con_buf0++ = (vcs_scr_readw(currcons, org++) & 0xff);
169 if (++col == maxcol) {
170 org = screen_pos(currcons, p, viewed);
171 col = 0;
172 p += maxcol;
175 } else {
176 if (p < HEADER_SIZE) {
177 size_t tmp_count;
179 con_buf0[0] = (char) video_num_lines;
180 con_buf0[1] = (char) video_num_columns;
181 getconsxy(currcons, con_buf0 + 2);
183 con_buf_start += p;
184 this_round += p;
185 if (this_round > CON_BUF_SIZE) {
186 this_round = CON_BUF_SIZE;
187 orig_count = this_round - p;
190 tmp_count = HEADER_SIZE;
191 if (tmp_count > this_round)
192 tmp_count = this_round;
194 /* Advance state pointers and move on. */
195 this_round -= tmp_count;
196 p = HEADER_SIZE;
197 con_buf0 = con_buf + HEADER_SIZE;
198 /* If this_round >= 0, then p is even... */
199 } else if (p & 1) {
200 /* Skip first byte for output if start address is odd
201 * Update region sizes up/down depending on free
202 * space in buffer.
204 con_buf_start++;
205 if (this_round < CON_BUF_SIZE)
206 this_round++;
207 else
208 orig_count--;
210 if (this_round > 0) {
211 unsigned short *tmp_buf = (unsigned short *)con_buf0;
213 p -= HEADER_SIZE;
214 p /= 2;
215 col = p % maxcol;
217 org = screen_pos(currcons, p, viewed);
218 p += maxcol - col;
220 /* Buffer has even length, so we can always copy
221 * character + attribute. We do not copy last byte
222 * to userspace if this_round is odd.
224 this_round = (this_round + 1) >> 1;
226 while (this_round) {
227 *tmp_buf++ = vcs_scr_readw(currcons, org++);
228 this_round --;
229 if (++col == maxcol) {
230 org = screen_pos(currcons, p, viewed);
231 col = 0;
232 p += maxcol;
238 /* Finally, release the console semaphore while we push
239 * all the data to userspace from our temporary buffer.
241 * AKPM: Even though it's a semaphore, we should drop it because
242 * the pagefault handling code may want to call printk().
245 release_console_sem();
246 ret = copy_to_user(buf, con_buf_start, orig_count);
247 acquire_console_sem();
249 if (ret) {
250 read += (orig_count - ret);
251 ret = -EFAULT;
252 break;
254 buf += orig_count;
255 pos += orig_count;
256 read += orig_count;
257 count -= orig_count;
259 *ppos += read;
260 if (read)
261 ret = read;
262 unlock_out:
263 release_console_sem();
264 up(&con_buf_sem);
265 return ret;
268 static ssize_t
269 vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
271 struct inode *inode = file->f_dentry->d_inode;
272 unsigned int currcons = iminor(inode);
273 long pos;
274 long viewed, attr, size, written;
275 char *con_buf0;
276 int col, maxcol;
277 u16 *org0 = NULL, *org = NULL;
278 size_t ret;
280 down(&con_buf_sem);
282 pos = *ppos;
284 /* Select the proper current console and verify
285 * sanity of the situation under the console lock.
287 acquire_console_sem();
289 attr = (currcons & 128);
290 currcons = (currcons & 127);
292 if (currcons == 0) {
293 currcons = fg_console;
294 viewed = 1;
295 } else {
296 currcons--;
297 viewed = 0;
299 ret = -ENXIO;
300 if (!vc_cons_allocated(currcons))
301 goto unlock_out;
303 size = vcs_size(inode);
304 ret = -EINVAL;
305 if (pos < 0 || pos > size)
306 goto unlock_out;
307 if (count > size - pos)
308 count = size - pos;
309 written = 0;
310 while (count) {
311 long this_round = count;
312 size_t orig_count;
313 long p;
315 if (this_round > CON_BUF_SIZE)
316 this_round = CON_BUF_SIZE;
318 /* Temporarily drop the console lock so that we can read
319 * in the write data from userspace safely.
321 release_console_sem();
322 ret = copy_from_user(con_buf, buf, this_round);
323 acquire_console_sem();
325 if (ret) {
326 this_round -= ret;
327 if (!this_round) {
328 /* Abort loop if no data were copied. Otherwise
329 * fail with -EFAULT.
331 if (written)
332 break;
333 ret = -EFAULT;
334 goto unlock_out;
338 /* The vcs_size might have changed while we slept to grab
339 * the user buffer, so recheck.
340 * Return data written up to now on failure.
342 size = vcs_size(inode);
343 if (pos >= size)
344 break;
345 if (this_round > size - pos)
346 this_round = size - pos;
348 /* OK, now actually push the write to the console
349 * under the lock using the local kernel buffer.
352 con_buf0 = con_buf;
353 orig_count = this_round;
354 maxcol = video_num_columns;
355 p = pos;
356 if (!attr) {
357 org0 = org = screen_pos(currcons, p, viewed);
358 col = p % maxcol;
359 p += maxcol - col;
361 while (this_round > 0) {
362 unsigned char c = *con_buf0++;
364 this_round--;
365 vcs_scr_writew(currcons,
366 (vcs_scr_readw(currcons, org) & 0xff00) | c, org);
367 org++;
368 if (++col == maxcol) {
369 org = screen_pos(currcons, p, viewed);
370 col = 0;
371 p += maxcol;
374 } else {
375 if (p < HEADER_SIZE) {
376 char header[HEADER_SIZE];
378 getconsxy(currcons, header + 2);
379 while (p < HEADER_SIZE && this_round > 0) {
380 this_round--;
381 header[p++] = *con_buf0++;
383 if (!viewed)
384 putconsxy(currcons, header + 2);
386 p -= HEADER_SIZE;
387 col = (p/2) % maxcol;
388 if (this_round > 0) {
389 org0 = org = screen_pos(currcons, p/2, viewed);
390 if ((p & 1) && this_round > 0) {
391 char c;
393 this_round--;
394 c = *con_buf0++;
395 #ifdef __BIG_ENDIAN
396 vcs_scr_writew(currcons, c |
397 (vcs_scr_readw(currcons, org) & 0xff00), org);
398 #else
399 vcs_scr_writew(currcons, (c << 8) |
400 (vcs_scr_readw(currcons, org) & 0xff), org);
401 #endif
402 org++;
403 p++;
404 if (++col == maxcol) {
405 org = screen_pos(currcons, p/2, viewed);
406 col = 0;
409 p /= 2;
410 p += maxcol - col;
412 while (this_round > 1) {
413 unsigned short w;
415 w = get_unaligned(((const unsigned short *)con_buf0));
416 vcs_scr_writew(currcons, w, org++);
417 con_buf0 += 2;
418 this_round -= 2;
419 if (++col == maxcol) {
420 org = screen_pos(currcons, p, viewed);
421 col = 0;
422 p += maxcol;
425 if (this_round > 0) {
426 unsigned char c;
428 c = *con_buf0++;
429 #ifdef __BIG_ENDIAN
430 vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff) | (c << 8), org);
431 #else
432 vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org);
433 #endif
436 count -= orig_count;
437 written += orig_count;
438 buf += orig_count;
439 pos += orig_count;
440 if (org0)
441 update_region(currcons, (unsigned long)(org0), org-org0);
443 *ppos += written;
444 ret = written;
446 unlock_out:
447 release_console_sem();
449 up(&con_buf_sem);
451 return ret;
454 static int
455 vcs_open(struct inode *inode, struct file *filp)
457 unsigned int currcons = iminor(inode) & 127;
458 if(currcons && !vc_cons_allocated(currcons-1))
459 return -ENXIO;
460 return 0;
463 static struct file_operations vcs_fops = {
464 .llseek = vcs_lseek,
465 .read = vcs_read,
466 .write = vcs_write,
467 .open = vcs_open,
470 static struct class_simple *vc_class;
472 void vcs_make_devfs(struct tty_struct *tty)
474 devfs_mk_cdev(MKDEV(VCS_MAJOR, tty->index + 1),
475 S_IFCHR|S_IRUSR|S_IWUSR,
476 "vcc/%u", tty->index + 1);
477 devfs_mk_cdev(MKDEV(VCS_MAJOR, tty->index + 129),
478 S_IFCHR|S_IRUSR|S_IWUSR,
479 "vcc/a%u", tty->index + 1);
480 class_simple_device_add(vc_class, MKDEV(VCS_MAJOR, tty->index + 1), NULL, "vcs%u", tty->index + 1);
481 class_simple_device_add(vc_class, MKDEV(VCS_MAJOR, tty->index + 129), NULL, "vcsa%u", tty->index + 1);
483 void vcs_remove_devfs(struct tty_struct *tty)
485 devfs_remove("vcc/%u", tty->index + 1);
486 devfs_remove("vcc/a%u", tty->index + 1);
487 class_simple_device_remove(MKDEV(VCS_MAJOR, tty->index + 1));
488 class_simple_device_remove(MKDEV(VCS_MAJOR, tty->index + 129));
491 int __init vcs_init(void)
493 if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops))
494 panic("unable to get major %d for vcs device", VCS_MAJOR);
495 vc_class = class_simple_create(THIS_MODULE, "vc");
497 devfs_mk_cdev(MKDEV(VCS_MAJOR, 0), S_IFCHR|S_IRUSR|S_IWUSR, "vcc/0");
498 devfs_mk_cdev(MKDEV(VCS_MAJOR, 128), S_IFCHR|S_IRUSR|S_IWUSR, "vcc/a0");
499 class_simple_device_add(vc_class, MKDEV(VCS_MAJOR, 0), NULL, "vcs");
500 class_simple_device_add(vc_class, MKDEV(VCS_MAJOR, 128), NULL, "vcsa");
501 return 0;