0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029 #include <linux/kernel.h>
0030 #include <linux/major.h>
0031 #include <linux/errno.h>
0032 #include <linux/export.h>
0033 #include <linux/tty.h>
0034 #include <linux/interrupt.h>
0035 #include <linux/mm.h>
0036 #include <linux/init.h>
0037 #include <linux/vt_kern.h>
0038 #include <linux/selection.h>
0039 #include <linux/kbd_kern.h>
0040 #include <linux/console.h>
0041 #include <linux/device.h>
0042 #include <linux/sched.h>
0043 #include <linux/fs.h>
0044 #include <linux/poll.h>
0045 #include <linux/signal.h>
0046 #include <linux/slab.h>
0047 #include <linux/notifier.h>
0048
0049 #include <linux/uaccess.h>
0050 #include <asm/byteorder.h>
0051 #include <asm/unaligned.h>
0052
0053 #define HEADER_SIZE 4u
0054 #define CON_BUF_SIZE (CONFIG_BASE_SMALL ? 256 : PAGE_SIZE)
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067 #if MAX_NR_CONSOLES > 63
0068 #warning "/dev/vcs* devices may not accommodate more than 63 consoles"
0069 #endif
0070
0071 #define console(inode) (iminor(inode) & 63)
0072 #define use_unicode(inode) (iminor(inode) & 64)
0073 #define use_attributes(inode) (iminor(inode) & 128)
0074
0075
0076 struct vcs_poll_data {
0077 struct notifier_block notifier;
0078 unsigned int cons_num;
0079 int event;
0080 wait_queue_head_t waitq;
0081 struct fasync_struct *fasync;
0082 };
0083
0084 static int
0085 vcs_notifier(struct notifier_block *nb, unsigned long code, void *_param)
0086 {
0087 struct vt_notifier_param *param = _param;
0088 struct vc_data *vc = param->vc;
0089 struct vcs_poll_data *poll =
0090 container_of(nb, struct vcs_poll_data, notifier);
0091 int currcons = poll->cons_num;
0092 int fa_band;
0093
0094 switch (code) {
0095 case VT_UPDATE:
0096 fa_band = POLL_PRI;
0097 break;
0098 case VT_DEALLOCATE:
0099 fa_band = POLL_HUP;
0100 break;
0101 default:
0102 return NOTIFY_DONE;
0103 }
0104
0105 if (currcons == 0)
0106 currcons = fg_console;
0107 else
0108 currcons--;
0109 if (currcons != vc->vc_num)
0110 return NOTIFY_DONE;
0111
0112 poll->event = code;
0113 wake_up_interruptible(&poll->waitq);
0114 kill_fasync(&poll->fasync, SIGIO, fa_band);
0115 return NOTIFY_OK;
0116 }
0117
0118 static void
0119 vcs_poll_data_free(struct vcs_poll_data *poll)
0120 {
0121 unregister_vt_notifier(&poll->notifier);
0122 kfree(poll);
0123 }
0124
0125 static struct vcs_poll_data *
0126 vcs_poll_data_get(struct file *file)
0127 {
0128 struct vcs_poll_data *poll = file->private_data, *kill = NULL;
0129
0130 if (poll)
0131 return poll;
0132
0133 poll = kzalloc(sizeof(*poll), GFP_KERNEL);
0134 if (!poll)
0135 return NULL;
0136 poll->cons_num = console(file_inode(file));
0137 init_waitqueue_head(&poll->waitq);
0138 poll->notifier.notifier_call = vcs_notifier;
0139
0140
0141
0142
0143
0144
0145
0146 poll->event = VT_UPDATE;
0147
0148 if (register_vt_notifier(&poll->notifier) != 0) {
0149 kfree(poll);
0150 return NULL;
0151 }
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161 spin_lock(&file->f_lock);
0162 if (!file->private_data) {
0163 file->private_data = poll;
0164 } else {
0165
0166 kill = poll;
0167 poll = file->private_data;
0168 }
0169 spin_unlock(&file->f_lock);
0170 if (kill)
0171 vcs_poll_data_free(kill);
0172
0173 return poll;
0174 }
0175
0176
0177
0178
0179
0180
0181
0182
0183 static struct vc_data *vcs_vc(struct inode *inode, bool *viewed)
0184 {
0185 unsigned int currcons = console(inode);
0186
0187 WARN_CONSOLE_UNLOCKED();
0188
0189 if (currcons == 0) {
0190 currcons = fg_console;
0191 if (viewed)
0192 *viewed = true;
0193 } else {
0194 currcons--;
0195 if (viewed)
0196 *viewed = false;
0197 }
0198 return vc_cons[currcons].d;
0199 }
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209 static int vcs_size(const struct vc_data *vc, bool attr, bool unicode)
0210 {
0211 int size;
0212
0213 WARN_CONSOLE_UNLOCKED();
0214
0215 size = vc->vc_rows * vc->vc_cols;
0216
0217 if (attr) {
0218 if (unicode)
0219 return -EOPNOTSUPP;
0220
0221 size = 2 * size + HEADER_SIZE;
0222 } else if (unicode)
0223 size *= 4;
0224
0225 return size;
0226 }
0227
0228 static loff_t vcs_lseek(struct file *file, loff_t offset, int orig)
0229 {
0230 struct inode *inode = file_inode(file);
0231 struct vc_data *vc;
0232 int size;
0233
0234 console_lock();
0235 vc = vcs_vc(inode, NULL);
0236 if (!vc) {
0237 console_unlock();
0238 return -ENXIO;
0239 }
0240
0241 size = vcs_size(vc, use_attributes(inode), use_unicode(inode));
0242 console_unlock();
0243 if (size < 0)
0244 return size;
0245 return fixed_size_llseek(file, offset, orig, size);
0246 }
0247
0248 static int vcs_read_buf_uni(struct vc_data *vc, char *con_buf,
0249 unsigned int pos, unsigned int count, bool viewed)
0250 {
0251 unsigned int nr, row, col, maxcol = vc->vc_cols;
0252 int ret;
0253
0254 ret = vc_uniscr_check(vc);
0255 if (ret)
0256 return ret;
0257
0258 pos /= 4;
0259 row = pos / maxcol;
0260 col = pos % maxcol;
0261 nr = maxcol - col;
0262 do {
0263 if (nr > count / 4)
0264 nr = count / 4;
0265 vc_uniscr_copy_line(vc, con_buf, viewed, row, col, nr);
0266 con_buf += nr * 4;
0267 count -= nr * 4;
0268 row++;
0269 col = 0;
0270 nr = maxcol;
0271 } while (count);
0272
0273 return 0;
0274 }
0275
0276 static void vcs_read_buf_noattr(const struct vc_data *vc, char *con_buf,
0277 unsigned int pos, unsigned int count, bool viewed)
0278 {
0279 u16 *org;
0280 unsigned int col, maxcol = vc->vc_cols;
0281
0282 org = screen_pos(vc, pos, viewed);
0283 col = pos % maxcol;
0284 pos += maxcol - col;
0285
0286 while (count-- > 0) {
0287 *con_buf++ = (vcs_scr_readw(vc, org++) & 0xff);
0288 if (++col == maxcol) {
0289 org = screen_pos(vc, pos, viewed);
0290 col = 0;
0291 pos += maxcol;
0292 }
0293 }
0294 }
0295
0296 static unsigned int vcs_read_buf(const struct vc_data *vc, char *con_buf,
0297 unsigned int pos, unsigned int count, bool viewed,
0298 unsigned int *skip)
0299 {
0300 u16 *org, *con_buf16;
0301 unsigned int col, maxcol = vc->vc_cols;
0302 unsigned int filled = count;
0303
0304 if (pos < HEADER_SIZE) {
0305
0306 con_buf[0] = min(vc->vc_rows, 0xFFu);
0307 con_buf[1] = min(vc->vc_cols, 0xFFu);
0308 getconsxy(vc, con_buf + 2);
0309
0310 *skip += pos;
0311 count += pos;
0312 if (count > CON_BUF_SIZE) {
0313 count = CON_BUF_SIZE;
0314 filled = count - pos;
0315 }
0316
0317
0318 count -= min(HEADER_SIZE, count);
0319 pos = HEADER_SIZE;
0320 con_buf += HEADER_SIZE;
0321
0322 } else if (pos & 1) {
0323
0324
0325
0326
0327 (*skip)++;
0328 if (count < CON_BUF_SIZE)
0329 count++;
0330 else
0331 filled--;
0332 }
0333
0334 if (!count)
0335 return filled;
0336
0337 pos -= HEADER_SIZE;
0338 pos /= 2;
0339 col = pos % maxcol;
0340
0341 org = screen_pos(vc, pos, viewed);
0342 pos += maxcol - col;
0343
0344
0345
0346
0347
0348 count = (count + 1) / 2;
0349 con_buf16 = (u16 *)con_buf;
0350
0351 while (count) {
0352 *con_buf16++ = vcs_scr_readw(vc, org++);
0353 count--;
0354 if (++col == maxcol) {
0355 org = screen_pos(vc, pos, viewed);
0356 col = 0;
0357 pos += maxcol;
0358 }
0359 }
0360
0361 return filled;
0362 }
0363
0364 static ssize_t
0365 vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
0366 {
0367 struct inode *inode = file_inode(file);
0368 struct vc_data *vc;
0369 struct vcs_poll_data *poll;
0370 unsigned int read;
0371 ssize_t ret;
0372 char *con_buf;
0373 loff_t pos;
0374 bool viewed, attr, uni_mode;
0375
0376 con_buf = (char *) __get_free_page(GFP_KERNEL);
0377 if (!con_buf)
0378 return -ENOMEM;
0379
0380 pos = *ppos;
0381
0382
0383
0384
0385 console_lock();
0386
0387 uni_mode = use_unicode(inode);
0388 attr = use_attributes(inode);
0389 ret = -ENXIO;
0390 vc = vcs_vc(inode, &viewed);
0391 if (!vc)
0392 goto unlock_out;
0393
0394 ret = -EINVAL;
0395 if (pos < 0)
0396 goto unlock_out;
0397
0398 if (uni_mode && (pos | count) & 3)
0399 goto unlock_out;
0400
0401 poll = file->private_data;
0402 if (count && poll)
0403 poll->event = 0;
0404 read = 0;
0405 ret = 0;
0406 while (count) {
0407 unsigned int this_round, skip = 0;
0408 int size;
0409
0410
0411
0412
0413
0414 size = vcs_size(vc, attr, uni_mode);
0415 if (size < 0) {
0416 if (read)
0417 break;
0418 ret = size;
0419 goto unlock_out;
0420 }
0421 if (pos >= size)
0422 break;
0423 if (count > size - pos)
0424 count = size - pos;
0425
0426 this_round = count;
0427 if (this_round > CON_BUF_SIZE)
0428 this_round = CON_BUF_SIZE;
0429
0430
0431
0432
0433
0434
0435 if (uni_mode) {
0436 ret = vcs_read_buf_uni(vc, con_buf, pos, this_round,
0437 viewed);
0438 if (ret)
0439 break;
0440 } else if (!attr) {
0441 vcs_read_buf_noattr(vc, con_buf, pos, this_round,
0442 viewed);
0443 } else {
0444 this_round = vcs_read_buf(vc, con_buf, pos, this_round,
0445 viewed, &skip);
0446 }
0447
0448
0449
0450
0451
0452
0453
0454
0455 console_unlock();
0456 ret = copy_to_user(buf, con_buf + skip, this_round);
0457 console_lock();
0458
0459 if (ret) {
0460 read += this_round - ret;
0461 ret = -EFAULT;
0462 break;
0463 }
0464 buf += this_round;
0465 pos += this_round;
0466 read += this_round;
0467 count -= this_round;
0468 }
0469 *ppos += read;
0470 if (read)
0471 ret = read;
0472 unlock_out:
0473 console_unlock();
0474 free_page((unsigned long) con_buf);
0475 return ret;
0476 }
0477
0478 static u16 *vcs_write_buf_noattr(struct vc_data *vc, const char *con_buf,
0479 unsigned int pos, unsigned int count, bool viewed, u16 **org0)
0480 {
0481 u16 *org;
0482 unsigned int col, maxcol = vc->vc_cols;
0483
0484 *org0 = org = screen_pos(vc, pos, viewed);
0485 col = pos % maxcol;
0486 pos += maxcol - col;
0487
0488 while (count > 0) {
0489 unsigned char c = *con_buf++;
0490
0491 count--;
0492 vcs_scr_writew(vc,
0493 (vcs_scr_readw(vc, org) & 0xff00) | c, org);
0494 org++;
0495 if (++col == maxcol) {
0496 org = screen_pos(vc, pos, viewed);
0497 col = 0;
0498 pos += maxcol;
0499 }
0500 }
0501
0502 return org;
0503 }
0504
0505
0506
0507
0508
0509 static inline u16 vc_compile_le16(u8 hi, u8 lo)
0510 {
0511 #ifdef __BIG_ENDIAN
0512 return (lo << 8u) | hi;
0513 #else
0514 return (hi << 8u) | lo;
0515 #endif
0516 }
0517
0518 static u16 *vcs_write_buf(struct vc_data *vc, const char *con_buf,
0519 unsigned int pos, unsigned int count, bool viewed, u16 **org0)
0520 {
0521 u16 *org;
0522 unsigned int col, maxcol = vc->vc_cols;
0523 unsigned char c;
0524
0525
0526 if (pos < HEADER_SIZE) {
0527 char header[HEADER_SIZE];
0528
0529 getconsxy(vc, header + 2);
0530 while (pos < HEADER_SIZE && count > 0) {
0531 count--;
0532 header[pos++] = *con_buf++;
0533 }
0534 if (!viewed)
0535 putconsxy(vc, header + 2);
0536 }
0537
0538 if (!count)
0539 return NULL;
0540
0541 pos -= HEADER_SIZE;
0542 col = (pos/2) % maxcol;
0543
0544 *org0 = org = screen_pos(vc, pos/2, viewed);
0545
0546
0547 if (pos & 1) {
0548 count--;
0549 c = *con_buf++;
0550 vcs_scr_writew(vc, vc_compile_le16(c, vcs_scr_readw(vc, org)),
0551 org);
0552 org++;
0553 pos++;
0554 if (++col == maxcol) {
0555 org = screen_pos(vc, pos/2, viewed);
0556 col = 0;
0557 }
0558 }
0559
0560 pos /= 2;
0561 pos += maxcol - col;
0562
0563
0564 while (count > 1) {
0565 unsigned short w;
0566
0567 w = get_unaligned(((unsigned short *)con_buf));
0568 vcs_scr_writew(vc, w, org++);
0569 con_buf += 2;
0570 count -= 2;
0571 if (++col == maxcol) {
0572 org = screen_pos(vc, pos, viewed);
0573 col = 0;
0574 pos += maxcol;
0575 }
0576 }
0577
0578 if (!count)
0579 return org;
0580
0581
0582 c = *con_buf++;
0583 vcs_scr_writew(vc, vc_compile_le16(vcs_scr_readw(vc, org) >> 8, c),
0584 org);
0585
0586 return org;
0587 }
0588
0589 static ssize_t
0590 vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
0591 {
0592 struct inode *inode = file_inode(file);
0593 struct vc_data *vc;
0594 char *con_buf;
0595 u16 *org0, *org;
0596 unsigned int written;
0597 int size;
0598 ssize_t ret;
0599 loff_t pos;
0600 bool viewed, attr;
0601
0602 if (use_unicode(inode))
0603 return -EOPNOTSUPP;
0604
0605 con_buf = (char *) __get_free_page(GFP_KERNEL);
0606 if (!con_buf)
0607 return -ENOMEM;
0608
0609 pos = *ppos;
0610
0611
0612
0613
0614 console_lock();
0615
0616 attr = use_attributes(inode);
0617 ret = -ENXIO;
0618 vc = vcs_vc(inode, &viewed);
0619 if (!vc)
0620 goto unlock_out;
0621
0622 size = vcs_size(vc, attr, false);
0623 if (size < 0) {
0624 ret = size;
0625 goto unlock_out;
0626 }
0627 ret = -EINVAL;
0628 if (pos < 0 || pos > size)
0629 goto unlock_out;
0630 if (count > size - pos)
0631 count = size - pos;
0632 written = 0;
0633 while (count) {
0634 unsigned int this_round = count;
0635
0636 if (this_round > CON_BUF_SIZE)
0637 this_round = CON_BUF_SIZE;
0638
0639
0640
0641
0642 console_unlock();
0643 ret = copy_from_user(con_buf, buf, this_round);
0644 console_lock();
0645
0646 if (ret) {
0647 this_round -= ret;
0648 if (!this_round) {
0649
0650
0651
0652 if (written)
0653 break;
0654 ret = -EFAULT;
0655 goto unlock_out;
0656 }
0657 }
0658
0659
0660
0661
0662
0663 size = vcs_size(vc, attr, false);
0664 if (size < 0) {
0665 if (written)
0666 break;
0667 ret = size;
0668 goto unlock_out;
0669 }
0670 if (pos >= size)
0671 break;
0672 if (this_round > size - pos)
0673 this_round = size - pos;
0674
0675
0676
0677
0678
0679 if (attr)
0680 org = vcs_write_buf(vc, con_buf, pos, this_round,
0681 viewed, &org0);
0682 else
0683 org = vcs_write_buf_noattr(vc, con_buf, pos, this_round,
0684 viewed, &org0);
0685
0686 count -= this_round;
0687 written += this_round;
0688 buf += this_round;
0689 pos += this_round;
0690 if (org)
0691 update_region(vc, (unsigned long)(org0), org - org0);
0692 }
0693 *ppos += written;
0694 ret = written;
0695 if (written)
0696 vcs_scr_updated(vc);
0697
0698 unlock_out:
0699 console_unlock();
0700 free_page((unsigned long) con_buf);
0701 return ret;
0702 }
0703
0704 static __poll_t
0705 vcs_poll(struct file *file, poll_table *wait)
0706 {
0707 struct vcs_poll_data *poll = vcs_poll_data_get(file);
0708 __poll_t ret = DEFAULT_POLLMASK|EPOLLERR;
0709
0710 if (poll) {
0711 poll_wait(file, &poll->waitq, wait);
0712 switch (poll->event) {
0713 case VT_UPDATE:
0714 ret = DEFAULT_POLLMASK|EPOLLPRI;
0715 break;
0716 case VT_DEALLOCATE:
0717 ret = DEFAULT_POLLMASK|EPOLLHUP|EPOLLERR;
0718 break;
0719 case 0:
0720 ret = DEFAULT_POLLMASK;
0721 break;
0722 }
0723 }
0724 return ret;
0725 }
0726
0727 static int
0728 vcs_fasync(int fd, struct file *file, int on)
0729 {
0730 struct vcs_poll_data *poll = file->private_data;
0731
0732 if (!poll) {
0733
0734 if (!on)
0735 return 0;
0736 poll = vcs_poll_data_get(file);
0737 if (!poll)
0738 return -ENOMEM;
0739 }
0740
0741 return fasync_helper(fd, file, on, &poll->fasync);
0742 }
0743
0744 static int
0745 vcs_open(struct inode *inode, struct file *filp)
0746 {
0747 unsigned int currcons = console(inode);
0748 bool attr = use_attributes(inode);
0749 bool uni_mode = use_unicode(inode);
0750 int ret = 0;
0751
0752
0753 if (attr && uni_mode)
0754 return -EOPNOTSUPP;
0755
0756 console_lock();
0757 if(currcons && !vc_cons_allocated(currcons-1))
0758 ret = -ENXIO;
0759 console_unlock();
0760 return ret;
0761 }
0762
0763 static int vcs_release(struct inode *inode, struct file *file)
0764 {
0765 struct vcs_poll_data *poll = file->private_data;
0766
0767 if (poll)
0768 vcs_poll_data_free(poll);
0769 return 0;
0770 }
0771
0772 static const struct file_operations vcs_fops = {
0773 .llseek = vcs_lseek,
0774 .read = vcs_read,
0775 .write = vcs_write,
0776 .poll = vcs_poll,
0777 .fasync = vcs_fasync,
0778 .open = vcs_open,
0779 .release = vcs_release,
0780 };
0781
0782 static struct class *vc_class;
0783
0784 void vcs_make_sysfs(int index)
0785 {
0786 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL,
0787 "vcs%u", index + 1);
0788 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 65), NULL,
0789 "vcsu%u", index + 1);
0790 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL,
0791 "vcsa%u", index + 1);
0792 }
0793
0794 void vcs_remove_sysfs(int index)
0795 {
0796 device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 1));
0797 device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 65));
0798 device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 129));
0799 }
0800
0801 int __init vcs_init(void)
0802 {
0803 unsigned int i;
0804
0805 if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops))
0806 panic("unable to get major %d for vcs device", VCS_MAJOR);
0807 vc_class = class_create(THIS_MODULE, "vc");
0808
0809 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs");
0810 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 64), NULL, "vcsu");
0811 device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa");
0812 for (i = 0; i < MIN_NR_CONSOLES; i++)
0813 vcs_make_sysfs(i);
0814 return 0;
0815 }