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)
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.
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>
33 #include <linux/init.h>
34 #include <linux/vt_kern.h>
35 #include <linux/console_struct.h>
36 #include <linux/selection.h>
37 #include <linux/kbd_kern.h>
38 #include <linux/console.h>
39 #include <asm/uaccess.h>
40 #include <asm/byteorder.h>
41 #include <asm/unaligned.h>
49 vcs_size(struct inode
*inode
)
52 int currcons
= MINOR(inode
->i_rdev
) & 127;
54 currcons
= fg_console
;
57 if (!vc_cons_allocated(currcons
))
60 size
= video_num_lines
* video_num_columns
;
62 if (MINOR(inode
->i_rdev
) & 128)
63 size
= 2*size
+ HEADER_SIZE
;
67 static loff_t
vcs_lseek(struct file
*file
, loff_t offset
, int orig
)
69 int size
= vcs_size(file
->f_dentry
->d_inode
);
78 offset
+= file
->f_pos
;
82 if (offset
< 0 || offset
> size
)
88 /* We share this temporary buffer with the console write code
89 * so that we can easily avoid touching user space while holding the
92 extern char con_buf
[PAGE_SIZE
];
93 #define CON_BUF_SIZE PAGE_SIZE
94 extern struct semaphore con_buf_sem
;
97 vcs_read(struct file
*file
, char *buf
, size_t count
, loff_t
*ppos
)
99 struct inode
*inode
= file
->f_dentry
->d_inode
;
100 unsigned int currcons
= MINOR(inode
->i_rdev
);
102 long viewed
, attr
, read
;
104 unsigned short *org
= NULL
;
109 /* Select the proper current console and verify
110 * sanity of the situation under the console lock.
112 spin_lock_irq(&console_lock
);
114 attr
= (currcons
& 128);
115 currcons
= (currcons
& 127);
117 currcons
= fg_console
;
124 if (!vc_cons_allocated(currcons
))
133 char *con_buf0
, *con_buf_start
;
134 long this_round
, size
;
138 /* Check whether we are above size each round,
139 * as copy_to_user at the end of this loop
142 size
= vcs_size(inode
);
145 if (count
> size
- pos
)
149 if (this_round
> CON_BUF_SIZE
)
150 this_round
= CON_BUF_SIZE
;
152 /* Perform the whole read into the local con_buf.
153 * Then we can drop the console spinlock and safely
154 * attempt to move it to userspace.
157 con_buf_start
= con_buf0
= con_buf
;
158 orig_count
= this_round
;
159 maxcol
= video_num_columns
;
161 org
= screen_pos(currcons
, p
, viewed
);
164 while (this_round
-- > 0) {
165 *con_buf0
++ = (vcs_scr_readw(currcons
, org
++) & 0xff);
166 if (++col
== maxcol
) {
167 org
= screen_pos(currcons
, p
, viewed
);
173 if (p
< HEADER_SIZE
) {
176 con_buf0
[0] = (char) video_num_lines
;
177 con_buf0
[1] = (char) video_num_columns
;
178 getconsxy(currcons
, con_buf0
+ 2);
182 if (this_round
> CON_BUF_SIZE
) {
183 this_round
= CON_BUF_SIZE
;
184 orig_count
= this_round
- p
;
187 tmp_count
= HEADER_SIZE
;
188 if (tmp_count
> this_round
)
189 tmp_count
= this_round
;
191 /* Advance state pointers and move on. */
192 this_round
-= tmp_count
;
194 con_buf0
= con_buf
+ HEADER_SIZE
;
195 /* If this_round >= 0, then p is even... */
197 /* Skip first byte for output if start address is odd
198 * Update region sizes up/down depending on free
202 if (this_round
< CON_BUF_SIZE
)
207 if (this_round
> 0) {
208 unsigned short *tmp_buf
= (unsigned short *)con_buf0
;
214 org
= screen_pos(currcons
, p
, viewed
);
217 /* Buffer has even length, so we can always copy
218 * character + attribute. We do not copy last byte
219 * to userspace if this_round is odd.
221 this_round
= (this_round
+ 1) >> 1;
224 *tmp_buf
++ = vcs_scr_readw(currcons
, org
++);
226 if (++col
== maxcol
) {
227 org
= screen_pos(currcons
, p
, viewed
);
235 /* Finally, temporarily drop the console lock and push
236 * all the data to userspace from our temporary buffer.
239 spin_unlock_irq(&console_lock
);
240 ret
= copy_to_user(buf
, con_buf_start
, orig_count
);
241 spin_lock_irq(&console_lock
);
244 read
+= (orig_count
- ret
);
257 spin_unlock_irq(&console_lock
);
263 vcs_write(struct file
*file
, const char *buf
, size_t count
, loff_t
*ppos
)
265 struct inode
*inode
= file
->f_dentry
->d_inode
;
266 unsigned int currcons
= MINOR(inode
->i_rdev
);
268 long viewed
, attr
, size
, written
;
271 u16
*org0
= NULL
, *org
= NULL
;
276 /* Select the proper current console and verify
277 * sanity of the situation under the console lock.
279 spin_lock_irq(&console_lock
);
281 attr
= (currcons
& 128);
282 currcons
= (currcons
& 127);
285 currcons
= fg_console
;
292 if (!vc_cons_allocated(currcons
))
295 size
= vcs_size(inode
);
297 if (pos
< 0 || pos
> size
)
299 if (count
> size
- pos
)
303 long this_round
= count
;
307 if (this_round
> CON_BUF_SIZE
)
308 this_round
= CON_BUF_SIZE
;
310 /* Temporarily drop the console lock so that we can read
311 * in the write data from userspace safely.
313 spin_unlock_irq(&console_lock
);
314 ret
= copy_from_user(con_buf
, buf
, this_round
);
315 spin_lock_irq(&console_lock
);
320 /* Abort loop if no data were copied. Otherwise
330 /* The vcs_size might have changed while we slept to grab
331 * the user buffer, so recheck.
332 * Return data written up to now on failure.
334 size
= vcs_size(inode
);
337 if (this_round
> size
- pos
)
338 this_round
= size
- pos
;
340 /* OK, now actually push the write to the console
341 * under the lock using the local kernel buffer.
345 orig_count
= this_round
;
346 maxcol
= video_num_columns
;
349 org0
= org
= screen_pos(currcons
, p
, viewed
);
353 while (this_round
> 0) {
354 unsigned char c
= *con_buf0
++;
357 vcs_scr_writew(currcons
,
358 (vcs_scr_readw(currcons
, org
) & 0xff00) | c
, org
);
360 if (++col
== maxcol
) {
361 org
= screen_pos(currcons
, p
, viewed
);
367 if (p
< HEADER_SIZE
) {
368 char header
[HEADER_SIZE
];
370 getconsxy(currcons
, header
+ 2);
371 while (p
< HEADER_SIZE
&& this_round
> 0) {
373 header
[p
++] = *con_buf0
++;
376 putconsxy(currcons
, header
+ 2);
379 col
= (p
/2) % maxcol
;
380 if (this_round
> 0) {
381 org0
= org
= screen_pos(currcons
, p
/2, viewed
);
382 if ((p
& 1) && this_round
> 0) {
388 vcs_scr_writew(currcons
, c
|
389 (vcs_scr_readw(currcons
, org
) & 0xff00), org
);
391 vcs_scr_writew(currcons
, (c
<< 8) |
392 (vcs_scr_readw(currcons
, org
) & 0xff), org
);
396 if (++col
== maxcol
) {
397 org
= screen_pos(currcons
, p
/2, viewed
);
404 while (this_round
> 1) {
407 w
= get_unaligned(((const unsigned short *)con_buf0
));
408 vcs_scr_writew(currcons
, w
, org
++);
411 if (++col
== maxcol
) {
412 org
= screen_pos(currcons
, p
, viewed
);
417 if (this_round
> 0) {
422 vcs_scr_writew(currcons
, (vcs_scr_readw(currcons
, org
) & 0xff) | (c
<< 8), org
);
424 vcs_scr_writew(currcons
, (vcs_scr_readw(currcons
, org
) & 0xff00) | c
, org
);
429 written
+= orig_count
;
433 update_region(currcons
, (unsigned long)(org0
), org
-org0
);
439 spin_unlock_irq(&console_lock
);
447 vcs_open(struct inode
*inode
, struct file
*filp
)
449 unsigned int currcons
= (MINOR(inode
->i_rdev
) & 127);
450 if(currcons
&& !vc_cons_allocated(currcons
-1))
455 static struct file_operations vcs_fops
= {
462 static devfs_handle_t devfs_handle
= NULL
;
464 void vcs_make_devfs (unsigned int index
, int unregister
)
466 #ifdef CONFIG_DEVFS_FS
469 sprintf (name
, "a%u", index
+ 1);
472 devfs_unregister ( devfs_find_handle (devfs_handle
, name
+ 1, 0, 0,
473 DEVFS_SPECIAL_CHR
, 0) );
474 devfs_unregister ( devfs_find_handle (devfs_handle
, name
, 0, 0,
475 DEVFS_SPECIAL_CHR
, 0) );
479 devfs_register (devfs_handle
, name
+ 1, DEVFS_FL_DEFAULT
,
480 VCS_MAJOR
, index
+ 1,
481 S_IFCHR
| S_IRUSR
| S_IWUSR
, &vcs_fops
, NULL
);
482 devfs_register (devfs_handle
, name
, DEVFS_FL_DEFAULT
,
483 VCS_MAJOR
, index
+ 129,
484 S_IFCHR
| S_IRUSR
| S_IWUSR
, &vcs_fops
, NULL
);
486 #endif /* CONFIG_DEVFS_FS */
489 int __init
vcs_init(void)
493 error
= devfs_register_chrdev(VCS_MAJOR
, "vcs", &vcs_fops
);
496 printk("unable to get major %d for vcs device", VCS_MAJOR
);
498 devfs_handle
= devfs_mk_dir (NULL
, "vcc", NULL
);
499 devfs_register (devfs_handle
, "0", DEVFS_FL_DEFAULT
,
501 S_IFCHR
| S_IRUSR
| S_IWUSR
, &vcs_fops
, NULL
);
502 devfs_register (devfs_handle
, "a", DEVFS_FL_DEFAULT
,
504 S_IFCHR
| S_IRUSR
| S_IWUSR
, &vcs_fops
, NULL
);