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