Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.17-rc1 511 lines 12 kB view raw
1/* 2 * linux/drivers/char/vc_screen.c 3 * 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] 8 * 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] 13 * 14 * This replaces screendump and part of selection, so that the system 15 * administrator can control access using file system permissions. 16 * 17 * aeb@cwi.nl - efter Friedas begravelse - 950211 18 * 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 22 */ 23 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> 43 44#undef attr 45#undef org 46#undef addr 47#define HEADER_SIZE 4 48 49static int 50vcs_size(struct inode *inode) 51{ 52 int size; 53 int minor = iminor(inode); 54 int currcons = minor & 127; 55 struct vc_data *vc; 56 57 if (currcons == 0) 58 currcons = fg_console; 59 else 60 currcons--; 61 if (!vc_cons_allocated(currcons)) 62 return -ENXIO; 63 vc = vc_cons[currcons].d; 64 65 size = vc->vc_rows * vc->vc_cols; 66 67 if (minor & 128) 68 size = 2*size + HEADER_SIZE; 69 return size; 70} 71 72static loff_t vcs_lseek(struct file *file, loff_t offset, int orig) 73{ 74 int size; 75 76 down(&con_buf_sem); 77 size = vcs_size(file->f_dentry->d_inode); 78 switch (orig) { 79 default: 80 up(&con_buf_sem); 81 return -EINVAL; 82 case 2: 83 offset += size; 84 break; 85 case 1: 86 offset += file->f_pos; 87 case 0: 88 break; 89 } 90 if (offset < 0 || offset > size) { 91 up(&con_buf_sem); 92 return -EINVAL; 93 } 94 file->f_pos = offset; 95 up(&con_buf_sem); 96 return file->f_pos; 97} 98 99 100static ssize_t 101vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 102{ 103 struct inode *inode = file->f_dentry->d_inode; 104 unsigned int currcons = iminor(inode); 105 struct vc_data *vc; 106 long pos; 107 long viewed, attr, read; 108 int col, maxcol; 109 unsigned short *org = NULL; 110 ssize_t ret; 111 112 down(&con_buf_sem); 113 114 pos = *ppos; 115 116 /* Select the proper current console and verify 117 * sanity of the situation under the console lock. 118 */ 119 acquire_console_sem(); 120 121 attr = (currcons & 128); 122 currcons = (currcons & 127); 123 if (currcons == 0) { 124 currcons = fg_console; 125 viewed = 1; 126 } else { 127 currcons--; 128 viewed = 0; 129 } 130 ret = -ENXIO; 131 if (!vc_cons_allocated(currcons)) 132 goto unlock_out; 133 vc = vc_cons[currcons].d; 134 135 ret = -EINVAL; 136 if (pos < 0) 137 goto unlock_out; 138 read = 0; 139 ret = 0; 140 while (count) { 141 char *con_buf0, *con_buf_start; 142 long this_round, size; 143 ssize_t orig_count; 144 long p = pos; 145 146 /* Check whether we are above size each round, 147 * as copy_to_user at the end of this loop 148 * could sleep. 149 */ 150 size = vcs_size(inode); 151 if (pos >= size) 152 break; 153 if (count > size - pos) 154 count = size - pos; 155 156 this_round = count; 157 if (this_round > CON_BUF_SIZE) 158 this_round = CON_BUF_SIZE; 159 160 /* Perform the whole read into the local con_buf. 161 * Then we can drop the console spinlock and safely 162 * attempt to move it to userspace. 163 */ 164 165 con_buf_start = con_buf0 = con_buf; 166 orig_count = this_round; 167 maxcol = vc->vc_cols; 168 if (!attr) { 169 org = screen_pos(vc, p, viewed); 170 col = p % maxcol; 171 p += maxcol - col; 172 while (this_round-- > 0) { 173 *con_buf0++ = (vcs_scr_readw(vc, org++) & 0xff); 174 if (++col == maxcol) { 175 org = screen_pos(vc, p, viewed); 176 col = 0; 177 p += maxcol; 178 } 179 } 180 } else { 181 if (p < HEADER_SIZE) { 182 size_t tmp_count; 183 184 con_buf0[0] = (char)vc->vc_rows; 185 con_buf0[1] = (char)vc->vc_cols; 186 getconsxy(vc, con_buf0 + 2); 187 188 con_buf_start += p; 189 this_round += p; 190 if (this_round > CON_BUF_SIZE) { 191 this_round = CON_BUF_SIZE; 192 orig_count = this_round - p; 193 } 194 195 tmp_count = HEADER_SIZE; 196 if (tmp_count > this_round) 197 tmp_count = this_round; 198 199 /* Advance state pointers and move on. */ 200 this_round -= tmp_count; 201 p = HEADER_SIZE; 202 con_buf0 = con_buf + HEADER_SIZE; 203 /* If this_round >= 0, then p is even... */ 204 } else if (p & 1) { 205 /* Skip first byte for output if start address is odd 206 * Update region sizes up/down depending on free 207 * space in buffer. 208 */ 209 con_buf_start++; 210 if (this_round < CON_BUF_SIZE) 211 this_round++; 212 else 213 orig_count--; 214 } 215 if (this_round > 0) { 216 unsigned short *tmp_buf = (unsigned short *)con_buf0; 217 218 p -= HEADER_SIZE; 219 p /= 2; 220 col = p % maxcol; 221 222 org = screen_pos(vc, p, viewed); 223 p += maxcol - col; 224 225 /* Buffer has even length, so we can always copy 226 * character + attribute. We do not copy last byte 227 * to userspace if this_round is odd. 228 */ 229 this_round = (this_round + 1) >> 1; 230 231 while (this_round) { 232 *tmp_buf++ = vcs_scr_readw(vc, org++); 233 this_round --; 234 if (++col == maxcol) { 235 org = screen_pos(vc, p, viewed); 236 col = 0; 237 p += maxcol; 238 } 239 } 240 } 241 } 242 243 /* Finally, release the console semaphore while we push 244 * all the data to userspace from our temporary buffer. 245 * 246 * AKPM: Even though it's a semaphore, we should drop it because 247 * the pagefault handling code may want to call printk(). 248 */ 249 250 release_console_sem(); 251 ret = copy_to_user(buf, con_buf_start, orig_count); 252 acquire_console_sem(); 253 254 if (ret) { 255 read += (orig_count - ret); 256 ret = -EFAULT; 257 break; 258 } 259 buf += orig_count; 260 pos += orig_count; 261 read += orig_count; 262 count -= orig_count; 263 } 264 *ppos += read; 265 if (read) 266 ret = read; 267unlock_out: 268 release_console_sem(); 269 up(&con_buf_sem); 270 return ret; 271} 272 273static ssize_t 274vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 275{ 276 struct inode *inode = file->f_dentry->d_inode; 277 unsigned int currcons = iminor(inode); 278 struct vc_data *vc; 279 long pos; 280 long viewed, attr, size, written; 281 char *con_buf0; 282 int col, maxcol; 283 u16 *org0 = NULL, *org = NULL; 284 size_t ret; 285 286 down(&con_buf_sem); 287 288 pos = *ppos; 289 290 /* Select the proper current console and verify 291 * sanity of the situation under the console lock. 292 */ 293 acquire_console_sem(); 294 295 attr = (currcons & 128); 296 currcons = (currcons & 127); 297 298 if (currcons == 0) { 299 currcons = fg_console; 300 viewed = 1; 301 } else { 302 currcons--; 303 viewed = 0; 304 } 305 ret = -ENXIO; 306 if (!vc_cons_allocated(currcons)) 307 goto unlock_out; 308 vc = vc_cons[currcons].d; 309 310 size = vcs_size(inode); 311 ret = -EINVAL; 312 if (pos < 0 || pos > size) 313 goto unlock_out; 314 if (count > size - pos) 315 count = size - pos; 316 written = 0; 317 while (count) { 318 long this_round = count; 319 size_t orig_count; 320 long p; 321 322 if (this_round > CON_BUF_SIZE) 323 this_round = CON_BUF_SIZE; 324 325 /* Temporarily drop the console lock so that we can read 326 * in the write data from userspace safely. 327 */ 328 release_console_sem(); 329 ret = copy_from_user(con_buf, buf, this_round); 330 acquire_console_sem(); 331 332 if (ret) { 333 this_round -= ret; 334 if (!this_round) { 335 /* Abort loop if no data were copied. Otherwise 336 * fail with -EFAULT. 337 */ 338 if (written) 339 break; 340 ret = -EFAULT; 341 goto unlock_out; 342 } 343 } 344 345 /* The vcs_size might have changed while we slept to grab 346 * the user buffer, so recheck. 347 * Return data written up to now on failure. 348 */ 349 size = vcs_size(inode); 350 if (pos >= size) 351 break; 352 if (this_round > size - pos) 353 this_round = size - pos; 354 355 /* OK, now actually push the write to the console 356 * under the lock using the local kernel buffer. 357 */ 358 359 con_buf0 = con_buf; 360 orig_count = this_round; 361 maxcol = vc->vc_cols; 362 p = pos; 363 if (!attr) { 364 org0 = org = screen_pos(vc, p, viewed); 365 col = p % maxcol; 366 p += maxcol - col; 367 368 while (this_round > 0) { 369 unsigned char c = *con_buf0++; 370 371 this_round--; 372 vcs_scr_writew(vc, 373 (vcs_scr_readw(vc, org) & 0xff00) | c, org); 374 org++; 375 if (++col == maxcol) { 376 org = screen_pos(vc, p, viewed); 377 col = 0; 378 p += maxcol; 379 } 380 } 381 } else { 382 if (p < HEADER_SIZE) { 383 char header[HEADER_SIZE]; 384 385 getconsxy(vc, header + 2); 386 while (p < HEADER_SIZE && this_round > 0) { 387 this_round--; 388 header[p++] = *con_buf0++; 389 } 390 if (!viewed) 391 putconsxy(vc, header + 2); 392 } 393 p -= HEADER_SIZE; 394 col = (p/2) % maxcol; 395 if (this_round > 0) { 396 org0 = org = screen_pos(vc, p/2, viewed); 397 if ((p & 1) && this_round > 0) { 398 char c; 399 400 this_round--; 401 c = *con_buf0++; 402#ifdef __BIG_ENDIAN 403 vcs_scr_writew(vc, c | 404 (vcs_scr_readw(vc, org) & 0xff00), org); 405#else 406 vcs_scr_writew(vc, (c << 8) | 407 (vcs_scr_readw(vc, org) & 0xff), org); 408#endif 409 org++; 410 p++; 411 if (++col == maxcol) { 412 org = screen_pos(vc, p/2, viewed); 413 col = 0; 414 } 415 } 416 p /= 2; 417 p += maxcol - col; 418 } 419 while (this_round > 1) { 420 unsigned short w; 421 422 w = get_unaligned(((unsigned short *)con_buf0)); 423 vcs_scr_writew(vc, w, org++); 424 con_buf0 += 2; 425 this_round -= 2; 426 if (++col == maxcol) { 427 org = screen_pos(vc, p, viewed); 428 col = 0; 429 p += maxcol; 430 } 431 } 432 if (this_round > 0) { 433 unsigned char c; 434 435 c = *con_buf0++; 436#ifdef __BIG_ENDIAN 437 vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff) | (c << 8), org); 438#else 439 vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff00) | c, org); 440#endif 441 } 442 } 443 count -= orig_count; 444 written += orig_count; 445 buf += orig_count; 446 pos += orig_count; 447 if (org0) 448 update_region(vc, (unsigned long)(org0), org - org0); 449 } 450 *ppos += written; 451 ret = written; 452 453unlock_out: 454 release_console_sem(); 455 456 up(&con_buf_sem); 457 458 return ret; 459} 460 461static int 462vcs_open(struct inode *inode, struct file *filp) 463{ 464 unsigned int currcons = iminor(inode) & 127; 465 if(currcons && !vc_cons_allocated(currcons-1)) 466 return -ENXIO; 467 return 0; 468} 469 470static struct file_operations vcs_fops = { 471 .llseek = vcs_lseek, 472 .read = vcs_read, 473 .write = vcs_write, 474 .open = vcs_open, 475}; 476 477static struct class *vc_class; 478 479void vcs_make_devfs(struct tty_struct *tty) 480{ 481 devfs_mk_cdev(MKDEV(VCS_MAJOR, tty->index + 1), 482 S_IFCHR|S_IRUSR|S_IWUSR, 483 "vcc/%u", tty->index + 1); 484 devfs_mk_cdev(MKDEV(VCS_MAJOR, tty->index + 129), 485 S_IFCHR|S_IRUSR|S_IWUSR, 486 "vcc/a%u", tty->index + 1); 487 class_device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 1), 488 NULL, "vcs%u", tty->index + 1); 489 class_device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 129), 490 NULL, "vcsa%u", tty->index + 1); 491} 492void vcs_remove_devfs(struct tty_struct *tty) 493{ 494 devfs_remove("vcc/%u", tty->index + 1); 495 devfs_remove("vcc/a%u", tty->index + 1); 496 class_device_destroy(vc_class, MKDEV(VCS_MAJOR, tty->index + 1)); 497 class_device_destroy(vc_class, MKDEV(VCS_MAJOR, tty->index + 129)); 498} 499 500int __init vcs_init(void) 501{ 502 if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops)) 503 panic("unable to get major %d for vcs device", VCS_MAJOR); 504 vc_class = class_create(THIS_MODULE, "vc"); 505 506 devfs_mk_cdev(MKDEV(VCS_MAJOR, 0), S_IFCHR|S_IRUSR|S_IWUSR, "vcc/0"); 507 devfs_mk_cdev(MKDEV(VCS_MAJOR, 128), S_IFCHR|S_IRUSR|S_IWUSR, "vcc/a0"); 508 class_device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs"); 509 class_device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa"); 510 return 0; 511}